Does java classloader validate method body? - java

For Java compiler to resolve reference to another class in different file needs to be on classpath.
I know that if the class is not found on the classpath or the method signatures do not match some Exceptions are thrown.
However, is there any validation that compares compile time classpath with runtime classpath when it comes to method implementations?
What If I provide classpath with method signatures matching the ones during compile time but with different implementation.
Will there be no Validation error or any Exception thrown? That seems very insecure.

Generally speaking, no, as long as the signatures match some JVMs can even replace methods at runtime (modulo some additional constraints).
Of course a custom classloader implementation could perform extended validations, such as checking hashes/signatures.
What the JVM does validate is the standards-conformance of the bytecode. I.e. bytecode cannot reference nonexistent stack slots, do arbitrary jumps or similar things that would lead to illegal behavior inside the JVM.

Related

What causes an IncompatibleClassChangeError when calling methods via JNI?

I have a native application that launches a JVM and interacts with it through the JNI API.
The native application constructs a (complex) JVM object and passes it as parameter in a method call.
The problem is that in some cases, on determinate inputs, the JVM crashes during the execution of the method with:
Exception in thread "Thread-1" java.lang.IncompatibleClassChangeError
I learned that this exception is thrown by the JVM when some incompatible binary changes has been made to a class used by a client that is not aware of the changes. However, I don't see how this could happen: in my case the JVM has only one fat jar in the classpath. I can't find duplicate classes in it, and the client code is always compiled to produce the fat jar.
On "What causes java.lang.IncompatibleClassChangeError?" I found that there is another potential reason: a method called via JNI with objects of the wrong type, e.g. in the wrong order. So, I added dynamic checks with IsInstanceOf to check the type of any object passed to the JVM. Unfortunately, all the checks succeed.
How can I debug this? The exception has no message (e.g. no message like "Expected non-static field ChildClass.message"). This may hint that it's a problem caused by the JNI, and not by the "more common" case of an incompatible binary change in a library.
I tried -verbose:class but I don't see anything strange in the log. The last loaded class seems a common Scala lambda function, nothing strange:
[Loaded a.b.c.FastPrettyPrinter$$$Lambda$457/868352876 from a.b.c.FastPrettyPrinter$]
Is there an exhaustive list, or explanation, of what might cause an IncompatibleClassChangeError when calling JVM methods via the JNI?

Why does java.lang.VerifyError prevent successful compilation?

According this topic:
Reasons of getting a java.lang.VerifyError
java.lang.VerifyError obtains if version of execution jvm newer than jvm that was used for compilation.
Always we can fix this problem using following jvm option: -XX:-UseSplitVerifier.
According this:
https://stackoverflow.com/a/16467026/2674303
to use this option is 'perfectly safe'.
Thus I don't understand why does java.lang.VerifyError is problem that prevents succesful compilation. Please clarify. Maybe it is not safe for libraries which instrument bytecode?
The Q&A's that you link to refer to a specific kind of verification error that it is safe to work around by using an alternative verifier. However, there are other kinds of verify error that you should not ignore ... and that you cannot deal with that way.
In short, the advice in the linked question does not apply in general. In general:
Switching to the alternative verifier probably won't help.
If you disable the verifier entirely, you risk running code that may violate the JVM's runtime type-safety (etc) constraints. That can lead to security issues and / or heap corruption and hard JVM crashes.
If you have a specific VerifyError that you need advice on, please include the full exception message and stacktrace, and describe the situation in which it occurs. Note that Andrey's answer is correct that a common cause of verification errors is bugs in code that is doing "bytecode engineering" for various purposes. Often, the fix is to change to a different version of the corresponding dependency.
VerifyError happens when your byte code is incorrect in some way. For example, when it tries to read from an uninitialized variable or assigns a primitive value to a field of type Object. An instrumentation library may have bugs leading to generation of such malformed bytecode. If you can share the exact details of the error, we could probably be more specific about the exact cause.
The goal is to force tools and libraries generate correct StackMapTable attribute whenever they manipulate Java bytecode, so that JVM does not have to do slow and complicated type inferencing phase of verification, but rather do only fast and simple type checking phase.
-XX:-UseSplitVerifier has been deprecated in Java 8, it won't help any more.
VerifyError is thrown during class loading by JVM, so it appears on runtime level, not compile level.
Not always we can fix probem by using old verifier. There is a particular case when this helps. It only helps when class is propher for older verifier and is missing StackMapTable which was introduced in JVM 1.6 and are obligatory in JVM 1.7
If you want to get rid VerifyError and UseSplitVerifier does not help that mean that your class is incorrect from JVM point of view. You can still turn off whole verification, but this can cause problems.
Bytecode manipulation is always danger when you don't know what are your'e doing. Now main bytecode manipulation libraries support StackMapFrames calculations so they could generate bytecode which is propher for every VM. But still, user can generate bytecode which is incorrect on class file level, in that case JVM will still thrown VerifyError on load, SplitVerifier won't help. Only disabling verification will force VM to load that class, but could be some error when it will be executing.

Class Loading: Compile time or Runtime

