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 }