Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/java/lang/invoke/AccessControlTest.java
47209 views
/*1* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/* @test24* @summary test access checking by java.lang.invoke.MethodHandles.Lookup25* @compile AccessControlTest.java AccessControlTest_subpkg/Acquaintance_remote.java26* @run testng/othervm test.java.lang.invoke.AccessControlTest27*/2829package test.java.lang.invoke;3031import java.lang.invoke.*;32import java.lang.reflect.*;33import java.util.*;34import org.testng.*;35import org.testng.annotations.*;3637import static java.lang.invoke.MethodHandles.*;38import static java.lang.invoke.MethodHandles.Lookup.*;39import static java.lang.invoke.MethodType.*;40import static org.testng.Assert.*;4142import test.java.lang.invoke.AccessControlTest_subpkg.Acquaintance_remote;434445/**46* Test many combinations of Lookup access and cross-class lookupStatic.47* @author jrose48*/49public class AccessControlTest {50static final Class<?> THIS_CLASS = AccessControlTest.class;51// How much output?52static int verbosity = 0;53static {54String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbosity");55if (vstr == null)56vstr = System.getProperty(THIS_CLASS.getName()+".verbosity");57if (vstr != null) verbosity = Integer.parseInt(vstr);58}5960private class LookupCase implements Comparable<LookupCase> {61final Lookup lookup;62final Class<?> lookupClass;63final int lookupModes;64public LookupCase(Lookup lookup) {65this.lookup = lookup;66this.lookupClass = lookup.lookupClass();67this.lookupModes = lookup.lookupModes();68assert(lookupString().equals(lookup.toString()));69numberOf(lookupClass().getClassLoader()); // assign CL#70}71public LookupCase(Class<?> lookupClass, int lookupModes) {72this.lookup = null;73this.lookupClass = lookupClass;74this.lookupModes = lookupModes;75numberOf(lookupClass().getClassLoader()); // assign CL#76}7778public final Class<?> lookupClass() { return lookupClass; }79public final int lookupModes() { return lookupModes; }8081public Lookup lookup() { lookup.getClass(); return lookup; }8283@Override84public int compareTo(LookupCase that) {85Class<?> c1 = this.lookupClass();86Class<?> c2 = that.lookupClass();87if (c1 != c2) {88int cmp = c1.getName().compareTo(c2.getName());89if (cmp != 0) return cmp;90cmp = numberOf(c1.getClassLoader()) - numberOf(c2.getClassLoader());91assert(cmp != 0);92return cmp;93}94return -(this.lookupModes() - that.lookupModes());95}9697@Override98public boolean equals(Object that) {99return (that instanceof LookupCase && equals((LookupCase)that));100}101public boolean equals(LookupCase that) {102return (this.lookupClass() == that.lookupClass() &&103this.lookupModes() == that.lookupModes());104}105106@Override107public int hashCode() {108return lookupClass().hashCode() + (lookupModes() * 31);109}110111/** Simulate all assertions in the spec. for Lookup.toString. */112private String lookupString() {113String name = lookupClass.getName();114String suffix = "";115if (lookupModes == 0)116suffix = "/noaccess";117else if (lookupModes == PUBLIC)118suffix = "/public";119else if (lookupModes == (PUBLIC|PACKAGE))120suffix = "/package";121else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE))122suffix = "/private";123else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE|PROTECTED))124suffix = "";125else126suffix = "/#"+Integer.toHexString(lookupModes);127return name+suffix;128}129130/** Simulate all assertions from the spec. for Lookup.in:131* <hr>132* Creates a lookup on the specified new lookup class.133* [A1] The resulting object will report the specified134* class as its own {@link #lookupClass lookupClass}.135* <p>136* [A2] However, the resulting {@code Lookup} object is guaranteed137* to have no more access capabilities than the original.138* In particular, access capabilities can be lost as follows:<ul>139* <li>[A3] If the new lookup class differs from the old one,140* protected members will not be accessible by virtue of inheritance.141* (Protected members may continue to be accessible because of package sharing.)142* <li>[A4] If the new lookup class is in a different package143* than the old one, protected and default (package) members will not be accessible.144* <li>[A5] If the new lookup class is not within the same package member145* as the old one, private members will not be accessible.146* <li>[A6] If the new lookup class is not accessible to the old lookup class,147* using the original access modes,148* then no members, not even public members, will be accessible.149* [A7] (In all other cases, public members will continue to be accessible.)150* </ul>151* Other than the above cases, the new lookup will have the same152* access capabilities as the original. [A8]153* <hr>154*/155public LookupCase in(Class<?> c2) {156Class<?> c1 = lookupClass();157int m1 = lookupModes();158int changed = 0;159boolean samePackage = (c1.getClassLoader() == c2.getClassLoader() &&160packagePrefix(c1).equals(packagePrefix(c2)));161boolean sameTopLevel = (topLevelClass(c1) == topLevelClass(c2));162boolean sameClass = (c1 == c2);163assert(samePackage || !sameTopLevel);164assert(sameTopLevel || !sameClass);165boolean accessible = sameClass; // [A6]166if ((m1 & PACKAGE) != 0) accessible |= samePackage;167if ((m1 & PUBLIC ) != 0) accessible |= (c2.getModifiers() & PUBLIC) != 0;168if (!accessible) {169// Different package and no access to c2; lose all access.170changed |= (PUBLIC|PACKAGE|PRIVATE|PROTECTED); // [A6]171}172if (!samePackage) {173// Different package; lose PACKAGE and lower access.174changed |= (PACKAGE|PRIVATE|PROTECTED); // [A4]175}176if (!sameTopLevel) {177// Different top-level class. Lose PRIVATE and lower access.178changed |= (PRIVATE|PROTECTED); // [A5]179}180if (!sameClass) {181changed |= (PROTECTED); // [A3]182} else {183assert(changed == 0); // [A8] (no deprivation if same class)184}185if (accessible) assert((changed & PUBLIC) == 0); // [A7]186int m2 = m1 & ~changed;187LookupCase l2 = new LookupCase(c2, m2);188assert(l2.lookupClass() == c2); // [A1]189assert((m1 | m2) == m1); // [A2] (no elevation of access)190return l2;191}192193@Override194public String toString() {195String s = lookupClass().getSimpleName();196String lstr = lookupString();197int sl = lstr.indexOf('/');198if (sl >= 0) s += lstr.substring(sl);199ClassLoader cld = lookupClass().getClassLoader();200if (cld != THIS_LOADER) s += "/loader#"+numberOf(cld);201return s;202}203204/** Predict the success or failure of accessing this method. */205public boolean willAccess(Method m) {206Class<?> c1 = lookupClass();207Class<?> c2 = m.getDeclaringClass();208LookupCase lc = this.in(c2);209int m1 = lc.lookupModes();210int m2 = fixMods(m.getModifiers());211// privacy is strictly enforced on lookups212if (c1 != c2) m1 &= ~PRIVATE;213// protected access is sometimes allowed214if ((m2 & PROTECTED) != 0) {215int prev = m2;216m2 |= PACKAGE; // it acts like a package method also217if ((lookupModes() & PROTECTED) != 0 &&218c2.isAssignableFrom(c1))219m2 |= PUBLIC; // from a subclass, it acts like a public method also220}221if (verbosity >= 2)222System.out.println(this+" willAccess "+lc+" m1="+m1+" m2="+m2+" => "+((m2 & m1) != 0));223return (m2 & m1) != 0;224}225}226227private static Class<?> topLevelClass(Class<?> cls) {228Class<?> c = cls;229for (Class<?> ec; (ec = c.getEnclosingClass()) != null; )230c = ec;231assert(c.getEnclosingClass() == null);232assert(c == cls || cls.getEnclosingClass() != null);233return c;234}235236private static String packagePrefix(Class<?> c) {237while (c.isArray()) c = c.getComponentType();238String s = c.getName();239assert(s.indexOf('/') < 0);240return s.substring(0, s.lastIndexOf('.')+1);241}242243244private final TreeSet<LookupCase> CASES = new TreeSet<>();245private final TreeMap<LookupCase,TreeSet<LookupCase>> CASE_EDGES = new TreeMap<>();246private final ArrayList<ClassLoader> LOADERS = new ArrayList<>();247private final ClassLoader THIS_LOADER = this.getClass().getClassLoader();248{ if (THIS_LOADER != null) LOADERS.add(THIS_LOADER); } // #1249250private LookupCase lookupCase(String name) {251for (LookupCase lc : CASES) {252if (lc.toString().equals(name))253return lc;254}255throw new AssertionError(name);256}257258private int numberOf(ClassLoader cl) {259if (cl == null) return 0;260int i = LOADERS.indexOf(cl);261if (i < 0) {262i = LOADERS.size();263LOADERS.add(cl);264}265return i+1;266}267268private void addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2) {269TreeSet<LookupCase> edges = CASE_EDGES.get(l2);270if (edges == null) CASE_EDGES.put(l2, edges = new TreeSet<>());271if (edges.add(l1)) {272Class<?> c1 = l1.lookupClass();273assert(l2.lookupClass() == c2); // [A1]274int m1 = l1.lookupModes();275int m2 = l2.lookupModes();276assert((m1 | m2) == m1); // [A2] (no elevation of access)277LookupCase expect = l1.in(c2);278if (!expect.equals(l2))279System.out.println("*** expect "+l1+" => "+expect+" but got "+l2);280assertEquals(expect, l2);281}282}283284private void makeCases(Lookup[] originalLookups) {285// make initial set of lookup test cases286CASES.clear(); LOADERS.clear(); CASE_EDGES.clear();287ArrayList<Class<?>> classes = new ArrayList<>();288for (Lookup l : originalLookups) {289CASES.add(new LookupCase(l));290classes.remove(l.lookupClass()); // no dups please291classes.add(l.lookupClass());292}293System.out.println("loaders = "+LOADERS);294int rounds = 0;295for (int lastCount = -1; lastCount != CASES.size(); ) {296lastCount = CASES.size(); // if CASES grow in the loop we go round again297for (LookupCase lc1 : CASES.toArray(new LookupCase[0])) {298for (Class<?> c2 : classes) {299LookupCase lc2 = new LookupCase(lc1.lookup().in(c2));300addLookupEdge(lc1, c2, lc2);301CASES.add(lc2);302}303}304rounds++;305}306System.out.println("filled in "+CASES.size()+" cases from "+originalLookups.length+" original cases in "+rounds+" rounds");307if (false) {308System.out.println("CASES: {");309for (LookupCase lc : CASES) {310System.out.println(lc);311Set<LookupCase> edges = CASE_EDGES.get(lc);312if (edges != null)313for (LookupCase prev : edges) {314System.out.println("\t"+prev);315}316}317System.out.println("}");318}319}320321@Test public void test() {322makeCases(lookups());323if (verbosity > 0) {324verbosity += 9;325Method pro_in_self = targetMethod(THIS_CLASS, PROTECTED, methodType(void.class));326testOneAccess(lookupCase("AccessControlTest/public"), pro_in_self, "find");327testOneAccess(lookupCase("Remote_subclass/public"), pro_in_self, "find");328testOneAccess(lookupCase("Remote_subclass"), pro_in_self, "find");329verbosity -= 9;330}331Set<Class<?>> targetClassesDone = new HashSet<>();332for (LookupCase targetCase : CASES) {333Class<?> targetClass = targetCase.lookupClass();334if (!targetClassesDone.add(targetClass)) continue; // already saw this one335String targetPlace = placeName(targetClass);336if (targetPlace == null) continue; // Object, String, not a target337for (int targetAccess : ACCESS_CASES) {338MethodType methodType = methodType(void.class);339Method method = targetMethod(targetClass, targetAccess, methodType);340// Try to access target method from various contexts.341for (LookupCase sourceCase : CASES) {342testOneAccess(sourceCase, method, "find");343testOneAccess(sourceCase, method, "unreflect");344}345}346}347System.out.println("tested "+testCount+" access scenarios; "+testCountFails+" accesses were denied");348}349350private int testCount, testCountFails;351352private void testOneAccess(LookupCase sourceCase, Method method, String kind) {353Class<?> targetClass = method.getDeclaringClass();354String methodName = method.getName();355MethodType methodType = methodType(method.getReturnType(), method.getParameterTypes());356boolean willAccess = sourceCase.willAccess(method);357boolean didAccess = false;358ReflectiveOperationException accessError = null;359try {360switch (kind) {361case "find":362if ((method.getModifiers() & Modifier.STATIC) != 0)363sourceCase.lookup().findStatic(targetClass, methodName, methodType);364else365sourceCase.lookup().findVirtual(targetClass, methodName, methodType);366break;367case "unreflect":368sourceCase.lookup().unreflect(method);369break;370default:371throw new AssertionError(kind);372}373didAccess = true;374} catch (ReflectiveOperationException ex) {375accessError = ex;376}377if (willAccess != didAccess) {378System.out.println(sourceCase+" => "+targetClass.getSimpleName()+"."+methodName+methodType);379System.out.println("fail on "+method+" ex="+accessError);380assertEquals(willAccess, didAccess);381}382testCount++;383if (!didAccess) testCountFails++;384}385386static Method targetMethod(Class<?> targetClass, int targetAccess, MethodType methodType) {387String methodName = accessName(targetAccess)+placeName(targetClass);388if (verbosity >= 2)389System.out.println(targetClass.getSimpleName()+"."+methodName+methodType);390try {391Method method = targetClass.getDeclaredMethod(methodName, methodType.parameterArray());392assertEquals(method.getReturnType(), methodType.returnType());393int haveMods = method.getModifiers();394assert(Modifier.isStatic(haveMods));395assert(targetAccess == fixMods(haveMods));396return method;397} catch (NoSuchMethodException ex) {398throw new AssertionError(methodName, ex);399}400}401402static String placeName(Class<?> cls) {403// return "self", "sibling", "nestmate", etc.404if (cls == AccessControlTest.class) return "self";405String cln = cls.getSimpleName();406int under = cln.lastIndexOf('_');407if (under < 0) return null;408return cln.substring(under+1);409}410static String accessName(int acc) {411switch (acc) {412case PUBLIC: return "pub_in_";413case PROTECTED: return "pro_in_";414case PACKAGE: return "pkg_in_";415case PRIVATE: return "pri_in_";416}417assert(false);418return "?";419}420private static final int[] ACCESS_CASES = {421PUBLIC, PACKAGE, PRIVATE, PROTECTED422};423/** Return one of the ACCESS_CASES. */424static int fixMods(int mods) {425mods &= (PUBLIC|PRIVATE|PROTECTED);426switch (mods) {427case PUBLIC: case PRIVATE: case PROTECTED: return mods;428case 0: return PACKAGE;429}430throw new AssertionError(mods);431}432433static Lookup[] lookups() {434ArrayList<Lookup> tem = new ArrayList<>();435Collections.addAll(tem,436AccessControlTest.lookup_in_self(),437Inner_nestmate.lookup_in_nestmate(),438AccessControlTest_sibling.lookup_in_sibling());439if (true) {440Collections.addAll(tem,Acquaintance_remote.lookups());441} else {442try {443Class<?> remc = Class.forName("test.java.lang.invoke.AccessControlTest_subpkg.Acquaintance_remote");444Lookup[] remls = (Lookup[]) remc.getMethod("lookups").invoke(null);445Collections.addAll(tem, remls);446} catch (ReflectiveOperationException ex) {447throw new LinkageError("reflection failed", ex);448}449}450tem.add(publicLookup());451tem.add(publicLookup().in(String.class));452tem.add(publicLookup().in(List.class));453return tem.toArray(new Lookup[0]);454}455456static Lookup lookup_in_self() {457return MethodHandles.lookup();458}459static public void pub_in_self() { }460static protected void pro_in_self() { }461static /*package*/ void pkg_in_self() { }462static private void pri_in_self() { }463464static class Inner_nestmate {465static Lookup lookup_in_nestmate() {466return MethodHandles.lookup();467}468static public void pub_in_nestmate() { }469static protected void pro_in_nestmate() { }470static /*package*/ void pkg_in_nestmate() { }471static private void pri_in_nestmate() { }472}473}474class AccessControlTest_sibling {475static Lookup lookup_in_sibling() {476return MethodHandles.lookup();477}478static public void pub_in_sibling() { }479static protected void pro_in_sibling() { }480static /*package*/ void pkg_in_sibling() { }481static private void pri_in_sibling() { }482}483484// This guy tests access from outside the package:485/*486package test.java.lang.invoke.AccessControlTest_subpkg;487public class Acquaintance_remote {488public static Lookup[] lookups() { ...489}490...491}492*/493494495