Skip to content

ThrowableStackTraceRenderer can throw an ArrayIndexOutOfBoundsException when rendering a Throwable with a concurrently-mutated stacktrace #3940

@mosche

Description

@mosche

Description

We observed this in Elasticsearch when attempting to upgrade to log4j 2.25.1.
In rare cases we drop stacktraces we don't care about on async response listeners prior to sending them over the wire. If these exceptions are also logged, we might hit an ArrayIndexOutOfBoundsException such as shown below.

Configuration

Version: 2.25.1

Operating system: max OS, Linux 6.11.0-1017-gcp (amd64)

JDK: JDK 21, 24, 25

Logs

java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
	at org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderStackTraceElements(ThrowableStackTraceRenderer.java:169)
	at org.apache.logging.log4j.core.pattern.ThrowableStackTraceRenderer.renderThrowable(ThrowableStackTraceRenderer.java:110)
	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.Main.lambda$main$0(Main.java:23)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
	at java.base/java.util.concurrent.ThreadPerTaskExecutor$ThreadBoundFuture.run(ThreadPerTaskExecutor.java:323)
	at java.base/java.lang.VirtualThread.run(VirtualThread.java:456)

Reproduction

package org.apache.logging.log4j.core.pattern;

import org.apache.logging.log4j.core.impl.ThrowableFormatOptions;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;

import static java.util.concurrent.TimeUnit.SECONDS;

public class Main {
    public static void main(String[] args) throws Exception {
        var stackTraceRenderer = ThrowableStackTraceRendererFactory.INSTANCE.createStackTraceRenderer(ThrowableFormatOptions.newInstance(null));
        var executor = Executors.newVirtualThreadPerTaskExecutor();

        for (int i = 0; i < 10; i++) {
            var latch = new CountDownLatch(2);
            var exception = new RuntimeException("exception");

            executor.submit(() -> {
                try {
                    stackTraceRenderer.renderThrowable(new StringBuilder(), exception, System.lineSeparator());
                    latch.countDown();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            });
            executor.submit(() -> {
                exception.setStackTrace(new StackTraceElement[0]);
                latch.countDown();
            });
            if (latch.await(10, SECONDS) == false) {
                throw new IllegalStateException("timed out waiting for tasks to complete");
            }
        }
    }
}

Metadata

Metadata

Assignees

Labels

bugIncorrect, unexpected, or unintended behavior of existing codelayoutsAffects one or more Layout plugins

Type

No type

Projects

Status

Done

Relationships

None yet

Development

No branches or pull requests

Issue actions