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: