Skip to content

CVE-2026-49844: Verified Repro With Script Download

CVE-2026-49844: Log4j MapMessage emits invalid JSON for non-finite values

CVE-2026-49844 is verified against org.apache.logging.log4j:log4j-api · maven. Affected versions: >=2.13.1,<2.25.5; >=2.26.0,<2.26.1; >=3.0.0-alpha1,<=3.0.0-beta2. Fixed in 2.25.5; 2.26.1. This medium reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00286.

REPRO-2026-00286 org.apache.logging.log4j:log4j-api · maven Variant found Jul 13, 2026 CVE entry ↗ .txt
Severity
MEDIUM
CVSS
6.3
Confidence
HIGH
Reproduced in
11m 14s
Tool calls
170
Spend
$1.95
01 · Overview

What Is CVE-2026-49844?

CVE-2026-49844 is a medium-severity improper-encoding flaw (CWE-116, CVSS 6.3) in Apache Log4j API. When a MapMessage containing non-finite floating-point values is serialized to JSON, Log4j emits invalid JSON that downstream parsers reject. Pruva reproduced it (reproduction REPRO-2026-00286).

02 · Severity & CVSS

CVE-2026-49844 Severity & CVSS Score

CVE-2026-49844 is rated medium severity, with a CVSS base score of 6.3 out of 10.

MEDIUM threat level
6.3 / 10 CVSS base
Weakness CWE-116 — Improper Encoding or Escaping of Output

Medium — meaningful risk under specific conditions. Schedule a fix in the normal cycle.

03 · Affected Versions

Affected org.apache.logging.log4j:log4j-api Versions

org.apache.logging.log4j:log4j-api · maven versions >=2.13.1,<2.25.5; >=2.26.0,<2.26.1; >=3.0.0-alpha1,<=3.0.0-beta2 are affected.

How to Reproduce CVE-2026-49844

$ pruva-verify REPRO-2026-00286
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00286/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-49844

Security impact — reproduced
  • reached the target end-to-end
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

non-finite floating-point values (NaN, Infinity, -Infinity) placed in a logged MapMessage, serialized via getFormattedMessage(["JSON"]) or asJson()

Attack chain
  1. MapMessage.getFormattedMessage(["JSON"])
  2. MapMessage.format(JSON)
  3. MapMessage.asJson(sb)
  4. MapMessageJsonFormatter.format(sb, data)
  5. formatNumber/formatDoubleArray/formatFloatArray
  6. sb.append(doubleNumber/floatNumber) emits bare NaN/Infinity tokens
Runnable proof: reproduction_steps.sh
Captured evidence: vulnerable outputfixed output
Variants tested

Variant/bypass analysis of CVE-2026-49844 (MapMessageJsonFormatter non-finite JSON encoding). Tested every alternate data-path dispatcher branch (nested Map, List, Object[], non-List Collection, custom Number via formatNumber else-branch, BigInteger overflow-to-Infinity, StringBuilderFormattable control) into the same…

How the agent worked 392 events · 170 tool calls · 11 min
11 minDuration
170Tool calls
103Reasoning steps
392Events
3Dead-ends
Agent activity over 11 min
Support
18
Repro
167
Judge
49
Variant
154
0:0011:14

Root Cause and Exploit Chain for CVE-2026-49844

Versions: log4j-api 2.13.1 through 2.25.4, and 2.26.0. (Also 3.0.0-alpha1+ per the Snyk advisory.)Fixed: 2.25.5 and 2.26.1.

