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 }