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