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 }