8344034: Remove security manager dependency in Serialization

Reviewed-by: mullan, alanb
This commit is contained in:
Roger Riggs 2024-11-18 16:17:07 +00:00
parent d52d136486
commit 9b0ab92b16
7 changed files with 106 additions and 419 deletions

@ -29,8 +29,6 @@ import jdk.internal.access.SharedSecrets;
import jdk.internal.util.StaticProperty; import jdk.internal.util.StaticProperty;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.Security; import java.security.Security;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -630,17 +628,13 @@ public interface ObjectInputFilter {
configLog = System.getLogger("java.io.serialization"); configLog = System.getLogger("java.io.serialization");
// Get the values of the system properties, if they are defined // Get the values of the system properties, if they are defined
@SuppressWarnings("removal")
String factoryClassName = StaticProperty.jdkSerialFilterFactory() != null String factoryClassName = StaticProperty.jdkSerialFilterFactory() != null
? StaticProperty.jdkSerialFilterFactory() ? StaticProperty.jdkSerialFilterFactory()
: AccessController.doPrivileged((PrivilegedAction<String>) () -> : Security.getProperty(SERIAL_FILTER_FACTORY_PROPNAME);
Security.getProperty(SERIAL_FILTER_FACTORY_PROPNAME));
@SuppressWarnings("removal")
String filterString = StaticProperty.jdkSerialFilter() != null String filterString = StaticProperty.jdkSerialFilter() != null
? StaticProperty.jdkSerialFilter() ? StaticProperty.jdkSerialFilter()
: AccessController.doPrivileged((PrivilegedAction<String>) () -> : Security.getProperty(SERIAL_FILTER_PROPNAME);
Security.getProperty(SERIAL_FILTER_PROPNAME));
// Initialize the static filter if the jdk.serialFilter is present // Initialize the static filter if the jdk.serialFilter is present
String filterMessage = null; String filterMessage = null;
@ -734,11 +728,6 @@ public interface ObjectInputFilter {
*/ */
public static void setSerialFilter(ObjectInputFilter filter) { public static void setSerialFilter(ObjectInputFilter filter) {
Objects.requireNonNull(filter, "filter"); Objects.requireNonNull(filter, "filter");
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(ObjectStreamConstants.SERIAL_FILTER_PERMISSION);
}
if (invalidFilterMessage != null) { if (invalidFilterMessage != null) {
throw new IllegalStateException(invalidFilterMessage); throw new IllegalStateException(invalidFilterMessage);
} }
@ -831,11 +820,6 @@ public interface ObjectInputFilter {
*/ */
public static void setSerialFilterFactory(BinaryOperator<ObjectInputFilter> filterFactory) { public static void setSerialFilterFactory(BinaryOperator<ObjectInputFilter> filterFactory) {
Objects.requireNonNull(filterFactory, "filterFactory"); Objects.requireNonNull(filterFactory, "filterFactory");
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(ObjectStreamConstants.SERIAL_FILTER_PERMISSION);
}
if (filterFactoryNoReplace.getAndSet(true)) { if (filterFactoryNoReplace.getAndSet(true)) {
final String msg = serialFilterFactory != null final String msg = serialFilterFactory != null
? "Cannot replace filter factory: " + serialFilterFactory.getClass().getName() ? "Cannot replace filter factory: " + serialFilterFactory.getClass().getName()

@ -34,13 +34,7 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import jdk.internal.access.JavaLangAccess; import jdk.internal.access.JavaLangAccess;
@ -49,8 +43,6 @@ import jdk.internal.event.DeserializationEvent;
import jdk.internal.misc.Unsafe; import jdk.internal.misc.Unsafe;
import jdk.internal.util.ByteArray; import jdk.internal.util.ByteArray;
import sun.reflect.misc.ReflectUtil; import sun.reflect.misc.ReflectUtil;
import sun.security.action.GetBooleanAction;
import sun.security.action.GetIntegerAction;
/** /**
* An ObjectInputStream deserializes primitive data and objects previously * An ObjectInputStream deserializes primitive data and objects previously
@ -278,8 +270,8 @@ public class ObjectInputStream
* have been read. * have been read.
* See {@link #setObjectInputFilter(ObjectInputFilter)} * See {@link #setObjectInputFilter(ObjectInputFilter)}
*/ */
static final boolean SET_FILTER_AFTER_READ = GetBooleanAction static final boolean SET_FILTER_AFTER_READ =
.privilegedGetProperty("jdk.serialSetFilterAfterRead"); Boolean.getBoolean("jdk.serialSetFilterAfterRead");
/** /**
* Property to control {@link GetField#get(String, Object)} conversion of * Property to control {@link GetField#get(String, Object)} conversion of
@ -287,8 +279,8 @@ public class ObjectInputStream
* {@link GetField#get(String, Object)} returns null otherwise * {@link GetField#get(String, Object)} returns null otherwise
* throwing {@link ClassNotFoundException}. * throwing {@link ClassNotFoundException}.
*/ */
private static final boolean GETFIELD_CNFE_RETURNS_NULL = GetBooleanAction private static final boolean GETFIELD_CNFE_RETURNS_NULL =
.privilegedGetProperty("jdk.serialGetFieldCnfeReturnsNull"); Boolean.getBoolean("jdk.serialGetFieldCnfeReturnsNull");
/** /**
* Property to override the implementation limit on the number * Property to override the implementation limit on the number
@ -296,8 +288,8 @@ public class ObjectInputStream
* The maximum number of interfaces allowed for a proxy is limited to 65535 by * The maximum number of interfaces allowed for a proxy is limited to 65535 by
* {@link java.lang.reflect.Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)}. * {@link java.lang.reflect.Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler)}.
*/ */
static final int PROXY_INTERFACE_LIMIT = Math.clamp(GetIntegerAction static final int PROXY_INTERFACE_LIMIT =
.privilegedGetProperty("jdk.serialProxyInterfaceLimit", 65535), 0, 65535); Math.clamp(Integer.getInteger("jdk.serialProxyInterfaceLimit", 65535), 0, 65535);
} }
/* /*
@ -386,7 +378,6 @@ public class ObjectInputStream
*/ */
@SuppressWarnings("this-escape") @SuppressWarnings("this-escape")
public ObjectInputStream(InputStream in) throws IOException { public ObjectInputStream(InputStream in) throws IOException {
verifySubclass();
bin = new BlockDataInputStream(in); bin = new BlockDataInputStream(in);
handles = new HandleTable(10); handles = new HandleTable(10);
vlist = new ValidationList(); vlist = new ValidationList();
@ -416,11 +407,6 @@ public class ObjectInputStream
* fails due to invalid serial filter or serial filter factory properties. * fails due to invalid serial filter or serial filter factory properties.
*/ */
protected ObjectInputStream() throws IOException { protected ObjectInputStream() throws IOException {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
bin = null; bin = null;
handles = null; handles = null;
vlist = null; vlist = null;
@ -907,13 +893,6 @@ public class ObjectInputStream
if (enable == enableResolve) { if (enable == enableResolve) {
return enable; return enable;
} }
if (enable) {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SUBSTITUTION_PERMISSION);
}
}
enableResolve = enable; enableResolve = enable;
return !enableResolve; return !enableResolve;
} }
@ -1309,11 +1288,6 @@ public class ObjectInputStream
* @since 9 * @since 9
*/ */
public final void setObjectInputFilter(ObjectInputFilter filter) { public final void setObjectInputFilter(ObjectInputFilter filter) {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(ObjectStreamConstants.SERIAL_FILTER_PERMISSION);
}
if (totalObjectRefs > 0 && !Caches.SET_FILTER_AFTER_READ) { if (totalObjectRefs > 0 && !Caches.SET_FILTER_AFTER_READ) {
throw new IllegalStateException( throw new IllegalStateException(
"filter can not be set after an object has been read"); "filter can not be set after an object has been read");
@ -1571,58 +1545,29 @@ public class ObjectInputStream
public abstract Object get(String name, Object val) throws IOException, ClassNotFoundException; public abstract Object get(String name, Object val) throws IOException, ClassNotFoundException;
} }
/**
* Verifies that this (possibly subclass) instance can be constructed
* without violating security constraints: the subclass must not override
* security-sensitive non-final methods, or else the
* "enableSubclassImplementation" SerializablePermission is checked.
*/
private void verifySubclass() {
Class<?> cl = getClass();
if (cl == ObjectInputStream.class) {
return;
}
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm == null) {
return;
}
boolean result = Caches.subclassAudits.get(cl);
if (!result) {
sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
/** /**
* Performs reflective checks on given subclass to verify that it doesn't * Performs reflective checks on given subclass to verify that it doesn't
* override security-sensitive non-final methods. Returns TRUE if subclass * override security-sensitive non-final methods. Returns TRUE if subclass
* is "safe", FALSE otherwise. * is "safe", FALSE otherwise.
*/ */
@SuppressWarnings("removal")
private static Boolean auditSubclass(Class<?> subcl) { private static Boolean auditSubclass(Class<?> subcl) {
return AccessController.doPrivileged( for (Class<?> cl = subcl;
new PrivilegedAction<Boolean>() { cl != ObjectInputStream.class;
public Boolean run() { cl = cl.getSuperclass())
for (Class<?> cl = subcl; {
cl != ObjectInputStream.class; try {
cl = cl.getSuperclass()) cl.getDeclaredMethod(
{ "readUnshared", (Class[]) null);
try { return Boolean.FALSE;
cl.getDeclaredMethod( } catch (NoSuchMethodException ex) {
"readUnshared", (Class[]) null);
return Boolean.FALSE;
} catch (NoSuchMethodException ex) {
}
try {
cl.getDeclaredMethod("readFields", (Class[]) null);
return Boolean.FALSE;
} catch (NoSuchMethodException ex) {
}
}
return Boolean.TRUE;
}
} }
); try {
cl.getDeclaredMethod("readFields", (Class[]) null);
return Boolean.FALSE;
} catch (NoSuchMethodException ex) {
}
}
return Boolean.TRUE;
} }
/** /**
@ -2702,16 +2647,11 @@ public class ObjectInputStream
final ObjectInputValidation obj; final ObjectInputValidation obj;
final int priority; final int priority;
Callback next; Callback next;
@SuppressWarnings("removal")
final AccessControlContext acc;
Callback(ObjectInputValidation obj, int priority, Callback next, Callback(ObjectInputValidation obj, int priority, Callback next) {
@SuppressWarnings("removal") AccessControlContext acc)
{
this.obj = obj; this.obj = obj;
this.priority = priority; this.priority = priority;
this.next = next; this.next = next;
this.acc = acc;
} }
} }
@ -2740,12 +2680,10 @@ public class ObjectInputStream
prev = cur; prev = cur;
cur = cur.next; cur = cur.next;
} }
@SuppressWarnings("removal")
AccessControlContext acc = AccessController.getContext();
if (prev != null) { if (prev != null) {
prev.next = new Callback(obj, priority, cur, acc); prev.next = new Callback(obj, priority, cur);
} else { } else {
list = new Callback(obj, priority, list, acc); list = new Callback(obj, priority, list);
} }
} }
@ -2756,23 +2694,15 @@ public class ObjectInputStream
* throws an InvalidObjectException, the callback process is terminated * throws an InvalidObjectException, the callback process is terminated
* and the exception propagated upwards. * and the exception propagated upwards.
*/ */
@SuppressWarnings("removal")
void doCallbacks() throws InvalidObjectException { void doCallbacks() throws InvalidObjectException {
try { try {
while (list != null) { while (list != null) {
AccessController.doPrivileged( list.obj.validateObject();
new PrivilegedExceptionAction<Void>()
{
public Void run() throws InvalidObjectException {
list.obj.validateObject();
return null;
}
}, list.acc);
list = list.next; list = list.next;
} }
} catch (PrivilegedActionException ex) { } catch (InvalidObjectException ex) {
list = null; list = null;
throw (InvalidObjectException) ex.getException(); throw ex;
} }
} }

@ -26,8 +26,6 @@
package java.io; package java.io;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -224,11 +222,8 @@ public class ObjectOutputStream
* value of "sun.io.serialization.extendedDebugInfo" property, * value of "sun.io.serialization.extendedDebugInfo" property,
* as true or false for extended information about exception's place * as true or false for extended information about exception's place
*/ */
@SuppressWarnings("removal")
private static final boolean extendedDebugInfo = private static final boolean extendedDebugInfo =
java.security.AccessController.doPrivileged( Boolean.getBoolean("sun.io.serialization.extendedDebugInfo");
new sun.security.action.GetBooleanAction(
"sun.io.serialization.extendedDebugInfo")).booleanValue();
/** /**
* Creates an ObjectOutputStream that writes to the specified OutputStream. * Creates an ObjectOutputStream that writes to the specified OutputStream.
@ -247,7 +242,6 @@ public class ObjectOutputStream
*/ */
@SuppressWarnings("this-escape") @SuppressWarnings("this-escape")
public ObjectOutputStream(OutputStream out) throws IOException { public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out); bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00); handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00); subs = new ReplaceTable(10, (float) 3.00);
@ -269,11 +263,6 @@ public class ObjectOutputStream
* @throws IOException if an I/O error occurs while creating this stream * @throws IOException if an I/O error occurs while creating this stream
*/ */
protected ObjectOutputStream() throws IOException { protected ObjectOutputStream() throws IOException {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
bout = null; bout = null;
handles = null; handles = null;
subs = null; subs = null;
@ -595,13 +584,6 @@ public class ObjectOutputStream
if (enable == enableReplace) { if (enable == enableReplace) {
return enable; return enable;
} }
if (enable) {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(SUBSTITUTION_PERMISSION);
}
}
enableReplace = enable; enableReplace = enable;
return !enableReplace; return !enableReplace;
} }
@ -625,8 +607,8 @@ public class ObjectOutputStream
* stream. Subclasses of ObjectOutputStream may override this method to * stream. Subclasses of ObjectOutputStream may override this method to
* customize the way in which class descriptors are written to the * customize the way in which class descriptors are written to the
* serialization stream. The corresponding method in ObjectInputStream, * serialization stream. The corresponding method in ObjectInputStream,
* {@link ObjectInputStream#readClassDescriptor readClassDescriptor}, should then be overridden to * {@link ObjectInputStream#readClassDescriptor readClassDescriptor}, should then be
* reconstitute the class descriptor from its custom stream representation. * overridden to reconstitute the class descriptor from its custom stream representation.
* By default, this method writes class descriptors according to the format * By default, this method writes class descriptors according to the format
* defined in the <a href="{@docRoot}/../specs/serialization/index.html"> * defined in the <a href="{@docRoot}/../specs/serialization/index.html">
* <cite>Java Object Serialization Specification</cite></a>. * <cite>Java Object Serialization Specification</cite></a>.
@ -1022,58 +1004,29 @@ public class ObjectOutputStream
} }
} }
/**
* Verifies that this (possibly subclass) instance can be constructed
* without violating security constraints: the subclass must not override
* security-sensitive non-final methods, or else the
* "enableSubclassImplementation" SerializablePermission is checked.
*/
private void verifySubclass() {
Class<?> cl = getClass();
if (cl == ObjectOutputStream.class) {
return;
}
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm == null) {
return;
}
boolean result = Caches.subclassAudits.get(cl);
if (!result) {
sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
/** /**
* Performs reflective checks on given subclass to verify that it doesn't * Performs reflective checks on given subclass to verify that it doesn't
* override security-sensitive non-final methods. Returns TRUE if subclass * override security-sensitive non-final methods. Returns TRUE if subclass
* is "safe", FALSE otherwise. * is "safe", FALSE otherwise.
*/ */
@SuppressWarnings("removal")
private static Boolean auditSubclass(Class<?> subcl) { private static Boolean auditSubclass(Class<?> subcl) {
return AccessController.doPrivileged( for (Class<?> cl = subcl;
new PrivilegedAction<>() { cl != ObjectOutputStream.class;
public Boolean run() { cl = cl.getSuperclass())
for (Class<?> cl = subcl; {
cl != ObjectOutputStream.class; try {
cl = cl.getSuperclass()) cl.getDeclaredMethod(
{ "writeUnshared", new Class<?>[] { Object.class });
try { return Boolean.FALSE;
cl.getDeclaredMethod( } catch (NoSuchMethodException ex) {
"writeUnshared", new Class<?>[] { Object.class });
return Boolean.FALSE;
} catch (NoSuchMethodException ex) {
}
try {
cl.getDeclaredMethod("putFields", (Class<?>[]) null);
return Boolean.FALSE;
} catch (NoSuchMethodException ex) {
}
}
return Boolean.TRUE;
}
} }
); try {
cl.getDeclaredMethod("putFields", (Class<?>[]) null);
return Boolean.FALSE;
} catch (NoSuchMethodException ex) {
}
}
return Boolean.TRUE;
} }
/** /**

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -32,21 +32,12 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.RecordComponent; import java.lang.reflect.RecordComponent;
import java.lang.reflect.UndeclaredThrowableException;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -58,13 +49,8 @@ import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.event.SerializationMisdeclarationEvent; import jdk.internal.event.SerializationMisdeclarationEvent;
import jdk.internal.misc.Unsafe; import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory; import jdk.internal.reflect.ReflectionFactory;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.JavaSecurityAccess;
import jdk.internal.util.ByteArray; import jdk.internal.util.ByteArray;
import sun.reflect.misc.ReflectUtil;
/** /**
* Serialization's descriptor for classes. It contains the name and * Serialization's descriptor for classes. It contains the name and
@ -98,12 +84,6 @@ public final class ObjectStreamClass implements Serializable {
private static final ObjectStreamField[] serialPersistentFields = private static final ObjectStreamField[] serialPersistentFields =
NO_FIELDS; NO_FIELDS;
/** reflection factory for obtaining serialization constructors */
@SuppressWarnings("removal")
private static final ReflectionFactory reflFactory =
AccessController.doPrivileged(
new ReflectionFactory.GetReflectionFactoryAction());
private static class Caches { private static class Caches {
/** cache mapping local classes -> descriptors */ /** cache mapping local classes -> descriptors */
static final ClassCache<ObjectStreamClass> localDescs = static final ClassCache<ObjectStreamClass> localDescs =
@ -206,8 +186,6 @@ public final class ObjectStreamClass implements Serializable {
/** session-cache of record deserialization constructor /** session-cache of record deserialization constructor
* (in de-serialized OSC only), or null */ * (in de-serialized OSC only), or null */
private MethodHandle deserializationCtr; private MethodHandle deserializationCtr;
/** protection domains that need to be checked when calling the constructor */
private ProtectionDomain[] domains;
/** class-defined writeObject method, or null if none */ /** class-defined writeObject method, or null if none */
private Method writeObjectMethod; private Method writeObjectMethod;
@ -280,20 +258,13 @@ public final class ObjectStreamClass implements Serializable {
* *
* @return the SUID of the class described by this descriptor * @return the SUID of the class described by this descriptor
*/ */
@SuppressWarnings("removal")
public long getSerialVersionUID() { public long getSerialVersionUID() {
// REMIND: synchronize instead of relying on volatile? // REMIND: synchronize instead of relying on volatile?
if (suid == null) { if (suid == null) {
if (isRecord) if (isRecord)
return 0L; return 0L;
suid = AccessController.doPrivileged( suid = computeDefaultSUID(cl);
new PrivilegedAction<Long>() {
public Long run() {
return computeDefaultSUID(cl);
}
}
);
} }
return suid.longValue(); return suid.longValue();
} }
@ -304,19 +275,11 @@ public final class ObjectStreamClass implements Serializable {
* *
* @return the {@code Class} instance that this descriptor represents * @return the {@code Class} instance that this descriptor represents
*/ */
@SuppressWarnings("removal")
@CallerSensitive
public Class<?> forClass() { public Class<?> forClass() {
if (cl == null) { if (cl == null) {
return null; return null;
} }
requireInitialized(); requireInitialized();
if (System.getSecurityManager() != null) {
Class<?> caller = Reflection.getCallerClass();
if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), cl.getClassLoader())) {
ReflectUtil.checkPackageAccess(cl);
}
}
return cl; return cl;
} }
@ -369,7 +332,6 @@ public final class ObjectStreamClass implements Serializable {
/** /**
* Creates local class descriptor representing given class. * Creates local class descriptor representing given class.
*/ */
@SuppressWarnings("removal")
private ObjectStreamClass(final Class<?> cl) { private ObjectStreamClass(final Class<?> cl) {
this.cl = cl; this.cl = cl;
name = cl.getName(); name = cl.getName();
@ -384,53 +346,44 @@ public final class ObjectStreamClass implements Serializable {
localDesc = this; localDesc = this;
if (serializable) { if (serializable) {
AccessController.doPrivileged(new PrivilegedAction<>() { if (isEnum) {
public Void run() { suid = 0L;
if (isEnum) { fields = NO_FIELDS;
suid = 0L; } else if (cl.isArray()) {
fields = NO_FIELDS; fields = NO_FIELDS;
return null; } else {
} suid = getDeclaredSUID(cl);
if (cl.isArray()) { try {
fields = NO_FIELDS; fields = getSerialFields(cl);
return null; computeFieldOffsets();
} } catch (InvalidClassException e) {
serializeEx = deserializeEx =
suid = getDeclaredSUID(cl);
try {
fields = getSerialFields(cl);
computeFieldOffsets();
} catch (InvalidClassException e) {
serializeEx = deserializeEx =
new ExceptionInfo(e.classname, e.getMessage()); new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS; fields = NO_FIELDS;
}
if (isRecord) {
canonicalCtr = canonicalRecordCtr(cl);
deserializationCtrs = new DeserializationConstructorsCache();
} else if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[] { ObjectOutputStream.class },
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[] { ObjectInputStream.class },
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
domains = getProtectionDomains(cons, cl);
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
} }
});
if (isRecord) {
canonicalCtr = canonicalRecordCtr(cl);
deserializationCtrs = new DeserializationConstructorsCache();
} else if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[]{ObjectOutputStream.class},
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[]{ObjectInputStream.class},
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
}
} else { } else {
suid = 0L; suid = 0L;
fields = NO_FIELDS; fields = NO_FIELDS;
@ -474,66 +427,6 @@ public final class ObjectStreamClass implements Serializable {
ObjectStreamClass() { ObjectStreamClass() {
} }
/**
* Creates a PermissionDomain that grants no permission.
*/
private ProtectionDomain noPermissionsDomain() {
PermissionCollection perms = new Permissions();
perms.setReadOnly();
return new ProtectionDomain(null, perms);
}
/**
* Aggregate the ProtectionDomains of all the classes that separate
* a concrete class {@code cl} from its ancestor's class declaring
* a constructor {@code cons}.
*
* If {@code cl} is defined by the boot loader, or the constructor
* {@code cons} is declared by {@code cl}, or if there is no security
* manager, then this method does nothing and {@code null} is returned.
*
* @param cons A constructor declared by {@code cl} or one of its
* ancestors.
* @param cl A concrete class, which is either the class declaring
* the constructor {@code cons}, or a serializable subclass
* of that class.
* @return An array of ProtectionDomain representing the set of
* ProtectionDomain that separate the concrete class {@code cl}
* from its ancestor's declaring {@code cons}, or {@code null}.
*/
@SuppressWarnings("removal")
private ProtectionDomain[] getProtectionDomains(Constructor<?> cons,
Class<?> cl) {
ProtectionDomain[] domains = null;
if (cons != null && cl.getClassLoader() != null
&& System.getSecurityManager() != null) {
Class<?> cls = cl;
Class<?> fnscl = cons.getDeclaringClass();
Set<ProtectionDomain> pds = null;
while (cls != fnscl) {
ProtectionDomain pd = cls.getProtectionDomain();
if (pd != null) {
if (pds == null) pds = new HashSet<>();
pds.add(pd);
}
cls = cls.getSuperclass();
if (cls == null) {
// that's not supposed to happen
// make a ProtectionDomain with no permission.
// should we throw instead?
if (pds == null) pds = new HashSet<>();
else pds.clear();
pds.add(noPermissionsDomain());
break;
}
}
if (pds != null) {
domains = pds.toArray(new ProtectionDomain[0]);
}
}
return domains;
}
/** /**
* Initializes class descriptor representing a proxy class. * Initializes class descriptor representing a proxy class.
*/ */
@ -564,7 +457,6 @@ public final class ObjectStreamClass implements Serializable {
writeReplaceMethod = localDesc.writeReplaceMethod; writeReplaceMethod = localDesc.writeReplaceMethod;
readResolveMethod = localDesc.readResolveMethod; readResolveMethod = localDesc.readResolveMethod;
deserializeEx = localDesc.deserializeEx; deserializeEx = localDesc.deserializeEx;
domains = localDesc.domains;
cons = localDesc.cons; cons = localDesc.cons;
} }
fieldRefl = getReflector(fields, localDesc); fieldRefl = getReflector(fields, localDesc);
@ -656,7 +548,6 @@ public final class ObjectStreamClass implements Serializable {
if (deserializeEx == null) { if (deserializeEx == null) {
deserializeEx = localDesc.deserializeEx; deserializeEx = localDesc.deserializeEx;
} }
domains = localDesc.domains;
assert cl.isRecord() ? localDesc.cons == null : true; assert cl.isRecord() ? localDesc.cons == null : true;
cons = localDesc.cons; cons = localDesc.cons;
} }
@ -1013,7 +904,6 @@ public final class ObjectStreamClass implements Serializable {
* class is non-serializable or if the appropriate no-arg constructor is * class is non-serializable or if the appropriate no-arg constructor is
* inaccessible/unavailable. * inaccessible/unavailable.
*/ */
@SuppressWarnings("removal")
Object newInstance() Object newInstance()
throws InstantiationException, InvocationTargetException, throws InstantiationException, InvocationTargetException,
UnsupportedOperationException UnsupportedOperationException
@ -1021,35 +911,7 @@ public final class ObjectStreamClass implements Serializable {
requireInitialized(); requireInitialized();
if (cons != null) { if (cons != null) {
try { try {
if (domains == null || domains.length == 0) { return cons.newInstance();
return cons.newInstance();
} else {
JavaSecurityAccess jsa = SharedSecrets.getJavaSecurityAccess();
PrivilegedAction<?> pea = () -> {
try {
return cons.newInstance();
} catch (InstantiationException
| InvocationTargetException
| IllegalAccessException x) {
throw new UndeclaredThrowableException(x);
}
}; // Can't use PrivilegedExceptionAction with jsa
try {
return jsa.doIntersectionPrivilege(pea,
AccessController.getContext(),
new AccessControlContext(domains));
} catch (UndeclaredThrowableException x) {
Throwable cause = x.getCause();
if (cause instanceof InstantiationException ie)
throw ie;
if (cause instanceof InvocationTargetException ite)
throw ite;
if (cause instanceof IllegalAccessException iae)
throw iae;
// not supposed to happen
throw x;
}
}
} catch (IllegalAccessException ex) { } catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed // should not occur, as access checks have been suppressed
throw new InternalError(ex); throw new InternalError(ex);
@ -1454,7 +1316,7 @@ public final class ObjectStreamClass implements Serializable {
* returned constructor (if any). * returned constructor (if any).
*/ */
private static Constructor<?> getSerializableConstructor(Class<?> cl) { private static Constructor<?> getSerializableConstructor(Class<?> cl) {
return reflFactory.newConstructorForSerialization(cl); return ReflectionFactory.getReflectionFactory().newConstructorForSerialization(cl);
} }
/** /**
@ -1462,22 +1324,18 @@ public final class ObjectStreamClass implements Serializable {
* the not found ( which should never happen for correctly generated record * the not found ( which should never happen for correctly generated record
* classes ). * classes ).
*/ */
@SuppressWarnings("removal")
private static MethodHandle canonicalRecordCtr(Class<?> cls) { private static MethodHandle canonicalRecordCtr(Class<?> cls) {
assert cls.isRecord() : "Expected record, got: " + cls; assert cls.isRecord() : "Expected record, got: " + cls;
PrivilegedAction<MethodHandle> pa = () -> { Class<?>[] paramTypes = Arrays.stream(cls.getRecordComponents())
Class<?>[] paramTypes = Arrays.stream(cls.getRecordComponents()) .map(RecordComponent::getType)
.map(RecordComponent::getType) .toArray(Class<?>[]::new);
.toArray(Class<?>[]::new); try {
try { Constructor<?> ctr = cls.getDeclaredConstructor(paramTypes);
Constructor<?> ctr = cls.getDeclaredConstructor(paramTypes); ctr.setAccessible(true);
ctr.setAccessible(true); return MethodHandles.lookup().unreflectConstructor(ctr);
return MethodHandles.lookup().unreflectConstructor(ctr); } catch (IllegalAccessException | NoSuchMethodException e) {
} catch (IllegalAccessException | NoSuchMethodException e) { return null;
return null; }
}
};
return AccessController.doPrivileged(pa);
} }
/** /**
@ -2358,7 +2216,6 @@ public final class ObjectStreamClass implements Serializable {
* and return * and return
* {@code Object} * {@code Object}
*/ */
@SuppressWarnings("removal")
static MethodHandle deserializationCtr(ObjectStreamClass desc) { static MethodHandle deserializationCtr(ObjectStreamClass desc) {
// check the cached value 1st // check the cached value 1st
MethodHandle mh = desc.deserializationCtr; MethodHandle mh = desc.deserializationCtr;
@ -2367,14 +2224,7 @@ public final class ObjectStreamClass implements Serializable {
if (mh != null) return desc.deserializationCtr = mh; if (mh != null) return desc.deserializationCtr = mh;
// retrieve record components // retrieve record components
RecordComponent[] recordComponents; RecordComponent[] recordComponents = desc.forClass().getRecordComponents();
try {
Class<?> cls = desc.forClass();
PrivilegedExceptionAction<RecordComponent[]> pa = cls::getRecordComponents;
recordComponents = AccessController.doPrivileged(pa);
} catch (PrivilegedActionException e) {
throw new InternalError(e.getCause());
}
// retrieve the canonical constructor // retrieve the canonical constructor
// (T1, T2, ..., Tn):TR // (T1, T2, ..., Tn):TR

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,9 +26,6 @@
package java.io; package java.io;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import sun.reflect.misc.ReflectUtil;
/** /**
* A description of a Serializable field from a Serializable class. An array * A description of a Serializable field from a Serializable class. An array
@ -161,15 +158,7 @@ public class ObjectStreamField
* @return a {@code Class} object representing the type of the * @return a {@code Class} object representing the type of the
* serializable field * serializable field
*/ */
@SuppressWarnings("removal")
@CallerSensitive
public Class<?> getType() { public Class<?> getType() {
if (System.getSecurityManager() != null) {
Class<?> caller = Reflection.getCallerClass();
if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), type.getClassLoader())) {
ReflectUtil.checkPackageAccess(type);
}
}
return type; return type;
} }

@ -29,8 +29,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays; import java.util.Arrays;
import static jdk.internal.event.SerializationMisdeclarationEvent.*; import static jdk.internal.event.SerializationMisdeclarationEvent.*;
@ -75,7 +73,7 @@ final class SerializationMisdeclarationChecker {
} }
private static void checkSerialVersionUID(Class<?> cl) { private static void checkSerialVersionUID(Class<?> cl) {
Field f = privilegedDeclaredField(cl, SUID_NAME); Field f = declaredField(cl, SUID_NAME);
if (f == null) { if (f == null) {
if (isOrdinaryClass(cl)) { if (isOrdinaryClass(cl)) {
commitEvent(cl, SUID_NAME + " should be declared explicitly" + commitEvent(cl, SUID_NAME + " should be declared explicitly" +
@ -101,7 +99,7 @@ final class SerializationMisdeclarationChecker {
} }
private static void checkSerialPersistentFields(Class<?> cl) { private static void checkSerialPersistentFields(Class<?> cl) {
Field f = privilegedDeclaredField(cl, SERIAL_PERSISTENT_FIELDS_NAME); Field f = declaredField(cl, SERIAL_PERSISTENT_FIELDS_NAME);
if (f == null) { if (f == null) {
return; return;
} }
@ -142,7 +140,7 @@ final class SerializationMisdeclarationChecker {
private static void checkPrivateMethod(Class<?> cl, private static void checkPrivateMethod(Class<?> cl,
String name, Class<?>[] paramTypes, Class<?> retType) { String name, Class<?>[] paramTypes, Class<?> retType) {
for (Method m : privilegedDeclaredMethods(cl)) { for (Method m : cl.getDeclaredMethods()) {
if (m.getName().equals(name)) { if (m.getName().equals(name)) {
checkPrivateMethod(cl, m, paramTypes, retType); checkPrivateMethod(cl, m, paramTypes, retType);
} }
@ -173,7 +171,7 @@ final class SerializationMisdeclarationChecker {
private static void checkAccessibleMethod(Class<?> cl, private static void checkAccessibleMethod(Class<?> cl,
String name, Class<?>[] paramTypes, Class<?> retType) { String name, Class<?>[] paramTypes, Class<?> retType) {
for (Class<?> superCl = cl; superCl != null; superCl = superCl.getSuperclass()) { for (Class<?> superCl = cl; superCl != null; superCl = superCl.getSuperclass()) {
for (Method m : privilegedDeclaredMethods(superCl)) { for (Method m : superCl.getDeclaredMethods()) {
if (m.getName().equals(name)) { if (m.getName().equals(name)) {
checkAccessibleMethod(cl, superCl, m, paramTypes, retType); checkAccessibleMethod(cl, superCl, m, paramTypes, retType);
} }
@ -236,14 +234,6 @@ final class SerializationMisdeclarationChecker {
return (m.getModifiers() & STATIC) != 0; return (m.getModifiers() & STATIC) != 0;
} }
@SuppressWarnings("removal")
private static Field privilegedDeclaredField(Class<?> cl, String name) {
if (System.getSecurityManager() == null) {
return declaredField(cl, name);
}
return AccessController.doPrivileged((PrivilegedAction<Field>) () ->
declaredField(cl, name));
}
private static Field declaredField(Class<?> cl, String name) { private static Field declaredField(Class<?> cl, String name) {
try { try {
@ -253,14 +243,6 @@ final class SerializationMisdeclarationChecker {
return null; return null;
} }
@SuppressWarnings("removal")
private static Method[] privilegedDeclaredMethods(Class<?> cl) {
if (System.getSecurityManager() == null) {
return cl.getDeclaredMethods();
}
return AccessController.doPrivileged(
(PrivilegedAction<Method[]>) cl::getDeclaredMethods);
}
private static Object objectFromStatic(Field f) { private static Object objectFromStatic(Field f) {
try { try {

@ -70,8 +70,7 @@ public class CheckCSMs {
// The goal is to remove this list of Non-final instance @CS methods // The goal is to remove this list of Non-final instance @CS methods
// over time. Do not add any new one to this list. // over time. Do not add any new one to this list.
private static final Set<String> KNOWN_NON_FINAL_CSMS = private static final Set<String> KNOWN_NON_FINAL_CSMS =
Set.of("java/io/ObjectStreamField#getType ()Ljava/lang/Class;", Set.of("java/lang/Runtime#load (Ljava/lang/String;)V",
"java/lang/Runtime#load (Ljava/lang/String;)V",
"java/lang/Runtime#loadLibrary (Ljava/lang/String;)V", "java/lang/Runtime#loadLibrary (Ljava/lang/String;)V",
"javax/sql/rowset/serial/SerialJavaObject#getFields ()[Ljava/lang/reflect/Field;" "javax/sql/rowset/serial/SerialJavaObject#getFields ()[Ljava/lang/reflect/Field;"
); );