-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Fix: prevent ThrowableStackTraceRenderer from throwing NPEs #3934
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The `ThrowableStackTraceRenderer` class can throw a `NullPointerException` if the suppressed exceptions associated with the `Throwable` it is rendering are being concurrently mutated. This happens because `ThrowableStackTraceRenderer` invokes `Throwable#getSuppressed()` twice: once in `ThrowableStackTraceRenderer.Context.Metadata#populateMetadata()`, and a second time in `ThrowableStackTraceRenderer#renderThrowable()`, ahead of invoking `ThrowableStackTraceRenderer#renderSuppressed()`. If a racing thread manages to add a new suppressed exception to the being-logged exception between these two invocations, then the `Map<Throwable, Context.Metadata>` constructed by `populateMetadata()` will contain no mapping for the newly-added suppression, and as a result the dereference performed on line 168 explodes. Note: the unit test I am adding here requires the ability to mock `Throwable#getSuppressed()`. Since this method is `final`, I had to add a dependency on `mockito-inline`, which then required that I fix up some unrelated unit tests that had been relying on the lack of support for mocking `final` methods. This fixes apache#3929
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much for the contribution! Would you mind also extending following tests to reproduce the issue, please?
ExtendedThrowablePatternConverterTest
RootThrowablePatternConverterTest
ThrowablePatternConverterTest
...st/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseAppenderTest.java
Show resolved
Hide resolved
...est/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java
Show resolved
Hide resolved
.../java/org/apache/logging/log4j/core/appender/rolling/RandomRollingAppenderOnStartupTest.java
Show resolved
Hide resolved
log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowableStackTraceRenderer.java
Outdated
Show resolved
Hide resolved
See conversation: apache#3934
Instead of modifying these tests, I added a new unit test specifically for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind removing ThrowableStackTraceRendererTest
, the newly mockito-inline
dependency, and changes necessitated due to that, please?
The
ThrowableStackTraceRenderer
class can throw aNullPointerException
if the suppressed exceptions associated with theThrowable
it is rendering are being concurrently mutated. This happens becauseThrowableStackTraceRenderer
invokesThrowable#getSuppressed()
twice: once inThrowableStackTraceRenderer.Context.Metadata#populateMetadata()
, and a second time inThrowableStackTraceRenderer#renderThrowable()
, ahead of invokingThrowableStackTraceRenderer#renderSuppressed()
. If a racing thread manages to add a new suppressed exception to the being-logged exception between these two invocations, then theMap<Throwable, Context.Metadata>
constructed bypopulateMetadata()
will contain no mapping for the newly-added suppression, and as a result the dereference performed on line 168 explodes.Note: the unit test I am adding here requires the ability to mock
Throwable#getSuppressed()
. Since this method isfinal
, I had to add a dependency onmockito-inline
, which then required that I fix up some unrelated unit tests that had been relying on the lack of support for mockingfinal
methods.This fixes #3929