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 347 records in the last 180 days details
Impacted products
Vendor Product Version CPE status
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",
  "vulnerability-lookup:meta": {
    "epss": {
      "cve": "CVE-2026-109821",
      "date": "2026-10-31",
      "epss": "0.00496",
      "percentile": "0.42577"
    },
    "microsoft_vex": {
      "current_release_date": "2026-10-31T01:04:29.000Z",
      "cve": "CVE-2026-109821",
      "id": "msrc_CVE-2026-109821",
      "initial_release_date": "2026-10-31T01:04:29.000Z",
      "product_status:under_investigation": "1",
      "source": "Microsoft CSAF VEX",
      "status": "final",
      "title": "Unbounded port parsing in URI.parse/1 enables CPU exhaustion denial of service in Elixir",
      "url": "https://msrc.microsoft.com/csaf/vex/2026/msrc_cve-2026-109821.json",
      "version": "1"
    },
    "nvd": {
      "cve": {
        "affected": [
          {
            "affectedData": [
              {
                "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"
                  }
                ]
              }
            ],
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db"
          }
        ],
        "cveTags": [],
        "descriptions": [
          {
            "lang": "en",
            "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."
          }
        ],
        "id": "CVE-2026-109821",
        "lastModified": "2026-10-30T19:39:14.897",
        "metrics": {
          "cvssMetricV40": [
            {
              "cvssData": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "PRESENT",
                "attackVector": "NETWORK",
                "availabilityRequirement": "NOT_DEFINED",
                "baseScore": 6.3,
                "baseSeverity": "MEDIUM",
                "confidentialityRequirement": "NOT_DEFINED",
                "exploitMaturity": "NOT_DEFINED",
                "integrityRequirement": "NOT_DEFINED",
                "modifiedAttackComplexity": "NOT_DEFINED",
                "modifiedAttackRequirements": "NOT_DEFINED",
                "modifiedAttackVector": "NOT_DEFINED",
                "modifiedPrivilegesRequired": "NOT_DEFINED",
                "modifiedSubAvailabilityImpact": "NOT_DEFINED",
                "modifiedSubConfidentialityImpact": "NOT_DEFINED",
                "modifiedSubIntegrityImpact": "NOT_DEFINED",
                "modifiedUserInteraction": "NOT_DEFINED",
                "modifiedVulnAvailabilityImpact": "NOT_DEFINED",
                "modifiedVulnConfidentialityImpact": "NOT_DEFINED",
                "modifiedVulnIntegrityImpact": "NOT_DEFINED",
                "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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
                "version": "4.0",
                "vulnAvailabilityImpact": "LOW",
                "vulnConfidentialityImpact": "NONE",
                "vulnIntegrityImpact": "NONE",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
              "type": "Secondary"
            }
          ],
          "ssvcV203": [
            {
              "source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "ssvcData": {
                "id": "CVE-2026-109821",
                "options": [
                  {
                    "exploitation": "poc"
                  },
                  {
                    "automatable": "no"
                  },
                  {
                    "technicalImpact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-10-30T16:52:15.736711Z",
                "version": "2.0.3"
              }
            }
          ]
        },
        "published": "2026-10-30T09:17:12.143",
        "references": [
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://cna.erlef.org/cves/CVE-2026-109821.html"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://github.com/elixir-lang/elixir/commit/07d9abc3f3ac4c8d2ba8384bd102b6d6205b08b7"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://github.com/elixir-lang/elixir/commit/39801821eef2d06524e5ebf5a0e4b3c380ad23ae"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://github.com/elixir-lang/elixir/commit/9a39d64a4473d02681c486bc70681c972479d32b"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://github.com/elixir-lang/elixir/commit/b70f33a591b6ab8eb0fa4a7e00bac1c2ed4e0d58"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://github.com/elixir-lang/elixir/commit/d879b39d778fec2f475689573d5672720c4f2a21"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-327h-97cv-59xg"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://github.com/erlang/otp/security/advisories/GHSA-8qw4-2chm-mvj2"
          },
          {
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "url": "https://osv.dev/vulnerability/EEF-CVE-2026-109821"
          },
          {
            "source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
            "url": "https://github.com/elixir-lang/elixir/security/advisories/GHSA-327h-97cv-59xg"
          }
        ],
        "sourceIdentifier": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
        "vulnStatus": "Deferred",
        "weaknesses": [
          {
            "description": [
              {
                "lang": "en",
                "value": "CWE-400"
              },
              {
                "lang": "en",
                "value": "CWE-1284"
              }
            ],
            "source": "6b3ad84c-e1a6-4bf7-a703-f496b71e49db",
            "type": "Secondary"
          }
        ]
      }
    },
    "vulnrichment": {
      "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:38.267Z",
              "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"
    }
  }
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.