Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

TimPushkin
Copy link

@TimPushkin TimPushkin commented Mar 25, 2025

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 URLClassLoaders 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.


Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed (2 reviews required, with at least 1 Reviewer, 1 Author)

Issue

  • JDK-8315130: java.lang.IllegalAccessError when processing classlist to create CDS archive (Bug - P4)

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/24223/head:pull/24223
$ git checkout pull/24223

Update a local copy of the PR:
$ git checkout pull/24223
$ git pull https://git.openjdk.org/jdk.git pull/24223/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 24223

View PR using the GUI difftool:
$ git pr show -t 24223

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/24223.diff

Using Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Mar 25, 2025

👋 Welcome back tpushkin! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Mar 25, 2025

❗ This change is not yet ready to be integrated.
See the Progress checklist in the description for automated requirements.

@openjdk openjdk bot added the rfr Pull request is ready for review label Mar 25, 2025
@openjdk
Copy link

openjdk bot commented Mar 25, 2025

@TimPushkin The following labels will be automatically applied to this pull request:

  • core-libs
  • hotspot-runtime

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command.

@mlbridge
Copy link

mlbridge bot commented Mar 25, 2025

Copy link
Member

@iklam iklam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Timofei, thanks for fixing this.

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

I have a suggestion for further simplification.

@TimPushkin TimPushkin requested a review from iklam April 10, 2025 06:12
@iklam
Copy link
Member

iklam commented Apr 15, 2025

/reviewers 2

@openjdk
Copy link

openjdk bot commented Apr 15, 2025

@iklam
The total number of required reviews for this PR (including the jcheck configuration and the last /reviewers command) is now set to 2 (with at least 1 Reviewer, 1 Author).

Copy link
Member

@calvinccheung calvinccheung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have one comment in CDS.java.

@openjdk
Copy link

openjdk bot commented Apr 16, 2025

@TimPushkin this pull request can not be integrated into master due to one or more merge conflicts. To resolve these merge conflicts and update this pull request you can run the following commands in the local repository for your personal fork:

git checkout one-loader
git fetch https://git.openjdk.org/jdk.git master
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge master"
git push

@openjdk openjdk bot added the merge-conflict Pull request has merge conflict with target branch label Apr 16, 2025
@openjdk openjdk bot removed the rfr Pull request is ready for review label Apr 16, 2025
@openjdk openjdk bot added rfr Pull request is ready for review and removed merge-conflict Pull request has merge conflict with target branch labels Apr 16, 2025
@TimPushkin
Copy link
Author

Besides adding the warnings I noticed that UnregisteredClassLoader is omitted from the dump and implemented the same for UnregisteredClassLoader$Source and its implementations.

