提交 bfeb9cc9 编写于 作者: M mbalao

8229366: JFR backport allows unchecked writing to memory

Reviewed-by: jbachorik
上级 0366f169
...@@ -63,10 +63,11 @@ final class EventHandlerCreator { ...@@ -63,10 +63,11 @@ final class EventHandlerCreator {
private static final String FIELD_EVENT_TYPE = "platformEventType"; private static final String FIELD_EVENT_TYPE = "platformEventType";
private static final String FIELD_PREFIX_STRING_POOL = "stringPool"; private static final String FIELD_PREFIX_STRING_POOL = "stringPool";
private final static Class<? extends EventHandler> eventHandlerProxy = EventHandlerProxyCreator.proxyClass;
private final static Type TYPE_STRING_POOL = Type.getType(StringPool.class); private final static Type TYPE_STRING_POOL = Type.getType(StringPool.class);
private final static Type TYPE_EVENT_WRITER = Type.getType(EventWriter.class); private final static Type TYPE_EVENT_WRITER = Type.getType(EventWriter.class);
private final static Type TYPE_PLATFORM_EVENT_TYPE = Type.getType(PlatformEventType.class); private final static Type TYPE_PLATFORM_EVENT_TYPE = Type.getType(PlatformEventType.class);
private final static Type TYPE_EVENT_HANDLER = Type.getType(EventHandler.class); private final static Type TYPE_EVENT_HANDLER = Type.getType(eventHandlerProxy);
private final static Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class); private final static Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class);
private final static Type TYPE_EVENT_TYPE = Type.getType(EventType.class); private final static Type TYPE_EVENT_TYPE = Type.getType(EventType.class);
private final static Type TYPE_EVENT_CONTROL = Type.getType(EventControl.class); private final static Type TYPE_EVENT_CONTROL = Type.getType(EventControl.class);
...@@ -90,7 +91,7 @@ final class EventHandlerCreator { ...@@ -90,7 +91,7 @@ final class EventHandlerCreator {
} }
public static String makeEventHandlerName(long id) { public static String makeEventHandlerName(long id) {
return EventHandler.class.getName() + id + SUFFIX; return eventHandlerProxy.getName() + id + SUFFIX;
} }
public EventHandlerCreator(long id, List<SettingInfo> settingInfos, EventType type, Class<? extends Event> eventClass) { public EventHandlerCreator(long id, List<SettingInfo> settingInfos, EventType type, Class<? extends Event> eventClass) {
...@@ -168,7 +169,7 @@ final class EventHandlerCreator { ...@@ -168,7 +169,7 @@ final class EventHandlerCreator {
mv.visitVarInsn(Opcodes.ILOAD, 1); // registered mv.visitVarInsn(Opcodes.ILOAD, 1); // registered
mv.visitVarInsn(Opcodes.ALOAD, 2); // event type mv.visitVarInsn(Opcodes.ALOAD, 2); // event type
mv.visitVarInsn(Opcodes.ALOAD, 3); // event control mv.visitVarInsn(Opcodes.ALOAD, 3); // event control
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(EventHandler.class), METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), METHOD_EVENT_HANDLER_CONSTRUCTOR.getDescriptor(), false); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(eventHandlerProxy), METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), METHOD_EVENT_HANDLER_CONSTRUCTOR.getDescriptor(), false);
for (SettingInfo si : settingInfos) { for (SettingInfo si : settingInfos) {
mv.visitVarInsn(Opcodes.ALOAD, 0); // this mv.visitVarInsn(Opcodes.ALOAD, 0); // this
mv.visitVarInsn(Opcodes.ALOAD, si.index + 4); // Setting Control mv.visitVarInsn(Opcodes.ALOAD, si.index + 4); // Setting Control
...@@ -180,7 +181,7 @@ final class EventHandlerCreator { ...@@ -180,7 +181,7 @@ final class EventHandlerCreator {
if (field.isString()) { if (field.isString()) {
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(EventHandler.class), "createStringFieldWriter", "()" + TYPE_STRING_POOL.getDescriptor(), false); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(eventHandlerProxy), "createStringFieldWriter", "()" + TYPE_STRING_POOL.getDescriptor(), false);
mv.visitFieldInsn(Opcodes.PUTFIELD, internalClassName, FIELD_PREFIX_STRING_POOL + fieldIndex, TYPE_STRING_POOL.getDescriptor()); mv.visitFieldInsn(Opcodes.PUTFIELD, internalClassName, FIELD_PREFIX_STRING_POOL + fieldIndex, TYPE_STRING_POOL.getDescriptor());
} }
fieldIndex++; fieldIndex++;
...@@ -191,7 +192,7 @@ final class EventHandlerCreator { ...@@ -191,7 +192,7 @@ final class EventHandlerCreator {
} }
private void buildClassInfo() { private void buildClassInfo() {
String internalSuperName = ASMToolkit.getInternalName(EventHandler.class.getName()); String internalSuperName = ASMToolkit.getInternalName(eventHandlerProxy.getName());
classWriter.visit(CLASS_VERSION, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null); classWriter.visit(CLASS_VERSION, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null);
for (SettingInfo si : settingInfos) { for (SettingInfo si : settingInfos) {
classWriter.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor(), null, null); classWriter.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor(), null, null);
......
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, Red Hat, Inc.
*
* 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.jfr.internal;
import java.util.StringJoiner;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.Method;
import jdk.jfr.Event;
import jdk.jfr.EventType;
import jdk.jfr.internal.handlers.EventHandler;
/*
* Generates an EventHandler subclass dynamically, named EventHandlerProxy.
* EventHandlerProxy is located in a publicly accessible package (jdk.jfr)
* and is used as a superclass for classes generated by EventHandlerCreator.
* The rationale behind this scheme is to block access to jdk.jfr.internal
* package and sub-packages when there is a SecurityManager installed, while
* allowing application-defined event classes to invoke the required internal
* APIs.
*/
final class EventHandlerProxyCreator {
private static final int CLASS_VERSION = 52;
private final static Type TYPE_EVENT_TYPE = Type.getType(EventType.class);
private final static Type TYPE_EVENT_CONTROL = Type.getType(EventControl.class);
private final static String DESCRIPTOR_EVENT_HANDLER = "(" + Type.BOOLEAN_TYPE.getDescriptor() + TYPE_EVENT_TYPE.getDescriptor() + TYPE_EVENT_CONTROL.getDescriptor() + ")V";
private final static Method METHOD_EVENT_HANDLER_CONSTRUCTOR = new Method("<init>", DESCRIPTOR_EVENT_HANDLER);
private final static String DESCRIPTOR_TIME_STAMP = "()" + Type.LONG_TYPE.getDescriptor();
private final static Method METHOD_TIME_STAMP = new Method("timestamp", DESCRIPTOR_TIME_STAMP);
private final static String DESCRIPTOR_DURATION = "(" + Type.LONG_TYPE.getDescriptor() + ")" + Type.LONG_TYPE.getDescriptor();
private final static Method METHOD_DURATION = new Method("duration", DESCRIPTOR_DURATION);
private final static ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
private final static String className = "jdk.jfr.proxy.internal.EventHandlerProxy";
private final static String internalClassName = ASMToolkit.getInternalName(className);
// Create the Proxy class instance after all the previous static fields were initialized (textual order)
static final Class<? extends EventHandler> proxyClass = EventHandlerProxyCreator.makeEventHandlerProxyClass();
static void ensureInitialized() {
// trigger clinit which will setup the EventHandlerProxy class.
}
public static Class<? extends EventHandler> makeEventHandlerProxyClass() {
buildClassInfo();
buildConstructor();
buildTimestampMethod();
buildDurationMethod();
byte[] bytes = classWriter.toByteArray();
ASMToolkit.logASM(className, bytes);
return SecuritySupport.defineClass(className, bytes, Event.class.getClassLoader()).asSubclass(EventHandler.class);
}
private static void buildConstructor() {
MethodVisitor mv = classWriter.visitMethod(0x0, METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), makeConstructorDescriptor(), null, null);
mv.visitVarInsn(Opcodes.ALOAD, 0); // this
mv.visitVarInsn(Opcodes.ILOAD, 1); // registered
mv.visitVarInsn(Opcodes.ALOAD, 2); // event type
mv.visitVarInsn(Opcodes.ALOAD, 3); // event control
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(EventHandler.class), METHOD_EVENT_HANDLER_CONSTRUCTOR.getName(), METHOD_EVENT_HANDLER_CONSTRUCTOR.getDescriptor(), false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private static void buildClassInfo() {
String internalSuperName = ASMToolkit.getInternalName(EventHandler.class.getName());
classWriter.visit(CLASS_VERSION, Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null);
}
private static void buildTimestampMethod() {
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), null, null);
mv.visitCode();
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(EventHandler.class), METHOD_TIME_STAMP.getName(), METHOD_TIME_STAMP.getDescriptor(), false);
mv.visitInsn(Opcodes.LRETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private static void buildDurationMethod() {
MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, METHOD_DURATION.getName(), METHOD_DURATION.getDescriptor(), null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.LLOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(EventHandler.class), METHOD_DURATION.getName(), METHOD_DURATION.getDescriptor(), false);
mv.visitInsn(Opcodes.LRETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private static String makeConstructorDescriptor() {
StringJoiner constructordescriptor = new StringJoiner("", "(", ")V");
constructordescriptor.add(Type.BOOLEAN_TYPE.getDescriptor());
constructordescriptor.add(Type.getType(EventType.class).getDescriptor());
constructordescriptor.add(Type.getType(EventControl.class).getDescriptor());
return constructordescriptor.toString();
}
}
...@@ -97,10 +97,11 @@ public final class EventInstrumentation { ...@@ -97,10 +97,11 @@ public final class EventInstrumentation {
static final String FIELD_EVENT_HANDLER = "eventHandler"; static final String FIELD_EVENT_HANDLER = "eventHandler";
static final String FIELD_START_TIME = "startTime"; static final String FIELD_START_TIME = "startTime";
private static final Class<? extends EventHandler> eventHandlerProxy = EventHandlerProxyCreator.proxyClass;
private static final Type ANNOTATION_TYPE_NAME = Type.getType(Name.class); private static final Type ANNOTATION_TYPE_NAME = Type.getType(Name.class);
private static final Type ANNOTATION_TYPE_REGISTERED = Type.getType(Registered.class); private static final Type ANNOTATION_TYPE_REGISTERED = Type.getType(Registered.class);
private static final Type ANNOTATION_TYPE_ENABLED = Type.getType(Enabled.class); private static final Type ANNOTATION_TYPE_ENABLED = Type.getType(Enabled.class);
private static final Type TYPE_EVENT_HANDLER = Type.getType(EventHandler.class); private static final Type TYPE_EVENT_HANDLER = Type.getType(eventHandlerProxy);
private static final Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class); private static final Type TYPE_SETTING_CONTROL = Type.getType(SettingControl.class);
private static final Method METHOD_COMMIT = new Method("commit", Type.VOID_TYPE, new Type[0]); private static final Method METHOD_COMMIT = new Method("commit", Type.VOID_TYPE, new Type[0]);
private static final Method METHOD_BEGIN = new Method("begin", Type.VOID_TYPE, new Type[0]); private static final Method METHOD_BEGIN = new Method("begin", Type.VOID_TYPE, new Type[0]);
...@@ -408,7 +409,7 @@ public final class EventInstrumentation { ...@@ -408,7 +409,7 @@ public final class EventInstrumentation {
// eventHandler.write(...); // eventHandler.write(...);
// } // }
methodVisitor.visitJumpInsn(Opcodes.IFEQ, end); methodVisitor.visitJumpInsn(Opcodes.IFEQ, end);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
for (FieldInfo fi : fieldInfos) { for (FieldInfo fi : fieldInfos) {
...@@ -427,7 +428,7 @@ public final class EventInstrumentation { ...@@ -427,7 +428,7 @@ public final class EventInstrumentation {
updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> { updateMethod(METHOD_EVENT_SHOULD_COMMIT, methodVisitor -> {
Label fail = new Label(); Label fail = new Label();
// if (!eventHandler.shoouldCommit(duration) goto fail; // if (!eventHandler.shoouldCommit(duration) goto fail;
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J"); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, getInternalClassName(), FIELD_DURATION, "J");
ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_EVENT_HANDLER_SHOULD_COMMIT); ASMToolkit.invokeVirtual(methodVisitor, TYPE_EVENT_HANDLER.getInternalName(), METHOD_EVENT_HANDLER_SHOULD_COMMIT);
...@@ -435,7 +436,7 @@ public final class EventInstrumentation { ...@@ -435,7 +436,7 @@ public final class EventInstrumentation {
for (SettingInfo si : settingInfos) { for (SettingInfo si : settingInfos) {
// if (!settingsMethod(eventHandler.settingX)) goto fail; // if (!settingsMethod(eventHandler.settingX)) goto fail;
methodVisitor.visitIntInsn(Opcodes.ALOAD, 0); methodVisitor.visitIntInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(EventHandler.class)); methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, getInternalClassName(), FIELD_EVENT_HANDLER, Type.getDescriptor(eventHandlerProxy));
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, eventHandlerXInternalName);
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, eventHandlerXInternalName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor()); methodVisitor.visitFieldInsn(Opcodes.GETFIELD, eventHandlerXInternalName, si.fieldName, TYPE_SETTING_CONTROL.getDescriptor());
methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, si.internalSettingName); methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, si.internalSettingName);
......
...@@ -54,6 +54,7 @@ public final class JVM { ...@@ -54,6 +54,7 @@ public final class JVM {
// subscribeLogLevel(tag, tag.id); // subscribeLogLevel(tag, tag.id);
// } // }
Options.ensureInitialized(); Options.ensureInitialized();
EventHandlerProxyCreator.ensureInitialized();
} }
/** /**
......
...@@ -45,7 +45,13 @@ public abstract class EventHandler { ...@@ -45,7 +45,13 @@ public abstract class EventHandler {
private final EventControl eventControl; private final EventControl eventControl;
// Accessed by generated sub class // Accessed by generated sub class
EventHandler(boolean registered, EventType eventType, EventControl eventControl) { protected EventHandler(boolean registered, EventType eventType, EventControl eventControl) {
if (System.getSecurityManager() != null) {
// Do not allow user subclasses when security is enforced.
if (EventHandler.class.getClassLoader() != this.getClass().getClassLoader()) {
throw new SecurityException("Illegal subclass");
}
}
this.eventType = eventType; this.eventType = eventType;
this.platformEventType = PrivateAccess.getInstance().getPlatformEventType(eventType); this.platformEventType = PrivateAccess.getInstance().getPlatformEventType(eventType);
this.eventControl = eventControl; this.eventControl = eventControl;
......
...@@ -223,7 +223,10 @@ package.access=sun.,\ ...@@ -223,7 +223,10 @@ package.access=sun.,\
jdk.nashorn.internal.,\ jdk.nashorn.internal.,\
jdk.nashorn.tools.,\ jdk.nashorn.tools.,\
jdk.xml.internal.,\ jdk.xml.internal.,\
com.sun.activation.registries. com.sun.activation.registries.,\
jdk.jfr.events.,\
jdk.jfr.internal.,\
jdk.management.jfr.internal.
# #
# List of comma-separated packages that start with or equal this string # List of comma-separated packages that start with or equal this string
......
...@@ -223,7 +223,10 @@ package.access=sun.,\ ...@@ -223,7 +223,10 @@ package.access=sun.,\
jdk.nashorn.internal.,\ jdk.nashorn.internal.,\
jdk.nashorn.tools.,\ jdk.nashorn.tools.,\
jdk.xml.internal.,\ jdk.xml.internal.,\
com.sun.activation.registries. com.sun.activation.registries.,\
jdk.jfr.events.,\
jdk.jfr.internal.,\
jdk.management.jfr.internal.
# #
# List of comma-separated packages that start with or equal this string # List of comma-separated packages that start with or equal this string
......
...@@ -225,7 +225,10 @@ package.access=sun.,\ ...@@ -225,7 +225,10 @@ package.access=sun.,\
jdk.nashorn.tools.,\ jdk.nashorn.tools.,\
jdk.xml.internal.,\ jdk.xml.internal.,\
com.sun.activation.registries.,\ com.sun.activation.registries.,\
apple. apple.,\
jdk.jfr.events.,\
jdk.jfr.internal.,\
jdk.management.jfr.internal.
# #
# List of comma-separated packages that start with or equal this string # List of comma-separated packages that start with or equal this string
......
...@@ -225,7 +225,10 @@ package.access=sun.,\ ...@@ -225,7 +225,10 @@ package.access=sun.,\
jdk.nashorn.internal.,\ jdk.nashorn.internal.,\
jdk.nashorn.tools.,\ jdk.nashorn.tools.,\
jdk.xml.internal.,\ jdk.xml.internal.,\
com.sun.activation.registries. com.sun.activation.registries.,\
jdk.jfr.events.,\
jdk.jfr.internal.,\
jdk.management.jfr.internal.
# #
# List of comma-separated packages that start with or equal this string # List of comma-separated packages that start with or equal this string
......
...@@ -225,7 +225,10 @@ package.access=sun.,\ ...@@ -225,7 +225,10 @@ package.access=sun.,\
jdk.nashorn.tools.,\ jdk.nashorn.tools.,\
jdk.xml.internal.,\ jdk.xml.internal.,\
com.sun.activation.registries.,\ com.sun.activation.registries.,\
com.sun.java.accessibility. com.sun.java.accessibility.,\
jdk.jfr.events.,\
jdk.jfr.internal.,\
jdk.management.jfr.internal.
# #
# List of comma-separated packages that start with or equal this string # List of comma-separated packages that start with or equal this string
......
/*
* Copyright (c) 2019, Red Hat, Inc.
*
* 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.
*/
import java.io.File;
import java.io.FilePermission;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.AccessControlContext;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Comparator;
import jdk.jfr.AnnotationElement;
import jdk.jfr.Configuration;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.Event;
import jdk.jfr.EventFactory;
import jdk.jfr.EventType;
import jdk.jfr.FlightRecorder;
import jdk.jfr.FlightRecorderListener;
import jdk.jfr.FlightRecorderPermission;
import jdk.jfr.internal.JVM;
import jdk.jfr.Label;
import jdk.jfr.Name;
import jdk.jfr.Recording;
import jdk.jfr.ValueDescriptor;
/*
* @test
* @requires (jdk.version.major >= 8)
* @run main/othervm/timeout=30 -XX:+FlightRecorder -XX:StartFlightRecording JFRSecurityTestSuite
* @author Martin Balao (mbalao@redhat.com)
*/
public class JFRSecurityTestSuite {
private static boolean DEBUG = true;
private static SecurityManager sm;
private static String failureMessage = null;
private static Path jfrTmpDirPath = null;
private static Path recFilePath = null;
interface F {
void call() throws Exception;
}
@Label("MyEvent")
static class MyEvent extends Event {
}
@Label("MyEvent2")
static class MyEvent2 extends Event {
}
@Label("MyEvent3")
static class MyEvent3 extends Event {
}
public static class MyPolicy extends Policy {
public MyPolicy() {
super();
}
@Override
public PermissionCollection getPermissions(ProtectionDomain domain) {
PermissionCollection perms = new Permissions();
perms.add(new AllPermission());
return perms;
}
}
private static class MyProtectionDomain extends ProtectionDomain {
MyProtectionDomain(CodeSource source, Permissions permissions) {
super(source, permissions);
}
public boolean implies(Permission permission) {
if (DEBUG) {
System.out.println("Permission checked: " + permission);
}
return super.implies(permission);
}
}
private static final Runnable periodicEvent = new Runnable() {
@Override
public void run() {
if (DEBUG) {
System.out.println("Periodic event");
}
}
};
private static final FlightRecorderListener frl =
new FlightRecorderListener() { };
public static void main(String[] args) throws Throwable {
// Temporary directory for JFR files
jfrTmpDirPath = Files.createTempDirectory("jfr_test");
try {
File recFile = new File(jfrTmpDirPath.toString(), "rec");
recFile.createNewFile();
recFilePath = recFile.toPath();
if (DEBUG) {
System.out.println("Test JFR tmp directory: " + jfrTmpDirPath);
}
// Create a SecurityManager with all permissions enabled
Policy.setPolicy(new MyPolicy());
sm = new SecurityManager();
System.setSecurityManager(sm);
CodeSource source =
Thread.currentThread().getClass().
getProtectionDomain().getCodeSource();
// Create a constrained execution environment
Permissions noPermissions = new Permissions();
ProtectionDomain constrainedPD = new MyProtectionDomain(source,
noPermissions);
AccessControlContext constrainedContext =
new AccessControlContext(new ProtectionDomain[] {
constrainedPD });
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
try {
doInConstrainedEnvironment();
} catch (Throwable t) {
if (DEBUG) {
t.printStackTrace();
}
failureMessage = t.toString();
}
return null;
}
}, constrainedContext);
// Create a JFR execution environment
Permissions JFRPermissions = new Permissions();
JFRPermissions.add(new FilePermission(recFilePath.toString(),
"read,write"));
JFRPermissions.add(new FlightRecorderPermission(
"accessFlightRecorder"));
JFRPermissions.add(new FlightRecorderPermission(
"registerEvent"));
JFRPermissions.add(new RuntimePermission(
"accessDeclaredMembers"));
ProtectionDomain JFRPD = new MyProtectionDomain(source,
JFRPermissions);
AccessControlContext JFRContext =
new AccessControlContext(new ProtectionDomain[] { JFRPD });
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
try {
doInJFREnvironment();
} catch (Throwable t) {
if (DEBUG) {
t.printStackTrace();
}
failureMessage = t.toString();
}
return null;
}
}, JFRContext);
if (failureMessage != null) {
throw new Exception("TEST FAILED" + System.lineSeparator() +
failureMessage);
}
System.out.println("TEST PASS - OK");
} finally {
Files.walk(jfrTmpDirPath)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}
private static void doInConstrainedEnvironment() throws Throwable {
final String testPath = "/etc";
checkNoDirectAccess();
assertPermission(() -> {
FlightRecorder.getFlightRecorder();
}, FlightRecorderPermission.class, "accessFlightRecorder", false);
assertPermission(() -> {
FlightRecorder.register(MyEvent.class);
}, FlightRecorderPermission.class, "registerEvent", false);
assertPermission(() -> {
FlightRecorder.unregister(MyEvent.class);
}, FlightRecorderPermission.class, "registerEvent", false);
assertPermission(() -> {
FlightRecorder.addPeriodicEvent(MyEvent.class, periodicEvent);
}, FlightRecorderPermission.class, "registerEvent", false);
assertPermission(() -> {
FlightRecorder.removePeriodicEvent(periodicEvent);
}, FlightRecorderPermission.class, "registerEvent", false);
assertPermission(() -> {
FlightRecorder.addListener(frl);
}, FlightRecorderPermission.class, "accessFlightRecorder", false);
assertPermission(() -> {
FlightRecorder.removeListener(frl);
}, FlightRecorderPermission.class, "accessFlightRecorder", false);
assertPermission(() -> {
new MyEvent().commit();
}, FlightRecorderPermission.class, "registerEvent", true);
assertPermission(() -> {
Configuration.create(Paths.get(testPath));
}, FilePermission.class, testPath, false);
assertPermission(() -> {
EventFactory.create(new ArrayList<AnnotationElement>(),
new ArrayList<ValueDescriptor>());
}, FlightRecorderPermission.class, "registerEvent", false);
assertPermission(() -> {
new AnnotationElement(Name.class, "com.example.HelloWorld");
}, FlightRecorderPermission.class, "registerEvent", false);
assertPermission(() -> {
new ValueDescriptor(MyEvent.class, "",
new ArrayList<AnnotationElement>());
}, FlightRecorderPermission.class, "registerEvent", false);
assertPermission(() -> {
new Recording();
}, FlightRecorderPermission.class, "accessFlightRecorder", false);
assertPermission(() -> {
new RecordingFile(Paths.get(testPath));
}, FilePermission.class, testPath, false);
assertPermission(() -> {
RecordingFile.readAllEvents(Paths.get(testPath));
}, FilePermission.class, testPath, false);
assertPermission(() -> {
EventType.getEventType(MyEvent2.class);
}, FlightRecorderPermission.class, "registerEvent", true);
}
private static void doInJFREnvironment() throws Throwable {
checkNoDirectAccess();
FlightRecorder fr = FlightRecorder.getFlightRecorder();
Configuration c = Configuration.getConfiguration("default");
Recording recordingDefault = new Recording(c);
recordingDefault.start();
FlightRecorder.addListener(frl);
FlightRecorder.register(MyEvent3.class);
FlightRecorder.addPeriodicEvent(MyEvent3.class, periodicEvent);
new MyEvent3().commit();
FlightRecorder.removePeriodicEvent(periodicEvent);
FlightRecorder.unregister(MyEvent3.class);
FlightRecorder.removeListener(frl);
recordingDefault.stop();
try (Recording snapshot = fr.takeSnapshot()) {
if (snapshot.getSize() > 0) {
snapshot.setMaxSize(100_000_000);
snapshot.setMaxAge(Duration.ofMinutes(5));
snapshot.dump(recFilePath);
}
}
checkRecording();
Files.write(recFilePath, new byte[0],
StandardOpenOption.TRUNCATE_EXISTING);
recordingDefault.dump(recFilePath);
checkRecording();
try {
class MyEventHandler extends jdk.jfr.internal.handlers.EventHandler {
MyEventHandler() {
super(true, null, null);
}
}
MyEventHandler myEv = new MyEventHandler();
throw new Exception("EventHandler must not be subclassable");
} catch (SecurityException e) {}
}
private static void checkRecording() throws Throwable {
boolean myEvent3Found = false;
try (RecordingFile rf = new RecordingFile(recFilePath)) {
while (rf.hasMoreEvents()) {
RecordedEvent event = rf.readEvent();
if (event.getEventType().getName().equals(
"JFRSecurityTestSuite$MyEvent3")) {
if (DEBUG) {
System.out.println("Recording of MyEvent3 event:");
System.out.println(event);
}
myEvent3Found = true;
break;
}
}
}
if (!myEvent3Found) {
throw new Exception("MyEvent3 event was expected in JFR recording");
}
}
private static void checkNoDirectAccess() throws Throwable {
assertPermission(() -> {
sm.checkPackageAccess("jdk.jfr.internal");
}, RuntimePermission.class, null, false);
assertPermission(() -> {
Class.forName("jdk.jfr.internal.JVM");
}, RuntimePermission.class, null, false);
assertPermission(() -> {
JVM.getJVM();
}, RuntimePermission.class, null, false);
assertPermission(() -> {
sm.checkPackageAccess("jdk.jfr.events");
}, RuntimePermission.class, null, false);
assertPermission(() -> {
Class.forName("jdk.jfr.events.AbstractJDKEvent");
}, RuntimePermission.class, null, false);
assertPermission(() -> {
sm.checkPackageAccess("jdk.management.jfr.internal");
}, RuntimePermission.class, null, false);
}
private static void assertPermission(F f, Class<?> expectedPermClass,
String expectedPermName, boolean expectedIsCause)
throws Throwable {
String exceptionMessage = expectedPermClass.toString() +
(expectedPermName != null && !expectedPermName.isEmpty() ?
" " + expectedPermName : "") +
" permission check expected.";
try {
f.call();
throw new Exception(exceptionMessage);
} catch (Throwable t) {
Throwable t2 = null;
if (expectedIsCause) {
t2 = t.getCause();
} else {
t2 = t;
}
if (t2 instanceof AccessControlException) {
AccessControlException ace = (AccessControlException)t2;
Permission p = ace.getPermission();
if (!p.getClass().equals(expectedPermClass) ||
(expectedPermName != null && !expectedPermName.isEmpty() &&
!p.getName().equals(expectedPermName))) {
throw new Exception(exceptionMessage, ace);
}
} else {
throw t;
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册