Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/invoke/util/VerifyAccess.java
38918 views
/*1* Copyright (c) 2008, 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. 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 sun.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 PACKAGE_ONLY = 0;40private static final int PACKAGE_ALLOWED = java.lang.invoke.MethodHandles.Lookup.PACKAGE;41private static final int PROTECTED_OR_PACKAGE_ALLOWED = (PACKAGE_ALLOWED|PROTECTED);42private static final int ALL_ACCESS_MODES = (PUBLIC|PRIVATE|PROTECTED|PACKAGE_ONLY);43private static final boolean ALLOW_NESTMATE_ACCESS = false;4445/**46* Evaluate the JVM linkage rules for access to the given method47* on behalf of a caller class which proposes to perform the access.48* Return true if the caller class has privileges to invoke a method49* or access a field with the given properties.50* This requires an accessibility check of the referencing class,51* plus an accessibility check of the member within the class,52* which depends on the member's modifier flags.53* <p>54* The relevant properties include the defining class ({@code defc})55* of the member, and its modifier flags ({@code mods}).56* Also relevant is the class used to make the initial symbolic reference57* to the member ({@code refc}). If this latter class is not distinguished,58* the defining class should be passed for both arguments ({@code defc == refc}).59* <h3>JVM Specification, 5.4.4 "Access Control"</h3>60* A field or method R is accessible to a class or interface D if61* and only if any of the following conditions is true:<ul>62* <li>R is public.63* <li>R is protected and is declared in a class C, and D is either64* a subclass of C or C itself. Furthermore, if R is not65* static, then the symbolic reference to R must contain a66* symbolic reference to a class T, such that T is either a67* subclass of D, a superclass of D or D itself.68* <li>R is either protected or has default access (that is,69* neither public nor protected nor private), and is declared70* by a class in the same runtime package as D.71* <li>R is private and is declared in D.72* </ul>73* This discussion of access control omits a related restriction74* on the target of a protected field access or method invocation75* (the target must be of class D or a subtype of D). That76* requirement is checked as part of the verification process77* (5.4.1); it is not part of link-time access control.78* @param refc the class used in the symbolic reference to the proposed member79* @param defc the class in which the proposed member is actually defined80* @param mods modifier flags for the proposed member81* @param lookupClass the class for which the access check is being made82* @return true iff the the accessing class can access such a member83*/84public static boolean isMemberAccessible(Class<?> refc, // symbolic ref class85Class<?> defc, // actual def class86int mods, // actual member mods87Class<?> lookupClass,88int allowedModes) {89if (allowedModes == 0) return false;90assert((allowedModes & PUBLIC) != 0 &&91(allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED)) == 0);92// The symbolic reference class (refc) must always be fully verified.93if (!isClassAccessible(refc, lookupClass, allowedModes)) {94return false;95}96// Usually refc and defc are the same, but verify defc also in case they differ.97if (defc == lookupClass &&98(allowedModes & PRIVATE) != 0)99return true; // easy check; all self-access is OK100switch (mods & ALL_ACCESS_MODES) {101case PUBLIC:102return true; // already checked above103case PROTECTED:104assert !defc.isInterface(); // protected members aren't allowed in interfaces105if ((allowedModes & PROTECTED_OR_PACKAGE_ALLOWED) != 0 &&106isSamePackage(defc, lookupClass))107return true;108if ((allowedModes & PROTECTED) == 0)109return false;110// Protected members are accessible by subclasses, which does not include interfaces.111// Interfaces are types, not classes. They should not have access to112// protected members in j.l.Object, even though it is their superclass.113if ((mods & STATIC) != 0 &&114!isRelatedClass(refc, lookupClass))115return false;116if ((allowedModes & PROTECTED) != 0 &&117isSubClass(lookupClass, defc))118return true;119return false;120case PACKAGE_ONLY: // That is, zero. Unmarked member is package-only access.121assert !defc.isInterface(); // package-private members aren't allowed in interfaces122return ((allowedModes & PACKAGE_ALLOWED) != 0 &&123isSamePackage(defc, lookupClass));124case PRIVATE:125// Loosened rules for privates follows access rules for inner classes.126return (ALLOW_NESTMATE_ACCESS &&127(allowedModes & PRIVATE) != 0 &&128isSamePackageMember(defc, lookupClass));129default:130throw new IllegalArgumentException("bad modifiers: "+Modifier.toString(mods));131}132}133134static boolean isRelatedClass(Class<?> refc, Class<?> lookupClass) {135return (refc == lookupClass ||136isSubClass(refc, lookupClass) ||137isSubClass(lookupClass, refc));138}139140static boolean isSubClass(Class<?> lookupClass, Class<?> defc) {141return defc.isAssignableFrom(lookupClass) &&142!lookupClass.isInterface(); // interfaces are types, not classes.143}144145static int getClassModifiers(Class<?> c) {146// This would return the mask stored by javac for the source-level modifiers.147// return c.getModifiers();148// But what we need for JVM access checks are the actual bits from the class header.149// ...But arrays and primitives are synthesized with their own odd flags:150if (c.isArray() || c.isPrimitive())151return c.getModifiers();152return Reflection.getClassAccessFlags(c);153}154155/**156* Evaluate the JVM linkage rules for access to the given class on behalf of caller.157* <h3>JVM Specification, 5.4.4 "Access Control"</h3>158* A class or interface C is accessible to a class or interface D159* if and only if either of the following conditions are true:<ul>160* <li>C is public.161* <li>C and D are members of the same runtime package.162* </ul>163* @param refc the symbolic reference class to which access is being checked (C)164* @param lookupClass the class performing the lookup (D)165*/166public static boolean isClassAccessible(Class<?> refc, Class<?> lookupClass,167int allowedModes) {168if (allowedModes == 0) return false;169assert((allowedModes & PUBLIC) != 0 &&170(allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED)) == 0);171int mods = getClassModifiers(refc);172if (isPublic(mods))173return true;174if ((allowedModes & PACKAGE_ALLOWED) != 0 &&175isSamePackage(lookupClass, refc))176return true;177return false;178}179180/**181* Decide if the given method type, attributed to a member or symbolic182* reference of a given reference class, is really visible to that class.183* @param type the supposed type of a member or symbolic reference of refc184* @param refc the class attempting to make the reference185*/186public static boolean isTypeVisible(Class<?> type, Class<?> refc) {187if (type == refc) {188return true; // easy check189}190while (type.isArray()) type = type.getComponentType();191if (type.isPrimitive() || type == Object.class) {192return true;193}194ClassLoader typeLoader = type.getClassLoader();195ClassLoader refcLoader = refc.getClassLoader();196if (typeLoader == refcLoader) {197return true;198}199if (refcLoader == null && typeLoader != null) {200return false;201}202if (typeLoader == null && type.getName().startsWith("java.")) {203// Note: The API for actually loading classes, ClassLoader.defineClass,204// guarantees that classes with names beginning "java." cannot be aliased,205// because class loaders cannot load them directly.206return true;207}208209// Do it the hard way: Look up the type name from the refc loader.210//211// Force the refc loader to report and commit to a particular binding for this type name (type.getName()).212//213// In principle, this query might force the loader to load some unrelated class,214// which would cause this query to fail (and the original caller to give up).215// This would be wasted effort, but it is expected to be very rare, occurring216// only when an attacker is attempting to create a type alias.217// In the normal case, one class loader will simply delegate to the other,218// and the same type will be visible through both, with no extra loading.219//220// It is important to go through Class.forName instead of ClassLoader.loadClass221// because Class.forName goes through the JVM system dictionary, which records222// the class lookup once for all. This means that even if a not-well-behaved class loader223// would "change its mind" about the meaning of the name, the Class.forName request224// will use the result cached in the JVM system dictionary. Note that the JVM system dictionary225// will record the first successful result. Unsuccessful results are not stored.226//227// We use doPrivileged in order to allow an unprivileged caller to ask an arbitrary228// class loader about the binding of the proposed name (type.getName()).229// The looked up type ("res") is compared for equality against the proposed230// type ("type") and then is discarded. Thus, the worst that can happen to231// the "child" class loader is that it is bothered to load and report a class232// that differs from "type"; this happens once due to JVM system dictionary233// memoization. And the caller never gets to look at the alternate type binding234// ("res"), whether it exists or not.235final String name = type.getName();236Class<?> res = java.security.AccessController.doPrivileged(237new java.security.PrivilegedAction<Class>() {238public Class<?> run() {239try {240return Class.forName(name, false, refcLoader);241} catch (ClassNotFoundException | LinkageError e) {242return null; // Assume the class is not found243}244}245});246return (type == res);247}248249/**250* Decide if the given method type, attributed to a member or symbolic251* reference of a given reference class, is really visible to that class.252* @param type the supposed type of a member or symbolic reference of refc253* @param refc the class attempting to make the reference254*/255public static boolean isTypeVisible(java.lang.invoke.MethodType type, Class<?> refc) {256for (int n = -1, max = type.parameterCount(); n < max; n++) {257Class<?> ptype = (n < 0 ? type.returnType() : type.parameterType(n));258if (!isTypeVisible(ptype, refc))259return false;260}261return true;262}263264/**265* Test if two classes have the same class loader and package qualifier.266* @param class1 a class267* @param class2 another class268* @return whether they are in the same package269*/270public static boolean isSamePackage(Class<?> class1, Class<?> class2) {271assert(!class1.isArray() && !class2.isArray());272if (class1 == class2)273return true;274if (class1.getClassLoader() != class2.getClassLoader())275return false;276String name1 = class1.getName(), name2 = class2.getName();277int dot = name1.lastIndexOf('.');278if (dot != name2.lastIndexOf('.'))279return false;280for (int i = 0; i < dot; i++) {281if (name1.charAt(i) != name2.charAt(i))282return false;283}284return true;285}286287/** Return the package name for this class.288*/289public static String getPackageName(Class<?> cls) {290assert(!cls.isArray());291String name = cls.getName();292int dot = name.lastIndexOf('.');293if (dot < 0) return "";294return name.substring(0, dot);295}296297/**298* Test if two classes are defined as part of the same package member (top-level class).299* If this is true, they can share private access with each other.300* @param class1 a class301* @param class2 another class302* @return whether they are identical or nested together303*/304public static boolean isSamePackageMember(Class<?> class1, Class<?> class2) {305if (class1 == class2)306return true;307if (!isSamePackage(class1, class2))308return false;309if (getOutermostEnclosingClass(class1) != getOutermostEnclosingClass(class2))310return false;311return true;312}313314private static Class<?> getOutermostEnclosingClass(Class<?> c) {315Class<?> pkgmem = c;316for (Class<?> enc = c; (enc = enc.getEnclosingClass()) != null; )317pkgmem = enc;318return pkgmem;319}320321private static boolean loadersAreRelated(ClassLoader loader1, ClassLoader loader2,322boolean loader1MustBeParent) {323if (loader1 == loader2 || loader1 == null324|| (loader2 == null && !loader1MustBeParent)) {325return true;326}327for (ClassLoader scan2 = loader2;328scan2 != null; scan2 = scan2.getParent()) {329if (scan2 == loader1) return true;330}331if (loader1MustBeParent) return false;332// see if loader2 is a parent of loader1:333for (ClassLoader scan1 = loader1;334scan1 != null; scan1 = scan1.getParent()) {335if (scan1 == loader2) return true;336}337return false;338}339340/**341* Is the class loader of parentClass identical to, or an ancestor of,342* the class loader of childClass?343* @param parentClass a class344* @param childClass another class, which may be a descendent of the first class345* @return whether parentClass precedes or equals childClass in class loader order346*/347public static boolean classLoaderIsAncestor(Class<?> parentClass, Class<?> childClass) {348return loadersAreRelated(parentClass.getClassLoader(), childClass.getClassLoader(), true);349}350}351352353