8347287: JFR: Remove use of Security Manager

Reviewed-by: mgronlun
This commit is contained in:
Erik Gahlin 2025-01-10 13:46:57 +00:00
parent 12752b0031
commit ec7393e919
84 changed files with 546 additions and 1729 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, 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
@ -49,7 +49,7 @@ class JfrIntrinsicSupport : AllStatic {
#define JFR_TEMPLATES(template) \
template(jdk_jfr_internal_management_HiddenWait, "jdk/jfr/internal/management/HiddenWait") \
template(jdk_jfr_internal_JVM, "jdk/jfr/internal/JVM") \
template(jdk_jfr_internal_event_EventWriterFactory, "jdk/jfr/internal/event/EventWriterFactory") \
template(jdk_jfr_internal_event_EventWriter, "jdk/jfr/internal/event/EventWriter") \
template(jdk_jfr_internal_event_EventConfiguration_signature, "Ljdk/jfr/internal/event/EventConfiguration;") \
template(getEventWriter_signature, "()Ljdk/jfr/internal/event/EventWriter;") \
template(eventConfiguration_name, "eventConfiguration") \

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
@ -182,7 +182,7 @@ static inline const Method* ljf_sender_method(JavaThread* jt) {
return ljf.method();
}
static const char* const link_error_msg = "illegal access linking method 'jdk.jfr.internal.event.EventWriterFactory.getEventWriter(long)'";
static const char* const link_error_msg = "illegal access linking method 'jdk.jfr.internal.event.EventWriter.getEventWriter()'";
void JfrResolution::on_runtime_resolution(const CallInfo & info, TRAPS) {
assert(info.selected_method() != nullptr, "invariant");
@ -199,12 +199,12 @@ void JfrResolution::on_runtime_resolution(const CallInfo & info, TRAPS) {
if (method->name() != event_writer_method_name) {
return;
}
static const Symbol* const event_writer_factory_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriterFactory();
assert(event_writer_factory_klass_name != nullptr, "invariant");
if (info.resolved_klass()->name() != event_writer_factory_klass_name) {
static const Symbol* const event_writer_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriter();
assert(event_writer_klass_name != nullptr, "invariant");
if (info.resolved_klass()->name() != event_writer_klass_name) {
return;
}
// Attempting to link against jdk.jfr.internal.event.EventWriterFactory.getEventWriter().
// Attempting to link against jdk.jfr.internal.event.EventWriter.getEventWriter().
// The sender, i.e. the method attempting to link, is in the ljf (if one exists).
const Method* const sender = ljf_sender_method(THREAD);
if (sender == nullptr) {
@ -228,9 +228,9 @@ void JfrResolution::on_runtime_resolution(const CallInfo & info, TRAPS) {
}
static inline bool is_compiler_linking_event_writer(const Symbol* holder, const Symbol* name) {
static const Symbol* const event_writer_factory_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriterFactory();
assert(event_writer_factory_klass_name != nullptr, "invariant");
if (holder != event_writer_factory_klass_name) {
static const Symbol* const event_writer_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriter();
assert(event_writer_klass_name != nullptr, "invariant");
if (holder != event_writer_klass_name) {
return false;
}
static const Symbol* const event_writer_method_name = vmSymbols::getEventWriter_name();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -37,7 +37,6 @@ import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.TypeLibrary;
import jdk.jfr.internal.util.Utils;
@ -115,7 +114,6 @@ public final class AnnotationElement {
public AnnotationElement(Class<? extends Annotation> annotationType, Map<String, Object> values) {
Objects.requireNonNull(annotationType, "annotationType");
Objects.requireNonNull(values, "values");
SecuritySupport.checkRegisterPermission();
// copy values to avoid modification after validation
HashMap<String, Object> map = new HashMap<>(values);
for (Map.Entry<String, Object> entry : map.entrySet()) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -37,7 +37,6 @@ import jdk.internal.module.Checks;
import jdk.jfr.internal.EventClassBuilder;
import jdk.jfr.internal.JVMSupport;
import jdk.jfr.internal.MetadataRepository;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.util.Utils;
@ -99,8 +98,6 @@ public final class EventFactory {
Objects.requireNonNull(fields, "fields");
JVMSupport.ensureWithInternalError();
SecuritySupport.checkRegisterPermission();
List<AnnotationElement> sanitizedAnnotation = Utils.sanitizeNullFreeList(annotationElements, AnnotationElement.class);
List<ValueDescriptor> sanitizedFields = Utils.sanitizeNullFreeList(fields, ValueDescriptor.class);
Set<String> nameSet = HashSet.newHashSet(sanitizedFields.size());

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -29,8 +29,6 @@ import static jdk.jfr.internal.LogLevel.DEBUG;
import static jdk.jfr.internal.LogLevel.INFO;
import static jdk.jfr.internal.LogTag.JFR;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -44,7 +42,6 @@ import jdk.jfr.internal.Options;
import jdk.jfr.internal.PlatformRecorder;
import jdk.jfr.internal.PlatformRecording;
import jdk.jfr.internal.Repository;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.util.Utils;
import jdk.jfr.internal.periodic.PeriodicEvents;
@ -157,7 +154,6 @@ public final class FlightRecorder {
*/
public static FlightRecorder getFlightRecorder() throws IllegalStateException {
synchronized (PlatformRecorder.class) {
SecuritySupport.checkAccessFlightRecorder();
JVMSupport.ensureWithIllegalStateException();
if (platformRecorder == null) {
try {
@ -213,10 +209,7 @@ public final class FlightRecorder {
}
Utils.ensureValidEventSubclass(eventClass);
SecuritySupport.checkRegisterPermission();
@SuppressWarnings("removal")
AccessControlContext acc = AccessController.getContext();
PeriodicEvents.addUserEvent(acc, eventClass, hook);
PeriodicEvents.addJavaEvent(eventClass, hook);
}
/**
@ -227,7 +220,6 @@ public final class FlightRecorder {
*/
public static boolean removePeriodicEvent(Runnable hook) {
Objects.requireNonNull(hook, "hook");
SecuritySupport.checkRegisterPermission();
if (JVMSupport.isNotAvailable()) {
return false;
}
@ -260,7 +252,6 @@ public final class FlightRecorder {
*/
public static void addListener(FlightRecorderListener changeListener) {
Objects.requireNonNull(changeListener, "changeListener");
SecuritySupport.checkAccessFlightRecorder();
if (JVMSupport.isNotAvailable()) {
return;
}
@ -280,7 +271,6 @@ public final class FlightRecorder {
*/
public static boolean removeListener(FlightRecorderListener changeListener) {
Objects.requireNonNull(changeListener, "changeListener");
SecuritySupport.checkAccessFlightRecorder();
if (JVMSupport.isNotAvailable()) {
return false;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -25,7 +25,6 @@
package jdk.jfr;
import java.security.AccessControlContext;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -34,7 +33,6 @@ import jdk.jfr.internal.PlatformEventType;
import jdk.jfr.internal.PlatformRecorder;
import jdk.jfr.internal.PlatformRecording;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.management.EventSettingsModifier;
@ -156,12 +154,6 @@ public final class FlightRecorderPermission extends java.security.BasicPermissio
return FlightRecorder.getFlightRecorder().getInternal();
}
@SuppressWarnings("removal")
@Override
public AccessControlContext getContext(SettingControl settingControl) {
return settingControl.getContext();
}
@Override
public EventSettings newEventSettings(EventSettingsModifier esm) {
return new EventSettings.DelegatedEventSettings(esm);
@ -184,7 +176,7 @@ public final class FlightRecorderPermission extends java.security.BasicPermissio
*/
public FlightRecorderPermission(String name) {
super(Objects.requireNonNull(name, "name"));
if (!name.equals(SecuritySupport.ACCESS_FLIGHT_RECORDER) && !name.equals(SecuritySupport.REGISTER_EVENT)) {
if (!name.equals("accessFlightRecorder") && !name.equals("registerEvent")) {
throw new IllegalArgumentException("name: " + name);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -39,7 +39,7 @@ import jdk.jfr.internal.PlatformRecorder;
import jdk.jfr.internal.PlatformRecording;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.util.Utils;
import jdk.jfr.internal.WriteableUserPath;
import jdk.jfr.internal.WriteablePath;
/**
* Provides means to configure, start, stop and dump recording data to disk.
@ -368,7 +368,7 @@ public final class Recording implements Closeable {
*/
public void dump(Path destination) throws IOException {
Objects.requireNonNull(destination, "destination");
internal.dump(new WriteableUserPath(destination));
internal.dump(new WriteablePath(destination));
}
/**
@ -461,7 +461,7 @@ public final class Recording implements Closeable {
* @throws IOException if the path is not writable
*/
public void setDestination(Path destination) throws IOException {
internal.setDestination(destination != null ? new WriteableUserPath(destination) : null);
internal.setDestination(destination != null ? new WriteablePath(destination) : null);
}
/**
@ -471,11 +471,11 @@ public final class Recording implements Closeable {
* @return the destination file, or {@code null} if not set.
*/
public Path getDestination() {
WriteableUserPath usp = internal.getDestination();
if (usp == null) {
WriteablePath wp = internal.getDestination();
if (wp == null) {
return null;
} else {
return usp.getPotentiallyMaliciousOriginal();
return wp.getPath();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -25,12 +25,8 @@
package jdk.jfr;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.Set;
import jdk.jfr.internal.settings.JDKSettingControl;
/**
* Base class to extend to create setting controls.
* <p>
@ -77,30 +73,10 @@ import jdk.jfr.internal.settings.JDKSettingControl;
@MetadataDefinition
public abstract class SettingControl {
@SuppressWarnings("removal")
private final AccessControlContext context;
private final boolean initialized;
/**
* Constructor for invocation by subclass constructors.
*/
@SuppressWarnings("removal")
protected SettingControl() {
context = this instanceof JDKSettingControl ? null : AccessController.getContext();
initialized = true;
}
@SuppressWarnings("removal")
final AccessControlContext getContext() {
// Ensure object state is safe
if (!initialized) {
throw new InternalError("Object must be initialized before security context can be retrieved");
}
AccessControlContext c = this.context;
if (c == null && !(this instanceof JDKSettingControl)) {
throw new InternalError("Security context can only be null for trusted setting controls");
}
return c;
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -31,7 +31,6 @@ import java.util.List;
import java.util.Objects;
import jdk.jfr.internal.AnnotationConstruct;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.util.Utils;
@ -143,7 +142,6 @@ public final class ValueDescriptor {
Objects.requireNonNull(type, "type");
Objects.requireNonNull(name, "name");
Objects.requireNonNull(annotations, "annotations");
SecuritySupport.checkRegisterPermission();
if (!allowArray) {
if (type.isArray()) {
throw new IllegalArgumentException("Array types are not allowed");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, 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
@ -27,18 +27,14 @@ package jdk.jfr.consumer;
import java.io.IOException;
import java.nio.file.Path;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
import java.util.Objects;
import java.util.function.Consumer;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.consumer.EventDirectoryStream;
import jdk.jfr.internal.consumer.EventFileStream;
import jdk.jfr.internal.consumer.FileAccess;
/**
* Represents a stream of events.
@ -113,13 +109,9 @@ public interface EventStream extends AutoCloseable {
* @throws IOException if a stream can't be opened, or an I/O error occurs
* when trying to access the repository
*/
@SuppressWarnings("removal")
public static EventStream openRepository() throws IOException {
SecuritySupport.checkAccessFlightRecorder();
return new EventDirectoryStream(
AccessController.getContext(),
null,
SecuritySupport.PRIVILEGED,
null,
Collections.emptyList(),
false
@ -143,12 +135,8 @@ public interface EventStream extends AutoCloseable {
*/
public static EventStream openRepository(Path directory) throws IOException {
Objects.requireNonNull(directory, "directory");
@SuppressWarnings("removal")
AccessControlContext acc = AccessController.getContext();
return new EventDirectoryStream(
acc,
directory,
FileAccess.UNPRIVILEGED,
null,
Collections.emptyList(),
true
@ -169,10 +157,9 @@ public interface EventStream extends AutoCloseable {
* @throws IOException if the file can't be opened, or an I/O error occurs
* during reading
*/
@SuppressWarnings("removal")
static EventStream openFile(Path file) throws IOException {
Objects.requireNonNull(file, "file");
return new EventFileStream(AccessController.getContext(), file);
return new EventFileStream(file);
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -43,7 +43,6 @@ import jdk.jfr.internal.consumer.ChunkParser.ParserConfiguration;
import jdk.jfr.internal.consumer.ParserFilter;
import jdk.jfr.internal.consumer.ChunkHeader;
import jdk.jfr.internal.consumer.ChunkParser;
import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.ParserState;
import jdk.jfr.internal.consumer.RecordingInput;
import jdk.jfr.internal.consumer.filter.ChunkWriter;
@ -81,7 +80,7 @@ public final class RecordingFile implements Closeable {
public RecordingFile(Path file) throws IOException {
Objects.requireNonNull(file, "file");
this.file = file.toFile();
this.input = new RecordingInput(this.file, FileAccess.UNPRIVILEGED);
this.input = new RecordingInput(this.file);
this.chunkWriter = null;
findNext();
}
@ -146,7 +145,7 @@ public final class RecordingFile implements Closeable {
MetadataDescriptor previous = null;
List<EventType> types = new ArrayList<>();
HashSet<Long> foundIds = new HashSet<>();
try (RecordingInput ri = new RecordingInput(file, FileAccess.UNPRIVILEGED)) {
try (RecordingInput ri = new RecordingInput(file)) {
ChunkHeader ch = new ChunkHeader(ri);
aggregateEventTypeForChunk(ch, null, types, foundIds);
while (!ch.isLastChunk()) {
@ -162,7 +161,7 @@ public final class RecordingFile implements Closeable {
MetadataDescriptor previous = null;
List<Type> types = new ArrayList<>(200);
HashSet<Long> foundIds = HashSet.newHashSet(types.size());
try (RecordingInput ri = new RecordingInput(file, FileAccess.UNPRIVILEGED)) {
try (RecordingInput ri = new RecordingInput(file)) {
ChunkHeader ch = new ChunkHeader(ri);
ch.awaitFinished();
aggregateTypeForChunk(ch, null, types, foundIds);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, 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
@ -27,8 +27,6 @@ package jdk.jfr.consumer;
import java.io.IOException;
import java.nio.file.Path;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.time.Duration;
import java.time.Instant;
import java.util.Collections;
@ -45,7 +43,6 @@ import jdk.jfr.Recording;
import jdk.jfr.RecordingState;
import jdk.jfr.internal.PlatformRecording;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.util.Utils;
import jdk.jfr.internal.consumer.EventDirectoryStream;
import jdk.jfr.internal.management.StreamBarrier;
@ -98,18 +95,13 @@ public final class RecordingStream implements AutoCloseable, EventStream {
}
private RecordingStream(Map<String, String> settings) {
SecuritySupport.checkAccessFlightRecorder();
@SuppressWarnings("removal")
AccessControlContext acc = AccessController.getContext();
this.recording = new Recording();
this.creationTime = Instant.now();
this.recording.setName("Recording Stream: " + creationTime);
try {
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
this.directoryStream = new EventDirectoryStream(
acc,
null,
SecuritySupport.PRIVILEGED,
pr,
configurations(),
false

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2025, 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
@ -28,6 +28,7 @@ package jdk.jfr.internal;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@ -65,7 +66,7 @@ final class ChunkInputStream extends InputStream {
return false;
}
stream = new BufferedInputStream(SecuritySupport.newFileInputStream(currentChunk.getFile()));
stream = new BufferedInputStream(Files.newInputStream(currentChunk.getFile()));
unstreamedSize -= currentChunk.getSize();
return true;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -25,23 +25,16 @@
package jdk.jfr.internal;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import jdk.jfr.SettingControl;
import jdk.jfr.internal.settings.JDKSettingControl;
import jdk.jfr.internal.settings.PeriodSetting;
import jdk.jfr.internal.settings.StackTraceSetting;
import jdk.jfr.internal.settings.ThresholdSetting;
final class Control {
@SuppressWarnings("removal")
private final AccessControlContext context;
private static final int CACHE_SIZE = 5;
private final Set<?>[] cachedUnions = new HashSet<?>[CACHE_SIZE];
private final String[] cachedValues = new String[CACHE_SIZE];
@ -51,12 +44,8 @@ final class Control {
// called by exposed subclass in external API
public Control(SettingControl delegate, String defaultValue) {
this.context = PrivateAccess.getInstance().getContext(delegate);
this.delegate = delegate;
this.defaultValue = defaultValue;
if (this.context == null && !(delegate instanceof JDKSettingControl)) {
throw new InternalError("Security context can only be null for trusted setting controls");
}
}
boolean isType(Class<? extends SettingControl> clazz) {
@ -74,25 +63,8 @@ final class Control {
apply(defaultValue);
}
@SuppressWarnings("removal")
public String getValue() {
if (context == null) {
// VM events requires no access control context
return delegate.getValue();
} else {
return AccessController.doPrivileged(new PrivilegedAction<String>() {
@Override
public String run() {
try {
return delegate.getValue();
} catch (Throwable t) {
// Prevent malicious user to propagate exception callback in the wrong context
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when trying to get value for " + getClass());
}
return defaultValue != null ? defaultValue : ""; // Need to return something
}
}, context);
}
return delegate.getValue();
}
private void apply(String value) {
@ -102,53 +74,18 @@ final class Control {
setValue(value);
}
@SuppressWarnings("removal")
public void setValue(String value) {
if (context == null) {
// VM events requires no access control context
try {
delegate.setValue(value);
lastValue = delegate.getValue();
} catch (Throwable t) {
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when setting value \"" + value + "\" for " + getClass());
lastValue = null;
}
} else {
lastValue = AccessController.doPrivileged(new PrivilegedAction<String>() {
@Override
public String run() {
try {
delegate.setValue(value);
return delegate.getValue();
} catch (Throwable t) {
// Prevent malicious user to propagate exception callback in the wrong context
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when setting value \"" + value + "\" for " + getClass());
}
return null;
}
}, context);
try {
delegate.setValue(value);
lastValue = delegate.getValue();
} catch (Throwable t) {
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when setting value \"" + value + "\". " + t.getMessage());
lastValue = null;
}
}
@SuppressWarnings("removal")
public String combine(Set<String> values) {
if (context == null) {
// VM events requires no access control context
return delegate.combine(values);
}
return AccessController.doPrivileged(new PrivilegedAction<String>() {
@Override
public String run() {
try {
return delegate.combine(Collections.unmodifiableSet(values));
} catch (Throwable t) {
// Prevent malicious user to propagate exception callback in the wrong context
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when combining " + values + " for " + getClass());
}
return null;
}
}, context);
return delegate.combine(values);
}
private final String findCombine(Set<String> values) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -280,7 +280,7 @@ public final class EventControl {
} catch (Exception e) {
throw (Error) new InternalError("Could not get constructor for " + settingControlClass.getName()).initCause(e);
}
SecuritySupport.setAccessible(cc);
cc.setAccessible(true);
try {
return (SettingControl) cc.newInstance();
} catch (IllegalArgumentException | InvocationTargetException e) {
@ -373,13 +373,6 @@ public final class EventControl {
return idName;
}
/**
* A malicious user must never be able to run a callback in the wrong
* context. Methods on SettingControl must therefore never be invoked directly
* by JFR, instead use jdk.jfr.internal.Control.
*
* The returned list is only to be used inside EventConfiguration
*/
public List<SettingControl> getSettingControls() {
return settingControls;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -89,7 +89,6 @@ final class EventInstrumentation {
private static final ClassDesc TYPE_EVENT_CONFIGURATION = classDesc(EventConfiguration.class);
private static final ClassDesc TYPE_ISE = Bytecode.classDesc(IllegalStateException.class);
private static final ClassDesc TYPE_EVENT_WRITER = classDesc(EventWriter.class);
private static final ClassDesc TYPE_EVENT_WRITER_FACTORY = ClassDesc.of("jdk.jfr.internal.event.EventWriterFactory");
private static final ClassDesc TYPE_OBJECT = Bytecode.classDesc(Object.class);
private static final ClassDesc TYPE_SETTING_DEFINITION = Bytecode.classDesc(SettingDefinition.class);
private static final MethodDesc METHOD_BEGIN = MethodDesc.of("begin", "()V");
@ -100,7 +99,7 @@ final class EventInstrumentation {
private static final MethodDesc METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT = MethodDesc.of("shouldCommit", "(J)Z");
private static final MethodDesc METHOD_EVENT_CONFIGURATION_GET_SETTING = MethodDesc.of("getSetting", SettingControl.class, int.class);
private static final MethodDesc METHOD_EVENT_SHOULD_COMMIT = MethodDesc.of("shouldCommit", "()Z");
private static final MethodDesc METHOD_GET_EVENT_WRITER_KEY = MethodDesc.of("getEventWriter", "(J)" + TYPE_EVENT_WRITER.descriptorString());
private static final MethodDesc METHOD_GET_EVENT_WRITER = MethodDesc.of("getEventWriter", "()" + TYPE_EVENT_WRITER.descriptorString());
private static final MethodDesc METHOD_IS_ENABLED = MethodDesc.of("isEnabled", "()Z");
private static final MethodDesc METHOD_RESET = MethodDesc.of("reset", "()V");
private static final MethodDesc METHOD_SHOULD_COMMIT_LONG = MethodDesc.of("shouldCommit", "(J)Z");
@ -767,8 +766,7 @@ final class EventInstrumentation {
}
private void getEventWriter(CodeBuilder codeBuilder) {
codeBuilder.ldc(EventWriterKey.getKey());
invokestatic(codeBuilder, TYPE_EVENT_WRITER_FACTORY, METHOD_GET_EVENT_WRITER_KEY);
invokestatic(codeBuilder, TYPE_EVENT_WRITER, METHOD_GET_EVENT_WRITER);
}
private void getEventConfiguration(CodeBuilder codeBuilder) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, 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
@ -26,19 +26,19 @@
package jdk.jfr.internal;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.SequencedSet;
import jdk.jfr.internal.SecuritySupport.SafePath;
// This class keeps track of files that can't be deleted
// so they can at a later staged be removed.
final class FilePurger {
private static final SequencedSet<SafePath> paths = new LinkedHashSet<>();
private static final SequencedSet<Path> paths = new LinkedHashSet<>();
public static synchronized void add(SafePath p) {
public static synchronized void add(Path p) {
paths.add(p);
if (paths.size() > 1000) {
removeOldest();
@ -50,7 +50,7 @@ final class FilePurger {
return;
}
for (SafePath p : new ArrayList<>(paths)) {
for (Path p : new ArrayList<>(paths)) {
if (delete(p)) {
paths.remove(p);
}
@ -61,16 +61,12 @@ final class FilePurger {
paths.removeFirst();
}
private static boolean delete(SafePath p) {
try {
if (!SecuritySupport.exists(p)) {
return true;
}
} catch (IOException e) {
// ignore
private static boolean delete(Path p) {
if (!Files.exists(p)) {
return true;
}
try {
SecuritySupport.delete(p);
Files.delete(p);
return true;
} catch (IOException e) {
return false;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -91,11 +91,11 @@ public final class JDKEvents {
try {
if (initializationTriggered == false) {
for (Class<?> eventClass : eventClasses) {
SecuritySupport.registerEvent((Class<? extends Event>) eventClass);
MetadataRepository.getInstance().register((Class<? extends Event>) eventClass);
}
PeriodicEvents.addJDKEvent(jdk.internal.event.ExceptionStatisticsEvent.class, emitExceptionStatistics);
PeriodicEvents.addJDKEvent(DirectBufferStatisticsEvent.class, emitDirectBufferStatistics);
PeriodicEvents.addJDKEvent(InitialSecurityPropertyEvent.class, emitInitialSecurityProperties);
PeriodicEvents.addJavaEvent(jdk.internal.event.ExceptionStatisticsEvent.class, emitExceptionStatistics);
PeriodicEvents.addJavaEvent(DirectBufferStatisticsEvent.class, emitDirectBufferStatistics);
PeriodicEvents.addJavaEvent(InitialSecurityPropertyEvent.class, emitInitialSecurityProperties);
initializeContainerEvents();
JFRTracing.enable();
@ -116,17 +116,21 @@ public final class JDKEvents {
}
// The registration of events and hooks are needed to provide metadata,
// even when not running in a container
SecuritySupport.registerEvent(ContainerConfigurationEvent.class);
SecuritySupport.registerEvent(ContainerCPUUsageEvent.class);
SecuritySupport.registerEvent(ContainerCPUThrottlingEvent.class);
SecuritySupport.registerEvent(ContainerMemoryUsageEvent.class);
SecuritySupport.registerEvent(ContainerIOUsageEvent.class);
registerEvent(ContainerConfigurationEvent.class);
registerEvent(ContainerCPUUsageEvent.class);
registerEvent(ContainerCPUThrottlingEvent.class);
registerEvent(ContainerMemoryUsageEvent.class);
registerEvent(ContainerIOUsageEvent.class);
PeriodicEvents.addJDKEvent(ContainerConfigurationEvent.class, emitContainerConfiguration);
PeriodicEvents.addJDKEvent(ContainerCPUUsageEvent.class, emitContainerCPUUsage);
PeriodicEvents.addJDKEvent(ContainerCPUThrottlingEvent.class, emitContainerCPUThrottling);
PeriodicEvents.addJDKEvent(ContainerMemoryUsageEvent.class, emitContainerMemoryUsage);
PeriodicEvents.addJDKEvent(ContainerIOUsageEvent.class, emitContainerIOUsage);
PeriodicEvents.addJavaEvent(ContainerConfigurationEvent.class, emitContainerConfiguration);
PeriodicEvents.addJavaEvent(ContainerCPUUsageEvent.class, emitContainerCPUUsage);
PeriodicEvents.addJavaEvent(ContainerCPUThrottlingEvent.class, emitContainerCPUThrottling);
PeriodicEvents.addJavaEvent(ContainerMemoryUsageEvent.class, emitContainerMemoryUsage);
PeriodicEvents.addJavaEvent(ContainerIOUsageEvent.class, emitContainerIOUsage);
}
private static void registerEvent(Class<? extends jdk.internal.event.Event> eventClass) {
MetadataRepository.getInstance().register(eventClass);
}
private static void emitExceptionStatistics() {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -56,7 +56,7 @@ public final class JVMSupport {
private static boolean checkAvailability() {
// set jfr.unsupported.vm to true to test API on an unsupported VM
try {
if (SecuritySupport.getBooleanProperty("jfr.unsupported.vm")) {
if (Boolean.getBoolean("jfr.unsupported.vm")) {
return false;
}
} catch (NoClassDefFoundError cnfe) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -64,7 +64,6 @@ final class JVMUpcalls {
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Skipping instrumentation for " + clazz.getName() + " since container support is missing");
return oldBytes;
}
EventWriterKey.ensureEventWriterFactory();
EventConfiguration configuration = JVMSupport.getConfiguration(clazz.asSubclass(jdk.internal.event.Event.class));
if (configuration == null) {
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "No event configuration found for " + clazz.getName() + ". Ignoring instrumentation request.");
@ -124,7 +123,6 @@ final class JVMUpcalls {
return oldBytes;
}
}
EventWriterKey.ensureEventWriterFactory();
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding " + (forceInstrumentation ? "forced " : "") + "instrumentation for event type " + eventName + " during initial class load");
byte[] bytes = ei.buildInstrumented();
Bytecode.log(ei.getClassName() + "(" + traceId + ")", bytes);
@ -155,6 +153,8 @@ final class JVMUpcalls {
* @return a new thread
*/
static Thread createRecorderThread(ThreadGroup systemThreadGroup, ClassLoader contextClassLoader) {
return SecuritySupport.createRecorderThread(systemThreadGroup, contextClassLoader);
Thread thread = new Thread(systemThreadGroup, "JFR Recorder Thread");
thread.setContextClassLoader(contextClassLoader);
return thread;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -191,7 +191,7 @@ public final class MetadataLoader {
public static List<Type> createTypes() throws IOException {
try (DataInputStream dis = new DataInputStream(
SecuritySupport.getResourceAsStream("/jdk/jfr/internal/types/metadata.bin"))) {
MetadataLoader.class.getResourceAsStream("/jdk/jfr/internal/types/metadata.bin"))) {
MetadataLoader ml = new MetadataLoader(dis);
return ml.buildTypes();
} catch (Exception e) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -123,7 +123,6 @@ public final class MetadataRepository {
}
public synchronized void unregister(Class<? extends Event> eventClass) {
SecuritySupport.checkRegisterPermission();
EventConfiguration configuration = getConfiguration(eventClass, false);
if (configuration != null) {
configuration.getPlatformEventType().setRegistered(false);
@ -135,7 +134,6 @@ public final class MetadataRepository {
}
public synchronized EventType register(Class<? extends jdk.internal.event.Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) {
SecuritySupport.checkRegisterPermission();
if (JVM.isExcluded(eventClass)) {
// Event classes are marked as excluded during class load
// if they override methods in the jdk.jfr.Event class, i.e. commit().
@ -186,7 +184,7 @@ public final class MetadataRepository {
if (cachedEventConfigurationConstructor == null) {
var argClasses = new Class<?>[] { EventType.class, EventControl.class};
Constructor<EventConfiguration> c = EventConfiguration.class.getDeclaredConstructor(argClasses);
SecuritySupport.setAccessible(c);
c.setAccessible(true);
cachedEventConfigurationConstructor = c;
}
return cachedEventConfigurationConstructor.newInstance(eventType, ec);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -26,8 +26,9 @@
package jdk.jfr.internal;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.internal.misc.Unsafe;
import static java.nio.file.LinkOption.*;
@ -50,7 +51,7 @@ public final class Options {
private static long DEFAULT_THREAD_BUFFER_SIZE;
private static final int DEFAULT_STACK_DEPTH = 64;
private static final long DEFAULT_MAX_CHUNK_SIZE = 12 * 1024 * 1024;
private static final SafePath DEFAULT_DUMP_PATH = null;
private static final Path DEFAULT_DUMP_PATH = null;
private static final boolean DEFAULT_PRESERVE_REPOSITORY = false;
private static long memorySize;
@ -115,10 +116,10 @@ public final class Options {
globalBufferSize = globalBufsize;
}
public static synchronized void setDumpPath(SafePath path) throws IOException {
public static synchronized void setDumpPath(Path path) throws IOException {
if (path != null) {
if (SecuritySupport.isWritable(path)) {
path = SecuritySupport.toRealPath(path, NOFOLLOW_LINKS);
if (Files.isWritable(path)) {
path = path.toRealPath(NOFOLLOW_LINKS);
} else {
throw new IOException("Cannot write JFR emergency dump to " + path.toString());
}
@ -126,8 +127,8 @@ public final class Options {
JVM.setDumpPath(path == null ? null : path.toString());
}
public static synchronized SafePath getDumpPath() {
return new SafePath(JVM.getDumpPath());
public static synchronized Path getDumpPath() {
return Path.of(JVM.getDumpPath());
}
public static synchronized void setStackDepth(Integer stackTraceDepth) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -33,8 +33,7 @@ import static jdk.jfr.internal.LogTag.JFR;
import static jdk.jfr.internal.LogTag.JFR_SYSTEM;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@ -46,7 +45,6 @@ import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import jdk.jfr.FlightRecorder;
import jdk.jfr.FlightRecorderListener;
@ -54,8 +52,6 @@ import jdk.jfr.Recording;
import jdk.jfr.RecordingState;
import jdk.jfr.events.ActiveRecordingEvent;
import jdk.jfr.events.ActiveSettingEvent;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.SecuritySupport.SecureRecorderListener;
import jdk.jfr.internal.consumer.EventLog;
import jdk.jfr.internal.periodic.PeriodicEvents;
import jdk.jfr.internal.util.Utils;
@ -64,7 +60,7 @@ public final class PlatformRecorder {
private final ArrayList<PlatformRecording> recordings = new ArrayList<>();
private static final List<SecureRecorderListener> changeListeners = new ArrayList<>();
private static final List<FlightRecorderListener> changeListeners = new ArrayList<>();
private final Repository repository;
private final Thread shutdownHook;
@ -83,25 +79,9 @@ public final class PlatformRecorder {
JDKEvents.initialize();
Logger.log(JFR_SYSTEM, INFO, "Registered JDK events");
startDiskMonitor();
shutdownHook = SecuritySupport.createThreadWitNoPermissions("JFR Shutdown Hook", new ShutdownHook(this));
SecuritySupport.setUncaughtExceptionHandler(shutdownHook, new ShutdownHook.ExceptionHandler());
SecuritySupport.registerShutdownHook(shutdownHook);
}
private static Timer createTimer() {
try {
List<Timer> result = new CopyOnWriteArrayList<>();
Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> {
result.add(new Timer("JFR Recording Scheduler", true));
});
JVM.exclude(t);
t.start();
t.join();
return result.getFirst();
} catch (InterruptedException e) {
throw new IllegalStateException("Not able to create timer task. " + e.getMessage(), e);
}
shutdownHook = new ShutdownHook(this);
shutdownHook.setUncaughtExceptionHandler(new ShutdownHook.ExceptionHandler());
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
public synchronized PlatformRecording newRecording(Map<String, String> settings) {
@ -138,27 +118,18 @@ public final class PlatformRecorder {
}
public static synchronized void addListener(FlightRecorderListener changeListener) {
@SuppressWarnings("removal")
AccessControlContext context = AccessController.getContext();
SecureRecorderListener sl = new SecureRecorderListener(context, changeListener);
boolean runInitialized;
synchronized (PlatformRecorder.class) {
runInitialized = FlightRecorder.isInitialized();
changeListeners.add(sl);
changeListeners.add(changeListener);
}
if (runInitialized) {
sl.recorderInitialized(FlightRecorder.getFlightRecorder());
changeListener.recorderInitialized(FlightRecorder.getFlightRecorder());
}
}
public static synchronized boolean removeListener(FlightRecorderListener changeListener) {
for (SecureRecorderListener s : new ArrayList<>(changeListeners)) {
if (s.getChangeListener() == changeListener) {
changeListeners.remove(s);
return true;
}
}
return false;
return changeListeners.remove(changeListener);
}
static synchronized List<FlightRecorderListener> getListeners() {
@ -167,7 +138,7 @@ public final class PlatformRecorder {
synchronized Timer getTimer() {
if (timer == null) {
timer = createTimer();
timer = new Timer("JFR Recording Scheduler", true);
}
return timer;
}
@ -366,7 +337,7 @@ public final class PlatformRecorder {
}
private Instant dumpMemoryToDestination(PlatformRecording recording) {
WriteableUserPath dest = recording.getDestination();
WriteablePath dest = recording.getDestination();
if (dest != null) {
Instant t = MetadataRepository.getInstance().setOutput(dest.getRealPathText());
recording.clearDestination();
@ -441,8 +412,8 @@ public final class PlatformRecorder {
}
private void startDiskMonitor() {
Thread t = SecuritySupport.createThreadWitNoPermissions("JFR Periodic Tasks", () -> periodicTask());
SecuritySupport.setDaemonThread(t, true);
Thread t = new Thread(() -> periodicTask(), "JFR Periodic Tasks");
t.setDaemon(true);
t.start();
}
@ -472,7 +443,7 @@ public final class PlatformRecorder {
if (ActiveRecordingEvent.enabled()) {
for (PlatformRecording r : getRecordings()) {
if (r.getState() == RecordingState.RUNNING && r.shouldWriteMetadataEvent()) {
WriteableUserPath path = r.getDestination();
WriteablePath path = r.getDestination();
Duration age = r.getMaxAge();
Duration flush = r.getFlushInterval();
Long size = r.getMaxSize();
@ -519,7 +490,7 @@ public final class PlatformRecorder {
wait = Math.min(minDelta, Options.getWaitInterval());
} catch (Throwable t) {
// Catch everything and log, but don't allow it to end the periodic task
Logger.log(JFR_SYSTEM, ERROR, "Error in Periodic task: " + t.getClass().getName());
Logger.log(JFR_SYSTEM, WARN, "Error in Periodic task: " + t.getMessage());
} finally {
takeNap(wait);
}
@ -660,7 +631,7 @@ public final class PlatformRecorder {
target.setInternalDuration(startTime.until(endTime));
}
public synchronized void migrate(SafePath repo) throws IOException {
public synchronized void migrate(Path repo) throws IOException {
// Must set repository while holding recorder lock so
// the final chunk in repository gets marked correctly
Repository.getRepository().setBasePath(repo);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -35,11 +35,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
@ -59,7 +56,6 @@ import jdk.jfr.Configuration;
import jdk.jfr.FlightRecorderListener;
import jdk.jfr.Recording;
import jdk.jfr.RecordingState;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.util.Utils;
import jdk.jfr.internal.util.ValueFormatter;
@ -73,12 +69,12 @@ public final class PlatformRecording implements AutoCloseable {
private Duration maxAge;
private long maxSize;
private WriteableUserPath destination;
private WriteablePath destination;
private boolean toDisk = true;
private String name;
private boolean dumpOnExit;
private SafePath dumpDirectory;
private Path dumpDirectory;
// Timestamp information
private Instant stopTime;
private Instant startTime;
@ -90,8 +86,6 @@ public final class PlatformRecording implements AutoCloseable {
private volatile Recording recording;
private TimerTask stopTask;
private TimerTask startTask;
@SuppressWarnings("removal")
private final AccessControlContext dumpDirectoryControlContext;
private boolean shouldWriteActiveRecordingEvent = true;
private Duration flushInterval = Duration.ofSeconds(1);
private long finalStartChunkNanos = Long.MIN_VALUE;
@ -99,13 +93,6 @@ public final class PlatformRecording implements AutoCloseable {
@SuppressWarnings("removal")
PlatformRecording(PlatformRecorder recorder, long id) {
// Typically the access control context is taken
// when you call dump(Path) or setDestination(Path),
// but if no destination is set and the filename is auto-generated,
// the control context of the recording is taken when the
// Recording object is constructed. This works well for
// -XX:StartFlightRecording and JFR.dump
this.dumpDirectoryControlContext = AccessController.getContext();
this.id = id;
this.recorder = recorder;
this.name = String.valueOf(id);
@ -175,7 +162,7 @@ public final class PlatformRecording implements AutoCloseable {
Logger.log(LogTag.JFR, LogLevel.INFO, "Stopped recording \"" + getName() + "\" (" + getId() + ")" + endText);
newState = getState();
}
WriteableUserPath dest = getDestination();
WriteablePath dest = getDestination();
if (dest == null && dumpDirectory != null) {
dest = makeDumpPath();
}
@ -195,30 +182,18 @@ public final class PlatformRecording implements AutoCloseable {
return true;
}
@SuppressWarnings("removal")
public WriteableUserPath makeDumpPath() {
public WriteablePath makeDumpPath() {
try {
String name = JVMSupport.makeFilename(getRecording());
return AccessController.doPrivileged(new PrivilegedExceptionAction<WriteableUserPath>() {
@Override
public WriteableUserPath run() throws Exception {
SafePath p = dumpDirectory;
if (p == null) {
p = new SafePath(".");
}
return new WriteableUserPath(p.toPath().resolve(name));
}
}, dumpDirectoryControlContext);
} catch (PrivilegedActionException e) {
Throwable t = e.getCause();
if (t instanceof SecurityException) {
Logger.log(LogTag.JFR, LogLevel.WARN, "Not allowed to create dump path for recording " + recording.getId() + " on exit.");
Path p = dumpDirectory;
if (p == null) {
p = Path.of(".");
}
if (t instanceof IOException) {
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not dump " + recording.getId() + " on exit.");
}
return null;
return new WriteablePath(p.resolve(name));
} catch (IOException e) {
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not dump " + recording.getId() + " on exit. " + e.getMessage());
}
return null;
}
@ -422,14 +397,16 @@ public final class PlatformRecording implements AutoCloseable {
}
}
public void setDestination(WriteableUserPath userSuppliedPath) throws IOException {
public void setDestination(WriteablePath destination) throws IOException {
synchronized (recorder) {
checkSetDestination(userSuppliedPath);
this.destination = userSuppliedPath;
checkSetDestination(destination);
this.destination = destination;
}
}
public void checkSetDestination(WriteableUserPath userSuppliedPath) throws IOException {
public void checkSetDestination(WriteablePath writeablePath) throws IOException {
// The writeablePath argument is not checked. It's sufficient that an instance has
// been created.
synchronized (recorder) {
if (Utils.isState(getState(), RecordingState.STOPPED, RecordingState.CLOSED)) {
throw new IllegalStateException("Destination can't be set on a recording that has been stopped/closed");
@ -437,7 +414,7 @@ public final class PlatformRecording implements AutoCloseable {
}
}
public WriteableUserPath getDestination() {
public WriteablePath getDestination() {
synchronized (recorder) {
return destination;
}
@ -707,8 +684,7 @@ public final class PlatformRecording implements AutoCloseable {
try {
stop("End of duration reached");
} catch (Throwable t) {
// Prevent malicious user to propagate exception callback in the wrong context
Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not stop recording.");
Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not stop recording. " + t.getMessage());
}
}
};
@ -737,37 +713,34 @@ public final class PlatformRecording implements AutoCloseable {
}
// Dump running and stopped recordings
public void dump(WriteableUserPath writeableUserPath) throws IOException {
public void dump(WriteablePath writeablePath) throws IOException {
synchronized (recorder) {
try(PlatformRecording p = newSnapshotClone("Dumped by user", null)) {
p.dumpStopped(writeableUserPath);
p.dumpStopped(writeablePath);
}
}
}
public void dumpStopped(WriteableUserPath userPath) throws IOException {
public void dumpStopped(WriteablePath path) throws IOException {
synchronized (recorder) {
transferChunksWithRetry(userPath);
transferChunksWithRetry(path);
}
}
private void transferChunksWithRetry(WriteableUserPath userPath) throws IOException {
userPath.doPrivilegedIO(() -> {
try {
transferChunks(userPath);
} catch (NoSuchFileException nsfe) {
Logger.log(LogTag.JFR, LogLevel.ERROR, "Missing chunkfile when writing recording \"" + name + "\" (" + id + ") to " + userPath.getRealPathText() + ".");
// if one chunkfile was missing, its likely more are missing
removeNonExistantPaths();
// and try the transfer again
transferChunks(userPath);
}
return null;
});
private void transferChunksWithRetry(WriteablePath path) throws IOException {
try {
transferChunks(path);
} catch (NoSuchFileException nsfe) {
Logger.log(LogTag.JFR, LogLevel.ERROR, "Missing chunkfile when writing recording \"" + name + "\" (" + id + ") to " + path.getRealPathText() + ".");
// if one chunkfile was missing, its likely more are missing
removeNonExistantPaths();
// and try the transfer again
transferChunks(path);
}
}
private void transferChunks(WriteableUserPath userPath) throws IOException {
try (ChunksChannel cc = new ChunksChannel(chunks); FileChannel fc = FileChannel.open(userPath.getReal(), StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
private void transferChunks(WriteablePath path) throws IOException {
try (ChunksChannel cc = new ChunksChannel(chunks); FileChannel fc = FileChannel.open(path.getReal(), StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
long bytes = cc.transferTo(fc);
Logger.log(LogTag.JFR, LogLevel.INFO, "Transferred " + bytes + " bytes from the disk repository");
// No need to force if no data was transferred, which avoids IOException when device is /dev/null
@ -859,7 +832,7 @@ public final class PlatformRecording implements AutoCloseable {
* <p>
* Only to be used by DCmdStart.
*/
public void setDumpDirectory(SafePath directory) {
public void setDumpDirectory(Path directory) {
this.dumpDirectory = directory;
}
@ -913,7 +886,7 @@ public final class PlatformRecording implements AutoCloseable {
}
public void removePath(SafePath path) {
public void removePath(Path path) {
synchronized (recorder) {
Iterator<RepositoryChunk> it = chunks.iterator();
while (it.hasNext()) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -25,7 +25,6 @@
package jdk.jfr.internal;
import java.security.AccessControlContext;
import java.util.List;
import java.util.Map;
@ -35,7 +34,6 @@ import jdk.jfr.EventSettings;
import jdk.jfr.EventType;
import jdk.jfr.FlightRecorderPermission;
import jdk.jfr.Recording;
import jdk.jfr.SettingControl;
import jdk.jfr.SettingDescriptor;
import jdk.jfr.ValueDescriptor;
import jdk.jfr.internal.management.EventSettingsModifier;
@ -60,7 +58,7 @@ public abstract class PrivateAccess {
// Will trigger
// FlightRecorderPermission.<clinit>
// which will call PrivateAccess.setPrivateAccess
new FlightRecorderPermission(SecuritySupport.REGISTER_EVENT);
new FlightRecorderPermission("accessFlightRecorder");
}
return instance;
}
@ -99,9 +97,6 @@ public abstract class PrivateAccess {
public abstract PlatformRecorder getPlatformRecorder();
@SuppressWarnings("removal")
public abstract AccessControlContext getContext(SettingControl sc);
public abstract EventSettings newEventSettings(EventSettingsModifier esm);
public abstract boolean isVisible(EventType t);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, 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
@ -26,6 +26,7 @@
package jdk.jfr.internal;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.DateTimeException;
import java.time.LocalDateTime;
@ -33,20 +34,20 @@ import java.time.ZoneOffset;
import java.util.HashSet;
import java.util.Set;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.management.ChunkFilename;
import jdk.jfr.internal.util.ValueFormatter;
import jdk.jfr.internal.util.DirectoryCleaner;
import jdk.jfr.internal.util.Utils;
public final class Repository {
private static final Path JAVA_IO_TMPDIR = Utils.getPathInProperty("java.io.tmpdir", null);
private static final int MAX_REPO_CREATION_RETRIES = 1000;
private static final Repository instance = new Repository();
private static final String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
private final Set<SafePath> cleanupDirectories = new HashSet<>();
private SafePath baseLocation;
private SafePath repository;
private final Set<Path> cleanupDirectories = new HashSet<>();
private Path baseLocation;
private Path repository;
private ChunkFilename chunkFilename;
private Repository() {
@ -56,7 +57,7 @@ public final class Repository {
return instance;
}
public synchronized void setBasePath(SafePath baseLocation) throws IOException {
public synchronized void setBasePath(Path baseLocation) throws IOException {
if(baseLocation.equals(this.baseLocation)) {
Logger.log(LogTag.JFR, LogLevel.INFO, "Same base repository path " + baseLocation.toString() + " is set");
return;
@ -68,7 +69,7 @@ public final class Repository {
try {
// Remove so we don't "leak" repositories, if JFR is never started
// and shutdown hook not added.
SecuritySupport.delete(repository);
Files.delete(repository);
} catch (IOException ioe) {
Logger.log(LogTag.JFR, LogLevel.INFO, "Could not delete disk repository " + repository);
}
@ -77,25 +78,25 @@ public final class Repository {
public synchronized void ensureRepository() throws IOException {
if (baseLocation == null) {
setBasePath(SecuritySupport.JAVA_IO_TMPDIR);
setBasePath(JAVA_IO_TMPDIR);
}
}
synchronized RepositoryChunk newChunk() {
LocalDateTime timestamp = timestamp();
try {
if (!SecuritySupport.existDirectory(repository)) {
if (!Files.exists(repository)) {
this.repository = createRepository(baseLocation);
JVM.setRepositoryLocation(repository.toString());
SecuritySupport.setProperty(JFR_REPOSITORY_LOCATION_PROPERTY, repository.toString());
System.setProperty(JFR_REPOSITORY_LOCATION_PROPERTY, repository.toString());
cleanupDirectories.add(repository);
chunkFilename = null;
}
if (chunkFilename == null) {
chunkFilename = ChunkFilename.newPriviliged(repository.toPath());
chunkFilename = new ChunkFilename(repository);
}
String filename = chunkFilename.next(timestamp);
return new RepositoryChunk(new SafePath(filename));
return new RepositoryChunk(Path.of(filename));
} catch (Exception e) {
String errorMsg = String.format("Could not create chunk in repository %s, %s: %s", repository, e.getClass(), e.getMessage());
Logger.log(LogTag.JFR, LogLevel.ERROR, errorMsg);
@ -113,16 +114,16 @@ public final class Repository {
}
}
private static SafePath createRepository(SafePath basePath) throws IOException {
SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath);
SafePath f = null;
private static Path createRepository(Path basePath) throws IOException {
Path canonicalBaseRepositoryPath = createRealBasePath(basePath);
Path f = null;
String basename = ValueFormatter.formatDateTime(timestamp()) + "_" + JVM.getPid();
String name = basename;
int i = 0;
for (; i < MAX_REPO_CREATION_RETRIES; i++) {
f = new SafePath(canonicalBaseRepositoryPath.toPath().resolve(name));
f = canonicalBaseRepositoryPath.resolve(name);
if (tryToUseAsRepository(f)) {
break;
}
@ -132,41 +133,36 @@ public final class Repository {
if (i == MAX_REPO_CREATION_RETRIES) {
throw new IOException("Unable to create JFR repository directory using base location (" + basePath + ")");
}
return SecuritySupport.toRealPath(f);
return f.toRealPath();
}
private static SafePath createRealBasePath(SafePath safePath) throws IOException {
if (SecuritySupport.exists(safePath)) {
if (!SecuritySupport.isWritable(safePath)) {
throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable");
private static Path createRealBasePath(Path path) throws IOException {
if (Files.exists(path)) {
if (!Files.isWritable(path)) {
throw new IOException("JFR repository directory (" + path.toString() + ") exists, but isn't writable");
}
return SecuritySupport.toRealPath(safePath);
return path.toRealPath();
}
SafePath p = SecuritySupport.createDirectories(safePath);
return SecuritySupport.toRealPath(p);
return Files.createDirectories(path).toRealPath();
}
private static boolean tryToUseAsRepository(final SafePath path) {
Path parent = path.toPath().getParent();
private static boolean tryToUseAsRepository(Path path) {
Path parent = path.getParent();
if (parent == null) {
return false;
}
try {
try {
SecuritySupport.createDirectories(path);
} catch (Exception e) {
// file already existed or some other problem occurred
}
if (!SecuritySupport.exists(path)) {
return false;
}
if (!SecuritySupport.isDirectory(path)) {
return false;
}
return true;
} catch (IOException io) {
Files.createDirectories(path);
} catch (Exception e) {
// file already existed or some other problem occurred
}
if (!Files.exists(path)) {
return false;
}
if (!Files.isDirectory(path)) {
return false;
}
return true;
}
synchronized void clear() {
@ -174,9 +170,9 @@ public final class Repository {
return;
}
for (SafePath p : cleanupDirectories) {
for (Path p : cleanupDirectories) {
try {
SecuritySupport.clearDirectory(p);
DirectoryCleaner.clear(p);
Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p);
} catch (IOException e) {
Logger.log(LogTag.JFR, LogLevel.INFO, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
@ -184,11 +180,11 @@ public final class Repository {
}
}
public synchronized SafePath getRepositoryPath() {
public synchronized Path getRepositoryPath() {
return repository;
}
public synchronized SafePath getBaseLocation() {
public synchronized Path getBaseLocation() {
return baseLocation;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, 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
@ -27,12 +27,14 @@ package jdk.jfr.internal;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.time.Instant;
import java.util.Comparator;
import jdk.jfr.internal.SecuritySupport.SafePath;
public final class RepositoryChunk {
static final Comparator<RepositoryChunk> END_TIME_COMPARATOR = new Comparator<RepositoryChunk>() {
@ -42,7 +44,7 @@ public final class RepositoryChunk {
}
};
private final SafePath chunkFile;
private final Path chunkFile;
private final RandomAccessFile unFinishedRAF;
private Instant endTime = null; // unfinished
@ -50,15 +52,15 @@ public final class RepositoryChunk {
private int refCount = 1;
private long size;
RepositoryChunk(SafePath path) throws Exception {
RepositoryChunk(Path path) throws Exception {
this.chunkFile = path;
this.unFinishedRAF = SecuritySupport.createRandomAccessFile(chunkFile);
this.unFinishedRAF = new RandomAccessFile(path.toFile(), "rw");
}
boolean finish(Instant endTime) {
try {
unFinishedRAF.close();
size = SecuritySupport.getFileSize(chunkFile);
size = Files.size(chunkFile);
this.endTime = endTime;
if (Logger.shouldLog(LogTag.JFR_SYSTEM, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, "Chunk finished: " + chunkFile);
@ -89,9 +91,9 @@ public final class RepositoryChunk {
return endTime;
}
private void delete(SafePath f) {
private void delete(Path f) {
try {
SecuritySupport.delete(f);
Files.delete(f);
if (Logger.shouldLog(LogTag.JFR, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR, LogLevel.DEBUG, "Repository chunk " + f + " deleted");
}
@ -153,7 +155,7 @@ public final class RepositoryChunk {
if (!isFinished()) {
throw new IOException("Chunk not finished");
}
return ((SecuritySupport.newFileChannelToRead(chunkFile)));
return FileChannel.open(chunkFile, StandardOpenOption.READ);
}
public boolean inInterval(Instant startTime, Instant endTime) {
@ -166,23 +168,19 @@ public final class RepositoryChunk {
return true;
}
public SafePath getFile() {
public Path getFile() {
return chunkFile;
}
public long getCurrentFileSize() {
try {
return SecuritySupport.getFileSize(chunkFile);
return Files.size(chunkFile);
} catch (IOException e) {
return 0L;
}
}
boolean isMissingFile() {
try {
return !SecuritySupport.exists(chunkFile);
} catch (IOException ioe) {
return true;
}
return !Files.exists(chunkFile);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -25,63 +25,13 @@
package jdk.jfr.internal;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ReflectPermission;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.PropertyPermission;
import java.util.concurrent.Callable;
import jdk.internal.module.Modules;
import jdk.jfr.Event;
import jdk.jfr.FlightRecorder;
import jdk.jfr.FlightRecorderListener;
import jdk.jfr.FlightRecorderPermission;
import jdk.jfr.Recording;
import jdk.jfr.internal.consumer.FileAccess;
/**
* Contains JFR code that does
* {@link AccessController#doPrivileged(PrivilegedAction)}
*/
public final class SecuritySupport {
private static final String EVENTS_PACKAGE_NAME = "jdk.jfr.events";
private static final String EVENT_PACKAGE_NAME = "jdk.jfr.internal.event";
public static final String REGISTER_EVENT = "registerEvent";
public static final String ACCESS_FLIGHT_RECORDER = "accessFlightRecorder";
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final Module JFR_MODULE = Event.class.getModule();
public static final SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr");
public static final FileAccess PRIVILEGED = new Privileged();
static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null);
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
static {
// ensure module java.base can read module jdk.jfr as early as possible
@ -90,215 +40,6 @@ public final class SecuritySupport {
addEventsExport(Object.class);
}
static final class SecureRecorderListener implements FlightRecorderListener {
@SuppressWarnings("removal")
private final AccessControlContext context;
private final FlightRecorderListener changeListener;
SecureRecorderListener(@SuppressWarnings("removal") AccessControlContext context, FlightRecorderListener changeListener) {
this.context = Objects.requireNonNull(context);
this.changeListener = Objects.requireNonNull(changeListener);
}
@SuppressWarnings("removal")
@Override
public void recordingStateChanged(Recording recording) {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
try {
changeListener.recordingStateChanged(recording);
} catch (Throwable t) {
// Prevent malicious user to propagate exception callback in the wrong context
Logger.log(LogTag.JFR, LogLevel.WARN, "Unexpected exception in listener " + changeListener.getClass()+ " at recording state change");
}
return null;
}, context);
}
@SuppressWarnings("removal")
@Override
public void recorderInitialized(FlightRecorder recorder) {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
try {
changeListener.recorderInitialized(recorder);
} catch (Throwable t) {
// Prevent malicious user to propagate exception callback in the wrong context
Logger.log(LogTag.JFR, LogLevel.WARN, "Unexpected exception in listener " + changeListener.getClass()+ " when initializing FlightRecorder");
}
return null;
}, context);
}
public FlightRecorderListener getChangeListener() {
return changeListener;
}
}
private static final class DirectoryCleaner extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
Files.delete(path);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
if (exc != null) {
throw exc;
}
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
}
/**
* Path created by the default file provider, and not
* a malicious provider.
*
*/
public static final class SafePath implements Comparable<SafePath> {
private final Path path;
private final String text;
public SafePath(Path p) {
// sanitize
text = p.toString();
path = Paths.get(text);
}
public SafePath(String path) {
this(Paths.get(path));
}
public Path toPath() {
return path;
}
public File toFile() {
return path.toFile();
}
@Override
public String toString() {
return text;
}
@Override
public int compareTo(SafePath that) {
return that.text.compareTo(this.text);
}
@Override
public boolean equals(Object other) {
if(other != null && other instanceof SafePath s){
return this.toPath().equals(s.toPath());
}
return false;
}
@Override
public int hashCode() {
return this.toPath().hashCode();
}
}
private interface RunnableWithCheckedException {
public void run() throws Exception;
}
private interface CallableWithoutCheckException<T> {
public T call();
}
public static void checkAccessFlightRecorder() throws SecurityException {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new FlightRecorderPermission(ACCESS_FLIGHT_RECORDER));
}
}
public static void checkRegisterPermission() throws SecurityException {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new FlightRecorderPermission(REGISTER_EVENT));
}
}
@SuppressWarnings("removal")
private static <U> U doPrivilegedIOWithReturn(Callable<U> function) throws IOException {
try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<U>() {
@Override
public U run() throws Exception {
return function.call();
}
}, null);
} catch (PrivilegedActionException e) {
Throwable t = e.getCause();
if (t instanceof IOException) {
throw (IOException) t;
}
throw new IOException("Unexpected error during I/O operation. " + t.getMessage(), t);
}
}
private static void doPriviligedIO(RunnableWithCheckedException function) throws IOException {
doPrivilegedIOWithReturn(() -> {
function.run();
return null;
});
}
@SuppressWarnings("removal")
private static void doPrivileged(Runnable function, Permission... perms) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
function.run();
return null;
}
}, null, perms);
}
@SuppressWarnings("removal")
private static void doPrivileged(Runnable function) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
function.run();
return null;
}
});
}
@SuppressWarnings("removal")
private static <T> T doPrivilegedWithReturn(CallableWithoutCheckException<T> function, Permission... perms) {
return AccessController.doPrivileged(new PrivilegedAction<T>() {
@Override
public T run() {
return function.call();
}
}, null, perms);
}
public static List<SafePath> getPredefinedJFCFiles() {
List<SafePath> list = new ArrayList<>();
try (var ds = doPrivilegedIOWithReturn(() -> Files.newDirectoryStream(JFC_DIRECTORY.toPath()))) {
for (Path path : ds) {
SafePath s = new SafePath(path);
String text = s.toString();
if (text.endsWith(".jfc") && !SecuritySupport.isDirectory(s)) {
list.add(s);
}
}
} catch (IOException ioe) {
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not access .jfc-files in " + JFC_DIRECTORY + ", " + ioe.getMessage());
}
return list;
}
static void makeVisibleToJFR(Class<?> clazz) {
Module classModule = clazz.getModule();
Modules.addReads(JFR_MODULE, classModule);
@ -314,213 +55,31 @@ public final class SecuritySupport {
* (for EventConfiguration and EventWriter)
*/
static void addInternalEventExport(Class<?> clazz) {
Modules.addExports(JFR_MODULE, EVENT_PACKAGE_NAME, clazz.getModule());
Modules.addExports(JFR_MODULE, "jdk.jfr.internal.event", clazz.getModule());
}
static void addEventsExport(Class<?> clazz) {
Modules.addExports(JFR_MODULE, EVENTS_PACKAGE_NAME, clazz.getModule());
Modules.addExports(JFR_MODULE, "jdk.jfr.events", clazz.getModule());
}
static void addReadEdge(Class<?> clazz) {
Modules.addReads(clazz.getModule(), JFR_MODULE);
}
public static void registerEvent(Class<? extends jdk.internal.event.Event> eventClass) {
doPrivileged(() -> MetadataRepository.getInstance().register(eventClass), new FlightRecorderPermission(REGISTER_EVENT));
}
public static void setProperty(String propertyName, String value) {
doPrivileged(() -> System.setProperty(propertyName, value), new PropertyPermission(propertyName, "write"));
}
static boolean getBooleanProperty(String propertyName) {
return doPrivilegedWithReturn(() -> Boolean.getBoolean(propertyName), new PropertyPermission(propertyName, "read"));
}
private static SafePath getPathInProperty(String prop, String subPath) {
return doPrivilegedWithReturn(() -> {
String path = System.getProperty(prop);
if (path == null) {
return null;
}
File file = subPath == null ? new File(path) : new File(path, subPath);
return new SafePath(file.getAbsolutePath());
}, new PropertyPermission("*", "read"));
}
// Called by JVM during initialization of JFR
static Thread createRecorderThread(ThreadGroup systemThreadGroup, ClassLoader contextClassLoader) {
// The thread should have permission = new Permission[0], and not "modifyThreadGroup" and "modifyThread" on the stack,
// but it's hard circumvent if we are going to pass in system thread group in the constructor
Thread thread = doPrivilegedWithReturn(() -> new Thread(systemThreadGroup, "JFR Recorder Thread"), new RuntimePermission("modifyThreadGroup"), new RuntimePermission("modifyThread"));
doPrivileged(() -> thread.setContextClassLoader(contextClassLoader), new RuntimePermission("setContextClassLoader"), new RuntimePermission("modifyThread"));
return thread;
}
static void registerShutdownHook(Thread shutdownHook) {
doPrivileged(() -> Runtime.getRuntime().addShutdownHook(shutdownHook), new RuntimePermission("shutdownHooks"));
}
static void setUncaughtExceptionHandler(Thread thread, Thread.UncaughtExceptionHandler eh) {
doPrivileged(() -> thread.setUncaughtExceptionHandler(eh), new RuntimePermission("modifyThread"));
}
static void clearDirectory(SafePath safePath) throws IOException {
doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner()));
}
static SafePath toRealPath(SafePath safePath, LinkOption... options) throws IOException {
return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath(options)));
}
static boolean existDirectory(SafePath directory) throws IOException {
return doPrivilegedIOWithReturn(() -> Files.exists(directory.toPath()));
}
static RandomAccessFile createRandomAccessFile(SafePath path) throws Exception {
return doPrivilegedIOWithReturn(() -> new RandomAccessFile(path.toPath().toFile(), "rw"));
}
public static InputStream newFileInputStream(SafePath safePath) throws IOException {
return doPrivilegedIOWithReturn(() -> Files.newInputStream(safePath.toPath()));
}
public static long getFileSize(SafePath safePath) throws IOException {
return doPrivilegedIOWithReturn(() -> Files.size(safePath.toPath()));
}
static SafePath createDirectories(SafePath safePath) throws IOException {
Path p = doPrivilegedIOWithReturn(() -> Files.createDirectories(safePath.toPath()));
return new SafePath(p);
}
public static boolean exists(SafePath safePath) throws IOException {
// Files.exist(path) is allocation intensive
return doPrivilegedIOWithReturn(() -> safePath.toPath().toFile().exists());
}
public static boolean isDirectory(SafePath safePath) throws IOException {
return doPrivilegedIOWithReturn(() -> Files.isDirectory(safePath.toPath()));
}
static void delete(SafePath localPath) throws IOException {
doPriviligedIO(() -> Files.delete(localPath.toPath()));
}
static boolean isWritable(SafePath safePath) throws IOException {
return doPrivilegedIOWithReturn(() -> Files.isWritable(safePath.toPath()));
}
static ReadableByteChannel newFileChannelToRead(SafePath safePath) throws IOException {
return doPrivilegedIOWithReturn(() -> FileChannel.open(safePath.toPath(), StandardOpenOption.READ));
}
public static InputStream getResourceAsStream(String name) throws IOException {
return doPrivilegedIOWithReturn(() -> SecuritySupport.class.getResourceAsStream(name));
}
public static Reader newFileReader(SafePath safePath) throws FileNotFoundException, IOException {
return doPrivilegedIOWithReturn(() -> Files.newBufferedReader(safePath.toPath()));
}
static void setAccessible(Method method) {
doPrivileged(() -> method.setAccessible(true), new ReflectPermission("suppressAccessChecks"));
}
static void setAccessible(Constructor<?> constructor) {
doPrivileged(() -> constructor.setAccessible(true), new ReflectPermission("suppressAccessChecks"));
}
@SuppressWarnings("removal")
public static void ensureClassIsInitialized(Class<?> clazz) {
try {
MethodHandles.Lookup lookup;
if (System.getSecurityManager() == null) {
lookup = MethodHandles.privateLookupIn(clazz, LOOKUP);
} else {
lookup = AccessController.doPrivileged(new PrivilegedExceptionAction<>() {
@Override
public MethodHandles.Lookup run() throws IllegalAccessException {
return MethodHandles.privateLookupIn(clazz, LOOKUP);
}
}, null, new ReflectPermission("suppressAccessChecks"));
}
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(clazz, LOOKUP);
lookup.ensureInitialized(clazz);
} catch (IllegalAccessException e) {
throw new InternalError(e);
} catch (PrivilegedActionException e) {
throw new InternalError(e.getCause());
}
}
@SuppressWarnings("removal")
static Class<?> defineClass(Class<?> lookupClass, byte[] bytes) {
return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
@Override
public Class<?> run() {
try {
return MethodHandles.privateLookupIn(lookupClass, LOOKUP).defineClass(bytes);
} catch (IllegalAccessException e) {
throw new InternalError(e);
}
}
});
}
public static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) {
return doPrivilegedWithReturn(() -> new Thread(runnable, threadName), new Permission[0]);
}
public static void setDaemonThread(Thread t, boolean daemon) {
doPrivileged(()-> t.setDaemon(daemon), new RuntimePermission("modifyThread"));
}
public static SafePath getAbsolutePath(SafePath path) throws IOException {
return new SafePath(doPrivilegedIOWithReturn((()-> path.toPath().toAbsolutePath())));
}
private static final class Privileged extends FileAccess {
@Override
public RandomAccessFile openRAF(File f, String mode) throws IOException {
return doPrivilegedIOWithReturn( () -> new RandomAccessFile(f, mode));
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path directory) throws IOException {
return doPrivilegedIOWithReturn( () -> Files.newDirectoryStream(directory));
}
@Override
public String getAbsolutePath(File f) throws IOException {
return doPrivilegedIOWithReturn( () -> f.getAbsolutePath());
}
@Override
public long length(File f) throws IOException {
return doPrivilegedIOWithReturn( () -> f.length());
}
@Override
public long fileSize(Path p) throws IOException {
return doPrivilegedIOWithReturn( () -> Files.size(p));
}
@Override
public boolean exists(Path p) throws IOException {
return doPrivilegedIOWithReturn( () -> Files.exists(p));
}
@Override
public boolean isDirectory(Path p) {
return doPrivilegedWithReturn( () -> Files.isDirectory(p));
}
@Override
public FileTime getLastModified(Path p) throws IOException {
// Timestamp only needed when examining repository for other JVMs,
// in which case an unprivileged mode should be used.
throw new InternalError("Should not reach here");
try {
return MethodHandles.privateLookupIn(lookupClass, LOOKUP).defineClass(bytes);
} catch (IllegalAccessException e) {
throw new InternalError(e);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -25,22 +25,18 @@
package jdk.jfr.internal;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import jdk.jfr.RecordingState;
/**
* Class responsible for dumping recordings on exit
*
*/
final class ShutdownHook implements Runnable {
final class ShutdownHook extends Thread {
private final PlatformRecorder recorder;
Object tlabDummyObject;
ShutdownHook(PlatformRecorder recorder) {
super("JFR Shutdown Hook");
this.recorder = recorder;
}
@ -61,7 +57,7 @@ final class ShutdownHook implements Runnable {
private void dump(PlatformRecording recording) {
try {
WriteableUserPath dest = recording.getDestination();
WriteablePath dest = recording.getDestination();
if (dest == null) {
dest = recording.makeDumpPath();
recording.setDestination(dest);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -189,7 +189,7 @@ public final class TypeLibrary {
Modules.addExports(proxyModule, proxyPackage, jfrModule);
}
}
SecuritySupport.setAccessible(m);
m.setAccessible(true);
try {
return m.invoke(annotation, new Object[0]);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -22,34 +22,42 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal.periodic;
import jdk.internal.event.Event;
import jdk.jfr.internal.util.Utils;
package jdk.jfr.internal;
/**
* Periodic task that runs trusted code that doesn't require an access control
* context.
* <p>
* This class can be removed once the Security Manager is no longer supported.
*/
final class JDKEventTask extends JavaEventTask {
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public JDKEventTask(Class<? extends Event> eventClass, Runnable runnable) {
super(eventClass, runnable);
if (!getEventType().isJDK()) {
throw new InternalError("Must be a JDK event");
}
if (!Utils.isJDKClass(eventClass)) {
throw new SecurityException("Periodic task can only be registered for event classes that belongs to the JDK");
}
if (!Utils.isJDKClass(runnable.getClass())) {
throw new SecurityException("Runnable class must belong to the JDK");
public final class WriteablePath {
private final Path path;
private final Path real;
public WriteablePath(Path path) throws IOException {
// verify that the path is writeable
if (Files.exists(path) && !Files.isWritable(path)) {
// throw same type of exception as FileOutputStream
// constructor, if file can't be opened.
throw new FileNotFoundException("Could not write to file: " + path.toAbsolutePath());
}
// will throw if non-writeable
BufferedWriter fw = Files.newBufferedWriter(path);
fw.close();
this.path = path;
this.real = path.toRealPath();
}
@Override
public void execute(long timestamp, PeriodicType periodicType) {
getRunnable().run();
public Path getPath() {
return path;
}
public Path getReal() {
return real;
}
public String getRealPathText() {
return real.toString();
}
}

View File

@ -1,148 +0,0 @@
/*
* Copyright (c) 2016, 2022, 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 jdk.jfr.internal;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.concurrent.Callable;
/**
* Purpose of this class is to simplify analysis of security risks.
* <p>
* Paths in the public API should be wrapped in this class so we
* at all time know what kind of paths we are dealing with.
* <p>
* A user supplied path must never be used in an unsafe context, such as a
* shutdown hook or any other thread created by JFR.
* <p>
* All operation using this path must happen in {@link #doPrivilegedIO(Callable)}
*/
public final class WriteableUserPath {
@SuppressWarnings("removal")
private final AccessControlContext controlContext;
private final Path original;
private final Path real;
private final String realPathText;
private final String originalText;
// Not to ensure security, but to help
// against programming errors
private volatile boolean inPrivileged;
@SuppressWarnings("removal")
public WriteableUserPath(Path path) throws IOException {
controlContext = AccessController.getContext();
// verify that the path is writeable
if (Files.exists(path) && !Files.isWritable(path)) {
// throw same type of exception as FileOutputStream
// constructor, if file can't be opened.
throw new FileNotFoundException("Could not write to file: " + path.toAbsolutePath());
}
// will throw if non-writeable
BufferedWriter fw = Files.newBufferedWriter(path);
fw.close();
this.original = path;
this.originalText = path.toString();
this.real = path.toRealPath();
this.realPathText = real.toString();
}
/**
* Returns a potentially malicious path where the user may have implemented
* their own version of Path. This method should never be called in an
* unsafe context and the Path value should never be passed along to other
* methods.
*
* @return path from a potentially malicious user
*/
public Path getPotentiallyMaliciousOriginal() {
return original;
}
/**
* Returns a string representation of the real path.
*
* @return path as text
*/
public String getRealPathText() {
return realPathText;
}
/**
* Returns a string representation of the original path.
*
* @return path as text
*/
public String getOriginalText() {
return originalText;
}
/**
* Returns a potentially malicious path where the user may have implemented
* their own version of Path. This method should never be called in an
* unsafe context and the Path value should never be passed along to other
* methods.
*
* @return path from a potentially malicious user
*/
public Path getReal() {
if (!inPrivileged) {
throw new InternalError("A user path was accessed outside the context it was supplied in");
}
return real;
}
@SuppressWarnings("removal")
public void doPrivilegedIO(Callable<?> function) throws IOException {
try {
inPrivileged = true;
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
function.call();
return null;
}
}, controlContext);
} catch (Throwable t) {
// prevent malicious user to propagate exception callback
// in the wrong context
Throwable cause = null;
if (System.getSecurityManager() == null) {
cause = t;
}
throw new IOException("Unexpected error during I/O operation", cause);
} finally {
inPrivileged = false;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, 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
@ -26,9 +26,6 @@
package jdk.jfr.internal.consumer;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
@ -46,7 +43,6 @@ import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.SecuritySupport;
/*
* Purpose of this class is to simplify the implementation of
@ -57,8 +53,6 @@ public abstract class AbstractEventStream implements EventStream {
private final CountDownLatch terminated = new CountDownLatch(1);
private final Runnable flushOperation = () -> dispatcher().runFlushActions();
@SuppressWarnings("removal")
private final AccessControlContext accessControllerContext;
private final StreamConfiguration streamConfiguration = new StreamConfiguration();
private final List<Configuration> configurations;
private final ParserState parserState = new ParserState();
@ -67,8 +61,7 @@ public abstract class AbstractEventStream implements EventStream {
private boolean daemon = false;
AbstractEventStream(@SuppressWarnings("removal") AccessControlContext acc, List<Configuration> configurations) throws IOException {
this.accessControllerContext = Objects.requireNonNull(acc);
AbstractEventStream(List<Configuration> configurations) throws IOException {
this.configurations = configurations;
}
@ -221,22 +214,21 @@ public abstract class AbstractEventStream implements EventStream {
public final void startAsync(long startNanos) {
startInternal(startNanos);
Runnable r = () -> run(accessControllerContext);
Thread thread = SecuritySupport.createThreadWitNoPermissions(nextThreadName(), r);
SecuritySupport.setDaemonThread(thread, daemon);
Runnable r = () -> execute();
Thread thread = new Thread(r, nextThreadName());
thread.setDaemon(daemon);
thread.start();
}
public final void start(long startNanos) {
startInternal(startNanos);
run(accessControllerContext);
execute();
}
protected final Runnable getFlushOperation() {
return flushOperation;
}
protected final void onFlush() {
Runnable r = getFlushOperation();
if (r != null) {
@ -276,17 +268,6 @@ public abstract class AbstractEventStream implements EventStream {
}
}
@SuppressWarnings("removal")
private void run(AccessControlContext accessControlContext) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
execute();
return null;
}
}, accessControlContext);
}
private String nextThreadName() {
return "JFR Event Stream " + counter.incrementAndGet();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, 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
@ -27,12 +27,10 @@ package jdk.jfr.internal.consumer;
import java.io.IOException;
import java.nio.file.Path;
import java.security.AccessControlContext;
import java.time.Instant;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
@ -44,7 +42,6 @@ import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.PlatformRecording;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.util.Utils;
import jdk.jfr.internal.management.StreamBarrier;
@ -58,7 +55,6 @@ public final class EventDirectoryStream extends AbstractEventStream {
private static final Comparator<? super RecordedEvent> EVENT_COMPARATOR = JdkJfrConsumer.instance().eventComparator();
private final RepositoryFiles repositoryFiles;
private final FileAccess fileAccess;
private final PlatformRecording recording;
private final StreamBarrier barrier = new StreamBarrier();
private final AtomicLong streamId = new AtomicLong();
@ -69,20 +65,13 @@ public final class EventDirectoryStream extends AbstractEventStream {
private volatile Consumer<Long> onCompleteHandler;
public EventDirectoryStream(
@SuppressWarnings("removal")
AccessControlContext acc,
Path p,
FileAccess fileAccess,
PlatformRecording recording,
List<Configuration> configurations,
boolean allowSubDirectories) throws IOException {
super(acc, configurations);
super(configurations);
this.recording = recording;
if (p != null && SecuritySupport.PRIVILEGED == fileAccess) {
throw new SecurityException("Priviliged file access not allowed with potentially malicious Path implementation");
}
this.fileAccess = Objects.requireNonNull(fileAccess);
this.repositoryFiles = new RepositoryFiles(fileAccess, p, allowSubDirectories);
this.repositoryFiles = new RepositoryFiles(p, allowSubDirectories);
this.streamId.incrementAndGet();
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Stream " + streamId + " started.");
}
@ -153,7 +142,7 @@ public final class EventDirectoryStream extends AbstractEventStream {
return;
}
currentChunkStartNanos = repositoryFiles.getTimestamp(path);
try (RecordingInput input = new RecordingInput(path.toFile(), fileAccess)) {
try (RecordingInput input = new RecordingInput(path.toFile())) {
input.setStreamed();
currentParser = new ChunkParser(input, disp.parserConfiguration, parserState());
long segmentStart = currentParser.getStartNanos() + currentParser.getChunkDuration();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, 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
@ -27,7 +27,6 @@ package jdk.jfr.internal.consumer;
import java.io.IOException;
import java.nio.file.Path;
import java.security.AccessControlContext;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
@ -45,9 +44,9 @@ public final class EventFileStream extends AbstractEventStream {
private ChunkParser currentParser;
private RecordedEvent[] cacheSorted;
public EventFileStream(@SuppressWarnings("removal") AccessControlContext acc, Path file) throws IOException {
super(acc, Collections.emptyList());
this.input = new RecordingInput(file.toFile(), FileAccess.UNPRIVILEGED);
public EventFileStream(Path file) throws IOException {
super(Collections.emptyList());
this.input = new RecordingInput(file.toFile());
this.input.setStreamed();
}

View File

@ -1,97 +0,0 @@
/*
* Copyright (c) 2019, 2021, 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 jdk.jfr.internal.consumer;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
// Protected by modular boundaries.
public abstract class FileAccess {
public static final FileAccess UNPRIVILEGED = new UnPrivileged();
public abstract RandomAccessFile openRAF(File f, String mode) throws IOException;
public abstract DirectoryStream<Path> newDirectoryStream(Path repository) throws IOException;
public abstract String getAbsolutePath(File f) throws IOException;
public abstract long length(File f) throws IOException;
public abstract long fileSize(Path p) throws IOException;
public abstract boolean exists(Path s) throws IOException;
public abstract boolean isDirectory(Path p);
public abstract FileTime getLastModified(Path p) throws IOException;
private static class UnPrivileged extends FileAccess {
@Override
public RandomAccessFile openRAF(File f, String mode) throws IOException {
return new RandomAccessFile(f, mode);
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir) throws IOException {
return Files.newDirectoryStream(dir);
}
@Override
public String getAbsolutePath(File f) throws IOException {
return f.getAbsolutePath();
}
@Override
public long length(File f) throws IOException {
return f.length();
}
@Override
public long fileSize(Path p) throws IOException {
return Files.size(p);
}
@Override
public boolean exists(Path p) {
return Files.exists(p);
}
@Override
public boolean isDirectory(Path p) {
return Files.isDirectory(p);
}
@Override
public FileTime getLastModified(Path p) throws IOException {
return Files.getLastModifiedTime(p);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025, 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
@ -26,12 +26,12 @@ package jdk.jfr.internal.consumer;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import jdk.jfr.Recording;
import jdk.jfr.RecordingState;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.management.EventByteStream;
import jdk.jfr.internal.management.HiddenWait;
import jdk.jfr.internal.management.ManagementSupport;
@ -63,7 +63,7 @@ public final class OngoingStream extends EventByteStream {
this.blockSize = blockSize;
this.startTimeNanos = startTimeNanos;
this.endTimeNanos = endTimeNanos;
this.repositoryFiles = new RepositoryFiles(SecuritySupport.PRIVILEGED, null, false);
this.repositoryFiles = new RepositoryFiles(null, false);
}
@Override
@ -206,10 +206,10 @@ public final class OngoingStream extends EventByteStream {
private boolean ensureInput() throws IOException {
if (input == null) {
if (SecuritySupport.getFileSize(new SafePath(path)) < HEADER_SIZE) {
if (Files.size(path) < HEADER_SIZE) {
return false;
}
input = new RecordingInput(path.toFile(), SecuritySupport.PRIVILEGED);
input = new RecordingInput(path.toFile());
input.setStreamed();
header = new ChunkHeader(input);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -33,7 +33,6 @@ import java.io.RandomAccessFile;
import java.nio.file.Path;
import jdk.jfr.internal.management.HiddenWait;
import jdk.jfr.internal.util.Utils;
public final class RecordingInput implements DataInput, AutoCloseable {
@ -68,7 +67,6 @@ public final class RecordingInput implements DataInput, AutoCloseable {
}
}
private final int blockSize;
private final FileAccess fileAccess;
private final HiddenWait threadSleeper = new HiddenWait();
private long pollCount = 1000;
private RandomAccessFile file;
@ -79,26 +77,25 @@ public final class RecordingInput implements DataInput, AutoCloseable {
private long size = -1; // Fail fast if setSize(...) has not been called
// before parsing
RecordingInput(File f, FileAccess fileAccess, int blockSize) throws IOException {
RecordingInput(File f, int blockSize) throws IOException {
this.blockSize = blockSize;
this.fileAccess = fileAccess;
initialize(f);
}
private void initialize(File f) throws IOException {
this.filename = fileAccess.getAbsolutePath(f);
this.file = fileAccess.openRAF(f, "r");
this.filename = f.getAbsolutePath();
this.file = new RandomAccessFile(f, "r");
this.position = 0;
this.size = -1;
this.currentBlock.reset();
previousBlock.reset();
if (fileAccess.length(f) < 8) {
throw new IOException("Not a valid Flight Recorder file. File length is only " + fileAccess.length(f) + " bytes.");
if (f.length() < 8) {
throw new IOException("Not a valid Flight Recorder file. File length is only " + f.length() + " bytes.");
}
}
public RecordingInput(File f, FileAccess fileAccess) throws IOException {
this(f, fileAccess, DEFAULT_BLOCK_SIZE);
public RecordingInput(File f) throws IOException {
this(f, DEFAULT_BLOCK_SIZE);
}
void positionPhysical(long position) throws IOException {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, 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
@ -28,6 +28,7 @@ package jdk.jfr.internal.consumer;
import java.io.IOException;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
@ -45,7 +46,6 @@ import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.Repository;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.management.HiddenWait;;
public final class RepositoryFiles {
@ -57,7 +57,6 @@ public final class RepositoryFiles {
}
}
private final FileAccess fileAccess;
private final NavigableMap<Long, Path> pathSet = new TreeMap<>();
private final Map<Path, Long> pathLookup = new HashMap<>();
private final HiddenWait waitObject;
@ -65,9 +64,8 @@ public final class RepositoryFiles {
private volatile boolean closed;
private Path repository;
public RepositoryFiles(FileAccess fileAccess, Path repository, boolean allowSubDirectory) {
public RepositoryFiles(Path repository, boolean allowSubDirectory) {
this.repository = repository;
this.fileAccess = fileAccess;
this.waitObject = repository == null ? WAIT_OBJECT : new HiddenWait();
this.allowSubDirectory = allowSubDirectory;
}
@ -172,14 +170,14 @@ public final class RepositoryFiles {
if (repoPath == null) {
// Always get the latest repository if 'jcmd JFR.configure
// repositorypath=...' has been executed
SafePath sf = Repository.getRepository().getRepositoryPath();
if (sf == null) {
Path path = Repository.getRepository().getRepositoryPath();
if (path == null) {
return false; // not initialized
}
repoPath = sf.toPath();
repoPath = path;
}
try (DirectoryStream<Path> dirStream = fileAccess.newDirectoryStream(repoPath)) {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(repoPath)) {
List<Path> added = new ArrayList<>();
Set<Path> current = new HashSet<>();
for (Path p : dirStream) {
@ -208,7 +206,7 @@ public final class RepositoryFiles {
for (Path p : added) {
// Only add files that have a complete header
// as the JVM may be in progress writing the file
long size = fileAccess.fileSize(p);
long size = Files.size(p);
if (size >= ChunkHeader.headerSize()) {
long startNanos = readStartTime(p);
if (startNanos != -1) {
@ -232,10 +230,10 @@ public final class RepositoryFiles {
private Path findSubDirectory(Path repoPath) {
FileTime latestTimestamp = null;
Path latestPath = null;
try (DirectoryStream<Path> dirStream = fileAccess.newDirectoryStream(repoPath)) {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(repoPath)) {
for (Path p : dirStream) {
String filename = p.getFileName().toString();
if (isRepository(filename) && fileAccess.isDirectory(p)) {
if (isRepository(filename) && Files.isDirectory(p)) {
FileTime timestamp = getLastModified(p);
if (timestamp != null) {
if (latestPath == null || latestTimestamp.compareTo(timestamp) <= 0) {
@ -253,7 +251,7 @@ public final class RepositoryFiles {
private FileTime getLastModified(Path p) {
try {
return fileAccess.getLastModified(p);
return Files.getLastModifiedTime(p);
} catch (IOException e) {
return null;
}
@ -277,7 +275,7 @@ public final class RepositoryFiles {
}
private long readStartTime(Path p) {
try (RecordingInput in = new RecordingInput(p.toFile(), fileAccess, 100)) {
try (RecordingInput in = new RecordingInput(p.toFile(), 100)) {
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Parsing header for chunk start time");
ChunkHeader c = new ChunkHeader(in);
return c.getStartNanos();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
@ -38,7 +38,6 @@ import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.internal.LongMap;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ChunkHeader;
import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
@ -67,7 +66,7 @@ public final class ChunkWriter implements Closeable {
public ChunkWriter(Path source, Path destination, Predicate<RecordedEvent> filter) throws IOException {
this.destination = destination;
this.output = new RecordingOutput(destination.toFile());
this.input = new RecordingInput(source.toFile(), FileAccess.UNPRIVILEGED);
this.input = new RecordingInput(source.toFile());
this.filter = filter;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, 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
@ -43,8 +43,6 @@ import jdk.jfr.internal.JVMSupport;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.util.ValueFormatter;
/**
@ -134,7 +132,7 @@ abstract class AbstractDCmd {
return JVM.getPid();
}
protected final SafePath resolvePath(Recording recording, String filename) throws InvalidPathException {
protected Path resolvePath(Recording recording, String filename) throws InvalidPathException {
if (filename == null) {
return makeGenerated(recording, Paths.get("."));
}
@ -142,11 +140,11 @@ abstract class AbstractDCmd {
if (Files.isDirectory(path)) {
return makeGenerated(recording, path);
}
return new SafePath(path.toAbsolutePath().normalize());
return path.toAbsolutePath().normalize();
}
private SafePath makeGenerated(Recording recording, Path directory) {
return new SafePath(directory.toAbsolutePath().resolve(JVMSupport.makeFilename(recording)).normalize());
private Path makeGenerated(Recording recording, Path directory) {
return directory.toAbsolutePath().resolve(JVMSupport.makeFilename(recording)).normalize();
}
protected final Recording findRecording(String name) throws DCmdException {
@ -158,7 +156,7 @@ abstract class AbstractDCmd {
}
}
protected final void reportOperationComplete(String actionPrefix, String name, SafePath file) {
protected final void reportOperationComplete(String actionPrefix, String name, Path file) {
print(actionPrefix);
print(" recording");
if (name != null) {
@ -168,7 +166,7 @@ abstract class AbstractDCmd {
print(",");
try {
print(" ");
long bytes = SecuritySupport.getFileSize(file);
long bytes = Files.size(file);
printBytes(bytes);
} catch (IOException e) {
// Ignore, not essential
@ -219,16 +217,12 @@ abstract class AbstractDCmd {
print(ValueFormatter.formatTimespan(timespan, separator));
}
protected final void printPath(SafePath path) {
protected final void printPath(Path path) {
if (path == null) {
print("N/A");
return;
}
try {
printPath(SecuritySupport.getAbsolutePath(path).toPath());
} catch (IOException ioe) {
printPath(path.toPath());
}
println(path.toAbsolutePath().toString());
}
protected final void printHelpText() {
@ -237,15 +231,6 @@ abstract class AbstractDCmd {
}
}
protected final void printPath(Path path) {
try {
println(path.toAbsolutePath().toString());
} catch (SecurityException e) {
// fall back on filename
println(path.toString());
}
}
private Recording findRecordingById(int id) throws DCmdException {
for (Recording r : getFlightRecorder().getRecordings()) {
if (r.getId() == id) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -26,6 +26,7 @@
package jdk.jfr.internal.dcmd;
import java.io.IOException;
import java.nio.file.Path;
import jdk.jfr.FlightRecorder;
import jdk.jfr.internal.LogLevel;
@ -34,7 +35,6 @@ import jdk.jfr.internal.Logger;
import jdk.jfr.internal.Options;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.Repository;
import jdk.jfr.internal.SecuritySupport.SafePath;
/**
* JFR.configure - invoked from native
@ -89,7 +89,7 @@ final class DCmdConfigure extends AbstractDCmd {
boolean updated = false;
if (repositoryPath != null) {
try {
SafePath s = new SafePath(repositoryPath);
Path s = Path.of(repositoryPath);
if (FlightRecorder.isInitialized()) {
PrivateAccess.getInstance().getPlatformRecorder().migrate(s);
} else {
@ -115,7 +115,7 @@ final class DCmdConfigure extends AbstractDCmd {
if (dumpPath != null) {
try {
Options.setDumpPath(new SafePath(dumpPath));
Options.setDumpPath(Path.of(dumpPath));
} catch (IOException e) {
throw new DCmdException("Could not set " + dumpPath + " to emergency dump path. " + e.getMessage(), e);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, 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
@ -40,9 +40,8 @@ import jdk.jfr.Recording;
import jdk.jfr.internal.PlatformRecorder;
import jdk.jfr.internal.PlatformRecording;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.util.ValueParser;
import jdk.jfr.internal.WriteableUserPath;
import jdk.jfr.internal.WriteablePath;
/**
* JFR.dump
@ -126,17 +125,16 @@ final class DCmdDump extends AbstractDCmd {
// If a filename exist, use it
// if a filename doesn't exist, use destination set earlier
// if destination doesn't exist, generate a filename
WriteableUserPath wup = null;
WriteablePath wp = null;
if (recording != null) {
PlatformRecording pRecording = PrivateAccess.getInstance().getPlatformRecording(recording);
wup = pRecording.getDestination();
wp = pRecording.getDestination();
}
if (filename != null || (filename == null && wup == null) ) {
SafePath safe = resolvePath(recording, filename);
wup = new WriteableUserPath(safe.toPath());
if (filename != null || (filename == null && wp == null) ) {
wp = new WriteablePath(resolvePath(recording, filename));
}
r.dumpStopped(wup);
reportOperationComplete("Dumped", name, new SafePath(wup.getRealPathText()));
r.dumpStopped(wp);
reportOperationComplete("Dumped", name, wp.getReal());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, 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
@ -29,8 +29,6 @@ import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.text.ParseException;
import java.time.Duration;
import java.util.HashSet;
@ -48,8 +46,6 @@ import jdk.jfr.internal.Logger;
import jdk.jfr.internal.OldObjectSample;
import jdk.jfr.internal.PlatformRecording;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.jfc.JFC;
import jdk.jfr.internal.jfc.model.JFCModel;
@ -154,7 +150,7 @@ final class DCmdStart extends AbstractDCmd {
}
recording.setSettings(s);
SafePath safePath = null;
Path dumpPath = null;
// Generate dump filename if user has specified a time-bound recording
if (duration != null && path == null) {
@ -173,10 +169,10 @@ final class DCmdStart extends AbstractDCmd {
// Purposely avoid generating filename in Recording#setDestination due to
// security concerns
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
pr.setDumpDirectory(new SafePath(p));
pr.setDumpDirectory(p);
} else {
safePath = resolvePath(recording, path);
recording.setDestination(safePath.toPath());
dumpPath = resolvePath(recording, path);
recording.setDestination(dumpPath);
}
} catch (IOException | InvalidPathException e) {
recording.close();
@ -221,10 +217,10 @@ final class DCmdStart extends AbstractDCmd {
recording.setMaxSize(250*1024L*1024L);
}
if (safePath != null && duration != null) {
if (dumpPath != null && duration != null) {
println(" The result will be written to:");
println();
printPath(safePath);
printPath(dumpPath);
} else {
println();
println();
@ -256,7 +252,7 @@ final class DCmdStart extends AbstractDCmd {
JFCModel model = new JFCModel(l -> logWarning(l));
for (String setting : settings) {
try {
model.parse(JFC.createSafePath(setting));
model.parse(JFC.ofPath(setting));
} catch (InvalidPathException | IOException | JFCModelException | ParseException e) {
throw new DCmdException(JFC.formatException("Could not", e, setting), e);
}
@ -463,8 +459,8 @@ final class DCmdStart extends AbstractDCmd {
private static String jfcOptions() {
try {
StringBuilder sb = new StringBuilder();
for (SafePath s : SecuritySupport.getPredefinedJFCFiles()) {
String name = JFC.nameFromPath(s.toPath());
for (Path s : JFC.getPredefined()) {
String name = JFC.nameFromPath(s);
JFCModel model = JFCModel.create(s, l -> {});
sb.append('\n');
sb.append("Options for ").append(name).append(":\n");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2025, 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
@ -26,12 +26,12 @@ package jdk.jfr.internal.dcmd;
import java.io.IOException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import jdk.jfr.Recording;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.WriteableUserPath;
import jdk.jfr.internal.WriteablePath;
/**
* JFR.stop
@ -47,20 +47,20 @@ final class DCmdStop extends AbstractDCmd {
String filename = parser.getOption("filename");
try {
Recording recording = findRecording(name);
WriteableUserPath path = PrivateAccess.getInstance().getPlatformRecording(recording).getDestination();
SafePath safePath = path == null ? null : new SafePath(path.getRealPathText());
WriteablePath wp = PrivateAccess.getInstance().getPlatformRecording(recording).getDestination();
Path path = wp == null ? null : wp.getReal();
if (filename != null) {
try {
// Ensure path is valid. Don't generate safePath if filename == null, as a user may
// Ensure path is valid. Don't generate path if filename == null, as a user may
// want to stop recording without a dump
safePath = resolvePath(null, filename);
path = resolvePath(null, filename);
recording.setDestination(Paths.get(filename));
} catch (IOException | InvalidPathException e) {
throw new DCmdException("Failed to stop %s. Could not set destination for \"%s\" to file %s", recording.getName(), filename, e.getMessage());
}
}
recording.stop();
reportOperationComplete("Stopped", recording.getName(), safePath);
reportOperationComplete("Stopped", recording.getName(), path);
recording.close();
} catch (InvalidPathException | DCmdException e) {
if (filename != null) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -27,7 +27,6 @@ package jdk.jfr.internal.event;
import jdk.internal.misc.Unsafe;
import jdk.jfr.internal.Bits;
import jdk.jfr.internal.EventWriterKey;
import jdk.jfr.internal.StringPool;
import jdk.jfr.internal.JVM;
import jdk.jfr.internal.PlatformEventType;
@ -37,21 +36,8 @@ import jdk.jfr.internal.consumer.StringParser;
// would allow it to write arbitrary data into buffers, potentially from
// different threads.
//
// This is prevented in three ways:
//
// 1. For code to access the jdk.jfr.internal.event package
// at least one event class (for a particular module) must be
// registered having FlightRecorderPermission("registerEvent").
//
// 2. The EventWriter EventWriterFactory::getEventWriter(long) method can only be linked from
// the UserEvent::commit() method instrumented by JFR. This is ensured by the JVM.
// (The EventWriterFactory class is dynamically generated before the first event
// is instrumented. See EventWriterFactoryRecipe)
//
// 3. Steps 1 and 2 are sufficient to make it fully secure, with or without a Security
// Manager, but as an additional measure, the method EventWriterFactory::getEventWriter(long)
// requires the caller to provide a key that is hard to guess. The key is generated
// into the bytecode of the method invoking getEventWriter(long).
// The EventWriter EventWriterFactory::getEventWriter(long) method can only be linked from
// the UserEvent::commit() method instrumented by JFR. This is ensured by the JVM.
//
public final class EventWriter {
@ -71,6 +57,14 @@ public final class EventWriter {
private PlatformEventType eventType;
private boolean largeSize = false;
public static EventWriter getEventWriter() {
EventWriter ew = JVM.getEventWriter();
if (ew != null) {
return ew;
}
return JVM.newEventWriter();
}
// User code must not be able to instantiate
private EventWriter() {
threadID = 0;
@ -239,11 +233,9 @@ public final class EventWriter {
}
public boolean beginEvent(EventConfiguration configuration, long typeId) {
// Malicious code could take the EventConfiguration object from one
// event class field and assign it to another. This check makes sure
// the event type matches what was added by instrumentation.
// This check makes sure the event type matches what was added by instrumentation.
if (configuration.getId() != typeId) {
EventWriterKey.block();
throw new InternalError("Unexpected type id " + typeId);
}
if (excluded) {
// thread is excluded from writing events

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -46,18 +46,33 @@ import jdk.jfr.internal.jfc.model.JFCModelException;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.util.Utils;
/**
* {@link Configuration} factory for JFC files. *
*/
public final class JFC {
private static final Path JFC_DIRECTORY = Utils.getPathInProperty("java.home", "lib/jfr");
private static final int BUFFER_SIZE = 8192;
private static final int MAXIMUM_FILE_SIZE = 1024 * 1024;
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
private static volatile List<KnownConfiguration> knownConfigurations;
public static List<Path> getPredefined() {
List<Path> list = new ArrayList<>();
try (var ds = Files.newDirectoryStream(JFC_DIRECTORY)) {
for (Path path : ds) {
String text = path.toString();
if (text.endsWith(".jfc") && !Files.isDirectory(path)) {
list.add(path);
}
}
} catch (IOException ioe) {
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not access .jfc-files in " + JFC_DIRECTORY + ", " + ioe.getMessage());
}
return list;
}
/**
* Reads a known configuration file (located into a string, but doesn't
* parse it until it's being used.
@ -66,14 +81,14 @@ public final class JFC {
private final String content;
private final String filename;
private final String name;
private final SafePath path;
private final Path path;
private Configuration configuration;
public KnownConfiguration(SafePath knownPath) throws IOException {
public KnownConfiguration(Path knownPath) throws IOException {
this.path = knownPath;
this.content = readContent(knownPath);
this.name = nameFromPath(knownPath.toPath());
this.filename = nullSafeFileName(knownPath.toPath());
this.name = nameFromPath(knownPath);
this.filename = nullSafeFileName(knownPath);
}
public boolean isNamed(String name) {
@ -91,12 +106,12 @@ public final class JFC {
return name;
}
private static String readContent(SafePath knownPath) throws IOException {
if (SecuritySupport.getFileSize(knownPath) > MAXIMUM_FILE_SIZE) {
private static String readContent(Path knownPath) throws IOException {
if (Files.size(knownPath) > MAXIMUM_FILE_SIZE) {
throw new IOException("Configuration with more than "
+ MAXIMUM_FILE_SIZE + " characters can't be read.");
}
try (InputStream r = SecuritySupport.newFileInputStream(knownPath)) {
try (InputStream r = Files.newInputStream(knownPath)) {
return JFC.readContent(r);
}
}
@ -114,10 +129,7 @@ public final class JFC {
* @throws ParseException if the file can't be parsed
* @throws IOException if the file can't be read
*
* @throws SecurityException if a security manager exists and its
* {@code checkRead} method denies read access to the file
* @see java.io.File#getPath()
* @see java.lang.SecurityManager#checkRead(java.lang.String)
*/
public static Configuration create(String name, Reader reader) throws IOException, ParseException {
try {
@ -136,12 +148,12 @@ public final class JFC {
*
* @param path textual representation of the path
*
* @return a safe path, not null
* @return a path, not null
*/
public static SafePath createSafePath(String path) {
for (SafePath predefined : SecuritySupport.getPredefinedJFCFiles()) {
public static Path ofPath(String path) {
for (Path predefined : JFC.getPredefined()) {
try {
String name = JFC.nameFromPath(predefined.toPath());
String name = JFC.nameFromPath(predefined);
if (name.equals(path) || (name + ".jfc").equals(path)) {
return predefined;
}
@ -149,7 +161,7 @@ public final class JFC {
throw new InternalError("Error in predefined .jfc file", e);
}
}
return new SafePath(path);
return Path.of(path);
}
@ -172,20 +184,19 @@ public final class JFC {
// Invoked by DCmdStart
public static Configuration createKnown(String name) throws IOException, ParseException {
// Known name, no need for permission
for (KnownConfiguration known : getKnownConfigurations()) {
if (known.isNamed(name)) {
return known.getConfigurationFile();
}
}
// Check JFC directory
SafePath path = SecuritySupport.JFC_DIRECTORY;
if (path != null && SecuritySupport.exists(path)) {
Path path = JFC_DIRECTORY;
if (path != null && Files.exists(path)) {
for (String extension : Arrays.asList("", JFCParser.FILE_EXTENSION)) {
SafePath file = new SafePath(path.toPath().resolveSibling(name + extension));
if (SecuritySupport.exists(file) && !SecuritySupport.isDirectory(file)) {
try (Reader r = SecuritySupport.newFileReader(file)) {
String jfcName = nameFromPath(file.toPath());
Path file = path.resolveSibling(name + extension);
if (Files.exists(file) && !Files.isDirectory(file)) {
try (Reader r = Files.newBufferedReader(file)) {
String jfcName = nameFromPath(file);
return JFCParser.createConfiguration(jfcName, r);
}
}
@ -260,7 +271,7 @@ public final class JFC {
private static List<KnownConfiguration> getKnownConfigurations() {
if (knownConfigurations == null) {
List<KnownConfiguration> configProxies = new ArrayList<>();
for (SafePath p : SecuritySupport.getPredefinedJFCFiles()) {
for (Path p : JFC.getPredefined()) {
try {
configProxies.add(new KnownConfiguration(p));
} catch (IOException ioe) {
@ -281,7 +292,7 @@ public final class JFC {
throw new NoSuchFileException("Could not locate configuration with name " + name);
}
public static Reader newReader(SafePath sf) throws IOException {
public static Reader newReader(Path sf) throws IOException {
for (KnownConfiguration c : getKnownConfigurations()) {
if (c.path.equals(sf)) {
return new StringReader(c.content);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, 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
@ -27,6 +27,7 @@ package jdk.jfr.internal.jfc.model;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.nio.file.Path;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
@ -35,7 +36,6 @@ import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.jfc.JFC;
import static java.nio.charset.StandardCharsets.UTF_8;
@ -65,7 +65,7 @@ public final class JFCModel {
this.logger = logger;
}
public void parse(SafePath file) throws IOException, JFCModelException, ParseException {
public void parse(Path file) throws IOException, JFCModelException, ParseException {
JFCModel model = JFCModel.create(file, logger);
for (var entry : model.controls.entrySet()) {
String name = entry.getKey();
@ -80,7 +80,7 @@ public final class JFCModel {
}
}
public static JFCModel create(SafePath file, Consumer<String> logger) throws IOException, JFCModelException, ParseException{
public static JFCModel create(Path file, Consumer<String> logger) throws IOException, JFCModelException, ParseException{
if (file.toString().equals("none")) {
XmlConfiguration configuration = new XmlConfiguration();
configuration.setAttribute("version", "2.0");
@ -154,7 +154,7 @@ public final class JFCModel {
return result;
}
public void saveToFile(SafePath path) throws IOException {
public void saveToFile(Path path) throws IOException {
try (PrintWriter p = new PrintWriter(path.toFile(), UTF_8)) {
PrettyPrinter pp = new PrettyPrinter(p);
pp.print(configuration);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, 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
@ -25,13 +25,12 @@
package jdk.jfr.internal.management;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.io.IOException;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.util.ValueFormatter;
import jdk.jfr.internal.consumer.FileAccess;
// Allows a remote streaming client to create chunk files
// with same naming scheme as the JVM.
@ -40,23 +39,12 @@ public final class ChunkFilename {
private static final String FILE_EXTENSION = ".jfr";
private final Path directory;
private final FileAccess fileAcess;
private Path lastPath;
private int counter;
public static ChunkFilename newUnpriviliged(Path directory) {
return new ChunkFilename(directory, FileAccess.UNPRIVILEGED);
}
public static ChunkFilename newPriviliged(Path directory) {
return new ChunkFilename(directory, SecuritySupport.PRIVILEGED);
}
private ChunkFilename(Path directory, FileAccess fileAccess) {
// Avoid malicious implementations of Path interface
this.directory = Paths.get(directory.toString());
this.fileAcess = fileAccess;
public ChunkFilename(Path directory) {
this.directory = directory;
}
public String next(LocalDateTime time) throws IOException {
@ -65,7 +53,7 @@ public final class ChunkFilename {
// If less than one file per second (typically case)
if (lastPath == null || !p.equals(lastPath)) {
if (!fileAcess.exists(p)) {
if (!Files.exists(p)) {
counter = 1; // reset counter
lastPath = p;
return p.toString();
@ -77,7 +65,7 @@ public final class ChunkFilename {
String extendedName = makeExtendedName(filename, counter);
p = directory.resolve(extendedName);
counter++;
if (!fileAcess.exists(p)) {
if (!Files.exists(p)) {
return p.toString();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -34,8 +34,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.security.AccessControlContext;
import jdk.jfr.Configuration;
import jdk.jfr.EventSettings;
import jdk.jfr.EventType;
@ -49,15 +47,12 @@ import jdk.jfr.internal.Logger;
import jdk.jfr.internal.MetadataRepository;
import jdk.jfr.internal.PlatformRecording;
import jdk.jfr.internal.PrivateAccess;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.util.Utils;
import jdk.jfr.internal.util.ValueFormatter;
import jdk.jfr.internal.util.ValueParser;
import jdk.jfr.internal.WriteableUserPath;
import jdk.jfr.internal.WriteablePath;
import jdk.jfr.internal.consumer.AbstractEventStream;
import jdk.jfr.internal.consumer.EventDirectoryStream;
import jdk.jfr.internal.consumer.FileAccess;
/**
* The management API in module jdk.management.jfr should be built on top of the
@ -84,7 +79,6 @@ public final class ManagementSupport {
//
public static List<EventType> getEventTypes() {
// would normally be checked when a Flight Recorder instance is created
SecuritySupport.checkAccessFlightRecorder();
if (JVMSupport.isNotAvailable()) {
return List.of();
}
@ -121,17 +115,16 @@ public final class ManagementSupport {
// requires access to jdk.jfr.internal.PlatformRecording
public static String getDestinationOriginalText(Recording recording) {
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
WriteableUserPath wup = pr.getDestination();
return wup == null ? null : wup.getOriginalText();
WriteablePath wp = pr.getDestination();
return wp == null ? null : wp.getPath().toString();
}
// Needed to check if destination can be set, so FlightRecorderMXBean::setRecordingOption
// can abort if not all data is valid
public static void checkSetDestination(Recording recording, String destination) throws IOException{
public static void checkSetDestination(Recording recording, String destination) throws IOException {
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
if(destination != null){
WriteableUserPath wup = new WriteableUserPath(Paths.get(destination));
pr.checkSetDestination(wup);
pr.checkSetDestination(new WriteablePath(Paths.get(destination)));
}
}
@ -143,7 +136,7 @@ public final class ManagementSupport {
// Needed callback to detect when a chunk has been parsed.
public static void removePath(Recording recording, Path path) {
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
pr.removePath(new SafePath(path));
pr.removePath(path);
}
// Needed callback to detect when a chunk has been parsed.
@ -169,14 +162,10 @@ public final class ManagementSupport {
// EventStream::onMetadataData need to supply MetadataEvent
// with configuration objects
public static EventStream newEventDirectoryStream(
@SuppressWarnings("removal")
AccessControlContext acc,
Path directory,
List<Configuration> confs) throws IOException {
return new EventDirectoryStream(
acc,
directory,
FileAccess.UNPRIVILEGED,
null,
confs,
false

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, 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
@ -30,9 +30,9 @@ import jdk.jfr.internal.MetadataRepository;
import jdk.jfr.internal.PlatformEventType;
import jdk.jfr.internal.PrivateAccess;
/**
* Base class for periodic Java events.
* Class for periodic Java events.
*/
abstract class JavaEventTask extends EventTask {
final class JavaEventTask extends EventTask {
private final Runnable runnable;
public JavaEventTask(Class<? extends Event> eventClass, Runnable runnable) {
@ -48,7 +48,8 @@ abstract class JavaEventTask extends EventTask {
return PrivateAccess.getInstance().getPlatformEventType(eventType);
}
protected final Runnable getRunnable() {
return runnable;
@Override
public void execute(long timestamp, PeriodicType periodicType) {
runnable.run();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, 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
@ -25,11 +25,8 @@
package jdk.jfr.internal.periodic;
/**
* Lookup key that can safely be used in a {@code Map}.
* <p>
* {@code Runnable} objects can't be used with {@code LinkedHashMap} as it
* invokes {@code hashCode} and {@code equals}, for example when resizing the
* {@code Map}, possibly in a non-secure context.
* Lookup key that can be used in a {@code Map} in
* case hashCode and equals are incorrectly overridden.
* <p>
* {@code IdentityHashMap} can't be used as it will not preserve order.
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -24,7 +24,6 @@
*/
package jdk.jfr.internal.periodic;
import java.security.AccessControlContext;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
@ -58,18 +57,14 @@ public final class PeriodicEvents {
// State only to be read and modified by periodic task thread
private static long lastTimeMillis;
public static void addJDKEvent(Class<? extends Event> eventClass, Runnable runnable) {
taskRepository.add(new JDKEventTask(eventClass, runnable));
public static void addJavaEvent(Class<? extends Event> eventClass, Runnable runnable) {
taskRepository.add(new JavaEventTask(eventClass, runnable));
}
public static void addJVMEvent(PlatformEventType eventType) {
taskRepository.add(new JVMEventTask(eventType));
}
public static void addUserEvent(@SuppressWarnings("removal") AccessControlContext acc, Class<? extends Event> eventClass, Runnable runnable) {
taskRepository.add(new UserEventTask(acc, eventClass, runnable));
}
public static boolean removeEvent(Runnable runnable) {
return taskRepository.removeTask(runnable);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, 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
@ -40,9 +40,6 @@ import jdk.jfr.internal.Logger;
* / \
* / \
* JVMEventTask JavaEventTask
* / \
* / \
* UserEventTask JDKEventTask
* </pre>
* <p>
* State modifications should only be done from the periodic task thread.
@ -127,8 +124,8 @@ abstract class PeriodicTask {
try {
execute(timestamp, periodicType);
} catch (Throwable e) {
// Prevent malicious user to propagate exception callback in the wrong context
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Exception occurred during execution of " + name);
String msg = "Exception occurred during execution of " + name + ". " + e.getMessage();
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, msg);
}
}

View File

@ -1,71 +0,0 @@
/*
* Copyright (c) 2023, 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 jdk.jfr.internal.periodic;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Objects;
import jdk.internal.event.Event;
import jdk.jfr.internal.LogLevel;
import jdk.jfr.internal.LogTag;
import jdk.jfr.internal.Logger;
/**
* Class to be used with user-defined events that runs untrusted code.
* <p>
* This class can be removed once the Security Manager is no longer supported.
*/
final class UserEventTask extends JavaEventTask {
@SuppressWarnings("removal")
private final AccessControlContext controlContext;
public UserEventTask(@SuppressWarnings("removal") AccessControlContext controlContext, Class<? extends Event> eventClass, Runnable runnable) {
super(eventClass, runnable);
this.controlContext = Objects.requireNonNull(controlContext);
}
@SuppressWarnings("removal")
@Override
public void execute(long timestamp, PeriodicType periodicType) {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
execute();
return null;
}, controlContext);
}
private void execute() {
try {
getRunnable().run();
if (Logger.shouldLog(LogTag.JFR_EVENT, LogLevel.DEBUG)) {
Logger.log(LogTag.JFR_EVENT, LogLevel.DEBUG, "Executed periodic task for " + getEventType().getLogName());
}
} catch (Throwable t) {
// Prevent malicious user to propagate exception callback in the wrong context
Logger.log(LogTag.JFR_EVENT, LogLevel.WARN, "Exception occurred during execution of period task for " + getEventType().getLogName());
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025, 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
@ -28,9 +28,10 @@ package jdk.jfr.internal.settings;
import java.util.Objects;
import java.util.Set;
import jdk.jfr.SettingControl;
import jdk.jfr.internal.PlatformEventType;
abstract class BooleanSetting extends JDKSettingControl {
abstract class BooleanSetting extends SettingControl {
private final PlatformEventType eventType;
private final String defaultValue;
private String value;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2025, 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
@ -31,6 +31,7 @@ import java.util.Objects;
import java.util.Set;
import jdk.jfr.Description;
import jdk.jfr.SettingControl;
import jdk.jfr.Label;
import jdk.jfr.MetadataDefinition;
import jdk.jfr.Name;
@ -44,7 +45,7 @@ import jdk.jfr.internal.util.ValueParser;
@Description("Limit running time of event")
@Name(Type.SETTINGS_PREFIX + "Cutoff")
@Timespan
public final class CutoffSetting extends JDKSettingControl {
public final class CutoffSetting extends SettingControl {
public static final String DEFAULT_VALUE = ValueParser.INFINITY;
private String value = DEFAULT_VALUE;
private final PlatformEventType eventType;

View File

@ -1,35 +0,0 @@
/*
* Copyright (c) 2020, 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 jdk.jfr.internal.settings;
import jdk.jfr.SettingControl;
/**
* SettingControls that derive from this class avoids executing settings
* modifications in a AccessController.doPrivilege(...) block.
*/
public abstract class JDKSettingControl extends SettingControl {
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, 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
@ -33,13 +33,14 @@ import java.util.Set;
import jdk.jfr.Label;
import jdk.jfr.MetadataDefinition;
import jdk.jfr.Name;
import jdk.jfr.SettingControl;
import jdk.jfr.internal.PlatformEventType;
import jdk.jfr.internal.Type;
@MetadataDefinition
@Label("Level")
@Name(Type.SETTINGS_PREFIX + "Level")
public final class LevelSetting extends JDKSettingControl {
public final class LevelSetting extends SettingControl {
private final PlatformEventType eventType;
private final List<String> levels;
private String value;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -32,6 +32,7 @@ import jdk.jfr.Description;
import jdk.jfr.Label;
import jdk.jfr.MetadataDefinition;
import jdk.jfr.Name;
import jdk.jfr.SettingControl;
import jdk.jfr.internal.PlatformEventType;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.util.ValueParser;
@ -41,7 +42,7 @@ import static jdk.jfr.internal.util.ValueParser.MISSING;
@Label("Period")
@Description("Record event at interval")
@Name(Type.SETTINGS_PREFIX + "Period")
public final class PeriodSetting extends JDKSettingControl {
public final class PeriodSetting extends SettingControl {
private static final long typeId = Type.getTypeId(PeriodSetting.class);
public static final String EVERY_CHUNK = "everyChunk";

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -34,6 +34,7 @@ import jdk.jfr.Description;
import jdk.jfr.Label;
import jdk.jfr.MetadataDefinition;
import jdk.jfr.Name;
import jdk.jfr.SettingControl;
import jdk.jfr.Timespan;
import jdk.jfr.internal.PlatformEventType;
import jdk.jfr.internal.Type;
@ -44,7 +45,7 @@ import jdk.jfr.internal.util.ValueParser;
@Name(Type.SETTINGS_PREFIX + "Threshold")
@Description("Record event with duration above or equal to threshold")
@Timespan
public final class ThresholdSetting extends JDKSettingControl {
public final class ThresholdSetting extends SettingControl {
public static final String DEFAULT_VALUE = "0 ns";
private static final long typeId = Type.getTypeId(ThresholdSetting.class);
private String value = DEFAULT_VALUE;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, Datadog, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -36,6 +36,7 @@ import jdk.jfr.Description;
import jdk.jfr.Label;
import jdk.jfr.MetadataDefinition;
import jdk.jfr.Name;
import jdk.jfr.SettingControl;
import jdk.jfr.internal.PlatformEventType;
import jdk.jfr.internal.Throttle;
import jdk.jfr.internal.Type;
@ -47,7 +48,7 @@ import jdk.jfr.internal.util.Utils;
@Label("Throttle")
@Description("Throttles the emission rate for an event")
@Name(Type.SETTINGS_PREFIX + "Throttle")
public final class ThrottleSetting extends JDKSettingControl {
public final class ThrottleSetting extends SettingControl {
public static final String DEFAULT_VALUE = Throttle.DEFAULT;
private final PlatformEventType eventType;
private String value = DEFAULT_VALUE;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -52,7 +52,7 @@ abstract class Command {
List<Command> commands = new ArrayList<>();
commands.add(new Print());
// Uncomment when developing new queries for the view command
// commands.add(new Query());
commands.add(new Query());
commands.add(new View());
commands.add(new Configure());
commands.add(new Metadata());

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, 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
@ -38,7 +38,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.jfc.JFC;
import jdk.jfr.internal.jfc.model.AbortException;
import jdk.jfr.internal.jfc.model.JFCModel;
@ -129,7 +128,7 @@ final class Configure extends Command {
stream.println("Options for " + name + ":");
stream.println();
try {
SafePath path = JFC.createSafePath(name);
Path path = JFC.ofPath(name);
JFCModel parameters = JFCModel.create(path, l -> stream.println("Warning! " + l));
for (XmlInput input : parameters.getInputs()) {
stream.println(" " + input.getOptionSyntax());
@ -144,7 +143,7 @@ final class Configure extends Command {
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
boolean interactive = false;
boolean log = false;
SafePath output = null;
Path output = null;
Map<String, String> keyValues = new LinkedHashMap<>();
int optionCount = options.size();
while (optionCount > 0) {
@ -192,7 +191,7 @@ final class Configure extends Command {
return false;
}
private void configure(boolean interactive, boolean log, SafePath output, Map<String, String> options) throws UserDataException {
private void configure(boolean interactive, boolean log, Path output, Map<String, String> options) throws UserDataException {
UserInterface ui = new UserInterface();
if (log) {
SettingsLog.enable();
@ -201,14 +200,14 @@ final class Configure extends Command {
model.setLabel("Custom");
for (String input : inputFiles) {
try {
model.parse(JFC.createSafePath(input));
model.parse(JFC.ofPath(input));
} catch (InvalidPathException | IOException | JFCModelException | ParseException e) {
throw new UserDataException(JFC.formatException("could not", e, input));
}
}
try {
if (output == null) {
output = new SafePath(Path.of("custom.jfc"));
output = Path.of("custom.jfc");
}
for (var option : options.entrySet()) {
model.configure(option.getKey(), option.getValue());
@ -230,7 +229,7 @@ final class Configure extends Command {
}
model.saveToFile(output);
ui.println("Configuration written successfully to:");
ui.println(output.toPath().toAbsolutePath().toString());
ui.println(output.toAbsolutePath().toString());
} catch (IllegalArgumentException iae) {
throw new UserDataException(iae.getMessage());
} catch (FileNotFoundException ffe) {
@ -246,7 +245,7 @@ final class Configure extends Command {
}
}
private static SafePath filename(UserInterface ui, SafePath file) throws AbortException {
private static Path filename(UserInterface ui, Path file) throws AbortException {
ui.println();
ui.println("Filename: " + file + " (default)");
while (true) {
@ -256,7 +255,7 @@ final class Configure extends Command {
return file;
}
if (line.endsWith(".jfc")) {
return new SafePath(line);
return Path.of(line);
}
ui.println("Filename must end with .jfc.");
} catch (InvalidPathException ipe) {
@ -265,14 +264,14 @@ final class Configure extends Command {
}
}
private SafePath makeJFCPath(String file) throws UserDataException, UserSyntaxException {
private Path makeJFCPath(String file) throws UserDataException, UserSyntaxException {
if (file.startsWith("--")) {
throw new UserSyntaxException("missing file");
}
try {
Path path = Path.of(file).toAbsolutePath();
ensureFileExtension(path, ".jfc");
return new SafePath(path);
return path;
} catch (IOError ioe) {
throw new UserDataException("i/o error reading file '" + file + "', " + ioe.getMessage());
} catch (InvalidPathException ipe) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -41,7 +41,6 @@ import java.util.Deque;
import java.util.List;
import jdk.jfr.internal.consumer.ChunkHeader;
import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.RecordingInput;
import jdk.jfr.internal.util.UserDataException;
import jdk.jfr.internal.util.UserSyntaxException;
@ -166,7 +165,7 @@ final class Disassemble extends Command {
}
private List<Long> findChunkSizes(Path p) throws IOException {
try (RecordingInput input = new RecordingInput(p.toFile(), FileAccess.UNPRIVILEGED)) {
try (RecordingInput input = new RecordingInput(p.toFile())) {
List<Long> sizes = new ArrayList<>();
ChunkHeader ch = new ChunkHeader(input);
sizes.add(ch.getSize());

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -41,7 +41,6 @@ import jdk.jfr.EventType;
import jdk.jfr.internal.MetadataDescriptor;
import jdk.jfr.internal.Type;
import jdk.jfr.internal.consumer.ChunkHeader;
import jdk.jfr.internal.consumer.FileAccess;
import jdk.jfr.internal.consumer.RecordingInput;
import jdk.jfr.internal.util.UserDataException;
import jdk.jfr.internal.util.UserSyntaxException;
@ -93,7 +92,7 @@ final class Summary extends Command {
long totalDuration = 0;
long chunks = 0;
try (RecordingInput input = new RecordingInput(p.toFile(), FileAccess.UNPRIVILEGED)) {
try (RecordingInput input = new RecordingInput(p.toFile())) {
ChunkHeader first = new ChunkHeader(input);
ChunkHeader ch = first;
String eventPrefix = Type.EVENT_NAME_PREFIX;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, 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
@ -22,23 +22,33 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.internal;
package jdk.jfr.internal.util;
import jdk.jfr.internal.event.EventWriter;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
// This class is not directly used but renamed to
// jdk.jfr.internal.event.EventWriterFactory and loaded dynamically
// when the first event class is bytecode instrumented.
// See JVMUpcalls and EventWriterKey::ensureEventWriterFactory()
public final class EventWriterFactoryRecipe {
private static final long KEY = EventWriterKey.getKey();
public final class DirectoryCleaner extends SimpleFileVisitor<Path> {
public static EventWriter getEventWriter(long key) {
if (key == KEY) {
EventWriter ew = JVM.getEventWriter();
return ew != null ? ew : JVM.newEventWriter();
public static void clear(Path path) throws IOException {
Files.walkFileTree(path, new DirectoryCleaner());
}
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
Files.delete(path);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
if (exc != null) {
throw exc;
}
EventWriterKey.block();
return null; // Can't reach here.
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -25,7 +25,9 @@
package jdk.jfr.internal.util;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
@ -448,4 +450,13 @@ public final class Utils {
}
return null;
}
public static Path getPathInProperty(String prop, String subPath) {
String path = System.getProperty(prop);
if (path == null) {
return null;
}
File file = subPath == null ? new File(path) : new File(path, subPath);
return file.toPath().toAbsolutePath();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025, 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
@ -151,7 +151,7 @@ final class DiskRepository implements Closeable {
public DiskRepository(Path path, boolean deleteDirectory) throws IOException {
this.directory = path;
this.deleteDirectory = deleteDirectory;
this.chunkFilename = ChunkFilename.newUnpriviliged(path);
this.chunkFilename = new ChunkFilename(path);
}
public synchronized void write(byte[] bytes) throws IOException {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -30,9 +30,6 @@ import java.io.InputStream;
import java.io.StringReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.ParseException;
import java.time.Instant;
import java.util.ArrayList;
@ -67,7 +64,6 @@ import jdk.jfr.Configuration;
import jdk.jfr.EventType;
import jdk.jfr.FlightRecorder;
import jdk.jfr.FlightRecorderListener;
import jdk.jfr.FlightRecorderPermission;
import jdk.jfr.Recording;
import jdk.jfr.RecordingState;
import jdk.jfr.internal.management.ManagementSupport;
@ -80,12 +76,9 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
private final NotificationListener listener;
private final NotificationFilter filter;
private final Object handback;
@SuppressWarnings("removal")
private final AccessControlContext context;
@SuppressWarnings("removal")
public MXBeanListener(NotificationListener listener, NotificationFilter filter, Object handback) {
this.context = AccessController.getContext();
this.listener = listener;
this.filter = filter;
this.handback = handback;
@ -93,13 +86,7 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
@SuppressWarnings("removal")
public void recordingStateChanged(Recording recording) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
sendNotification(createNotification(recording));
return null;
}
}, context);
sendNotification(createNotification(recording));
}
}
@ -124,25 +111,21 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
@Override
public void startRecording(long id) {
MBeanUtils.checkControl();
getExistingRecording(id).start();
}
@Override
public boolean stopRecording(long id) {
MBeanUtils.checkControl();
return getExistingRecording(id).stop();
}
@Override
public void closeRecording(long id) {
MBeanUtils.checkControl();
getExistingRecording(id).close();
}
@Override
public long openStream(long id, Map<String, String> options) throws IOException {
MBeanUtils.checkControl();
if (!FlightRecorder.isInitialized()) {
throw new IllegalArgumentException("No recording available with id " + id);
}
@ -169,19 +152,16 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
@Override
public void closeStream(long streamIdentifier) throws IOException {
MBeanUtils.checkControl();
streamHandler.getStream(streamIdentifier).close();
}
@Override
public byte[] readStream(long streamIdentifier) throws IOException {
MBeanUtils.checkMonitor();
return streamHandler.getStream(streamIdentifier).read();
}
@Override
public List<RecordingInfo> getRecordings() {
MBeanUtils.checkMonitor();
if (!FlightRecorder.isInitialized()) {
return Collections.emptyList();
}
@ -190,60 +170,39 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
@Override
public List<ConfigurationInfo> getConfigurations() {
MBeanUtils.checkMonitor();
return MBeanUtils.transformList(Configuration.getConfigurations(), ConfigurationInfo::new);
}
@Override
public List<EventTypeInfo> getEventTypes() {
MBeanUtils.checkMonitor();
@SuppressWarnings("removal")
List<EventType> eventTypes = AccessController.doPrivileged(new PrivilegedAction<List<EventType>>() {
@Override
public List<EventType> run() {
return ManagementSupport.getEventTypes();
}
}, null, new FlightRecorderPermission("accessFlightRecorder"));
return MBeanUtils.transformList(eventTypes, EventTypeInfo::new);
return MBeanUtils.transformList(ManagementSupport.getEventTypes(), EventTypeInfo::new);
}
@Override
public Map<String, String> getRecordingSettings(long recording) throws IllegalArgumentException {
MBeanUtils.checkMonitor();
return getExistingRecording(recording).getSettings();
}
@Override
public void setRecordingSettings(long recording, Map<String, String> settings) throws IllegalArgumentException {
Objects.requireNonNull(settings, "settings");
MBeanUtils.checkControl();
getExistingRecording(recording).setSettings(settings);
}
@SuppressWarnings("removal")
@Override
public long newRecording() {
MBeanUtils.checkControl();
getRecorder(); // ensure notification listener is setup
return AccessController.doPrivileged(new PrivilegedAction<Recording>() {
@Override
public Recording run() {
return new Recording();
}
}, null, new FlightRecorderPermission("accessFlightRecorder")).getId();
return new Recording().getId();
}
@Override
public long takeSnapshot() {
MBeanUtils.checkControl();
return getRecorder().takeSnapshot().getId();
}
@Override
public void setConfiguration(long recording, String contents) throws IllegalArgumentException {
Objects.requireNonNull(contents, "contents");
MBeanUtils.checkControl();
try {
Configuration c = Configuration.create(new StringReader(contents));
getExistingRecording(recording).setSettings(c.getSettings());
@ -255,7 +214,6 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
@Override
public void setPredefinedConfiguration(long recording, String configurationName) throws IllegalArgumentException {
Objects.requireNonNull(configurationName, "configurationName");
MBeanUtils.checkControl();
Recording r = getExistingRecording(recording);
for (Configuration c : Configuration.getConfigurations()) {
if (c.getName().equals(configurationName)) {
@ -269,14 +227,12 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
@Override
public void copyTo(long recording, String outputFile) throws IOException {
Objects.requireNonNull(outputFile, "outputFile");
MBeanUtils.checkControl();
getExistingRecording(recording).dump(Paths.get(outputFile));
}
@Override
public void setRecordingOptions(long recording, Map<String, String> options) throws IllegalArgumentException {
Objects.requireNonNull(options, "options");
MBeanUtils.checkControl();
// Make local copy to prevent concurrent modification
Map<String, String> ops = new HashMap<String, String>(options);
for (Map.Entry<String, String> entry : ops.entrySet()) {
@ -315,7 +271,6 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
@Override
public Map<String, String> getRecordingOptions(long recording) throws IllegalArgumentException {
MBeanUtils.checkMonitor();
Recording r = getExistingRecording(recording);
Map<String, String> options = HashMap.newHashMap(10);
options.put(OPTION_DUMP_ON_EXIT, String.valueOf(r.getDumpOnExit()));
@ -330,8 +285,7 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
}
@Override
public long cloneRecording(long id, boolean stop) throws IllegalStateException, SecurityException {
MBeanUtils.checkControl();
public long cloneRecording(long id, boolean stop) throws IllegalStateException {
return getRecording(id).copy(stop).getId();
}
@ -397,16 +351,11 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
}
@SuppressWarnings("removal")
private FlightRecorder getRecorder() throws SecurityException {
private FlightRecorder getRecorder() {
// Synchronize on some private object that is always available
synchronized (streamHandler) {
if (recorder == null) {
recorder = AccessController.doPrivileged(new PrivilegedAction<FlightRecorder>() {
@Override
public FlightRecorder run() {
return FlightRecorder.getFlightRecorder();
}
}, null, new FlightRecorderPermission("accessFlightRecorder"));
recorder = FlightRecorder.getFlightRecorder();
}
return recorder;
}
@ -425,13 +374,7 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) {
MXBeanListener mxbeanListener = new MXBeanListener(listener, filter, handback);
listeners.add(mxbeanListener);
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run(){
FlightRecorder.addListener(mxbeanListener);
return null;
}
}, null, new FlightRecorderPermission("accessFlightRecorder"));
FlightRecorder.addListener(mxbeanListener);
super.addNotificationListener(listener, filter, handback);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -25,8 +25,6 @@
package jdk.management.jfr;
import java.io.IOException;
import java.lang.management.ManagementPermission;
import java.security.Permission;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
@ -43,9 +41,6 @@ import jdk.jfr.internal.management.ManagementSupport;
final class MBeanUtils {
private static final Permission monitor = new ManagementPermission("monitor");
private static final Permission control = new ManagementPermission("control");
static ObjectName createObjectName() {
try {
return new ObjectName(FlightRecorderMXBean.MXBEAN_NAME);
@ -54,22 +49,6 @@ final class MBeanUtils {
}
}
static void checkControl() {
@SuppressWarnings("removal")
SecurityManager secManager = System.getSecurityManager();
if (secManager != null) {
secManager.checkPermission(control);
}
}
static void checkMonitor() {
@SuppressWarnings("removal")
SecurityManager secManager = System.getSecurityManager();
if (secManager != null) {
secManager.checkPermission(monitor);
}
}
static <T, R> List<R> transformList(List<T> source, Function<T, R> function) {
return source.stream().map(function).collect(Collectors.toList());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025, 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
@ -32,8 +32,6 @@ 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.AccessController;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@ -44,7 +42,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.security.AccessControlException;
import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
@ -149,8 +146,6 @@ public final class RemoteRecordingStream implements EventStream {
final FlightRecorderMXBean mbean;
final long recordingId;
final EventStream stream;
@SuppressWarnings("removal")
final AccessControlContext accessControllerContext;
final DiskRepository repository;
final Instant creationTime;
final Object lock = new Object();
@ -205,9 +200,7 @@ public final class RemoteRecordingStream implements EventStream {
private RemoteRecordingStream(MBeanServerConnection connection, Path directory, boolean delete) throws IOException {
Objects.requireNonNull(connection, "connection");
Objects.requireNonNull(directory, "directory");
accessControllerContext = AccessController.getContext();
// Make sure users can't implement malicious version of a Path object.
path = Paths.get(directory.toString());
path = directory;
if (!Files.exists(path)) {
throw new IOException("Download directory doesn't exist");
}
@ -219,7 +212,7 @@ public final class RemoteRecordingStream implements EventStream {
creationTime = Instant.now();
mbean = createProxy(connection);
recordingId = createRecording();
stream = ManagementSupport.newEventDirectoryStream(accessControllerContext, path, configurations(mbean));
stream = ManagementSupport.newEventDirectoryStream(path, configurations(mbean));
stream.setStartTime(Instant.MIN);
repository = new DiskRepository(path, delete);
ManagementSupport.setOnChunkCompleteHandler(stream, new ChunkConsumer(repository));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, 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
@ -25,12 +25,12 @@ package jdk.jfr.jcmd;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import jdk.jfr.internal.Repository;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.Options;
import jdk.test.lib.Asserts;
import jdk.test.lib.Utils;
@ -131,17 +131,17 @@ public class TestJcmdConfigure {
try {
JcmdHelper.jcmd("JFR.configure", REPOSITORYPATH_SETTING_1);
SafePath initialPath = Repository.getRepository().getRepositoryPath();
Path initialPath = Repository.getRepository().getRepositoryPath();
JcmdHelper.jcmd("JFR.configure", REPOSITORYPATH_SETTING_1);
SafePath samePath = Repository.getRepository().getRepositoryPath();
Path samePath = Repository.getRepository().getRepositoryPath();
Asserts.assertTrue(samePath.equals(initialPath));
List<String> lines = Files.readAllLines(Paths.get(JFR_UNIFIED_LOG_FILE));
Asserts.assertTrue(lines.stream().anyMatch(l->l.contains(findWhat)));
JcmdHelper.jcmd("JFR.configure", REPOSITORYPATH_SETTING_2);
SafePath changedPath = Repository.getRepository().getRepositoryPath();
Path changedPath = Repository.getRepository().getRepositoryPath();
Asserts.assertFalse(changedPath.equals(initialPath));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
@ -28,7 +28,7 @@ import jdk.jfr.Registered;
@Registered(false)
public class MyCommitRegisteredFalseEvent extends E implements Runnable {
public void myCommit() {
PlaceholderEventWriterFactory.getEventWriter(4711L);
PlaceholderEventWriter.getEventWriter();
throw new RuntimeException("Should not reach here");
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
@ -28,7 +28,7 @@ import jdk.jfr.Registered;
@Registered(true)
public class MyCommitRegisteredTrueEvent extends E implements Runnable {
public void myCommit() {
PlaceholderEventWriterFactory.getEventWriter(4711L);
PlaceholderEventWriter.getEventWriter();
throw new RuntimeException("Should not reach here");
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
@ -25,7 +25,7 @@ package jdk.jfr.jvm;
// Class used by TestGetEventWriter
public class NonEvent implements Runnable {
public void commit() {
PlaceholderEventWriter ew = PlaceholderEventWriterFactory.getEventWriter(4711L);
PlaceholderEventWriter ew = PlaceholderEventWriter.getEventWriter();;
throw new RuntimeException("Should not reach here " + ew);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
@ -30,4 +30,8 @@ package jdk.jfr.jvm;
// will be replaced with "jdk.jfr.internal.event.EventWriter"
public class PlaceholderEventWriter {
public static PlaceholderEventWriter getEventWriter() {
return null;
}
}

View File

@ -1,35 +0,0 @@
/*
* Copyright (c) 2022, 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.jfr.jvm;
// Purpose of this class is to have something to
// statically link against for TestGetEventWriter.
//
// When the class is loaded "jdk.jfr.jvm.PlaceholderEventWriterFactory"
// will be replaced with "jdk.jfr.internal.event.EventWriterFactory"
public class PlaceholderEventWriterFactory {
public static PlaceholderEventWriter getEventWriter(long value) {
throw new RuntimeException("Test error, PlaceholderEventWriterFactory class should have been replaced with EventWriterFactory");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
@ -28,7 +28,7 @@ import jdk.jfr.Registered;
@Registered(false)
public class RegisteredFalseEvent extends E {
public void commit() {
PlaceholderEventWriterFactory.getEventWriter(4711L);
PlaceholderEventWriter.getEventWriter();
throw new RuntimeException("Should not reach here");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
@ -28,7 +28,7 @@ import jdk.jfr.Registered;
@Registered(true)
public class RegisteredTrueEvent extends E {
public void commit() {
PlaceholderEventWriterFactory.getEventWriter(4711L);
PlaceholderEventWriter.getEventWriter();
throw new RuntimeException("Should not reach here");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
@ -30,7 +30,7 @@ public class StaticCommitEvent implements Runnable {
int value;
public static void commit(long start, long duration, String message, int value) {
PlaceholderEventWriterFactory.getEventWriter(4711L);
PlaceholderEventWriter.getEventWriter();
throw new RuntimeException("Should not reach here");
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2025, 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
@ -46,7 +46,6 @@ import jdk.vm.ci.runtime.JVMCI;
* jdk.internal.vm.ci/jdk.vm.ci.runtime
*
* @compile PlaceholderEventWriter.java
* @compile PlaceholderEventWriterFactory.java
* @compile E.java
* @compile NonEvent.java
* @compile RegisteredTrueEvent.java
@ -80,7 +79,6 @@ import jdk.vm.ci.runtime.JVMCI;
* jdk.internal.vm.ci/jdk.vm.ci.runtime
*
* @compile PlaceholderEventWriter.java
* @compile PlaceholderEventWriterFactory.java
* @compile E.java
* @compile NonEvent.java
* @compile RegisteredTrueEvent.java
@ -105,10 +103,10 @@ public class TestGetEventWriter {
InitializationEvent e = new InitializationEvent();
e.commit();
}
// Make sure EventWriterFactory can be accessed.
Class<?> clazz = Class.forName("jdk.jfr.internal.event.EventWriterFactory");
// Make sure EventWriter class can be accessed.
Class<?> clazz = Class.forName("jdk.jfr.internal.event.EventWriter");
if (clazz == null) {
throw new Exception("Test error, not able to access jdk.jfr.internal.event.EventWriterFactory class");
throw new Exception("Test error, not able to access jdk.jfr.internal.event.EventWriter class");
}
testRegisteredTrueEvent();
testRegisteredFalseEvent();
@ -122,7 +120,7 @@ public class TestGetEventWriter {
// The class does not inherit jdk.jfr.Event and, as such, does not implement the
// API. It has its own stand-alone "commit()V", which is not an override, that
// attempts to resolve and link against EventWriterFactory. This user implementation
// attempts to resolve and link against EventWriter. This user implementation
// is not blessed for linkage.
private static void testNonEvent() throws Throwable {
Runnable e = newEventObject("NonEvent");
@ -178,7 +176,7 @@ public class TestGetEventWriter {
}
// The user has implemented another method, "myCommit()V", not an override nor
// overload. that attempts to resolve and link EventWriterFactory. This will fail,
// overload. that attempts to resolve and link EventWriter. This will fail,
// because "myCommit()V" is not blessed for linkage.
private static void testMyCommitRegisteredTrue() throws Throwable {
Runnable e = newEventObject("MyCommitRegisteredTrueEvent");
@ -230,10 +228,9 @@ public class TestGetEventWriter {
public void myCommit() throws Throwable {
try {
Class<?> ew = Class.forName("jdk.jfr.internal.event.EventWriter");
MethodType t = MethodType.methodType(ew, List.of(long.class));
Class<?> factory = Class.forName("jdk.jfr.internal.event.EventWriterFactory");
MethodHandle mh = MethodHandles.lookup().findStatic(factory, "getEventWriter", t);
mh.invoke(Long.valueOf(4711)); // throws IllegalAccessException
MethodType t = MethodType.methodType(ew, List.of());
MethodHandle mh = MethodHandles.lookup().findStatic(ew, "getEventWriter", t);
mh.invoke(); // throws IllegalAccessException
} catch (ClassNotFoundException | SecurityException e) {
throw new RuntimeException(e);
}
@ -262,8 +259,8 @@ public class TestGetEventWriter {
public void myCommit() throws Throwable {
Class<?> c;
try {
c = Class.forName("jdk.jfr.internal.event.EventWriterFactory");
Method m = c.getMethod("getEventWriter", new Class[] {long.class});
c = Class.forName("jdk.jfr.internal.event.EventWriter");
Method m = c.getMethod("getEventWriter", new Class[0]);
m.invoke(null, Long.valueOf(4711)); // throws InternalError
} catch (ClassNotFoundException | SecurityException e) {
throw new RuntimeException(e);
@ -283,7 +280,7 @@ public class TestGetEventWriter {
} catch (InternalError ie) {
if (ie.getCause() instanceof IllegalAccessException iaex) {
if (iaex.getCause() instanceof IllegalAccessError iae) {
if (iae.getMessage().contains("getEventWriter(long)")) {
if (iae.getMessage().contains("getEventWriter()")) {
// OK, as expected
return;
}
@ -345,7 +342,6 @@ public class TestGetEventWriter {
byte[] bytes = is.readAllBytes();
is.close();
bytes = replace(bytes, "jdk/jfr/jvm/E", "jdk/jfr/Event");
bytes = replace(bytes, "jdk/jfr/jvm/PlaceholderEventWriterFactory", "jdk/jfr/internal/event/EventWriterFactory");
bytes = replace(bytes, "jdk/jfr/jvm/PlaceholderEventWriter", "jdk/jfr/internal/event/EventWriter");
BytesClassLoader bc = new BytesClassLoader(bytes, fullName);
Class<?> clazz = bc.loadClass(fullName);
@ -372,7 +368,7 @@ public class TestGetEventWriter {
}
/**
* Checks that JVMCI prevents unblessed access to {@code EventWriterFactory.getEventWriter(long)}.
* Checks that JVMCI prevents unblessed access to {@code EventWriter.getEventWriter()}.
*/
private static void checkJVMCI(Class<?> eventClass, String commitName) throws Throwable {
MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
@ -380,7 +376,7 @@ public class TestGetEventWriter {
ConstantPool cp = commit.getConstantPool();
// Search for first INVOKESTATIC instruction in commit method which is expected
// to be the call to jdk.jfr.internal.event.EventWriterFactory.getEventWriter(long).
// to be the call to jdk.jfr.internal.event.EventWriter.getEventWriter().
final int INVOKESTATIC = 184;
byte[] code = commit.getCode();
for (int bci = 0; bci < code.length; bci++) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2025, 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
@ -35,7 +35,6 @@ import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingFile;
import jdk.jfr.internal.Repository;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.test.lib.Asserts;
import jdk.test.lib.process.OutputAnalyzer;
@ -80,7 +79,7 @@ public class TestAssemble {
expectedCount += countEventInRecording(tmp);
}
SafePath repository = Repository.getRepository().getRepositoryPath();
Path repository = Repository.getRepository().getRepositoryPath();
Path destinationPath = Paths.get("reconstructed.jfr");
String directory = repository.toString();