Comment on lines +508 to +517
// If an unregistered class U is specified to have a registered supertype S1
// named SN but an unregistered class S2 also named SN has already been loaded
// S2 will be incorrectly used as the supertype of U instead of S1 due to
// limitations in the loading mechanism of unregistered classes.
void ClassListParser::check_supertype_overshadowing(int supertype_id, const InstanceKlass* supertype, TRAPS) {
const InstanceKlass* overshadower = SystemDictionaryShared::get_unregistered_class(supertype->name());
if (overshadower == nullptr || overshadower == supertype) {
return;
}
assert(!supertype->is_shared_unregistered_class(), "unregistered supertype cannot be overshadowed");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this always prevent an unregistered class to use any other unregistered class as its super type?

I think a better check would be here:

    if (!k->local_interfaces()->contains(specified_interface)) {
+     jio_fprintf(defaultStream::error_stream(), "Specified interface %s (id %d) is not directly implemented\n",
+           specified_interface->external_name(), _interfaces->at(i));
      print_specified_interfaces();
      print_actual_interfaces(k);
+     throw exception .....      
-     error("Specified interface %s (id %d) is not directly implemented",
-           specified_interface->external_name(), _interfaces->at(i));
      }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overshadowing should be checked before attempting to load the class. Otherwise if the wrongly used super has a different classfile than the specified super we may get class loading errors (see JVMS 5.3.5.3 and 5.3.5.4), e.g. if the specified super wasn't final but we try to use a final class instead of it. I'll extend the related explanation comments in the code.

Does this always prevent an unregistered class to use any other unregistered class as its super type?

The only way I see for another class to be used instead is for the unregistered class being loaded to reference supertypes of unexpected names in its classfile. But this is an error and it will be detected as such in the C++ code you've cited. If you have any other cases in mind please share.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I didn't notice the overshadower == supertype check. Now I understand how it works.

However, I think the logic of the code isn't expressed in the same way as mentioned in the comments. That's why I misread it. I would suggest restructuring it like this for clarity.

Also, the opposite to "unregistered" is "built-in". We used to have a third type (classes for "registered" custom class loaders) but that has been removed, leaving us the odd terminology. Maybe we'll clean that up when more support is added to custom loaders in Leyden.

BTW, I think we should use "shadow" instead of "overshadow" for brevity.

void ClassListParser::check_supertype_overshadowing(int supertype_id, const InstanceKlass* specified_supertype, TRAPS) {
  if (!specified_supertype->is_shared_unregistered_class()) {
    const InstanceKlass* overshadower = SystemDictionaryShared::get_unregistered_class(specified_supertype->name());
    if (overshadower != nullptr) {
      // If an unregistered class U is specified to have a built-in supertype S1
      // named SN but an unregistered class S2 also named SN has already been loaded
      // S2 will be incorrectly resolved as the supertype of U instead of S1 due to
      // limitations in the loading mechanism of unregistered classes.
      // This happens very rarely and we don't support such use cases in the CDS archive.
      ResourceMark rm;
      THROW_MSG(vmSymbols::java_lang_UnsupportedOperationException(),
                err_msg("%s (id %d) has super-type %s (id %d) overshadowed by another class with the same name",
                        _class_name, _id, supertype->external_name(), supertype_id));
    }
  }
}

Copy link
Member

@calvinccheung calvinccheung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found an issue on the RegUnregSuperTest.java.

Comment on lines 71 to 77
out.shouldContain("app CustomLoadee3\n");
out.shouldContain("unreg CustomLoadee3\n");
if ("reg".equals(variant)) {
out.shouldNotContain("unreg CustomLoadee3Child\n");
out.shouldContain("CustomLoadee3Child (id 3) has super-type CustomLoadee3 (id 1) overshadowed by another class with the same name");
} else {
out.shouldContain("unreg CustomLoadee3Child\n");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove the linefeed ('\n') chars?
I'm seeing test failure when running with the -XX:+AOTClassLinking option.

java.lang.RuntimeException: 'app   CustomLoadee3
' missing from stdout/stderr
	at jdk.test.lib.process.OutputAnalyzer.shouldContain(OutputAnalyzer.java:253)
	at RegUnregSuperTest.main(RegUnregSuperTest.java:71)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at com.sun.javatest.regtest.agent.MainActionHelper$AgentVMRunnable.run(MainActionHelper.java:335)
	at java.base/java.lang.Thread.run(Thread.java:1447)

The expected output is there:
[1.686s][debug ][cds,class] klasses[ 1548] = 0x00000008004c7518 app CustomLoadee3 aot-linked

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand correctly that you modified the test's code to use -XX:+AOTClassLinking to get this error? Do you think there should be another test variant with -XX:+AOTClassLinking (it is easy to add)?

"\n" is used after "CustomLoadee3" because shouldContain("unreg CustomLoadee3") will accept either "unreg CustomLoadee3" or "unreg CustomLoadee3Child" and the second case is not what I want to detect.

Anyway, I've changed the test to pass when -XX:+AOTClassLinking is used.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I didn't modify the test code but ran your patch through our internal testing tiers. One of the tier specifies the -XX:+AOTClassLinking option. You can also run it via jtreg command line using the -javaoptions, e.g. -javaoptions:"-XX:+AOTClassLinking"

Copy link
Member

@iklam iklam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me overall. I just have a request for cleaning up ClassListParser::check_supertype_overshadowing()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

3 participants