diff --git a/advisories/github-reviewed/2026/05/GHSA-rhv4-8758-jx7v/GHSA-rhv4-8758-jx7v.json b/advisories/github-reviewed/2026/05/GHSA-rhv4-8758-jx7v/GHSA-rhv4-8758-jx7v.json index 94f3f5f21c549..54783ab071091 100644 --- a/advisories/github-reviewed/2026/05/GHSA-rhv4-8758-jx7v/GHSA-rhv4-8758-jx7v.json +++ b/advisories/github-reviewed/2026/05/GHSA-rhv4-8758-jx7v/GHSA-rhv4-8758-jx7v.json @@ -1,13 +1,13 @@ { "schema_version": "1.4.0", "id": "GHSA-rhv4-8758-jx7v", - "modified": "2026-05-12T15:09:20Z", + "modified": "2026-05-12T15:09:21Z", "published": "2026-05-12T15:09:20Z", "aliases": [ "CVE-2026-32686" ], "summary": "Decimal: Unbounded exponent in `Decimal.new` enables unauthenticated DoS", - "details": "Summary\n`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.\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's 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 — 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.", + "details": "Summary\n`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.\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's 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 — 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.\n\n### Note on the security fixes done in version 2.4.0\nWhile `2.4.0` has the changes to mitigate this issue it's not considered as a patched version because it doesn't have them enabled by default.", "severity": [ { "type": "CVSS_V4", @@ -56,6 +56,10 @@ "type": "PACKAGE", "url": "https://github.com/ericmj/decimal" }, + { + "type": "WEB", + "url": "https://github.com/ericmj/decimal/releases/tag/v2.4.0" + }, { "type": "WEB", "url": "https://github.com/ericmj/decimal/releases/tag/v3.0.0"