1 /* 2 * Copyright (c) 2001, 2022, 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 import java.lang.reflect.*; 29 import java.util.HashMap; 30 import java.util.Map; 31 import java.util.Objects; 32 import java.util.Set; 33 34 import jdk.internal.access.JavaLangAccess; 35 import jdk.internal.access.SharedSecrets; 36 import jdk.internal.misc.VM; 37 import jdk.internal.module.ModuleBootstrap; 38 import jdk.internal.vm.annotation.ForceInline; 39 import jdk.internal.vm.annotation.IntrinsicCandidate; 40 41 /** Common utility routines used by both java.lang and 42 java.lang.reflect */ 43 44 public class Reflection { 45 46 /** Used to filter out fields and methods from certain classes from public 47 view, where they are sensitive or they may contain VM-internal objects. 48 These Maps are updated very rarely. Rather than synchronize on 49 each access, we use copy-on-write */ 50 private static volatile Map<Class<?>, Set<String>> fieldFilterMap; 51 private static volatile Map<Class<?>, Set<String>> methodFilterMap; 52 private static final String WILDCARD = "*"; 53 public static final Set<String> ALL_MEMBERS = Set.of(WILDCARD); 54 55 static { 56 fieldFilterMap = Map.of( 57 Reflection.class, ALL_MEMBERS, 58 AccessibleObject.class, ALL_MEMBERS, 59 Class.class, Set.of("classLoader", "classData"), 60 ClassLoader.class, ALL_MEMBERS, 61 Constructor.class, ALL_MEMBERS, 62 Field.class, ALL_MEMBERS, 63 Method.class, ALL_MEMBERS, 64 Module.class, ALL_MEMBERS, 65 System.class, Set.of("security") 66 ); 67 methodFilterMap = Map.of(); 68 } 69 70 /** Returns the class of the caller of the method calling this method, 71 ignoring frames associated with java.lang.reflect.Method.invoke() 72 and its implementation. */ 73 @CallerSensitive 74 @IntrinsicCandidate 75 public static native Class<?> getCallerClass(); 76 77 /** Retrieves the access flags written to the class file. For 78 inner classes these flags may differ from those returned by 79 Class.getModifiers(), which searches the InnerClasses 80 attribute to find the source-level access flags. This is used 81 instead of Class.getModifiers() for run-time access checks due 82 to compatibility reasons; see 4471811. Only the values of the 83 low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be 84 valid. */ 85 @IntrinsicCandidate 86 public static native int getClassAccessFlags(Class<?> c); 87 88 89 /** 90 * Ensures that access to a member is granted and throws 91 * IllegalAccessException if not. 92 * 93 * @param currentClass the class performing the access 94 * @param memberClass the declaring class of the member being accessed 95 * @param targetClass the class of target object if accessing instance 96 * field or method; 97 * or the declaring class if accessing constructor; 98 * or null if accessing static field or method 99 * @param modifiers the member's access modifiers 100 * @throws IllegalAccessException if access to member is denied 101 */ 102 public static void ensureMemberAccess(Class<?> currentClass, 103 Class<?> memberClass, 104 Class<?> targetClass, 105 int modifiers) 106 throws IllegalAccessException 107 { 108 if (!verifyMemberAccess(currentClass, memberClass, targetClass, modifiers)) { 109 throw newIllegalAccessException(currentClass, memberClass, targetClass, modifiers); 110 } 111 } 112 113 @ForceInline 114 public static void ensureNativeAccess(Class<?> currentClass, Class<?> owner, String methodName) { 115 // if there is no caller class, act as if the call came from unnamed module of system class loader 116 Module module = currentClass != null ? 117 currentClass.getModule() : 118 ClassLoader.getSystemClassLoader().getUnnamedModule(); 119 class Holder { 120 static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); 121 } 122 Holder.JLA.ensureNativeAccess(module, owner, methodName, currentClass); 123 } 124 125 /** 126 * Verify access to a member and return {@code true} if it is granted. 127 * 128 * @param currentClass the class performing the access 129 * @param memberClass the declaring class of the member being accessed 130 * @param targetClass the class of target object if accessing instance 131 * field or method; 132 * or the declaring class if accessing constructor; 133 * or null if accessing static field or method 134 * @param modifiers the member's access modifiers 135 * @return {@code true} if access to member is granted 136 */ 137 public static boolean verifyMemberAccess(Class<?> currentClass, 138 Class<?> memberClass, 139 Class<?> targetClass, 140 int modifiers) 141 { 142 Objects.requireNonNull(currentClass); 143 Objects.requireNonNull(memberClass); 144 145 if (currentClass == memberClass) { 146 // Always succeeds 147 return true; 148 } 149 150 if (!verifyModuleAccess(currentClass.getModule(), memberClass)) { 151 return false; 152 } 153 154 boolean gotIsSameClassPackage = false; 155 boolean isSameClassPackage = false; 156 157 if (!Modifier.isPublic(getClassAccessFlags(memberClass))) { 158 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 159 gotIsSameClassPackage = true; 160 if (!isSameClassPackage) { 161 return false; 162 } 163 } 164 165 // At this point we know that currentClass can access memberClass. 166 167 if (Modifier.isPublic(modifiers)) { 168 return true; 169 } 170 171 // Check for nestmate access if member is private 172 if (Modifier.isPrivate(modifiers)) { 173 // Note: targetClass may be outside the nest, but that is okay 174 // as long as memberClass is in the nest. 175 if (areNestMates(currentClass, memberClass)) { 176 return true; 177 } 178 } 179 180 boolean successSoFar = false; 181 182 if (Modifier.isProtected(modifiers)) { 183 // See if currentClass is a subclass of memberClass 184 if (isSubclassOf(currentClass, memberClass)) { 185 successSoFar = true; 186 } 187 } 188 189 if (!successSoFar && !Modifier.isPrivate(modifiers)) { 190 if (!gotIsSameClassPackage) { 191 isSameClassPackage = isSameClassPackage(currentClass, 192 memberClass); 193 gotIsSameClassPackage = true; 194 } 195 196 if (isSameClassPackage) { 197 successSoFar = true; 198 } 199 } 200 201 if (!successSoFar) { 202 return false; 203 } 204 205 // Additional test for protected instance members 206 // and protected constructors: JLS 6.6.2 207 if (targetClass != null && Modifier.isProtected(modifiers) && 208 targetClass != currentClass) 209 { 210 if (!gotIsSameClassPackage) { 211 isSameClassPackage = isSameClassPackage(currentClass, memberClass); 212 gotIsSameClassPackage = true; 213 } 214 if (!isSameClassPackage) { 215 if (!isSubclassOf(targetClass, currentClass)) { 216 return false; 217 } 218 } 219 } 220 221 return true; 222 } 223 224 /* 225 * Verify if a member is public and memberClass is a public type 226 * in a package that is unconditionally exported and 227 * return {@code true} if it is granted. 228 * 229 * @param memberClass the declaring class of the member being accessed 230 * @param modifiers the member's access modifiers 231 * @return {@code true} if the member is public and in a publicly accessible type 232 */ 233 public static boolean verifyPublicMemberAccess(Class<?> memberClass, int modifiers) { 234 Module m = memberClass.getModule(); 235 return Modifier.isPublic(modifiers) 236 && m.isExported(memberClass.getPackageName()) 237 && Modifier.isPublic(Reflection.getClassAccessFlags(memberClass)); 238 } 239 240 /** 241 * Returns {@code true} if memberClass's module exports memberClass's 242 * package to currentModule. 243 */ 244 public static boolean verifyModuleAccess(Module currentModule, Class<?> memberClass) { 245 Module memberModule = memberClass.getModule(); 246 if (currentModule == memberModule) { 247 // same module (named or unnamed) or both null if called 248 // before module system is initialized, which means we are 249 // dealing with java.base only. 250 return true; 251 } else { 252 String pkg = memberClass.getPackageName(); 253 return memberModule.isExported(pkg, currentModule); 254 } 255 } 256 257 /** 258 * Returns true if two classes in the same package. 259 */ 260 private static boolean isSameClassPackage(Class<?> c1, Class<?> c2) { 261 if (c1.getClassLoader() != c2.getClassLoader()) 262 return false; 263 return Objects.equals(c1.getPackageName(), c2.getPackageName()); 264 } 265 266 static boolean isSubclassOf(Class<?> queryClass, 267 Class<?> ofClass) 268 { 269 while (queryClass != null) { 270 if (queryClass == ofClass) { 271 return true; 272 } 273 queryClass = queryClass.getSuperclass(); 274 } 275 return false; 276 } 277 278 // fieldNames must contain only interned Strings 279 public static synchronized void registerFieldsToFilter(Class<?> containingClass, 280 Set<String> fieldNames) { 281 fieldFilterMap = 282 registerFilter(fieldFilterMap, containingClass, fieldNames); 283 } 284 285 // methodNames must contain only interned Strings 286 public static synchronized void registerMethodsToFilter(Class<?> containingClass, 287 Set<String> methodNames) { 288 methodFilterMap = 289 registerFilter(methodFilterMap, containingClass, methodNames); 290 } 291 292 private static Map<Class<?>, Set<String>> registerFilter(Map<Class<?>, Set<String>> map, 293 Class<?> containingClass, 294 Set<String> names) { 295 if (map.get(containingClass) != null) { 296 throw new IllegalArgumentException 297 ("Filter already registered: " + containingClass); 298 } 299 map = new HashMap<>(map); 300 map.put(containingClass, Set.copyOf(names)); 301 return map; 302 } 303 304 public static Field[] filterFields(Class<?> containingClass, Field[] fields) { 305 if (fieldFilterMap == null) { 306 // Bootstrapping 307 return fields; 308 } 309 return (Field[])filter(fields, fieldFilterMap.get(containingClass)); 310 } 311 312 public static Method[] filterMethods(Class<?> containingClass, Method[] methods) { 313 if (methodFilterMap == null) { 314 // Bootstrapping 315 return methods; 316 } 317 return (Method[])filter(methods, methodFilterMap.get(containingClass)); 318 } 319 320 private static Member[] filter(Member[] members, Set<String> filteredNames) { 321 if ((filteredNames == null) || (members.length == 0)) { 322 return members; 323 } 324 Class<?> memberType = members[0].getClass(); 325 if (filteredNames.contains(WILDCARD)) { 326 return (Member[]) Array.newInstance(memberType, 0); 327 } 328 int numNewMembers = 0; 329 for (Member member : members) { 330 if (!filteredNames.contains(member.getName())) { 331 ++numNewMembers; 332 } 333 } 334 Member[] newMembers = (Member[])Array.newInstance(memberType, numNewMembers); 335 int destIdx = 0; 336 for (Member member : members) { 337 if (!filteredNames.contains(member.getName())) { 338 newMembers[destIdx++] = member; 339 } 340 } 341 return newMembers; 342 } 343 344 /** 345 * Tests if the given method is caller-sensitive and the declaring class 346 * is defined by either the bootstrap class loader or platform class loader. 347 */ 348 public static boolean isCallerSensitive(Method m) { 349 final ClassLoader loader = m.getDeclaringClass().getClassLoader(); 350 if (VM.isSystemDomainLoader(loader)) { 351 return m.isAnnotationPresent(CallerSensitive.class); 352 } 353 return false; 354 } 355 356 /* 357 * Tests if the given Field is a trusted final field and it cannot be 358 * modified reflectively regardless of the value of its accessible flag. 359 */ 360 public static boolean isTrustedFinalField(Field field) { 361 return SharedSecrets.getJavaLangReflectAccess().isTrustedFinalField(field); 362 } 363 364 /** 365 * Returns an IllegalAccessException with an exception message based on 366 * the access that is denied. 367 */ 368 public static IllegalAccessException newIllegalAccessException(Class<?> currentClass, 369 Class<?> memberClass, 370 Class<?> targetClass, 371 int modifiers) 372 { 373 if (currentClass == null) 374 return newIllegalAccessException(memberClass, modifiers); 375 376 String currentSuffix = ""; 377 String memberSuffix = ""; 378 Module m1 = currentClass.getModule(); 379 if (m1.isNamed()) 380 currentSuffix = " (in " + m1 + ")"; 381 Module m2 = memberClass.getModule(); 382 if (m2.isNamed()) 383 memberSuffix = " (in " + m2 + ")"; 384 385 String memberPackageName = memberClass.getPackageName(); 386 387 String msg = currentClass + currentSuffix + " cannot access "; 388 if (m2.isExported(memberPackageName, m1)) { 389 // module access okay so include the modifiers in the message 390 msg += "a member of " + memberClass + memberSuffix + msgSuffix(modifiers); 391 } else { 392 // module access failed 393 msg += memberClass + memberSuffix+ " because " 394 + m2 + " does not export " + memberPackageName; 395 if (m2.isNamed()) msg += " to " + m1; 396 } 397 398 return new IllegalAccessException(msg); 399 } 400 401 /** 402 * Returns an IllegalAccessException with an exception message where 403 * there is no caller frame. 404 */ 405 private static IllegalAccessException newIllegalAccessException(Class<?> memberClass, 406 int modifiers) 407 { 408 String memberSuffix = ""; 409 Module m2 = memberClass.getModule(); 410 if (m2.isNamed()) 411 memberSuffix = " (in " + m2 + ")"; 412 413 String memberPackageName = memberClass.getPackageName(); 414 415 String msg = "JNI attached native thread (null caller frame) cannot access "; 416 if (m2.isExported(memberPackageName)) { 417 // module access okay so include the modifiers in the message 418 msg += "a member of " + memberClass + memberSuffix + msgSuffix(modifiers); 419 } else { 420 // module access failed 421 msg += memberClass + memberSuffix+ " because " 422 + m2 + " does not export " + memberPackageName; 423 } 424 425 return new IllegalAccessException(msg); 426 } 427 428 private static String msgSuffix(int modifiers) { 429 boolean packageAccess = 430 ((Modifier.PRIVATE | 431 Modifier.PROTECTED | 432 Modifier.PUBLIC) & modifiers) == 0; 433 return packageAccess ? 434 " with package access" : 435 " with modifiers \"" + Modifier.toString(modifiers) + "\""; 436 } 437 438 /** 439 * Returns true if {@code currentClass} and {@code memberClass} 440 * are nestmates - that is, if they have the same nesthost as 441 * determined by the VM. 442 */ 443 public static native boolean areNestMates(Class<?> currentClass, 444 Class<?> memberClass); 445 }