{"affected":[{"package":{"ecosystem":"Hex","name":"protobuf","purl":"pkg:hex/protobuf"},"ranges":[{"events":[{"introduced":"0.8.0"},{"fixed":"0.17.1"}],"type":"SEMVER"}],"versions":["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"]},{"ranges":[{"events":[{"introduced":"b0a1d4eaffaf50012fa71a8e931a47cf252d0370"},{"fixed":"e9432ad1c4099511905353cebcececa3a1f7c3ff"}],"repo":"https://github.com/elixir-protobuf/protobuf","type":"GIT"}]}],"aliases":["GHSA-m497-c2h9-rvw6","CVE-2026-104635"],"credits":[{"name":"Daniel Coles","type":"REPORTER"},{"name":"Daniel Coles","type":"FINDER"},{"name":"Andrea Leopardi","type":"REMEDIATION_DEVELOPER"},{"name":"Jonatan Männchen / EEF","type":"COORDINATOR"}],"database_specific":{"capec_ids":["CAPEC-230"],"cpe_ids":["cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*"],"cwe_ids":["CWE-674"]},"details":"## Summary\n\nUncontrolled 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.\n\nIn `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.\n\nThis issue affects protobuf: from 0.8.0 before 0.17.1.\n\n## Details\n\n**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.\n\n**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`.\n\n**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.\n\n**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.\n\n## Proof of concept\n\n1. Define and compile a self-referential proto3 message, for example a `TreeNode` message with a single embedded field `child` of type `TreeNode`.\n2. Build a JSON document that nests the `child` field several thousand levels deep, for example by wrapping `{}` in `{\"child\": ...}` 5,000 times.\n3. Decode it with `Protobuf.JSON.decode(json, TreeNode, recursion_limit: 100)`.\n4. 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.\n\n## Impact\n\nAn 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.\n\n## Workarounds\n\nReject 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.\n\n## Configurations\n\nThe 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.","id":"EEF-CVE-2026-104635","modified":"2026-10-09T09:24:37.810596Z","published":"2026-10-09T08:17:52.372Z","references":[{"type":"ADVISORY","url":"https://github.com/elixir-protobuf/protobuf/security/advisories/GHSA-m497-c2h9-rvw6"},{"type":"WEB","url":"https://cna.erlef.org/cves/CVE-2026-104635.html"},{"type":"WEB","url":"https://github.com/elixir-protobuf/protobuf/commit/b0a1d4eaffaf50012fa71a8e931a47cf252d0370"},{"type":"FIX","url":"https://github.com/elixir-protobuf/protobuf/commit/e9432ad1c4099511905353cebcececa3a1f7c3ff"},{"type":"PACKAGE","url":"https://hex.pm/packages/protobuf"}],"related":[],"schema_version":"1.7.3","severity":[{"score":"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","type":"CVSS_V4"}],"summary":"Uncontrolled recursion in elixir-protobuf/protobuf JSON decoding of self-referential messages","upstream":[]}