# HG changeset patch # User jfranck # Date 1366378831 -3600 # Node ID 996e8c4c858d695584f2365e218c84c79d2cc746 # Parent 1b37223926e5402afc33df0dd31b1753228b84d3 8007812: (reflect) Class.getEnclosingMethod problematic for some classes Summary: Better checking in getEnclosing(Method|Constructor|Class) Reviewed-by: darcy, ahgross, mchung diff -r 1b37223926e5 -r 996e8c4c858d jdk/src/share/classes/java/lang/Class.java --- a/jdk/src/share/classes/java/lang/Class.java Fri Apr 19 11:43:19 2013 +0100 +++ b/jdk/src/share/classes/java/lang/Class.java Fri Apr 19 14:40:31 2013 +0100 @@ -961,9 +961,28 @@ * * @return the immediately enclosing method of the underlying class, if * that class is a local or anonymous class; otherwise {@code null}. + * @exception SecurityException + * If a security manager, s, is present and any of the + * following conditions is met: + * + * * @since 1.5 */ - public Method getEnclosingMethod() { + @CallerSensitive + public Method getEnclosingMethod() throws SecurityException { EnclosingMethodInfo enclosingInfo = getEnclosingMethodInfo(); if (enclosingInfo == null) @@ -984,13 +1003,22 @@ for(int i = 0; i < parameterClasses.length; i++) parameterClasses[i] = toClass(parameterTypes[i]); + // Perform access check + Class enclosingCandidate = enclosingInfo.getEnclosingClass(); + // be very careful not to change the stack depth of this + // checkMemberAccess call for security reasons + // see java.lang.SecurityManager.checkMemberAccess + // + // Note that we need to do this on the enclosing class + enclosingCandidate.checkMemberAccess(Member.DECLARED, + Reflection.getCallerClass(), true); /* * Loop over all declared methods; match method name, * number of and type of parameters, *and* return * type. Matching return type is also necessary * because of covariant returns, etc. */ - for(Method m: enclosingInfo.getEnclosingClass().getDeclaredMethods()) { + for(Method m: enclosingCandidate.getDeclaredMethods()) { if (m.getName().equals(enclosingInfo.getName()) ) { Class[] candidateParamClasses = m.getParameterTypes(); if (candidateParamClasses.length == parameterClasses.length) { @@ -1089,9 +1117,28 @@ * * @return the immediately enclosing constructor of the underlying class, if * that class is a local or anonymous class; otherwise {@code null}. + * @exception SecurityException + * If a security manager, s, is present and any of the + * following conditions is met: + * + * * @since 1.5 */ - public Constructor getEnclosingConstructor() { + @CallerSensitive + public Constructor getEnclosingConstructor() throws SecurityException { EnclosingMethodInfo enclosingInfo = getEnclosingMethodInfo(); if (enclosingInfo == null) @@ -1111,11 +1158,20 @@ for(int i = 0; i < parameterClasses.length; i++) parameterClasses[i] = toClass(parameterTypes[i]); + // Perform access check + Class enclosingCandidate = enclosingInfo.getEnclosingClass(); + // be very careful not to change the stack depth of this + // checkMemberAccess call for security reasons + // see java.lang.SecurityManager.checkMemberAccess + // + // Note that we need to do this on the enclosing class + enclosingCandidate.checkMemberAccess(Member.DECLARED, + Reflection.getCallerClass(), true); /* * Loop over all declared constructors; match number * of and type of parameters. */ - for(Constructor c: enclosingInfo.getEnclosingClass().getDeclaredConstructors()) { + for(Constructor c: enclosingCandidate.getDeclaredConstructors()) { Class[] candidateParamClasses = c.getParameterTypes(); if (candidateParamClasses.length == parameterClasses.length) { boolean matches = true; @@ -1155,9 +1211,16 @@ * class. If the underlying class is a top level class this * method returns {@code null}. * @return the immediately enclosing class of the underlying class + * @exception SecurityException + * If a security manager, s, is present and the caller's + * class loader is not the same as or an ancestor of the class + * loader for the enclosing class and invocation of {@link + * SecurityManager#checkPackageAccess s.checkPackageAccess()} + * denies access to the package of the enclosing class * @since 1.5 */ - public Class getEnclosingClass() { + @CallerSensitive + public Class getEnclosingClass() throws SecurityException { // There are five kinds of classes (or interfaces): // a) Top level classes // b) Nested classes (static member classes) @@ -1170,18 +1233,28 @@ // attribute if and only if it is a local class or an // anonymous class. EnclosingMethodInfo enclosingInfo = getEnclosingMethodInfo(); + Class enclosingCandidate; if (enclosingInfo == null) { // This is a top level or a nested class or an inner class (a, b, or c) - return getDeclaringClass(); + enclosingCandidate = getDeclaringClass(); } else { Class enclosingClass = enclosingInfo.getEnclosingClass(); // This is a local class or an anonymous class (d or e) if (enclosingClass == this || enclosingClass == null) throw new InternalError("Malformed enclosing method information"); else - return enclosingClass; + enclosingCandidate = enclosingClass; } + + // be very careful not to change the stack depth of this + // checkMemberAccess call for security reasons + // see java.lang.SecurityManager.checkMemberAccess + if (enclosingCandidate != null) { + enclosingCandidate.checkMemberAccess(Member.DECLARED, + Reflection.getCallerClass(), true); + } + return enclosingCandidate; } /** diff -r 1b37223926e5 -r 996e8c4c858d jdk/test/lib/testlibrary/ClassFileInstaller.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/lib/testlibrary/ClassFileInstaller.java Fri Apr 19 14:40:31 2013 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +/** + * Dump a class file for a class on the class path in the current directory + */ +public class ClassFileInstaller { + /** + * @param args The names of the classes to dump + * @throws Exception + */ + public static void main(String... args) throws Exception { + for (String arg : args) { + ClassLoader cl = ClassFileInstaller.class.getClassLoader(); + + // Convert dotted class name to a path to a class file + String pathName = arg.replace('.', '/').concat(".class"); + InputStream is = cl.getResourceAsStream(pathName); + + // Create the class file's package directory + Path p = Paths.get(pathName); + Files.createDirectories(p.getParent()); + // Create the class file + Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING); + } + } +}