Serialize the LogEvent message delegate through the filtered wrapper - #4271
Serialize the LogEvent message delegate through the filtered wrapper#4271ppkarwasz wants to merge 1 commit into
LogEvent message delegate through the filtered wrapper#4271Conversation
`Log4jLogEvent.LogEventProxy` used `java.rmi.MarshalledObject` to transport its `Message` delegate. `MarshalledObject.get()` deserializes its embedded bytes on a private, unfiltered `ObjectInputStream`, so the delegate escaped the deserialization allow-list entirely. The message is now written with the same `SerializationUtil.writeWrappedObject`/`readWrappedObject` mechanism used by `ObjectMessage`, which re-applies the allow-list to the nested stream, and `java.rmi.MarshalledObject` is removed from the allow-list. Compatibility: log events serialized by Log4j 2.8.2-2.25.x are rejected by newer readers, since `MarshalledObject` no longer passes the filter. Events serialized by this version remain readable by older versions, with the message downgraded to a `SimpleMessage`. The serialized-event fixture in `Log4jLogEventTest` is regenerated accordingly, and the stale, unused `serializedEvent.dat` resource is removed (`SerializedLayoutTest` always rewrites it before reading). Part of the hardening series from #4168. Assisted-By: Claude Fable 5 <[email protected]>
3b05d04 to
3df4199
Compare
| this.messageString = message.getFormattedMessage(); | ||
| this.marshalledMessage = marshall(message); | ||
| s.defaultWriteObject(); | ||
| SerializationUtil.writeWrappedObject(message, s); |
There was a problem hiding this comment.
writeWrappedObject serializes into a ByteArrayOutputStream and touches s only in its last statement. So a failed attempt leaves s untouched and a fallback write is still well formed.
try {
SerializationUtil.writeWrappedObject(message, s);
} catch (final Exception ex) {
SerializationUtil.writeWrappedObject(new SimpleMessage(messageString), s);
}messageString is set two lines above and message() already prefers message over it, so the reader needs no change.
|
The One problem. A |
|
Old streams also stop being readable. And nothing tests the new behaviour. Neither the filtered nested read nor the fallback to |
Important
This PR is part of the deserialization hardening work tracked in #4168. The Logging Services PMC does not use nor recommend Java serialization/deserialization, and our security FAQ has long documented this position. This work is submitted solely to reduce the false-positive "vulnerability" reports that keep being filed regardless of that FAQ. Its utility for end users is close to zero.
Since 2.8.2,
Log4jLogEvent.LogEventProxytransported itsMessagedelegate inside ajava.rmi.MarshalledObject.MarshalledObject.get()deserializes its embedded bytes on a private, unfilteredObjectInputStream, so the nested message bypassed bothFilteredObjectInputStreamand the JEP 290 filter.The message is now written with the same
SerializationUtil.writeWrappedObject/readWrappedObjectmechanism already used byObjectMessage, which re-applies the deserialization filter to the nested stream, andjava.rmi.MarshalledObjectis removed from the allowlist. A message that is rejected or unreadable on the receiving side degrades to aSimpleMessagebuilt from the formatted message string, as before.Compatibility: this changes the serialized form of
LogEventProxy.MarshalledObjectno longer passes the filter.SimpleMessage.ObjectMessagesemantics, such a failure now propagates to the caller as anIOException.The serialized-event fixture in
Log4jLogEventTestpredatednanoOfMillisecondand the trace-context fields and has been regenerated; the staleserializedEvent.dattest resource was unused (SerializedLayoutTestalways rewrites it before reading) and is removed.Stacked on #4270.