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