CVE-2026-49844 is an improper encoding/serialization flaw in Apache Log4j API's MapMessage JSON output. When a MapMessage containing non-finite floating-point values (NaN, Infinity, -Infinity) is serialized to JSON via MapMessage.asJson() or MapMessage.getFormattedMessage(new String[]{"JSON"}), the MapMessageJsonFormatter emits the bare tokens NaN, Infinity, and -Infinity instead of an RFC 8259-compliant representation. These bare tokens are not valid JSON values, so any conformant downstream JSON parser or log-ingestion system will reject or fail to process the affected log records. This is a bypass of the earlier CVE-2026-34481 fix (which addressed JsonTemplateLayout but did not cover the MapMessage.asJson() code path in log4j-api).

  • Package/component affected: org.apache.logging.log4j:log4j-api — class org.apache.logging.log4j.message.MapMessageJsonFormatter, reached via MapMessage.asJson(StringBuilder) and MapMessage.getFormattedMessage(String[]) with the "JSON" format.
  • Affected versions: log4j-api 2.13.1 through 2.25.4, and 2.26.0. (Also 3.0.0-alpha1+ per the Snyk advisory.)
  • Fixed versions: 2.25.5 and 2.26.1.
  • Risk level: Medium (CWE-116: Improper Encoding or Escaping of Output).
  • Consequences: Malformed JSON in log output. Downstream JSON parsers and log-ingestion/indexing pipelines that enforce RFC 8259 will reject or fail on affected records. An attacker who can influence a floating-point value logged in a MapMessage (e.g., via JsonTemplateLayout's message resolver or any layout relying on MapMessage.asJson()) can inject non-finite values to corrupt log records or disrupt log processing. This is not a remote code execution issue.

Impact Parity

  • Disclosed/claimed maximum impact: Improper encoding of non-finite floating-point values during MapMessage JSON serialization producing output that is not valid JSON (serialization/encoding flaw, other). Explicitly stated as not an RCE issue.
  • Reproduced impact from this run: All 13 test cases (NaN, +Infinity, -Infinity as scalar Double, double[], scalar Float, float[], plus a combined asJson() direct call) produce bare NaN/Infinity/-Infinity tokens in the vulnerable version. A strict RFC 8259 JSON parser (Gson JsonReader with lenient=false) rejects all 13 outputs. The fixed version quotes all values as JSON strings ("NaN", "Infinity", "-Infinity") and all 13 parse successfully.
  • Parity: full — the reproduced behavior exactly matches the disclosed impact (invalid JSON from non-finite values; fix quotes them).
  • Not demonstrated: N/A — no code execution was claimed or expected.

Root Cause

In the vulnerable MapMessageJsonFormatter.java (log4j-api ≤ 2.25.4 / 2.26.0), the formatNumber() method handles Double and Float values by directly appending the primitive to the StringBuilder:

// VULNERABLE (rel/2.25.4)
} else if (number instanceof Double) {
    final double doubleNumber = (Double) number;
    sb.append(doubleNumber);          // <-- produces "NaN", "Infinity", "-Infinity"
} else if (number instanceof Float) {
    final float floatNumber = (float) number;
    sb.append(floatNumber);           // <-- produces "NaN", "Infinity", "-Infinity"
}

Java's StringBuilder.append(double) delegates to Double.toString(double), which returns the strings "NaN", "Infinity", and "-Infinity" for non-finite values. These are bare literals in the JSON output — they are not enclosed in quotes and are not valid JSON number syntax per RFC 8259 §6 (which only permits finite numbers). The same issue exists in formatDoubleArray() and formatFloatArray(), which also call sb.append(item) directly for array elements.

The call chain is:

MapMessage.getFormattedMessage(new String[]{"JSON"})
  → MapMessage.format(MapFormat.JSON, sb)
    → MapMessage.asJson(sb)                         [protected]
      → MapMessageJsonFormatter.format(sb, data)
        → formatNumber(sb, number) / formatDoubleArray(...) / formatFloatArray(...)

