# HG changeset patch # User rfield # Date 1354859755 28800 # Node ID 34956da26ceb8064cf5af355d9ad8c50f6871634 # Parent b31f1eaee88ae5263db3ebbaf9567422930bb65e 8003881: Prevent lambda implementing inner classes from allowing the creation of new instances Summary: Lambda implementing inner classes now has private constructor (thanks Kumar) Reviewed-by: ksrini diff -r b31f1eaee88a -r 34956da26ceb jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java --- a/jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Thu Dec 06 15:51:44 2012 -0500 +++ b/jdk/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Thu Dec 06 21:55:55 2012 -0800 @@ -25,15 +25,15 @@ package java.lang.invoke; -import java.io.FileOutputStream; -import java.io.IOException; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.security.ProtectionDomain; import java.util.concurrent.atomic.AtomicInteger; -import sun.util.logging.PlatformLogger; import jdk.internal.org.objectweb.asm.*; import static jdk.internal.org.objectweb.asm.Opcodes.*; import sun.misc.Unsafe; +import java.security.AccessController; +import java.security.PrivilegedAction; /** * InnerClassLambdaMetafactory @@ -120,13 +120,34 @@ * * @return a CallSite, which, when invoked, will return an instance of the * functional interface - * @throws ReflectiveOperationException + * @throws ReflectiveOperationException, LambdaConversionException */ @Override CallSite buildCallSite() throws ReflectiveOperationException, LambdaConversionException { final Class innerClass = spinInnerClass(); if (invokedType.parameterCount() == 0) { - return new ConstantCallSite(MethodHandles.constant(samBase, innerClass.newInstance())); + final Constructor[] ctrs = AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Constructor[] run() { + return innerClass.getDeclaredConstructors(); + } + }); + if (ctrs.length != 1) { + throw new ReflectiveOperationException("Expected one lambda constructor for " + + innerClass.getCanonicalName() + ", got " + ctrs.length); + } + // The lambda implementing inner class constructor is private, set + // it accessible (by us) before creating the constant sole instance + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + ctrs[0].setAccessible(true); + return null; + } + }); + Object inst = ctrs[0].newInstance(); + return new ConstantCallSite(MethodHandles.constant(samBase, inst)); } else { return new ConstantCallSite( MethodHandles.Lookup.IMPL_LOOKUP @@ -144,7 +165,7 @@ private Class spinInnerClass() throws LambdaConversionException { String samName = samBase.getName().replace('.', '/'); - cw.visit(CLASSFILE_VERSION, ACC_PUBLIC + ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL, + cw.visit(CLASSFILE_VERSION, ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL, isSerializable ? new String[]{samName, NAME_SERIALIZABLE} : new String[]{samName}); // Generate final fields to be filled in by constructor @@ -186,17 +207,27 @@ final byte[] classBytes = cw.toByteArray(); - if (System.getProperty("debug.dump.generated") != null) { + /*** Uncomment to dump the generated file System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName, classBytes.length); try (FileOutputStream fos = new FileOutputStream(lambdaClassName.replace('/', '.') + ".class")) { fos.write(classBytes); } catch (IOException ex) { - PlatformLogger.getLogger(InnerClassLambdaMetafactory.class.getName()).severe(ex.getMessage(), ex); + Logger.getLogger(InnerClassLambdaMetafactory.class.getName()).log(Level.SEVERE, null, ex); } - } + ***/ ClassLoader loader = targetClass.getClassLoader(); - ProtectionDomain pd = (loader == null) ? null : targetClass.getProtectionDomain(); + ProtectionDomain pd = (loader == null) + ? null + : AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public ProtectionDomain run() { + return targetClass.getProtectionDomain(); + } + } + ); + return (Class) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd); } @@ -205,7 +236,7 @@ */ private void generateConstructor() { // Generate constructor - MethodVisitor ctor = cw.visitMethod(ACC_PUBLIC, NAME_CTOR, constructorDesc, null, null); + MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR, constructorDesc, null, null); ctor.visitCode(); ctor.visitVarInsn(ALOAD, 0); ctor.visitMethodInsn(INVOKESPECIAL, NAME_MAGIC_ACCESSOR_IMPL, NAME_CTOR, METHOD_DESCRIPTOR_VOID); diff -r b31f1eaee88a -r 34956da26ceb jdk/test/java/lang/invoke/lambda/LambdaAccessControlDoPrivilegedTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/lambda/LambdaAccessControlDoPrivilegedTest.java Thu Dec 06 21:55:55 2012 -0800 @@ -0,0 +1,226 @@ +/* + * Copyright (c) 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 + * 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 8003881 + * @summary tests DoPrivileged action (implemented as lambda expressions) by + * inserting them into the BootClassPath. + * @compile -XDignore.symbol.file LambdaAccessControlDoPrivilegedTest.java + * @run main/othervm LambdaAccessControlDoPrivilegedTest + */ +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class LambdaAccessControlDoPrivilegedTest extends LUtils { + public static void main(String... args) { + final List scratch = new ArrayList(); + scratch.clear(); + scratch.add("import java.security.*;"); + scratch.add("public class DoPriv {"); + scratch.add("public static void main(String... args) {"); + scratch.add("String prop = AccessController.doPrivileged((PrivilegedAction) () -> {"); + scratch.add("return System.getProperty(\"user.home\");"); + scratch.add("});"); + scratch.add("}"); + scratch.add("}"); + File doprivJava = new File("DoPriv.java"); + File doprivClass = getClassFile(doprivJava); + createFile(doprivJava, scratch); + + scratch.clear(); + scratch.add("public class Bar {"); + scratch.add("public static void main(String... args) {"); + scratch.add("System.out.println(\"sun.boot.class.path\" + \"=\" +"); + scratch.add(" System.getProperty(\"sun.boot.class.path\", \"\"));"); + scratch.add("System.setSecurityManager(new SecurityManager());"); + scratch.add("DoPriv.main();"); + scratch.add("}"); + scratch.add("}"); + + File barJava = new File("Bar.java"); + File barClass = getClassFile(barJava); + createFile(barJava, scratch); + + String[] javacArgs = {barJava.getName(), doprivJava.getName()}; + compile(javacArgs); + File jarFile = new File("foo.jar"); + String[] jargs = {"cvf", jarFile.getName(), doprivClass.getName()}; + jarTool.run(jargs); + doprivJava.delete(); + doprivClass.delete(); + TestResult tr = doExec(JAVA_CMD.getAbsolutePath(), + "-Xbootclasspath/p:foo.jar", + "-cp", ".", "Bar"); + tr.assertZero("testDoPrivileged fails"); + barJava.delete(); + barClass.delete(); + jarFile.delete(); + } +} + +/* + * support infrastructure to invoke a java class from the command line + */ +class LUtils { + static final sun.tools.jar.Main jarTool = + new sun.tools.jar.Main(System.out, System.err, "jar-tool"); + static final com.sun.tools.javac.Main javac = + new com.sun.tools.javac.Main(); + static final File cwd = new File(".").getAbsoluteFile(); + static final String JAVAHOME = System.getProperty("java.home"); + static final boolean isWindows = + System.getProperty("os.name", "unknown").startsWith("Windows"); + //static final boolean isSDK = JAVAHOME.endsWith("jre"); + static final File JAVA_BIN_FILE = new File(JAVAHOME, "bin"); + static final File JAVA_CMD = new File(JAVA_BIN_FILE, + isWindows ? "java.exe" : "java"); + + protected LUtils() { + } + + public static void compile(String... args) { + if (javac.compile(args) != 0) { + throw new RuntimeException("compilation fails"); + } + } + + static void createFile(File outFile, List content) { + try { + Files.write(outFile.getAbsoluteFile().toPath(), content, + Charset.defaultCharset()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + static File getClassFile(File javaFile) { + return javaFile.getName().endsWith(".java") + ? new File(javaFile.getName().replace(".java", ".class")) + : null; + } + + static String getSimpleName(File inFile) { + String fname = inFile.getName(); + return fname.substring(0, fname.indexOf(".")); + } + + static TestResult doExec(String... cmds) { + return doExec(null, null, cmds); + } + + /* + * A method which executes a java cmd and returns the results in a container + */ + static TestResult doExec(Map envToSet, + java.util.Set envToRemove, String... cmds) { + String cmdStr = ""; + for (String x : cmds) { + cmdStr = cmdStr.concat(x + " "); + } + ProcessBuilder pb = new ProcessBuilder(cmds); + Map env = pb.environment(); + if (envToRemove != null) { + for (String key : envToRemove) { + env.remove(key); + } + } + if (envToSet != null) { + env.putAll(envToSet); + } + BufferedReader rdr = null; + try { + List outputList = new ArrayList<>(); + pb.redirectErrorStream(true); + Process p = pb.start(); + rdr = new BufferedReader(new InputStreamReader(p.getInputStream())); + String in = rdr.readLine(); + while (in != null) { + outputList.add(in); + in = rdr.readLine(); + } + p.waitFor(); + p.destroy(); + + return new TestResult(cmdStr, p.exitValue(), outputList, + env, new Throwable("current stack of the test")); + } catch (Exception ex) { + ex.printStackTrace(); + throw new RuntimeException(ex.getMessage()); + } + } + + static class TestResult { + String cmd; + int exitValue; + List testOutput; + Map env; + Throwable t; + + public TestResult(String str, int rv, List oList, + Map env, Throwable t) { + cmd = str; + exitValue = rv; + testOutput = oList; + this.env = env; + this.t = t; + } + + void assertZero(String message) { + if (exitValue != 0) { + System.err.println(this); + throw new RuntimeException(message); + } + } + + @Override + public String toString() { + StringWriter sw = new StringWriter(); + PrintWriter status = new PrintWriter(sw); + status.println("Cmd: " + cmd); + status.println("Return code: " + exitValue); + status.println("Environment variable:"); + for (String x : env.keySet()) { + status.println("\t" + x + "=" + env.get(x)); + } + status.println("Output:"); + for (String x : testOutput) { + status.println("\t" + x); + } + status.println("Exception:"); + status.println(t.getMessage()); + t.printStackTrace(status); + + return sw.getBuffer().toString(); + } + } +} diff -r b31f1eaee88a -r 34956da26ceb jdk/test/java/lang/invoke/lambda/LambdaAccessControlTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/lambda/LambdaAccessControlTest.java Thu Dec 06 21:55:55 2012 -0800 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 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 + * 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 8003881 + * @summary tests Lambda expression with a a security manager at top level + * @compile -XDignore.symbol.file LambdaAccessControlTest.java + * + * @run main/othervm LambdaAccessControlTest + */ + +public class LambdaAccessControlTest extends LUtils { + public static void main(String... args) { + System.setSecurityManager(new SecurityManager()); + JJ iii = (new CC())::impl; + System.out.printf(">>> %s\n", iii.foo(44)); + iii = DD::impl; + System.out.printf(">>> %s\n", iii.foo(44)); + return; + } +} +/* + * support classes for the test + */ +interface II { Object foo(T x); } +interface JJ extends II { } +class CC { String impl(int i) { return "impl:"+i; }} +class DD { static String impl(int i) { return "impl:"+i; }}