Skip to content

Commit 822236a

Browse files
nfullerandi34
authored andcommitted
Add additional checks in ObjectInputStream
Thanks to Jann Horn for reporting a bug in ObjectInputStream and sending the initial patch. Add some checks that the class of an object being deserialized still conforms to the requirements for serialization. Add some checks that the class being deserialized matches the type information (enum, serializable, externalizable) held in the stream. Delayed static initialization of classes until the type of the class has been validated against the stream content in some cases. Added more tests. Bug: 15874291 (cherry picked from commit 738c833) Change-Id: I9f5437ed60936882de56589537176466624e631d
1 parent 9562048 commit 822236a

File tree

4 files changed

+349
-21
lines changed

4 files changed

+349
-21
lines changed

luni/src/main/java/java/io/ObjectInputStream.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,8 @@ private void readFieldValues(EmulatedFieldsForLoading emulatedFields)
10521052
* @see #readFields
10531053
* @see #readObject()
10541054
*/
1055-
private void readFieldValues(Object obj, ObjectStreamClass classDesc) throws OptionalDataException, ClassNotFoundException, IOException {
1055+
private void readFieldValues(Object obj, ObjectStreamClass classDesc)
1056+
throws OptionalDataException, ClassNotFoundException, IOException {
10561057
// Now we must read all fields and assign them to the receiver
10571058
ObjectStreamField[] fields = classDesc.getLoadFields();
10581059
fields = (fields == null) ? ObjectStreamClass.NO_FIELDS : fields;
@@ -1574,6 +1575,9 @@ private Object readEnum(boolean unshared) throws OptionalDataException,
15741575
ClassNotFoundException, IOException {
15751576
// read classdesc for Enum first
15761577
ObjectStreamClass classDesc = readEnumDesc();
1578+
1579+
Class enumType = classDesc.checkAndGetTcEnumClass();
1580+
15771581
int newHandle = nextHandle();
15781582
// read name after class desc
15791583
String name;
@@ -1595,9 +1599,11 @@ private Object readEnum(boolean unshared) throws OptionalDataException,
15951599

15961600
Enum<?> result;
15971601
try {
1598-
result = Enum.valueOf((Class) classDesc.forClass(), name);
1602+
result = Enum.valueOf(enumType, name);
15991603
} catch (IllegalArgumentException e) {
1600-
throw new InvalidObjectException(e.getMessage());
1604+
InvalidObjectException ioe = new InvalidObjectException(e.getMessage());
1605+
ioe.initCause(e);
1606+
throw ioe;
16011607
}
16021608
registerObjectRead(result, newHandle, unshared);
16031609
return result;
@@ -1781,9 +1787,10 @@ private Object readNewObject(boolean unshared)
17811787
throw missingClassDescriptor();
17821788
}
17831789

1790+
Class<?> objectClass = classDesc.checkAndGetTcObjectClass();
1791+
17841792
int newHandle = nextHandle();
1785-
Class<?> objectClass = classDesc.forClass();
1786-
Object result = null;
1793+
Object result;
17871794
Object registeredResult = null;
17881795
if (objectClass != null) {
17891796
// Now we know which class to instantiate and which constructor to
@@ -2055,8 +2062,7 @@ public short readShort() throws IOException {
20552062
* if the source stream does not contain readable serialized
20562063
* objects.
20572064
*/
2058-
protected void readStreamHeader() throws IOException,
2059-
StreamCorruptedException {
2065+
protected void readStreamHeader() throws IOException, StreamCorruptedException {
20602066
if (input.readShort() == STREAM_MAGIC
20612067
&& input.readShort() == STREAM_VERSION) {
20622068
return;
@@ -2256,7 +2262,7 @@ protected Class<?> resolveClass(ObjectStreamClass osClass)
22562262
// not primitive class
22572263
// Use the first non-null ClassLoader on the stack. If null, use
22582264
// the system class loader
2259-
cls = Class.forName(className, true, callerClassLoader);
2265+
cls = Class.forName(className, false, callerClassLoader);
22602266
}
22612267
}
22622268
return cls;
@@ -2330,8 +2336,7 @@ private void verifyAndInit(ObjectStreamClass loadedStreamClass)
23302336
throws InvalidClassException {
23312337

23322338
Class<?> localClass = loadedStreamClass.forClass();
2333-
ObjectStreamClass localStreamClass = ObjectStreamClass
2334-
.lookupStreamClass(localClass);
2339+
ObjectStreamClass localStreamClass = ObjectStreamClass.lookupStreamClass(localClass);
23352340

23362341
if (loadedStreamClass.getSerialVersionUID() != localStreamClass
23372342
.getSerialVersionUID()) {

luni/src/main/java/java/io/ObjectStreamClass.java

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,6 @@ static ObjectStreamClass lookupStreamClass(Class<?> cl) {
10881088
tlc.put(cl, cachedValue);
10891089
}
10901090
return cachedValue;
1091-
10921091
}
10931092

10941093
/**
@@ -1318,4 +1317,72 @@ private int primitiveSize(Class<?> type) {
13181317
public String toString() {
13191318
return getName() + ": static final long serialVersionUID =" + getSerialVersionUID() + "L;";
13201319
}
1320+
1321+
/**
1322+
* Checks the local class to make sure it is valid for {@link ObjectStreamConstants#TC_OBJECT}
1323+
* deserialization. Also performs some sanity checks of the stream data. This method is used
1324+
* during deserialization to confirm the local class is likely to be compatible with the coming
1325+
* stream data, but before an instance is instantiated.
1326+
*
1327+
* @hide used internally during deserialization
1328+
*/
1329+
public Class<?> checkAndGetTcObjectClass() throws InvalidClassException {
1330+
// We check some error possibilities that might cause problems later.
1331+
boolean wasSerializable = (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
1332+
boolean wasExternalizable = (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
1333+
if (wasSerializable == wasExternalizable) {
1334+
throw new InvalidClassException(
1335+
getName() + " stream data is corrupt: SC_SERIALIZABLE=" + wasSerializable
1336+
+ " SC_EXTERNALIZABLE=" + wasExternalizable
1337+
+ ", classDescFlags must have one or the other");
1338+
}
1339+
1340+
// TC_ENUM is handled elsewhere. See checkAndGetTcEnumClass().
1341+
if (isEnum()) {
1342+
throw new InvalidClassException(
1343+
getName() + " local class is incompatible: Local class is an enum, streamed"
1344+
+ " data is tagged with TC_OBJECT");
1345+
}
1346+
1347+
// isSerializable() is true if the local class implements Serializable. Externalizable
1348+
// classes are also Serializable via inheritance.
1349+
if (!isSerializable()) {
1350+
throw new InvalidClassException(getName() + " local class is incompatible: Not"
1351+
+ " Serializable");
1352+
}
1353+
1354+
// The stream class was externalizable, but is only serializable locally.
1355+
if (wasExternalizable != isExternalizable()) {
1356+
throw new InvalidClassException(
1357+
getName() + " local class is incompatible: Local class is Serializable, stream"
1358+
+ " data requires Externalizable");
1359+
}
1360+
1361+
// The following are left unchecked and thus are treated leniently at this point.
1362+
// SC_BLOCK_DATA may be set iff SC_EXTERNALIZABLE is set AND version 2 of the protocol is in
1363+
// use.
1364+
// SC_ENUM should not be set.
1365+
1366+
return forClass();
1367+
}
1368+
1369+
/**
1370+
* Checks the local class to make sure it is valid for {@link ObjectStreamConstants#TC_ENUM}
1371+
* deserialization. This method is used during deserialization to confirm the local class is
1372+
* likely to be compatible with the coming stream data, but before an instance is instantiated.
1373+
*
1374+
* @hide used internally during deserialization
1375+
*/
1376+
public Class<?> checkAndGetTcEnumClass() throws InvalidClassException {
1377+
if (!isEnum()) {
1378+
throw new InvalidClassException(
1379+
getName() + " local class is incompatible: Local class is not an enum,"
1380+
+ " streamed data is tagged with TC_ENUM");
1381+
}
1382+
1383+
// The stream flags are expected to be SC_SERIALIZABLE | SC_ENUM but these and the
1384+
// other flags are not used when reading enum data so they are treated leniently.
1385+
1386+
return forClass();
1387+
}
13211388
}

luni/src/main/java/java/io/ObjectStreamConstants.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,25 +149,25 @@ public abstract interface ObjectStreamConstants {
149149
// Flags that indicate if the object was serializable, externalizable
150150
// and had a writeObject method when dumped.
151151
/**
152-
* Bit mask for the {@code flag} field in ObjectStreamClass. Indicates
153-
* that a serializable class has its own {@code writeObject} method.
152+
* Bit mask for the {@code flag} field in {@link ObjectStreamClass}. Indicates
153+
* that a {@link Serializable} class has its own {@code writeObject} method.
154154
*/
155155
public static final byte SC_WRITE_METHOD = 0x01; // If SC_SERIALIZABLE
156156

157157
/**
158-
* Bit mask for the {@code flag} field in ObjectStreamClass. Indicates
159-
* that a class is serializable.
158+
* Bit mask for the {@code flag} field in {@link ObjectStreamClass}. Indicates
159+
* that a class implements {@link Serializable} but not {@link Externalizable}.
160160
*/
161161
public static final byte SC_SERIALIZABLE = 0x02;
162162

163163
/**
164-
* Bit mask for the {@code flag} field in ObjectStreamClass. Indicates
165-
* that a class is externalizable.
164+
* Bit mask for the {@code flag} field in {@link ObjectStreamClass}. Indicates
165+
* that a class implements {@link Externalizable}.
166166
*/
167167
public static final byte SC_EXTERNALIZABLE = 0x04;
168168

169169
/**
170-
* Bit mask for the {@code flag} field in ObjectStreamClass. Indicates
170+
* Bit mask for the {@code flag} field in {@link ObjectStreamClass}. Indicates
171171
* that an externalizable class is written in block data mode.
172172
*/
173173
public static final byte SC_BLOCK_DATA = 0x08; // If SC_EXTERNALIZABLE
@@ -178,7 +178,7 @@ public abstract interface ObjectStreamConstants {
178178
public static final byte TC_ENUM = 0x7E;
179179

180180
/**
181-
* Bit mask for the {@code flag} field in ObjectStreamClass. Indicates
181+
* Bit mask for the {@code flag} field in {@link ObjectStreamClass}. Indicates
182182
* that a class is an enum type.
183183
*/
184184
public static final byte SC_ENUM = 0x10;

0 commit comments

Comments
 (0)