-
- Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Description
It's possible for StatusData#getFormattedStatus() to throw an ArrayIndexOutOfBoundsException when given a message with a non-null, 0-length parameters array. This happens because there is only a null check on parameters, but no bounds check.
I discovered this very trivially by using slf4j + log4j with no extra configuration, which by default utilizes DefaultConfiguration with the new DefaultLayout which uses StatusData under the hood. The 0-length parameters array seems to be a result of a call to MutableLogEvent#setMessage which allocates a new Object array when swapping parameters with another ReusableMessage, though I haven't fully debugged that codepath to confirm if this is the source.
Either way, the lack of a bounds check in getFormattedStatus seems wrong, and is trivial to cause the exception to be thrown.
Configuration
Version: 2.x branch (e.g. 2.25.0-SNAPSHOT)
Operating system: macOS Sequoia 15.3
JDK: OpenJDK 64-Bit Server VM Corretto-17.0.6.10.1 (build 17.0.6+10-LTS, mixed mode, sharing)
Logs
See the unit test reproducers below. On 2.x, the test that directly calls getFormattedStatus it will fail with:
java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 0 at org.apache.logging.log4j.status.StatusData.getFormattedStatus(StatusData.java:182) The test that logs via slf4j will not cause a failure, but will log the stacktrace for the ArrayIndexOutOfBoundsException instead of the actual log message:
2025-03-20T14:23:21.756536Z main ERROR An exception occurred processing Appender DefaultConsole-2 java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 0 at org.apache.logging.log4j.status.StatusData.getFormattedStatus(StatusData.java:182) at org.apache.logging.log4j.core.config.DefaultLayout.toSerializable(DefaultLayout.java:50) at org.apache.logging.log4j.core.config.DefaultLayout.toByteArray(DefaultLayout.java:55) at org.apache.logging.log4j.core.config.DefaultLayout.encode(DefaultLayout.java:60) at org.apache.logging.log4j.core.config.DefaultLayout.encode(DefaultLayout.java:36) 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:714) at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:672) at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:648) at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:584) at org.apache.logging.log4j.core.config.DefaultReliabilityStrategy.log(DefaultReliabilityStrategy.java:73) 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:2653) at org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:2393) at org.apache.logging.slf4j.Log4jLogger.error(Log4jLogger.java:293) at org.apache.logging.slf4j.Slf4jDefaultConfigurationTest.test_logging_with_default_configuration(Slf4jDefaultConfigurationTest.java:13) Reproduction
The following JUnit test demonstrates the problem:
public class StatusDataTest { @Test void test_getFormattedData_does_not_throw() { Message message = new Message() { @Override public String getFormattedMessage() { return "formatted"; } @Override public Object[] getParameters() { return Constants.EMPTY_OBJECT_ARRAY; } @Override public Throwable getThrowable() { return null; } }; StatusData statusData = new StatusData(null, Level.ERROR, message, null, null); // will throw assertThat(statusData.getFormattedStatus()).contains("formatted"); } } It is also trivial to construct a test in log4j-slf4j-impl to trigger this codepath via slf4j:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Slf4jDefaultConfigurationTest { private static final Logger logger = LoggerFactory.getLogger(Slf4jDefaultConfigurationTest.class); @Test void test_logging_with_default_configuration() { // logs the ArrayIndexOutOfBoundsException, rather than the message; on 2.24.3, logs the message as expected logger.error("foo"); } } Metadata
Metadata
Assignees
Labels
Type
Projects
Status