Path: blob/master/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java
41159 views
/*1* Copyright (c) 2008, 2021, 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. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.invoke.util;2627import java.lang.reflect.Modifier;28import static java.lang.reflect.Modifier.*;29import jdk.internal.reflect.Reflection;3031/**32* This class centralizes information about the JVM's linkage access control.33* @author jrose34*/35public class VerifyAccess {3637private VerifyAccess() { } // cannot instantiate3839private static final int UNCONDITIONAL_ALLOWED = java.lang.invoke.MethodHandles.Lookup.UNCONDITIONAL;40private static final int ORIGINAL_ALLOWED = java.lang.invoke.MethodHandles.Lookup.ORIGINAL;41private static final int MODULE_ALLOWED = java.lang.invoke.MethodHandles.Lookup.MODULE;42private static final int PACKAGE_ONLY = 0;43private static final int PACKAGE_ALLOWED = java.lang.invoke.MethodHandles.Lookup.PACKAGE;44private static final int PROTECTED_OR_PACKAGE_ALLOWED = (PACKAGE_ALLOWED|PROTECTED);45private static final int ALL_ACCESS_MODES = (PUBLIC|PRIVATE|PROTECTED|PACKAGE_ONLY);4647/**48* Evaluate the JVM linkage rules for access to the given method49* on behalf of a caller class which proposes to perform the access.50* Return true if the caller class has privileges to invoke a method51* or access a field with the given properties.52* This requires an accessibility check of the referencing class,53* plus an accessibility check of the member within the class,54* which depends on the member's modifier flags.55* <p>56* The relevant properties include the defining class ({@code defc})57* of the member, and its modifier flags ({@code mods}).58* Also relevant is the class used to make the initial symbolic reference59* to the member ({@code refc}). If this latter class is not distinguished,60* the defining class should be passed for both arguments ({@code defc == refc}).61* <h3>JVM Specification, 5.4.4 "Access Control"</h3>62* A field or method R is accessible to a class or interface D if63* and only if any of the following is true:64* <ul>65* <li>R is public.</li>66* <li>R is protected and is declared in a class C, and D is either67* a subclass of C or C itself. Furthermore, if R is not static,68* then the symbolic reference to R must contain a symbolic69* reference to a class T, such that T is either a subclass of D,70* a superclass of D, or D itself.71* <p>During verification, it was also required that, even if T is72* a superclass of D, the target reference of a protected instance73* field access or method invocation must be an instance of D or a74* subclass of D (4.10.1.8).</p></li>75* <li>R is either protected or has default access (that is, neither76* public nor protected nor private), and is declared by a class77* in the same run-time package as D.</li>78* <li>R is private and is declared in D by a class or interface79* belonging to the same nest as D.</li>80* </ul>81* If a referenced field or method is not accessible, access checking82* throws an IllegalAccessError. If an exception is thrown while83* attempting to determine the nest host of a class or interface,84* access checking fails for the same reason.85*86* @param refc the class used in the symbolic reference to the proposed member87* @param defc the class in which the proposed member is actually defined88* @param mods modifier flags for the proposed member89* @param lookupClass the class for which the access check is being made90* @param prevLookupClass the class for which the access check is being made91* @param allowedModes allowed modes92* @return true iff the accessing class can access such a member93*/94public static boolean isMemberAccessible(Class<?> refc, // symbolic ref class95Class<?> defc, // actual def class96int mods, // actual member mods97Class<?> lookupClass,98Class<?> prevLookupClass,99int allowedModes) {100if (allowedModes == 0) return false;101assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED|ORIGINAL_ALLOWED)) == 0);102// The symbolic reference class (refc) must always be fully verified.103if (!isClassAccessible(refc, lookupClass, prevLookupClass, allowedModes)) {104return false;105}106// Usually refc and defc are the same, but verify defc also in case they differ.107if (defc == lookupClass &&108(allowedModes & PRIVATE) != 0)109return true; // easy check; all self-access is OK with a private lookup110111switch (mods & ALL_ACCESS_MODES) {112case PUBLIC:113assert (allowedModes & PUBLIC) != 0 || (allowedModes & UNCONDITIONAL_ALLOWED) != 0;114return true; // already checked above115case PROTECTED:116assert !defc.isInterface(); // protected members aren't allowed in interfaces117if ((allowedModes & PROTECTED_OR_PACKAGE_ALLOWED) != 0 &&118isSamePackage(defc, lookupClass))119return true;120if ((allowedModes & PROTECTED) == 0)121return false;122// Protected members are accessible by subclasses, which does not include interfaces.123// Interfaces are types, not classes. They should not have access to124// protected members in j.l.Object, even though it is their superclass.125if ((mods & STATIC) != 0 &&126!isRelatedClass(refc, lookupClass))127return false;128if ((allowedModes & PROTECTED) != 0 &&129isSubClass(lookupClass, defc))130return true;131return false;132case PACKAGE_ONLY: // That is, zero. Unmarked member is package-only access.133assert !defc.isInterface(); // package-private members aren't allowed in interfaces134return ((allowedModes & PACKAGE_ALLOWED) != 0 &&135isSamePackage(defc, lookupClass));136case PRIVATE:137// Rules for privates follows access rules for nestmates.138boolean canAccess = ((allowedModes & PRIVATE) != 0 &&139Reflection.areNestMates(defc, lookupClass));140// for private methods the selected method equals the141// resolved method - so refc == defc142assert (canAccess && refc == defc) || !canAccess;143return canAccess;144default:145throw new IllegalArgumentException("bad modifiers: "+Modifier.toString(mods));146}147}148149static boolean isRelatedClass(Class<?> refc, Class<?> lookupClass) {150return (refc == lookupClass ||151isSubClass(refc, lookupClass) ||152isSubClass(lookupClass, refc));153}154155static boolean isSubClass(Class<?> lookupClass, Class<?> defc) {156return defc.isAssignableFrom(lookupClass) &&157!lookupClass.isInterface(); // interfaces are types, not classes.158}159160static int getClassModifiers(Class<?> c) {161// This would return the mask stored by javac for the source-level modifiers.162// return c.getModifiers();163// But what we need for JVM access checks are the actual bits from the class header.164// ...But arrays and primitives are synthesized with their own odd flags:165if (c.isArray() || c.isPrimitive())166return c.getModifiers();167return Reflection.getClassAccessFlags(c);168}169170/**171* Evaluate the JVM linkage rules for access to the given class on behalf of caller.172* <h3>JVM Specification, 5.4.4 "Access Control"</h3>173* A class or interface C is accessible to a class or interface D174* if and only if any of the following conditions are true:<ul>175* <li>C is public and in the same module as D.176* <li>D is in a module that reads the module containing C, C is public and in a177* package that is exported to the module that contains D.178* <li>C and D are members of the same runtime package.179* </ul>180*181* @param refc the symbolic reference class to which access is being checked (C)182* @param lookupClass the class performing the lookup (D)183* @param prevLookupClass the class from which the lookup was teleported or null184* @param allowedModes allowed modes185*/186public static boolean isClassAccessible(Class<?> refc,187Class<?> lookupClass,188Class<?> prevLookupClass,189int allowedModes) {190if (allowedModes == 0) return false;191assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED|ORIGINAL_ALLOWED)) == 0);192193if ((allowedModes & PACKAGE_ALLOWED) != 0 &&194isSamePackage(lookupClass, refc))195return true;196197int mods = getClassModifiers(refc);198if (isPublic(mods)) {199200Module lookupModule = lookupClass.getModule();201Module refModule = refc.getModule();202203// early VM startup case, java.base not defined204if (lookupModule == null) {205assert refModule == null;206return true;207}208209// allow access to public types in all unconditionally exported packages210if ((allowedModes & UNCONDITIONAL_ALLOWED) != 0) {211return refModule.isExported(refc.getPackageName());212}213214if (lookupModule == refModule && prevLookupClass == null) {215// allow access to all public types in lookupModule216if ((allowedModes & MODULE_ALLOWED) != 0)217return true;218219assert (allowedModes & PUBLIC) != 0;220return refModule.isExported(refc.getPackageName());221}222223// cross-module access224// 1. refc is in different module from lookupModule, or225// 2. refc is in lookupModule and a different module from prevLookupModule226Module prevLookupModule = prevLookupClass != null ? prevLookupClass.getModule()227: null;228assert refModule != lookupModule || refModule != prevLookupModule;229if (isModuleAccessible(refc, lookupModule, prevLookupModule))230return true;231232// not exported but allow access during VM initialization233// because java.base does not have its exports setup234if (!jdk.internal.misc.VM.isModuleSystemInited())235return true;236237// public class not accessible to lookupClass238return false;239}240241return false;242}243244/*245* Tests if a class or interface REFC is accessible to m1 and m2 where m2246* may be null.247*248* A class or interface REFC in m is accessible to m1 and m2 if and only if249* both m1 and m2 read m and m exports the package of REFC at least to250* both m1 and m2.251*/252public static boolean isModuleAccessible(Class<?> refc, Module m1, Module m2) {253Module refModule = refc.getModule();254assert refModule != m1 || refModule != m2;255int mods = getClassModifiers(refc);256if (isPublic(mods)) {257if (m1.canRead(refModule) && (m2 == null || m2.canRead(refModule))) {258String pn = refc.getPackageName();259260// refc is exported package to at least both m1 and m2261if (refModule.isExported(pn, m1) && (m2 == null || refModule.isExported(pn, m2)))262return true;263}264}265return false;266}267268/**269* Decide if the given method type, attributed to a member or symbolic270* reference of a given reference class, is really visible to that class.271* @param type the supposed type of a member or symbolic reference of refc272* @param refc the class attempting to make the reference273*/274public static boolean isTypeVisible(Class<?> type, Class<?> refc) {275if (type == refc) {276return true; // easy check277}278while (type.isArray()) type = type.getComponentType();279if (type.isPrimitive() || type == Object.class) {280return true;281}282ClassLoader typeLoader = type.getClassLoader();283ClassLoader refcLoader = refc.getClassLoader();284if (typeLoader == refcLoader) {285return true;286}287if (refcLoader == null && typeLoader != null) {288return false;289}290if (typeLoader == null && type.getName().startsWith("java.")) {291// Note: The API for actually loading classes, ClassLoader.defineClass,292// guarantees that classes with names beginning "java." cannot be aliased,293// because class loaders cannot load them directly.294return true;295}296297// Do it the hard way: Look up the type name from the refc loader.298//299// Force the refc loader to report and commit to a particular binding for this type name (type.getName()).300//301// In principle, this query might force the loader to load some unrelated class,302// which would cause this query to fail (and the original caller to give up).303// This would be wasted effort, but it is expected to be very rare, occurring304// only when an attacker is attempting to create a type alias.305// In the normal case, one class loader will simply delegate to the other,306// and the same type will be visible through both, with no extra loading.307//308// It is important to go through Class.forName instead of ClassLoader.loadClass309// because Class.forName goes through the JVM system dictionary, which records310// the class lookup once for all. This means that even if a not-well-behaved class loader311// would "change its mind" about the meaning of the name, the Class.forName request312// will use the result cached in the JVM system dictionary. Note that the JVM system dictionary313// will record the first successful result. Unsuccessful results are not stored.314//315// We use doPrivileged in order to allow an unprivileged caller to ask an arbitrary316// class loader about the binding of the proposed name (type.getName()).317// The looked up type ("res") is compared for equality against the proposed318// type ("type") and then is discarded. Thus, the worst that can happen to319// the "child" class loader is that it is bothered to load and report a class320// that differs from "type"; this happens once due to JVM system dictionary321// memoization. And the caller never gets to look at the alternate type binding322// ("res"), whether it exists or not.323final String name = type.getName();324@SuppressWarnings("removal")325Class<?> res = java.security.AccessController.doPrivileged(326new java.security.PrivilegedAction<>() {327public Class<?> run() {328try {329return Class.forName(name, false, refcLoader);330} catch (ClassNotFoundException | LinkageError e) {331return null; // Assume the class is not found332}333}334});335return (type == res);336}337338/**339* Decide if the given method type, attributed to a member or symbolic340* reference of a given reference class, is really visible to that class.341* @param type the supposed type of a member or symbolic reference of refc342* @param refc the class attempting to make the reference343*/344public static boolean isTypeVisible(java.lang.invoke.MethodType type, Class<?> refc) {345if (!isTypeVisible(type.returnType(), refc)) {346return false;347}348for (int n = 0, max = type.parameterCount(); n < max; n++) {349if (!isTypeVisible(type.parameterType(n), refc)) {350return false;351}352}353return true;354}355356/**357* Tests if two classes are in the same module.358* @param class1 a class359* @param class2 another class360* @return whether they are in the same module361*/362public static boolean isSameModule(Class<?> class1, Class<?> class2) {363return class1.getModule() == class2.getModule();364}365366/**367* Test if two classes have the same class loader and package qualifier.368* @param class1 a class369* @param class2 another class370* @return whether they are in the same package371*/372public static boolean isSamePackage(Class<?> class1, Class<?> class2) {373if (class1 == class2)374return true;375if (class1.getClassLoader() != class2.getClassLoader())376return false;377return class1.getPackageName() == class2.getPackageName();378}379380/**381* Test if two classes are defined as part of the same package member (top-level class).382* If this is true, they can share private access with each other.383* @param class1 a class384* @param class2 another class385* @return whether they are identical or nested together386*/387public static boolean isSamePackageMember(Class<?> class1, Class<?> class2) {388if (class1 == class2)389return true;390if (!isSamePackage(class1, class2))391return false;392if (getOutermostEnclosingClass(class1) != getOutermostEnclosingClass(class2))393return false;394return true;395}396397private static Class<?> getOutermostEnclosingClass(Class<?> c) {398Class<?> pkgmem = c;399for (Class<?> enc = c; (enc = enc.getEnclosingClass()) != null; )400pkgmem = enc;401return pkgmem;402}403404private static boolean loadersAreRelated(ClassLoader loader1, ClassLoader loader2,405boolean loader1MustBeParent) {406if (loader1 == loader2 || loader1 == null407|| (loader2 == null && !loader1MustBeParent)) {408return true;409}410for (ClassLoader scan2 = loader2;411scan2 != null; scan2 = scan2.getParent()) {412if (scan2 == loader1) return true;413}414if (loader1MustBeParent) return false;415// see if loader2 is a parent of loader1:416for (ClassLoader scan1 = loader1;417scan1 != null; scan1 = scan1.getParent()) {418if (scan1 == loader2) return true;419}420return false;421}422423/**424* Is the class loader of parentClass identical to, or an ancestor of,425* the class loader of childClass?426* @param parentClass a class427* @param childClass another class, which may be a descendent of the first class428* @return whether parentClass precedes or equals childClass in class loader order429*/430public static boolean classLoaderIsAncestor(Class<?> parentClass, Class<?> childClass) {431return loadersAreRelated(parentClass.getClassLoader(), childClass.getClassLoader(), true);432}433}434435436