cookiejack15 opened a new issue, #146:
URL: https://github.com/apache/logging-log4j-kotlin/issues/146
## Bug Description
`ContextStack.push(message, vararg args)` delegates to
`ThreadContext.push(message, args)` **without the Kotlin spread operator
(`*`)**. This causes the `args` array to be wrapped inside another array when
passed to the Java varargs method, resulting in systematically corrupted
context stack entries for every parameterized call.
**Affected file:**
`log4j-api-kotlin/src/main/kotlin/org/apache/logging/log4j/kotlin/ContextStack.kt`
(line 106)
**Introduced in:** 1.3.0 (when ContextStack facade was added)
**Affected versions:** 1.3.0, 1.4.0, 1.5.0 (current latest), 1.6.0-SNAPSHOT
## Root Cause
In Kotlin, when passing an array to a Java varargs method, the spread
operator `*` is required to unpack the array into individual varargs elements.
Without it, the Kotlin compiler wraps the entire array as a single element of a
new array.
**Current code (buggy):**
```kotlin
fun push(message: String, vararg args: Any?) = ThreadContext.push(message,
args)
```
**Expected code (fix):**
```kotlin
fun push(message: String, vararg args: Any?) = ThreadContext.push(message,
*args)
```
## What Happens
When a user calls:
```kotlin
ContextStack.push("requestId={} userId={}", "req-123", "user-42")
```
1. Kotlin creates the vararg array: `args = Object[]{"req-123", "user-42"}`
2. Without `*`, the call compiles to: `ThreadContext.push(message, new
Object[] { new Object[]{"req-123", "user-42"} })`
3. The args array is wrapped inside another array — Java's varargs receives
**one** argument (the inner array) instead of two separate strings
**Actual output:** `requestId=[req-123, user-42] userId={}`
**Expected output:** `requestId=req-123 userId=user-42`
Even single-parameter calls are corrupted:
```kotlin
ContextStack.push("userId={}", "user-42")
// Actual: "userId=[user-42]"
// Expected: "userId=user-42"
```
## Proof of Concept
```kotlin
import org.apache.logging.log4j.kotlin.ContextStack
fun main() {
// Multi-parameter: all values merged, second value lost
ContextStack.push("requestId={} userId={}", "req-abc-123", "user-42")
println(ContextStack.peek())
// Actual: "requestId=[req-abc-123, user-42] userId={}"
// Expected: "requestId=req-abc-123 userId=user-42"
ContextStack.pop()
// Single parameter: brackets injected
ContextStack.push("userId={}", "user-42")
println(ContextStack.peek())
// Actual: "userId=[user-42]"
// Expected: "userId=user-42"
ContextStack.pop()
// Three parameters: 2nd and 3rd entirely lost
ContextStack.push("a={} b={} c={}", "1", "2", "3")
println(ContextStack.peek())
// Actual: "a=[1, 2, 3] b={} c={}"
// Expected: "a=1 b=2 c=3"
ContextStack.pop()
}
```
## Unit Test
```kotlin
import org.apache.logging.log4j.kotlin.ContextStack
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class ContextStackPushTest {
@AfterEach
fun tearDown() {
ContextStack.clear()
}
@Test
fun `parameterized push loses second parameter`() {
ContextStack.push("user={} action={}", "admin", "login")
assertEquals("user=admin action=login", ContextStack.peek())
}
@Test
fun `single parameter push corrupts value format`() {
ContextStack.push("user={}", "admin")
assertEquals("user=admin", ContextStack.peek())
}
@Test
fun `three parameters - values lost from designated fields`() {
ContextStack.push("a={} b={} c={}", "1", "2", "3")
assertEquals("a=1 b=2 c=3", ContextStack.peek())
}
}
```
## Proposed Fix
One-character fix — add the spread operator `*`:
```diff
- fun push(message: String, vararg args: Any?) = ThreadContext.push(message,
args)
+ fun push(message: String, vararg args: Any?) = ThreadContext.push(message,
*args)
```
## Impact
- The bug is **systematic** — 100% of parameterized `ContextStack.push()`
calls produce corrupted output
- The bug is **silent** — no exception, no warning, corrupted data is
silently pushed
- Context data used for log correlation, audit trails, and request tracing
is corrupted or lost
- The method has been non-functional for its intended purpose since its
introduction in 1.3.0
--
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]