[ 
https://issues.apache.org/jira/browse/LOG4J2-1409?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15314094#comment-15314094
 ] 

Remko Popma commented on LOG4J2-1409:
-------------------------------------

Actually, this can happen even without a lambda, whenever a parameter array of 
length less than 10 is specified. For example:
{code}
        logger.info("Hello {}", new Object[]{1});
        logger.info("Hello {} {} {}", 1, 2,3);
        logger.info("Hello {} {} {}", 1, 2,3);

gives
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
        at 
org.apache.logging.log4j.message.ReusableParameterizedMessage.set(ReusableParameterizedMessage.java:129)
        at 
org.apache.logging.log4j.message.ReusableMessageFactory.newMessage(ReusableMessageFactory.java:112)
        at 
org.apache.logging.log4j.spi.AbstractLogger.logMessage(AbstractLogger.java:2030)
        at 
org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1906)
        at 
org.apache.logging.log4j.spi.AbstractLogger.info(AbstractLogger.java:1445)
        at LOG4J2_1409.main(LOG4J2_1409.java:16)
{code}

It has to do with the mechanism introduced to preserve parameters 
(LOG4J2-1342): the swapParameters() method.
Essentially, explicitly specifying a small parameter array will mean that this 
small array ends up being swapped in.

Unit test that reproduces the issue:
{code}
package org.apache.logging.log4j.message; 
// test must be in log4j-core but in org.apache.logging.log4j.message package 
because it calls package-private methods

import org.apache.logging.log4j.core.impl.MutableLogEvent;
import org.junit.Test;

public class MutableLogEventWithReusableParamMsgTest {

    @Test
    public void testInteractionWithReusableParameterizedMessage() {
        MutableLogEvent evt = new MutableLogEvent();
        ReusableParameterizedMessage msg = new ReusableParameterizedMessage();
        msg.set("Hello {} {} {}", 1, 2, 3);
        evt.setMessage(msg);
        evt.clear();

        msg.set("Hello {}", new Object[]{1});
        evt.setMessage(msg);
        evt.clear();

        msg.set("Hello {}", 1);
        evt.setMessage(msg);
        evt.clear();

        // Uncomment out this log event and the params gets reset correctly (No 
exception occurs)
        //        msg.set("Hello {}", 1);
        //        evt.setMessage(msg);
        //        evt.clear();

        // Exception at this log event - as the params is set to 1!
        msg.set("Hello {} {} {}", 1, 2, 3);
        evt.setMessage(msg);
        evt.clear();
    }
}
{code}

Fix
{code}
Index: 
log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- 
log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
  (revision )
+++ 
log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java
  (revision )
@@ -62,10 +62,32 @@
         Object[] result;
         if (varargs == null) {
             result = params;
-            params = Objects.requireNonNull(emptyReplacement);
+            if (emptyReplacement.length < 10) { // Bad replacement! Too small, 
may blow up future 10-arg messages.
+                if (argCount <= emptyReplacement.length) {
+                    // copy params into the specified replacement array and 
return that
+                    System.arraycopy(params, 0, emptyReplacement, 0, argCount);
+                    result = emptyReplacement;
-        } else {
+                } else {
+                    // replacement array is too small for current content and 
future content: discard it
+                    params = new Object[10];
+                }
+            } else {
+                params = emptyReplacement;
+            }
+        } else {
+            // Whatever array we return, the caller will likely try to reuse 
it in future swapParameter() calls.
+            // However, if the vararg array is less than 10 params it is not 
reusable.
+            // If we ignore this fact and still *swap* with the specified 
array, then the specified array will likely
+            // be discarded (even though it had 10 slots and was perfectly 
reusable).
+            // Instead, we
+            if (argCount <= emptyReplacement.length) {
+                // copy params into the specified replacement array and return 
that
+                System.arraycopy(varargs, 0, emptyReplacement, 0, argCount);
+                result = emptyReplacement;
+            } else {
-            result = varargs;
-            varargs = Objects.requireNonNull(emptyReplacement);
+                result = varargs;
+                varargs = Objects.requireNonNull(emptyReplacement);
+            }
         }
         return result;
     }
{code}


> ArrayIndexOutOfBoundsException in ReusableParameterizedMessage 
> ---------------------------------------------------------------
>
>                 Key: LOG4J2-1409
>                 URL: https://issues.apache.org/jira/browse/LOG4J2-1409
>             Project: Log4j 2
>          Issue Type: Bug
>          Components: API, Core
>    Affects Versions: 2.6
>            Reporter: Shahan
>            Assignee: Remko Popma
>            Priority: Critical
>
> A specific set of parameterised logging messages produces an Array Index Out 
> of Bounds exception. It occurs due to the params array (initially set to 10) 
> being reset to a lower number due to a lambda function passed in as an 
> parameter. Then the subsequent logging messages don't reset this number.
> Best to explain in an example:
> {code}
>     static Logger logger = LogManager.getLogger();
>     public static void main(String[] args) {
>         logger.info("Hello {} {} {}", 1, 2,3);
>         logger.info("Hello {}", () -> "World");
>         logger.info("Hello {}", 1);
>         
>         // Uncomment out this log event and the params gets reset correctly
>         // No exception occurs
>         // logger.info("Hello {}", 1);
>         // Exception at this log event - as the params is set to 1!
>         logger.info("Hello {} {} {}", 1, 2,3);
>     }
> {code}
> Work around for this is not to use the ReusableParameterizedMessage. Instead 
> to specify the legacy Message Factory of ParameterizedMessageFactory as a 
> property of the program and no exception occurs.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to