OpenJDK / jdk / hs
changeset 45027:0cf367e546fb
8020801: Apply the restriction of invoking MethodHandles.lookup to j.l.r.Method.invoke
Reviewed-by: plevart, psandoz
author | mchung |
---|---|
date | Mon, 08 May 2017 21:21:39 -0700 |
parents | bc9b3e93b6c5 |
children | b0ea3c0bfb81 |
files | jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java jdk/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java jdk/test/java/lang/invoke/lookup/ReflectiveLookupTest.java jdk/test/java/lang/invoke/lookup/java.base/java/lang/LookupTest.java |
diffstat | 4 files changed, 192 insertions(+), 18 deletions(-) [+] |
line wrap: on
line diff
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Sun May 07 19:01:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Mon May 08 21:21:39 2017 -0700 @@ -113,6 +113,19 @@ } /** + * This reflected$lookup method is the alternate implementation of + * the lookup method when being invoked by reflection. + */ + @CallerSensitive + private static Lookup reflected$lookup() { + Class<?> caller = Reflection.getCallerClass(); + if (caller.getClassLoader() == null) { + throw newIllegalArgumentException("illegal lookupClass: "+caller); + } + return new Lookup(caller); + } + + /** * Returns a {@link Lookup lookup object} which is trusted minimally. * The lookup has the {@code PUBLIC} and {@code UNCONDITIONAL} modes. * It can only be used to create method handles to public members of @@ -747,7 +760,7 @@ Lookup(Class<?> lookupClass) { this(lookupClass, FULL_POWER_MODES); // make sure we haven't accidentally picked up a privileged class: - checkUnprivilegedlookupClass(lookupClass, FULL_POWER_MODES); + checkUnprivilegedlookupClass(lookupClass); } private Lookup(Class<?> lookupClass, int allowedModes) { @@ -827,7 +840,7 @@ newModes = 0; } - checkUnprivilegedlookupClass(requestedLookupClass, newModes); + checkUnprivilegedlookupClass(requestedLookupClass); return new Lookup(requestedLookupClass, newModes); } @@ -979,25 +992,10 @@ */ static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, (PUBLIC|UNCONDITIONAL)); - private static void checkUnprivilegedlookupClass(Class<?> lookupClass, int allowedModes) { + private static void checkUnprivilegedlookupClass(Class<?> lookupClass) { String name = lookupClass.getName(); if (name.startsWith("java.lang.invoke.")) throw newIllegalArgumentException("illegal lookupClass: "+lookupClass); - - // For caller-sensitive MethodHandles.lookup() disallow lookup from - // restricted packages. This a fragile and blunt approach. - // TODO replace with a more formal and less fragile mechanism - // that does not bluntly restrict classes under packages within - // java.base from looking up MethodHandles or VarHandles. - if (allowedModes == FULL_POWER_MODES && lookupClass.getClassLoader() == null) { - if ((name.startsWith("java.") && - !name.equals("java.lang.Thread") && - !name.startsWith("java.util.concurrent.")) || - (name.startsWith("sun.") && - !name.startsWith("sun.invoke."))) { - throw newIllegalArgumentException("illegal lookupClass: " + lookupClass); - } - } } /**
--- a/jdk/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java Sun May 07 19:01:13 2017 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java Mon May 08 21:21:39 2017 -0700 @@ -135,6 +135,24 @@ return soleInstance; } + /** + * Returns an alternate reflective Method instance for the given method + * intended for reflection to invoke, if present. + * + * A trusted method can define an alternate implementation for a method `foo` + * by defining a method named "reflected$foo" that will be invoked + * reflectively. + */ + private static Method findMethodForReflection(Method method) { + String altName = "reflected$" + method.getName(); + try { + return method.getDeclaringClass() + .getDeclaredMethod(altName, method.getParameterTypes()); + } catch (NoSuchMethodException ex) { + return null; + } + } + //-------------------------------------------------------------------------- // // Routines used by java.lang.reflect @@ -161,6 +179,13 @@ public MethodAccessor newMethodAccessor(Method method) { checkInitted(); + if (Reflection.isCallerSensitive(method)) { + Method altMethod = findMethodForReflection(method); + if (altMethod != null) { + method = altMethod; + } + } + if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { return new MethodAccessorGenerator(). generateMethod(method.getDeclaringClass(),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/lookup/ReflectiveLookupTest.java Mon May 08 21:21:39 2017 -0700 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 8020801 + * @summary Restriction on reflective call to MethodHandles.lookup method + * @run main java.base/java.lang.LookupTest + * @run main ReflectiveLookupTest + * @run main/othervm -Dsun.reflect.noInflation=true ReflectiveLookupTest + */ + +import java.lang.invoke.*; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.Method; + +import static java.lang.invoke.MethodType.*; + +/* + * Lookup object can be obtained statically or reflectively. + */ +public class ReflectiveLookupTest { + public static void main(String... args) throws Throwable { + // Get a full power lookup + Lookup lookup1 = MethodHandles.lookup(); + MethodHandle mh1 = lookup1.findStatic(lookup1.lookupClass(), + "foo", + methodType(String.class)); + assertEquals((String) mh1.invokeExact(), foo()); + + Method lookupMethod = MethodHandles.class.getMethod("lookup"); + System.out.println("reflection method: " + lookupMethod); + if (!lookupMethod.getName().equals("lookup")) { + throw new RuntimeException("Unexpected name: " + lookupMethod.getName()); + } + + // Get a full power Lookup reflectively. + Lookup lookup2 = (Lookup) lookupMethod.invoke(null); + assertEquals(lookup1.lookupClass(), lookup2.lookupClass()); + assertEquals(lookup1.lookupModes(), lookup2.lookupModes()); + MethodHandle mh2 = lookup2.findStatic(lookup2.lookupClass(), + "foo", + methodType(String.class)); + assertEquals((String) mh2.invokeExact(), foo()); + } + + static String foo() { + return "foo!"; + } + + static void assertEquals(Object o1, Object o2) { + if (!o1.equals(o2)) { + throw new RuntimeException(o1 + " != " + o2); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/lookup/java.base/java/lang/LookupTest.java Mon May 08 21:21:39 2017 -0700 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package java.lang; + +import java.lang.invoke.*; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.InvocationTargetException; + +import static java.lang.invoke.MethodType.*; + +/* + * Verify that a Lookup object can be obtained statically from java.base + * but fails when it's obtained via reflection from java.base. + */ +public class LookupTest { + public static void main(String... args) throws Throwable { + // Get a full power lookup + Lookup lookup1 = MethodHandles.lookup(); + MethodHandle mh1 = lookup1.findStatic(lookup1.lookupClass(), + "foo", + methodType(String.class)); + assertEquals((String) mh1.invokeExact(), foo()); + + // access protected member + MethodHandle mh2 = lookup1.findVirtual(java.lang.ClassLoader.class, + "getPackage", + methodType(Package.class, String.class)); + ClassLoader loader = ClassLoader.getPlatformClassLoader(); + Package pkg = (Package)mh2.invokeExact(loader, "java.lang"); + assertEquals(pkg.getName(), "java.lang"); + + // MethodHandles.lookup will fail if it's called reflectively + try { + MethodHandles.class.getMethod("lookup").invoke(null); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof IllegalArgumentException)) { + throw e.getCause(); + } + } + } + + static String foo() { return "foo!"; } + + static void assertEquals(Object o1, Object o2) { + if (!o1.equals(o2)) { + throw new RuntimeException(o1 + " != " + o2); + } + } +}