OpenJDK / jdk / jdk
changeset 32268:9fe7ee60d49d
8073056: Repeating annotations throws java.security.AccessControlException with a SecurityManager
Reviewed-by: ahgross, darcy
author | jfranck |
---|---|
date | Mon, 24 Aug 2015 11:00:12 +0200 |
parents | 4e96a9ee01b1 |
children | cb52640fd0e8 |
files | jdk/src/java.base/share/classes/java/lang/reflect/Method.java jdk/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java jdk/src/java.base/share/classes/sun/reflect/LangReflectAccess.java jdk/src/java.base/share/classes/sun/reflect/ReflectionFactory.java jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationSupport.java jdk/test/java/lang/annotation/repeatingAnnotations/CustomRepeatingWithSecurityManager.java jdk/test/java/lang/annotation/repeatingAnnotations/RepeatingWithSecurityManager.java |
diffstat | 7 files changed, 261 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Method.java Wed Jul 08 23:26:48 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Method.java Mon Aug 24 11:00:12 2015 +0200 @@ -161,6 +161,21 @@ } /** + * Make a copy of a leaf method. + */ + Method leafCopy() { + if (this.root == null) + throw new IllegalArgumentException("Can only leafCopy a non-root Method"); + + Method res = new Method(clazz, name, parameterTypes, returnType, + exceptionTypes, modifiers, slot, signature, + annotations, parameterAnnotations, annotationDefault); + res.root = root; + res.methodAccessor = methodAccessor; + return res; + } + + /** * Used by Excecutable for annotation sharing. */ @Override
--- a/jdk/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java Wed Jul 08 23:26:48 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/ReflectAccess.java Mon Aug 24 11:00:12 2015 +0200 @@ -139,6 +139,9 @@ public Method copyMethod(Method arg) { return arg.copy(); } + public Method leafCopyMethod(Method arg) { + return arg.leafCopy(); + } public Field copyField(Field arg) { return arg.copy();
--- a/jdk/src/java.base/share/classes/sun/reflect/LangReflectAccess.java Wed Jul 08 23:26:48 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/reflect/LangReflectAccess.java Mon Aug 24 11:00:12 2015 +0200 @@ -104,6 +104,9 @@ /** Makes a "child" copy of a Method */ public Method copyMethod(Method arg); + /** Makes a copy of this non-root a Method */ + public Method leafCopyMethod(Method arg); + /** Makes a "child" copy of a Field */ public Field copyField(Field arg);
--- a/jdk/src/java.base/share/classes/sun/reflect/ReflectionFactory.java Wed Jul 08 23:26:48 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/reflect/ReflectionFactory.java Mon Aug 24 11:00:12 2015 +0200 @@ -302,6 +302,14 @@ return langReflectAccess().copyMethod(arg); } + /** Makes a copy of the passed method. The returned method is NOT + * a "child" but a "sibling" of the Method in arg. Should only be + * used on non-root methods. */ + public Method leafCopyMethod(Method arg) { + return langReflectAccess().leafCopyMethod(arg); + } + + /** Makes a copy of the passed field. The returned field is a "child" of the passed one; see the comments in Field.java for details. */
--- a/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationSupport.java Wed Jul 08 23:26:48 2015 -0700 +++ b/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationSupport.java Mon Aug 24 11:00:12 2015 +0200 @@ -27,14 +27,17 @@ import java.lang.annotation.*; import java.lang.reflect.*; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import sun.misc.JavaLangAccess; +import sun.reflect.LangReflectAccess; +import sun.reflect.ReflectionFactory; public final class AnnotationSupport { private static final JavaLangAccess LANG_ACCESS = sun.misc.SharedSecrets.getJavaLangAccess(); @@ -62,7 +65,7 @@ public static <A extends Annotation> A[] getDirectlyAndIndirectlyPresent( Map<Class<? extends Annotation>, Annotation> annotations, Class<A> annoClass) { - List<A> result = new ArrayList<A>(); + List<A> result = new ArrayList<>(); @SuppressWarnings("unchecked") A direct = (A) annotations.get(annoClass); @@ -188,27 +191,68 @@ AnnotationType annoType = AnnotationType.getInstance(containerClass); if (annoType == null) throw invalidContainerException(container, null); - Method m = annoType.members().get("value"); if (m == null) throw invalidContainerException(container, null); - m.setAccessible(true); + if (Proxy.isProxyClass(container.getClass())) { + // Invoke by invocation handler + InvocationHandler handler = Proxy.getInvocationHandler(container); - // This will erase to (Annotation[]) but we do a runtime cast on the - // return-value in the method that call this method. - @SuppressWarnings("unchecked") - A[] values = (A[]) m.invoke(container); + try { + // This will erase to (Annotation[]) but we do a runtime cast on the + // return-value in the method that call this method. + @SuppressWarnings("unchecked") + A[] values = (A[]) handler.invoke(container, m, null); + return values; + } catch (Throwable t) { // from InvocationHandler::invoke + throw invalidContainerException(container, t); + } + } else { + // In theory there might be instances of Annotations that are not + // implemented using Proxies. Try to invoke the "value" element with + // reflection. - return values; + // Declaring class should be an annotation type + Class<?> iface = m.getDeclaringClass(); + if (!iface.isAnnotation()) + throw new UnsupportedOperationException("Unsupported container annotation type."); + // Method must be public + if (!Modifier.isPublic(m.getModifiers())) + throw new UnsupportedOperationException("Unsupported value member."); + // Interface might not be public though + final Method toInvoke; + if (!Modifier.isPublic(iface.getModifiers())) { + if (System.getSecurityManager() != null) { + toInvoke = AccessController.doPrivileged(new PrivilegedAction<Method>() { + @Override + public Method run() { + Method res = ReflectionFactory.getReflectionFactory().leafCopyMethod(m); + res.setAccessible(true); + return res; + } + }); + } else { + toInvoke = ReflectionFactory.getReflectionFactory().leafCopyMethod(m); + toInvoke.setAccessible(true); + } + } else { + toInvoke = m; + } + + // This will erase to (Annotation[]) but we do a runtime cast on the + // return-value in the method that call this method. + @SuppressWarnings("unchecked") + A[] values = (A[]) toInvoke.invoke(container); + + return values; + } } catch (IllegalAccessException | // couldn't loosen security IllegalArgumentException | // parameters doesn't match InvocationTargetException | // the value method threw an exception ClassCastException e) { - throw invalidContainerException(container, e); - } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/annotation/repeatingAnnotations/CustomRepeatingWithSecurityManager.java Mon Aug 24 11:00:12 2015 +0200 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8073056 + * @summary Repeating annotations throws java.security.AccessControlException with a SecurityManager + * + * @library /lib/testlibrary + * @build jdk.testlibrary.Asserts + * @run main CustomRepeatingWithSecurityManager + * @run main/othervm CustomRepeatingWithSecurityManager "withSM" + */ + +import java.lang.annotation.*; +import java.lang.reflect.*; + +import jdk.testlibrary.Asserts; + +public class CustomRepeatingWithSecurityManager { + public static void main(String[] args) throws Exception { + if (args.length == 1) { + SecurityManager sm = new SecurityManager(); + System.setSecurityManager(sm); + } + + Asserts.assertTrue(new CustomAnnotations().getAnnotationsByType(MyAnnotation.class).length == 2, + "Array should contain 2 annotations"); + Asserts.assertEquals(new CustomAnnotations().getAnnotationsByType(MyAnnotation.class)[1].name(), + "Bar", "Should be 'Bar'"); + } + + static class CustomAnnotations implements AnnotatedElement { + @Override + public Annotation[] getDeclaredAnnotations() { + Annotation[] res = new Annotation[1]; + res[0] = new MyAnnotationsImpl(); + return res; + } + + @Override + public Annotation[] getAnnotations() { + return getDeclaredAnnotations(); + } + + @Override + public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { + return null; + } + } + + static class MyAnnotationsImpl implements MyAnnotations { + public MyAnnotation[] value() { + MyAnnotation[] res = new MyAnnotation[2]; + res[0] = new MyAnnotationImpl("Foo"); + res[1] = new MyAnnotationImpl("Bar"); + return res; + } + + @Override + public Class<? extends Annotation> annotationType() { + return MyAnnotations.class; + } + } + + static class MyAnnotationImpl implements MyAnnotation { + private String val; + MyAnnotationImpl(String val) { + this.val = val; + } + + public String name() { return val; } + + @Override + public Class<? extends Annotation> annotationType() { + return MyAnnotations.class; + } + } + + @Retention(RetentionPolicy.RUNTIME) + @interface MyAnnotations { + MyAnnotation[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Repeatable(MyAnnotations.class) + @interface MyAnnotation { + String name(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/annotation/repeatingAnnotations/RepeatingWithSecurityManager.java Mon Aug 24 11:00:12 2015 +0200 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8073056 + * @summary Repeating annotations throws java.security.AccessControlException with a SecurityManager + * + * @library /lib/testlibrary + * @build jdk.testlibrary.Asserts + * @run main RepeatingWithSecurityManager + * @run main/othervm RepeatingWithSecurityManager "withSM" + */ + +import java.lang.annotation.*; +import java.util.*; + +import jdk.testlibrary.Asserts; + +public class RepeatingWithSecurityManager { + public static void main(String[] args) throws Exception { + if (args.length == 1) { + SecurityManager sm = new SecurityManager(); + System.setSecurityManager(sm); + } + + Asserts.assertTrue(TwoAnnotations.class.getAnnotationsByType(MyAnnotation.class).length == 2, + "Array should contain 2 annotations: " + + Arrays.toString(TwoAnnotations.class.getAnnotationsByType(MyAnnotation.class))); + } + + @MyAnnotation(name = "foo") + @MyAnnotation(name = "bar") + private static class TwoAnnotations { + } + + @Retention(RetentionPolicy.RUNTIME) + @interface MyAnnotations { + MyAnnotation[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Repeatable(MyAnnotations.class) + @interface MyAnnotation { + String name(); + } +}