Is java class loaded at compile time or Runtime? I know you can dynamically load a java class using your own Class Loaders or calling Class.forName("fully qualified name of java file").
But if I am not using any of the above techniques then does it means that Java classes are loaded at compile time?
No. The JLS explains how loading and linking is runtime. Any binary-compatible revision of a class can be thrown in place of an existing class.
A class is compiled with a symbolic reference to other classes, not the code of those classes.
However with certain non-JVM compilers like GCJ classes can be compiled ahead of time into one executable.
Classes are loaded at runtime to execute their code.
Classes are loaded at compile time to check code using the class for type safety.
Whenever you write code that uses a class (eg, calling a method on it), the compiler needs to load that class to make sense of your code (eg, to check methods or base type)
At compile time nothing is loaded. At compile time classes are just generated from sources.
The difference could be if a class is loaded by the ClassLoader when the JVM powers up or if you do it dynamically during the execution, but they are two sides of the same medal.
In both cases they are loaded dynamically, but in the former this is done as soon as the JVM starts.
As many others have stated, classes are loaded at runtime unless you are using an AOT compiler to allow them to run in non-JVM environments. If you want to read up all the details on how the Java Virtual Machine starts up, initializes and loads the first then subsequent classes you can take a look at the Java SE 7 specifications.

Java Path Finder for Bytecode Verification in an existing project, Bytecode Verification

I'm trying to use JPF to verify my bytecode generated while runtime with javassist.
The code I'm trying to verify is supplied by the user while my program is running. As I can't check all OOP models and stuff like that I need a verification process before running his code.
At the moment I simply generate bytecode with javassist from his classes.
My problem now is that I get exceptions sometimes because the user did some inheritance mistakes and stuff and my application shuts down with an exception cause I tried to load and execute his classes.
Therefore I would like to verify that generated bytecode in runtime to avoid such exceptions and to know earlier if the classes supplied from the user are faulty (or contain any problem).
Is this possible with JPF while in runtime?
Any other solutions on this?
Thanks!
As JPF uses BCEL Stand-alone Bytecode Verifier might be helpful. Just programmatically invoke the Verifier class - or even dive into the details of this class.
hth
There are many points to check:
the bytecode itself according to the class file format
the runtime phases: loading, linking and initializing
From my point of view, a ClassLoader does all that steps but it generally loads one Class at a time, and only on demand.
In your context, I propose you write a ClassLoader that loads in sequence all classes from generated bytecodes and reports each failing class name with caught exceptions. The ClassLoader is instantiated with the reference to the relevant parent ClassLoader and is discarded after the test passed, the generated bytecode is then loaded by the original ClassLoader of your runtime context.
Probably this class loading check may be implemented thanks to OSGi but it will require more efforts than a standalone ClassLoader.
If you don't have an absolute requirement to use JPF, the ASM library includes CheckClassAdapter which can verify byte code. It is only a sanity check however - I don't believe it will catch problems with inheritance etc.

-classpath option for javac and java

I'm confused with the role -classpath option plays in both compiling and running a java program. Please help me understand.
Because they are two separate operations and not necessarily the same paths.
The runtime dependencies are often more extensive than the compile time dependencies. For example, many programs will code to interfaces, which limits the compile time dependencies to those interfaces. At runtime, the VM must be able to resolve the implementations of those interfaces, which are not required until they are loaded at runtime.
it simply in both cases tells javac and java where to find dependencies required for your program to both compile and run
The reason it is done twice is that the environment you compile the code in may not be the same environment you run the code in.
Java loads classes at runtime. For example, you could write a method that forces loading of class X, compile it, write class X, compile it, and then run them together. In addition, you typically refer to classes by a fully specified name, but could run the same program with different versions of that class (e.g., a different version of the library). Thus, you need to tell Java where it could potentially find the classes that it needs to load.
As for compilation, to ensure type safety, you have to provide the Java compiler at least with the interfaces or base classes that you are referring to and making calls on, so that the compiler can at least ensure that the call would be legal. For that reason, you have to tell it where to find the jars containing them.
Here is an example. Let's say you want to use JMS (a messaging framework) in a core Java program. At compile time, you need to at least tell javac where to find the JMS interfaces. At runtime, you need to provide these interfaces, but you also need to provide the JAR with the actual implementation (e.g., ActiveMQ).
In C++ I believe it is the case that linking happens around compile-time, to create an executable (I am not a C++ programmer so I'm not sure about that).
In Java, the linker step happens at runtime (see the JVM spec, "Loading, Linking and Initalizing"). From your question it sounds like you understand why the classpath needs to be specified at compile time (because you might reference classes from third-party JARs in your code), so I will just explain that when your program is being run, those classes are not loaded into the JVM until they are referenced. At this point, the JVM needs to know where to find their representation.
The compiler has to know where to look to satisfy compile-time dependencies.
The VM has to know where to look to satisfy runtime dependencies.
At compile time, you need to tell javac where to find third-party and user-defined classes. At runtime, you also need to tell java where to find third-party and user-defined classes. In both cases, one way to change the class path is to use the JDK Tools' -classpath option. Checkout the Setting the Class Path technical note for more details.

Categories