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 (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) {
 108             throwIllegalAccessException(currentClass, memberClass, targetClass, modifiers);
 109         }
 110     }
 111 
 112     /**
 113      * Verify access to a member and return {@code true} if it is granted.
 114      *
 115      * @param currentClass the class performing the access
 116      * @param memberClass the declaring class of the member being accessed
 117      * @param targetClass the class of target object if accessing instance
 118      *                    field or method;
 119      *                    or the declaring class if accessing constructor;
 120      *                    or null if accessing static field or method
 121      * @param modifiers the member's access modifiers
 122      * @return {@code true} if access to member is granted
 123      */
 124     public static boolean verifyMemberAccess(Class<?> currentClass,
 125                                              Class<?> memberClass,
 126                                              Class<?> targetClass,
 127                                              int modifiers)
 128     {
 129         Objects.requireNonNull(currentClass, "currentClass");
 130         Objects.requireNonNull(memberClass, "memberClass");
 131 
 132         if (currentClass == memberClass) {
 133             // Always succeeds
 134             return true;
 135         }
 136 
 137         if (!verifyModuleAccess(currentClass.getModule(), memberClass)) {
 138             return false;
 139         }
 140 
 141         boolean gotIsSameClassPackage = false;
 142         boolean isSameClassPackage = false;
 143 
 144         if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
 145             isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 146             gotIsSameClassPackage = true;
 147             if (!isSameClassPackage) {
 148                 return false;
 149             }
 150         }
 151 
 152         // At this point we know that currentClass can access memberClass.
 153 
 154         if (Modifier.isPublic(modifiers)) {
 155             return true;
 156         }
 157 
 158         boolean successSoFar = false;
 159 
 160         if (Modifier.isProtected(modifiers)) {
 161             // See if currentClass is a subclass of memberClass
 162             if (isSubclassOf(currentClass, memberClass)) {
 163                 successSoFar = true;
 164             }
 165         }
 166 
 167         if (!successSoFar && !Modifier.isPrivate(modifiers)) {
 168             if (!gotIsSameClassPackage) {
 169                 isSameClassPackage = isSameClassPackage(currentClass,
 170                                                         memberClass);
 171                 gotIsSameClassPackage = true;
 172             }
 173 
 174             if (isSameClassPackage) {
 175                 successSoFar = true;
 176             }
 177         }
 178 
 179         if (!successSoFar) {
 180             return false;
 181         }
 182 
 183         // Additional test for protected instance members
 184         // and protected constructors: JLS 6.6.2
 185         if (targetClass != null && Modifier.isProtected(modifiers) &&
 186             targetClass != currentClass)
 187         {
 188             if (!gotIsSameClassPackage) {
 189                 isSameClassPackage = isSameClassPackage(currentClass, memberClass);
 190                 gotIsSameClassPackage = true;
 191             }
 192             if (!isSameClassPackage) {
 193                 if (!isSubclassOf(targetClass, currentClass)) {
 194                     return false;
 195                 }
 196             }
 197         }
 198 
 199         return true;
 200     }
 201 
 202     /**
 203      * Returns {@code true} if memberClass's's module exports memberClass's
 204      * package to currentModule.
 205      */
 206     public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) {
 207         if (VM.isModuleSystemInited())
 208             Objects.requireNonNull(currentModule, "currentModule");
 209         Objects.requireNonNull(memberClass, "memberClass");
 210 
 211         Module memberModule = memberClass.getModule();
 212 
 213         // module may be null during startup (initLevel < VM.MODULE_SYSTEM_INITED)
 214         if (currentModule == memberModule)
 215            return true;  // same module (named or unnamed) or both are null
 216 
 217         String pkg = memberClass.getPackageName();
 218         boolean allowed = memberModule.isExported(pkg, currentModule);
 219         if (allowed && memberModule.isNamed() && printStackTraceWhenAccessSucceeds()) {
 220             if (!SharedSecrets.getJavaLangReflectModuleAccess()
 221                     .isStaticallyExported(memberModule, pkg, currentModule)) {
 222                 String msg = currentModule + " allowed access to member of " + memberClass;
 223                 new Exception(msg).printStackTrace(System.err);
 224             }
 225         }
 226         return allowed;
 227     }
 228 
 229     /**
 230      * Returns true if two classes in the same package.
 231      */
 232     private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) {
 233         if (c1.getClassLoader() != c2.getClassLoader())
 234             return false;
 235         return Objects.equals(c1.getPackageName(), c2.getPackageName());
 236     }
 237 
 238     static boolean isSubclassOf(Class<?> queryClass,
 239                                 Class<?> ofClass)
 240     {
 241         while (queryClass != null) {
 242             if (queryClass == ofClass) {
 243                 return true;
 244             }
 245             queryClass = queryClass.getSuperclass();
 246         }
 247         return false;
 248     }
 249 
 250     // fieldNames must contain only interned Strings
 251     public static synchronized void registerFieldsToFilter(Class<?> containingClass,
 252                                               String ... fieldNames) {
 253         fieldFilterMap =
 254             registerFilter(fieldFilterMap, containingClass, fieldNames);
 255     }
 256 
 257     // methodNames must contain only interned Strings
 258     public static synchronized void registerMethodsToFilter(Class<?> containingClass,
 259                                               String ... methodNames) {
 260         methodFilterMap =
 261             registerFilter(methodFilterMap, containingClass, methodNames);
 262     }
 263 
 264     private static Map<Class<?>,String[]> registerFilter(Map<Class<?>,String[]> map,
 265             Class<?> containingClass, String ... names) {
 266         if (map.get(containingClass) != null) {
 267             throw new IllegalArgumentException
 268                             ("Filter already registered: " + containingClass);
 269         }
 270         map = new HashMap<Class<?>,String[]>(map);
 271         map.put(containingClass, names);
 272         return map;
 273     }
 274 
 275     public static Field[] filterFields(Class<?> containingClass,
 276                                        Field[] fields) {
 277         if (fieldFilterMap == null) {
 278             // Bootstrapping
 279             return fields;
 280         }
 281         return (Field[])filter(fields, fieldFilterMap.get(containingClass));
 282     }
 283 
 284     public static Method[] filterMethods(Class<?> containingClass, Method[] methods) {
 285         if (methodFilterMap == null) {
 286             // Bootstrapping
 287             return methods;
 288         }
 289         return (Method[])filter(methods, methodFilterMap.get(containingClass));
 290     }
 291 
 292     private static Member[] filter(Member[] members, String[] filteredNames) {
 293         if ((filteredNames == null) || (members.length == 0)) {
 294             return members;
 295         }
 296         int numNewMembers = 0;
 297         for (Member member : members) {
 298             boolean shouldSkip = false;
 299             for (String filteredName : filteredNames) {
 300                 if (member.getName() == filteredName) {
 301                     shouldSkip = true;
 302                     break;
 303                 }
 304             }
 305             if (!shouldSkip) {
 306                 ++numNewMembers;
 307             }
 308         }
 309         Member[] newMembers =
 310             (Member[])Array.newInstance(members[0].getClass(), numNewMembers);
 311         int destIdx = 0;
 312         for (Member member : members) {
 313             boolean shouldSkip = false;
 314             for (String filteredName : filteredNames) {
 315                 if (member.getName() == filteredName) {
 316                     shouldSkip = true;
 317                     break;
 318                 }
 319             }
 320             if (!shouldSkip) {
 321                 newMembers[destIdx++] = member;
 322             }
 323         }
 324         return newMembers;
 325     }
 326 
 327     /**
 328      * Tests if the given method is caller-sensitive and the declaring class
 329      * is defined by either the bootstrap class loader or platform class loader.
 330      */
 331     public static boolean isCallerSensitive(Method m) {
 332         final ClassLoader loader = m.getDeclaringClass().getClassLoader();
 333         if (VM.isSystemDomainLoader(loader) || isExtClassLoader(loader))  {
 334             return m.isAnnotationPresent(CallerSensitive.class);
 335         }
 336         return false;
 337     }
 338 
 339     private static boolean isExtClassLoader(ClassLoader loader) {
 340         ClassLoader cl = ClassLoader.getSystemClassLoader();
 341         while (cl != null) {
 342             if (cl.getParent() == null && cl == loader) {
 343                 return true;
 344             }
 345             cl = cl.getParent();
 346         }
 347         return false;
 348     }
 349 
 350 
 351     // true to print a stack trace when access fails
 352     private static volatile boolean printStackWhenAccessFails;
 353 
 354     // true to print a stack trace when access succeeds
 355     private static volatile boolean printStackWhenAccessSucceeds;
 356 
 357     // true if printStack* values are initialized
 358     private static volatile boolean printStackPropertiesSet;
 359 
 360     private static void ensurePrintStackPropertiesSet() {
 361         if (!printStackPropertiesSet && VM.initLevel() >= 1) {
 362             String s = GetPropertyAction.privilegedGetProperty(
 363                     "sun.reflect.debugModuleAccessChecks");
 364             if (s != null) {
 365                 printStackWhenAccessFails = !s.equalsIgnoreCase("false");
 366                 printStackWhenAccessSucceeds = s.equalsIgnoreCase("access");
 367             }
 368             printStackPropertiesSet = true;
 369         }
 370     }
 371 
 372     public static boolean printStackTraceWhenAccessFails() {
 373         ensurePrintStackPropertiesSet();
 374         return printStackWhenAccessFails;
 375     }
 376 
 377     public static boolean printStackTraceWhenAccessSucceeds() {
 378         ensurePrintStackPropertiesSet();
 379         return printStackWhenAccessSucceeds;
 380     }
 381 
 382     /**
 383      * Throws IllegalAccessException with the an exception message based on
 384      * the access that is denied. This method throws meaningful exception only
 385      * when {@link #verifyMemberAccess(Class, Class, Class, int)} returns false
 386      * for the same parameters.
 387      */
 388     public static void throwIllegalAccessException(Class<?> currentClass,
 389                                                     Class<?> memberClass,
 390                                                     Class<?> targetClass,
 391                                                     int modifiers)
 392         throws IllegalAccessException
 393     {
 394         String currentSuffix = "";
 395         String memberSuffix = "";
 396         Module m1 = currentClass.getModule();
 397         if (m1.isNamed())
 398             currentSuffix = " (in " + m1 + ")";
 399         Module m2 = memberClass.getModule();
 400         if (m2.isNamed())
 401             memberSuffix = " (in " + m2 + ")";
 402 
 403         String memberPackageName = memberClass.getPackageName();
 404 
 405         String msg = currentClass + currentSuffix + " cannot access ";
 406         if (m2.isExported(memberPackageName, m1)) {
 407 
 408             // module access okay so include the modifiers in the message
 409             msg += "a member of " + memberClass + memberSuffix +
 410                     " with modifiers \"" + Modifier.toString(modifiers) + "\"";
 411 
 412         } else {
 413             // module access failed
 414             msg += memberClass + memberSuffix+ " because "
 415                    + m2 + " does not export " + memberPackageName;
 416             if (m2.isNamed()) msg += " to " + m1;
 417         }
 418 
 419         throwIllegalAccessException(msg);
 420     }
 421 
 422     /**
 423      * Throws IllegalAccessException with the given exception message.
 424      */
 425     public static void throwIllegalAccessException(String msg)
 426         throws IllegalAccessException
 427     {
 428         IllegalAccessException e = new IllegalAccessException(msg);
 429         ensurePrintStackPropertiesSet();
 430         if (printStackWhenAccessFails) {
 431             e.printStackTrace(System.err);
 432         }
 433         throw e;
 434     }
 435 }