EEF-CVE-2026-104635

Source
https://cna.erlef.org/osv/EEF-CVE-2026-104635.html
Import Source
https://cna.erlef.org/osv/EEF-CVE-2026-104635.json
JSON Data
https://api.osv.dev/v1/vulns/EEF-CVE-2026-104635
Aliases
  • CVE-2026-104635
  • GHSA-m497-c2h9-rvw6
Published
2026-10-09T08:17:52Z
Modified
2026-10-09T09:30:02Z
Severity
  • 8.2 (High) CVSS_V4 - CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N CVSS Calculator
Summary
Uncontrolled recursion in elixir-protobuf/protobuf JSON decoding of self-referential messages
Details

Summary

Uncontrolled Recursion vulnerability in Protobuf.JSON.Decode in elixir-protobuf protobuf allows an unauthenticated remote attacker to crash the decoding process via a deeply nested JSON document. Any application that decodes attacker-supplied JSON with Protobuf.JSON.decode/3, Protobuf.JSON.decode!/3, or Protobuf.JSON.from_decoded/3 into a schema that contains a self-referential or cyclic message type is affected.

In lib/protobuf/json/decode.ex, the embedded-message clause of decode_singular/3 recurses into internal_from_json_data/3 once per nesting level without incrementing or checking the decoder's depth counter. The depth guard increase_depth_and_maybe_throw/1 covers only the Google.Protobuf.ListValue and Google.Protobuf.Struct clauses, so the recursion_limit option has no effect on user-defined message types. Each nesting level allocates a stack frame and heap objects, and a sufficiently deep document exhausts the memory of the decoding process. Confidentiality and integrity are not affected.

This issue affects protobuf: from 0.8.0 before 0.17.1.

Details

1. Entry points. Protobuf.JSON.decode/3, Protobuf.JSON.decode!/3, and Protobuf.JSON.from_decoded/3 in lib/protobuf/json.ex validate options and call Protobuf.JSON.Decode.from_json_data/3, which builds a state map carrying depth: 0 and the recursion_limit (default 100). from_decoded/3 accepts already-parsed data, so the underlying JSON parser never bounds the depth either.

2. Unguarded recursion. For a user-defined message module, internal_from_json_data/3 calls decode_message/4, which calls decode_regular_fields/3 and decode_oneof_fields/3. Each field value passes through decode_value/3 and, for a singular embedded message, reaches the decode_singular/3 clause matching embedded?: true. That clause calls internal_from_json_data/3 with state unchanged, so state.depth stays at 0 at every nesting level. Repeated fields and map values reach the same clause through decode_repeated/3 and decode_map/3.

3. Guard coverage. increase_depth_and_maybe_throw/1 increments depth and throws {:recursion_limit_exceeded, limit} when it exceeds the limit. It is called only from the Google.Protobuf.ListValue and Google.Protobuf.Struct clauses, which the Google.Protobuf.Value clause delegates to. The recursion_limit documentation scopes the option to those wrappers, and no other path checks depth.

4. Result. A self-referential schema such as a Tree message with a Tree child field recurses once per JSON nesting level with no bound. Each level holds a live stack frame and allocates heap objects, so a sufficiently deep document exhausts the memory of the decoding process and crashes it. The BEAM max_heap_size process flag defaults to unlimited, so repeated or concurrent requests can exhaust the memory of the whole node.

Proof of concept

  1. Define and compile a self-referential proto3 message, for example a TreeNode message with a single embedded field child of type TreeNode.
  2. Build a JSON document that nests the child field several thousand levels deep, for example by wrapping {} in {"child": ...} 5,000 times.
  3. Decode it with Protobuf.JSON.decode(json, TreeNode, recursion_limit: 100).
  4. Observe that no Protobuf.JSON.DecodeError for an exceeded recursion limit is raised and the decoder walks every level. The same depth in a Google.Protobuf.Struct payload raises {:recursion_limit_exceeded, 100}. At larger depths the decoding process consumes hundreds of megabytes and crashes with stack and heap exhaustion.

Impact

An unauthenticated client that can reach an endpoint decoding JSON into a self-referential message type can crash the decoding process through stack and memory exhaustion with a single request. Repeated or concurrent requests can exhaust the memory of the whole node and disrupt co-located workloads.

Workarounds

Reject JSON documents whose nesting depth exceeds a fixed bound at the web or middleware layer before passing them to the decoder. No decoder option limits recursion for user-defined message types, so the check must happen before Protobuf.JSON.decode/3, Protobuf.JSON.decode!/3, or Protobuf.JSON.from_decoded/3 is called.

Configurations

The application decodes attacker-controlled JSON with Protobuf.JSON.decode/3, Protobuf.JSON.decode!/3, or Protobuf.JSON.from_decoded/3 into a message type whose schema contains a self-referential or cyclic embedded message.

Database specific
{
    "capec_ids": [
        "CAPEC-230"
    ],
    "cpe_ids": [
        "cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*"
    ],
    "cwe_ids": [
        "CWE-674"
    ]
}
References
Credits
    • Daniel Coles - REPORTER
    • Daniel Coles - FINDER
    • Andrea Leopardi - REMEDIATION_DEVELOPER
    • Jonatan Männchen / EEF - COORDINATOR

Affected packages

Hex / protobuf

Package

Name
protobuf
Purl
pkg:hex/protobuf

Affected ranges

Type
SEMVER
Events
Introduced
0.8.0
Fixed
0.17.1

Affected versions

0.*
0.8.0
0.9.0
0.10.0
0.11.0
0.12.0
0.13.0
0.14.0
0.14.1
0.15.0
0.16.0
0.16.1
0.17.0

Database specific

source
"https://cna.erlef.org/osv/EEF-CVE-2026-104635.json"

Git / github.com/elixir-protobuf/protobuf

Affected ranges

Type
GIT
Repo
https://github.com/elixir-protobuf/protobuf
Events

Affected versions

v0.*
v0.10.0
v0.11.0
v0.12.0
v0.13.0
v0.14.0
v0.14.1
v0.15.0
v0.16.0
v0.17.0
v0.8.0
v0.9.0

Database specific

source
"https://cna.erlef.org/osv/EEF-CVE-2026-104635.json"