# HG changeset patch # User mbankal # Date 1355327990 28800 # Node ID 41f8dd88c5e9aec4975c36414a865aed5643c880 # Parent 708c134c36312faf8721c0c981be6553e4ebf49f 7197546: (proxy) Reflect about creating reflective proxies Reviewed-by: mchung diff --git a/src/share/classes/java/lang/Class.java b/src/share/classes/java/lang/Class.java --- jdk/src/share/classes/java/lang/Class.java +++ jdk/src/share/classes/java/lang/Class.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2012, 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 @@ -65,7 +65,9 @@ import sun.reflect.generics.scope.ClassS import sun.reflect.generics.scope.ClassScope; import sun.security.util.SecurityConstants; import java.lang.annotation.Annotation; +import java.lang.reflect.Proxy; import sun.reflect.annotation.*; +import sun.reflect.misc.ReflectUtil; /** * Instances of the class {@code Class} represent classes and @@ -320,7 +322,7 @@ public final throws InstantiationException, IllegalAccessException { if (System.getSecurityManager() != null) { - checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), false); } return newInstance0(); } @@ -1294,7 +1296,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), false); // Privileged so this implementation can look at DECLARED classes, // something the caller might not have privilege to do. The code here @@ -1372,7 +1374,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), true); return copyFields(privateGetPublicFields(null)); } @@ -1423,7 +1425,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), true); return copyMethods(privateGetPublicMethods()); } @@ -1472,7 +1474,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), true); return copyConstructors(privateGetDeclaredConstructors(true)); } @@ -1531,7 +1533,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), true); Field field = getField0(name); if (field == null) { throw new NoSuchFieldException(name); @@ -1616,7 +1618,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), true); Method method = getMethod0(name, parameterTypes); if (method == null) { throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); @@ -1670,7 +1672,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader(), true); return getConstructor0(parameterTypes, Member.PUBLIC); } @@ -1712,7 +1714,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader(), false); return getDeclaredClasses0(); } @@ -1756,7 +1758,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader(), true); return copyFields(privateGetDeclaredFields(false)); } @@ -1804,7 +1806,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader(), true); return copyMethods(privateGetDeclaredMethods(false)); } @@ -1849,7 +1851,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader(), true); return copyConstructors(privateGetDeclaredConstructors(false)); } @@ -1893,7 +1895,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader(), true); Field field = searchFields(privateGetDeclaredFields(false), name); if (field == null) { throw new NoSuchFieldException(name); @@ -1948,7 +1950,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader(), true); Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes); if (method == null) { throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); @@ -1998,7 +2000,7 @@ public final // be very careful not to change the stack depth of this // checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess - checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader()); + checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader(), true); return getConstructor0(parameterTypes, Member.DECLARED); } @@ -2168,18 +2170,26 @@ public final *

