RFR: 8315130: java.lang.IllegalAccessError when processing classlist to create CDS archive

Ioi Lam iklam at openjdk.org
Fri Apr 4 06:21:49 UTC 2025


On Tue, 25 Mar 2025 11:08:24 GMT, Timofei Pushkin <tpushkin at openjdk.org> wrote:

> If a base class is package-private then its subclasses should have the same package name and defining class loader, otherwise `IllegalAccessError` is thrown when linking a subclass. Currently when dumping a static archive separate `URLClassLoader`s are used for each unregistered classes' source. Thus if two unregistered classes, a package-private base class and a sub class, from the same package reside in different sources `IllegalAccessError` will be thrown when linking the sub class. This can be unexpected because the app could have used a single class loader for both classes and thus not have seen the error — see `DifferentSourcesApp.java` from this patch for an example of such app.
> 
> This patch fixes the issue by using a single class loader for all unregistered classes. CDS does not allow classes with the same name making such solution possible.
> 
> Implementation note: `URLClassLoader` does not allow selecting a specific URL to load a specific class — I used reflection to override a private part of `URLClassLoader` responsible for URL selection while being able to use the rest of its implementation.

Hi Timofei, thanks for fixing this. 

(Sorry I didn't notice this PR until now ...)

I have a suggestion for further simplification.

src/java.base/share/classes/jdk/internal/misc/CDS.java line 420:

> 418:                 // class loader. Thus it is safe to delegate their loading to system class loader
> 419:                 // (our parent) - this is what the default implementation of loadClass() will do.
> 420:                 return (Class<?>) DEFINE_CLASS.invoke(this, name, res);

Instead of subclassing from `URLClassLoader` and go through the trouble of calling its `defineClass()` method, maybe we should just subclass from `ClassLoader` and maintain a hashtable of java.util.jar.JarFiles.


HashMap<String, JarFile> map = ....;
JarFile jar = map.get(source) ... or open a new JarFile if not found
byte[] buffer = read jar file for the given class name;
call ClassLoader.defineClass(buffer, ...)

src/java.base/share/classes/jdk/internal/misc/CDS.java line 446:

> 444:         protected Class<?> findClass(String name) throws ClassNotFoundException {
> 445:             // Unregistered classes should be found in load(...), registered classes should be
> 446:             // handeled by parent loaders

This does seem like a good simplification, as we no longer need to check the `currentClassName`, `currentSuperClass`, etc. 

We had a plan for supported multiple classes for the same name, but we never go around doing it (even though the classlist format supports that with the use of IDs).

I am not sure if the current tests includes a case where a class name `Foo` exists both in the classpath as well as in a custom loader. If not, I think we should add such a case (to ensure that UnregisteredClassLoader never delegates to the application class loader and finds the wrong `Foo`).

-------------

PR Review: https://git.openjdk.org/jdk/pull/24223#pullrequestreview-2741852934
PR Review Comment: https://git.openjdk.org/jdk/pull/24223#discussion_r2028155863
PR Review Comment: https://git.openjdk.org/jdk/pull/24223#discussion_r2028141440


More information about the core-libs-dev mailing list