8261160: Add a deserialization JFR event
Co-authored-by: Sean Coffey <coffeys@openjdk.org> Co-authored-by: Chris Hegarty <chegar@openjdk.org> Reviewed-by: coffeys, rriggs, dfuchs, egahlin
This commit is contained in:
parent
a305743cfa
commit
3dc6f52a89
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 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
|
||||
@ -48,6 +48,7 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import static java.io.ObjectStreamClass.processQueue;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.event.DeserializationEvent;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
import sun.security.action.GetBooleanAction;
|
||||
@ -1323,9 +1324,12 @@ public class ObjectInputStream
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the serialization filter if non-null.
|
||||
* Invokes the serialization filter if non-null.
|
||||
*
|
||||
* If the filter rejects or an exception is thrown, throws InvalidClassException.
|
||||
*
|
||||
* Logs and/or commits a {@code DeserializationEvent}, if configured.
|
||||
*
|
||||
* @param clazz the class; may be null
|
||||
* @param arrayLength the array length requested; use {@code -1} if not creating an array
|
||||
* @throws InvalidClassException if it rejected by the filter or
|
||||
@ -1333,11 +1337,12 @@ public class ObjectInputStream
|
||||
*/
|
||||
private void filterCheck(Class<?> clazz, int arrayLength)
|
||||
throws InvalidClassException {
|
||||
// Info about the stream is not available if overridden by subclass, return 0
|
||||
long bytesRead = (bin == null) ? 0 : bin.getBytesRead();
|
||||
RuntimeException ex = null;
|
||||
ObjectInputFilter.Status status = null;
|
||||
|
||||
if (serialFilter != null) {
|
||||
RuntimeException ex = null;
|
||||
ObjectInputFilter.Status status;
|
||||
// Info about the stream is not available if overridden by subclass, return 0
|
||||
long bytesRead = (bin == null) ? 0 : bin.getBytesRead();
|
||||
try {
|
||||
status = serialFilter.checkInput(new FilterValues(clazz, arrayLength,
|
||||
totalObjectRefs, depth, bytesRead));
|
||||
@ -1355,12 +1360,24 @@ public class ObjectInputStream
|
||||
status, clazz, arrayLength, totalObjectRefs, depth, bytesRead,
|
||||
Objects.toString(ex, "n/a"));
|
||||
}
|
||||
if (status == null ||
|
||||
status == ObjectInputFilter.Status.REJECTED) {
|
||||
InvalidClassException ice = new InvalidClassException("filter status: " + status);
|
||||
ice.initCause(ex);
|
||||
throw ice;
|
||||
}
|
||||
}
|
||||
DeserializationEvent event = new DeserializationEvent();
|
||||
if (event.shouldCommit()) {
|
||||
event.filterConfigured = serialFilter != null;
|
||||
event.filterStatus = status != null ? status.name() : null;
|
||||
event.type = clazz;
|
||||
event.arrayLength = arrayLength;
|
||||
event.objectReferences = totalObjectRefs;
|
||||
event.depth = depth;
|
||||
event.bytesRead = bytesRead;
|
||||
event.exceptionType = ex != null ? ex.getClass() : null;
|
||||
event.exceptionMessage = ex != null ? ex.getMessage() : null;
|
||||
event.commit();
|
||||
}
|
||||
if (serialFilter != null && (status == null || status == ObjectInputFilter.Status.REJECTED)) {
|
||||
InvalidClassException ice = new InvalidClassException("filter status: " + status);
|
||||
ice.initCause(ex);
|
||||
throw ice;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 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.internal.event;
|
||||
|
||||
/**
|
||||
* Event details relating to deserialization.
|
||||
*/
|
||||
|
||||
public final class DeserializationEvent extends Event {
|
||||
public boolean filterConfigured;
|
||||
public String filterStatus;
|
||||
public Class<?> type;
|
||||
public int arrayLength;
|
||||
public long objectReferences;
|
||||
public long depth;
|
||||
public long bytesRead;
|
||||
public Class<?> exceptionType;
|
||||
public String exceptionMessage;
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 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.events;
|
||||
|
||||
import jdk.jfr.Category;
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.internal.MirrorEvent;
|
||||
|
||||
@Category({"Java Development Kit", "Serialization"})
|
||||
@Label("Deserialization")
|
||||
@Name("jdk.Deserialization")
|
||||
@Description("Results of deserialiation and ObjectInputFilter checks")
|
||||
@MirrorEvent(className = "jdk.internal.event.DeserializationEvent")
|
||||
public final class DeserializationEvent extends AbstractJDKEvent {
|
||||
|
||||
@Label("Filter Configured")
|
||||
public boolean filterConfigured;
|
||||
|
||||
@Label("Filter Status")
|
||||
public String filterStatus;
|
||||
|
||||
@Label ("Type")
|
||||
public Class<?> type;
|
||||
|
||||
@Label ("Array Length")
|
||||
public int arrayLength;
|
||||
|
||||
@Label ("Object References")
|
||||
public long objectReferences;
|
||||
|
||||
@Label ("Depth")
|
||||
public long depth;
|
||||
|
||||
@Label ("Bytes Read")
|
||||
public long bytesRead;
|
||||
|
||||
@Label ("Exception Type")
|
||||
public Class<?> exceptionType;
|
||||
|
||||
@Label ("Exception Message")
|
||||
public String exceptionMessage;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 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
|
||||
@ -38,6 +38,7 @@ import jdk.jfr.events.ExceptionThrownEvent;
|
||||
import jdk.jfr.events.FileForceEvent;
|
||||
import jdk.jfr.events.FileReadEvent;
|
||||
import jdk.jfr.events.FileWriteEvent;
|
||||
import jdk.jfr.events.DeserializationEvent;
|
||||
import jdk.jfr.events.ProcessStartEvent;
|
||||
import jdk.jfr.events.SecurityPropertyModificationEvent;
|
||||
import jdk.jfr.events.SocketReadEvent;
|
||||
@ -55,11 +56,12 @@ import jdk.jfr.internal.SecuritySupport;
|
||||
public final class JDKEvents {
|
||||
|
||||
private static final Class<?>[] mirrorEventClasses = {
|
||||
DeserializationEvent.class,
|
||||
ProcessStartEvent.class,
|
||||
SecurityPropertyModificationEvent.class,
|
||||
TLSHandshakeEvent.class,
|
||||
X509CertificateEvent.class,
|
||||
X509ValidationEvent.class,
|
||||
ProcessStartEvent.class
|
||||
X509ValidationEvent.class
|
||||
};
|
||||
|
||||
private static final Class<?>[] eventClasses = {
|
||||
@ -73,11 +75,13 @@ public final class JDKEvents {
|
||||
ErrorThrownEvent.class,
|
||||
ActiveSettingEvent.class,
|
||||
ActiveRecordingEvent.class,
|
||||
jdk.internal.event.DeserializationEvent.class,
|
||||
jdk.internal.event.ProcessStartEvent.class,
|
||||
jdk.internal.event.SecurityPropertyModificationEvent.class,
|
||||
jdk.internal.event.TLSHandshakeEvent.class,
|
||||
jdk.internal.event.X509CertificateEvent.class,
|
||||
jdk.internal.event.X509ValidationEvent.class,
|
||||
jdk.internal.event.ProcessStartEvent.class,
|
||||
|
||||
DirectBufferStatisticsEvent.class
|
||||
};
|
||||
|
||||
|
@ -665,6 +665,11 @@
|
||||
<setting name="threshold" control="socket-io-threshold">20 ms</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.Deserialization">
|
||||
<setting name="enabled">false</setting>
|
||||
<setting name="stackTrace">true</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.SecurityPropertyModification">
|
||||
<setting name="enabled">false</setting>
|
||||
<setting name="stackTrace">true</setting>
|
||||
|
@ -665,6 +665,11 @@
|
||||
<setting name="threshold" control="socket-io-threshold">10 ms</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.Deserialization">
|
||||
<setting name="enabled">false</setting>
|
||||
<setting name="stackTrace">true</setting>
|
||||
</event>
|
||||
|
||||
<event name="jdk.SecurityPropertyModification">
|
||||
<setting name="enabled">false</setting>
|
||||
<setting name="stackTrace">true</setting>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 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
|
||||
@ -53,6 +53,18 @@ import org.testng.annotations.DataProvider;
|
||||
*
|
||||
* @summary Test Global Filters
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8261160
|
||||
* @summary Add a deserialization JFR event
|
||||
* @build GlobalFilterTest SerialFilterTest
|
||||
* @requires vm.hasJFR
|
||||
* @run testng/othervm/policy=security.policy
|
||||
* -XX:StartFlightRecording=name=DeserializationEvent,dumponexit=true
|
||||
* -Djava.security.properties=${test.src}/java.security-extra1
|
||||
* -Djava.security.debug=properties GlobalFilterTest
|
||||
*/
|
||||
|
||||
@Test
|
||||
public class GlobalFilterTest {
|
||||
private static final String serialPropName = "jdk.serialFilter";
|
||||
|
437
test/jdk/jdk/jfr/event/io/TestDeserializationEvent.java
Normal file
437
test/jdk/jdk/jfr/event/io/TestDeserializationEvent.java
Normal file
@ -0,0 +1,437 @@
|
||||
/*
|
||||
* Copyright (c) 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.
|
||||
*
|
||||
* 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.event.io;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidClassException;
|
||||
import java.io.ObjectInputFilter.Status;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.consumer.RecordedClass;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.test.lib.jfr.EventNames;
|
||||
import jdk.test.lib.jfr.Events;
|
||||
import jdk.test.lib.serial.SerialObjectBuilder;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static java.lang.System.out;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8261160
|
||||
* @summary Add a deserialization JFR event
|
||||
* @key jfr
|
||||
* @requires vm.hasJFR
|
||||
* @library /test/lib
|
||||
* @run testng/othervm jdk.jfr.event.io.TestDeserializationEvent
|
||||
*/
|
||||
public class TestDeserializationEvent {
|
||||
|
||||
public record R() implements Serializable { }
|
||||
|
||||
@DataProvider(name = "scenarios")
|
||||
public Object[][] scenarios() throws Exception {
|
||||
byte[] ba1 = serialize(new R());
|
||||
byte[] ba2 = serialize(new int[] { 56, 67, 58, 59, 60 });
|
||||
byte[] ba3 = serialize(new R[] { new R(), new R() });
|
||||
byte[] ba4 = serialize(new char[][] { new char[] {'a', 'b'}, new char[] {'c'} });
|
||||
|
||||
// data provider columns- 1:id, 2:deserialize-operation, 3:expected-event-checkers
|
||||
return new Object[][] {
|
||||
{ 1, // single stream object, R
|
||||
(Runnable)() -> deserialize(ba1),
|
||||
List.of(
|
||||
Set.of(
|
||||
assertFilterStatus(null),
|
||||
assertType(R.class),
|
||||
assertArrayLength(-1),
|
||||
assertObjectReferences(1),
|
||||
assertDepth(1),
|
||||
assertHasBytesRead(),
|
||||
assertExceptionType(null),
|
||||
assertExceptionMessage(null))) },
|
||||
{ 2, // primitive int array
|
||||
(Runnable)() -> deserialize(ba2),
|
||||
List.of(
|
||||
Set.of( // TC_CLASS, for array class int[]
|
||||
assertType(int[].class),
|
||||
assertArrayLength(-1)),
|
||||
Set.of( // TC_ARRAY, actual array
|
||||
assertType(int[].class),
|
||||
assertArrayLength(5))) },
|
||||
{ 3, // reference array, R
|
||||
(Runnable)() -> deserialize(ba3),
|
||||
List.of(
|
||||
Set.of( // TC_CLASS, for array class R[]
|
||||
assertType(R[].class),
|
||||
assertArrayLength(-1)),
|
||||
Set.of( // TC_ARRAY, actual array
|
||||
assertType(R[].class),
|
||||
assertArrayLength(2)),
|
||||
Set.of( // TC_CLASS, for R
|
||||
assertType(R.class),
|
||||
assertArrayLength(-1)),
|
||||
Set.of( // TC_REFERENCE, for TC_CLASS relating second stream obj
|
||||
assertType(null),
|
||||
assertArrayLength(-1))) },
|
||||
{ 4, // multi-dimensional prim char array
|
||||
(Runnable)() -> deserialize(ba4),
|
||||
List.of(
|
||||
Set.of( // TC_CLASS, for array class char[][]
|
||||
assertType(char[][].class),
|
||||
assertArrayLength(-1),
|
||||
assertDepth(1)),
|
||||
Set.of( // TC_ARRAY, actual char[][] array
|
||||
assertType(char[][].class),
|
||||
assertArrayLength(2),
|
||||
assertDepth(1)),
|
||||
Set.of( // TC_CLASS, for array class char[]
|
||||
assertType(char[].class),
|
||||
assertArrayLength(-1),
|
||||
assertDepth(2)),
|
||||
Set.of( // TC_ARRAY, first char[] array
|
||||
assertType(char[].class),
|
||||
assertArrayLength(2),
|
||||
assertDepth(2)),
|
||||
Set.of( // TC_REFERENCE, for TC_CLASS relating to second stream array
|
||||
assertType(null),
|
||||
assertArrayLength(-1),
|
||||
assertDepth(2)),
|
||||
Set.of( // TC_ARRAY, second char[] array
|
||||
assertType(char[].class),
|
||||
assertArrayLength(1),
|
||||
assertDepth(2))) }
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "scenarios")
|
||||
public void test(int id,
|
||||
Runnable thunk,
|
||||
List<Set<Consumer<RecordedEvent>>> expectedValuesChecker)
|
||||
throws IOException
|
||||
{
|
||||
try (Recording recording = new Recording()) {
|
||||
recording.enable(EventNames.Deserialization);
|
||||
recording.start();
|
||||
thunk.run();
|
||||
recording.stop();
|
||||
List<RecordedEvent> events = Events.fromRecording(recording);
|
||||
assertEquals(events.size(), expectedValuesChecker.size());
|
||||
assertEventList(events, expectedValuesChecker);
|
||||
}
|
||||
}
|
||||
|
||||
static final Class<InvalidClassException> ICE = InvalidClassException.class;
|
||||
|
||||
@DataProvider(name = "filterDisallowedValues")
|
||||
public Object[][] filterDisallowedValues() {
|
||||
return new Object[][] {
|
||||
{ Status.REJECTED, "REJECTED" },
|
||||
{ null, null }
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "filterDisallowedValues")
|
||||
public void testFilterDisallow(Status filterStatus,
|
||||
String expectedValue)
|
||||
throws Exception
|
||||
{
|
||||
try (Recording recording = new Recording();
|
||||
var bais = new ByteArrayInputStream(serialize(new R()));
|
||||
var ois = new ObjectInputStream(bais)) {
|
||||
ois.setObjectInputFilter(fv -> filterStatus);
|
||||
recording.enable(EventNames.Deserialization);
|
||||
recording.start();
|
||||
assertThrows(ICE, () -> ois.readObject());
|
||||
recording.stop();
|
||||
List<RecordedEvent> events = Events.fromRecording(recording);
|
||||
assertEquals(events.size(), 1);
|
||||
assertEquals(events.get(0).getEventType().getName(), "jdk.Deserialization");
|
||||
assertFilterConfigured(true).accept(events.get(0));
|
||||
assertFilterStatus(expectedValue).accept(events.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "filterAllowedValues")
|
||||
public Object[][] filterAllowedValues() {
|
||||
return new Object[][] {
|
||||
{ Status.ALLOWED, "ALLOWED" },
|
||||
{ Status.UNDECIDED, "UNDECIDED" },
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "filterAllowedValues")
|
||||
public void testFilterAllowed(Status filterStatus,
|
||||
String expectedValue) throws Exception {
|
||||
try (Recording recording = new Recording();
|
||||
var bais = new ByteArrayInputStream(serialize(new R()));
|
||||
var ois = new ObjectInputStream(bais)) {
|
||||
ois.setObjectInputFilter(fv -> filterStatus);
|
||||
recording.enable(EventNames.Deserialization);
|
||||
recording.start();
|
||||
ois.readObject();
|
||||
recording.stop();
|
||||
List<RecordedEvent> events = Events.fromRecording(recording);
|
||||
assertEquals(events.size(), 1);
|
||||
assertEquals(events.get(0).getEventType().getName(), "jdk.Deserialization");
|
||||
assertFilterConfigured(true).accept(events.get(0));
|
||||
assertFilterStatus(expectedValue).accept(events.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
static class XYZException extends RuntimeException {
|
||||
XYZException(String msg) { super(msg); }
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testException() throws Exception {
|
||||
try (Recording recording = new Recording();
|
||||
var bais = new ByteArrayInputStream(serialize(new R()));
|
||||
var ois = new ObjectInputStream(bais)) {
|
||||
ois.setObjectInputFilter(fv -> { throw new XYZException("I am a bad filter!!!"); });
|
||||
recording.enable(EventNames.Deserialization);
|
||||
recording.start();
|
||||
assertThrows(ICE, () -> ois.readObject());
|
||||
recording.stop();
|
||||
List<RecordedEvent> events = Events.fromRecording(recording);
|
||||
assertEquals(events.size(), 1);
|
||||
assertEquals(events.get(0).getEventType().getName(), "jdk.Deserialization");
|
||||
assertFilterConfigured(true).accept(events.get(0));
|
||||
assertExceptionType(XYZException.class).accept(events.get(0));
|
||||
assertExceptionMessage("I am a bad filter!!!").accept(events.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
static void assertEventList(List<RecordedEvent> actualEvents,
|
||||
List<Set<Consumer<RecordedEvent>>> expectedValuesChecker) {
|
||||
int found = 0;
|
||||
for (RecordedEvent recordedEvent : actualEvents) {
|
||||
assertEquals(recordedEvent.getEventType().getName(), "jdk.Deserialization");
|
||||
out.println("Checking recorded event:" + recordedEvent);
|
||||
Set<Consumer<RecordedEvent>> checkers = expectedValuesChecker.get(found);
|
||||
for (Consumer<RecordedEvent> checker : checkers) {
|
||||
out.println(" checking:" + checker);
|
||||
checker.accept(recordedEvent);
|
||||
}
|
||||
assertFilterConfigured(false).accept(recordedEvent); // no filter expected
|
||||
assertExceptionType(null).accept(recordedEvent); // no exception type expected
|
||||
assertExceptionMessage(null).accept(recordedEvent); // no exception message expected
|
||||
found++;
|
||||
}
|
||||
assertEquals(found, expectedValuesChecker.size());
|
||||
}
|
||||
|
||||
static Consumer<RecordedEvent> assertFilterConfigured(boolean expectedValue) {
|
||||
return new Consumer<>() {
|
||||
@Override public void accept(RecordedEvent recordedEvent) {
|
||||
assertTrue(recordedEvent.hasField("filterConfigured"));
|
||||
assertEquals((boolean)recordedEvent.getValue("filterConfigured"), expectedValue);
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "assertFilterConfigured, expectedValue=" + expectedValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Consumer<RecordedEvent> assertFilterStatus(String expectedValue) {
|
||||
return new Consumer<>() {
|
||||
@Override public void accept(RecordedEvent recordedEvent) {
|
||||
assertTrue(recordedEvent.hasField("filterStatus"));
|
||||
assertEquals(recordedEvent.getValue("filterStatus"), expectedValue);
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "assertFilterStatus, expectedValue=" + expectedValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Consumer<RecordedEvent> assertType(Class<?> expectedValue) {
|
||||
return new Consumer<>() {
|
||||
@Override public void accept(RecordedEvent recordedEvent) {
|
||||
assertTrue(recordedEvent.hasField("type"));
|
||||
assertClassOrNull(expectedValue, recordedEvent, "type");
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "assertType, expectedValue=" + expectedValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Consumer<RecordedEvent> assertArrayLength(int expectedValue) {
|
||||
return new Consumer<>() {
|
||||
@Override public void accept(RecordedEvent recordedEvent) {
|
||||
assertTrue(recordedEvent.hasField("arrayLength"));
|
||||
assertEquals((int)recordedEvent.getValue("arrayLength"), expectedValue);
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "assertArrayLength, expectedValue=" + expectedValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Consumer<RecordedEvent> assertObjectReferences(long expectedValue) {
|
||||
return new Consumer<>() {
|
||||
@Override public void accept(RecordedEvent recordedEvent) {
|
||||
assertTrue(recordedEvent.hasField("objectReferences"));
|
||||
assertEquals((long)recordedEvent.getValue("objectReferences"), expectedValue);
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "assertObjectReferences, expectedValue=" + expectedValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Consumer<RecordedEvent> assertDepth(long expectedValue) {
|
||||
return new Consumer<>() {
|
||||
@Override public void accept(RecordedEvent recordedEvent) {
|
||||
assertTrue(recordedEvent.hasField("depth"));
|
||||
assertEquals((long)recordedEvent.getValue("depth"), expectedValue);
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "assertDepth, expectedValue=" + expectedValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Consumer<RecordedEvent> assertHasBytesRead() {
|
||||
return new Consumer<>() {
|
||||
@Override public void accept(RecordedEvent recordedEvent) {
|
||||
assertTrue(recordedEvent.hasField("bytesRead"));
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "assertHasBytesRead,";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Consumer<RecordedEvent> assertBytesRead(long expectedValue) {
|
||||
return new Consumer<>() {
|
||||
@Override public void accept(RecordedEvent recordedEvent) {
|
||||
assertHasBytesRead().accept(recordedEvent);
|
||||
assertEquals((long)recordedEvent.getValue("bytesRead"), expectedValue);
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "assertBytesRead, expectedValue=" + expectedValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Consumer<RecordedEvent> assertExceptionType(Class<?> expectedValue) {
|
||||
return new Consumer<>() {
|
||||
@Override public void accept(RecordedEvent recordedEvent) {
|
||||
assertTrue(recordedEvent.hasField("exceptionType"));
|
||||
assertClassOrNull(expectedValue, recordedEvent, "exceptionType");
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "assertExceptionType, expectedValue=" + expectedValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Consumer<RecordedEvent> assertExceptionMessage(String expectedValue) {
|
||||
return new Consumer<>() {
|
||||
@Override public void accept(RecordedEvent recordedEvent) {
|
||||
assertTrue(recordedEvent.hasField("exceptionMessage"));
|
||||
assertEquals(recordedEvent.getValue("exceptionMessage"), expectedValue);
|
||||
}
|
||||
@Override public String toString() {
|
||||
return "assertExceptionMessage, expectedValue=" + expectedValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static void assertClassOrNull(Class<?> expectedValue,
|
||||
RecordedEvent recordedEvent,
|
||||
String valueName) {
|
||||
if (expectedValue == null && recordedEvent.getValue(valueName) == null)
|
||||
return;
|
||||
|
||||
if (recordedEvent.getValue(valueName) instanceof RecordedClass recordedClass)
|
||||
assertEquals(recordedClass.getName(), expectedValue.getName());
|
||||
else
|
||||
fail("Expected RecordedClass, got:" + recordedEvent.getValue(valueName).getClass());
|
||||
}
|
||||
|
||||
static <T> byte[] serialize(T obj) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
oos.writeObject(obj);
|
||||
oos.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> T deserialize(byte[] streamBytes) {
|
||||
try {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes);
|
||||
ObjectInputStream ois = new ObjectInputStream(bais);
|
||||
return (T) ois.readObject();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
static volatile boolean initializedFoo; // false
|
||||
// Do not inadvertently initialize this class, Foo.
|
||||
static class Foo implements Serializable {
|
||||
static { TestDeserializationEvent.initializedFoo = true; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the creation and recording of the Deserialization event does
|
||||
* not inadvertently trigger initialization of the class of the stream
|
||||
* object, when deserialization is rejected by the filter.
|
||||
*/
|
||||
@Test
|
||||
public void testRejectedClassNotInitialized() throws Exception {
|
||||
byte[] bytes = SerialObjectBuilder.newBuilder("Foo").build();
|
||||
assertFalse(initializedFoo); // sanity
|
||||
|
||||
try (Recording recording = new Recording();
|
||||
var bais = new ByteArrayInputStream(bytes);
|
||||
var ois = new ObjectInputStream(bais)) {
|
||||
ois.setObjectInputFilter(fv -> Status.REJECTED);
|
||||
recording.enable(EventNames.Deserialization);
|
||||
recording.start();
|
||||
assertThrows(ICE, () -> ois.readObject());
|
||||
recording.stop();
|
||||
List<RecordedEvent> events = Events.fromRecording(recording);
|
||||
assertEquals(events.size(), 1);
|
||||
assertEquals(events.get(0).getEventType().getName(), "jdk.Deserialization");
|
||||
assertFilterConfigured(true).accept(events.get(0));
|
||||
assertFilterStatus("REJECTED").accept(events.get(0));
|
||||
assertFalse(initializedFoo);
|
||||
assertType(Foo.class);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -178,6 +178,7 @@ public class TestDefaultConfigurations {
|
||||
insertSetting(doc, EventNames.X509Certificate, "threshold", "0 ns");
|
||||
insertSetting(doc, EventNames.X509Validation, "threshold", "0 ns");
|
||||
insertSetting(doc, EventNames.ProcessStart, "threshold", "0 ns");
|
||||
insertSetting(doc, EventNames.Deserialization, "threshold", "0 ns");
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 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
|
||||
@ -212,9 +212,9 @@ public class TestEventMetadata {
|
||||
String lowerCased = name.toLowerCase();
|
||||
Asserts.assertFalse(lowerCased.contains("info") && !lowerCased.contains("information"), "Use 'information' instead 'info' in name");
|
||||
Asserts.assertFalse(lowerCased.contains("alloc") && !lowerCased.contains("alloca"), "Use 'allocation' instead 'alloc' in name");
|
||||
Asserts.assertFalse(lowerCased.contains("config") && !lowerCased.contains("configuration"), "Use 'configuration' instead of 'config' in name");
|
||||
Asserts.assertFalse(lowerCased.contains("config") && !(lowerCased.contains("configuration") || lowerCased.contains("filterconfigured")), "Use 'configuration' instead of 'config' in name");
|
||||
Asserts.assertFalse(lowerCased.contains("evac") && !lowerCased.contains("evacu"), "Use 'evacuation' instead of 'evac' in name");
|
||||
Asserts.assertFalse(lowerCased.contains("stat") && !(lowerCased.contains("state") ||lowerCased.contains("statistic")) , "Use 'statistics' instead of 'stat' in name");
|
||||
Asserts.assertFalse(lowerCased.contains("stat") && !(lowerCased.contains("state") ||lowerCased.contains("statistic") ||lowerCased.contains("filterstatus")) , "Use 'statistics' instead of 'stat' in name");
|
||||
Asserts.assertFalse(name.contains("ID") , "Use 'id' or 'Id' instead of 'ID' in name");
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 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
|
||||
@ -206,6 +206,7 @@ public final class TestActiveSettingEvent {
|
||||
settingValues.put(EventNames.X509Certificate + "#threshold", "0 ns");
|
||||
settingValues.put(EventNames.X509Validation + "#threshold", "0 ns");
|
||||
settingValues.put(EventNames.ProcessStart + "#threshold", "0 ns");
|
||||
settingValues.put(EventNames.Deserialization + "#threshold", "0 ns");
|
||||
|
||||
try (Recording recording = new Recording(c)) {
|
||||
Map<Long, EventType> eventTypes = new HashMap<>();
|
||||
|
@ -199,6 +199,8 @@ public class EventNames {
|
||||
public final static String X509Validation = PREFIX + "X509Validation";
|
||||
public final static String SecurityProperty = PREFIX + "SecurityPropertyModification";
|
||||
public final static String DirectBufferStatistics = PREFIX + "DirectBufferStatistics";
|
||||
public final static String Deserialization = PREFIX + "Deserialization";
|
||||
|
||||
|
||||
// Flight Recorder
|
||||
public final static String DumpReason = PREFIX + "DumpReason";
|
||||
|
Loading…
x
Reference in New Issue
Block a user