Stephen,
I think the reason you get the IllegalAccessError is because the VM
thinks you are loading from a remote url.
I don't think the user of a classloader per-se forces the
verification of the class. (I wrote a class loader like yours that
just loads a file with no package in the current dir and was able to
access the private method).
You can also note that your class isn't verified if -noverify is
passed (perhaps this is obvious :)
-- Michael
On 5/13/06, Stephen de Vries <[EMAIL PROTECTED]> wrote:
On 12 May 2006, at 09:10, Charles Miller wrote:
>
> It's not reflection: you're confusing IllegalAccessException and
> IllegalAccessError.
>
> For any non-Java nerd still listening in: there are two fundamental
> types of "Throwable" exception-conditions in Java: Exceptions and
> Errors[1]. Exceptions represent application-level conditions --
> things an application is likely to be able to recover from, like
> network timeouts, trying to read beyond the end of a file, and so
> on. Errors, on the other hand, represent VM-level problems that an
> application can't really do anything about, like running out of
> memory, not finding a required native library, or encountering
> corrupted class files.
>
> IllegalAccessException happens when reflective code attempts to
> access some field or method it's not supposed to. Because it's a
> result of reflection, it's considered an application-level problem
> and it's assumed your code can recover gracefully.
>
> Amusingly enough, you can get around most IllegalAccessExceptions
> in java just by calling {field|method}.setAccessible(true). So long
> as there's no explicit SecurityManager installed, as soon as you've
> done that you're free to modify the field or call method to your
> heart's content[2].
>
> IllegalAccess_Error_, on the other hand, happens when some non-
> reflective code issues a bytecode instruction that attempts to
> access a field or method it shouldn't be able to see. If you look
> at its class hierarchy, the meaning of the class is pretty clear:
> IllegalAccessError is a subclass of IncompatibleClassChangeError,
> which is a subclass of LinkageError. Because this is a problem at
> the bytecode/classloading level, and literally something that could
> happen on _any_ method-call or field-access, it's flagged as an error.
>
> The Error generally occurs when class A has been compiled against a
> version of class B where a method is public, but that method is
> private in the version of the same class it encounters at runtime.
> This sort of thing happens quite often in Java, you're frequently
> stuck in "jar file hell", in a twisty turny maze of library
> interdependencies, all with slightly different version numbers.
>
> More about the circumstances of IllegalAccessError here:
>
> http://java.sun.com/docs/books/vmspec/2nd-edition/html/
> ConstantPool.doc.html
>
> Dynamic classloading isn't really at fault here. There are all
> sorts of pits you can fall into when you start rolling your own
> classloader (the Java webapp I develop supports dynamic runtime-
> deployable plugins, and the classloading issues are a HUGE
> headache), but IllegalAccessError isn't one of them.
>
> Charles
>
> [1] Exceptions are further divided into checked exceptions and
> runtime exceptions, but that's beyond the scope of this email
> [2] See also: http://www.javaspecialists.co.za/archive/
> Issue014.html
Thanks for clearing this up Charles.
I've created another example that uses a class loader to load the
classes, and this time, it throws an IllegalAccessError just like
Tomcat does:
Loading class: /Users/stephen/data/dev/classloader/myclass/
somepackage/MyTest.class
Loading class: /Users/stephen/data/dev/classloader/myclass/java/lang/
Runnable.class
Loading class: /Users/stephen/data/dev/classloader/myclass/java/lang/
Object.class
Loading class: /Users/stephen/data/dev/classloader/myclass/
somepackage/MyData.class
Loading class: /Users/stephen/data/dev/classloader/myclass/java/lang/
System.class
Exception in thread "main" java.lang.IllegalAccessError: tried to
access method somepackage.MyData.getName()Ljava/lang/String; from
class somepackage.MyTest
at somepackage.MyTest.run(MyTest.java:15)
at classloader.Main.main(Main.java:26)
Java Result: 1
This error is thrown irrespective of the -verify flag. So it looks
like using a classloader causes the VM to perform verification,
whether or not the "verifier" was enabled. Michael Silk made a
similar statement earlier in this thread. Would you agree?
PoC code below:
package classloader;
public class Main {
public Main() {
}
public static void main(String[] args) {
//Illegal Access Error
try {
CustomLoader cl = new CustomLoader(System.getProperty
("user.dir")+"/myclass/");
Class myClass = cl.loadClass("somepackage.MyTest");
Runnable r = (Runnable)myClass.newInstance();
r.run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package classloader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class CustomLoader extends ClassLoader {
private String path = null;
public CustomLoader(String path) {
this.path = path;
}
private byte[] getBytes( String filename ) throws IOException {
File file = new File( filename );
long len = file.length();
byte raw[] = new byte[(int)len];
FileInputStream fin = new FileInputStream( file );
int r = fin.read( raw );
if (r != len)
throw new IOException( "Can't read all, "+r+" != "+len );
fin.close();
return raw;
}
public Class loadClass( String name, boolean resolve )
throws ClassNotFoundException {
Class clas = null;
String fileStub = name.replace( '.', '/' );
String classFilename = path+fileStub+".class";
System.out.println("Loading class: "+classFilename);
File classFile = new File( classFilename );
try {
byte raw[] = getBytes( classFilename );
clas = defineClass( name, raw, 0, raw.length );
} catch( IOException ie ) {
}
if (clas==null) {
clas = findSystemClass( name );
}
if (resolve && clas != null)
resolveClass( clas );
if (clas == null)
throw new ClassNotFoundException( name );
return clas;
}
}
In current directory create a folder ./myclass/somepackage with the
following two files:
package somepackage;
public class MyData {
private String name;
public MyData() {
name = "No one can read me";
}
public String getName() {
System.out.println("private method called");
return (name);
}
}
package somepackage;
public class MyTest implements Runnable {
MyData m;
public MyTest() {
m = new MyData();
}
public void run() {
System.out.println(m.getName());
}
}
Compile both these classes, then change the MyData.getName method to
private access, and recompile MyData.
Stephen
_______________________________________________
Secure Coding mailing list (SC-L)
[email protected]
List information, subscriptions, etc - http://krvw.com/mailman/listinfo/sc-l
List charter available at - http://www.securecoding.org/list/charter.php
_______________________________________________
Secure Coding mailing list (SC-L)
[email protected]
List information, subscriptions, etc - http://krvw.com/mailman/listinfo/sc-l
List charter available at - http://www.securecoding.org/list/charter.php