Search

Find a vulnerability

Search criteria

    4 vulnerabilities by elixir-protobuf

    CVE-2026-104635 (GCVE-0-2026-104635)

    Vulnerability from nvd – Published: 2026-10-09 08:17 – Updated: 2026-10-09 09:24
    VLAI
    Title
    Uncontrolled recursion in elixir-protobuf/protobuf JSON decoding of self-referential messages
    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.
    CWE
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 96/100 over 297 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-protobuf protobuf Affected: 0.8.0 , < 0.17.1 (semver)
        cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-protobuf protobuf Affected: b0a1d4eaffaf50012fa71a8e931a47cf252d0370 , < e9432ad1c4099511905353cebcececa3a1f7c3ff (git)
        cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Date Public
    2026-10-09 08:17
    Show details on NVD website

    {
      "containers": {
        "cna": {
          "affected": [
            {
              "collectionURL": "https://repo.hex.pm",
              "cpes": [
                "cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Protobuf.JSON.Decode\u0027",
                "\u0027Elixir.Protobuf.JSON\u0027"
              ],
              "packageName": "protobuf",
              "packageURL": "pkg:hex/protobuf",
              "product": "protobuf",
              "programFiles": [
                "lib/protobuf/json/decode.ex",
                "lib/protobuf/json.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Protobuf.JSON.Decode\u0027:from_json_data/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON.Decode\u0027:decode_singular/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:decode/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:decode!/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:from_decoded/3"
                }
              ],
              "repo": "https://github.com/elixir-protobuf/protobuf",
              "vendor": "elixir-protobuf",
              "versions": [
                {
                  "lessThan": "0.17.1",
                  "status": "affected",
                  "version": "0.8.0",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Protobuf.JSON.Decode\u0027",
                "\u0027Elixir.Protobuf.JSON\u0027"
              ],
              "packageName": "elixir-protobuf/protobuf",
              "packageURL": "pkg:github/elixir-protobuf/protobuf",
              "product": "protobuf",
              "programFiles": [
                "lib/protobuf/json/decode.ex",
                "lib/protobuf/json.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Protobuf.JSON.Decode\u0027:from_json_data/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON.Decode\u0027:decode_singular/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:decode/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:decode!/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:from_decoded/3"
                }
              ],
              "repo": "https://github.com/elixir-protobuf/protobuf",
              "vendor": "elixir-protobuf",
              "versions": [
                {
                  "lessThan": "e9432ad1c4099511905353cebcececa3a1f7c3ff",
                  "status": "affected",
                  "version": "b0a1d4eaffaf50012fa71a8e931a47cf252d0370",
                  "versionType": "git"
                }
              ]
            }
          ],
          "configurations": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eThe application decodes attacker-controlled JSON with \u003ccode\u003eProtobuf.JSON.decode/3\u003c/code\u003e, \u003ccode\u003eProtobuf.JSON.decode!/3\u003c/code\u003e, or \u003ccode\u003eProtobuf.JSON.from_decoded/3\u003c/code\u003e into a message type whose schema contains a self-referential or cyclic embedded message.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "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."
                }
              ],
              "value": "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."
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "0.17.1",
                      "versionStartIncluding": "0.8.0",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "reporter",
              "value": "Daniel Coles"
            },
            {
              "lang": "en",
              "type": "finder",
              "value": "Daniel Coles"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Andrea Leopardi"
            },
            {
              "lang": "en",
              "type": "coordinator",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "dateAssigned": "2026-10-08T15:40:42.000Z",
          "datePublic": "2026-10-09T08:17:52.000Z",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eUncontrolled Recursion vulnerability in \u003ccode\u003eProtobuf.JSON.Decode\u003c/code\u003e 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 \u003ccode\u003eProtobuf.JSON.decode/3\u003c/code\u003e, \u003ccode\u003eProtobuf.JSON.decode!/3\u003c/code\u003e, or \u003ccode\u003eProtobuf.JSON.from_decoded/3\u003c/code\u003e into a schema that contains a self-referential or cyclic message type is affected.\u003c/p\u003e\n\u003cp\u003eIn \u003ccode\u003elib/protobuf/json/decode.ex\u003c/code\u003e, the embedded-message clause of \u003ccode\u003edecode_singular/3\u003c/code\u003e recurses into \u003ccode\u003einternal_from_json_data/3\u003c/code\u003e once per nesting level without incrementing or checking the decoder\u0027s depth counter. The depth guard \u003ccode\u003eincrease_depth_and_maybe_throw/1\u003c/code\u003e covers only the \u003ccode\u003eGoogle.Protobuf.ListValue\u003c/code\u003e and \u003ccode\u003eGoogle.Protobuf.Struct\u003c/code\u003e clauses, so the \u003ccode\u003erecursion_limit\u003c/code\u003e 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.\u003c/p\u003e\n\u003cp\u003eThis issue affects protobuf: from 0.8.0 before 0.17.1.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "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.\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\u0027s 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."
                }
              ],
              "value": "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.\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\u0027s 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."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-230",
              "descriptions": [
                {
                  "lang": "en",
                  "supportingMedia": [
                    {
                      "base64": false,
                      "type": "text/html",
                      "value": "\u003cp\u003eAn 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.\u003c/p\u003e"
                    },
                    {
                      "base64": false,
                      "type": "text/markdown",
                      "value": "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."
                    }
                  ],
                  "value": "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."
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "NETWORK",
                "baseScore": 8.2,
                "baseSeverity": "HIGH",
                "privilegesRequired": "NONE",
                "providerUrgency": "NOT_DEFINED",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "NONE",
                "valueDensity": "NOT_DEFINED",
                "vectorString": "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",
                "version": "4.0",
                "vulnAvailabilityImpact": "HIGH",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "GENERAL"
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-674",
                  "description": "CWE-674 Uncontrolled Recursion",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-10-09T09:24:37.025Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "name": "GitHub Advisory",
              "tags": [
                "related",
                "vendor-advisory"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/security/advisories/GHSA-m497-c2h9-rvw6"
            },
            {
              "name": "EEF CNA record for CVE-2026-104635",
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-104635.html"
            },
            {
              "name": "OSV record EEF-CVE-2026-104635",
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-104635"
            },
            {
              "name": "Introducing commit b0a1d4e in elixir-protobuf/protobuf",
              "tags": [
                "related"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/commit/b0a1d4eaffaf50012fa71a8e931a47cf252d0370"
            },
            {
              "name": "Fix commit e9432ad in elixir-protobuf/protobuf",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/commit/e9432ad1c4099511905353cebcececa3a1f7c3ff"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "Uncontrolled recursion in elixir-protobuf/protobuf JSON decoding of self-referential messages",
          "workarounds": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eReject 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 \u003ccode\u003eProtobuf.JSON.decode/3\u003c/code\u003e, \u003ccode\u003eProtobuf.JSON.decode!/3\u003c/code\u003e, or \u003ccode\u003eProtobuf.JSON.from_decoded/3\u003c/code\u003e is called.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "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."
                }
              ],
              "value": "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."
            }
          ],
          "x_proofOfConcept": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003col\u003e\n\u003cli\u003eDefine and compile a self-referential proto3 message, for example a \u003ccode\u003eTreeNode\u003c/code\u003e message with a single embedded field \u003ccode\u003echild\u003c/code\u003e of type \u003ccode\u003eTreeNode\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eBuild a JSON document that nests the \u003ccode\u003echild\u003c/code\u003e field several thousand levels deep, for example by wrapping \u003ccode\u003e{}\u003c/code\u003e in \u003ccode\u003e{\"child\": ...}\u003c/code\u003e 5,000 times.\u003c/li\u003e\n\u003cli\u003eDecode it with \u003ccode\u003eProtobuf.JSON.decode(json, TreeNode, recursion_limit: 100)\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eObserve that no \u003ccode\u003eProtobuf.JSON.DecodeError\u003c/code\u003e for an exceeded recursion limit is raised and the decoder walks every level. The same depth in a \u003ccode\u003eGoogle.Protobuf.Struct\u003c/code\u003e payload raises \u003ccode\u003e{:recursion_limit_exceeded, 100}\u003c/code\u003e. At larger depths the decoding process consumes hundreds of megabytes and crashes with stack and heap exhaustion.\u003c/li\u003e\n\u003c/ol\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "1. 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."
                }
              ],
              "value": "* Define and compile a self-referential proto3 message, for example a TreeNode message with a single embedded field child of type TreeNode.\n* Build a JSON document that nests the child field several thousand levels deep, for example by wrapping {} in {\"child\": ...} 5,000 times.\n* Decode it with Protobuf.JSON.decode(json, TreeNode, recursion_limit: 100).\n* 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."
            }
          ],
          "x_technicalAnalysis": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003e\u003cstrong\u003e1. Entry points.\u003c/strong\u003e \u003ccode\u003eProtobuf.JSON.decode/3\u003c/code\u003e, \u003ccode\u003eProtobuf.JSON.decode!/3\u003c/code\u003e, and \u003ccode\u003eProtobuf.JSON.from_decoded/3\u003c/code\u003e in \u003ccode\u003elib/protobuf/json.ex\u003c/code\u003e validate options and call \u003ccode\u003eProtobuf.JSON.Decode.from_json_data/3\u003c/code\u003e, which builds a \u003ccode\u003estate\u003c/code\u003e map carrying \u003ccode\u003edepth: 0\u003c/code\u003e and the \u003ccode\u003erecursion_limit\u003c/code\u003e (default 100). \u003ccode\u003efrom_decoded/3\u003c/code\u003e accepts already-parsed data, so the underlying JSON parser never bounds the depth either.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Unguarded recursion.\u003c/strong\u003e For a user-defined message module, \u003ccode\u003einternal_from_json_data/3\u003c/code\u003e calls \u003ccode\u003edecode_message/4\u003c/code\u003e, which calls \u003ccode\u003edecode_regular_fields/3\u003c/code\u003e and \u003ccode\u003edecode_oneof_fields/3\u003c/code\u003e. Each field value passes through \u003ccode\u003edecode_value/3\u003c/code\u003e and, for a singular embedded message, reaches the \u003ccode\u003edecode_singular/3\u003c/code\u003e clause matching \u003ccode\u003eembedded?: true\u003c/code\u003e. That clause calls \u003ccode\u003einternal_from_json_data/3\u003c/code\u003e with \u003ccode\u003estate\u003c/code\u003e unchanged, so \u003ccode\u003estate.depth\u003c/code\u003e stays at 0 at every nesting level. Repeated fields and map values reach the same clause through \u003ccode\u003edecode_repeated/3\u003c/code\u003e and \u003ccode\u003edecode_map/3\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Guard coverage.\u003c/strong\u003e \u003ccode\u003eincrease_depth_and_maybe_throw/1\u003c/code\u003e increments \u003ccode\u003edepth\u003c/code\u003e and throws \u003ccode\u003e{:recursion_limit_exceeded, limit}\u003c/code\u003e when it exceeds the limit. It is called only from the \u003ccode\u003eGoogle.Protobuf.ListValue\u003c/code\u003e and \u003ccode\u003eGoogle.Protobuf.Struct\u003c/code\u003e clauses, which the \u003ccode\u003eGoogle.Protobuf.Value\u003c/code\u003e clause delegates to. The \u003ccode\u003erecursion_limit\u003c/code\u003e documentation scopes the option to those wrappers, and no other path checks depth.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4. Result.\u003c/strong\u003e A self-referential schema such as a \u003ccode\u003eTree\u003c/code\u003e message with a \u003ccode\u003eTree child\u003c/code\u003e 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 \u003ccode\u003emax_heap_size\u003c/code\u003e process flag defaults to unlimited, so repeated or concurrent requests can exhaust the memory of the whole node.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "**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."
                }
              ],
              "value": "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\n2. 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\n3. 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\n4. 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."
            }
          ]
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-104635",
        "datePublished": "2026-10-09T08:17:52.372Z",
        "dateReserved": "2026-10-08T14:45:01.406Z",
        "dateUpdated": "2026-10-09T09:24:37.025Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-54451 (GCVE-0-2026-54451)

    Vulnerability from nvd – Published: 2026-09-17 18:19 – Updated: 2026-09-21 21:03
    VLAI
    Title
    Elixir protobuf: Unbounded recursion depth in embedded-message decoding
    Summary
    Elixir protobuf is a pure Elixir implementation of Google Protobuf. From 0.8.0 until 0.16.1, services that decode attacker-controlled protobuf bytes with Protobuf.Decoder can be taken offline when the schema contains a self-referential or cyclic message type. In lib/protobuf/decoder.ex, Protobuf.Decoder.value_for_field/3 handles an embedded?: true field by recursively entering the decode / build_message / handle_value / value_for_field call chain without enforcing a nesting-depth limit. Deeply nested embedded fields retain non-tail recursive frames, allowing a comparatively small request to consume substantial CPU and memory, pin a BEAM scheduler, and exhaust the node. This issue is fixed in version 0.16.1.
    SSVC
    Exploitation: poc Automatable: yes Technical Impact: partial
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-09-21 21:03 UTC
    CWE
    Assigner
    GitHub_M CNA under the mitre root
    CNA scorecard C 72/100 over 7580 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-protobuf protobuf Affected: >= 0.8.0, < 0.16.1
    Create a notification for this product.
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-54451",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "yes"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-09-21T21:03:29.889055Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-09-21T21:03:54.370Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/elixir-protobuf/protobuf/security/advisories/GHSA-rv48-qqj5-crxg"
              }
            ],
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "product": "protobuf",
              "vendor": "elixir-protobuf",
              "versions": [
                {
                  "status": "affected",
                  "version": "\u003e= 0.8.0, \u003c 0.16.1"
                }
              ]
            }
          ],
          "descriptions": [
            {
              "lang": "en",
              "value": "Elixir protobuf is a pure Elixir implementation of Google Protobuf. From 0.8.0 until 0.16.1, services that decode attacker-controlled protobuf bytes with Protobuf.Decoder can be taken offline when the schema contains a self-referential or cyclic message type. In lib/protobuf/decoder.ex, Protobuf.Decoder.value_for_field/3 handles an embedded?: true field by recursively entering the decode / build_message / handle_value / value_for_field call chain without enforcing a nesting-depth limit. Deeply nested embedded fields retain non-tail recursive frames, allowing a comparatively small request to consume substantial CPU and memory, pin a BEAM scheduler, and exhaust the node. This issue is fixed in version 0.16.1."
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "NETWORK",
                "baseScore": 8.2,
                "baseSeverity": "HIGH",
                "privilegesRequired": "NONE",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "NONE",
                "vectorString": "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",
                "version": "4.0",
                "vulnAvailabilityImpact": "HIGH",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE"
              }
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-674",
                  "description": "CWE-674: Uncontrolled Recursion",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-09-17T18:19:25.334Z",
            "orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
            "shortName": "GitHub_M"
          },
          "references": [
            {
              "name": "https://github.com/elixir-protobuf/protobuf/security/advisories/GHSA-rv48-qqj5-crxg",
              "tags": [
                "x_refsource_CONFIRM"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/security/advisories/GHSA-rv48-qqj5-crxg"
            },
            {
              "name": "https://github.com/elixir-protobuf/protobuf/commit/352d1769a0ef575a8d18c3f9620609efdb73e2bc",
              "tags": [
                "x_refsource_MISC"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/commit/352d1769a0ef575a8d18c3f9620609efdb73e2bc"
            },
            {
              "name": "https://github.com/elixir-protobuf/protobuf/commit/b8efa97790eece3d2d0e8e7c31a45ed409fe5338",
              "tags": [
                "x_refsource_MISC"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/commit/b8efa97790eece3d2d0e8e7c31a45ed409fe5338"
            },
            {
              "name": "https://github.com/elixir-protobuf/protobuf/releases/tag/v0.16.1",
              "tags": [
                "x_refsource_MISC"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/releases/tag/v0.16.1"
            }
          ],
          "source": {
            "advisory": "GHSA-rv48-qqj5-crxg",
            "discovery": "UNKNOWN"
          },
          "title": "Elixir protobuf: Unbounded recursion depth in embedded-message decoding"
        }
      },
      "cveMetadata": {
        "assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
        "assignerShortName": "GitHub_M",
        "cveId": "CVE-2026-54451",
        "datePublished": "2026-09-17T18:19:25.334Z",
        "dateReserved": "2026-06-15T15:30:40.318Z",
        "dateUpdated": "2026-09-21T21:03:54.370Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-104635 (GCVE-0-2026-104635)

    Vulnerability from cvelistv5 – Published: 2026-10-09 08:17 – Updated: 2026-10-09 09:24
    VLAI
    Title
    Uncontrolled recursion in elixir-protobuf/protobuf JSON decoding of self-referential messages
    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.
    CWE
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 96/100 over 297 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-protobuf protobuf Affected: 0.8.0 , < 0.17.1 (semver)
        cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-protobuf protobuf Affected: b0a1d4eaffaf50012fa71a8e931a47cf252d0370 , < e9432ad1c4099511905353cebcececa3a1f7c3ff (git)
        cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Date Public
    2026-10-09 08:17
    Show details on NVD website

    {
      "containers": {
        "cna": {
          "affected": [
            {
              "collectionURL": "https://repo.hex.pm",
              "cpes": [
                "cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Protobuf.JSON.Decode\u0027",
                "\u0027Elixir.Protobuf.JSON\u0027"
              ],
              "packageName": "protobuf",
              "packageURL": "pkg:hex/protobuf",
              "product": "protobuf",
              "programFiles": [
                "lib/protobuf/json/decode.ex",
                "lib/protobuf/json.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Protobuf.JSON.Decode\u0027:from_json_data/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON.Decode\u0027:decode_singular/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:decode/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:decode!/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:from_decoded/3"
                }
              ],
              "repo": "https://github.com/elixir-protobuf/protobuf",
              "vendor": "elixir-protobuf",
              "versions": [
                {
                  "lessThan": "0.17.1",
                  "status": "affected",
                  "version": "0.8.0",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Protobuf.JSON.Decode\u0027",
                "\u0027Elixir.Protobuf.JSON\u0027"
              ],
              "packageName": "elixir-protobuf/protobuf",
              "packageURL": "pkg:github/elixir-protobuf/protobuf",
              "product": "protobuf",
              "programFiles": [
                "lib/protobuf/json/decode.ex",
                "lib/protobuf/json.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Protobuf.JSON.Decode\u0027:from_json_data/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON.Decode\u0027:decode_singular/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:decode/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:decode!/3"
                },
                {
                  "name": "\u0027Elixir.Protobuf.JSON\u0027:from_decoded/3"
                }
              ],
              "repo": "https://github.com/elixir-protobuf/protobuf",
              "vendor": "elixir-protobuf",
              "versions": [
                {
                  "lessThan": "e9432ad1c4099511905353cebcececa3a1f7c3ff",
                  "status": "affected",
                  "version": "b0a1d4eaffaf50012fa71a8e931a47cf252d0370",
                  "versionType": "git"
                }
              ]
            }
          ],
          "configurations": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eThe application decodes attacker-controlled JSON with \u003ccode\u003eProtobuf.JSON.decode/3\u003c/code\u003e, \u003ccode\u003eProtobuf.JSON.decode!/3\u003c/code\u003e, or \u003ccode\u003eProtobuf.JSON.from_decoded/3\u003c/code\u003e into a message type whose schema contains a self-referential or cyclic embedded message.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "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."
                }
              ],
              "value": "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."
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-protobuf:protobuf:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "0.17.1",
                      "versionStartIncluding": "0.8.0",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "reporter",
              "value": "Daniel Coles"
            },
            {
              "lang": "en",
              "type": "finder",
              "value": "Daniel Coles"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Andrea Leopardi"
            },
            {
              "lang": "en",
              "type": "coordinator",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "dateAssigned": "2026-10-08T15:40:42.000Z",
          "datePublic": "2026-10-09T08:17:52.000Z",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eUncontrolled Recursion vulnerability in \u003ccode\u003eProtobuf.JSON.Decode\u003c/code\u003e 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 \u003ccode\u003eProtobuf.JSON.decode/3\u003c/code\u003e, \u003ccode\u003eProtobuf.JSON.decode!/3\u003c/code\u003e, or \u003ccode\u003eProtobuf.JSON.from_decoded/3\u003c/code\u003e into a schema that contains a self-referential or cyclic message type is affected.\u003c/p\u003e\n\u003cp\u003eIn \u003ccode\u003elib/protobuf/json/decode.ex\u003c/code\u003e, the embedded-message clause of \u003ccode\u003edecode_singular/3\u003c/code\u003e recurses into \u003ccode\u003einternal_from_json_data/3\u003c/code\u003e once per nesting level without incrementing or checking the decoder\u0027s depth counter. The depth guard \u003ccode\u003eincrease_depth_and_maybe_throw/1\u003c/code\u003e covers only the \u003ccode\u003eGoogle.Protobuf.ListValue\u003c/code\u003e and \u003ccode\u003eGoogle.Protobuf.Struct\u003c/code\u003e clauses, so the \u003ccode\u003erecursion_limit\u003c/code\u003e 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.\u003c/p\u003e\n\u003cp\u003eThis issue affects protobuf: from 0.8.0 before 0.17.1.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "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.\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\u0027s 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."
                }
              ],
              "value": "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.\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\u0027s 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."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-230",
              "descriptions": [
                {
                  "lang": "en",
                  "supportingMedia": [
                    {
                      "base64": false,
                      "type": "text/html",
                      "value": "\u003cp\u003eAn 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.\u003c/p\u003e"
                    },
                    {
                      "base64": false,
                      "type": "text/markdown",
                      "value": "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."
                    }
                  ],
                  "value": "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."
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "NETWORK",
                "baseScore": 8.2,
                "baseSeverity": "HIGH",
                "privilegesRequired": "NONE",
                "providerUrgency": "NOT_DEFINED",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "NONE",
                "valueDensity": "NOT_DEFINED",
                "vectorString": "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",
                "version": "4.0",
                "vulnAvailabilityImpact": "HIGH",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "GENERAL"
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-674",
                  "description": "CWE-674 Uncontrolled Recursion",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-10-09T09:24:37.025Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "name": "GitHub Advisory",
              "tags": [
                "related",
                "vendor-advisory"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/security/advisories/GHSA-m497-c2h9-rvw6"
            },
            {
              "name": "EEF CNA record for CVE-2026-104635",
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-104635.html"
            },
            {
              "name": "OSV record EEF-CVE-2026-104635",
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-104635"
            },
            {
              "name": "Introducing commit b0a1d4e in elixir-protobuf/protobuf",
              "tags": [
                "related"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/commit/b0a1d4eaffaf50012fa71a8e931a47cf252d0370"
            },
            {
              "name": "Fix commit e9432ad in elixir-protobuf/protobuf",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/commit/e9432ad1c4099511905353cebcececa3a1f7c3ff"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "Uncontrolled recursion in elixir-protobuf/protobuf JSON decoding of self-referential messages",
          "workarounds": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eReject 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 \u003ccode\u003eProtobuf.JSON.decode/3\u003c/code\u003e, \u003ccode\u003eProtobuf.JSON.decode!/3\u003c/code\u003e, or \u003ccode\u003eProtobuf.JSON.from_decoded/3\u003c/code\u003e is called.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "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."
                }
              ],
              "value": "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."
            }
          ],
          "x_proofOfConcept": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003col\u003e\n\u003cli\u003eDefine and compile a self-referential proto3 message, for example a \u003ccode\u003eTreeNode\u003c/code\u003e message with a single embedded field \u003ccode\u003echild\u003c/code\u003e of type \u003ccode\u003eTreeNode\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eBuild a JSON document that nests the \u003ccode\u003echild\u003c/code\u003e field several thousand levels deep, for example by wrapping \u003ccode\u003e{}\u003c/code\u003e in \u003ccode\u003e{\"child\": ...}\u003c/code\u003e 5,000 times.\u003c/li\u003e\n\u003cli\u003eDecode it with \u003ccode\u003eProtobuf.JSON.decode(json, TreeNode, recursion_limit: 100)\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eObserve that no \u003ccode\u003eProtobuf.JSON.DecodeError\u003c/code\u003e for an exceeded recursion limit is raised and the decoder walks every level. The same depth in a \u003ccode\u003eGoogle.Protobuf.Struct\u003c/code\u003e payload raises \u003ccode\u003e{:recursion_limit_exceeded, 100}\u003c/code\u003e. At larger depths the decoding process consumes hundreds of megabytes and crashes with stack and heap exhaustion.\u003c/li\u003e\n\u003c/ol\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "1. 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."
                }
              ],
              "value": "* Define and compile a self-referential proto3 message, for example a TreeNode message with a single embedded field child of type TreeNode.\n* Build a JSON document that nests the child field several thousand levels deep, for example by wrapping {} in {\"child\": ...} 5,000 times.\n* Decode it with Protobuf.JSON.decode(json, TreeNode, recursion_limit: 100).\n* 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."
            }
          ],
          "x_technicalAnalysis": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003e\u003cstrong\u003e1. Entry points.\u003c/strong\u003e \u003ccode\u003eProtobuf.JSON.decode/3\u003c/code\u003e, \u003ccode\u003eProtobuf.JSON.decode!/3\u003c/code\u003e, and \u003ccode\u003eProtobuf.JSON.from_decoded/3\u003c/code\u003e in \u003ccode\u003elib/protobuf/json.ex\u003c/code\u003e validate options and call \u003ccode\u003eProtobuf.JSON.Decode.from_json_data/3\u003c/code\u003e, which builds a \u003ccode\u003estate\u003c/code\u003e map carrying \u003ccode\u003edepth: 0\u003c/code\u003e and the \u003ccode\u003erecursion_limit\u003c/code\u003e (default 100). \u003ccode\u003efrom_decoded/3\u003c/code\u003e accepts already-parsed data, so the underlying JSON parser never bounds the depth either.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Unguarded recursion.\u003c/strong\u003e For a user-defined message module, \u003ccode\u003einternal_from_json_data/3\u003c/code\u003e calls \u003ccode\u003edecode_message/4\u003c/code\u003e, which calls \u003ccode\u003edecode_regular_fields/3\u003c/code\u003e and \u003ccode\u003edecode_oneof_fields/3\u003c/code\u003e. Each field value passes through \u003ccode\u003edecode_value/3\u003c/code\u003e and, for a singular embedded message, reaches the \u003ccode\u003edecode_singular/3\u003c/code\u003e clause matching \u003ccode\u003eembedded?: true\u003c/code\u003e. That clause calls \u003ccode\u003einternal_from_json_data/3\u003c/code\u003e with \u003ccode\u003estate\u003c/code\u003e unchanged, so \u003ccode\u003estate.depth\u003c/code\u003e stays at 0 at every nesting level. Repeated fields and map values reach the same clause through \u003ccode\u003edecode_repeated/3\u003c/code\u003e and \u003ccode\u003edecode_map/3\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Guard coverage.\u003c/strong\u003e \u003ccode\u003eincrease_depth_and_maybe_throw/1\u003c/code\u003e increments \u003ccode\u003edepth\u003c/code\u003e and throws \u003ccode\u003e{:recursion_limit_exceeded, limit}\u003c/code\u003e when it exceeds the limit. It is called only from the \u003ccode\u003eGoogle.Protobuf.ListValue\u003c/code\u003e and \u003ccode\u003eGoogle.Protobuf.Struct\u003c/code\u003e clauses, which the \u003ccode\u003eGoogle.Protobuf.Value\u003c/code\u003e clause delegates to. The \u003ccode\u003erecursion_limit\u003c/code\u003e documentation scopes the option to those wrappers, and no other path checks depth.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4. Result.\u003c/strong\u003e A self-referential schema such as a \u003ccode\u003eTree\u003c/code\u003e message with a \u003ccode\u003eTree child\u003c/code\u003e 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 \u003ccode\u003emax_heap_size\u003c/code\u003e process flag defaults to unlimited, so repeated or concurrent requests can exhaust the memory of the whole node.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "**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."
                }
              ],
              "value": "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\n2. 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\n3. 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\n4. 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."
            }
          ]
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-104635",
        "datePublished": "2026-10-09T08:17:52.372Z",
        "dateReserved": "2026-10-08T14:45:01.406Z",
        "dateUpdated": "2026-10-09T09:24:37.025Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-54451 (GCVE-0-2026-54451)

    Vulnerability from cvelistv5 – Published: 2026-09-17 18:19 – Updated: 2026-09-21 21:03
    VLAI
    Title
    Elixir protobuf: Unbounded recursion depth in embedded-message decoding
    Summary
    Elixir protobuf is a pure Elixir implementation of Google Protobuf. From 0.8.0 until 0.16.1, services that decode attacker-controlled protobuf bytes with Protobuf.Decoder can be taken offline when the schema contains a self-referential or cyclic message type. In lib/protobuf/decoder.ex, Protobuf.Decoder.value_for_field/3 handles an embedded?: true field by recursively entering the decode / build_message / handle_value / value_for_field call chain without enforcing a nesting-depth limit. Deeply nested embedded fields retain non-tail recursive frames, allowing a comparatively small request to consume substantial CPU and memory, pin a BEAM scheduler, and exhaust the node. This issue is fixed in version 0.16.1.
    SSVC
    Exploitation: poc Automatable: yes Technical Impact: partial
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-09-21 21:03 UTC
    CWE
    Assigner
    GitHub_M CNA under the mitre root
    CNA scorecard C 72/100 over 7580 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-protobuf protobuf Affected: >= 0.8.0, < 0.16.1
    Create a notification for this product.
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-54451",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "yes"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-09-21T21:03:29.889055Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-09-21T21:03:54.370Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/elixir-protobuf/protobuf/security/advisories/GHSA-rv48-qqj5-crxg"
              }
            ],
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "product": "protobuf",
              "vendor": "elixir-protobuf",
              "versions": [
                {
                  "status": "affected",
                  "version": "\u003e= 0.8.0, \u003c 0.16.1"
                }
              ]
            }
          ],
          "descriptions": [
            {
              "lang": "en",
              "value": "Elixir protobuf is a pure Elixir implementation of Google Protobuf. From 0.8.0 until 0.16.1, services that decode attacker-controlled protobuf bytes with Protobuf.Decoder can be taken offline when the schema contains a self-referential or cyclic message type. In lib/protobuf/decoder.ex, Protobuf.Decoder.value_for_field/3 handles an embedded?: true field by recursively entering the decode / build_message / handle_value / value_for_field call chain without enforcing a nesting-depth limit. Deeply nested embedded fields retain non-tail recursive frames, allowing a comparatively small request to consume substantial CPU and memory, pin a BEAM scheduler, and exhaust the node. This issue is fixed in version 0.16.1."
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "NETWORK",
                "baseScore": 8.2,
                "baseSeverity": "HIGH",
                "privilegesRequired": "NONE",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "NONE",
                "vectorString": "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",
                "version": "4.0",
                "vulnAvailabilityImpact": "HIGH",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE"
              }
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-674",
                  "description": "CWE-674: Uncontrolled Recursion",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-09-17T18:19:25.334Z",
            "orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
            "shortName": "GitHub_M"
          },
          "references": [
            {
              "name": "https://github.com/elixir-protobuf/protobuf/security/advisories/GHSA-rv48-qqj5-crxg",
              "tags": [
                "x_refsource_CONFIRM"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/security/advisories/GHSA-rv48-qqj5-crxg"
            },
            {
              "name": "https://github.com/elixir-protobuf/protobuf/commit/352d1769a0ef575a8d18c3f9620609efdb73e2bc",
              "tags": [
                "x_refsource_MISC"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/commit/352d1769a0ef575a8d18c3f9620609efdb73e2bc"
            },
            {
              "name": "https://github.com/elixir-protobuf/protobuf/commit/b8efa97790eece3d2d0e8e7c31a45ed409fe5338",
              "tags": [
                "x_refsource_MISC"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/commit/b8efa97790eece3d2d0e8e7c31a45ed409fe5338"
            },
            {
              "name": "https://github.com/elixir-protobuf/protobuf/releases/tag/v0.16.1",
              "tags": [
                "x_refsource_MISC"
              ],
              "url": "https://github.com/elixir-protobuf/protobuf/releases/tag/v0.16.1"
            }
          ],
          "source": {
            "advisory": "GHSA-rv48-qqj5-crxg",
            "discovery": "UNKNOWN"
          },
          "title": "Elixir protobuf: Unbounded recursion depth in embedded-message decoding"
        }
      },
      "cveMetadata": {
        "assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
        "assignerShortName": "GitHub_M",
        "cveId": "CVE-2026-54451",
        "datePublished": "2026-09-17T18:19:25.334Z",
        "dateReserved": "2026-06-15T15:30:40.318Z",
        "dateUpdated": "2026-09-21T21:03:54.370Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }