Skip to content

Commit e430766

Browse files
committed
GROOVY-12268: Resolve deserialized class names without initializing them
The class-loader-aware newObjectInputStream overrode resolveClass with Class.forName(desc.getName(), true, classLoader) where ObjectInputStream's own resolveClass passes false. Pass false too. History: the override was written in af544cb (2008-12-05) for GROOVY-1627, "Deserialization fails to work", and has never been changed since; the 2012 extraction of IOGroovyMethods out of DefaultGroovyMethods (f0ca689) only moved it. The initialize flag looks incidental rather than chosen. The same 2008 commit added newObjectInputStream(InputStream) with no override at all, which therefore defers initialization the way the JDK does, so the two siblings disagreed from the day they were written; and the problem being solved was which loader can see a script-defined class, not whether that class should be initialized. The accompanying test, StreamClassloaderInScriptTest, passes either way. Effect: a class named in a stream that then fails to deserialize no longer has its static initializer run. The window is narrow but reachable by anyone who controls the stream, since resolveClass runs before the local class descriptor is looked up: a stream truncated between those two points initializes the named class under true and does not under false. Once the local descriptor is looked up the class may initialize regardless, because reading a declared serialVersionUID field initializes it, and a stream that deserializes successfully initializes the class when the instance is created. No legitimate stream can observe the difference. No test accompanies this. Reaching the window requires failing between resolveClass and the descriptor lookup, which is only expressible as a byte-offset truncation of a serialized stream and would be brittle; an attempt using a serialVersionUID mismatch to fail deterministically was discarded because reading that field initializes the class by itself and so cannot distinguish the two settings.
1 parent e3f320e commit e430766

1 file changed

Lines changed: 5 additions & 2 deletions

File tree

src/main/java/org/codehaus/groovy/runtime/IOGroovyMethods.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,10 @@ public static ObjectInputStream newObjectInputStream(InputStream inputStream) th
270270

271271
/**
272272
* Create an object input stream for this input stream using the given class loader.
273+
* <p>
274+
* Classes named in the stream are resolved without running their static initializers,
275+
* matching how {@link ObjectInputStream} resolves them itself. A class that is actually
276+
* deserialized is still initialized when its instance is created.
273277
*
274278
* @param inputStream an input stream
275279
* @param classLoader the class loader to use when loading the class
@@ -281,8 +285,7 @@ public static ObjectInputStream newObjectInputStream(InputStream inputStream, fi
281285
return new ObjectInputStream(inputStream) {
282286
@Override
283287
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
284-
return Class.forName(desc.getName(), true, classLoader);
285-
288+
return Class.forName(desc.getName(), false, classLoader);
286289
}
287290
};
288291
}

0 commit comments

Comments
 (0)