Fix (PR #4163, merged as commit 19edb23/squash c7103d5 on 2.x; feadf8eb on 2.25.x; 1352b987 on 2.26.x): The fix adds two private helper methods that check Double.isFinite() / Float.isFinite() and, for non-finite values, call formatString() to wrap the value in JSON string quotes:

// FIXED (rel/2.25.5)
private static void formatDouble(StringBuilder sb, double doubleNumber) {
    if (!Double.isFinite(doubleNumber)) {
        formatString(sb, Double.toString(doubleNumber));  // quotes: "NaN", "Infinity", etc.
    } else {
        sb.append(doubleNumber);
    }
}

All sb.append(doubleNumber) / sb.append(floatNumber) / sb.append(item) call sites in formatNumber(), formatDoubleArray(), and formatFloatArray() are replaced with calls to formatDouble() / formatFloat().

Reproduction Steps

  1. Script: bundle/repro/reproduction_steps.sh (self-contained, idempotent).
  2. What the script does:
    • Installs OpenJDK 17 if not present.
    • Downloads the official Apache-published log4j-api artifacts from Maven Central: log4j-api-2.25.4.jar (vulnerable, built from tag rel/2.25.4) and log4j-api-2.25.5.jar (fixed, built from tag rel/2.25.5). Also downloads gson-2.11.0.jar for strict JSON validation.
    • Verifies via javap that the vulnerable jar lacks formatDouble()/formatFloat() helper methods and the fixed jar has them.
    • Compiles bundle/repro/NonFiniteJsonTest.java — a Java harness that creates a concrete MapMessage subclass and exercises both getFormattedMessage(new String[]{"JSON"}) (public API) and asJson(StringBuilder) (protected, via subclass) with NaN, +Infinity, -Infinity as scalar double/float and as double[]/float[] array elements (13 total cases).
    • Runs the harness against each jar. The harness detects bare (unquoted) non-finite tokens via a character-level scanner and validates each output with a strict RFC 8259 JSON parser (Gson JsonReader with setLenient(false) + skipValue()).
    • Emits a machine-readable VERDICT|... line and writes bundle/repro/runtime_manifest.json.
  3. Expected evidence of reproduction:
    • Vulnerable 2.25.4: bare=13 quoted=0 parseFail=13 — all outputs contain bare NaN/Infinity/-Infinity and fail strict JSON parsing.
    • Fixed 2.25.5: bare=0 quoted=13 parseFail=0 — all outputs quote the values as JSON strings and parse successfully.

Evidence

  • Log files:
    • bundle/logs/vulnerable_output.log — full harness output for vulnerable log4j-api 2.25.4
    • bundle/logs/fixed_output.log — full harness output for fixed log4j-api 2.25.5
    • bundle/logs/compile.log — javac compilation output
  • Key excerpts (vulnerable 2.25.4):
    JSON output: {"number":NaN}
    >> BARE non-finite token detected (invalid JSON per RFC 8259)
    >> strict JSON parse FAILED (invalid per RFC 8259)
    
    JSON output: {"numbers":[-Infinity]}
    >> BARE non-finite token detected (invalid JSON per RFC 8259)
    >> strict JSON parse FAILED (invalid per RFC 8259)
    
    SUMMARY: bare=13, quoted=0, parseFail=13
    VERDICT|2.25.4-vulnerable|VULNERABLE|bare=13|quoted=0|parseFail=13
    
  • Key excerpts (fixed 2.25.5):
    JSON output: {"number":"NaN"}
    >> non-finite value is QUOTED (valid JSON string)
    >> strict JSON parse OK (valid RFC 8259 JSON)
    
    JSON output: {"numbers":["-Infinity"]}
    >> non-finite value is QUOTED (valid JSON string)
    >> strict JSON parse OK (valid RFC 8259 JSON)
    
    SUMMARY: bare=0, quoted=13, parseFail=0
    VERDICT|2.25.5-fixed|FIXED|bare=0|quoted=13|parseFail=0
    
  • javap verification:
    • Vulnerable 2.25.4 MapMessageJsonFormatter methods: no formatDouble(, no formatFloat(.
    • Fixed 2.25.5 MapMessageJsonFormatter methods: has formatDouble(StringBuilder, double) and formatFloat(StringBuilder, float).
  • Environment: OpenJDK 17.0.19, Gson 2.11.0 (strict JSON validator), official Apache log4j-api jars from Maven Central (verified identical to source-built jar from rel/2.25.4 tag — same file size 351127 bytes).
  • Runtime manifest: bundle/repro/runtime_manifest.json

Recommendations / Next Steps

  • Upgrade guidance: Upgrade log4j-api to version 2.25.5+ (2.25.x line) or 2.26.1+ (2.26.x line). If on 3.0.0-alpha, monitor for a fix.
  • Suggested fix approach: Already implemented in PR #4163 — gate non-finite Double/Float values through formatString() so they are emitted as JSON strings. This mirrors Jackson's JsonWriteFeature#WRITE_NAN_AS_STRINGS behavior.
  • Testing recommendations: The fix includes regression tests (MapMessageTest.testJsonFormatterDoubleNonFiniteSupport and testJsonFormatterFloatNonFiniteSupport) that parameterize over NaN, +Infinity, -Infinity for both double and float. Any future changes to MapMessageJsonFormatter should ensure these tests continue to pass. Consider adding strict-JSON-parser validation to CI for all MapMessage JSON output.
  • Downstream mitigation: Log-ingestion systems that use lenient JSON parsers (e.g., Gson default mode, some JavaScript engines) may silently accept bare NaN/Infinity, but strict parsers (RFC 8259) will reject them. Validate log JSON output with a strict parser.

Additional Notes

  • Idempotency confirmation: The script was run twice consecutively; both runs produced identical results (exit code 0, bare=13 for vulnerable, bare=0 for fixed). The script skips re-downloading jars if they already exist.
  • Source build verification: In addition to the Maven Central artifacts, the log4j-api module was built from source at tag rel/2.25.4 using Maven (mvn -pl log4j-api-java9,log4j-api -am install -DskipTests). The source-built jar (log4j-api-2.25.4.jar, 351127 bytes) is byte-size-identical to the Maven Central artifact, confirming the published jar matches the repository source.
  • Edge cases covered: The harness tests both scalar and array forms for both Double and Float, covering all code paths in formatNumber(), formatDoubleArray(), and formatFloatArray(). The BigDecimal path is unaffected (it already uses toString() which never produces non-finite tokens).
  • Scope: This is a library_api claim (claimed_surface=library_api, required_entrypoint_kind=function_call). The reproduction exercises the real MapMessage.getFormattedMessage(["JSON"]) and MapMessage.asJson() functions from the official Apache log4j-api artifacts. No service/network boundary is involved.

Variant Analysis & Alternative Triggers for CVE-2026-49844

Versions: versions (as tested):Fixed: log4j-api 2.25.5 (rel/2.25.5, commit 2e1d9c62) and 2.26.1 (rel/2.26.1, commit dd0f9d25). Both quote all 18 cases (0 bare, 0 parse failures). The fix is also present on 2.x HEAD (b6ba7d0a).

This variant analysis searched for a bypass of the CVE-2026-49844 fix (PR #4163, formatDouble/formatFloat gating in MapMessageJsonFormatter) and for alternate triggers that reach the same sink from different data-path entry points. The original reproduction (bundle/repro/NonFiniteJsonTest) only exercised the direct scalar Double/Float and primitive double[]/float[] paths. This variant harness exercises every alternate dispatcher branch in MapMessageJsonFormatter.format() that can carry a non-finite floating-point value — nested java.util.Map, List, Object[], non-List Collection (Set/ArrayDeque), a custom Number subclass (the formatNumber else-branch), a BigInteger whose doubleValue() overflows to Infinity, and a StringBuilderFormattable. Result: no bypass was found. The alternate data paths reproduce the bug on the vulnerable versions (2.25.4 and 2.26.0 both emit 17 bare NaN/Infinity/-Infinity tokens with 17 strict-JSON parse failures), but the fixed versions (2.25.5 and 2.26.1) quote all 18 cases (0 bare tokens, 0 parse failures). The fix is complete: every code path that can emit a non-finite double/float value routes through formatDouble/formatFloat, which gate non-finite values into formatString (quoting them as JSON strings). The only path that does not route through formatDouble/formatFloatStringBuilderFormattable via formatFormattable — was always safe because it wraps the formattable's output in quotes and JSON-escapes it (confirmed quoted in both vulnerable and fixed versions).

Fix Coverage / Assumptions

  • Invariant the fix relies on: every Number-bearing value that reaches MapMessageJsonFormatter ultimately flows through one of: formatNumber (for scalar Number), formatDoubleArray (for double[]), or formatFloatArray (for float[]). The fix gates the three raw sb.append(<double>/<float>) sites — and the formatNumber else-branch's sb.append(doubleValue) for non-Double/Float/BigDecimal/integer Number subclasses — through the new formatDouble/formatFloat helpers, which check Double.isFinite/Float.isFinite and quote non-finite values via formatString.
  • Code paths the fix explicitly covers:
    • formatNumber: DoubleformatDouble; FloatformatFloat; the else branch (custom Number, BigInteger, etc.) → formatDouble(sb, doubleValue).
    • formatDoubleArray: each double element → formatDouble.
    • formatFloatArray: each float element → formatFloat.
    • All container branches (formatMap, formatList, formatCollection, formatObjectArray, formatIndexedStringMap) recurse into format()formatNumber, so they inherit the fix transitively.
  • What the fix does NOT cover (tested and ruled out): none of the alternate data paths bypass the fix. The StringBuilderFormattable branch (formatFormattable) is intentionally outside the formatDouble/formatFloat gating, but it is safe-by-construction because it wraps output in quotes + escapeJson. This was verified empirically (control case G: quoted in both vulnerable and fixed).
  • Sibling component (out of scope, ruled out): JsonWriter in log4j-layout-template-json (the CVE-2026-34481 surface) independently implements the same isFinite gating in its writeNumber(double)/writeNumber(float) and its else-branch. When JsonTemplateLayout's MessageResolver encounters a MapMessage, it calls MapMessage.getFormattedMessage(["JSON"]) and writes the result via writeRawString — so it inherits the MapMessageJsonFormatter fix. No separate unfixed sink was found.

Variant / Alternate Trigger

Although no bypass of the fixed code was found, the following alternate triggers reproduce the same root-cause bug (bare non-finite JSON tokens) on the vulnerable versions. These are materially different dispatcher branches reaching the same sink, and were not covered by the original repro's 13 cases:

# Data path Dispatcher branch reached Entry point
A nested java.util.Map (e.g. HashMap/LinkedHashMap with Double.NaN) formatMapformatformatNumber MapMessage.with("data", map).getFormattedMessage(["JSON"])
B java.util.List (e.g. ArrayList of Double) formatListformatformatNumber MapMessage.with("items", list).getFormattedMessage(["JSON"])
C Object[] of boxed Double formatObjectArrayformatformatNumber MapMessage.with("arr", objArray).getFormattedMessage(["JSON"])
D non-List Collection (LinkedHashSet, ArrayDeque) formatCollectionformatformatNumber MapMessage.with("set", set).getFormattedMessage(["JSON"])
E custom Number subclass with doubleValue() = NaN/±Infinity formatNumber else-branchsb.append(doubleValue) MapMessage.with("custom", new CustomNumber(NaN)).getFormattedMessage(["JSON"])
F BigInteger so large that doubleValue() overflows to Infinity formatNumber else-branch (BigInteger is not special-cased in MapMessageJsonFormatter, unlike JsonWriter) MapMessage.with("huge", BigInteger.TEN.pow(400)).getFormattedMessage(["JSON"])
G StringBuilderFormattable emitting bare NaN formatFormattable (quotes + escapes) — control, always safe MapMessage.with("formattable", new NaNEmittingFormattable()).getFormattedMessage(["JSON"])
  • Exact entry points: all reachable via the public API MapMessage.getFormattedMessage(new String[]{"JSON"}) (and equivalently MapMessage.asJson(StringBuilder) via a subclass, or MapMessage.asString("JSON")). These are the same public entry points as the parent CVE; the difference is the value type placed in the map, which selects a different internal dispatcher branch.

  • Specific code paths (file/function): log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessageJsonFormatter.javaformat(sb, object, depth) dispatcher → formatMap / formatList / formatCollection / formatObjectArray / formatNumber (and its else branch) / formatFormattable.

  • Package/component affected: org.apache.logging.log4j:log4j-apiorg.apache.logging.log4j.message.MapMessageJsonFormatter, reached via MapMessage.asJson(StringBuilder) and MapMessage.getFormattedMessage(String[]) with the "JSON" format. Same component as the parent CVE.

  • Affected versions (as tested):

    • Vulnerable: log4j-api 2.25.4 (rel/2.25.4, commit 0628e53b) and 2.26.0 (rel/2.26.0, commit c1ad2a66). Both emit 17 bare non-finite tokens / 17 strict-JSON parse failures across the alternate paths.
    • Fixed: log4j-api 2.25.5 (rel/2.25.5, commit 2e1d9c62) and 2.26.1 (rel/2.26.1, commit dd0f9d25). Both quote all 18 cases (0 bare, 0 parse failures). The fix is also present on 2.x HEAD (b6ba7d0a).
  • Risk level / consequences: Medium (CWE-116). Same as parent: malformed JSON in log output; strict RFC 8259 parsers / log-ingestion pipelines reject affected records. Not an RCE. The alternate paths widen the set of attacker-controllable inputs that can trigger the bug on unpatched versions (any nested container or custom Number, not just scalar/double[]), but the patched versions are fully covered.

Impact Parity

  • Disclosed/claimed maximum impact (parent): improper encoding of non-finite floating-point values during MapMessage JSON serialization producing output that is not valid JSON (serialization/encoding flaw); explicitly not an RCE.
  • Reproduced impact from this variant run: on vulnerable versions, the alternate data paths produce the identical invalid-JSON symptom — bare NaN/Infinity/-Infinity tokens rejected by a strict (RFC 8259) Gson JsonReader (lenient=false). On fixed versions, all alternate paths produce valid quoted-string JSON.
  • Parity: full — the alternate triggers exhibit byte-for-byte the same invalid-JSON behavior as the parent on vulnerable versions, and the fix neutralizes them identically.
  • Not demonstrated: N/A — no code execution was claimed or expected.

Root Cause

The root cause is unchanged from the parent CVE: MapMessageJsonFormatter appends primitive double/float values directly to the StringBuilder via sb.append(<double>), and StringBuilder.append(double) delegates to Double.toString(double), which returns the bare literals "NaN", "Infinity", "-Infinity" for non-finite values. These are not valid JSON number tokens (RFC 8259 §6 permits only finite numbers). The alternate data paths (A–F) all funnel into the same formatNumber/formatDoubleArray/formatFloatArray sinks, so on unpatched versions any container or custom Number carrying a non-finite value produces the same bare tokens.

The fix (commit 19edb23e on 2.x; feadf8eb on 2.25.x; 1352b987 on 2.26.x — PR #4163) introduces formatDouble/formatFloat helpers that gate non-finite values through formatString (quoting them). Critically, the fix updates all three raw-append sites, including the formatNumber else-branch (formatDouble(sb, doubleValue)), which is why the custom-Number (E) and BigInteger-overflow (F) alternate triggers — which reach that else-branch rather than the Double/Float instanceof branches — are also covered.

Because the fix touches the sink (formatNumber/formatDoubleArray/formatFloatArray) rather than any single entry path, and because every Number-bearing dispatcher branch ultimately recurses to that sink, the fix is complete: there is no remaining code path that appends a raw non-finite double/float to the JSON output.

Reproduction Steps

  1. Script: bundle/vuln_variant/reproduction_steps.sh (self-contained, idempotent).
  2. What the script does:
    • Installs OpenJDK 17 if needed.
    • Downloads the official Apache log4j-api jars from Maven Central for four versions: 2.25.4 (vulnerable), 2.25.5 (fixed), 2.26.0 (vulnerable), 2.26.1 (fixed), plus gson-2.11.0 for strict JSON validation.
    • Verifies fix presence per jar via javap (formatDouble(java.lang.StringBuilder, double) present iff fixed).
    • Compiles bundle/vuln_variant/VariantTest.java — a harness that builds a concrete MapMessage subclass and places each alternate value type (nested Map, List, Object[], Set/ArrayDeque, custom Number, BigInteger overflow, StringBuilderFormattable) into the map, then serializes via getFormattedMessage(["JSON"]). Each output is scanned for bare non-finite tokens and validated with a strict RFC 8259 parser (Gson JsonReader, lenient=false, skipValue()).
    • Runs the harness against all four jars, emits a VERDICT|... line per version, and writes bundle/vuln_variant/runtime_manifest.json and bundle/logs/vuln_variant/latest_version.txt.
  3. Expected evidence:
    • Vulnerable 2.25.4 / 2.26.0: bare=17 quoted=1 parseFail=17 (the 1 quoted is the StringBuilderFormattable control case G).
    • Fixed 2.25.5 / 2.26.1: bare=0 quoted=18 parseFail=0.
    • Exit code 1 = no bypass (fix covers all alternate paths); exit 0 = bypass. This run exits 1.

Evidence

  • Log files:
    • bundle/logs/vuln_variant/variant_2.25.4.log — vulnerable 2.25.x line
    • bundle/logs/vuln_variant/variant_2.25.5.log — fixed 2.25.x line
    • bundle/logs/vuln_variant/variant_2.26.0.log — vulnerable 2.26.x line
    • bundle/logs/vuln_variant/variant_2.26.1.log — fixed 2.26.x line
    • bundle/logs/vuln_variant/compile.log — javac output
    • bundle/logs/vuln_variant/latest_version.txt — tested fixed refs
  • Key excerpts (vulnerable 2.25.4, alternate paths):
    --- A. nested java.util.Map value (formatMap -> formatNumber) ---
      [nested-map] value=NaN
        JSON output: {"data":{"v":NaN}}
        >> BARE non-finite token detected (invalid JSON per RFC 8259)
        >> strict JSON parse FAILED (invalid per RFC 8259)
    --- E. custom Number subclass (formatNumber else-branch) ---
      [custom-number] value=Infinity
        JSON output: {"custom":Infinity}
        >> BARE non-finite token detected (invalid JSON per RFC 8259)
    --- F. BigInteger overflow -> Infinity (formatNumber else-branch) ---
      (sanity) huge.doubleValue() = Infinity  (expect Infinity; isFinite=false)
        JSON output: {"huge":Infinity}
        >> BARE non-finite token detected (invalid JSON per RFC 8259)
    --- G. StringBuilderFormattable emitting bare NaN (control: always quoted) ---
        JSON output: {"formattable":"NaN"}
        >> non-finite value is QUOTED (valid JSON string)
    SUMMARY: bare=17, quoted=1, parseFail=17
    VERDICT|2.25.4-vulnerable-2.25.x|VULNERABLE|bare=17|quoted=1|parseFail=17
    
  • Key excerpts (fixed 2.25.5, same alternate paths):
    --- A. nested java.util.Map value (formatMap -> formatNumber) ---
      [nested-map] value=NaN
        JSON output: {"data":{"v":"NaN"}}
        >> non-finite value is QUOTED (valid JSON string)
        >> strict JSON parse OK (valid RFC 8259 JSON)
    --- E. custom Number subclass (formatNumber else-branch) ---
      [custom-number] value=Infinity
        JSON output: {"custom":"Infinity"}
        >> non-finite value is QUOTED (valid JSON string)
    --- F. BigInteger overflow -> Infinity (formatNumber else-branch) ---
        JSON output: {"huge":"Infinity"}
        >> non-finite value is QUOTED (valid JSON string)
    SUMMARY: bare=0, quoted=18, parseFail=0
    VERDICT|2.25.5-fixed-2.25.x|FIXED|bare=0|quoted=18|parseFail=0
    
  • javap fix-presence verification:
    • 2.25.4 / 2.26.0: no formatDouble/formatFloat (vulnerable).
    • 2.25.5 / 2.26.1: private static void formatDouble(java.lang.StringBuilder, double) and formatFloat(...) present (fixed).
  • Environment: OpenJDK 17.0.19, Gson 2.11.0 (strict validator), official Apache log4j-api jars from Maven Central.
  • Runtime manifest: bundle/vuln_variant/runtime_manifest.json

Recommendations / Next Steps

  • No fix gap identified. The PR #4163 fix is complete: it gates the sink rather than individual entry paths, so every Number-bearing dispatcher branch is covered. No code change is required to close a bypass.
  • Defense-in-depth suggestions (optional):
    1. Add a regression test that exercises the container and custom-Number data paths (nested Map, List, Object[], non-List Collection, a custom Number whose doubleValue() is non-finite, and a BigInteger whose doubleValue() overflows to Infinity) — the existing MapMessageTest regression tests only cover scalar double/float. This variant harness (VariantTest.java) can serve as the test skeleton.
    2. Add a BigInteger instanceof branch to MapMessageJsonFormatter.formatNumber mirroring JsonWriter's handling, so BigInteger is serialized via BigDecimal-style logic rather than falling through to the else-branch's longValue()/doubleValue() comparison (currently safe only because the else-branch routes through formatDouble, but a BigIntegerInfinity value is semantically better represented as a quoted string or a BigDecimal-rounded finite value).
    3. Consider adding strict-JSON-parser validation to CI for all MapMessage JSON output shapes (not just scalar values).
  • Upgrade guidance (unchanged): upgrade log4j-api to ≥ 2.25.5 (2.25.x line) or ≥ 2.26.1 (2.26.x line).

Additional Notes

  • Idempotency confirmation: the script was run twice consecutively; both runs produced identical results (exit code 1; bare=17 for vulnerable, bare=0 for fixed across both release lines). The script skips re-downloading jars that already exist.
  • Bounded search justification: the search space for variants within the CVE's sink (MapMessageJsonFormatter) is the set of dispatcher branches in format() that can carry a non-finite floating-point value. There are exactly six such branches plus the StringBuilderFormattable control (A–G above). Every one was tested. No additional materially distinct entry/data path exists within this sink: the remaining branches (null, Boolean, char[], boolean[], byte[], short[], int[], long[], and the else String branch) cannot carry non-finite floating-point values by type. Searching further within this sink would repeat the same root cause/surface.
  • Out-of-scope sibling sinks: JsonWriter (CVE-2026-34481 surface) and JsonLayout (log4j-core, Jackson-backed) are separate components with their own non-finite handling. A defect in those would be a separate bug, not a bypass of the MapMessageJsonFormatter fix, and is therefore out of scope for this variant analysis per the rule that a bug in component A is not a bypass of a fix in component B. JsonWriter was inspected and confirmed to already implement isFinite gating.
  • Edge cases covered: nested Map of Double, List of Double, Object[] of boxed Double, non-List Collection (Set/ArrayDeque) of Double, custom Number subclass (else-branch), BigInteger overflow-to-Infinity (else-branch), and StringBuilderFormattable (control). The BigDecimal path is unaffected (BigDecimal is always finite).

CVE-2026-49844 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:000:42
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-49844 · REPRO-20
0:04
0:06
web search
0:08
web search
0:14
0:15
web search
0:17
0:19
0:21
0:23
web search
0:30
0:30
extract_facts
no facts extracted
0:31
0:31
0:31
supportrepro
0:33
0:33
0:33
0:34
0:34
0:34
0:34
0:36
0:36
0:36
0:36
0:37
0:37
0:40
0:42
web search
08 · How to Fix

How to Fix CVE-2026-49844

Upgrade org.apache.logging.log4j:log4j-api · maven to 2.25.5; 2.26.1 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-49844 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-49844

Is CVE-2026-49844 a bypass of an earlier fix?

Yes. It bypasses the incomplete fix for CVE-2026-34481, which corrected JsonTemplateLayout's JsonWriter but did not cover the MapMessage.asJson() / getFormattedMessage(["JSON"]) code path in log4j-api.

Which Log4j versions are affected by CVE-2026-49844, and where is it fixed?

org.apache.logging.log4j:log4j-api is affected in >= 2.13.1 < 2.25.5, >= 2.26.0 < 2.26.1, and >= 3.0.0-alpha1 <= 3.0.0-beta2. It is fixed in 2.25.5 and 2.26.1 — upgrade to the corresponding patched release.

How severe is CVE-2026-49844?

It is rated medium severity (CVSS 6.3). The impact is availability/integrity of log processing — malformed JSON log records that break downstream parsers — rather than code execution.

How can I reproduce CVE-2026-49844?

Download the verified script from this page and run it in an isolated environment against an affected log4j-api version. It logs a MapMessage containing NaN/Infinity, serializes via asJson(), and shows the bare non-finite tokens producing invalid JSON — which the fixed versions no longer emit.
11 · References

References for CVE-2026-49844

Authoritative sources for CVE-2026-49844 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.