Skip to content

Commit

Permalink
8173978: Lookup.in should allow teleporting from a lookup class in a …
Browse files Browse the repository at this point in the history
…named module without dropping all access

Reviewed-by: alanb, plevart
  • Loading branch information
Mandy Chung committed Jul 24, 2019
1 parent 221da20 commit 068575e
Show file tree
Hide file tree
Showing 26 changed files with 2,241 additions and 299 deletions.
14 changes: 11 additions & 3 deletions src/java.base/share/classes/java/lang/invoke/MemberName.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -521,7 +521,7 @@ public boolean isCallerSensitive() {
public boolean isAccessibleFrom(Class<?> lookupClass) {
int mode = (ALL_ACCESS|MethodHandles.Lookup.PACKAGE|MethodHandles.Lookup.MODULE);
return VerifyAccess.isMemberAccessible(this.getDeclaringClass(), this.getDeclaringClass(), flags,
lookupClass, mode);
lookupClass, null, mode);
}

/**
Expand Down Expand Up @@ -930,13 +930,21 @@ public IllegalAccessException makeAccessException(String message, Object from) {
message += ", from public Lookup";
} else {
Module m;
Class<?> plc;
if (from instanceof MethodHandles.Lookup) {
MethodHandles.Lookup lookup = (MethodHandles.Lookup)from;
from = lookup.lookupClass();
m = lookup.lookupClass().getModule();
plc = lookup.previousLookupClass();
} else {
m = from.getClass().getModule();
m = ((Class<?>)from).getModule();
plc = null;
}
message += ", from " + from + " (" + m + ")";
if (plc != null) {
message += ", previous lookup " +
plc.getName() + " (" + plc.getModule() + ")";
}
}
}
return new IllegalAccessException(message);
Expand Down
904 changes: 800 additions & 104 deletions src/java.base/share/classes/java/lang/invoke/MethodHandles.java

Large diffs are not rendered by default.

97 changes: 67 additions & 30 deletions src/java.base/share/classes/sun/invoke/util/VerifyAccess.java
Expand Up @@ -88,18 +88,20 @@ private VerifyAccess() { } // cannot instantiate
* @param defc the class in which the proposed member is actually defined
* @param mods modifier flags for the proposed member
* @param lookupClass the class for which the access check is being made
* @param prevLookupClass the class for which the access check is being made
* @param allowedModes allowed modes
* @return true iff the accessing class can access such a member
*/
public static boolean isMemberAccessible(Class<?> refc, // symbolic ref class
Class<?> defc, // actual def class
int mods, // actual member mods
Class<?> lookupClass,
Class<?> prevLookupClass,
int allowedModes) {
if (allowedModes == 0) return false;
assert((allowedModes & PUBLIC) != 0 &&
(allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
// The symbolic reference class (refc) must always be fully verified.
if (!isClassAccessible(refc, lookupClass, allowedModes)) {
if (!isClassAccessible(refc, lookupClass, prevLookupClass, allowedModes)) {
return false;
}
// Usually refc and defc are the same, but verify defc also in case they differ.
Expand All @@ -109,6 +111,7 @@ public static boolean isMemberAccessible(Class<?> refc, // symbolic ref class

switch (mods & ALL_ACCESS_MODES) {
case PUBLIC:
assert (allowedModes & PUBLIC) != 0 || (allowedModes & UNCONDITIONAL_ALLOWED) != 0;
return true; // already checked above
case PROTECTED:
assert !defc.isInterface(); // protected members aren't allowed in interfaces
Expand Down Expand Up @@ -175,14 +178,23 @@ static int getClassModifiers(Class<?> c) {
* package that is exported to the module that contains D.
* <li>C and D are members of the same runtime package.
* </ul>
*
* @param refc the symbolic reference class to which access is being checked (C)
* @param lookupClass the class performing the lookup (D)
* @param prevLookupClass the class from which the lookup was teleported or null
* @param allowedModes allowed modes
*/
public static boolean isClassAccessible(Class<?> refc, Class<?> lookupClass,
public static boolean isClassAccessible(Class<?> refc,
Class<?> lookupClass,
Class<?> prevLookupClass,
int allowedModes) {
if (allowedModes == 0) return false;
assert((allowedModes & PUBLIC) != 0 &&
(allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);

if ((allowedModes & PACKAGE_ALLOWED) != 0 &&
isSamePackage(lookupClass, refc))
return true;

int mods = getClassModifiers(refc);
if (isPublic(mods)) {

Expand All @@ -195,37 +207,62 @@ public static boolean isClassAccessible(Class<?> refc, Class<?> lookupClass,
return true;
}

// trivially allow
if ((allowedModes & MODULE_ALLOWED) != 0 &&
(lookupModule == refModule))
return true;
// allow access to public types in all unconditionally exported packages
if ((allowedModes & UNCONDITIONAL_ALLOWED) != 0) {
return refModule.isExported(refc.getPackageName());
}

// check readability when UNCONDITIONAL not allowed
if (((allowedModes & UNCONDITIONAL_ALLOWED) != 0)
|| lookupModule.canRead(refModule)) {

// check that refc is in an exported package
if ((allowedModes & MODULE_ALLOWED) != 0) {
if (refModule.isExported(refc.getPackageName(), lookupModule))
return true;
} else {
// exported unconditionally
if (refModule.isExported(refc.getPackageName()))
return true;
}

// not exported but allow access during VM initialization
// because java.base does not have its exports setup
if (!jdk.internal.misc.VM.isModuleSystemInited())
if (lookupModule == refModule && prevLookupClass == null) {
// allow access to all public types in lookupModule
if ((allowedModes & MODULE_ALLOWED) != 0)
return true;

assert (allowedModes & PUBLIC) != 0;
return refModule.isExported(refc.getPackageName());
}

// cross-module access
// 1. refc is in different module from lookupModule, or
// 2. refc is in lookupModule and a different module from prevLookupModule
Module prevLookupModule = prevLookupClass != null ? prevLookupClass.getModule()
: null;
assert refModule != lookupModule || refModule != prevLookupModule;
if (isModuleAccessible(refc, lookupModule, prevLookupModule))
return true;

// not exported but allow access during VM initialization
// because java.base does not have its exports setup
if (!jdk.internal.misc.VM.isModuleSystemInited())
return true;

// public class not accessible to lookupClass
return false;
}
if ((allowedModes & PACKAGE_ALLOWED) != 0 &&
isSamePackage(lookupClass, refc))
return true;

return false;
}

/*
* Tests if a class or interface REFC is accessible to m1 and m2 where m2
* may be null.
*
* A class or interface REFC in m is accessible to m1 and m2 if and only if
* both m1 and m2 read m and m exports the package of REFC at least to
* both m1 and m2.
*/
public static boolean isModuleAccessible(Class<?> refc, Module m1, Module m2) {
Module refModule = refc.getModule();
assert refModule != m1 || refModule != m2;
int mods = getClassModifiers(refc);
if (isPublic(mods)) {
if (m1.canRead(refModule) && (m2 == null || m2.canRead(refModule))) {
String pn = refc.getPackageName();

// refc is exported package to at least both m1 and m2
if (refModule.isExported(pn, m1) && (m2 == null || refModule.isExported(pn, m2)))
return true;
}
}
return false;
}

Expand Down

0 comments on commit 068575e

Please sign in to comment.