Secure Coding mailing list archives
By default, the Verifier is disabled on .Net and Java
From: cradle at umd.edu (David Eisner)
Date: Thu, 04 May 2006 18:58:48 -0400
Tim Hollebeek wrote:
The fact that editing the .class file allows you to produce one that causes the JVM to crash is pretty strong evidence the verifier was NOT used to validate the file.
Right. What follows is a summary of what we know so far, and what I think is going on. The details get a bit gory at the end, but you can skim it. I ran the corrupted .class file three ways, and obtained these results: 1) (no switch): ClassFormatError exception 2) -verify: ClassFormatError exception 3) -noverify: JVM crash (EXCEPTION_ACCESS_VIOLATION) I thought (incorrectly) that this might indicate that providing no switch was equivalent to -verify. In fact, it now appears that providing no switch is equivalent to -verifyremote (see my previous message). If I do the same experiment again, I see that, yes, "java -verify" and "java -verifyremote" produce the same result, and "java -noverify" crashes the JVM. This suggests to me that the locally loaded HelloWorld.class file is considered "remote" code. On the other hand, Michael's and Dinis's experiments show that (using Michael's example, after recompiling Foo.java once k is made a private member): 1) (no switch / -verifyremote ): illegal access to private member ALLOWED 2) -verify (i.e. -Xverify:all): illegal access to private member CAUGHT 3) -noverify( i.e. -Xverify:none): illegal access to private member ALLOWED So in this case, cases 1 and 3 are the same (with my mangled .class file experiment, cases 1 and 2 are the same). This suggests that the Foo.class file is being treated as "local" code. How are we to understand this? The short answer is that the meaning of "remote" depends on what type of verification Sun's hotspot VM is doing. With the help of Gary's book [1][2] (thanks!) we note that the Verifier, in verifying classes, performs two kinds of checks: "verification time" checks (steps 1-3 of page [2]) on the class file and its bytecodes, and "runtime" checks, checks that can't be done at verification time because "aspects of Java's type system cannot be statically checked." This includes "check[ing] the executing method for access privilege." This is step 4. on page [2] of Gary's book). We see that my experiment involves static, verification-time checks. Michael and Divis's experiments, on the other hand, involve runtime checks. It appears that the VM is treating .class files loaded through the CLASSPATH as "remote" for the purpose of verification-time (i.e. static) checks, but treating them as "local" for the purpose of run-time access checking verification. Just to verify this, here is another experiment, again with the corrupt class file (actually, this is the HelloWorld.class from my last email, so it's a slightly different ClassFormatError). First, I run with java -verifyremote and get the ClassFormatError. This means the VM thinks ./HelloWorld.class is remote. Next, I use the -Xbootclasspath/a switch to add the current directory to the bootclasspath. This should cause the VM to treat ./HelloWorld.class as it does the system classes in rt.jar, etc, i.e., "local." And, yes, now, I get the JVM crash, even with -verifyremote: ---8<------------------ $ java -verifyremote HelloWorld Exception in thread "main" java.lang.ClassFormatError: Invalid method Code length 858993413 in class file HelloWorld at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:620) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124) at java.net.URLClassLoader.defineClass(URLClassLoader.java:260) at java.net.URLClassLoader.access$100(URLClassLoader.java:56) at java.net.URLClassLoader$1.run(URLClassLoader.java:195) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:188) at java.lang.ClassLoader.loadClass(ClassLoader.java:306) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268) at java.lang.ClassLoader.loadClass(ClassLoader.java:251) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319) $ java -Xbootclasspath/a:. -verifyremote HelloWorld # # An unexpected error has been detected by HotSpot Virtual Machine: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x6d72dacf, pid=5168, tid=3576 # # Java VM: Java HotSpot(TM) Client VM (1.5.0_06-b05 mixed mode) # Problematic frame: # V [jvm.dll+0x4dacf] # # An error report file with more information is saved as hs_err_pid5168.log # # If you would like to submit a bug report, please visit: # http://java.sun.com/webapps/bugreport/crash.jsp # ---8<------------------ Summary of what I think happens, more or less, with the 1.5 JRE. 1. DEFINITIONS a) "Bootstrap Class": any class that was loaded by the "Primordial Class Loader." [3] (aka "bootstrap class loader.") Classes in the bootstrap classpath are one example (like classes in rt.jar). b) "Ordinary Class": any class that was loaded by a Class Loader that is not the Primordial class loader. This includes classes on the local filesystem loaded from the CLASSPATH. (note that this is different than the behavior described in [3]). In other words, the set of Ordinary Classes is the complement of the set of Bootstrap classes. This also includes classes loaded over the network. c) "CP Class": any class that was loaded by the (confusingly named) "System Class Loader." These are the classes found through CLASSPATH or by the -cp switch. [5] ("CP stands for CLASSPATH, but I thought "CLASSPATH Class" was too unwieldy."). Classes loaded over the network are *not* CP classes. 2. STATIC (or "verification time") VERIFICATION Static verification refers to class-file format verification and bytecode analsysis described in steps 1-3 of [2]. By default, static verification is performed on Ordinary Classes only. Specifying java -verify/-Xverify:all causes both Bootsrap and Ordinary classes to undergo static verification. And with -noverify/-Xverify:none, no class will undergo static verification. [See jdk-1_5_0_scsl/hotspot/src/share/vm/runtime/classFileParser.cpp [4] Notice that "_need_verify" is set by calling Verifier::should_verify_for(), defined in jdk-1_5_0_scsl/hotspot/src/share/vm/runtime/verifier.cpp. Then trace how BytecodeVerificationLocal and BytecodeVerificationRemote are set by the commandline args. ] 3. RUNTIME VERIFICATION Runtime verification refers to non-static verification that can't be performed until class execution, including access to private/public/etc. fields. [2] I *think* that, by default, (e.g. no switch or -verifyremote), only classes that are Ordinary but not CP classes undergo runtime verification. Network-loaded classes would be an example. If -verify (-Xverify:all) is specified, all classes undergo verification. Finally, if -noverify (-Xverify:none) is specified, no classes undergo verification. [See Reflection::verify_field_access() in jdk-1_5_0_scsl/hotspot/src/share/vm/runtime/reflection.cpp. This is the method that checks whether access to a private field is permitted, for example. Notice that (depending on how it is called) verify_field_access() calls Verifier::relax_verify_for( <the class's class loader>) to see whether it can skip the check. It looks like (see jdk-1_5_0_scsl/hotspot/src/share/vm/runtime/verifier.cpp again) relax_verify_for() decides whether the passed-in Class loader (i.e. the loader for the class trying to get access to a field) should get to skip the access checks. Here is how it decides (if you decode the logic): 1. if -verify (i.e. -Xverify:all) was specified when the VM was initialized, then no class loader gets to skip the access check. 2. else if -verifyremote (-Xverify:remote) was specified, then the class loader gets to skip the access check if java_lang_ClassLoader::is_trusted_loader() says it's trusted! 3. else nobody gets to skip the check. And, finally (my head hurts), what is a "trusted_loader" ? See is_trusted_loader() in jdk-1_5_0_scsl/hotspot/src/share/vm/memory/javaClasses.cpp It says (I paraphrase), "This loader is trusted if it is the SystemDictionary::java_system_loader(), or one of its ancestors." This is where I'm a little lost. I think this is the "System Class Loader" refered to in [5]] Is anybody still reading? -David [1] http://www.securingjava.com/chapter-two/chapter-two-5.html [2] http://www.securingjava.com/chapter-two/chapter-two-6.html [3] http://www.securingjava.com/chapter-two/chapter-two-7.html [4] http://www.sun.com/software/communitysource/j2se/java2/download.xml [5] http://archives.java.sun.com/cgi-bin/wa?A2=ind0107&L=java-security&D=0&P=1305
Current thread:
- By default, the Verifier is disabled on .Net and Java, (continued)
- By default, the Verifier is disabled on .Net and Java Tim Hollebeek (May 04)
- By default, the Verifier is disabled on .Net and Java Dinis Cruz (May 12)
- By default, the Verifier is disabled on .Net and Java Wall, Kevin (May 03)
- By default, the Verifier is disabled on .Net and Java Michael Silk (May 03)
- By default, the Verifier is disabled on .Net and Java Dinis Cruz (May 03)
- By default, the Verifier is disabled on .Net and Java David Eisner (May 04)
- By default, the Verifier is disabled on .Net and Java Stephen de Vries (May 04)
- By default, the Verifier is disabled on .Net and Java Dinis Cruz (May 03)
- By default, the Verifier is disabled on .Net and Java Michael Silk (May 04)
- By default, the Verifier is disabled on .Net and Java Dinis Cruz (May 12)
- By default, the Verifier is disabled on .Net and Java Michael Silk (May 11)
- By default, the Verifier is disabled on .Net and Java David Eisner (May 11)