SSerguey Asael Shinder
Java coding notes: the JVM, and writing software that lasts

Serguey Asael Shinder: Guava 33.7.2 stops three collections sizing memory by a number from the stream

· by Serguey Asael Shinder / Serguey Shinder

Guava 33.7.2 was released on 29 September with a one-line changelog, and the line is a security fix. The release notes say MapMaker, CompactHashMap and CompactHashSet deserialization was changed to avoid "eagerly allocating as much memory as a serialization payload requests", and link to the advisory GHSA-xxph-c9ww-hj94.

What was wrong. The advisory describes a familiar shape. The readObject methods of CompactHashMap, CompactHashSet and MapMakerInternalMap read an element count or initial capacity from the stream, checked only that it was not negative, and then allocated arrays sized to that value, up to about a billion entries, before the stream had supplied the elements it promised. A tiny serialized payload could therefore demand gigabytes and end in OutOfMemoryError. CompactLinkedHashMap and CompactLinkedHashSet inherit the same path.

Serguey Asael Shinder: Guava 33.7.2 stops three collections sizing memory by a number from the stream
Guava 33.7.2 stops three collections sizing memory by a number from the stream — Serguey Asael Shinder

The advisory points out that Guava had already fixed this shape once, in CVE-2018-10237 for AtomicDoubleArray, by growing storage per element actually read, and that HashBiMap.readObject carries a comment about resisting "hostile attempts to allocate gratuitous heap". The newer collections simply did not follow the pattern.

Who is exposed. Only code that runs native Java deserialization on data it does not control. The classes are serializable and Guava is on almost every JVM backend classpath, so an application that deserializes untrusted bytes can be sent one of these classes whether or not it ever used them itself. Code that never calls ObjectInputStream.readObject on outside data is not affected by this path.

What I would do. Upgrade to 33.7.2. Then look for any ObjectInputStream that reads bytes from a network, a queue or a cache someone else can write to, and put an allow-list filter on it. I wrote about why the obvious size limit in that filter would not have caught this particular bug in a separate note on ObjectInputFilter.