joepembe opened a new issue, #3929:
URL: https://github.com/apache/logging-log4j2/issues/3929

   ## Description
   
   The `ThrowableStackTraceRenderer` class can throw a `NullPointerException` 
if the suppressed exceptions associated with the `Throwable` it is rendering 
are being concurrently mutated. This is happening 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.
   
   I have a repro below. To make the issue easier to detect, this repro does 
not use async logging, and I've configured the logger with 
`ignoreExceptions="false"` so that the NPE bubbles up to the logging site. 
However, it is worth noting that in an async logging setup, the appender 
responsible for invoking `ThrowableStackTraceRenderer` would run in the LMAX 
disruptor thread (rather than the application thread that submitted the log 
event), which likely means that this issue would occur if an application that 
uses async logging performed the following sequence of operations in a single 
thread:
   
   1. Generate an exception, then
   2. Log that exception, then
   3. Perform an operation that resulted in the attachment of a new suppressed 
exception to the logged exception.
   
   ## Configuration
   
   **Version:** 2.25.1
   
   **Operating system:** mac OS
   
   **JDK:** Java 21
   
   ## Logs
   
   ```
   Log4jBugTest > test() STANDARD_ERROR
       2025-09-12T16:09:59.750535Z Test worker ERROR An exception occurred 
processing Appender CONSOLE
       java.lang.NullPointerException: Cannot read field "stackLength" because 
"metadata" is null
           at 
org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderStackTraceElements(ThrowableStackTraceRenderer.java:168)
           at 
org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderThrowable(ThrowableStackTraceRenderer.java:110)
           at 
org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderSuppressed(ThrowableStackTraceRenderer.java:133)
           at 
org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderThrowable(ThrowableStackTraceRenderer.java:111)
           at 
org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderThrowable(ThrowableStackTraceRenderer.java:87)
           at 
org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderThrowable(ThrowableStackTraceRenderer.java:59)
           at 
org.apache.logging.log4j.core.pattern.ThrowablePatternConverter.format(ThrowablePatternConverter.java:130)
           at 
org.apache.logging.log4j.core.layout.PatternLayout$NoFormatPatternSerializer.toSerializable(PatternLayout.java:354)
           at 
org.apache.logging.log4j.core.layout.PatternLayout.toText(PatternLayout.java:251)
           at 
org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:237)
           at 
org.apache.logging.log4j.core.layout.PatternLayout.encode(PatternLayout.java:57)
           at 
org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.directEncodeEvent(AbstractOutputStreamAppender.java:227)
           at 
org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.tryAppend(AbstractOutputStreamAppender.java:220)
           at 
org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.append(AbstractOutputStreamAppender.java:211)
           at 
org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:160)
           at 
org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:133)
           at 
org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:124)
           at 
org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:88)
           at 
org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:711)
           at 
org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:669)
           at 
org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:645)
           at 
org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:589)
           at 
org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy.log(AwaitCompletionReliabilityStrategy.java:92)
           at org.apache.logging.log4j.core.Logger.log(Logger.java:187)
           at 
org.apache.logging.log4j.spi.AbstractLogger.tryLogMessage(AbstractLogger.java:2970)
           at 
org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(AbstractLogger.java:2922)
           at 
org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(AbstractLogger.java:2904)
           at 
org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:2682)
           at 
org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:2435)
           at 
org.apache.logging.log4j.spi.AbstractLogger.info(AbstractLogger.java:1779)
           at Log4jBugTest.test(Log4jBugTest.java:34)
   
   ```
   
   ## Reproduction
   
   ```
   import java.util.concurrent.CountDownLatch;
   import java.util.concurrent.ExecutionException;
   import java.util.concurrent.ExecutorService;
   import java.util.concurrent.Executors;
   import java.util.concurrent.Future;
   import java.util.concurrent.atomic.AtomicBoolean;
   
   import org.apache.logging.log4j.LogManager;
   import org.apache.logging.log4j.Logger;
   import org.junit.jupiter.api.Test;
   
   public class Log4jBugTest {
   
       private static final Logger log = 
LogManager.getLogger(Log4jBugTest.class);
   
       @Test
       public void test() throws InterruptedException, ExecutionException {
           try (ExecutorService executor = Executors.newSingleThreadExecutor()) 
{
               for (int i = 0; i < 1000; i++) {
                   AtomicBoolean done = new AtomicBoolean(false);
                   CountDownLatch latch = new CountDownLatch(1);
                   Exception e = new Exception("root-" + i);
                   Future<?> f = executor.submit(() -> {
                       latch.countDown();
                       while (!done.get()) {
                           e.addSuppressed(new Exception("suppressed"));
                       }
                   });
                   latch.await();
                   log.info("The error", e);
                   done.set(true);
                   f.get();
               }
           }
       }
   }
   ```
   
   And my log4j2 config:
   
   ```
   <?xml version="1.0" encoding="UTF-8"?>
   <Configuration status="INFO">
       <Appenders>
           <Console name="CONSOLE" target="SYSTEM_OUT" ignoreExceptions="false">
               <PatternLayout pattern= "%d{dd MMM yyyy HH:mm:ss,SSS} [%p] (%t) 
%c: %m%n%ex"/>
           </Console>
       </Appenders>
       <Loggers>
           <Root level="INFO">
               <AppenderRef ref="CONSOLE" />
           </Root>
       </Loggers>
   </Configuration>
   ```
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to