Search

Find a vulnerability

Search criteria

    10 vulnerabilities by elixir-lang

    CVE-2026-97209 (GCVE-0-2026-97209)

    Vulnerability from nvd – Published: 2026-10-30 08:59 – Updated: 2026-10-30 16:50
    VLAI
    Title
    Insecure temporary file in mix deps.partition lets a local user execute code in another user's dependency build
    Summary
    Insecure Temporary File vulnerability in Mix deps.partition in Elixir allows a local user to execute code as another user via a swapped partition config file. Partitioned dependency compilation passes the project configuration to each partition VM through a file in a fixed-name directory under the shared system temporary directory, and the partition trusts what it reads back. Another local user on the same host can exploit this during a mix deps.compile run that uses partitioned compilation. Mix.Tasks.Deps.Partition.server/4 in lib/mix/lib/mix/tasks/deps.partition.ex creates the directory with File.mkdir_p/1, which accepts an existing directory without checking its owner, so a user who created it first can replace the file before client/0 reads it. This issue affects mix: from 1.20.0-rc.4 before 1.20.5.
    SSVC
    Exploitation: poc Automatable: no Technical Impact: total
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-10-30 16:50 UTC
    CWE
    • CWE-377 - Insecure Temporary File
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 97/100 over 352 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-lang elixir Affected: 1.20.0-rc.4 , < 1.20.5 (semver)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-lang elixir Affected: 7f3d95b60c28ae3026a5c1bf943166391245bfd1 , < * (git)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Date Public
    2026-10-30 08:59
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-97209",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "no"
                      },
                      {
                        "Technical Impact": "total"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-10-30T16:50:17.250968Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-10-30T16:50:46.465Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-96q6-xhwp-fc8q"
              }
            ],
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027"
              ],
              "packageName": "mix",
              "packageURL": "pkg:otp/mix?repository_url=https:%2F%2Fgithub.com%2Felixir-lang%2Felixir\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Felixir-lang%2Felixir.git",
              "product": "elixir",
              "programFiles": [
                "lib/mix/tasks/deps.partition.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:server/3"
                },
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:server/4"
                },
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:client/0"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "lessThan": "1.20.5",
                  "status": "affected",
                  "version": "1.20.0-rc.4",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027"
              ],
              "packageName": "elixir-lang/elixir",
              "packageURL": "pkg:github/elixir-lang/elixir",
              "product": "elixir",
              "programFiles": [
                "lib/mix/lib/mix/tasks/deps.partition.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:server/3"
                },
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:server/4"
                },
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:client/0"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "changes": [
                    {
                      "at": "55eb91220a1de14dc5aa27029d1f45b0f5832b2d",
                      "status": "unaffected"
                    },
                    {
                      "at": "9adbe955c8664f4be77d879dd38c003dd39fa777",
                      "status": "unaffected"
                    }
                  ],
                  "lessThan": "*",
                  "status": "affected",
                  "version": "7f3d95b60c28ae3026a5c1bf943166391245bfd1",
                  "versionType": "git"
                }
              ]
            }
          ],
          "configurations": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eThe attack requires partitioned dependency compilation, which is off by default and enabled by setting \u003ccode\u003eMIX_OS_DEPS_COMPILE_PARTITION_COUNT\u003c/code\u003e to a value greater than one, a \u003ccode\u003emix deps.compile\u003c/code\u003e run that compiles at least two non-local dependencies, and a system temporary directory shared with other local users, which is the default on Linux when \u003ccode\u003eTMPDIR\u003c/code\u003e is unset. Platforms with per-user temporary directories, such as macOS and Windows by default, are not affected unless \u003ccode\u003eTMPDIR\u003c/code\u003e points at a shared location.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "The attack requires partitioned dependency compilation, which is off by default and enabled by setting `MIX_OS_DEPS_COMPILE_PARTITION_COUNT` to a value greater than one, a `mix deps.compile` run that compiles at least two non-local dependencies, and a system temporary directory shared with other local users, which is the default on Linux when `TMPDIR` is unset. Platforms with per-user temporary directories, such as macOS and Windows by default, are not affected unless `TMPDIR` points at a shared location."
                }
              ],
              "value": "The attack requires partitioned dependency compilation, which is off by default and enabled by setting MIX_OS_DEPS_COMPILE_PARTITION_COUNT to a value greater than one, a mix deps.compile run that compiles at least two non-local dependencies, and a system temporary directory shared with other local users, which is the default on Linux when TMPDIR is unset. Platforms with per-user temporary directories, such as macOS and Windows by default, are not affected unless TMPDIR points at a shared location."
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.20.5",
                      "versionStartIncluding": "1.20.0-rc.4",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "finder",
              "value": "Quinn Wilton"
            },
            {
              "lang": "en",
              "type": "reporter",
              "value": "Quinn Wilton"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Jos\u00e9 Valim"
            },
            {
              "lang": "en",
              "type": "coordinator",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "dateAssigned": "2026-10-15T10:43:20.000Z",
          "datePublic": "2026-10-30T08:59:17.000Z",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eInsecure Temporary File vulnerability in Mix \u003ccode\u003edeps.partition\u003c/code\u003e in Elixir allows a local user to execute code as another user via a swapped partition config file. Partitioned dependency compilation passes the project configuration to each partition VM through a file in a fixed-name directory under the shared system temporary directory, and the partition trusts what it reads back. Another local user on the same host can exploit this during a \u003ccode\u003emix deps.compile\u003c/code\u003e run that uses partitioned compilation.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eMix.Tasks.Deps.Partition.server/4\u003c/code\u003e in \u003ccode\u003elib/mix/lib/mix/tasks/deps.partition.ex\u003c/code\u003e creates the directory with \u003ccode\u003eFile.mkdir_p/1\u003c/code\u003e, which accepts an existing directory without checking its owner, so a user who created it first can replace the file before \u003ccode\u003eclient/0\u003c/code\u003e reads it.\u003c/p\u003e\n\u003cp\u003eThis issue affects mix: from 1.20.0-rc.4 before 1.20.5.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Insecure Temporary File vulnerability in Mix `deps.partition` in Elixir allows a local user to execute code as another user via a swapped partition config file. Partitioned dependency compilation passes the project configuration to each partition VM through a file in a fixed-name directory under the shared system temporary directory, and the partition trusts what it reads back. Another local user on the same host can exploit this during a `mix deps.compile` run that uses partitioned compilation.\n\n`Mix.Tasks.Deps.Partition.server/4` in `lib/mix/lib/mix/tasks/deps.partition.ex` creates the directory with `File.mkdir_p/1`, which accepts an existing directory without checking its owner, so a user who created it first can replace the file before `client/0` reads it.\n\nThis issue affects mix: from 1.20.0-rc.4 before 1.20.5."
                }
              ],
              "value": "Insecure Temporary File vulnerability in Mix deps.partition in Elixir allows a local user to execute code as another user via a swapped partition config file. Partitioned dependency compilation passes the project configuration to each partition VM through a file in a fixed-name directory under the shared system temporary directory, and the partition trusts what it reads back. Another local user on the same host can exploit this during a mix deps.compile run that uses partitioned compilation.\n\nMix.Tasks.Deps.Partition.server/4 in lib/mix/lib/mix/tasks/deps.partition.ex creates the directory with File.mkdir_p/1, which accepts an existing directory without checking its owner, so a user who created it first can replace the file before client/0 reads it.\n\nThis issue affects mix: from 1.20.0-rc.4 before 1.20.5."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-26",
              "descriptions": [
                {
                  "lang": "en",
                  "supportingMedia": [
                    {
                      "base64": false,
                      "type": "text/html",
                      "value": "\u003cp\u003eA local user on a shared host who creates the partition directory before the victim\u0027s build replaces the serialized configuration and runs arbitrary shell commands as the developer or CI user whose \u003ccode\u003emix deps.compile\u003c/code\u003e uses partitioned compilation.\u003c/p\u003e"
                    },
                    {
                      "base64": false,
                      "type": "text/markdown",
                      "value": "A local user on a shared host who creates the partition directory before the victim\u0027s build replaces the serialized configuration and runs arbitrary shell commands as the developer or CI user whose `mix deps.compile` uses partitioned compilation."
                    }
                  ],
                  "value": "A local user on a shared host who creates the partition directory before the victim\u0027s build replaces the serialized configuration and runs arbitrary shell commands as the developer or CI user whose mix deps.compile uses partitioned compilation."
                }
              ]
            },
            {
              "capecId": "CAPEC-155",
              "descriptions": [
                {
                  "lang": "en",
                  "supportingMedia": [
                    {
                      "base64": false,
                      "type": "text/html",
                      "value": "\u003cp\u003eEven without pre-creating the directory, the world-readable partition file discloses the project configuration, including \u003ccode\u003eMix.install\u003c/code\u003e compile-time config, to other local users while the build runs.\u003c/p\u003e"
                    },
                    {
                      "base64": false,
                      "type": "text/markdown",
                      "value": "Even without pre-creating the directory, the world-readable partition file discloses the project configuration, including `Mix.install` compile-time config, to other local users while the build runs."
                    }
                  ],
                  "value": "Even without pre-creating the directory, the world-readable partition file discloses the project configuration, including Mix.install compile-time config, to other local users while the build runs."
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "LOCAL",
                "baseScore": 5.4,
                "baseSeverity": "MEDIUM",
                "privilegesRequired": "LOW",
                "providerUrgency": "NOT_DEFINED",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "PASSIVE",
                "valueDensity": "NOT_DEFINED",
                "vectorString": "CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
                "version": "4.0",
                "vulnAvailabilityImpact": "HIGH",
                "vulnConfidentialityImpact": "HIGH",
                "vulnIntegrityImpact": "HIGH",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "GENERAL"
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-377",
                  "description": "CWE-377 Insecure Temporary File",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-10-30T09:01:05.212Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "name": "Insecure temporary file in mix deps.partition lets a local user execute code in another user\u0027s dependency build",
              "tags": [
                "vendor-advisory",
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-96q6-xhwp-fc8q"
            },
            {
              "name": "EEF CNA record for CVE-2026-97209",
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-97209.html"
            },
            {
              "name": "OSV record EEF-CVE-2026-97209",
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-97209"
            },
            {
              "name": "Introducing commit 7f3d95b in elixir-lang/elixir",
              "tags": [
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/7f3d95b60c28ae3026a5c1bf943166391245bfd1"
            },
            {
              "name": "Fix commit 55eb912 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/55eb91220a1de14dc5aa27029d1f45b0f5832b2d"
            },
            {
              "name": "Fix commit 9adbe95 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/9adbe955c8664f4be77d879dd38c003dd39fa777"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "Insecure temporary file in mix deps.partition lets a local user execute code in another user\u0027s dependency build",
          "workarounds": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eOn a multi-user host, set \u003ccode\u003eTMPDIR\u003c/code\u003e to a private per-user directory with mode \u003ccode\u003e0700\u003c/code\u003e before running Mix, so that \u003ccode\u003eSystem.tmp_dir/0\u003c/code\u003e resolves to a location other users cannot pre-create or read. Alternatively, keep partitioned compilation disabled by leaving \u003ccode\u003eMIX_OS_DEPS_COMPILE_PARTITION_COUNT\u003c/code\u003e unset or at \u003ccode\u003e1\u003c/code\u003e, which keeps the configuration inside the parent process.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "On a multi-user host, set `TMPDIR` to a private per-user directory with mode `0700` before running Mix, so that `System.tmp_dir/0` resolves to a location other users cannot pre-create or read. Alternatively, keep partitioned compilation disabled by leaving `MIX_OS_DEPS_COMPILE_PARTITION_COUNT` unset or at `1`, which keeps the configuration inside the parent process."
                }
              ],
              "value": "On a multi-user host, set TMPDIR to a private per-user directory with mode 0700 before running Mix, so that System.tmp_dir/0 resolves to a location other users cannot pre-create or read. Alternatively, keep partitioned compilation disabled by leaving MIX_OS_DEPS_COMPILE_PARTITION_COUNT unset or at 1, which keeps the configuration inside the parent process."
            }
          ],
          "x_proofOfConcept": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003col\u003e\n\u003cli\u003eOn a multi-user Linux host where \u003ccode\u003eSystem.tmp_dir()\u003c/code\u003e resolves to \u003ccode\u003e/tmp\u003c/code\u003e, the attacker creates \u003ccode\u003e/tmp/mix_deps_partition\u003c/code\u003e as their own directory and watches it for new files.\u003c/li\u003e\n\u003cli\u003eThe victim runs \u003ccode\u003emix deps.compile\u003c/code\u003e (directly, or through \u003ccode\u003emix compile\u003c/code\u003e or \u003ccode\u003emix test\u003c/code\u003e) with \u003ccode\u003eMIX_OS_DEPS_COMPILE_PARTITION_COUNT=2\u003c/code\u003e on a project with at least two non-local dependencies. \u003ccode\u003eFile.mkdir_p/1\u003c/code\u003e succeeds on the existing directory and the config file is written into it.\u003c/li\u003e\n\u003cli\u003eBefore the partition VMs boot and read the file, the attacker decodes it, adds a \u003ccode\u003ecompile:\u003c/code\u003e shell command to every dependency in \u003ccode\u003econfig[:deps]\u003c/code\u003e, re-encodes it and writes it back.\u003c/li\u003e\n\u003cli\u003eEach partition decodes the attacker\u0027s term and \u003ccode\u003edeps.compile\u003c/code\u003e runs the injected command as the victim user.\u003c/li\u003e\n\u003c/ol\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "1. On a multi-user Linux host where `System.tmp_dir()` resolves to `/tmp`, the attacker creates `/tmp/mix_deps_partition` as their own directory and watches it for new files.\n2. The victim runs `mix deps.compile` (directly, or through `mix compile` or `mix test`) with `MIX_OS_DEPS_COMPILE_PARTITION_COUNT=2` on a project with at least two non-local dependencies. `File.mkdir_p/1` succeeds on the existing directory and the config file is written into it.\n3. Before the partition VMs boot and read the file, the attacker decodes it, adds a `compile:` shell command to every dependency in `config[:deps]`, re-encodes it and writes it back.\n4. Each partition decodes the attacker\u0027s term and `deps.compile` runs the injected command as the victim user."
                }
              ],
              "value": "* On a multi-user Linux host where System.tmp_dir() resolves to /tmp, the attacker creates /tmp/mix_deps_partition as their own directory and watches it for new files.\n* The victim runs mix deps.compile (directly, or through mix compile or mix test) with MIX_OS_DEPS_COMPILE_PARTITION_COUNT=2 on a project with at least two non-local dependencies. File.mkdir_p/1 succeeds on the existing directory and the config file is written into it.\n* Before the partition VMs boot and read the file, the attacker decodes it, adds a compile: shell command to every dependency in config[:deps], re-encodes it and writes it back.\n* Each partition decodes the attacker\u0027s term and deps.compile runs the injected command as the victim user."
            }
          ],
          "x_technicalAnalysis": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003e\u003cstrong\u003e1. Payload written to a shared location\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eMix.Tasks.Deps.Partition.server/4\u003c/code\u003e in \u003ccode\u003elib/mix/lib/mix/tasks/deps.partition.ex\u003c/code\u003e encodes \u003ccode\u003eMix.ProjectStack.peek()\u003c/code\u003e with \u003ccode\u003e:erlang.term_to_binary/1\u003c/code\u003e and Base64, calls \u003ccode\u003eFile.mkdir_p/1\u003c/code\u003e on \u003ccode\u003ePath.join(System.tmp_dir(), \"mix_deps_partition\")\u003c/code\u003e, and writes the payload to a file with a random name inside it. The directory name is fixed, carries no user id, and \u003ccode\u003eFile.mkdir_p/1\u003c/code\u003e succeeds on a directory that already exists, whoever owns it. The file is created with the default mode, so it is readable by other users in a shared temporary directory.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Partition VMs trust the file\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eEach partition is a fresh \u003ccode\u003eelixir\u003c/code\u003e VM started with \u003ccode\u003e--config-file \u0026lt;path\u0026gt;\u003c/code\u003e. \u003ccode\u003eMix.Tasks.Deps.Partition.client/0\u003c/code\u003e reads the file with \u003ccode\u003eFile.read!/1\u003c/code\u003e, decodes it with \u003ccode\u003e:erlang.binary_to_term/1\u003c/code\u003e, and pushes the decoded entry (project name, config and \u003ccode\u003emix.exs\u003c/code\u003e path) with \u003ccode\u003eMix.ProjectStack.push/3\u003c/code\u003e. Nothing verifies that the file still holds what the parent wrote.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Attacker control\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eA local user who created \u003ccode\u003emix_deps_partition\u003c/code\u003e before the victim\u0027s build owns it and may replace or rewrite any file inside, so they swap the payload between the parent\u0027s write and the client\u0027s read. The client then compiles the attacker\u0027s \u003ccode\u003edeps\u003c/code\u003e list, and a dependency\u0027s \u003ccode\u003ecompile:\u003c/code\u003e option is a shell command Mix executes in the victim\u0027s build. On the development branch the payload also carries \u003ccode\u003ecode_path\u003c/code\u003e and \u003ccode\u003ecompiler_options\u003c/code\u003e, which the client applies with \u003ccode\u003e:code.set_path/1\u003c/code\u003e and \u003ccode\u003eCode.compiler_options/1\u003c/code\u003e, so there the attacker also controls the client VM\u0027s code path.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "**1. Payload written to a shared location**\n\n`Mix.Tasks.Deps.Partition.server/4` in `lib/mix/lib/mix/tasks/deps.partition.ex` encodes `Mix.ProjectStack.peek()` with `:erlang.term_to_binary/1` and Base64, calls `File.mkdir_p/1` on `Path.join(System.tmp_dir(), \"mix_deps_partition\")`, and writes the payload to a file with a random name inside it. The directory name is fixed, carries no user id, and `File.mkdir_p/1` succeeds on a directory that already exists, whoever owns it. The file is created with the default mode, so it is readable by other users in a shared temporary directory.\n\n**2. Partition VMs trust the file**\n\nEach partition is a fresh `elixir` VM started with `--config-file \u003cpath\u003e`. `Mix.Tasks.Deps.Partition.client/0` reads the file with `File.read!/1`, decodes it with `:erlang.binary_to_term/1`, and pushes the decoded entry (project name, config and `mix.exs` path) with `Mix.ProjectStack.push/3`. Nothing verifies that the file still holds what the parent wrote.\n\n**3. Attacker control**\n\nA local user who created `mix_deps_partition` before the victim\u0027s build owns it and may replace or rewrite any file inside, so they swap the payload between the parent\u0027s write and the client\u0027s read. The client then compiles the attacker\u0027s `deps` list, and a dependency\u0027s `compile:` option is a shell command Mix executes in the victim\u0027s build. On the development branch the payload also carries `code_path` and `compiler_options`, which the client applies with `:code.set_path/1` and `Code.compiler_options/1`, so there the attacker also controls the client VM\u0027s code path."
                }
              ],
              "value": "1. Payload written to a shared location\n\nMix.Tasks.Deps.Partition.server/4 in lib/mix/lib/mix/tasks/deps.partition.ex encodes Mix.ProjectStack.peek() with :erlang.term_to_binary/1 and Base64, calls File.mkdir_p/1 on Path.join(System.tmp_dir(), \"mix_deps_partition\"), and writes the payload to a file with a random name inside it. The directory name is fixed, carries no user id, and File.mkdir_p/1 succeeds on a directory that already exists, whoever owns it. The file is created with the default mode, so it is readable by other users in a shared temporary directory.\n\n2. Partition VMs trust the file\n\nEach partition is a fresh elixir VM started with --config-file \u003cpath\u003e. Mix.Tasks.Deps.Partition.client/0 reads the file with File.read!/1, decodes it with :erlang.binary_to_term/1, and pushes the decoded entry (project name, config and mix.exs path) with Mix.ProjectStack.push/3. Nothing verifies that the file still holds what the parent wrote.\n\n3. Attacker control\n\nA local user who created mix_deps_partition before the victim\u0027s build owns it and may replace or rewrite any file inside, so they swap the payload between the parent\u0027s write and the client\u0027s read. The client then compiles the attacker\u0027s deps list, and a dependency\u0027s compile: option is a shell command Mix executes in the victim\u0027s build. On the development branch the payload also carries code_path and compiler_options, which the client applies with :code.set_path/1 and Code.compiler_options/1, so there the attacker also controls the client VM\u0027s code path."
            }
          ]
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-97209",
        "datePublished": "2026-10-30T08:59:17.615Z",
        "dateReserved": "2026-10-14T21:45:01.541Z",
        "dateUpdated": "2026-10-30T16:50:46.465Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-115274 (GCVE-0-2026-115274)

    Vulnerability from nvd – Published: 2026-10-30 08:57 – Updated: 2026-10-30 16:51
    VLAI
    Title
    Unbounded integer parsing in JSON.decode/1 enables CPU and memory exhaustion denial of service in Elixir
    Summary
    Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library's JSON module allows an unauthenticated attacker who supplies a JSON document to cause a denial of service through CPU and memory exhaustion. JSON.decode/1 and JSON.decode!/1 convert each integer literal with erlang:binary_to_integer/1 and do not bound its length. A literal of one million digits decodes successfully but costs 100 to 300 times as much CPU as a string of the same length, so one request body of a few megabytes holds a scheduler for about a second. A literal of more than about 1.29 million digits raises SystemLimitError. Applications generated by phx.new decode request bodies with JSON in Plug.Parsers, before routing and authentication. This issue affects elixir: from 1.18.0-rc.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5.
    SSVC
    Exploitation: poc Automatable: yes Technical Impact: partial
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-10-30 16:51 UTC
    CWE
    • CWE-1284 - Improper Validation of Specified Quantity in Input
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 97/100 over 352 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-lang elixir Affected: 1.18.0-rc.0 , < 1.18.6 (semver)
    Affected: 1.19.0-rc.0 , < 1.19.7 (semver)
    Affected: 1.20.0-rc.0 , < 1.20.5 (semver)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-lang elixir Affected: 793c1147210388587defb649f25f293ffbfa6b1b , < * (git)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Date Public
    2026-10-30 08:57
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-115274",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "yes"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-10-30T16:51:17.679403Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-10-30T16:51:45.396Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-rp5q-xqhq-926h"
              }
            ],
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.JSON\u0027",
                "elixir_json"
              ],
              "packageName": "elixir",
              "packageURL": "pkg:otp/elixir?repository_url=https:%2F%2Fgithub.com%2Felixir-lang%2Felixir\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Felixir-lang%2Felixir.git",
              "product": "elixir",
              "programFiles": [
                "lib/json.ex",
                "src/elixir_json.erl"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.JSON\u0027:decode/1"
                },
                {
                  "name": "\u0027Elixir.JSON\u0027:\u0027decode!\u0027/1"
                },
                {
                  "name": "\u0027Elixir.JSON\u0027:decode/3"
                },
                {
                  "name": "elixir_json:decode/3"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "lessThan": "1.18.6",
                  "status": "affected",
                  "version": "1.18.0-rc.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.19.7",
                  "status": "affected",
                  "version": "1.19.0-rc.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.20.5",
                  "status": "affected",
                  "version": "1.20.0-rc.0",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.JSON\u0027",
                "elixir_json"
              ],
              "packageName": "elixir-lang/elixir",
              "packageURL": "pkg:github/elixir-lang/elixir",
              "product": "elixir",
              "programFiles": [
                "lib/elixir/lib/json.ex",
                "lib/elixir/src/elixir_json.erl"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.JSON\u0027:decode/1"
                },
                {
                  "name": "\u0027Elixir.JSON\u0027:\u0027decode!\u0027/1"
                },
                {
                  "name": "\u0027Elixir.JSON\u0027:decode/3"
                },
                {
                  "name": "elixir_json:decode/3"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "changes": [
                    {
                      "at": "c81f12265919c34a0d5134f1905a4eb4edaea1c2",
                      "status": "unaffected"
                    },
                    {
                      "at": "7154e8781da9fd016a973ea01776a011c8b5726a",
                      "status": "unaffected"
                    },
                    {
                      "at": "cd158efacdc10b30232864251747dff9b8387dfe",
                      "status": "unaffected"
                    },
                    {
                      "at": "ee5cc23f8928aa5a0f25bb7f28e42ecfc7b7d396",
                      "status": "unaffected"
                    }
                  ],
                  "lessThan": "*",
                  "status": "affected",
                  "version": "793c1147210388587defb649f25f293ffbfa6b1b",
                  "versionType": "git"
                }
              ]
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.18.6",
                      "versionStartIncluding": "1.18.0-rc.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.19.7",
                      "versionStartIncluding": "1.19.0-rc.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.20.5",
                      "versionStartIncluding": "1.20.0-rc.0",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "finder",
              "value": "David Vieira Kurz"
            },
            {
              "lang": "en",
              "type": "finder",
              "value": "Peter Ullrich"
            },
            {
              "lang": "en",
              "type": "reporter",
              "value": "David Vieira Kurz"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Jos\u00e9 Valim"
            },
            {
              "lang": "en",
              "type": "coordinator",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "dateAssigned": "2026-10-25T12:21:17.000Z",
          "datePublic": "2026-10-30T08:57:17.000Z",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eImproper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s \u003ccode\u003eJSON\u003c/code\u003e module allows an unauthenticated attacker who supplies a JSON document to cause a denial of service through CPU and memory exhaustion.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eJSON.decode/1\u003c/code\u003e and \u003ccode\u003eJSON.decode!/1\u003c/code\u003e convert each integer literal with \u003ccode\u003eerlang:binary_to_integer/1\u003c/code\u003e and do not bound its length. A literal of one million digits decodes successfully but costs 100 to 300 times as much CPU as a string of the same length, so one request body of a few megabytes holds a scheduler for about a second. A literal of more than about 1.29 million digits raises \u003ccode\u003eSystemLimitError\u003c/code\u003e. Applications generated by \u003ccode\u003ephx.new\u003c/code\u003e decode request bodies with \u003ccode\u003eJSON\u003c/code\u003e in \u003ccode\u003ePlug.Parsers\u003c/code\u003e, before routing and authentication.\u003c/p\u003e\n\u003cp\u003eThis issue affects elixir: from 1.18.0-rc.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s `JSON` module allows an unauthenticated attacker who supplies a JSON document to cause a denial of service through CPU and memory exhaustion.\n\n`JSON.decode/1` and `JSON.decode!/1` convert each integer literal with `erlang:binary_to_integer/1` and do not bound its length. A literal of one million digits decodes successfully but costs 100 to 300 times as much CPU as a string of the same length, so one request body of a few megabytes holds a scheduler for about a second. A literal of more than about 1.29 million digits raises `SystemLimitError`. Applications generated by `phx.new` decode request bodies with `JSON` in `Plug.Parsers`, before routing and authentication.\n\nThis issue affects elixir: from 1.18.0-rc.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5."
                }
              ],
              "value": "Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s JSON module allows an unauthenticated attacker who supplies a JSON document to cause a denial of service through CPU and memory exhaustion.\n\nJSON.decode/1 and JSON.decode!/1 convert each integer literal with erlang:binary_to_integer/1 and do not bound its length. A literal of one million digits decodes successfully but costs 100 to 300 times as much CPU as a string of the same length, so one request body of a few megabytes holds a scheduler for about a second. A literal of more than about 1.29 million digits raises SystemLimitError. Applications generated by phx.new decode request bodies with JSON in Plug.Parsers, before routing and authentication.\n\nThis issue affects elixir: from 1.18.0-rc.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-130",
              "descriptions": [
                {
                  "lang": "en",
                  "supportingMedia": [
                    {
                      "base64": false,
                      "type": "text/html",
                      "value": "\u003cp\u003eAn unauthenticated client that sends a JSON body with integer literals of about a million digits makes the server spend about a second of CPU per request and allocate large bignums. Sustained requests exhaust CPU on applications that decode untrusted JSON with \u003ccode\u003eJSON\u003c/code\u003e, including new Phoenix applications by default.\u003c/p\u003e"
                    },
                    {
                      "base64": false,
                      "type": "text/markdown",
                      "value": "An unauthenticated client that sends a JSON body with integer literals of about a million digits makes the server spend about a second of CPU per request and allocate large bignums. Sustained requests exhaust CPU on applications that decode untrusted JSON with `JSON`, including new Phoenix applications by default."
                    }
                  ],
                  "value": "An unauthenticated client that sends a JSON body with integer literals of about a million digits makes the server spend about a second of CPU per request and allocate large bignums. Sustained requests exhaust CPU on applications that decode untrusted JSON with JSON, including new Phoenix applications by default."
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "NONE",
                "attackVector": "NETWORK",
                "baseScore": 6.9,
                "baseSeverity": "MEDIUM",
                "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:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
                "version": "4.0",
                "vulnAvailabilityImpact": "LOW",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "GENERAL"
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-1284",
                  "description": "CWE-1284 Improper Validation of Specified Quantity in Input",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-10-30T09:01:54.926Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "name": "Unbounded integer parsing in JSON.decode/1 enables CPU and memory exhaustion denial of service in Elixir",
              "tags": [
                "vendor-advisory",
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-rp5q-xqhq-926h"
            },
            {
              "name": "EEF CNA record for CVE-2026-115274",
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-115274.html"
            },
            {
              "name": "OSV record EEF-CVE-2026-115274",
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-115274"
            },
            {
              "name": "Introducing commit 793c114 in elixir-lang/elixir",
              "tags": [
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/793c1147210388587defb649f25f293ffbfa6b1b"
            },
            {
              "name": "Fix commit c81f122 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/c81f12265919c34a0d5134f1905a4eb4edaea1c2"
            },
            {
              "name": "Fix commit 7154e87 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/7154e8781da9fd016a973ea01776a011c8b5726a"
            },
            {
              "name": "Fix commit cd158ef in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/cd158efacdc10b30232864251747dff9b8387dfe"
            },
            {
              "name": "Fix commit ee5cc23 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/ee5cc23f8928aa5a0f25bb7f28e42ecfc7b7d396"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "Unbounded integer parsing in JSON.decode/1 enables CPU and memory exhaustion denial of service in Elixir",
          "workarounds": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eDecode untrusted JSON with an \u003ccode\u003einteger\u003c/code\u003e decoder that rejects long digit runs before conversion:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-elixir\"\u003edefmodule SafeJSON do\n  def decode!(binary) do\n    case JSON.decode(binary, :ok, integer: \u0026amp;safe_integer/1) do\n      {decoded, :ok, \"\"} -\u0026gt; decoded\n      _ -\u0026gt; raise ArgumentError, \"invalid JSON\"\n    end\n  end\n\n  defp safe_integer(digits) when byte_size(digits) \u0026lt;= 1024, do: String.to_integer(digits)\n  defp safe_integer(_digits), do: raise(ArgumentError, \"integer too long\")\nend\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIn Phoenix, pass \u003ccode\u003eSafeJSON\u003c/code\u003e to \u003ccode\u003ePlug.Parsers\u003c/code\u003e as \u003ccode\u003ejson_decoder\u003c/code\u003e instead of \u003ccode\u003ePhoenix.json_library()\u003c/code\u003e.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Decode untrusted JSON with an `integer` decoder that rejects long digit runs before conversion:\n\n```elixir\ndefmodule SafeJSON do\n  def decode!(binary) do\n    case JSON.decode(binary, :ok, integer: \u0026safe_integer/1) do\n      {decoded, :ok, \"\"} -\u003e decoded\n      _ -\u003e raise ArgumentError, \"invalid JSON\"\n    end\n  end\n\n  defp safe_integer(digits) when byte_size(digits) \u003c= 1024, do: String.to_integer(digits)\n  defp safe_integer(_digits), do: raise(ArgumentError, \"integer too long\")\nend\n```\n\nIn Phoenix, pass `SafeJSON` to `Plug.Parsers` as `json_decoder` instead of `Phoenix.json_library()`."
                }
              ],
              "value": "Decode untrusted JSON with an integer decoder that rejects long digit runs before conversion:\n\ndefmodule SafeJSON do\n  def decode!(binary) do\n    case JSON.decode(binary, :ok, integer: \u0026safe_integer/1) do\n      {decoded, :ok, \"\"} -\u003e decoded\n      _ -\u003e raise ArgumentError, \"invalid JSON\"\n    end\n  end\n\n  defp safe_integer(digits) when byte_size(digits) \u003c= 1024, do: String.to_integer(digits)\n  defp safe_integer(_digits), do: raise(ArgumentError, \"integer too long\")\nend\n\nIn Phoenix, pass SafeJSON to Plug.Parsers as json_decoder instead of Phoenix.json_library()."
            }
          ],
          "x_proofOfConcept": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003col\u003e\n\u003cli\u003eBuild \u003ccode\u003ebody = \"[\" \u0026lt;\u0026gt; Enum.map_join(1..7, \",\", fn _ -\u0026gt; String.duplicate(\"9\", 1_000_000) end) \u0026lt;\u0026gt; \"]\"\u003c/code\u003e, a body of about 7 MB.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eJSON.decode(body)\u003c/code\u003e returns \u003ccode\u003e{:ok, ...}\u003c/code\u003e after about one second of CPU.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eJSON.decode(String.duplicate(\"9\", 1_000_000))\u003c/code\u003e takes 100 to 300 times as long as \u003ccode\u003eJSON.decode(\"\\\"\" \u0026lt;\u0026gt; String.duplicate(\"9\", 1_000_000) \u0026lt;\u0026gt; \"\\\"\")\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eJSON.decode(String.duplicate(\"9\", 1_400_000))\u003c/code\u003e raises \u003ccode\u003eSystemLimitError\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "1. Build `body = \"[\" \u003c\u003e Enum.map_join(1..7, \",\", fn _ -\u003e String.duplicate(\"9\", 1_000_000) end) \u003c\u003e \"]\"`, a body of about 7 MB.\n2. `JSON.decode(body)` returns `{:ok, ...}` after about one second of CPU.\n3. `JSON.decode(String.duplicate(\"9\", 1_000_000))` takes 100 to 300 times as long as `JSON.decode(\"\\\"\" \u003c\u003e String.duplicate(\"9\", 1_000_000) \u003c\u003e \"\\\"\")`.\n4. `JSON.decode(String.duplicate(\"9\", 1_400_000))` raises `SystemLimitError`."
                }
              ],
              "value": "* Build body = \"[\" \u003c\u003e Enum.map_join(1..7, \",\", fn _ -\u003e String.duplicate(\"9\", 1_000_000) end) \u003c\u003e \"]\", a body of about 7 MB.\n* JSON.decode(body) returns {:ok, ...} after about one second of CPU.\n* JSON.decode(String.duplicate(\"9\", 1_000_000)) takes 100 to 300 times as long as JSON.decode(\"\\\"\" \u003c\u003e String.duplicate(\"9\", 1_000_000) \u003c\u003e \"\\\"\").\n* JSON.decode(String.duplicate(\"9\", 1_400_000)) raises SystemLimitError."
            }
          ],
          "x_technicalAnalysis": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003e\u003cstrong\u003e1. Default integer decoder\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eJSON.decode/1\u003c/code\u003e and \u003ccode\u003eJSON.decode!/1\u003c/code\u003e call \u003ccode\u003eJSON.decode/3\u003c/code\u003e without an \u003ccode\u003einteger\u003c/code\u003e decoder. Up to the removal of Erlang/OTP 26 support, \u003ccode\u003eJSON.decode/3\u003c/code\u003e used the vendored \u003ccode\u003eelixir_json\u003c/code\u003e module, whose default is \u003ccode\u003efun erlang:binary_to_integer/1\u003c/code\u003e. It now calls OTP\u0027s \u003ccode\u003e:json.decode/3\u003c/code\u003e, which has the same default. The \u003ccode\u003eJSON.decode/3\u003c/code\u003e documentation names \u003ccode\u003e\u0026amp;String.to_integer/1\u003c/code\u003e as the default, which converts the same way.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Unbounded conversion\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe decoder scans the digits of an integer literal and passes the whole run to the conversion. Nothing limits the run\u0027s length, so the cost grows faster than linearly with the number of digits. At one million digits an integer literal costs 100 to 300 times as much CPU as a string literal of the same length. Above about 1.29 million digits the conversion raises \u003ccode\u003eSystemLimitError\u003c/code\u003e, which \u003ccode\u003eJSON.decode/3\u003c/code\u003e does not catch.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Reach\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ephx.new\u003c/code\u003e sets \u003ccode\u003econfig :phoenix, :json_library, JSON\u003c/code\u003e, and the generated endpoint passes \u003ccode\u003ePhoenix.json_library()\u003c/code\u003e to \u003ccode\u003ePlug.Parsers\u003c/code\u003e as \u003ccode\u003ejson_decoder\u003c/code\u003e. \u003ccode\u003ePlug.Parsers\u003c/code\u003e runs before routing and authentication and accepts bodies up to 8 MB by default. It rescues the \u003ccode\u003eSystemLimitError\u003c/code\u003e into a 400 response after the CPU cost is spent. Jason, the previous default, rejects integer literals longer than its \u003ccode\u003edecoding_integer_digit_limit\u003c/code\u003e (1024 by default) before conversion.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "**1. Default integer decoder**\n\n`JSON.decode/1` and `JSON.decode!/1` call `JSON.decode/3` without an `integer` decoder. Up to the removal of Erlang/OTP 26 support, `JSON.decode/3` used the vendored `elixir_json` module, whose default is `fun erlang:binary_to_integer/1`. It now calls OTP\u0027s `:json.decode/3`, which has the same default. The `JSON.decode/3` documentation names `\u0026String.to_integer/1` as the default, which converts the same way.\n\n**2. Unbounded conversion**\n\nThe decoder scans the digits of an integer literal and passes the whole run to the conversion. Nothing limits the run\u0027s length, so the cost grows faster than linearly with the number of digits. At one million digits an integer literal costs 100 to 300 times as much CPU as a string literal of the same length. Above about 1.29 million digits the conversion raises `SystemLimitError`, which `JSON.decode/3` does not catch.\n\n**3. Reach**\n\n`phx.new` sets `config :phoenix, :json_library, JSON`, and the generated endpoint passes `Phoenix.json_library()` to `Plug.Parsers` as `json_decoder`. `Plug.Parsers` runs before routing and authentication and accepts bodies up to 8 MB by default. It rescues the `SystemLimitError` into a 400 response after the CPU cost is spent. Jason, the previous default, rejects integer literals longer than its `decoding_integer_digit_limit` (1024 by default) before conversion."
                }
              ],
              "value": "1. Default integer decoder\n\nJSON.decode/1 and JSON.decode!/1 call JSON.decode/3 without an integer decoder. Up to the removal of Erlang/OTP 26 support, JSON.decode/3 used the vendored elixir_json module, whose default is fun erlang:binary_to_integer/1. It now calls OTP\u0027s :json.decode/3, which has the same default. The JSON.decode/3 documentation names \u0026String.to_integer/1 as the default, which converts the same way.\n\n2. Unbounded conversion\n\nThe decoder scans the digits of an integer literal and passes the whole run to the conversion. Nothing limits the run\u0027s length, so the cost grows faster than linearly with the number of digits. At one million digits an integer literal costs 100 to 300 times as much CPU as a string literal of the same length. Above about 1.29 million digits the conversion raises SystemLimitError, which JSON.decode/3 does not catch.\n\n3. Reach\n\nphx.new sets config :phoenix, :json_library, JSON, and the generated endpoint passes Phoenix.json_library() to Plug.Parsers as json_decoder. Plug.Parsers runs before routing and authentication and accepts bodies up to 8 MB by default. It rescues the SystemLimitError into a 400 response after the CPU cost is spent. Jason, the previous default, rejects integer literals longer than its decoding_integer_digit_limit (1024 by default) before conversion."
            }
          ]
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-115274",
        "datePublished": "2026-10-30T08:57:16.847Z",
        "dateReserved": "2026-10-23T12:45:01.103Z",
        "dateUpdated": "2026-10-30T16:51:45.396Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-109821 (GCVE-0-2026-109821)

    Vulnerability from nvd – Published: 2026-10-30 08:54 – Updated: 2026-10-30 16:52
    VLAI
    Title
    Unbounded port parsing in URI.parse/1 enables CPU exhaustion denial of service in Elixir
    Summary
    Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library's URI module allows an attacker who supplies a URL to cause a denial of service through CPU exhaustion. URI.parse/1 converts the port component with String.to_integer/1 in split_authority/1 (lib/elixir/lib/uri.ex) without bounding its length, so a port of up to 1,262,592 decimal digits produces a bignum of that size and a longer one raises SystemLimitError. Rendering the parsed URI back to text (URI.to_string/1, inspect/1, logging, error formatting) converts the bignum with :erlang.integer_to_binary/1, which is quadratic on Erlang/OTP 27, 28 and 29.0: a 400,000-digit port renders in about 2.6 seconds and a 1 MB port in about 18 seconds. Ports above 65535 are also accepted. This issue affects elixir: from 0.5.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5.
    SSVC
    Exploitation: poc Automatable: no Technical Impact: partial
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-10-30 16:52 UTC
    CWE
    • CWE-1284 - Improper Validation of Specified Quantity in Input
    • CWE-400 - Uncontrolled Resource Consumption
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 97/100 over 352 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-lang elixir Affected: 0.5.0 , < 1.18.6 (semver)
    Affected: 1.19.0-rc.0 , < 1.19.7 (semver)
    Affected: 1.20.0-rc.0 , < 1.20.5 (semver)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-lang elixir Affected: 39801821eef2d06524e5ebf5a0e4b3c380ad23ae , < * (git)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Date Public
    2026-10-30 08:54
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-109821",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "no"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-10-30T16:52:15.736711Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-10-30T16:52:43.052Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-327h-97cv-59xg"
              }
            ],
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.URI\u0027",
                "\u0027Elixir.String.Chars.URI\u0027"
              ],
              "packageName": "elixir",
              "packageURL": "pkg:otp/elixir?repository_url=https:%2F%2Fgithub.com%2Felixir-lang%2Felixir\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Felixir-lang%2Felixir.git",
              "product": "elixir",
              "programFiles": [
                "lib/uri.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.URI\u0027:parse/1"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:split_authority/1"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:merge/2"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:to_string/1"
                },
                {
                  "name": "\u0027Elixir.String.Chars.URI\u0027:to_string/1"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "lessThan": "1.18.6",
                  "status": "affected",
                  "version": "0.5.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.19.7",
                  "status": "affected",
                  "version": "1.19.0-rc.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.20.5",
                  "status": "affected",
                  "version": "1.20.0-rc.0",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.URI\u0027",
                "\u0027Elixir.String.Chars.URI\u0027"
              ],
              "packageName": "elixir-lang/elixir",
              "packageURL": "pkg:github/elixir-lang/elixir",
              "product": "elixir",
              "programFiles": [
                "lib/elixir/lib/uri.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.URI\u0027:parse/1"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:split_authority/1"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:merge/2"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:to_string/1"
                },
                {
                  "name": "\u0027Elixir.String.Chars.URI\u0027:to_string/1"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "changes": [
                    {
                      "at": "9a39d64a4473d02681c486bc70681c972479d32b",
                      "status": "unaffected"
                    },
                    {
                      "at": "b70f33a591b6ab8eb0fa4a7e00bac1c2ed4e0d58",
                      "status": "unaffected"
                    },
                    {
                      "at": "07d9abc3f3ac4c8d2ba8384bd102b6d6205b08b7",
                      "status": "unaffected"
                    },
                    {
                      "at": "d879b39d778fec2f475689573d5672720c4f2a21",
                      "status": "unaffected"
                    }
                  ],
                  "lessThan": "*",
                  "status": "affected",
                  "version": "39801821eef2d06524e5ebf5a0e4b3c380ad23ae",
                  "versionType": "git"
                }
              ]
            }
          ],
          "configurations": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eThe application parses an attacker-supplied URL with \u003ccode\u003eURI.parse/1\u003c/code\u003e, for example a URL field in a form or JSON request body, and then renders the result or a value containing it. The multi-second render cost requires Erlang/OTP 27, 28 or 29.0; on OTP 29.1 and later rendering a 1 MB port takes under a second. URLs in HTTP headers and the request line are bounded by the server\u0027s size limits.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "The application parses an attacker-supplied URL with `URI.parse/1`, for example a URL field in a form or JSON request body, and then renders the result or a value containing it. The multi-second render cost requires Erlang/OTP 27, 28 or 29.0; on OTP 29.1 and later rendering a 1 MB port takes under a second. URLs in HTTP headers and the request line are bounded by the server\u0027s size limits."
                }
              ],
              "value": "The application parses an attacker-supplied URL with URI.parse/1, for example a URL field in a form or JSON request body, and then renders the result or a value containing it. The multi-second render cost requires Erlang/OTP 27, 28 or 29.0; on OTP 29.1 and later rendering a 1 MB port takes under a second. URLs in HTTP headers and the request line are bounded by the server\u0027s size limits."
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.18.6",
                      "versionStartIncluding": "0.5.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.19.7",
                      "versionStartIncluding": "1.19.0-rc.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.20.5",
                      "versionStartIncluding": "1.20.0-rc.0",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "finder",
              "value": "Eric Meadows-J\u00f6nsson"
            },
            {
              "lang": "en",
              "type": "reporter",
              "value": "Eric Meadows-J\u00f6nsson"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Jos\u00e9 Valim"
            },
            {
              "lang": "en",
              "type": "coordinator",
              "value": "Eric Meadows-J\u00f6nsson"
            },
            {
              "lang": "en",
              "type": "analyst",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "dateAssigned": "2026-10-25T10:39:55.000Z",
          "datePublic": "2026-10-30T08:54:08.000Z",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eImproper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s \u003ccode\u003eURI\u003c/code\u003e module allows an attacker who supplies a URL to cause a denial of service through CPU exhaustion.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eURI.parse/1\u003c/code\u003e converts the port component with \u003ccode\u003eString.to_integer/1\u003c/code\u003e in \u003ccode\u003esplit_authority/1\u003c/code\u003e (\u003ccode\u003elib/elixir/lib/uri.ex\u003c/code\u003e) without bounding its length, so a port of up to 1,262,592 decimal digits produces a bignum of that size and a longer one raises \u003ccode\u003eSystemLimitError\u003c/code\u003e. Rendering the parsed URI back to text (\u003ccode\u003eURI.to_string/1\u003c/code\u003e, \u003ccode\u003einspect/1\u003c/code\u003e, logging, error formatting) converts the bignum with \u003ccode\u003e:erlang.integer_to_binary/1\u003c/code\u003e, which is quadratic on Erlang/OTP 27, 28 and 29.0: a 400,000-digit port renders in about 2.6 seconds and a 1 MB port in about 18 seconds. Ports above 65535 are also accepted.\u003c/p\u003e\n\u003cp\u003eThis issue affects elixir: from 0.5.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s `URI` module allows an attacker who supplies a URL to cause a denial of service through CPU exhaustion.\n\n`URI.parse/1` converts the port component with `String.to_integer/1` in `split_authority/1` (`lib/elixir/lib/uri.ex`) without bounding its length, so a port of up to 1,262,592 decimal digits produces a bignum of that size and a longer one raises `SystemLimitError`. Rendering the parsed URI back to text (`URI.to_string/1`, `inspect/1`, logging, error formatting) converts the bignum with `:erlang.integer_to_binary/1`, which is quadratic on Erlang/OTP 27, 28 and 29.0: a 400,000-digit port renders in about 2.6 seconds and a 1 MB port in about 18 seconds. Ports above 65535 are also accepted.\n\nThis issue affects elixir: from 0.5.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5."
                }
              ],
              "value": "Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s URI module allows an attacker who supplies a URL to cause a denial of service through CPU exhaustion.\n\nURI.parse/1 converts the port component with String.to_integer/1 in split_authority/1 (lib/elixir/lib/uri.ex) without bounding its length, so a port of up to 1,262,592 decimal digits produces a bignum of that size and a longer one raises SystemLimitError. Rendering the parsed URI back to text (URI.to_string/1, inspect/1, logging, error formatting) converts the bignum with :erlang.integer_to_binary/1, which is quadratic on Erlang/OTP 27, 28 and 29.0: a 400,000-digit port renders in about 2.6 seconds and a 1 MB port in about 18 seconds. Ports above 65535 are also accepted.\n\nThis issue affects elixir: from 0.5.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-130",
              "descriptions": [
                {
                  "lang": "en",
                  "supportingMedia": [
                    {
                      "base64": false,
                      "type": "text/html",
                      "value": "\u003cp\u003eAn unauthenticated client that submits a URL with a port of hundreds of thousands of digits makes the server build a bignum of that size, and each later rendering of the parsed URI costs seconds of CPU on Erlang/OTP 27, 28 and 29.0. Sustained requests exhaust CPU on applications that parse and log, store or echo user-supplied URLs.\u003c/p\u003e"
                    },
                    {
                      "base64": false,
                      "type": "text/markdown",
                      "value": "An unauthenticated client that submits a URL with a port of hundreds of thousands of digits makes the server build a bignum of that size, and each later rendering of the parsed URI costs seconds of CPU on Erlang/OTP 27, 28 and 29.0. Sustained requests exhaust CPU on applications that parse and log, store or echo user-supplied URLs."
                    }
                  ],
                  "value": "An unauthenticated client that submits a URL with a port of hundreds of thousands of digits makes the server build a bignum of that size, and each later rendering of the parsed URI costs seconds of CPU on Erlang/OTP 27, 28 and 29.0. Sustained requests exhaust CPU on applications that parse and log, store or echo user-supplied URLs."
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "NETWORK",
                "baseScore": 6.3,
                "baseSeverity": "MEDIUM",
                "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:L/SC:N/SI:N/SA:N",
                "version": "4.0",
                "vulnAvailabilityImpact": "LOW",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "GENERAL"
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-1284",
                  "description": "CWE-1284 Improper Validation of Specified Quantity in Input",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            },
            {
              "descriptions": [
                {
                  "cweId": "CWE-400",
                  "description": "CWE-400 Uncontrolled Resource Consumption",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-10-30T09:02:51.828Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "name": "Unbounded port parsing in URI.parse/1 enables CPU exhaustion denial of service",
              "tags": [
                "vendor-advisory",
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-327h-97cv-59xg"
            },
            {
              "name": "EEF CNA record for CVE-2026-109821",
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-109821.html"
            },
            {
              "name": "OSV record EEF-CVE-2026-109821",
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-109821"
            },
            {
              "name": "Erlang/OTP advisory for the same issue in uri_string",
              "tags": [
                "related"
              ],
              "url": "https://github.com/erlang/otp/security/advisories/GHSA-8qw4-2chm-mvj2"
            },
            {
              "name": "Introducing commit 3980182 in elixir-lang/elixir",
              "tags": [
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/39801821eef2d06524e5ebf5a0e4b3c380ad23ae"
            },
            {
              "name": "Fix commit 9a39d64 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/9a39d64a4473d02681c486bc70681c972479d32b"
            },
            {
              "name": "Fix commit b70f33a in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/b70f33a591b6ab8eb0fa4a7e00bac1c2ed4e0d58"
            },
            {
              "name": "Fix commit 07d9abc in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/07d9abc3f3ac4c8d2ba8384bd102b6d6205b08b7"
            },
            {
              "name": "Fix commit d879b39 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/d879b39d778fec2f475689573d5672720c4f2a21"
            }
          ],
          "source": {
            "discovery": "INTERNAL"
          },
          "title": "Unbounded port parsing in URI.parse/1 enables CPU exhaustion denial of service in Elixir",
          "workarounds": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eReject long URLs before parsing them, for example anything above a few kilobytes, and reject a parsed \u003ccode\u003eport\u003c/code\u003e above 65535 before rendering or using the \u003ccode\u003e%URI{}\u003c/code\u003e. \u003ccode\u003eURI.new/1\u003c/code\u003e is not affected on Erlang/OTP 27.3.4.17, 28.5.0.6, 29.0.6 and later, where \u003ccode\u003e:uri_string\u003c/code\u003e limits the port to 5 digits.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Reject long URLs before parsing them, for example anything above a few kilobytes, and reject a parsed `port` above 65535 before rendering or using the `%URI{}`. `URI.new/1` is not affected on Erlang/OTP 27.3.4.17, 28.5.0.6, 29.0.6 and later, where `:uri_string` limits the port to 5 digits."
                }
              ],
              "value": "Reject long URLs before parsing them, for example anything above a few kilobytes, and reject a parsed port above 65535 before rendering or using the %URI{}. URI.new/1 is not affected on Erlang/OTP 27.3.4.17, 28.5.0.6, 29.0.6 and later, where :uri_string limits the port to 5 digits."
            }
          ],
          "x_proofOfConcept": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003col\u003e\n\u003cli\u003eBuild \u003ccode\u003eurl = \"http://example.com:\" \u0026lt;\u0026gt; String.duplicate(\"9\", 400_000)\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eURI.parse(url)\u003c/code\u003e returns in about 30 ms.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eURI.to_string(URI.parse(url))\u003c/code\u003e takes 2,613 ms on OTP 27.3.4.18, 2,604 ms on OTP 28.5 and 108 ms on OTP 29.1.1. With 200,000 digits it takes 654 ms on OTP 27 and 28, so the cost grows 4x per doubling.\u003c/li\u003e\n\u003cli\u003eThe same digits placed in the path instead of the port parse and render in 1 ms or less.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eURI.parse(\"http://example.com:65536\").port\u003c/code\u003e returns \u003ccode\u003e65536\u003c/code\u003e, and a port of 1,262,593 digits raises \u003ccode\u003eSystemLimitError\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "1. Build `url = \"http://example.com:\" \u003c\u003e String.duplicate(\"9\", 400_000)`.\n2. `URI.parse(url)` returns in about 30 ms.\n3. `URI.to_string(URI.parse(url))` takes 2,613 ms on OTP 27.3.4.18, 2,604 ms on OTP 28.5 and 108 ms on OTP 29.1.1. With 200,000 digits it takes 654 ms on OTP 27 and 28, so the cost grows 4x per doubling.\n4. The same digits placed in the path instead of the port parse and render in 1 ms or less.\n5. `URI.parse(\"http://example.com:65536\").port` returns `65536`, and a port of 1,262,593 digits raises `SystemLimitError`."
                }
              ],
              "value": "* Build url = \"http://example.com:\" \u003c\u003e String.duplicate(\"9\", 400_000).\n* URI.parse(url) returns in about 30 ms.\n* URI.to_string(URI.parse(url)) takes 2,613 ms on OTP 27.3.4.18, 2,604 ms on OTP 28.5 and 108 ms on OTP 29.1.1. With 200,000 digits it takes 654 ms on OTP 27 and 28, so the cost grows 4x per doubling.\n* The same digits placed in the path instead of the port parse and render in 1 ms or less.\n* URI.parse(\"http://example.com:65536\").port returns 65536, and a port of 1,262,593 digits raises SystemLimitError."
            }
          ],
          "x_technicalAnalysis": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003e\u003cstrong\u003e1. Unbounded conversion\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003esplit_authority/1\u003c/code\u003e matches the port with the regex \u003ccode\u003e(:(\\d*))?\u003c/code\u003e and converts it with \u003ccode\u003eString.to_integer/1\u003c/code\u003e. Nothing bounds the digit run, so the VM builds an integer of up to 1,262,592 decimal digits (about 30 ms to parse 400,000 digits) and raises \u003ccode\u003eSystemLimitError\u003c/code\u003e above that, which escapes \u003ccode\u003eURI.parse/1\u003c/code\u003e instead of producing a \u003ccode\u003e%URI{}\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Render cost\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eURI.to_string/1\u003c/code\u003e renders the port with \u003ccode\u003eInteger.to_string/1\u003c/code\u003e, and \u003ccode\u003einspect/1\u003c/code\u003e renders it the same way, both through \u003ccode\u003e:erlang.integer_to_binary/1\u003c/code\u003e. That conversion is quadratic on OTP 27, 28 and 29.0 and sub-quadratic from OTP 29.1 (\u003ca href=\"https://github.com/erlang/otp/pull/11074\" rel=\"noopener noreferrer\"\u003ehttps://github.com/erlang/otp/pull/11074\u003c/a\u003e). Per GHSA-8qw4-2chm-mvj2 it occupies a dirty CPU scheduler for its full duration.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Comparison with \u003ccode\u003eURI.new/1\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eErlang/OTP limited the port to 5 bytes in \u003ccode\u003e:uri_string.parse/1\u003c/code\u003e as part of GHSA-8qw4-2chm-mvj2. \u003ccode\u003eURI.new/1\u003c/code\u003e and \u003ccode\u003eURI.new!/1\u003c/code\u003e use \u003ccode\u003e:uri_string\u003c/code\u003e and get that limit on patched OTP versions. \u003ccode\u003eURI.parse/1\u003c/code\u003e does its own parsing and does not.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4. Reach\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eHeader-sourced URLs are bounded by HTTP server limits. Form and JSON body fields are not: Plug\u0027s default JSON body limit is 8 MB. One observed library path: a user-supplied URL passed to Req inside a Bandit request exits with the port in its arguments, and Bandit\u0027s error logging formats it, which took 2.8 seconds at 400,000 digits on OTP 28.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "**1. Unbounded conversion**\n\n`split_authority/1` matches the port with the regex `(:(\\d*))?` and converts it with `String.to_integer/1`. Nothing bounds the digit run, so the VM builds an integer of up to 1,262,592 decimal digits (about 30 ms to parse 400,000 digits) and raises `SystemLimitError` above that, which escapes `URI.parse/1` instead of producing a `%URI{}`.\n\n**2. Render cost**\n\n`URI.to_string/1` renders the port with `Integer.to_string/1`, and `inspect/1` renders it the same way, both through `:erlang.integer_to_binary/1`. That conversion is quadratic on OTP 27, 28 and 29.0 and sub-quadratic from OTP 29.1 (https://github.com/erlang/otp/pull/11074). Per GHSA-8qw4-2chm-mvj2 it occupies a dirty CPU scheduler for its full duration.\n\n**3. Comparison with `URI.new/1`**\n\nErlang/OTP limited the port to 5 bytes in `:uri_string.parse/1` as part of GHSA-8qw4-2chm-mvj2. `URI.new/1` and `URI.new!/1` use `:uri_string` and get that limit on patched OTP versions. `URI.parse/1` does its own parsing and does not.\n\n**4. Reach**\n\nHeader-sourced URLs are bounded by HTTP server limits. Form and JSON body fields are not: Plug\u0027s default JSON body limit is 8 MB. One observed library path: a user-supplied URL passed to Req inside a Bandit request exits with the port in its arguments, and Bandit\u0027s error logging formats it, which took 2.8 seconds at 400,000 digits on OTP 28."
                }
              ],
              "value": "1. Unbounded conversion\n\nsplit_authority/1 matches the port with the regex (:(\\d*))? and converts it with String.to_integer/1. Nothing bounds the digit run, so the VM builds an integer of up to 1,262,592 decimal digits (about 30 ms to parse 400,000 digits) and raises SystemLimitError above that, which escapes URI.parse/1 instead of producing a %URI{}.\n\n2. Render cost\n\nURI.to_string/1 renders the port with Integer.to_string/1, and inspect/1 renders it the same way, both through :erlang.integer_to_binary/1. That conversion is quadratic on OTP 27, 28 and 29.0 and sub-quadratic from OTP 29.1 (https://github.com/erlang/otp/pull/11074). Per GHSA-8qw4-2chm-mvj2 it occupies a dirty CPU scheduler for its full duration.\n\n3. Comparison with URI.new/1\n\nErlang/OTP limited the port to 5 bytes in :uri_string.parse/1 as part of GHSA-8qw4-2chm-mvj2. URI.new/1 and URI.new!/1 use :uri_string and get that limit on patched OTP versions. URI.parse/1 does its own parsing and does not.\n\n4. Reach\n\nHeader-sourced URLs are bounded by HTTP server limits. Form and JSON body fields are not: Plug\u0027s default JSON body limit is 8 MB. One observed library path: a user-supplied URL passed to Req inside a Bandit request exits with the port in its arguments, and Bandit\u0027s error logging formats it, which took 2.8 seconds at 400,000 digits on OTP 28."
            }
          ]
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-109821",
        "datePublished": "2026-10-30T08:54:07.752Z",
        "dateReserved": "2026-10-23T14:00:01.560Z",
        "dateUpdated": "2026-10-30T16:52:43.052Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-75758 (GCVE-0-2026-75758)

    Vulnerability from nvd – Published: 2026-08-28 11:28 – Updated: 2026-08-28 18:29
    VLAI
    Title
    Unbounded recursion between Inspect.List charlist rendering and List.to_string/1 error path in Elixir
    Summary
    Uncontrolled Recursion vulnerability in the Elixir standard library allows an attacker who controls a list passed to inspect/1, List.to_string/1, or List.to_charlist/1 to exhaust a BEAM node's memory. Inspect.List's charlist branch in lib/elixir/lib/inspect.ex classifies a list as a charlist using List.ascii_printable?/2, which examines only the first :printable_limit (4096 by default) elements, and then calls IO.chardata_to_string/1 on the whole term. A list whose printable prefix exceeds that limit but which contains a later element that is not a code point (an atom, an out-of-range integer, or an improper tail) is therefore mis-classified, and the conversion raises ArgumentError. That conversion runs inside List.to_string/1, whose rescue clause builds its message by interpolating inspect(list), which re-enters the same branch and raises again. The nested inspection is an argument to raise, so the recursion is not in tail position and every level is retained: the process stack grows monotonically while each cycle re-walks the list, until the process is killed by max_heap_size or, by default, the node runs out of memory. List.to_charlist/1 has the same rescue shape. Below the printable limit the inner inspect/1 sees the invalid element within its counter and renders the list in ordinary bracket form, so a single ArgumentError is raised and no recursion occurs. This issue affects elixir: from 1.15.0-rc.0 before 1.18.5, from 1.19.0-rc.0 before 1.19.6, and from 1.20.0-rc.0 before 1.20.4.
    SSVC
    Exploitation: poc Automatable: no Technical Impact: partial
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-08-28 18:28 UTC
    CWE
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 97/100 over 352 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-lang elixir Affected: 1.15.0-rc.0 , < 1.18.5 (semver)
    Affected: 1.19.0-rc.0 , < 1.19.6 (semver)
    Affected: 1.20.0-rc.0 , < 1.20.4 (semver)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-lang elixir Affected: 9718f2b90ce086ff8614d5782f4ec528495c98dd , < * (git)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-75758",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "no"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-08-28T18:28:19.564474Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-08-28T18:29:23.745Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-jf5q-v438-665c"
              }
            ],
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Inspect.List\u0027",
                "\u0027Elixir.List\u0027",
                "\u0027Elixir.IO\u0027"
              ],
              "packageName": "elixir",
              "packageURL": "pkg:otp/elixir?repository_url=https:%2F%2Fgithub.com%2Felixir-lang%2Felixir\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Felixir-lang%2Felixir.git",
              "product": "elixir",
              "programFiles": [
                "lib/inspect.ex",
                "lib/list.ex",
                "lib/io.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Inspect.List\u0027:inspect/2"
                },
                {
                  "name": "\u0027Elixir.List\u0027:to_string/1"
                },
                {
                  "name": "\u0027Elixir.List\u0027:to_charlist/1"
                },
                {
                  "name": "\u0027Elixir.List\u0027:ascii_printable?/2"
                },
                {
                  "name": "\u0027Elixir.IO\u0027:chardata_to_string/1"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "lessThan": "1.18.5",
                  "status": "affected",
                  "version": "1.15.0-rc.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.19.6",
                  "status": "affected",
                  "version": "1.19.0-rc.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.20.4",
                  "status": "affected",
                  "version": "1.20.0-rc.0",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Inspect.List\u0027",
                "\u0027Elixir.List\u0027",
                "\u0027Elixir.IO\u0027"
              ],
              "packageName": "elixir-lang/elixir",
              "packageURL": "pkg:github/elixir-lang/elixir",
              "product": "elixir",
              "programFiles": [
                "lib/elixir/lib/inspect.ex",
                "lib/elixir/lib/list.ex",
                "lib/elixir/lib/io.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Inspect.List\u0027:inspect/2"
                },
                {
                  "name": "\u0027Elixir.List\u0027:to_string/1"
                },
                {
                  "name": "\u0027Elixir.List\u0027:to_charlist/1"
                },
                {
                  "name": "\u0027Elixir.List\u0027:ascii_printable?/2"
                },
                {
                  "name": "\u0027Elixir.IO\u0027:chardata_to_string/1"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "changes": [
                    {
                      "at": "1eff1acffd49bdcc0d7f57ca74c1603328eed68a",
                      "status": "unaffected"
                    },
                    {
                      "at": "5230d73968f1b4969d2a2646786fa6c71475f5cc",
                      "status": "unaffected"
                    },
                    {
                      "at": "a983c8c043b1fbf1d95df78a29149222dac2988c",
                      "status": "unaffected"
                    },
                    {
                      "at": "0bba5887577b1e328da825bd018815fdc519685a",
                      "status": "unaffected"
                    }
                  ],
                  "lessThan": "*",
                  "status": "affected",
                  "version": "9718f2b90ce086ff8614d5782f4ec528495c98dd",
                  "versionType": "git"
                }
              ]
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.18.5",
                      "versionStartIncluding": "1.15.0-rc.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.19.6",
                      "versionStartIncluding": "1.19.0-rc.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.20.4",
                      "versionStartIncluding": "1.20.0-rc.0",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "reporter",
              "value": "Thomas Cioppettini / Scalpel Software Inc"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Jos\u00e9 Valim"
            },
            {
              "lang": "en",
              "type": "coordinator",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eUncontrolled Recursion vulnerability in the Elixir standard library allows an attacker who controls a list passed to \u003ccode\u003einspect/1\u003c/code\u003e, \u003ccode\u003eList.to_string/1\u003c/code\u003e, or \u003ccode\u003eList.to_charlist/1\u003c/code\u003e to exhaust a BEAM node\u0027s memory.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eInspect.List\u003c/code\u003e\u0027s charlist branch in \u003ccode\u003elib/elixir/lib/inspect.ex\u003c/code\u003e classifies a list as a charlist using \u003ccode\u003eList.ascii_printable?/2\u003c/code\u003e, which examines only the first \u003ccode\u003e:printable_limit\u003c/code\u003e (4096 by default) elements, and then calls \u003ccode\u003eIO.chardata_to_string/1\u003c/code\u003e on the whole term. A list whose printable prefix exceeds that limit but which contains a later element that is not a code point (an atom, an out-of-range integer, or an improper tail) is therefore mis-classified, and the conversion raises \u003ccode\u003eArgumentError\u003c/code\u003e. That conversion runs inside \u003ccode\u003eList.to_string/1\u003c/code\u003e, whose rescue clause builds its message by interpolating \u003ccode\u003einspect(list)\u003c/code\u003e, which re-enters the same branch and raises again. The nested inspection is an argument to \u003ccode\u003eraise\u003c/code\u003e, so the recursion is not in tail position and every level is retained: the process stack grows monotonically while each cycle re-walks the list, until the process is killed by \u003ccode\u003emax_heap_size\u003c/code\u003e or, by default, the node runs out of memory. \u003ccode\u003eList.to_charlist/1\u003c/code\u003e has the same rescue shape.\u003c/p\u003e\n\u003cp\u003eBelow the printable limit the inner \u003ccode\u003einspect/1\u003c/code\u003e sees the invalid element within its counter and renders the list in ordinary bracket form, so a single \u003ccode\u003eArgumentError\u003c/code\u003e is raised and no recursion occurs.\u003c/p\u003e\n\u003cp\u003eThis issue affects elixir: from 1.15.0-rc.0 before 1.18.5, from 1.19.0-rc.0 before 1.19.6, and from 1.20.0-rc.0 before 1.20.4.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Uncontrolled Recursion vulnerability in the Elixir standard library allows an attacker who controls a list passed to `inspect/1`, `List.to_string/1`, or `List.to_charlist/1` to exhaust a BEAM node\u0027s memory.\n\n`Inspect.List`\u0027s charlist branch in `lib/elixir/lib/inspect.ex` classifies a list as a charlist using `List.ascii_printable?/2`, which examines only the first `:printable_limit` (4096 by default) elements, and then calls `IO.chardata_to_string/1` on the whole term. A list whose printable prefix exceeds that limit but which contains a later element that is not a code point (an atom, an out-of-range integer, or an improper tail) is therefore mis-classified, and the conversion raises `ArgumentError`. That conversion runs inside `List.to_string/1`, whose rescue clause builds its message by interpolating `inspect(list)`, which re-enters the same branch and raises again. The nested inspection is an argument to `raise`, so the recursion is not in tail position and every level is retained: the process stack grows monotonically while each cycle re-walks the list, until the process is killed by `max_heap_size` or, by default, the node runs out of memory. `List.to_charlist/1` has the same rescue shape.\n\nBelow the printable limit the inner `inspect/1` sees the invalid element within its counter and renders the list in ordinary bracket form, so a single `ArgumentError` is raised and no recursion occurs.\n\nThis issue affects elixir: from 1.15.0-rc.0 before 1.18.5, from 1.19.0-rc.0 before 1.19.6, and from 1.20.0-rc.0 before 1.20.4."
                }
              ],
              "value": "Uncontrolled Recursion vulnerability in the Elixir standard library allows an attacker who controls a list passed to inspect/1, List.to_string/1, or List.to_charlist/1 to exhaust a BEAM node\u0027s memory.\n\nInspect.List\u0027s charlist branch in lib/elixir/lib/inspect.ex classifies a list as a charlist using List.ascii_printable?/2, which examines only the first :printable_limit (4096 by default) elements, and then calls IO.chardata_to_string/1 on the whole term. A list whose printable prefix exceeds that limit but which contains a later element that is not a code point (an atom, an out-of-range integer, or an improper tail) is therefore mis-classified, and the conversion raises ArgumentError. That conversion runs inside List.to_string/1, whose rescue clause builds its message by interpolating inspect(list), which re-enters the same branch and raises again. The nested inspection is an argument to raise, so the recursion is not in tail position and every level is retained: the process stack grows monotonically while each cycle re-walks the list, until the process is killed by max_heap_size or, by default, the node runs out of memory. List.to_charlist/1 has the same rescue shape.\n\nBelow the printable limit the inner inspect/1 sees the invalid element within its counter and renders the list in ordinary bracket form, so a single ArgumentError is raised and no recursion occurs.\n\nThis issue affects elixir: from 1.15.0-rc.0 before 1.18.5, from 1.19.0-rc.0 before 1.19.6, and from 1.20.0-rc.0 before 1.20.4."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-130",
              "descriptions": [
                {
                  "lang": "en",
                  "value": "CAPEC-130 Excessive Allocation"
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "LOCAL",
                "baseScore": 5.9,
                "baseSeverity": "MEDIUM",
                "privilegesRequired": "NONE",
                "providerUrgency": "NOT_DEFINED",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "NONE",
                "valueDensity": "NOT_DEFINED",
                "vectorString": "CVSS:4.0/AV:L/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-08-28T11:28:53.505Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "tags": [
                "vendor-advisory",
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-jf5q-v438-665c"
            },
            {
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-75758.html"
            },
            {
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-75758"
            },
            {
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/1eff1acffd49bdcc0d7f57ca74c1603328eed68a"
            },
            {
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/5230d73968f1b4969d2a2646786fa6c71475f5cc"
            },
            {
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/a983c8c043b1fbf1d95df78a29149222dac2988c"
            },
            {
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/0bba5887577b1e328da825bd018815fdc519685a"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "Unbounded recursion between Inspect.List charlist rendering and List.to_string/1 error path in Elixir",
          "workarounds": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eInstall a global inspect function that renders lists as lists rather than inferring charlists, by calling \u003ccode\u003eInspect.Opts.default_inspect_fun/1\u003c/code\u003e with \u003ccode\u003efn term, opts -\u0026gt; Inspect.inspect(term, %{opts | charlists: :as_lists}) end\u003c/code\u003e from the application\u0027s start callback. This keeps the charlist branch from being entered at all, so the mis-inferred conversion never raises and the error path cannot recurse.\u003c/p\u003e\n\u003cp\u003eThe trade-off is global: charlists then render in bracket form, so \u003ccode\u003e~c\"abc\"\u003c/code\u003e inspects as \u003ccode\u003e[97, 98, 99]\u003c/code\u003e everywhere in the application, including in logs and error reports.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Install a global inspect function that renders lists as lists rather than inferring charlists, by calling `Inspect.Opts.default_inspect_fun/1` with `fn term, opts -\u003e Inspect.inspect(term, %{opts | charlists: :as_lists}) end` from the application\u0027s start callback. This keeps the charlist branch from being entered at all, so the mis-inferred conversion never raises and the error path cannot recurse.\n\nThe trade-off is global: charlists then render in bracket form, so `~c\"abc\"` inspects as `[97, 98, 99]` everywhere in the application, including in logs and error reports."
                }
              ],
              "value": "Install a global inspect function that renders lists as lists rather than inferring charlists, by calling Inspect.Opts.default_inspect_fun/1 with fn term, opts -\u003e Inspect.inspect(term, %{opts | charlists: :as_lists}) end from the application\u0027s start callback. This keeps the charlist branch from being entered at all, so the mis-inferred conversion never raises and the error path cannot recurse.\n\nThe trade-off is global: charlists then render in bracket form, so ~c\"abc\" inspects as [97, 98, 99] everywhere in the application, including in logs and error reports."
            }
          ]
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-75758",
        "datePublished": "2026-08-28T11:28:53.505Z",
        "dateReserved": "2026-08-19T09:00:01.955Z",
        "dateUpdated": "2026-08-28T18:29:23.745Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-49762 (GCVE-0-2026-49762)

    Vulnerability from nvd – Published: 2026-06-09 14:04 – Updated: 2026-10-06 08:35
    VLAI
    Title
    Unbounded integer parsing in the Version module enables CPU and memory exhaustion denial of service
    Summary
    Uncontrolled Resource Consumption vulnerability in the Elixir standard library's Version module allows an attacker who controls a version string to cause a denial of service through CPU and memory exhaustion. The version parser converts numeric version components (major, minor, patch and numeric pre-release/build identifiers) to integers without bounding their length. A single large all-digit component therefore forces a super-linear, non-yielding base-10 to arbitrary-precision integer conversion (String.to_integer/1, i.e. :erlang.binary_to_integer/1) that pins a BEAM scheduler, and a larger component raises an uncaught SystemLimitError that crashes the calling process. A single moderately sized string (around one megabyte) is enough; no authentication is required. This is reachable from the public entry points Version.parse/1, Version.parse!/1, Version.match?/3, Version.compare/2, and Version.parse_requirement/1, which applications routinely call on untrusted input such as HTTP parameters, dependency-manifest fields, and package metadata. This vulnerability is associated with program files lib/version.ex and program routines 'Elixir.Version.Parser':parse_digits/2. This issue affects elixir: from 0.9.3 before 1.20.1.
    SSVC
    Exploitation: none Automatable: no Technical Impact: partial
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-06-09 14:48 UTC
    CWE
    • CWE-400 - Uncontrolled Resource Consumption
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 97/100 over 352 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-lang elixir Affected: 0.9.3 , < 1.20.1 (semver)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-lang elixir Affected: 057691722eb8cfe315f5606358954a2b173ff7a3 , < * (git)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Date Public
    2026-06-09 14:04
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-49762",
                    "options": [
                      {
                        "Exploitation": "none"
                      },
                      {
                        "Automatable": "no"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-06-09T14:48:56.343391Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-06-09T14:49:07.338Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Version\u0027",
                "\u0027Elixir.Version.Parser\u0027"
              ],
              "packageName": "elixir",
              "packageURL": "pkg:otp/elixir?repository_url=https:%2F%2Fgithub.com%2Felixir-lang%2Felixir\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Felixir-lang%2Felixir.git",
              "product": "elixir",
              "programFiles": [
                "lib/version.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Version\u0027:parse/1"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:parse!/1"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:match?/3"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:compare/2"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:parse_requirement/1"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:parse_version/2"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:parse_digits/2"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:require_digits/1"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:convert_parts_to_integer/2"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "lessThan": "1.20.1",
                  "status": "affected",
                  "version": "0.9.3",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Version\u0027",
                "\u0027Elixir.Version.Parser\u0027"
              ],
              "packageName": "elixir-lang/elixir",
              "packageURL": "pkg:github/elixir-lang/elixir",
              "product": "elixir",
              "programFiles": [
                "lib/elixir/lib/version.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Version\u0027:parse/1"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:parse!/1"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:match?/3"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:compare/2"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:parse_requirement/1"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:parse_version/2"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:parse_digits/2"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:require_digits/1"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:convert_parts_to_integer/2"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "changes": [
                    {
                      "at": "c64417d72fd5c7d09e963ca3ac5fa2b140978d9e",
                      "status": "unaffected"
                    },
                    {
                      "at": "64e6707233464c3e55a9ca6b44019aed25b0390a",
                      "status": "unaffected"
                    }
                  ],
                  "lessThan": "*",
                  "status": "affected",
                  "version": "057691722eb8cfe315f5606358954a2b173ff7a3",
                  "versionType": "git"
                }
              ]
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.20.1",
                      "versionStartIncluding": "0.9.3",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "finder",
              "value": "Peter Ullrich"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Jos\u00e9 Valim"
            },
            {
              "lang": "en",
              "type": "remediation reviewer",
              "value": "Eric Meadows-J\u00f6nsson"
            },
            {
              "lang": "en",
              "type": "analyst",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "datePublic": "2026-06-09T14:04:07.000Z",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eUncontrolled Resource Consumption vulnerability in the Elixir standard library\u0027s \u003ccode\u003eVersion\u003c/code\u003e module allows an attacker who controls a version string to cause a denial of service through CPU and memory exhaustion.\u003c/p\u003e\n\u003cp\u003eThe version parser converts numeric version components (major, minor, patch and numeric pre-release/build identifiers) to integers without bounding their length. A single large all-digit component therefore forces a super-linear, non-yielding base-10 to arbitrary-precision integer conversion (\u003ccode\u003eString.to_integer/1\u003c/code\u003e, i.e. \u003ccode\u003e:erlang.binary_to_integer/1\u003c/code\u003e) that pins a BEAM scheduler, and a larger component raises an uncaught \u003ccode\u003eSystemLimitError\u003c/code\u003e that crashes the calling process. A single moderately sized string (around one megabyte) is enough; no authentication is required.\u003c/p\u003e\n\u003cp\u003eThis is reachable from the public entry points \u003ccode\u003eVersion.parse/1\u003c/code\u003e, \u003ccode\u003eVersion.parse!/1\u003c/code\u003e, \u003ccode\u003eVersion.match?/3\u003c/code\u003e, \u003ccode\u003eVersion.compare/2\u003c/code\u003e, and \u003ccode\u003eVersion.parse_requirement/1\u003c/code\u003e, which applications routinely call on untrusted input such as HTTP parameters, dependency-manifest fields, and package metadata.\u003c/p\u003e\n\u003cp\u003eThis vulnerability is associated with program files \u003ccode\u003elib/version.ex\u003c/code\u003e and program routines \u003ccode\u003e\u0027Elixir.Version.Parser\u0027:parse_digits/2\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThis issue affects elixir: from 0.9.3 before 1.20.1.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Uncontrolled Resource Consumption vulnerability in the Elixir standard library\u0027s `Version` module allows an attacker who controls a version string to cause a denial of service through CPU and memory exhaustion.\n\nThe version parser converts numeric version components (major, minor, patch and numeric pre-release/build identifiers) to integers without bounding their length. A single large all-digit component therefore forces a super-linear, non-yielding base-10 to arbitrary-precision integer conversion (`String.to_integer/1`, i.e. `:erlang.binary_to_integer/1`) that pins a BEAM scheduler, and a larger component raises an uncaught `SystemLimitError` that crashes the calling process. A single moderately sized string (around one megabyte) is enough; no authentication is required.\n\nThis is reachable from the public entry points `Version.parse/1`, `Version.parse!/1`, `Version.match?/3`, `Version.compare/2`, and `Version.parse_requirement/1`, which applications routinely call on untrusted input such as HTTP parameters, dependency-manifest fields, and package metadata.\n\nThis vulnerability is associated with program files `lib/version.ex` and program routines `\u0027Elixir.Version.Parser\u0027:parse_digits/2`.\n\nThis issue affects elixir: from 0.9.3 before 1.20.1."
                }
              ],
              "value": "Uncontrolled Resource Consumption vulnerability in the Elixir standard library\u0027s Version module allows an attacker who controls a version string to cause a denial of service through CPU and memory exhaustion.\n\nThe version parser converts numeric version components (major, minor, patch and numeric pre-release/build identifiers) to integers without bounding their length. A single large all-digit component therefore forces a super-linear, non-yielding base-10 to arbitrary-precision integer conversion (String.to_integer/1, i.e. :erlang.binary_to_integer/1) that pins a BEAM scheduler, and a larger component raises an uncaught SystemLimitError that crashes the calling process. A single moderately sized string (around one megabyte) is enough; no authentication is required.\n\nThis is reachable from the public entry points Version.parse/1, Version.parse!/1, Version.match?/3, Version.compare/2, and Version.parse_requirement/1, which applications routinely call on untrusted input such as HTTP parameters, dependency-manifest fields, and package metadata.\n\nThis vulnerability is associated with program files lib/version.ex and program routines \u0027Elixir.Version.Parser\u0027:parse_digits/2.\n\nThis issue affects elixir: from 0.9.3 before 1.20.1."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-130",
              "descriptions": [
                {
                  "lang": "en",
                  "value": "CAPEC-130 Excessive Allocation"
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "NONE",
                "attackVector": "LOCAL",
                "baseScore": 5.1,
                "baseSeverity": "MEDIUM",
                "privilegesRequired": "NONE",
                "providerUrgency": "NOT_DEFINED",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "NONE",
                "valueDensity": "NOT_DEFINED",
                "vectorString": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
                "version": "4.0",
                "vulnAvailabilityImpact": "LOW",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "GENERAL"
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-400",
                  "description": "CWE-400 Uncontrolled Resource Consumption",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-10-06T08:35:59.691Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "name": "GitHub Advisory",
              "tags": [
                "related",
                "vendor-advisory"
              ],
              "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-w2h8-8x3g-278p"
            },
            {
              "name": "EEF CNA record for CVE-2026-49762",
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-49762.html"
            },
            {
              "name": "OSV record EEF-CVE-2026-49762",
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-49762"
            },
            {
              "name": "Introducing commit 0576917 in elixir-lang/elixir",
              "tags": [
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/057691722eb8cfe315f5606358954a2b173ff7a3"
            },
            {
              "name": "Fix commit c64417d in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/c64417d72fd5c7d09e963ca3ac5fa2b140978d9e"
            },
            {
              "name": "Fix commit 64e6707 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/64e6707233464c3e55a9ca6b44019aed25b0390a"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "Unbounded integer parsing in the Version module enables CPU and memory exhaustion denial of service"
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-49762",
        "datePublished": "2026-06-09T14:04:07.405Z",
        "dateReserved": "2026-06-01T13:45:22.449Z",
        "dateUpdated": "2026-10-06T08:35:59.691Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-97209 (GCVE-0-2026-97209)

    Vulnerability from cvelistv5 – Published: 2026-10-30 08:59 – Updated: 2026-10-30 16:50
    VLAI
    Title
    Insecure temporary file in mix deps.partition lets a local user execute code in another user's dependency build
    Summary
    Insecure Temporary File vulnerability in Mix deps.partition in Elixir allows a local user to execute code as another user via a swapped partition config file. Partitioned dependency compilation passes the project configuration to each partition VM through a file in a fixed-name directory under the shared system temporary directory, and the partition trusts what it reads back. Another local user on the same host can exploit this during a mix deps.compile run that uses partitioned compilation. Mix.Tasks.Deps.Partition.server/4 in lib/mix/lib/mix/tasks/deps.partition.ex creates the directory with File.mkdir_p/1, which accepts an existing directory without checking its owner, so a user who created it first can replace the file before client/0 reads it. This issue affects mix: from 1.20.0-rc.4 before 1.20.5.
    SSVC
    Exploitation: poc Automatable: no Technical Impact: total
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-10-30 16:50 UTC
    CWE
    • CWE-377 - Insecure Temporary File
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 97/100 over 352 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-lang elixir Affected: 1.20.0-rc.4 , < 1.20.5 (semver)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-lang elixir Affected: 7f3d95b60c28ae3026a5c1bf943166391245bfd1 , < * (git)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Date Public
    2026-10-30 08:59
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-97209",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "no"
                      },
                      {
                        "Technical Impact": "total"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-10-30T16:50:17.250968Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-10-30T16:50:46.465Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-96q6-xhwp-fc8q"
              }
            ],
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027"
              ],
              "packageName": "mix",
              "packageURL": "pkg:otp/mix?repository_url=https:%2F%2Fgithub.com%2Felixir-lang%2Felixir\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Felixir-lang%2Felixir.git",
              "product": "elixir",
              "programFiles": [
                "lib/mix/tasks/deps.partition.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:server/3"
                },
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:server/4"
                },
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:client/0"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "lessThan": "1.20.5",
                  "status": "affected",
                  "version": "1.20.0-rc.4",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027"
              ],
              "packageName": "elixir-lang/elixir",
              "packageURL": "pkg:github/elixir-lang/elixir",
              "product": "elixir",
              "programFiles": [
                "lib/mix/lib/mix/tasks/deps.partition.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:server/3"
                },
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:server/4"
                },
                {
                  "name": "\u0027Elixir.Mix.Tasks.Deps.Partition\u0027:client/0"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "changes": [
                    {
                      "at": "55eb91220a1de14dc5aa27029d1f45b0f5832b2d",
                      "status": "unaffected"
                    },
                    {
                      "at": "9adbe955c8664f4be77d879dd38c003dd39fa777",
                      "status": "unaffected"
                    }
                  ],
                  "lessThan": "*",
                  "status": "affected",
                  "version": "7f3d95b60c28ae3026a5c1bf943166391245bfd1",
                  "versionType": "git"
                }
              ]
            }
          ],
          "configurations": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eThe attack requires partitioned dependency compilation, which is off by default and enabled by setting \u003ccode\u003eMIX_OS_DEPS_COMPILE_PARTITION_COUNT\u003c/code\u003e to a value greater than one, a \u003ccode\u003emix deps.compile\u003c/code\u003e run that compiles at least two non-local dependencies, and a system temporary directory shared with other local users, which is the default on Linux when \u003ccode\u003eTMPDIR\u003c/code\u003e is unset. Platforms with per-user temporary directories, such as macOS and Windows by default, are not affected unless \u003ccode\u003eTMPDIR\u003c/code\u003e points at a shared location.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "The attack requires partitioned dependency compilation, which is off by default and enabled by setting `MIX_OS_DEPS_COMPILE_PARTITION_COUNT` to a value greater than one, a `mix deps.compile` run that compiles at least two non-local dependencies, and a system temporary directory shared with other local users, which is the default on Linux when `TMPDIR` is unset. Platforms with per-user temporary directories, such as macOS and Windows by default, are not affected unless `TMPDIR` points at a shared location."
                }
              ],
              "value": "The attack requires partitioned dependency compilation, which is off by default and enabled by setting MIX_OS_DEPS_COMPILE_PARTITION_COUNT to a value greater than one, a mix deps.compile run that compiles at least two non-local dependencies, and a system temporary directory shared with other local users, which is the default on Linux when TMPDIR is unset. Platforms with per-user temporary directories, such as macOS and Windows by default, are not affected unless TMPDIR points at a shared location."
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.20.5",
                      "versionStartIncluding": "1.20.0-rc.4",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "finder",
              "value": "Quinn Wilton"
            },
            {
              "lang": "en",
              "type": "reporter",
              "value": "Quinn Wilton"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Jos\u00e9 Valim"
            },
            {
              "lang": "en",
              "type": "coordinator",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "dateAssigned": "2026-10-15T10:43:20.000Z",
          "datePublic": "2026-10-30T08:59:17.000Z",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eInsecure Temporary File vulnerability in Mix \u003ccode\u003edeps.partition\u003c/code\u003e in Elixir allows a local user to execute code as another user via a swapped partition config file. Partitioned dependency compilation passes the project configuration to each partition VM through a file in a fixed-name directory under the shared system temporary directory, and the partition trusts what it reads back. Another local user on the same host can exploit this during a \u003ccode\u003emix deps.compile\u003c/code\u003e run that uses partitioned compilation.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eMix.Tasks.Deps.Partition.server/4\u003c/code\u003e in \u003ccode\u003elib/mix/lib/mix/tasks/deps.partition.ex\u003c/code\u003e creates the directory with \u003ccode\u003eFile.mkdir_p/1\u003c/code\u003e, which accepts an existing directory without checking its owner, so a user who created it first can replace the file before \u003ccode\u003eclient/0\u003c/code\u003e reads it.\u003c/p\u003e\n\u003cp\u003eThis issue affects mix: from 1.20.0-rc.4 before 1.20.5.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Insecure Temporary File vulnerability in Mix `deps.partition` in Elixir allows a local user to execute code as another user via a swapped partition config file. Partitioned dependency compilation passes the project configuration to each partition VM through a file in a fixed-name directory under the shared system temporary directory, and the partition trusts what it reads back. Another local user on the same host can exploit this during a `mix deps.compile` run that uses partitioned compilation.\n\n`Mix.Tasks.Deps.Partition.server/4` in `lib/mix/lib/mix/tasks/deps.partition.ex` creates the directory with `File.mkdir_p/1`, which accepts an existing directory without checking its owner, so a user who created it first can replace the file before `client/0` reads it.\n\nThis issue affects mix: from 1.20.0-rc.4 before 1.20.5."
                }
              ],
              "value": "Insecure Temporary File vulnerability in Mix deps.partition in Elixir allows a local user to execute code as another user via a swapped partition config file. Partitioned dependency compilation passes the project configuration to each partition VM through a file in a fixed-name directory under the shared system temporary directory, and the partition trusts what it reads back. Another local user on the same host can exploit this during a mix deps.compile run that uses partitioned compilation.\n\nMix.Tasks.Deps.Partition.server/4 in lib/mix/lib/mix/tasks/deps.partition.ex creates the directory with File.mkdir_p/1, which accepts an existing directory without checking its owner, so a user who created it first can replace the file before client/0 reads it.\n\nThis issue affects mix: from 1.20.0-rc.4 before 1.20.5."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-26",
              "descriptions": [
                {
                  "lang": "en",
                  "supportingMedia": [
                    {
                      "base64": false,
                      "type": "text/html",
                      "value": "\u003cp\u003eA local user on a shared host who creates the partition directory before the victim\u0027s build replaces the serialized configuration and runs arbitrary shell commands as the developer or CI user whose \u003ccode\u003emix deps.compile\u003c/code\u003e uses partitioned compilation.\u003c/p\u003e"
                    },
                    {
                      "base64": false,
                      "type": "text/markdown",
                      "value": "A local user on a shared host who creates the partition directory before the victim\u0027s build replaces the serialized configuration and runs arbitrary shell commands as the developer or CI user whose `mix deps.compile` uses partitioned compilation."
                    }
                  ],
                  "value": "A local user on a shared host who creates the partition directory before the victim\u0027s build replaces the serialized configuration and runs arbitrary shell commands as the developer or CI user whose mix deps.compile uses partitioned compilation."
                }
              ]
            },
            {
              "capecId": "CAPEC-155",
              "descriptions": [
                {
                  "lang": "en",
                  "supportingMedia": [
                    {
                      "base64": false,
                      "type": "text/html",
                      "value": "\u003cp\u003eEven without pre-creating the directory, the world-readable partition file discloses the project configuration, including \u003ccode\u003eMix.install\u003c/code\u003e compile-time config, to other local users while the build runs.\u003c/p\u003e"
                    },
                    {
                      "base64": false,
                      "type": "text/markdown",
                      "value": "Even without pre-creating the directory, the world-readable partition file discloses the project configuration, including `Mix.install` compile-time config, to other local users while the build runs."
                    }
                  ],
                  "value": "Even without pre-creating the directory, the world-readable partition file discloses the project configuration, including Mix.install compile-time config, to other local users while the build runs."
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "LOCAL",
                "baseScore": 5.4,
                "baseSeverity": "MEDIUM",
                "privilegesRequired": "LOW",
                "providerUrgency": "NOT_DEFINED",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "PASSIVE",
                "valueDensity": "NOT_DEFINED",
                "vectorString": "CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
                "version": "4.0",
                "vulnAvailabilityImpact": "HIGH",
                "vulnConfidentialityImpact": "HIGH",
                "vulnIntegrityImpact": "HIGH",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "GENERAL"
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-377",
                  "description": "CWE-377 Insecure Temporary File",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-10-30T09:01:05.212Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "name": "Insecure temporary file in mix deps.partition lets a local user execute code in another user\u0027s dependency build",
              "tags": [
                "vendor-advisory",
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-96q6-xhwp-fc8q"
            },
            {
              "name": "EEF CNA record for CVE-2026-97209",
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-97209.html"
            },
            {
              "name": "OSV record EEF-CVE-2026-97209",
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-97209"
            },
            {
              "name": "Introducing commit 7f3d95b in elixir-lang/elixir",
              "tags": [
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/7f3d95b60c28ae3026a5c1bf943166391245bfd1"
            },
            {
              "name": "Fix commit 55eb912 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/55eb91220a1de14dc5aa27029d1f45b0f5832b2d"
            },
            {
              "name": "Fix commit 9adbe95 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/9adbe955c8664f4be77d879dd38c003dd39fa777"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "Insecure temporary file in mix deps.partition lets a local user execute code in another user\u0027s dependency build",
          "workarounds": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eOn a multi-user host, set \u003ccode\u003eTMPDIR\u003c/code\u003e to a private per-user directory with mode \u003ccode\u003e0700\u003c/code\u003e before running Mix, so that \u003ccode\u003eSystem.tmp_dir/0\u003c/code\u003e resolves to a location other users cannot pre-create or read. Alternatively, keep partitioned compilation disabled by leaving \u003ccode\u003eMIX_OS_DEPS_COMPILE_PARTITION_COUNT\u003c/code\u003e unset or at \u003ccode\u003e1\u003c/code\u003e, which keeps the configuration inside the parent process.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "On a multi-user host, set `TMPDIR` to a private per-user directory with mode `0700` before running Mix, so that `System.tmp_dir/0` resolves to a location other users cannot pre-create or read. Alternatively, keep partitioned compilation disabled by leaving `MIX_OS_DEPS_COMPILE_PARTITION_COUNT` unset or at `1`, which keeps the configuration inside the parent process."
                }
              ],
              "value": "On a multi-user host, set TMPDIR to a private per-user directory with mode 0700 before running Mix, so that System.tmp_dir/0 resolves to a location other users cannot pre-create or read. Alternatively, keep partitioned compilation disabled by leaving MIX_OS_DEPS_COMPILE_PARTITION_COUNT unset or at 1, which keeps the configuration inside the parent process."
            }
          ],
          "x_proofOfConcept": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003col\u003e\n\u003cli\u003eOn a multi-user Linux host where \u003ccode\u003eSystem.tmp_dir()\u003c/code\u003e resolves to \u003ccode\u003e/tmp\u003c/code\u003e, the attacker creates \u003ccode\u003e/tmp/mix_deps_partition\u003c/code\u003e as their own directory and watches it for new files.\u003c/li\u003e\n\u003cli\u003eThe victim runs \u003ccode\u003emix deps.compile\u003c/code\u003e (directly, or through \u003ccode\u003emix compile\u003c/code\u003e or \u003ccode\u003emix test\u003c/code\u003e) with \u003ccode\u003eMIX_OS_DEPS_COMPILE_PARTITION_COUNT=2\u003c/code\u003e on a project with at least two non-local dependencies. \u003ccode\u003eFile.mkdir_p/1\u003c/code\u003e succeeds on the existing directory and the config file is written into it.\u003c/li\u003e\n\u003cli\u003eBefore the partition VMs boot and read the file, the attacker decodes it, adds a \u003ccode\u003ecompile:\u003c/code\u003e shell command to every dependency in \u003ccode\u003econfig[:deps]\u003c/code\u003e, re-encodes it and writes it back.\u003c/li\u003e\n\u003cli\u003eEach partition decodes the attacker\u0027s term and \u003ccode\u003edeps.compile\u003c/code\u003e runs the injected command as the victim user.\u003c/li\u003e\n\u003c/ol\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "1. On a multi-user Linux host where `System.tmp_dir()` resolves to `/tmp`, the attacker creates `/tmp/mix_deps_partition` as their own directory and watches it for new files.\n2. The victim runs `mix deps.compile` (directly, or through `mix compile` or `mix test`) with `MIX_OS_DEPS_COMPILE_PARTITION_COUNT=2` on a project with at least two non-local dependencies. `File.mkdir_p/1` succeeds on the existing directory and the config file is written into it.\n3. Before the partition VMs boot and read the file, the attacker decodes it, adds a `compile:` shell command to every dependency in `config[:deps]`, re-encodes it and writes it back.\n4. Each partition decodes the attacker\u0027s term and `deps.compile` runs the injected command as the victim user."
                }
              ],
              "value": "* On a multi-user Linux host where System.tmp_dir() resolves to /tmp, the attacker creates /tmp/mix_deps_partition as their own directory and watches it for new files.\n* The victim runs mix deps.compile (directly, or through mix compile or mix test) with MIX_OS_DEPS_COMPILE_PARTITION_COUNT=2 on a project with at least two non-local dependencies. File.mkdir_p/1 succeeds on the existing directory and the config file is written into it.\n* Before the partition VMs boot and read the file, the attacker decodes it, adds a compile: shell command to every dependency in config[:deps], re-encodes it and writes it back.\n* Each partition decodes the attacker\u0027s term and deps.compile runs the injected command as the victim user."
            }
          ],
          "x_technicalAnalysis": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003e\u003cstrong\u003e1. Payload written to a shared location\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eMix.Tasks.Deps.Partition.server/4\u003c/code\u003e in \u003ccode\u003elib/mix/lib/mix/tasks/deps.partition.ex\u003c/code\u003e encodes \u003ccode\u003eMix.ProjectStack.peek()\u003c/code\u003e with \u003ccode\u003e:erlang.term_to_binary/1\u003c/code\u003e and Base64, calls \u003ccode\u003eFile.mkdir_p/1\u003c/code\u003e on \u003ccode\u003ePath.join(System.tmp_dir(), \"mix_deps_partition\")\u003c/code\u003e, and writes the payload to a file with a random name inside it. The directory name is fixed, carries no user id, and \u003ccode\u003eFile.mkdir_p/1\u003c/code\u003e succeeds on a directory that already exists, whoever owns it. The file is created with the default mode, so it is readable by other users in a shared temporary directory.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Partition VMs trust the file\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eEach partition is a fresh \u003ccode\u003eelixir\u003c/code\u003e VM started with \u003ccode\u003e--config-file \u0026lt;path\u0026gt;\u003c/code\u003e. \u003ccode\u003eMix.Tasks.Deps.Partition.client/0\u003c/code\u003e reads the file with \u003ccode\u003eFile.read!/1\u003c/code\u003e, decodes it with \u003ccode\u003e:erlang.binary_to_term/1\u003c/code\u003e, and pushes the decoded entry (project name, config and \u003ccode\u003emix.exs\u003c/code\u003e path) with \u003ccode\u003eMix.ProjectStack.push/3\u003c/code\u003e. Nothing verifies that the file still holds what the parent wrote.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Attacker control\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eA local user who created \u003ccode\u003emix_deps_partition\u003c/code\u003e before the victim\u0027s build owns it and may replace or rewrite any file inside, so they swap the payload between the parent\u0027s write and the client\u0027s read. The client then compiles the attacker\u0027s \u003ccode\u003edeps\u003c/code\u003e list, and a dependency\u0027s \u003ccode\u003ecompile:\u003c/code\u003e option is a shell command Mix executes in the victim\u0027s build. On the development branch the payload also carries \u003ccode\u003ecode_path\u003c/code\u003e and \u003ccode\u003ecompiler_options\u003c/code\u003e, which the client applies with \u003ccode\u003e:code.set_path/1\u003c/code\u003e and \u003ccode\u003eCode.compiler_options/1\u003c/code\u003e, so there the attacker also controls the client VM\u0027s code path.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "**1. Payload written to a shared location**\n\n`Mix.Tasks.Deps.Partition.server/4` in `lib/mix/lib/mix/tasks/deps.partition.ex` encodes `Mix.ProjectStack.peek()` with `:erlang.term_to_binary/1` and Base64, calls `File.mkdir_p/1` on `Path.join(System.tmp_dir(), \"mix_deps_partition\")`, and writes the payload to a file with a random name inside it. The directory name is fixed, carries no user id, and `File.mkdir_p/1` succeeds on a directory that already exists, whoever owns it. The file is created with the default mode, so it is readable by other users in a shared temporary directory.\n\n**2. Partition VMs trust the file**\n\nEach partition is a fresh `elixir` VM started with `--config-file \u003cpath\u003e`. `Mix.Tasks.Deps.Partition.client/0` reads the file with `File.read!/1`, decodes it with `:erlang.binary_to_term/1`, and pushes the decoded entry (project name, config and `mix.exs` path) with `Mix.ProjectStack.push/3`. Nothing verifies that the file still holds what the parent wrote.\n\n**3. Attacker control**\n\nA local user who created `mix_deps_partition` before the victim\u0027s build owns it and may replace or rewrite any file inside, so they swap the payload between the parent\u0027s write and the client\u0027s read. The client then compiles the attacker\u0027s `deps` list, and a dependency\u0027s `compile:` option is a shell command Mix executes in the victim\u0027s build. On the development branch the payload also carries `code_path` and `compiler_options`, which the client applies with `:code.set_path/1` and `Code.compiler_options/1`, so there the attacker also controls the client VM\u0027s code path."
                }
              ],
              "value": "1. Payload written to a shared location\n\nMix.Tasks.Deps.Partition.server/4 in lib/mix/lib/mix/tasks/deps.partition.ex encodes Mix.ProjectStack.peek() with :erlang.term_to_binary/1 and Base64, calls File.mkdir_p/1 on Path.join(System.tmp_dir(), \"mix_deps_partition\"), and writes the payload to a file with a random name inside it. The directory name is fixed, carries no user id, and File.mkdir_p/1 succeeds on a directory that already exists, whoever owns it. The file is created with the default mode, so it is readable by other users in a shared temporary directory.\n\n2. Partition VMs trust the file\n\nEach partition is a fresh elixir VM started with --config-file \u003cpath\u003e. Mix.Tasks.Deps.Partition.client/0 reads the file with File.read!/1, decodes it with :erlang.binary_to_term/1, and pushes the decoded entry (project name, config and mix.exs path) with Mix.ProjectStack.push/3. Nothing verifies that the file still holds what the parent wrote.\n\n3. Attacker control\n\nA local user who created mix_deps_partition before the victim\u0027s build owns it and may replace or rewrite any file inside, so they swap the payload between the parent\u0027s write and the client\u0027s read. The client then compiles the attacker\u0027s deps list, and a dependency\u0027s compile: option is a shell command Mix executes in the victim\u0027s build. On the development branch the payload also carries code_path and compiler_options, which the client applies with :code.set_path/1 and Code.compiler_options/1, so there the attacker also controls the client VM\u0027s code path."
            }
          ]
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-97209",
        "datePublished": "2026-10-30T08:59:17.615Z",
        "dateReserved": "2026-10-14T21:45:01.541Z",
        "dateUpdated": "2026-10-30T16:50:46.465Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-115274 (GCVE-0-2026-115274)

    Vulnerability from cvelistv5 – Published: 2026-10-30 08:57 – Updated: 2026-10-30 16:51
    VLAI
    Title
    Unbounded integer parsing in JSON.decode/1 enables CPU and memory exhaustion denial of service in Elixir
    Summary
    Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library's JSON module allows an unauthenticated attacker who supplies a JSON document to cause a denial of service through CPU and memory exhaustion. JSON.decode/1 and JSON.decode!/1 convert each integer literal with erlang:binary_to_integer/1 and do not bound its length. A literal of one million digits decodes successfully but costs 100 to 300 times as much CPU as a string of the same length, so one request body of a few megabytes holds a scheduler for about a second. A literal of more than about 1.29 million digits raises SystemLimitError. Applications generated by phx.new decode request bodies with JSON in Plug.Parsers, before routing and authentication. This issue affects elixir: from 1.18.0-rc.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5.
    SSVC
    Exploitation: poc Automatable: yes Technical Impact: partial
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-10-30 16:51 UTC
    CWE
    • CWE-1284 - Improper Validation of Specified Quantity in Input
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 97/100 over 352 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-lang elixir Affected: 1.18.0-rc.0 , < 1.18.6 (semver)
    Affected: 1.19.0-rc.0 , < 1.19.7 (semver)
    Affected: 1.20.0-rc.0 , < 1.20.5 (semver)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-lang elixir Affected: 793c1147210388587defb649f25f293ffbfa6b1b , < * (git)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Date Public
    2026-10-30 08:57
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-115274",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "yes"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-10-30T16:51:17.679403Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-10-30T16:51:45.396Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-rp5q-xqhq-926h"
              }
            ],
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.JSON\u0027",
                "elixir_json"
              ],
              "packageName": "elixir",
              "packageURL": "pkg:otp/elixir?repository_url=https:%2F%2Fgithub.com%2Felixir-lang%2Felixir\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Felixir-lang%2Felixir.git",
              "product": "elixir",
              "programFiles": [
                "lib/json.ex",
                "src/elixir_json.erl"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.JSON\u0027:decode/1"
                },
                {
                  "name": "\u0027Elixir.JSON\u0027:\u0027decode!\u0027/1"
                },
                {
                  "name": "\u0027Elixir.JSON\u0027:decode/3"
                },
                {
                  "name": "elixir_json:decode/3"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "lessThan": "1.18.6",
                  "status": "affected",
                  "version": "1.18.0-rc.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.19.7",
                  "status": "affected",
                  "version": "1.19.0-rc.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.20.5",
                  "status": "affected",
                  "version": "1.20.0-rc.0",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.JSON\u0027",
                "elixir_json"
              ],
              "packageName": "elixir-lang/elixir",
              "packageURL": "pkg:github/elixir-lang/elixir",
              "product": "elixir",
              "programFiles": [
                "lib/elixir/lib/json.ex",
                "lib/elixir/src/elixir_json.erl"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.JSON\u0027:decode/1"
                },
                {
                  "name": "\u0027Elixir.JSON\u0027:\u0027decode!\u0027/1"
                },
                {
                  "name": "\u0027Elixir.JSON\u0027:decode/3"
                },
                {
                  "name": "elixir_json:decode/3"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "changes": [
                    {
                      "at": "c81f12265919c34a0d5134f1905a4eb4edaea1c2",
                      "status": "unaffected"
                    },
                    {
                      "at": "7154e8781da9fd016a973ea01776a011c8b5726a",
                      "status": "unaffected"
                    },
                    {
                      "at": "cd158efacdc10b30232864251747dff9b8387dfe",
                      "status": "unaffected"
                    },
                    {
                      "at": "ee5cc23f8928aa5a0f25bb7f28e42ecfc7b7d396",
                      "status": "unaffected"
                    }
                  ],
                  "lessThan": "*",
                  "status": "affected",
                  "version": "793c1147210388587defb649f25f293ffbfa6b1b",
                  "versionType": "git"
                }
              ]
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.18.6",
                      "versionStartIncluding": "1.18.0-rc.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.19.7",
                      "versionStartIncluding": "1.19.0-rc.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.20.5",
                      "versionStartIncluding": "1.20.0-rc.0",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "finder",
              "value": "David Vieira Kurz"
            },
            {
              "lang": "en",
              "type": "finder",
              "value": "Peter Ullrich"
            },
            {
              "lang": "en",
              "type": "reporter",
              "value": "David Vieira Kurz"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Jos\u00e9 Valim"
            },
            {
              "lang": "en",
              "type": "coordinator",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "dateAssigned": "2026-10-25T12:21:17.000Z",
          "datePublic": "2026-10-30T08:57:17.000Z",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eImproper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s \u003ccode\u003eJSON\u003c/code\u003e module allows an unauthenticated attacker who supplies a JSON document to cause a denial of service through CPU and memory exhaustion.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eJSON.decode/1\u003c/code\u003e and \u003ccode\u003eJSON.decode!/1\u003c/code\u003e convert each integer literal with \u003ccode\u003eerlang:binary_to_integer/1\u003c/code\u003e and do not bound its length. A literal of one million digits decodes successfully but costs 100 to 300 times as much CPU as a string of the same length, so one request body of a few megabytes holds a scheduler for about a second. A literal of more than about 1.29 million digits raises \u003ccode\u003eSystemLimitError\u003c/code\u003e. Applications generated by \u003ccode\u003ephx.new\u003c/code\u003e decode request bodies with \u003ccode\u003eJSON\u003c/code\u003e in \u003ccode\u003ePlug.Parsers\u003c/code\u003e, before routing and authentication.\u003c/p\u003e\n\u003cp\u003eThis issue affects elixir: from 1.18.0-rc.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s `JSON` module allows an unauthenticated attacker who supplies a JSON document to cause a denial of service through CPU and memory exhaustion.\n\n`JSON.decode/1` and `JSON.decode!/1` convert each integer literal with `erlang:binary_to_integer/1` and do not bound its length. A literal of one million digits decodes successfully but costs 100 to 300 times as much CPU as a string of the same length, so one request body of a few megabytes holds a scheduler for about a second. A literal of more than about 1.29 million digits raises `SystemLimitError`. Applications generated by `phx.new` decode request bodies with `JSON` in `Plug.Parsers`, before routing and authentication.\n\nThis issue affects elixir: from 1.18.0-rc.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5."
                }
              ],
              "value": "Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s JSON module allows an unauthenticated attacker who supplies a JSON document to cause a denial of service through CPU and memory exhaustion.\n\nJSON.decode/1 and JSON.decode!/1 convert each integer literal with erlang:binary_to_integer/1 and do not bound its length. A literal of one million digits decodes successfully but costs 100 to 300 times as much CPU as a string of the same length, so one request body of a few megabytes holds a scheduler for about a second. A literal of more than about 1.29 million digits raises SystemLimitError. Applications generated by phx.new decode request bodies with JSON in Plug.Parsers, before routing and authentication.\n\nThis issue affects elixir: from 1.18.0-rc.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-130",
              "descriptions": [
                {
                  "lang": "en",
                  "supportingMedia": [
                    {
                      "base64": false,
                      "type": "text/html",
                      "value": "\u003cp\u003eAn unauthenticated client that sends a JSON body with integer literals of about a million digits makes the server spend about a second of CPU per request and allocate large bignums. Sustained requests exhaust CPU on applications that decode untrusted JSON with \u003ccode\u003eJSON\u003c/code\u003e, including new Phoenix applications by default.\u003c/p\u003e"
                    },
                    {
                      "base64": false,
                      "type": "text/markdown",
                      "value": "An unauthenticated client that sends a JSON body with integer literals of about a million digits makes the server spend about a second of CPU per request and allocate large bignums. Sustained requests exhaust CPU on applications that decode untrusted JSON with `JSON`, including new Phoenix applications by default."
                    }
                  ],
                  "value": "An unauthenticated client that sends a JSON body with integer literals of about a million digits makes the server spend about a second of CPU per request and allocate large bignums. Sustained requests exhaust CPU on applications that decode untrusted JSON with JSON, including new Phoenix applications by default."
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "NONE",
                "attackVector": "NETWORK",
                "baseScore": 6.9,
                "baseSeverity": "MEDIUM",
                "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:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
                "version": "4.0",
                "vulnAvailabilityImpact": "LOW",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "GENERAL"
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-1284",
                  "description": "CWE-1284 Improper Validation of Specified Quantity in Input",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-10-30T09:01:54.926Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "name": "Unbounded integer parsing in JSON.decode/1 enables CPU and memory exhaustion denial of service in Elixir",
              "tags": [
                "vendor-advisory",
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-rp5q-xqhq-926h"
            },
            {
              "name": "EEF CNA record for CVE-2026-115274",
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-115274.html"
            },
            {
              "name": "OSV record EEF-CVE-2026-115274",
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-115274"
            },
            {
              "name": "Introducing commit 793c114 in elixir-lang/elixir",
              "tags": [
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/793c1147210388587defb649f25f293ffbfa6b1b"
            },
            {
              "name": "Fix commit c81f122 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/c81f12265919c34a0d5134f1905a4eb4edaea1c2"
            },
            {
              "name": "Fix commit 7154e87 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/7154e8781da9fd016a973ea01776a011c8b5726a"
            },
            {
              "name": "Fix commit cd158ef in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/cd158efacdc10b30232864251747dff9b8387dfe"
            },
            {
              "name": "Fix commit ee5cc23 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/ee5cc23f8928aa5a0f25bb7f28e42ecfc7b7d396"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "Unbounded integer parsing in JSON.decode/1 enables CPU and memory exhaustion denial of service in Elixir",
          "workarounds": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eDecode untrusted JSON with an \u003ccode\u003einteger\u003c/code\u003e decoder that rejects long digit runs before conversion:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-elixir\"\u003edefmodule SafeJSON do\n  def decode!(binary) do\n    case JSON.decode(binary, :ok, integer: \u0026amp;safe_integer/1) do\n      {decoded, :ok, \"\"} -\u0026gt; decoded\n      _ -\u0026gt; raise ArgumentError, \"invalid JSON\"\n    end\n  end\n\n  defp safe_integer(digits) when byte_size(digits) \u0026lt;= 1024, do: String.to_integer(digits)\n  defp safe_integer(_digits), do: raise(ArgumentError, \"integer too long\")\nend\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIn Phoenix, pass \u003ccode\u003eSafeJSON\u003c/code\u003e to \u003ccode\u003ePlug.Parsers\u003c/code\u003e as \u003ccode\u003ejson_decoder\u003c/code\u003e instead of \u003ccode\u003ePhoenix.json_library()\u003c/code\u003e.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Decode untrusted JSON with an `integer` decoder that rejects long digit runs before conversion:\n\n```elixir\ndefmodule SafeJSON do\n  def decode!(binary) do\n    case JSON.decode(binary, :ok, integer: \u0026safe_integer/1) do\n      {decoded, :ok, \"\"} -\u003e decoded\n      _ -\u003e raise ArgumentError, \"invalid JSON\"\n    end\n  end\n\n  defp safe_integer(digits) when byte_size(digits) \u003c= 1024, do: String.to_integer(digits)\n  defp safe_integer(_digits), do: raise(ArgumentError, \"integer too long\")\nend\n```\n\nIn Phoenix, pass `SafeJSON` to `Plug.Parsers` as `json_decoder` instead of `Phoenix.json_library()`."
                }
              ],
              "value": "Decode untrusted JSON with an integer decoder that rejects long digit runs before conversion:\n\ndefmodule SafeJSON do\n  def decode!(binary) do\n    case JSON.decode(binary, :ok, integer: \u0026safe_integer/1) do\n      {decoded, :ok, \"\"} -\u003e decoded\n      _ -\u003e raise ArgumentError, \"invalid JSON\"\n    end\n  end\n\n  defp safe_integer(digits) when byte_size(digits) \u003c= 1024, do: String.to_integer(digits)\n  defp safe_integer(_digits), do: raise(ArgumentError, \"integer too long\")\nend\n\nIn Phoenix, pass SafeJSON to Plug.Parsers as json_decoder instead of Phoenix.json_library()."
            }
          ],
          "x_proofOfConcept": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003col\u003e\n\u003cli\u003eBuild \u003ccode\u003ebody = \"[\" \u0026lt;\u0026gt; Enum.map_join(1..7, \",\", fn _ -\u0026gt; String.duplicate(\"9\", 1_000_000) end) \u0026lt;\u0026gt; \"]\"\u003c/code\u003e, a body of about 7 MB.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eJSON.decode(body)\u003c/code\u003e returns \u003ccode\u003e{:ok, ...}\u003c/code\u003e after about one second of CPU.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eJSON.decode(String.duplicate(\"9\", 1_000_000))\u003c/code\u003e takes 100 to 300 times as long as \u003ccode\u003eJSON.decode(\"\\\"\" \u0026lt;\u0026gt; String.duplicate(\"9\", 1_000_000) \u0026lt;\u0026gt; \"\\\"\")\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eJSON.decode(String.duplicate(\"9\", 1_400_000))\u003c/code\u003e raises \u003ccode\u003eSystemLimitError\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "1. Build `body = \"[\" \u003c\u003e Enum.map_join(1..7, \",\", fn _ -\u003e String.duplicate(\"9\", 1_000_000) end) \u003c\u003e \"]\"`, a body of about 7 MB.\n2. `JSON.decode(body)` returns `{:ok, ...}` after about one second of CPU.\n3. `JSON.decode(String.duplicate(\"9\", 1_000_000))` takes 100 to 300 times as long as `JSON.decode(\"\\\"\" \u003c\u003e String.duplicate(\"9\", 1_000_000) \u003c\u003e \"\\\"\")`.\n4. `JSON.decode(String.duplicate(\"9\", 1_400_000))` raises `SystemLimitError`."
                }
              ],
              "value": "* Build body = \"[\" \u003c\u003e Enum.map_join(1..7, \",\", fn _ -\u003e String.duplicate(\"9\", 1_000_000) end) \u003c\u003e \"]\", a body of about 7 MB.\n* JSON.decode(body) returns {:ok, ...} after about one second of CPU.\n* JSON.decode(String.duplicate(\"9\", 1_000_000)) takes 100 to 300 times as long as JSON.decode(\"\\\"\" \u003c\u003e String.duplicate(\"9\", 1_000_000) \u003c\u003e \"\\\"\").\n* JSON.decode(String.duplicate(\"9\", 1_400_000)) raises SystemLimitError."
            }
          ],
          "x_technicalAnalysis": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003e\u003cstrong\u003e1. Default integer decoder\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eJSON.decode/1\u003c/code\u003e and \u003ccode\u003eJSON.decode!/1\u003c/code\u003e call \u003ccode\u003eJSON.decode/3\u003c/code\u003e without an \u003ccode\u003einteger\u003c/code\u003e decoder. Up to the removal of Erlang/OTP 26 support, \u003ccode\u003eJSON.decode/3\u003c/code\u003e used the vendored \u003ccode\u003eelixir_json\u003c/code\u003e module, whose default is \u003ccode\u003efun erlang:binary_to_integer/1\u003c/code\u003e. It now calls OTP\u0027s \u003ccode\u003e:json.decode/3\u003c/code\u003e, which has the same default. The \u003ccode\u003eJSON.decode/3\u003c/code\u003e documentation names \u003ccode\u003e\u0026amp;String.to_integer/1\u003c/code\u003e as the default, which converts the same way.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Unbounded conversion\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe decoder scans the digits of an integer literal and passes the whole run to the conversion. Nothing limits the run\u0027s length, so the cost grows faster than linearly with the number of digits. At one million digits an integer literal costs 100 to 300 times as much CPU as a string literal of the same length. Above about 1.29 million digits the conversion raises \u003ccode\u003eSystemLimitError\u003c/code\u003e, which \u003ccode\u003eJSON.decode/3\u003c/code\u003e does not catch.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Reach\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ephx.new\u003c/code\u003e sets \u003ccode\u003econfig :phoenix, :json_library, JSON\u003c/code\u003e, and the generated endpoint passes \u003ccode\u003ePhoenix.json_library()\u003c/code\u003e to \u003ccode\u003ePlug.Parsers\u003c/code\u003e as \u003ccode\u003ejson_decoder\u003c/code\u003e. \u003ccode\u003ePlug.Parsers\u003c/code\u003e runs before routing and authentication and accepts bodies up to 8 MB by default. It rescues the \u003ccode\u003eSystemLimitError\u003c/code\u003e into a 400 response after the CPU cost is spent. Jason, the previous default, rejects integer literals longer than its \u003ccode\u003edecoding_integer_digit_limit\u003c/code\u003e (1024 by default) before conversion.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "**1. Default integer decoder**\n\n`JSON.decode/1` and `JSON.decode!/1` call `JSON.decode/3` without an `integer` decoder. Up to the removal of Erlang/OTP 26 support, `JSON.decode/3` used the vendored `elixir_json` module, whose default is `fun erlang:binary_to_integer/1`. It now calls OTP\u0027s `:json.decode/3`, which has the same default. The `JSON.decode/3` documentation names `\u0026String.to_integer/1` as the default, which converts the same way.\n\n**2. Unbounded conversion**\n\nThe decoder scans the digits of an integer literal and passes the whole run to the conversion. Nothing limits the run\u0027s length, so the cost grows faster than linearly with the number of digits. At one million digits an integer literal costs 100 to 300 times as much CPU as a string literal of the same length. Above about 1.29 million digits the conversion raises `SystemLimitError`, which `JSON.decode/3` does not catch.\n\n**3. Reach**\n\n`phx.new` sets `config :phoenix, :json_library, JSON`, and the generated endpoint passes `Phoenix.json_library()` to `Plug.Parsers` as `json_decoder`. `Plug.Parsers` runs before routing and authentication and accepts bodies up to 8 MB by default. It rescues the `SystemLimitError` into a 400 response after the CPU cost is spent. Jason, the previous default, rejects integer literals longer than its `decoding_integer_digit_limit` (1024 by default) before conversion."
                }
              ],
              "value": "1. Default integer decoder\n\nJSON.decode/1 and JSON.decode!/1 call JSON.decode/3 without an integer decoder. Up to the removal of Erlang/OTP 26 support, JSON.decode/3 used the vendored elixir_json module, whose default is fun erlang:binary_to_integer/1. It now calls OTP\u0027s :json.decode/3, which has the same default. The JSON.decode/3 documentation names \u0026String.to_integer/1 as the default, which converts the same way.\n\n2. Unbounded conversion\n\nThe decoder scans the digits of an integer literal and passes the whole run to the conversion. Nothing limits the run\u0027s length, so the cost grows faster than linearly with the number of digits. At one million digits an integer literal costs 100 to 300 times as much CPU as a string literal of the same length. Above about 1.29 million digits the conversion raises SystemLimitError, which JSON.decode/3 does not catch.\n\n3. Reach\n\nphx.new sets config :phoenix, :json_library, JSON, and the generated endpoint passes Phoenix.json_library() to Plug.Parsers as json_decoder. Plug.Parsers runs before routing and authentication and accepts bodies up to 8 MB by default. It rescues the SystemLimitError into a 400 response after the CPU cost is spent. Jason, the previous default, rejects integer literals longer than its decoding_integer_digit_limit (1024 by default) before conversion."
            }
          ]
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-115274",
        "datePublished": "2026-10-30T08:57:16.847Z",
        "dateReserved": "2026-10-23T12:45:01.103Z",
        "dateUpdated": "2026-10-30T16:51:45.396Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-109821 (GCVE-0-2026-109821)

    Vulnerability from cvelistv5 – Published: 2026-10-30 08:54 – Updated: 2026-10-30 16:52
    VLAI
    Title
    Unbounded port parsing in URI.parse/1 enables CPU exhaustion denial of service in Elixir
    Summary
    Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library's URI module allows an attacker who supplies a URL to cause a denial of service through CPU exhaustion. URI.parse/1 converts the port component with String.to_integer/1 in split_authority/1 (lib/elixir/lib/uri.ex) without bounding its length, so a port of up to 1,262,592 decimal digits produces a bignum of that size and a longer one raises SystemLimitError. Rendering the parsed URI back to text (URI.to_string/1, inspect/1, logging, error formatting) converts the bignum with :erlang.integer_to_binary/1, which is quadratic on Erlang/OTP 27, 28 and 29.0: a 400,000-digit port renders in about 2.6 seconds and a 1 MB port in about 18 seconds. Ports above 65535 are also accepted. This issue affects elixir: from 0.5.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5.
    SSVC
    Exploitation: poc Automatable: no Technical Impact: partial
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-10-30 16:52 UTC
    CWE
    • CWE-1284 - Improper Validation of Specified Quantity in Input
    • CWE-400 - Uncontrolled Resource Consumption
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 97/100 over 352 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-lang elixir Affected: 0.5.0 , < 1.18.6 (semver)
    Affected: 1.19.0-rc.0 , < 1.19.7 (semver)
    Affected: 1.20.0-rc.0 , < 1.20.5 (semver)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-lang elixir Affected: 39801821eef2d06524e5ebf5a0e4b3c380ad23ae , < * (git)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Date Public
    2026-10-30 08:54
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-109821",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "no"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-10-30T16:52:15.736711Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-10-30T16:52:43.052Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-327h-97cv-59xg"
              }
            ],
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.URI\u0027",
                "\u0027Elixir.String.Chars.URI\u0027"
              ],
              "packageName": "elixir",
              "packageURL": "pkg:otp/elixir?repository_url=https:%2F%2Fgithub.com%2Felixir-lang%2Felixir\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Felixir-lang%2Felixir.git",
              "product": "elixir",
              "programFiles": [
                "lib/uri.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.URI\u0027:parse/1"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:split_authority/1"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:merge/2"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:to_string/1"
                },
                {
                  "name": "\u0027Elixir.String.Chars.URI\u0027:to_string/1"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "lessThan": "1.18.6",
                  "status": "affected",
                  "version": "0.5.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.19.7",
                  "status": "affected",
                  "version": "1.19.0-rc.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.20.5",
                  "status": "affected",
                  "version": "1.20.0-rc.0",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.URI\u0027",
                "\u0027Elixir.String.Chars.URI\u0027"
              ],
              "packageName": "elixir-lang/elixir",
              "packageURL": "pkg:github/elixir-lang/elixir",
              "product": "elixir",
              "programFiles": [
                "lib/elixir/lib/uri.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.URI\u0027:parse/1"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:split_authority/1"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:merge/2"
                },
                {
                  "name": "\u0027Elixir.URI\u0027:to_string/1"
                },
                {
                  "name": "\u0027Elixir.String.Chars.URI\u0027:to_string/1"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "changes": [
                    {
                      "at": "9a39d64a4473d02681c486bc70681c972479d32b",
                      "status": "unaffected"
                    },
                    {
                      "at": "b70f33a591b6ab8eb0fa4a7e00bac1c2ed4e0d58",
                      "status": "unaffected"
                    },
                    {
                      "at": "07d9abc3f3ac4c8d2ba8384bd102b6d6205b08b7",
                      "status": "unaffected"
                    },
                    {
                      "at": "d879b39d778fec2f475689573d5672720c4f2a21",
                      "status": "unaffected"
                    }
                  ],
                  "lessThan": "*",
                  "status": "affected",
                  "version": "39801821eef2d06524e5ebf5a0e4b3c380ad23ae",
                  "versionType": "git"
                }
              ]
            }
          ],
          "configurations": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eThe application parses an attacker-supplied URL with \u003ccode\u003eURI.parse/1\u003c/code\u003e, for example a URL field in a form or JSON request body, and then renders the result or a value containing it. The multi-second render cost requires Erlang/OTP 27, 28 or 29.0; on OTP 29.1 and later rendering a 1 MB port takes under a second. URLs in HTTP headers and the request line are bounded by the server\u0027s size limits.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "The application parses an attacker-supplied URL with `URI.parse/1`, for example a URL field in a form or JSON request body, and then renders the result or a value containing it. The multi-second render cost requires Erlang/OTP 27, 28 or 29.0; on OTP 29.1 and later rendering a 1 MB port takes under a second. URLs in HTTP headers and the request line are bounded by the server\u0027s size limits."
                }
              ],
              "value": "The application parses an attacker-supplied URL with URI.parse/1, for example a URL field in a form or JSON request body, and then renders the result or a value containing it. The multi-second render cost requires Erlang/OTP 27, 28 or 29.0; on OTP 29.1 and later rendering a 1 MB port takes under a second. URLs in HTTP headers and the request line are bounded by the server\u0027s size limits."
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.18.6",
                      "versionStartIncluding": "0.5.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.19.7",
                      "versionStartIncluding": "1.19.0-rc.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.20.5",
                      "versionStartIncluding": "1.20.0-rc.0",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "finder",
              "value": "Eric Meadows-J\u00f6nsson"
            },
            {
              "lang": "en",
              "type": "reporter",
              "value": "Eric Meadows-J\u00f6nsson"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Jos\u00e9 Valim"
            },
            {
              "lang": "en",
              "type": "coordinator",
              "value": "Eric Meadows-J\u00f6nsson"
            },
            {
              "lang": "en",
              "type": "analyst",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "dateAssigned": "2026-10-25T10:39:55.000Z",
          "datePublic": "2026-10-30T08:54:08.000Z",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eImproper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s \u003ccode\u003eURI\u003c/code\u003e module allows an attacker who supplies a URL to cause a denial of service through CPU exhaustion.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eURI.parse/1\u003c/code\u003e converts the port component with \u003ccode\u003eString.to_integer/1\u003c/code\u003e in \u003ccode\u003esplit_authority/1\u003c/code\u003e (\u003ccode\u003elib/elixir/lib/uri.ex\u003c/code\u003e) without bounding its length, so a port of up to 1,262,592 decimal digits produces a bignum of that size and a longer one raises \u003ccode\u003eSystemLimitError\u003c/code\u003e. Rendering the parsed URI back to text (\u003ccode\u003eURI.to_string/1\u003c/code\u003e, \u003ccode\u003einspect/1\u003c/code\u003e, logging, error formatting) converts the bignum with \u003ccode\u003e:erlang.integer_to_binary/1\u003c/code\u003e, which is quadratic on Erlang/OTP 27, 28 and 29.0: a 400,000-digit port renders in about 2.6 seconds and a 1 MB port in about 18 seconds. Ports above 65535 are also accepted.\u003c/p\u003e\n\u003cp\u003eThis issue affects elixir: from 0.5.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s `URI` module allows an attacker who supplies a URL to cause a denial of service through CPU exhaustion.\n\n`URI.parse/1` converts the port component with `String.to_integer/1` in `split_authority/1` (`lib/elixir/lib/uri.ex`) without bounding its length, so a port of up to 1,262,592 decimal digits produces a bignum of that size and a longer one raises `SystemLimitError`. Rendering the parsed URI back to text (`URI.to_string/1`, `inspect/1`, logging, error formatting) converts the bignum with `:erlang.integer_to_binary/1`, which is quadratic on Erlang/OTP 27, 28 and 29.0: a 400,000-digit port renders in about 2.6 seconds and a 1 MB port in about 18 seconds. Ports above 65535 are also accepted.\n\nThis issue affects elixir: from 0.5.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5."
                }
              ],
              "value": "Improper Validation of Specified Quantity in Input vulnerability in the Elixir standard library\u0027s URI module allows an attacker who supplies a URL to cause a denial of service through CPU exhaustion.\n\nURI.parse/1 converts the port component with String.to_integer/1 in split_authority/1 (lib/elixir/lib/uri.ex) without bounding its length, so a port of up to 1,262,592 decimal digits produces a bignum of that size and a longer one raises SystemLimitError. Rendering the parsed URI back to text (URI.to_string/1, inspect/1, logging, error formatting) converts the bignum with :erlang.integer_to_binary/1, which is quadratic on Erlang/OTP 27, 28 and 29.0: a 400,000-digit port renders in about 2.6 seconds and a 1 MB port in about 18 seconds. Ports above 65535 are also accepted.\n\nThis issue affects elixir: from 0.5.0 before 1.18.6, from 1.19.0-rc.0 before 1.19.7, and from 1.20.0-rc.0 before 1.20.5."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-130",
              "descriptions": [
                {
                  "lang": "en",
                  "supportingMedia": [
                    {
                      "base64": false,
                      "type": "text/html",
                      "value": "\u003cp\u003eAn unauthenticated client that submits a URL with a port of hundreds of thousands of digits makes the server build a bignum of that size, and each later rendering of the parsed URI costs seconds of CPU on Erlang/OTP 27, 28 and 29.0. Sustained requests exhaust CPU on applications that parse and log, store or echo user-supplied URLs.\u003c/p\u003e"
                    },
                    {
                      "base64": false,
                      "type": "text/markdown",
                      "value": "An unauthenticated client that submits a URL with a port of hundreds of thousands of digits makes the server build a bignum of that size, and each later rendering of the parsed URI costs seconds of CPU on Erlang/OTP 27, 28 and 29.0. Sustained requests exhaust CPU on applications that parse and log, store or echo user-supplied URLs."
                    }
                  ],
                  "value": "An unauthenticated client that submits a URL with a port of hundreds of thousands of digits makes the server build a bignum of that size, and each later rendering of the parsed URI costs seconds of CPU on Erlang/OTP 27, 28 and 29.0. Sustained requests exhaust CPU on applications that parse and log, store or echo user-supplied URLs."
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "NETWORK",
                "baseScore": 6.3,
                "baseSeverity": "MEDIUM",
                "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:L/SC:N/SI:N/SA:N",
                "version": "4.0",
                "vulnAvailabilityImpact": "LOW",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "GENERAL"
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-1284",
                  "description": "CWE-1284 Improper Validation of Specified Quantity in Input",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            },
            {
              "descriptions": [
                {
                  "cweId": "CWE-400",
                  "description": "CWE-400 Uncontrolled Resource Consumption",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-10-30T09:02:51.828Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "name": "Unbounded port parsing in URI.parse/1 enables CPU exhaustion denial of service",
              "tags": [
                "vendor-advisory",
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-327h-97cv-59xg"
            },
            {
              "name": "EEF CNA record for CVE-2026-109821",
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-109821.html"
            },
            {
              "name": "OSV record EEF-CVE-2026-109821",
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-109821"
            },
            {
              "name": "Erlang/OTP advisory for the same issue in uri_string",
              "tags": [
                "related"
              ],
              "url": "https://github.com/erlang/otp/security/advisories/GHSA-8qw4-2chm-mvj2"
            },
            {
              "name": "Introducing commit 3980182 in elixir-lang/elixir",
              "tags": [
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/39801821eef2d06524e5ebf5a0e4b3c380ad23ae"
            },
            {
              "name": "Fix commit 9a39d64 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/9a39d64a4473d02681c486bc70681c972479d32b"
            },
            {
              "name": "Fix commit b70f33a in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/b70f33a591b6ab8eb0fa4a7e00bac1c2ed4e0d58"
            },
            {
              "name": "Fix commit 07d9abc in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/07d9abc3f3ac4c8d2ba8384bd102b6d6205b08b7"
            },
            {
              "name": "Fix commit d879b39 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/d879b39d778fec2f475689573d5672720c4f2a21"
            }
          ],
          "source": {
            "discovery": "INTERNAL"
          },
          "title": "Unbounded port parsing in URI.parse/1 enables CPU exhaustion denial of service in Elixir",
          "workarounds": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eReject long URLs before parsing them, for example anything above a few kilobytes, and reject a parsed \u003ccode\u003eport\u003c/code\u003e above 65535 before rendering or using the \u003ccode\u003e%URI{}\u003c/code\u003e. \u003ccode\u003eURI.new/1\u003c/code\u003e is not affected on Erlang/OTP 27.3.4.17, 28.5.0.6, 29.0.6 and later, where \u003ccode\u003e:uri_string\u003c/code\u003e limits the port to 5 digits.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Reject long URLs before parsing them, for example anything above a few kilobytes, and reject a parsed `port` above 65535 before rendering or using the `%URI{}`. `URI.new/1` is not affected on Erlang/OTP 27.3.4.17, 28.5.0.6, 29.0.6 and later, where `:uri_string` limits the port to 5 digits."
                }
              ],
              "value": "Reject long URLs before parsing them, for example anything above a few kilobytes, and reject a parsed port above 65535 before rendering or using the %URI{}. URI.new/1 is not affected on Erlang/OTP 27.3.4.17, 28.5.0.6, 29.0.6 and later, where :uri_string limits the port to 5 digits."
            }
          ],
          "x_proofOfConcept": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003col\u003e\n\u003cli\u003eBuild \u003ccode\u003eurl = \"http://example.com:\" \u0026lt;\u0026gt; String.duplicate(\"9\", 400_000)\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eURI.parse(url)\u003c/code\u003e returns in about 30 ms.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eURI.to_string(URI.parse(url))\u003c/code\u003e takes 2,613 ms on OTP 27.3.4.18, 2,604 ms on OTP 28.5 and 108 ms on OTP 29.1.1. With 200,000 digits it takes 654 ms on OTP 27 and 28, so the cost grows 4x per doubling.\u003c/li\u003e\n\u003cli\u003eThe same digits placed in the path instead of the port parse and render in 1 ms or less.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eURI.parse(\"http://example.com:65536\").port\u003c/code\u003e returns \u003ccode\u003e65536\u003c/code\u003e, and a port of 1,262,593 digits raises \u003ccode\u003eSystemLimitError\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "1. Build `url = \"http://example.com:\" \u003c\u003e String.duplicate(\"9\", 400_000)`.\n2. `URI.parse(url)` returns in about 30 ms.\n3. `URI.to_string(URI.parse(url))` takes 2,613 ms on OTP 27.3.4.18, 2,604 ms on OTP 28.5 and 108 ms on OTP 29.1.1. With 200,000 digits it takes 654 ms on OTP 27 and 28, so the cost grows 4x per doubling.\n4. The same digits placed in the path instead of the port parse and render in 1 ms or less.\n5. `URI.parse(\"http://example.com:65536\").port` returns `65536`, and a port of 1,262,593 digits raises `SystemLimitError`."
                }
              ],
              "value": "* Build url = \"http://example.com:\" \u003c\u003e String.duplicate(\"9\", 400_000).\n* URI.parse(url) returns in about 30 ms.\n* URI.to_string(URI.parse(url)) takes 2,613 ms on OTP 27.3.4.18, 2,604 ms on OTP 28.5 and 108 ms on OTP 29.1.1. With 200,000 digits it takes 654 ms on OTP 27 and 28, so the cost grows 4x per doubling.\n* The same digits placed in the path instead of the port parse and render in 1 ms or less.\n* URI.parse(\"http://example.com:65536\").port returns 65536, and a port of 1,262,593 digits raises SystemLimitError."
            }
          ],
          "x_technicalAnalysis": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003e\u003cstrong\u003e1. Unbounded conversion\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003esplit_authority/1\u003c/code\u003e matches the port with the regex \u003ccode\u003e(:(\\d*))?\u003c/code\u003e and converts it with \u003ccode\u003eString.to_integer/1\u003c/code\u003e. Nothing bounds the digit run, so the VM builds an integer of up to 1,262,592 decimal digits (about 30 ms to parse 400,000 digits) and raises \u003ccode\u003eSystemLimitError\u003c/code\u003e above that, which escapes \u003ccode\u003eURI.parse/1\u003c/code\u003e instead of producing a \u003ccode\u003e%URI{}\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e2. Render cost\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eURI.to_string/1\u003c/code\u003e renders the port with \u003ccode\u003eInteger.to_string/1\u003c/code\u003e, and \u003ccode\u003einspect/1\u003c/code\u003e renders it the same way, both through \u003ccode\u003e:erlang.integer_to_binary/1\u003c/code\u003e. That conversion is quadratic on OTP 27, 28 and 29.0 and sub-quadratic from OTP 29.1 (\u003ca href=\"https://github.com/erlang/otp/pull/11074\" rel=\"noopener noreferrer\"\u003ehttps://github.com/erlang/otp/pull/11074\u003c/a\u003e). Per GHSA-8qw4-2chm-mvj2 it occupies a dirty CPU scheduler for its full duration.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e3. Comparison with \u003ccode\u003eURI.new/1\u003c/code\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eErlang/OTP limited the port to 5 bytes in \u003ccode\u003e:uri_string.parse/1\u003c/code\u003e as part of GHSA-8qw4-2chm-mvj2. \u003ccode\u003eURI.new/1\u003c/code\u003e and \u003ccode\u003eURI.new!/1\u003c/code\u003e use \u003ccode\u003e:uri_string\u003c/code\u003e and get that limit on patched OTP versions. \u003ccode\u003eURI.parse/1\u003c/code\u003e does its own parsing and does not.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4. Reach\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eHeader-sourced URLs are bounded by HTTP server limits. Form and JSON body fields are not: Plug\u0027s default JSON body limit is 8 MB. One observed library path: a user-supplied URL passed to Req inside a Bandit request exits with the port in its arguments, and Bandit\u0027s error logging formats it, which took 2.8 seconds at 400,000 digits on OTP 28.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "**1. Unbounded conversion**\n\n`split_authority/1` matches the port with the regex `(:(\\d*))?` and converts it with `String.to_integer/1`. Nothing bounds the digit run, so the VM builds an integer of up to 1,262,592 decimal digits (about 30 ms to parse 400,000 digits) and raises `SystemLimitError` above that, which escapes `URI.parse/1` instead of producing a `%URI{}`.\n\n**2. Render cost**\n\n`URI.to_string/1` renders the port with `Integer.to_string/1`, and `inspect/1` renders it the same way, both through `:erlang.integer_to_binary/1`. That conversion is quadratic on OTP 27, 28 and 29.0 and sub-quadratic from OTP 29.1 (https://github.com/erlang/otp/pull/11074). Per GHSA-8qw4-2chm-mvj2 it occupies a dirty CPU scheduler for its full duration.\n\n**3. Comparison with `URI.new/1`**\n\nErlang/OTP limited the port to 5 bytes in `:uri_string.parse/1` as part of GHSA-8qw4-2chm-mvj2. `URI.new/1` and `URI.new!/1` use `:uri_string` and get that limit on patched OTP versions. `URI.parse/1` does its own parsing and does not.\n\n**4. Reach**\n\nHeader-sourced URLs are bounded by HTTP server limits. Form and JSON body fields are not: Plug\u0027s default JSON body limit is 8 MB. One observed library path: a user-supplied URL passed to Req inside a Bandit request exits with the port in its arguments, and Bandit\u0027s error logging formats it, which took 2.8 seconds at 400,000 digits on OTP 28."
                }
              ],
              "value": "1. Unbounded conversion\n\nsplit_authority/1 matches the port with the regex (:(\\d*))? and converts it with String.to_integer/1. Nothing bounds the digit run, so the VM builds an integer of up to 1,262,592 decimal digits (about 30 ms to parse 400,000 digits) and raises SystemLimitError above that, which escapes URI.parse/1 instead of producing a %URI{}.\n\n2. Render cost\n\nURI.to_string/1 renders the port with Integer.to_string/1, and inspect/1 renders it the same way, both through :erlang.integer_to_binary/1. That conversion is quadratic on OTP 27, 28 and 29.0 and sub-quadratic from OTP 29.1 (https://github.com/erlang/otp/pull/11074). Per GHSA-8qw4-2chm-mvj2 it occupies a dirty CPU scheduler for its full duration.\n\n3. Comparison with URI.new/1\n\nErlang/OTP limited the port to 5 bytes in :uri_string.parse/1 as part of GHSA-8qw4-2chm-mvj2. URI.new/1 and URI.new!/1 use :uri_string and get that limit on patched OTP versions. URI.parse/1 does its own parsing and does not.\n\n4. Reach\n\nHeader-sourced URLs are bounded by HTTP server limits. Form and JSON body fields are not: Plug\u0027s default JSON body limit is 8 MB. One observed library path: a user-supplied URL passed to Req inside a Bandit request exits with the port in its arguments, and Bandit\u0027s error logging formats it, which took 2.8 seconds at 400,000 digits on OTP 28."
            }
          ]
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-109821",
        "datePublished": "2026-10-30T08:54:07.752Z",
        "dateReserved": "2026-10-23T14:00:01.560Z",
        "dateUpdated": "2026-10-30T16:52:43.052Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-75758 (GCVE-0-2026-75758)

    Vulnerability from cvelistv5 – Published: 2026-08-28 11:28 – Updated: 2026-08-28 18:29
    VLAI
    Title
    Unbounded recursion between Inspect.List charlist rendering and List.to_string/1 error path in Elixir
    Summary
    Uncontrolled Recursion vulnerability in the Elixir standard library allows an attacker who controls a list passed to inspect/1, List.to_string/1, or List.to_charlist/1 to exhaust a BEAM node's memory. Inspect.List's charlist branch in lib/elixir/lib/inspect.ex classifies a list as a charlist using List.ascii_printable?/2, which examines only the first :printable_limit (4096 by default) elements, and then calls IO.chardata_to_string/1 on the whole term. A list whose printable prefix exceeds that limit but which contains a later element that is not a code point (an atom, an out-of-range integer, or an improper tail) is therefore mis-classified, and the conversion raises ArgumentError. That conversion runs inside List.to_string/1, whose rescue clause builds its message by interpolating inspect(list), which re-enters the same branch and raises again. The nested inspection is an argument to raise, so the recursion is not in tail position and every level is retained: the process stack grows monotonically while each cycle re-walks the list, until the process is killed by max_heap_size or, by default, the node runs out of memory. List.to_charlist/1 has the same rescue shape. Below the printable limit the inner inspect/1 sees the invalid element within its counter and renders the list in ordinary bracket form, so a single ArgumentError is raised and no recursion occurs. This issue affects elixir: from 1.15.0-rc.0 before 1.18.5, from 1.19.0-rc.0 before 1.19.6, and from 1.20.0-rc.0 before 1.20.4.
    SSVC
    Exploitation: poc Automatable: no Technical Impact: partial
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-08-28 18:28 UTC
    CWE
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 97/100 over 352 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-lang elixir Affected: 1.15.0-rc.0 , < 1.18.5 (semver)
    Affected: 1.19.0-rc.0 , < 1.19.6 (semver)
    Affected: 1.20.0-rc.0 , < 1.20.4 (semver)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-lang elixir Affected: 9718f2b90ce086ff8614d5782f4ec528495c98dd , < * (git)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-75758",
                    "options": [
                      {
                        "Exploitation": "poc"
                      },
                      {
                        "Automatable": "no"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-08-28T18:28:19.564474Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-08-28T18:29:23.745Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "references": [
              {
                "tags": [
                  "exploit"
                ],
                "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-jf5q-v438-665c"
              }
            ],
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Inspect.List\u0027",
                "\u0027Elixir.List\u0027",
                "\u0027Elixir.IO\u0027"
              ],
              "packageName": "elixir",
              "packageURL": "pkg:otp/elixir?repository_url=https:%2F%2Fgithub.com%2Felixir-lang%2Felixir\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Felixir-lang%2Felixir.git",
              "product": "elixir",
              "programFiles": [
                "lib/inspect.ex",
                "lib/list.ex",
                "lib/io.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Inspect.List\u0027:inspect/2"
                },
                {
                  "name": "\u0027Elixir.List\u0027:to_string/1"
                },
                {
                  "name": "\u0027Elixir.List\u0027:to_charlist/1"
                },
                {
                  "name": "\u0027Elixir.List\u0027:ascii_printable?/2"
                },
                {
                  "name": "\u0027Elixir.IO\u0027:chardata_to_string/1"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "lessThan": "1.18.5",
                  "status": "affected",
                  "version": "1.15.0-rc.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.19.6",
                  "status": "affected",
                  "version": "1.19.0-rc.0",
                  "versionType": "semver"
                },
                {
                  "lessThan": "1.20.4",
                  "status": "affected",
                  "version": "1.20.0-rc.0",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Inspect.List\u0027",
                "\u0027Elixir.List\u0027",
                "\u0027Elixir.IO\u0027"
              ],
              "packageName": "elixir-lang/elixir",
              "packageURL": "pkg:github/elixir-lang/elixir",
              "product": "elixir",
              "programFiles": [
                "lib/elixir/lib/inspect.ex",
                "lib/elixir/lib/list.ex",
                "lib/elixir/lib/io.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Inspect.List\u0027:inspect/2"
                },
                {
                  "name": "\u0027Elixir.List\u0027:to_string/1"
                },
                {
                  "name": "\u0027Elixir.List\u0027:to_charlist/1"
                },
                {
                  "name": "\u0027Elixir.List\u0027:ascii_printable?/2"
                },
                {
                  "name": "\u0027Elixir.IO\u0027:chardata_to_string/1"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "changes": [
                    {
                      "at": "1eff1acffd49bdcc0d7f57ca74c1603328eed68a",
                      "status": "unaffected"
                    },
                    {
                      "at": "5230d73968f1b4969d2a2646786fa6c71475f5cc",
                      "status": "unaffected"
                    },
                    {
                      "at": "a983c8c043b1fbf1d95df78a29149222dac2988c",
                      "status": "unaffected"
                    },
                    {
                      "at": "0bba5887577b1e328da825bd018815fdc519685a",
                      "status": "unaffected"
                    }
                  ],
                  "lessThan": "*",
                  "status": "affected",
                  "version": "9718f2b90ce086ff8614d5782f4ec528495c98dd",
                  "versionType": "git"
                }
              ]
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.18.5",
                      "versionStartIncluding": "1.15.0-rc.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.19.6",
                      "versionStartIncluding": "1.19.0-rc.0",
                      "vulnerable": true
                    },
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.20.4",
                      "versionStartIncluding": "1.20.0-rc.0",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "reporter",
              "value": "Thomas Cioppettini / Scalpel Software Inc"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Jos\u00e9 Valim"
            },
            {
              "lang": "en",
              "type": "coordinator",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eUncontrolled Recursion vulnerability in the Elixir standard library allows an attacker who controls a list passed to \u003ccode\u003einspect/1\u003c/code\u003e, \u003ccode\u003eList.to_string/1\u003c/code\u003e, or \u003ccode\u003eList.to_charlist/1\u003c/code\u003e to exhaust a BEAM node\u0027s memory.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eInspect.List\u003c/code\u003e\u0027s charlist branch in \u003ccode\u003elib/elixir/lib/inspect.ex\u003c/code\u003e classifies a list as a charlist using \u003ccode\u003eList.ascii_printable?/2\u003c/code\u003e, which examines only the first \u003ccode\u003e:printable_limit\u003c/code\u003e (4096 by default) elements, and then calls \u003ccode\u003eIO.chardata_to_string/1\u003c/code\u003e on the whole term. A list whose printable prefix exceeds that limit but which contains a later element that is not a code point (an atom, an out-of-range integer, or an improper tail) is therefore mis-classified, and the conversion raises \u003ccode\u003eArgumentError\u003c/code\u003e. That conversion runs inside \u003ccode\u003eList.to_string/1\u003c/code\u003e, whose rescue clause builds its message by interpolating \u003ccode\u003einspect(list)\u003c/code\u003e, which re-enters the same branch and raises again. The nested inspection is an argument to \u003ccode\u003eraise\u003c/code\u003e, so the recursion is not in tail position and every level is retained: the process stack grows monotonically while each cycle re-walks the list, until the process is killed by \u003ccode\u003emax_heap_size\u003c/code\u003e or, by default, the node runs out of memory. \u003ccode\u003eList.to_charlist/1\u003c/code\u003e has the same rescue shape.\u003c/p\u003e\n\u003cp\u003eBelow the printable limit the inner \u003ccode\u003einspect/1\u003c/code\u003e sees the invalid element within its counter and renders the list in ordinary bracket form, so a single \u003ccode\u003eArgumentError\u003c/code\u003e is raised and no recursion occurs.\u003c/p\u003e\n\u003cp\u003eThis issue affects elixir: from 1.15.0-rc.0 before 1.18.5, from 1.19.0-rc.0 before 1.19.6, and from 1.20.0-rc.0 before 1.20.4.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Uncontrolled Recursion vulnerability in the Elixir standard library allows an attacker who controls a list passed to `inspect/1`, `List.to_string/1`, or `List.to_charlist/1` to exhaust a BEAM node\u0027s memory.\n\n`Inspect.List`\u0027s charlist branch in `lib/elixir/lib/inspect.ex` classifies a list as a charlist using `List.ascii_printable?/2`, which examines only the first `:printable_limit` (4096 by default) elements, and then calls `IO.chardata_to_string/1` on the whole term. A list whose printable prefix exceeds that limit but which contains a later element that is not a code point (an atom, an out-of-range integer, or an improper tail) is therefore mis-classified, and the conversion raises `ArgumentError`. That conversion runs inside `List.to_string/1`, whose rescue clause builds its message by interpolating `inspect(list)`, which re-enters the same branch and raises again. The nested inspection is an argument to `raise`, so the recursion is not in tail position and every level is retained: the process stack grows monotonically while each cycle re-walks the list, until the process is killed by `max_heap_size` or, by default, the node runs out of memory. `List.to_charlist/1` has the same rescue shape.\n\nBelow the printable limit the inner `inspect/1` sees the invalid element within its counter and renders the list in ordinary bracket form, so a single `ArgumentError` is raised and no recursion occurs.\n\nThis issue affects elixir: from 1.15.0-rc.0 before 1.18.5, from 1.19.0-rc.0 before 1.19.6, and from 1.20.0-rc.0 before 1.20.4."
                }
              ],
              "value": "Uncontrolled Recursion vulnerability in the Elixir standard library allows an attacker who controls a list passed to inspect/1, List.to_string/1, or List.to_charlist/1 to exhaust a BEAM node\u0027s memory.\n\nInspect.List\u0027s charlist branch in lib/elixir/lib/inspect.ex classifies a list as a charlist using List.ascii_printable?/2, which examines only the first :printable_limit (4096 by default) elements, and then calls IO.chardata_to_string/1 on the whole term. A list whose printable prefix exceeds that limit but which contains a later element that is not a code point (an atom, an out-of-range integer, or an improper tail) is therefore mis-classified, and the conversion raises ArgumentError. That conversion runs inside List.to_string/1, whose rescue clause builds its message by interpolating inspect(list), which re-enters the same branch and raises again. The nested inspection is an argument to raise, so the recursion is not in tail position and every level is retained: the process stack grows monotonically while each cycle re-walks the list, until the process is killed by max_heap_size or, by default, the node runs out of memory. List.to_charlist/1 has the same rescue shape.\n\nBelow the printable limit the inner inspect/1 sees the invalid element within its counter and renders the list in ordinary bracket form, so a single ArgumentError is raised and no recursion occurs.\n\nThis issue affects elixir: from 1.15.0-rc.0 before 1.18.5, from 1.19.0-rc.0 before 1.19.6, and from 1.20.0-rc.0 before 1.20.4."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-130",
              "descriptions": [
                {
                  "lang": "en",
                  "value": "CAPEC-130 Excessive Allocation"
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "LOCAL",
                "baseScore": 5.9,
                "baseSeverity": "MEDIUM",
                "privilegesRequired": "NONE",
                "providerUrgency": "NOT_DEFINED",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "NONE",
                "valueDensity": "NOT_DEFINED",
                "vectorString": "CVSS:4.0/AV:L/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-08-28T11:28:53.505Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "tags": [
                "vendor-advisory",
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-jf5q-v438-665c"
            },
            {
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-75758.html"
            },
            {
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-75758"
            },
            {
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/1eff1acffd49bdcc0d7f57ca74c1603328eed68a"
            },
            {
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/5230d73968f1b4969d2a2646786fa6c71475f5cc"
            },
            {
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/a983c8c043b1fbf1d95df78a29149222dac2988c"
            },
            {
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/0bba5887577b1e328da825bd018815fdc519685a"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "Unbounded recursion between Inspect.List charlist rendering and List.to_string/1 error path in Elixir",
          "workarounds": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eInstall a global inspect function that renders lists as lists rather than inferring charlists, by calling \u003ccode\u003eInspect.Opts.default_inspect_fun/1\u003c/code\u003e with \u003ccode\u003efn term, opts -\u0026gt; Inspect.inspect(term, %{opts | charlists: :as_lists}) end\u003c/code\u003e from the application\u0027s start callback. This keeps the charlist branch from being entered at all, so the mis-inferred conversion never raises and the error path cannot recurse.\u003c/p\u003e\n\u003cp\u003eThe trade-off is global: charlists then render in bracket form, so \u003ccode\u003e~c\"abc\"\u003c/code\u003e inspects as \u003ccode\u003e[97, 98, 99]\u003c/code\u003e everywhere in the application, including in logs and error reports.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Install a global inspect function that renders lists as lists rather than inferring charlists, by calling `Inspect.Opts.default_inspect_fun/1` with `fn term, opts -\u003e Inspect.inspect(term, %{opts | charlists: :as_lists}) end` from the application\u0027s start callback. This keeps the charlist branch from being entered at all, so the mis-inferred conversion never raises and the error path cannot recurse.\n\nThe trade-off is global: charlists then render in bracket form, so `~c\"abc\"` inspects as `[97, 98, 99]` everywhere in the application, including in logs and error reports."
                }
              ],
              "value": "Install a global inspect function that renders lists as lists rather than inferring charlists, by calling Inspect.Opts.default_inspect_fun/1 with fn term, opts -\u003e Inspect.inspect(term, %{opts | charlists: :as_lists}) end from the application\u0027s start callback. This keeps the charlist branch from being entered at all, so the mis-inferred conversion never raises and the error path cannot recurse.\n\nThe trade-off is global: charlists then render in bracket form, so ~c\"abc\" inspects as [97, 98, 99] everywhere in the application, including in logs and error reports."
            }
          ]
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-75758",
        "datePublished": "2026-08-28T11:28:53.505Z",
        "dateReserved": "2026-08-19T09:00:01.955Z",
        "dateUpdated": "2026-08-28T18:29:23.745Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }

    CVE-2026-49762 (GCVE-0-2026-49762)

    Vulnerability from cvelistv5 – Published: 2026-06-09 14:04 – Updated: 2026-10-06 08:35
    VLAI
    Title
    Unbounded integer parsing in the Version module enables CPU and memory exhaustion denial of service
    Summary
    Uncontrolled Resource Consumption vulnerability in the Elixir standard library's Version module allows an attacker who controls a version string to cause a denial of service through CPU and memory exhaustion. The version parser converts numeric version components (major, minor, patch and numeric pre-release/build identifiers) to integers without bounding their length. A single large all-digit component therefore forces a super-linear, non-yielding base-10 to arbitrary-precision integer conversion (String.to_integer/1, i.e. :erlang.binary_to_integer/1) that pins a BEAM scheduler, and a larger component raises an uncaught SystemLimitError that crashes the calling process. A single moderately sized string (around one megabyte) is enough; no authentication is required. This is reachable from the public entry points Version.parse/1, Version.parse!/1, Version.match?/3, Version.compare/2, and Version.parse_requirement/1, which applications routinely call on untrusted input such as HTTP parameters, dependency-manifest fields, and package metadata. This vulnerability is associated with program files lib/version.ex and program routines 'Elixir.Version.Parser':parse_digits/2. This issue affects elixir: from 0.9.3 before 1.20.1.
    SSVC
    Exploitation: none Automatable: no Technical Impact: partial
    CISA Coordinator · CISA-ADP (v2.0.3)
    Decision recorded 2026-06-09 14:48 UTC
    CWE
    • CWE-400 - Uncontrolled Resource Consumption
    Assigner
    EEF CNA under the mitre root
    CNA scorecard A 97/100 over 352 records in the last 180 days details
    Impacted products
    Vendor Product Version
    elixir-lang elixir Affected: 0.9.3 , < 1.20.1 (semver)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    elixir-lang elixir Affected: 057691722eb8cfe315f5606358954a2b173ff7a3 , < * (git)
        cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*
    Create a notification for this product.
    Date Public
    2026-06-09 14:04
    Show details on NVD website

    {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-49762",
                    "options": [
                      {
                        "Exploitation": "none"
                      },
                      {
                        "Automatable": "no"
                      },
                      {
                        "Technical Impact": "partial"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-06-09T14:48:56.343391Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-06-09T14:49:07.338Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Version\u0027",
                "\u0027Elixir.Version.Parser\u0027"
              ],
              "packageName": "elixir",
              "packageURL": "pkg:otp/elixir?repository_url=https:%2F%2Fgithub.com%2Felixir-lang%2Felixir\u0026vcs_url=git%2Bhttps:%2F%2Fgithub.com%2Felixir-lang%2Felixir.git",
              "product": "elixir",
              "programFiles": [
                "lib/version.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Version\u0027:parse/1"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:parse!/1"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:match?/3"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:compare/2"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:parse_requirement/1"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:parse_version/2"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:parse_digits/2"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:require_digits/1"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:convert_parts_to_integer/2"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "lessThan": "1.20.1",
                  "status": "affected",
                  "version": "0.9.3",
                  "versionType": "semver"
                }
              ]
            },
            {
              "collectionURL": "https://github.com",
              "cpes": [
                "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*"
              ],
              "defaultStatus": "unaffected",
              "modules": [
                "\u0027Elixir.Version\u0027",
                "\u0027Elixir.Version.Parser\u0027"
              ],
              "packageName": "elixir-lang/elixir",
              "packageURL": "pkg:github/elixir-lang/elixir",
              "product": "elixir",
              "programFiles": [
                "lib/elixir/lib/version.ex"
              ],
              "programRoutines": [
                {
                  "name": "\u0027Elixir.Version\u0027:parse/1"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:parse!/1"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:match?/3"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:compare/2"
                },
                {
                  "name": "\u0027Elixir.Version\u0027:parse_requirement/1"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:parse_version/2"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:parse_digits/2"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:require_digits/1"
                },
                {
                  "name": "\u0027Elixir.Version.Parser\u0027:convert_parts_to_integer/2"
                }
              ],
              "repo": "https://github.com/elixir-lang/elixir",
              "vendor": "elixir-lang",
              "versions": [
                {
                  "changes": [
                    {
                      "at": "c64417d72fd5c7d09e963ca3ac5fa2b140978d9e",
                      "status": "unaffected"
                    },
                    {
                      "at": "64e6707233464c3e55a9ca6b44019aed25b0390a",
                      "status": "unaffected"
                    }
                  ],
                  "lessThan": "*",
                  "status": "affected",
                  "version": "057691722eb8cfe315f5606358954a2b173ff7a3",
                  "versionType": "git"
                }
              ]
            }
          ],
          "cpeApplicability": [
            {
              "nodes": [
                {
                  "cpeMatch": [
                    {
                      "criteria": "cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*",
                      "versionEndExcluding": "1.20.1",
                      "versionStartIncluding": "0.9.3",
                      "vulnerable": true
                    }
                  ],
                  "negate": false,
                  "operator": "OR"
                }
              ],
              "operator": "AND"
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "finder",
              "value": "Peter Ullrich"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Jos\u00e9 Valim"
            },
            {
              "lang": "en",
              "type": "remediation reviewer",
              "value": "Eric Meadows-J\u00f6nsson"
            },
            {
              "lang": "en",
              "type": "analyst",
              "value": "Jonatan M\u00e4nnchen / EEF"
            }
          ],
          "datePublic": "2026-06-09T14:04:07.000Z",
          "descriptions": [
            {
              "lang": "en",
              "supportingMedia": [
                {
                  "base64": false,
                  "type": "text/html",
                  "value": "\u003cp\u003eUncontrolled Resource Consumption vulnerability in the Elixir standard library\u0027s \u003ccode\u003eVersion\u003c/code\u003e module allows an attacker who controls a version string to cause a denial of service through CPU and memory exhaustion.\u003c/p\u003e\n\u003cp\u003eThe version parser converts numeric version components (major, minor, patch and numeric pre-release/build identifiers) to integers without bounding their length. A single large all-digit component therefore forces a super-linear, non-yielding base-10 to arbitrary-precision integer conversion (\u003ccode\u003eString.to_integer/1\u003c/code\u003e, i.e. \u003ccode\u003e:erlang.binary_to_integer/1\u003c/code\u003e) that pins a BEAM scheduler, and a larger component raises an uncaught \u003ccode\u003eSystemLimitError\u003c/code\u003e that crashes the calling process. A single moderately sized string (around one megabyte) is enough; no authentication is required.\u003c/p\u003e\n\u003cp\u003eThis is reachable from the public entry points \u003ccode\u003eVersion.parse/1\u003c/code\u003e, \u003ccode\u003eVersion.parse!/1\u003c/code\u003e, \u003ccode\u003eVersion.match?/3\u003c/code\u003e, \u003ccode\u003eVersion.compare/2\u003c/code\u003e, and \u003ccode\u003eVersion.parse_requirement/1\u003c/code\u003e, which applications routinely call on untrusted input such as HTTP parameters, dependency-manifest fields, and package metadata.\u003c/p\u003e\n\u003cp\u003eThis vulnerability is associated with program files \u003ccode\u003elib/version.ex\u003c/code\u003e and program routines \u003ccode\u003e\u0027Elixir.Version.Parser\u0027:parse_digits/2\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThis issue affects elixir: from 0.9.3 before 1.20.1.\u003c/p\u003e"
                },
                {
                  "base64": false,
                  "type": "text/markdown",
                  "value": "Uncontrolled Resource Consumption vulnerability in the Elixir standard library\u0027s `Version` module allows an attacker who controls a version string to cause a denial of service through CPU and memory exhaustion.\n\nThe version parser converts numeric version components (major, minor, patch and numeric pre-release/build identifiers) to integers without bounding their length. A single large all-digit component therefore forces a super-linear, non-yielding base-10 to arbitrary-precision integer conversion (`String.to_integer/1`, i.e. `:erlang.binary_to_integer/1`) that pins a BEAM scheduler, and a larger component raises an uncaught `SystemLimitError` that crashes the calling process. A single moderately sized string (around one megabyte) is enough; no authentication is required.\n\nThis is reachable from the public entry points `Version.parse/1`, `Version.parse!/1`, `Version.match?/3`, `Version.compare/2`, and `Version.parse_requirement/1`, which applications routinely call on untrusted input such as HTTP parameters, dependency-manifest fields, and package metadata.\n\nThis vulnerability is associated with program files `lib/version.ex` and program routines `\u0027Elixir.Version.Parser\u0027:parse_digits/2`.\n\nThis issue affects elixir: from 0.9.3 before 1.20.1."
                }
              ],
              "value": "Uncontrolled Resource Consumption vulnerability in the Elixir standard library\u0027s Version module allows an attacker who controls a version string to cause a denial of service through CPU and memory exhaustion.\n\nThe version parser converts numeric version components (major, minor, patch and numeric pre-release/build identifiers) to integers without bounding their length. A single large all-digit component therefore forces a super-linear, non-yielding base-10 to arbitrary-precision integer conversion (String.to_integer/1, i.e. :erlang.binary_to_integer/1) that pins a BEAM scheduler, and a larger component raises an uncaught SystemLimitError that crashes the calling process. A single moderately sized string (around one megabyte) is enough; no authentication is required.\n\nThis is reachable from the public entry points Version.parse/1, Version.parse!/1, Version.match?/3, Version.compare/2, and Version.parse_requirement/1, which applications routinely call on untrusted input such as HTTP parameters, dependency-manifest fields, and package metadata.\n\nThis vulnerability is associated with program files lib/version.ex and program routines \u0027Elixir.Version.Parser\u0027:parse_digits/2.\n\nThis issue affects elixir: from 0.9.3 before 1.20.1."
            }
          ],
          "impacts": [
            {
              "capecId": "CAPEC-130",
              "descriptions": [
                {
                  "lang": "en",
                  "value": "CAPEC-130 Excessive Allocation"
                }
              ]
            }
          ],
          "metrics": [
            {
              "cvssV4_0": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "NONE",
                "attackVector": "LOCAL",
                "baseScore": 5.1,
                "baseSeverity": "MEDIUM",
                "privilegesRequired": "NONE",
                "providerUrgency": "NOT_DEFINED",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "NONE",
                "valueDensity": "NOT_DEFINED",
                "vectorString": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
                "version": "4.0",
                "vulnAvailabilityImpact": "LOW",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "GENERAL"
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-400",
                  "description": "CWE-400 Uncontrolled Resource Consumption",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-10-06T08:35:59.691Z",
            "orgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "shortName": "EEF"
          },
          "references": [
            {
              "name": "GitHub Advisory",
              "tags": [
                "related",
                "vendor-advisory"
              ],
              "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-w2h8-8x3g-278p"
            },
            {
              "name": "EEF CNA record for CVE-2026-49762",
              "tags": [
                "related"
              ],
              "url": "https://cna.erlef.org/cves/CVE-2026-49762.html"
            },
            {
              "name": "OSV record EEF-CVE-2026-49762",
              "tags": [
                "related"
              ],
              "url": "https://osv.dev/vulnerability/EEF-CVE-2026-49762"
            },
            {
              "name": "Introducing commit 0576917 in elixir-lang/elixir",
              "tags": [
                "related"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/057691722eb8cfe315f5606358954a2b173ff7a3"
            },
            {
              "name": "Fix commit c64417d in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/c64417d72fd5c7d09e963ca3ac5fa2b140978d9e"
            },
            {
              "name": "Fix commit 64e6707 in elixir-lang/elixir",
              "tags": [
                "patch"
              ],
              "url": "https://github.com/elixir-lang/elixir/commit/64e6707233464c3e55a9ca6b44019aed25b0390a"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "Unbounded integer parsing in the Version module enables CPU and memory exhaustion denial of service"
        }
      },
      "cveMetadata": {
        "assignerOrgId": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "assignerShortName": "EEF",
        "cveId": "CVE-2026-49762",
        "datePublished": "2026-06-09T14:04:07.405Z",
        "dateReserved": "2026-06-01T13:45:22.449Z",
        "dateUpdated": "2026-10-06T08:35:59.691Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }