1 /*
   2  * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.internal.reflect;
  27 
  28 
  29 import java.lang.reflect.*;
  30 import java.util.HashMap;
  31 import java.util.Map;
  32 import java.util.Objects;
  33 import jdk.internal.HotSpotIntrinsicCandidate;
  34 import jdk.internal.misc.SharedSecrets;
  35 import jdk.internal.misc.VM;
  36 import sun.security.action.GetPropertyAction;
  37 
  38 /** Common utility routines used by both java.lang and
  39     java.lang.reflect */
  40 
  41 public class Reflection {
  42 
  43     /** Used to filter out fields and methods from certain classes from public
  44         view, where they are sensitive or they may contain VM-internal objects.
  45         These Maps are updated very rarely. Rather than synchronize on
  46         each access, we use copy-on-write */
  47     private static volatile Map<Class<?>,String[]> fieldFilterMap;
  48     private static volatile Map<Class<?>,String[]> methodFilterMap;
  49 
  50     static {
  51         Map<Class<?>,String[]> map = new HashMap<Class<?>,String[]>();
  52         map.put(Reflection.class,
  53             new String[] {"fieldFilterMap", "methodFilterMap"});
  54         map.put(System.class, new String[] {"security"});
  55         map.put(Class.class, new String[] {"classLoader"});
  56         fieldFilterMap = map;
  57 
  58         methodFilterMap = new HashMap<>();
  59     }
  60 
  61     /** Returns the class of the caller of the method calling this method,
  62         ignoring frames associated with java.lang.reflect.Method.invoke()
  63         and its implementation. */
  64     @CallerSensitive
  65     @HotSpotIntrinsicCandidate
  66     public static native Class<?> getCallerClass();
  67 
  68     /**
  69      * @deprecated This method will be removed.
  70      * This method is a private JDK API and retained temporarily to
  71      * simplify the implementation of sun.misc.Reflection.getCallerClass.
  72      */
  73     @Deprecated(forRemoval=true)
  74     public static native Class<?> getCallerClass(int depth);
  75 
  76     /** Retrieves the access flags written to the class file. For
  77         inner classes these flags may differ from those returned by
  78         Class.getModifiers(), which searches the InnerClasses
  79         attribute to find the source-level access flags. This is used
  80         instead of Class.getModifiers() for run-time access checks due
  81         to compatibility reasons; see 4471811. Only the values of the
  82         low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be
  83         valid. */
  84     @HotSpotIntrinsicCandidate
  85     public static native int getClassAccessFlags(Class<?> c);
  86 
  87 
  88     /**
  89      * Ensures that access to a member is granted and throws
  90      * IllegalAccessException if not.
  91      *
  92      * @param currentClass the class performing the access
  93      * @param memberClass the declaring class of the member being accessed
  94      * @param targetClass the class of target object if accessing instance
  95      *                    field or method;
  96      *                    or the declaring class if accessing constructor;
  97      *                    or null if accessing static field or method
  98      * @param modifiers the member's access modifiers
  99      * @throws IllegalAccessException if access to member is denied
 100      */
 101     public static void ensureMemberAccess(Class<?> currentClass,
 102                                           Class<?> memberClass,
 103                                           Class<?> targetClass,
 104                                           int modifiers)
 105         throws IllegalAccessException
 106     {
 107         if (currentClass == null || memberClass == null) {
 108             throw new InternalError();
 109         }
 110 
 111         if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) {
 112             throwIllegalAccessException(currentClass, memberClass, targetClass, modifiers);
 113         }
 114     }
 115 
 116     /**
 117      * Verify access to a member, returning {@code false} if no access
 118      */
 119     public static boolean verifyMemberAccess(Class<?> currentClass,
 120                                              Class<?> memberClass,
 121                                              Class<?> targetClass,
 122                                              int modifiers)
 123     {
 124         // Verify that currentClass can access a field, method, or
 125         // constructor of memberClass, where that member's access bits are
 126         // "modifiers".
 127 
 128         boolean gotIsSameClassPackage = false;
 129         boolean isSameClassPackage = false;
 130 
 131         if (currentClass == memberClass) {
 132             // Always succeeds
 133             return true;
 134         }
 135 
 136         if (!verifyModuleAccess(currentClass, memberClass)) {
 137             return false;
 138         }
 139 
 140         if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
 141             isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 142             gotIsSameClassPackage = true;
 143             if (!isSameClassPackage) {
 144                 return false;
 145             }
 146         }
 147 
 148         // At this point we know that currentClass can access memberClass.
 149 
 150         if (Modifier.isPublic(modifiers)) {
 151             return true;
 152         }
 153 
 154         boolean successSoFar = false;
 155 
 156         if (Modifier.isProtected(modifiers)) {
 157             // See if currentClass is a subclass of memberClass
 158             if (isSubclassOf(currentClass, memberClass)) {
 159                 successSoFar = true;
 160             }
 161         }
 162 
 163         if (!successSoFar && !Modifier.isPrivate(modifiers)) {
 164             if (!gotIsSameClassPackage) {
 165                 isSameClassPackage = isSameClassPackage(currentClass,
 166                                                         memberClass);
 167                 gotIsSameClassPackage = true;
 168             }
 169 
 170             if (isSameClassPackage) {
 171                 successSoFar = true;
 172             }
 173         }
 174 
 175         if (!successSoFar) {
 176             return false;
 177         }
 178 
 179         // Additional test for protected instance members
 180         // and protected constructors: JLS 6.6.2
 181         if (targetClass != null && Modifier.isProtected(modifiers) &&
 182             targetClass != currentClass)
 183         {
 184             if (!gotIsSameClassPackage) {
 185                 isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 186                 gotIsSameClassPackage = true;
 187             }
 188             if (!isSameClassPackage) {
 189                 if (!isSubclassOf(targetClass, currentClass)) {
 190                     return false;
 191                 }
 192             }
 193         }
 194 
 195         return true;
 196     }
 197 
 198     /**
 199      * Returns {@code true} if memberClass's's module exports memberClass's
 200      * package to currentClass's module.
 201      */
 202     public static boolean verifyModuleAccess(Class<?> currentClass,
 203                                              Class<?> memberClass) {
 204         return verifyModuleAccess(currentClass.getModule(), memberClass);
 205     }
 206 
 207     public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
 208         Module memberModule = memberClass.getModule();
 209 
 210         // module may be null during startup (initLevel 0)
 211         if (currentModule == memberModule)
 212            return true;  // same module (named or unnamed)
 213 
 214         String pkg = memberClass.getPackageName();
 215         boolean allowed = memberModule.isExported(pkg, currentModule);
 216         if (allowed && memberModule.isNamed() && printStackTraceWhenAccessSucceeds()) {
 217             if (!SharedSecrets.getJavaLangReflectModuleAccess()
 218                     .isStaticallyExported(memberModule, pkg, currentModule)) {
 219                 String msg = currentModule + " allowed access to member of " + memberClass;
 220                 new Exception(msg).printStackTrace(System.err);
 221             }
 222         }
 223         return allowed;
 224     }
 225 
 226     /**
 227      * Returns true if two classes in the same package.
 228      */
 229     private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
 230         if (c1.getClassLoader() != c2.getClassLoader())
 231             return false;
 232         return Objects.equals(c1.getPackageName(), c2.getPackageName());
 233     }
 234 
 235     static boolean isSubclassOf(Class<?> queryClass,
 236                                 Class<?> ofClass)
 237     {
 238         while (queryClass != null) {
 239             if (queryClass == ofClass) {
 240                 return true;
 241             }
 242             queryClass = queryClass.getSuperclass();
 243         }
 244         return false;
 245     }
 246 
 247     // fieldNames must contain only interned Strings
 248     public static synchronized void registerFieldsToFilter(Class<?> containingClass,
 249                                               String ... fieldNames) {
 250         fieldFilterMap =
 251             registerFilter(fieldFilterMap, containingClass, fieldNames);
 252     }
 253 
 254     // methodNames must contain only interned Strings
 255     public static synchronized void registerMethodsToFilter(Class<?> containingClass,
 256                                               String ... methodNames) {
 257         methodFilterMap =
 258             registerFilter(methodFilterMap, containingClass, methodNames);
 259     }
 260 
 261     private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map,
 262             Class<?> containingClass, String ... names) {
 263         if (map.get(containingClass) != null) {
 264             throw new IllegalArgumentException
 265                             ("Filter already registered: " + containingClass);
 266         }
 267         map = new HashMap<Class<?>,String[]>(map);
 268         map.put(containingClass, names);
 269         return map;
 270     }
 271 
 272     public static Field[] filterFields(Class<?> containingClass,
 273                                        Field[] fields) {
 274         if (fieldFilterMap == null) {
 275             // Bootstrapping
 276             return fields;
 277         }
 278         return (Field[])filter(fields, fieldFilterMap.get(containingClass));
 279     }
 280 
 281     public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
 282         if (methodFilterMap == null) {
 283             // Bootstrapping
 284             return methods;
 285         }
 286         return (Method[])filter(methods, methodFilterMap.get(containingClass));
 287     }
 288 
 289     private static Member[] filter(Member[] members, String[] filteredNames) {
 290         if ((filteredNames == null) || (members.length == 0)) {
 291             return members;
 292         }
 293         int numNewMembers = 0;
 294         for (Member member : members) {
 295             boolean shouldSkip = false;
 296             for (String filteredName : filteredNames) {
 297                 if (member.getName() == filteredName) {
 298                     shouldSkip = true;
 299                     break;
 300                 }
 301             }
 302             if (!shouldSkip) {
 303                 ++numNewMembers;
 304             }
 305         }
 306         Member[] newMembers =
 307             (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
 308         int destIdx = 0;
 309         for (Member member : members) {
 310             boolean shouldSkip = false;
 311             for (String filteredName : filteredNames) {
 312                 if (member.getName() == filteredName) {
 313                     shouldSkip = true;
 314                     break;
 315                 }
 316             }
 317             if (!shouldSkip) {
 318                 newMembers[destIdx++] = member;
 319             }
 320         }
 321         return newMembers;
 322     }
 323 
 324     /**
 325      * Tests if the given method is caller-sensitive and the declaring class
 326      * is defined by either the bootstrap class loader or platform class loader.
 327      */
 328     public static boolean isCallerSensitive(Method m) {
 329         final ClassLoader loader = m.getDeclaringClass().getClassLoader();
 330         if (VM.isSystemDomainLoader(loader) || isExtClassLoader(loader))  {
 331             return m.isAnnotationPresent(CallerSensitive.class);
 332         }
 333         return false;
 334     }
 335 
 336     private static boolean isExtClassLoader(ClassLoader loader) {
 337         ClassLoader cl = ClassLoader.getSystemClassLoader();
 338         while (cl != null) {
 339             if (cl.getParent() == null && cl == loader) {
 340                 return true;
 341             }
 342             cl = cl.getParent();
 343         }
 344         return false;
 345     }
 346 
 347 
 348     // true to print a stack trace when access fails
 349     private static volatile boolean printStackWhenAccessFails;
 350 
 351     // true to print a stack trace when access succeeds
 352     private static volatile boolean printStackWhenAccessSucceeds;
 353 
 354     // true if printStack* values are initialized
 355     private static volatile boolean printStackPropertiesSet;
 356 
 357     private static void ensurePrintStackPropertiesSet() {
 358         if (!printStackPropertiesSet && VM.initLevel() >= 1) {
 359             String s = GetPropertyAction.privilegedGetProperty(
 360                     "sun.reflect.debugModuleAccessChecks");
 361             if (s != null) {
 362                 printStackWhenAccessFails = !s.equalsIgnoreCase("false");
 363                 printStackWhenAccessSucceeds = s.equalsIgnoreCase("access");
 364             }
 365             printStackPropertiesSet = true;
 366         }
 367     }
 368 
 369     public static boolean printStackTraceWhenAccessFails() {
 370         ensurePrintStackPropertiesSet();
 371         return printStackWhenAccessFails;
 372     }
 373 
 374     public static boolean printStackTraceWhenAccessSucceeds() {
 375         ensurePrintStackPropertiesSet();
 376         return printStackWhenAccessSucceeds;
 377     }
 378 
 379     /**
 380      * Throws IllegalAccessException with the an exception message based on
 381      * the access that is denied.
 382      */
 383     private static void throwIllegalAccessException(Class<?> currentClass,
 384                                                     Class<?> memberClass,
 385                                                     Object target,
 386                                                     int modifiers)
 387         throws IllegalAccessException
 388     {
 389         String currentSuffix = "";
 390         String memberSuffix = "";
 391         Module m1 = currentClass.getModule();
 392         if (m1.isNamed())
 393             currentSuffix = " (in " + m1 + ")";
 394         Module m2 = memberClass.getModule();
 395         if (m2.isNamed())
 396             memberSuffix = " (in " + m2 + ")";
 397 
 398         String memberPackageName = memberClass.getPackageName();
 399 
 400         String msg = currentClass + currentSuffix + " cannot access ";
 401         if (m2.isExported(memberPackageName, m1)) {
 402 
 403             // module access okay so include the modifiers in the message
 404             msg += "a member of " + memberClass + memberSuffix +
 405                     " with modifiers \"" + Modifier.toString(modifiers) + "\"";
 406 
 407         } else {
 408             // module access failed
 409             msg += memberClass + memberSuffix+ " because "
 410                    + m2 + " does not export " + memberPackageName;
 411             if (m2.isNamed()) msg += " to " + m1;
 412         }
 413 
 414         throwIllegalAccessException(msg);
 415     }
 416 
 417     /**
 418      * Throws IllegalAccessException with the given exception message.
 419      */
 420     public static void throwIllegalAccessException(String msg)
 421         throws IllegalAccessException
 422     {
 423         IllegalAccessException e = new IllegalAccessException(msg);
 424         ensurePrintStackPropertiesSet();
 425         if (printStackWhenAccessFails) {
 426             e.printStackTrace(System.err);
 427         }
 428         throw e;
 429     }
 430 }