-
Notifications
You must be signed in to change notification settings - Fork 6.2k
8013527: calling MethodHandles.lookup on itself leads to errors #2367
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
Conversation
|
👋 Welcome back mchung! A progress list of the required criteria for merging this PR into |
|
/label remove hotspot-compiler |
|
@mlchung |
|
/contributor add Johannes Kuhn info@j-kuhn.de |
|
@mlchung |
Webrevs
|
|
/issue add JDK-8257874 |
|
/issue add JDK-8013527 |
|
@mlchung |
|
@mlchung This issue is referenced in the PR title - it will now be updated. |
|
/label remove hotspot-compiler |
|
@mlchung The |
DasBrain
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Mandy.
Looks good, except the possibility for an attacker to teleport within the same nest.
| } | ||
| Class<?> invokerClass = new Lookup(targetClass) | ||
| .makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE, Set.of(NESTMATE)) | ||
| .defineClass(true, targetClass); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the target class directly could lead to some unintended problems.
An attacker can define it's own hidden class as nestmate and with a name ending in $$InjectedInvoker.
This allows the attacker to "teleport" into a nestmate with full privileges.
An attacker could then access MethodHandles.classData.
Suggested remedy: Create a holder that is only visible to java.lang.invoke:
/* package-private */ static class OriginalCallerHolder {
final Class<?> originalCaller;
OriginalCallerHolder(Class<?> originalCaller) {
this.originalCaller = originalCaller;
}
}As this type is only visible inside java.lang.invoke, it can't be created without hacking into java.lang.invoke, at which point all bets are off anyway.
(A previous commit was even more dangerous, as you can force jlr.Proxy to inject a class into your package with a null-PD)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only Lookup with the original access can access MethodHandles.classData. A hidden class HC$$InjectedInvoker/0x1234 can access private members of another class C in the same nest but not C's class data.
I don't follow which previous commit you refer to more dangerous. Please elaborate. I don't see any security concern with class data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Last night, I had a second thought that the fix for these two JBS issues does not need the class data. It's more for a future use. I plan to revise it and drop class data in this fix anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, got it confused with the future use.
With this fix, MethodHandle -> Method.invoke -> MethodHandles.lookup() will still return a lookup on the injected invoker.
I somehow missed that this is not part of the fix, but for the future use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MethodHandle -> Method.invoke -> MethodHandles.lookup() is a corner case that can be fixed easily using the class data approach. See the new commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The security issue I mentioned was in an other branch, method-invoke.
I used commit mlchung@4a3c914 (i.e. before strengthening the injected invoker checks) to test the my exploit. (Yes, full sandbox escape.)
I hope the same is not possible with the nestmate requirement.
PS.: Hidden Class -> MethodHandle -> Method.invoke -> MethodHandles might break due to mangling of the hidden class name for the injected invoker. Will write a test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, my branch method-invoke is a prototype and work-in-progress. I won't dig too much to it.
Do you reproduce the issue with this patch? The fix will ensure it's the invoker class injected by BindCaller.
DasBrain
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good.
|
@mlchung This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
|
@mlchung This pull request has been inactive for more than 8 weeks and will now be automatically closed. If you would like to continue working on this pull request in the future, feel free to reopen it! This can be done using the |
JDK-8013527: calling MethodHandles.lookup on itself leads to errors
JDK-8257874: MethodHandle injected invoker doesn't have necessary private access
Johannes Kuhn is also a contributor to this patch.
A caller-sensitive method can behave differently depending on the identity
of its immediate caller. If a method handle for a caller-sensitive method is
requested, this resulting method handle behaves as if it were called from an
instruction contained in the lookup class. The current implementation injects
a trampoline class (aka the invoker class) which is the caller class invoking
such caller-sensitive method handle. It works in all CSMs except
MethodHandles::lookupbecause the caller-sensitive behavior depends on the module of the caller class,
the class loader of the caller class, the accessibility of the caller class, or
the protection domain of the caller class. The invoker class is a hidden class
defined in the same runtime package with the same protection domain as the
lookup class, which is why the current implementation works for all CSMs except
MethodHandles::lookupwhich uses the caller class as the lookup class.Two issues with current implementation:
The invoker class only has the package access as the lookup class. It cannot
access private members of the lookup class and its nest members.
The fix is to make the invoker class as a nestmate of the lookup class.
MethodHandles::lookupif invoked via a method handle produces aLookupobject of an injected invoker class which is a bug.
There are two alternatives:
MethodHandles::lookupwill get the caller class from the class data ifit's the injected invoker
trailing caller class parameter is present, use the alternate implementation
and bind the method handle with the lookup class as the caller class argument.
There has been several discussions on the improvement to support caller sensitive
methods for example the calling sequences and security implication. I have
looked at how each CSM uses the caller class. The second approach (i.e.
defining an alternate implementation for a caller-sensitive method taking
an additional caller class parameter) does not work for non-static non-final
caller-sensitive method. In addition, it is not ideal to pollute the source
code to provide an alternatve implementation for all 120+ caller-sensitive methods
whereas the injected invoker works for all except
MethodHandles::lookup.I propose to use both approaches. We can add an alternative implementation for
a caller-sensitive method when desirable such as
MethodHandles::lookupinthis PR. For the injected invoker case, one could extract the original lookup
class from class data if needed.
test/jdk/jdk/internal/reflect/CallerSensitive/CheckCSM.java ensures that
no new non-static non-final caller-sensitive method will be added to the JDK.
I extend this test to catch that non-static non-final caller-sensitive method
cannot have the alternate implementation taking the additional caller class
parameter.
This fix for JDK-8013527 is needed by the prototype for JDK-6824466 I'm working on.
[1] https://mail.openjdk.java.net/pipermail/core-libs-dev/2021-January/073184.html
Progress
Issues
Reviewers
Contributors
<info@j-kuhn.de>Download
$ git fetch https://git.openjdk.java.net/jdk pull/2367/head:pull/2367$ git checkout pull/2367