GHSA-RHV4-8758-JX7V

Vulnerability from github – Published: 2026-05-12 15:09 – Updated: 2026-05-12 15:09
VLAI?
Summary
Decimal: Unbounded exponent in `Decimal.new` enables unauthenticated DoS
Details

Summary decimal doesn't bound the exponent on parsed input, so something like "1e10000000" is parsed fine but then explodes the memory to more than 7GB if you run e.g. Decimal.add(Decimal.parse("1e10000000"), 1) because for positive exp, the function tail-recurses with coef * 10 and exp - 1 per iteration, growing the bignum coefficient by one digit each step. In the worst case, one request is enough to OOM the BEAM.

Details

Decimal.new/parse/cast happily store huge exponents. After that, a bunch of core paths allocate proportional to exp: - add/sub/div go through add_align, which calls pow10(exp1 - exp2) and builds a giant bignum (lib/decimal.ex:1734-1738, 1827). - to_string/2 with :normal (also :xsd and the String.Chars impl) does :lists.duplicate(exp, ?0) (lib/decimal.ex:1506, 1513). - to_integer/1 recurses coef * 10, exp - 1 once per unit of exp (lib/decimal.ex:1603-1605). - round/3 does the same :lists.duplicate trick on the exp difference (lib/decimal.ex:1850, 1874). - compare/3 with a threshold argument loops back into add/sub, so it's vulnerable too (lib/decimal.ex:331-332).

PoC

Any of these will hang or OOM the BEAM:

Decimal.add(Decimal.new("1"), Decimal.new("1e1000000000"))
Decimal.to_string(Decimal.new("1e1000000000"), :normal)
Decimal.to_integer(Decimal.new("1e1000000000"))
Decimal.round(Decimal.new("1e1000000000"))

Impact

Unauthenticated remote DoS. Anything that takes a user-supplied decimal (JSON, form field, Ecto :decimal field — basically everywhere) and then does arithmetic, rounding, to_integer, or to_string on it is exposed. One request can kill the node with a Out-of-Memory exception.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Hex",
        "name": "decimal"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.1.0"
            },
            {
              "fixed": "3.0.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-32686"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-12T15:09:20Z",
    "nvd_published_at": "2026-05-07T15:16:05Z",
    "severity": "MODERATE"
  },
  "details": "Summary\n`decimal` doesn\u0027t bound the exponent on parsed input, so something like `\"1e10000000\"` is parsed fine but then explodes the memory to more than 7GB if you run e.g. `Decimal.add(Decimal.parse(\"1e10000000\"), 1)` because for positive `exp`, the function tail-recurses with `coef * 10` and `exp - 1` per iteration, growing the bignum coefficient by one digit each step. In the worst case, one request is enough to OOM the BEAM.\n\n### Details\n`Decimal.new/parse/cast` happily store huge exponents. After that, a bunch of core paths allocate proportional to `exp`:\n- `add/sub/div` go through `add_align`, which calls `pow10(exp1 - exp2)` and builds a giant bignum (lib/decimal.ex:1734-1738, 1827).\n- `to_string/2` with `:normal` (also `:xsd` and the `String.Chars` impl) does `:lists.duplicate(exp, ?0)` (lib/decimal.ex:1506, 1513).\n- `to_integer/1` recurses `coef * 10`, `exp - 1` once per unit of `exp` (lib/decimal.ex:1603-1605).\n- `round/3` does the same `:lists.duplicate` trick on the exp difference (lib/decimal.ex:1850, 1874).\n- `compare/3` with a threshold argument loops back into `add`/`sub`, so it\u0027s vulnerable too (lib/decimal.ex:331-332).\n\n### PoC\nAny of these will hang or OOM the BEAM:\n```elixir\nDecimal.add(Decimal.new(\"1\"), Decimal.new(\"1e1000000000\"))\nDecimal.to_string(Decimal.new(\"1e1000000000\"), :normal)\nDecimal.to_integer(Decimal.new(\"1e1000000000\"))\nDecimal.round(Decimal.new(\"1e1000000000\"))\n```\n\n### Impact\nUnauthenticated remote DoS. Anything that takes a user-supplied decimal (JSON, form field, Ecto `:decimal` field \u2014 basically everywhere) and then does arithmetic, rounding, `to_integer`, or `to_string` on it is exposed. One request can kill the node with a Out-of-Memory exception.",
  "id": "GHSA-rhv4-8758-jx7v",
  "modified": "2026-05-12T15:09:20Z",
  "published": "2026-05-12T15:09:20Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/ericmj/decimal/security/advisories/GHSA-rhv4-8758-jx7v"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32686"
    },
    {
      "type": "WEB",
      "url": "https://github.com/ericmj/decimal/commit/6a523f3a73b8c9974540e21c7aa88f1258bb35ae"
    },
    {
      "type": "WEB",
      "url": "https://cna.erlef.org/cves/CVE-2026-32686.html"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/ericmj/decimal"
    },
    {
      "type": "WEB",
      "url": "https://github.com/ericmj/decimal/releases/tag/v3.0.0"
    },
    {
      "type": "WEB",
      "url": "https://osv.dev/vulnerability/EEF-CVE-2026-32686"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Decimal: Unbounded exponent in `Decimal.new` enables unauthenticated DoS"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…
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.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…