8157464: Disallow StackWalker.getCallerClass() be called by caller-sensitive method
Reviewed-by: bchristi, coleenp, dfuchs, sspitsyn
--- a/jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java Wed Sep 14 08:16:16 2016 -0400
+++ b/jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java Wed Sep 14 11:53:36 2016 -0700
@@ -71,6 +71,7 @@
// These flags must match the values maintained in the VM
@Native private static final int DEFAULT_MODE = 0x0;
@Native private static final int FILL_CLASS_REFS_ONLY = 0x2;
+ @Native private static final int GET_CALLER_CLASS = 0x4;
@Native private static final int SHOW_HIDDEN_FRAMES = 0x20; // LambdaForms are hidden by the VM
@Native private static final int FILL_LIVE_STACK_FRAMES = 0x100;
/*
@@ -614,9 +615,7 @@
private Class<?> caller;
CallerClassFinder(StackWalker walker) {
- super(walker, FILL_CLASS_REFS_ONLY);
- assert (mode & FILL_CLASS_REFS_ONLY) == FILL_CLASS_REFS_ONLY
- : "mode should contain FILL_CLASS_REFS_ONLY";
+ super(walker, FILL_CLASS_REFS_ONLY|GET_CALLER_CLASS);
}
final class ClassBuffer extends FrameBuffer<Class<?>> {
--- a/jdk/src/java.base/share/native/include/jvm.h Wed Sep 14 08:16:16 2016 -0400
+++ b/jdk/src/java.base/share/native/include/jvm.h Wed Sep 14 11:53:36 2016 -0700
@@ -179,6 +179,7 @@
*/
enum {
JVM_STACKWALK_FILL_CLASS_REFS_ONLY = 0x2,
+ JVM_STACKWALK_GET_CALLER_CLASS = 0x04,
JVM_STACKWALK_SHOW_HIDDEN_FRAMES = 0x20,
JVM_STACKWALK_FILL_LIVE_STACK_FRAMES = 0x100
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/CallerSensitiveMethod/Main.java Wed Sep 14 11:53:36 2016 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, 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 8157464
+ * @summary Basic test for StackWalker.getCallerClass()
+ * @library src
+ * @build java.base/java.util.CSM csm/*
+ * @run main/othervm csm/jdk.test.CallerSensitiveTest
+ * @run main/othervm csm/jdk.test.CallerSensitiveTest sm
+ */
+public class Main {
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/CallerSensitiveMethod/csm/jdk/test/CallerSensitiveTest.java Wed Sep 14 11:53:36 2016 -0700
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+package jdk.test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.CSM.Result;
+import java.util.function.Supplier;
+
+/**
+ * This test invokes StackWalker::getCallerClass via static reference,
+ * reflection, MethodHandle, lambda. Also verify that
+ * StackWalker::getCallerClass can't be called from @CallerSensitive method.
+ */
+public class CallerSensitiveTest {
+ private static final String NON_CSM_CALLER_METHOD = "getCallerClass";
+ private static final String CSM_CALLER_METHOD = "caller";
+
+ public static void main(String... args) throws Throwable {
+ boolean sm = false;
+ if (args.length > 0 && args[0].equals("sm")) {
+ sm = true;
+ PermissionCollection perms = new Permissions();
+ perms.add(new StackFramePermission("retainClassReference"));
+ Policy.setPolicy(new Policy() {
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission p) {
+ return perms.implies(p);
+ }
+ });
+ System.setSecurityManager(new SecurityManager());
+ }
+
+ System.err.format("Test %s security manager.%n",
+ sm ? "with" : "without");
+
+ CallerSensitiveTest cstest = new CallerSensitiveTest();
+ // test static call to java.util.CSM::caller and CSM::getCallerClass
+ cstest.staticMethodCall();
+ // test java.lang.reflect.Method call
+ cstest.reflectMethodCall();
+ // test java.lang.invoke.MethodHandle
+ cstest.invokeMethodHandle(Lookup1.lookup);
+ cstest.invokeMethodHandle(Lookup2.lookup);
+ // test method ref
+ cstest.lambda();
+
+ LambdaTest.lambda();
+
+ if (failed > 0) {
+ throw new RuntimeException(failed + " test cases failed.");
+ }
+ }
+
+ void staticMethodCall() {
+ java.util.CSM.caller();
+
+ Result result = java.util.CSM.getCallerClass();
+ checkNonCSMCaller(CallerSensitiveTest.class, result);
+ }
+
+ void reflectMethodCall() throws Throwable {
+ Method method1 = java.util.CSM.class.getMethod(CSM_CALLER_METHOD);
+ method1.invoke(null);
+
+ Method method2 = java.util.CSM.class.getMethod(NON_CSM_CALLER_METHOD);
+ Result result = (Result) method2.invoke(null);
+ checkNonCSMCaller(CallerSensitiveTest.class, result);
+ }
+
+ void invokeMethodHandle(Lookup lookup) throws Throwable {
+ MethodHandle mh1 = lookup.findStatic(java.util.CSM.class, CSM_CALLER_METHOD,
+ MethodType.methodType(Class.class));
+ Class<?> c = (Class<?>)mh1.invokeExact();
+
+ MethodHandle mh2 = lookup.findStatic(java.util.CSM.class, NON_CSM_CALLER_METHOD,
+ MethodType.methodType(Result.class));
+ Result result = (Result)mh2.invokeExact();
+ checkNonCSMCaller(CallerSensitiveTest.class, result);
+ }
+
+ void lambda() {
+ Result result = LambdaTest.getCallerClass.get();
+ checkNonCSMCaller(CallerSensitiveTest.class, result);
+
+ LambdaTest.caller.get();
+ }
+
+ static int failed = 0;
+
+ static void checkNonCSMCaller(Class<?> expected, Result result) {
+ if (result.callers.size() != 1) {
+ throw new RuntimeException("Expected result.callers contain one element");
+ }
+ if (expected != result.callers.get(0)) {
+ System.err.format("ERROR: Expected %s but got %s%n", expected,
+ result.callers);
+ result.frames.stream()
+ .forEach(f -> System.err.println(" " + f));
+ failed++;
+ }
+ }
+
+ static class Lookup1 {
+ static Lookup lookup = MethodHandles.lookup();
+ }
+
+ static class Lookup2 {
+ static Lookup lookup = MethodHandles.lookup();
+ }
+
+ static class LambdaTest {
+ static Supplier<Class<?>> caller = java.util.CSM::caller;
+ static Supplier<Result> getCallerClass = java.util.CSM::getCallerClass;
+
+ static void caller() {
+ caller.get();
+ }
+ static Result getCallerClass() {
+ return getCallerClass.get();
+ }
+
+ static void lambda() {
+ Result result = LambdaTest.getCallerClass();
+ checkNonCSMCaller(LambdaTest.class, result);
+
+ LambdaTest.caller();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/CallerSensitiveMethod/csm/module-info.java Wed Sep 14 11:53:36 2016 -0700
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+module csm {
+ exports jdk.test;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/StackWalker/CallerSensitiveMethod/src/java.base/java/util/CSM.java Wed Sep 14 11:53:36 2016 -0700
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+package java.util;
+
+import static java.lang.StackWalker.Option.*;
+import java.lang.StackWalker.StackFrame;
+import java.util.stream.Collectors;
+
+import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.Reflection;
+
+public class CSM {
+ private static StackWalker walker =
+ StackWalker.getInstance(EnumSet.of(RETAIN_CLASS_REFERENCE,
+ SHOW_HIDDEN_FRAMES,
+ SHOW_REFLECT_FRAMES));
+
+ public static class Result {
+ public final List<Class<?>> callers;
+ public final List<StackWalker.StackFrame> frames;
+ Result(List<Class<?>> callers,
+ List<StackFrame> frames) {
+ this.callers = callers;
+ this.frames = frames;
+ }
+ }
+
+ /**
+ * Returns the caller of this caller-sensitive method returned by
+ * by Reflection::getCallerClass.
+ *
+ * StackWalker::getCallerClass is expected to throw UOE
+ */
+ @CallerSensitive
+ public static Class<?> caller() {
+ Class<?> c1 = Reflection.getCallerClass();
+
+ try {
+ Class<?> c2 = walker.getCallerClass();
+ throw new RuntimeException("Exception not thrown by StackWalker::getCallerClass");
+ } catch (UnsupportedOperationException e) {}
+ return c1;
+ }
+
+ /**
+ * Returns the caller of this non-caller-sensitive method.
+ */
+ public static Result getCallerClass() {
+ Class<?> caller = walker.getCallerClass();
+ return new Result(List.of(caller), dump());
+ }
+
+ static List<StackFrame> dump() {
+ return walker.walk(s -> s.collect(Collectors.toList()));
+ }
+}