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