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