Default policy: allow all clients access with normal Java access * control. */ - private void checkMemberAccess(int which, ClassLoader ccl) { + private void checkMemberAccess(int which, ClassLoader ccl, boolean checkProxyInterfaces) { SecurityManager s = System.getSecurityManager(); if (s != null) { s.checkMemberAccess(this, which); ClassLoader cl = getClassLoader0(); - if ((ccl != null) && (ccl != cl) && - ((cl == null) || !cl.isAncestor(ccl))) { + if (ReflectUtil.needsPackageAccessCheck(ccl, cl)) { + String name = this.getName(); int i = name.lastIndexOf('.'); if (i != -1) { - s.checkPackageAccess(name.substring(0, i)); + // skip the package access check on a proxy class in default proxy package + String pkg = name.substring(0, i); + if (!Proxy.isProxyClass(this) || !pkg.equals(ReflectUtil.PROXY_PACKAGE)) { + s.checkPackageAccess(pkg); + } } + } + // check package access on the proxy interfaces + if (checkProxyInterfaces && Proxy.isProxyClass(this)) { + ReflectUtil.checkProxyPackageAccess(ccl, this.getInterfaces()); } } } diff --git a/src/share/classes/java/lang/reflect/Proxy.java b/src/share/classes/java/lang/reflect/Proxy.java --- jdk/src/share/classes/java/lang/reflect/Proxy.java +++ jdk/src/share/classes/java/lang/reflect/Proxy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2012, 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 @@ -27,6 +27,9 @@ package java.lang.reflect; import java.lang.ref.Reference; import java.lang.ref.WeakReference; +import java.security.AccessController; +import java.security.Permission; +import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -35,6 +38,9 @@ import java.util.Set; import java.util.Set; import java.util.WeakHashMap; import sun.misc.ProxyGenerator; +import sun.reflect.Reflection; +import sun.reflect.misc.ReflectUtil; +import sun.security.util.SecurityConstants; /** * {@code Proxy} provides static methods for creating dynamic proxy @@ -263,7 +269,67 @@ public class Proxy implements java.io.Se * @param h the invocation handler for this proxy instance */ protected Proxy(InvocationHandler h) { + doNewInstanceCheck(); this.h = h; + } + + private static class ProxyAccessHelper { + // The permission is implementation specific. + static final Permission PROXY_PERMISSION = + new ReflectPermission("proxyConstructorNewInstance"); + // These system properties are defined to provide a short-term + // workaround if customers need to disable the new security checks. + static final boolean allowNewInstance; + static final boolean allowNullLoader; + static { + allowNewInstance = getBooleanProperty("sun.reflect.proxy.allowsNewInstance"); + allowNullLoader = getBooleanProperty("sun.reflect.proxy.allowsNullLoader"); + } + + private static boolean getBooleanProperty(final String key) { + String s = AccessController.doPrivileged(new PrivilegedAction() { + public String run() { + return System.getProperty(key); + } + }); + return Boolean.valueOf(s); + } + + static boolean needsNewInstanceCheck(Class proxyClass) { + if (!Proxy.isProxyClass(proxyClass) || allowNewInstance) { + return false; + } + + if (proxyClass.getName().startsWith(ReflectUtil.PROXY_PACKAGE + ".")) { + // all proxy interfaces are public + return false; + } + for (Class intf : proxyClass.getInterfaces()) { + if (!Modifier.isPublic(intf.getModifiers())) { + return true; + } + } + return false; + } + } + + /* + * Access check on a proxy class that implements any non-public interface. + * + * @throws SecurityException if a security manager exists, and + * the caller does not have the permission. + */ + private void doNewInstanceCheck() { + SecurityManager sm = System.getSecurityManager(); + Class proxyClass = this.getClass(); + if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(proxyClass)) { + try { + sm.checkPermission(ProxyAccessHelper.PROXY_PERMISSION); + } catch (SecurityException e) { + throw new SecurityException("Not allowed to construct a Proxy " + + "instance that implements a non-public interface", e); + } + } } /** @@ -344,6 +410,50 @@ public class Proxy implements java.io.Se Class... interfaces) throws IllegalArgumentException { + return getProxyClass0(loader, interfaces); // stack walk magic: do not refactor + } + + private static void checkProxyLoader(ClassLoader ccl, + ClassLoader loader) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + if (loader == null && ccl != null) { + if (!ProxyAccessHelper.allowNullLoader) { + sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); + } + } + } + } + + /* + * Generate a proxy class (caller-sensitive). + * + * To define a proxy class, it performs the access checks as in + * Class.forName (VM will invoke ClassLoader.checkPackageAccess): + * 1. "getClassLoader" permission check if loader == null + * 2. checkPackageAccess on the interfaces it implements + * + * To get a constructor and new instance of a proxy class, it performs + * the package access check on the interfaces it implements + * as in Class.getConstructor. + * + * If an interface is non-public, the proxy class must be defined by + * the defining loader of the interface. If the caller's class loader + * is not the same as the defining loader of the interface, the VM + * will throw IllegalAccessError when the generated proxy class is + * being defined via the defineClass0 method. + */ + private static Class getProxyClass0(ClassLoader loader, + Class... interfaces) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + final int CALLER_FRAME = 3; // 0: Reflection, 1: getProxyClass0 2: Proxy 3: caller + final Class caller = Reflection.getCallerClass(CALLER_FRAME); + final ClassLoader ccl = caller.getClassLoader(); + checkProxyLoader(ccl, loader); + ReflectUtil.checkProxyPackageAccess(ccl, interfaces); + } if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } @@ -494,8 +604,9 @@ public class Proxy implements java.io.Se } } - if (proxyPkg == null) { // if no non-public proxy interfaces, - proxyPkg = ""; // use the unnamed package + if (proxyPkg == null) { + // if no non-public proxy interfaces, use sun.proxy package + proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } { @@ -595,22 +706,45 @@ public class Proxy implements java.io.Se /* * Look up or generate the designated proxy class. */ - Class cl = getProxyClass(loader, interfaces); + Class cl = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor /* * Invoke its constructor with the designated invocation handler. */ try { - Constructor cons = cl.getConstructor(constructorParams); - return (Object) cons.newInstance(new Object[] { h }); + final Constructor cons = cl.getConstructor(constructorParams); + final InvocationHandler ih = h; + SecurityManager sm = System.getSecurityManager(); + if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { + // create proxy instance with doPrivilege as the proxy class may + // implement non-public interfaces that requires a special permission + return AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + return newInstance(cons, ih); + } + }); + } else { + return newInstance(cons, ih); + } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); + } + } + + private static Object newInstance(Constructor cons, InvocationHandler h) { + try { + return cons.newInstance(new Object[] {h} ); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { - throw new InternalError(e.toString()); + Throwable t = e.getCause(); + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } else { + throw new InternalError(t.toString()); + } } } diff --git a/src/share/classes/sun/reflect/misc/ReflectUtil.java b/src/share/classes/sun/reflect/misc/ReflectUtil.java --- jdk/src/share/classes/sun/reflect/misc/ReflectUtil.java +++ jdk/src/share/classes/sun/reflect/misc/ReflectUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2012, 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 @@ -144,4 +144,63 @@ public final class ReflectUtil { } return true; } + + // Returns true if p is an ancestor of cl i.e. class loader 'p' can + // be found in the cl's delegation chain + private static boolean isAncestor(ClassLoader p, ClassLoader cl) { + ClassLoader acl = cl; + do { + acl = acl.getParent(); + if (p == acl) { + return true; + } + } while (acl != null); + return false; + } + + /** + * Returns true if package access check is needed for reflective + * access from a class loader 'from' to classes or members in + * a class defined by class loader 'to'. This method returns true + * if 'from' is not the same as or an ancestor of 'to'. All code + * in a system domain are granted with all permission and so this + * method returns false if 'from' class loader is a class loader + * loading system classes. On the other hand, if a class loader + * attempts to access system domain classes, it requires package + * access check and this method will return true. + */ + public static boolean needsPackageAccessCheck(ClassLoader from, ClassLoader to) { + if (from == null || from == to) + return false; + + if (to == null) + return true; + + return !isAncestor(from, to); + } + + /** + * Access check on the interfaces that a proxy class implements and throw + * {@code SecurityException} if it accesses a restricted package. + * + * @param ccl the caller's class loader + * @param interfaces the list of interfaces that a proxy class implements + * + * @see Proxy#checkProxyAccess + */ + public static void checkProxyPackageAccess(ClassLoader ccl, + Class... interfaces) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + for (Class intf : interfaces) { + ClassLoader cl = intf.getClassLoader(); + if (needsPackageAccessCheck(ccl, cl)) { + checkPackageAccess(intf); + } + } + } + } + + public static final String PROXY_PACKAGE = "sun.proxy"; }