Skip to content
Permalink
Browse files

8235351: Lookup::unreflect should bind with the original caller indep…

…endent of Method's accessible flag

Reviewed-by: alanb
  • Loading branch information
Mandy Chung
Mandy Chung committed Dec 6, 2019
1 parent 41b4803 commit e7fa637179bfa685af2b67cddc9f7403d06aaec5
@@ -1789,7 +1789,7 @@ public String toString() {
*/
public MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
MemberName method = resolveOrFail(REF_invokeStatic, refc, name, type);
return getDirectMethod(REF_invokeStatic, refc, method, findBoundCallerClass(method));
return getDirectMethod(REF_invokeStatic, refc, method, findBoundCallerLookup(method));
}

/**
@@ -1881,7 +1881,7 @@ public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) thr
}
byte refKind = (refc.isInterface() ? REF_invokeInterface : REF_invokeVirtual);
MemberName method = resolveOrFail(refKind, refc, name, type);
return getDirectMethod(refKind, refc, method, findBoundCallerClass(method));
return getDirectMethod(refKind, refc, method, findBoundCallerLookup(method));
}
private MethodHandle findVirtualForMH(String name, MethodType type) {
// these names require special lookups because of the implicit MethodType argument
@@ -2133,7 +2133,7 @@ public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
checkSpecialCaller(specialCaller, refc);
Lookup specialLookup = this.in(specialCaller);
MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type);
return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, findBoundCallerClass(method));
return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, findBoundCallerLookup(method));
}

/**
@@ -2434,7 +2434,7 @@ public VarHandle findStaticVarHandle(Class<?> decl, String name, Class<?> type)
public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
Class<? extends Object> refc = receiver.getClass(); // may get NPE
MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type);
MethodHandle mh = getDirectMethodNoRestrictInvokeSpecial(refc, method, findBoundCallerClass(method));
MethodHandle mh = getDirectMethodNoRestrictInvokeSpecial(refc, method, findBoundCallerLookup(method));
if (!mh.type().leadingReferenceParameter().isAssignableFrom(receiver.getClass())) {
throw new IllegalAccessException("The restricted defining class " +
mh.type().leadingReferenceParameter().getName() +
@@ -2486,7 +2486,7 @@ public MethodHandle unreflect(Method m) throws IllegalAccessException {
assert(method.isMethod());
@SuppressWarnings("deprecation")
Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this;
return lookup.getDirectMethodNoSecurityManager(refKind, method.getDeclaringClass(), method, findBoundCallerClass(method));
return lookup.getDirectMethodNoSecurityManager(refKind, method.getDeclaringClass(), method, findBoundCallerLookup(method));
}
private MethodHandle unreflectForMH(Method m) {
// these names require special lookups because they throw UnsupportedOperationException
@@ -2537,7 +2537,7 @@ public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws Il
MemberName method = new MemberName(m, true);
assert(method.isMethod());
// ignore m.isAccessible: this is a new kind of access
return specialLookup.getDirectMethodNoSecurityManager(REF_invokeSpecial, method.getDeclaringClass(), method, findBoundCallerClass(method));
return specialLookup.getDirectMethodNoSecurityManager(REF_invokeSpecial, method.getDeclaringClass(), method, findBoundCallerLookup(method));
}

/**
@@ -2828,17 +2828,12 @@ void checkMethodName(byte refKind, String name) throws NoSuchMethodException {
* If this lookup object has full privilege access, then the caller class is the lookupClass.
* Otherwise, if m is caller-sensitive, throw IllegalAccessException.
*/
Class<?> findBoundCallerClass(MemberName m) throws IllegalAccessException {
Class<?> callerClass = null;
if (MethodHandleNatives.isCallerSensitive(m)) {
Lookup findBoundCallerLookup(MemberName m) throws IllegalAccessException {
if (MethodHandleNatives.isCallerSensitive(m) && !hasFullPrivilegeAccess()) {
// Only lookups with full privilege access are allowed to resolve caller-sensitive methods
if (hasFullPrivilegeAccess()) {
callerClass = lookupClass;
} else {
throw new IllegalAccessException("Attempt to lookup caller-sensitive method using restricted lookup object");
}
throw new IllegalAccessException("Attempt to lookup caller-sensitive method using restricted lookup object");
}
return callerClass;
return this;
}

/**
@@ -3042,28 +3037,28 @@ private MethodHandle restrictReceiver(MemberName method, DirectMethodHandle mh,
}

/** Check access and get the requested method. */
private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method, Class<?> boundCallerClass) throws IllegalAccessException {
private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method, Lookup callerLookup) throws IllegalAccessException {
final boolean doRestrict = true;
final boolean checkSecurity = true;
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, boundCallerClass);
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerLookup);
}
/** Check access and get the requested method, for invokespecial with no restriction on the application of narrowing rules. */
private MethodHandle getDirectMethodNoRestrictInvokeSpecial(Class<?> refc, MemberName method, Class<?> boundCallerClass) throws IllegalAccessException {
private MethodHandle getDirectMethodNoRestrictInvokeSpecial(Class<?> refc, MemberName method, Lookup callerLookup) throws IllegalAccessException {
final boolean doRestrict = false;
final boolean checkSecurity = true;
return getDirectMethodCommon(REF_invokeSpecial, refc, method, checkSecurity, doRestrict, boundCallerClass);
return getDirectMethodCommon(REF_invokeSpecial, refc, method, checkSecurity, doRestrict, callerLookup);
}
/** Check access and get the requested method, eliding security manager checks. */
private MethodHandle getDirectMethodNoSecurityManager(byte refKind, Class<?> refc, MemberName method, Class<?> boundCallerClass) throws IllegalAccessException {
private MethodHandle getDirectMethodNoSecurityManager(byte refKind, Class<?> refc, MemberName method, Lookup callerLookup) throws IllegalAccessException {
final boolean doRestrict = true;
final boolean checkSecurity = false; // not needed for reflection or for linking CONSTANT_MH constants
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, boundCallerClass);
return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerLookup);
}
/** Common code for all methods; do not call directly except from immediately above. */
private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
boolean checkSecurity,
boolean doRestrict, Class<?> boundCallerClass) throws IllegalAccessException {

boolean doRestrict,
Lookup boundCaller) throws IllegalAccessException {
checkMethod(refKind, refc, method);
// Optionally check with the security manager; this isn't needed for unreflect* calls.
if (checkSecurity)
@@ -3101,30 +3096,32 @@ private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberNa
// redo basic checks
checkMethod(refKind, refc, method);
}

DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method, lookupClass());
MethodHandle mh = dmh;
// Optionally narrow the receiver argument to lookupClass using restrictReceiver.
if ((doRestrict && refKind == REF_invokeSpecial) ||
(MethodHandleNatives.refKindHasReceiver(refKind) && restrictProtectedReceiver(method))) {
mh = restrictReceiver(method, dmh, lookupClass());
}
mh = maybeBindCaller(method, mh, boundCallerClass);
mh = maybeBindCaller(method, mh, boundCaller);
mh = mh.setVarargs(method);
return mh;
}
private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
Class<?> boundCallerClass)
private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh, Lookup boundCaller)
throws IllegalAccessException {
if (allowedModes == TRUSTED || !MethodHandleNatives.isCallerSensitive(method))
if (boundCaller.allowedModes == TRUSTED || !MethodHandleNatives.isCallerSensitive(method))
return mh;
Class<?> hostClass = lookupClass;
if (!hasFullPrivilegeAccess()) // caller must have full power access
hostClass = boundCallerClass; // boundCallerClass came from a security manager style stack walk
MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass);

// boundCaller must have full privilege access.
// It should have been checked by findBoundCallerLookup. Safe to check this again.
if (!boundCaller.hasFullPrivilegeAccess())
throw new IllegalAccessException("Attempt to lookup caller-sensitive method using restricted lookup object");

MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, boundCaller.lookupClass);
// Note: caller will apply varargs after this step happens.
return cbmh;
}

/** Check access and get the requested field. */
private MethodHandle getDirectField(byte refKind, Class<?> refc, MemberName field) throws IllegalAccessException {
final boolean checkSecurity = true;
@@ -3296,7 +3293,7 @@ private MethodHandle getDirectMethodForConstant(byte refKind, Class<?> defc, Mem
if (MethodHandleNatives.refKindIsField(refKind)) {
return getDirectFieldNoSecurityManager(refKind, defc, member);
} else if (MethodHandleNatives.refKindIsMethod(refKind)) {
return getDirectMethodNoSecurityManager(refKind, defc, member, lookupClass);
return getDirectMethodNoSecurityManager(refKind, defc, member, findBoundCallerLookup(member));
} else if (refKind == REF_newInvokeSpecial) {
return getDirectConstructorNoSecurityManager(defc, member);
}
@@ -22,7 +22,7 @@
*/

/* @test
* @bug 8196830
* @bug 8196830 8235351
* @modules java.base/jdk.internal.reflect
* @run testng/othervm --illegal-access=deny CallerSensitiveAccess
* @summary Check Lookup findVirtual, findStatic and unreflect behavior with
@@ -95,6 +95,27 @@ public void testPublicLookupUnreflect(@NoInjection Method method, String desc) t
MethodHandles.publicLookup().unreflect(method);
}

/**
* public accessible caller sensitive methods in APIs exported by java.base.
*/
@DataProvider(name = "accessibleCallerSensitiveMethods")
static Object[][] accessibleCallerSensitiveMethods() {
return callerSensitiveMethods(Object.class.getModule())
.filter(m -> Modifier.isPublic(m.getModifiers()))
.map(m -> { m.setAccessible(true); return m; })
.map(m -> new Object[] { m, shortDescription(m) })
.toArray(Object[][]::new);
}

/**
* Using publicLookup, attempt to use unreflect to obtain a method handle to a
* caller sensitive method.
*/
@Test(dataProvider = "accessibleCallerSensitiveMethods",
expectedExceptions = IllegalAccessException.class)
public void testLookupUnreflect(@NoInjection Method method, String desc) throws Exception {
MethodHandles.publicLookup().unreflect(method);
}

// -- Test method handles to setAccessible --

0 comments on commit e7fa637

Please sign in to comment.
You can’t perform that action at this time.