Skip to content

Serialize the LogEvent message delegate through the filtered wrapper - #4271

Open
ppkarwasz wants to merge 1 commit into
feat/2.x/serial-util-java9-filterfrom
feat/2.x/log-event-proxy-wrapped-message
Open

Serialize the LogEvent message delegate through the filtered wrapper#4271
ppkarwasz wants to merge 1 commit into
feat/2.x/serial-util-java9-filterfrom
feat/2.x/log-event-proxy-wrapped-message

Conversation

@ppkarwasz

Copy link
Copy Markdown
Member

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.LogEventProxy transported its Message delegate inside a java.rmi.MarshalledObject. MarshalledObject.get() deserializes its embedded bytes on a private, unfiltered ObjectInputStream, so the nested message bypassed both FilteredObjectInputStream and the JEP 290 filter.

The message is now written with the same SerializationUtil.writeWrappedObject/readWrappedObject mechanism already used by ObjectMessage, which re-applies the deserialization filter to the nested stream, and java.rmi.MarshalledObject is removed from the allowlist. A message that is rejected or unreadable on the receiving side degrades to a SimpleMessage built from the formatted message string, as before.

Compatibility: this changes the serialized form of LogEventProxy.

  • Log events serialized by Log4j 2.8.2–2.25.x are rejected by readers with this change, since MarshalledObject no longer passes the filter.
  • Events serialized with this change remain readable by older versions, with the message downgraded to a SimpleMessage.
  • Previously, a message that threw during serialization was silently replaced by its formatted string on the writing side; matching ObjectMessage semantics, such a failure now propagates to the caller as an IOException.

The serialized-event fixture in Log4jLogEventTest predated nanoOfMillisecond and the trace-context fields and has been regenerated; the stale serializedEvent.dat test resource was unused (SerializedLayoutTest always rewrites it before reading) and is removed.

Stacked on #4270.

`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 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

1 participant