CVE-2026-115274: Unbounded integer parsing in JSON.decode/1 enables CPU and memory exhaustion denial of service in Elixir

Published Oct 30, 2026
·
Updated

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:binarytointeger/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.

Details

1. Default integer decoder

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 elixirjson module, whose default is fun erlang:binarytointeger/1. It now calls OTP's :json.decode/3, which has the same default. The JSON.decode/3 documentation names &String.tointeger/1 as the default, which converts the same way.

2. Unbounded conversion

The decoder scans the digits of an integer literal and passes the whole run to the conversion. Nothing limits the run's 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.

3. Reach

phx.new sets config :phoenix, :jsonlibrary, JSON, and the generated endpoint passes Phoenix.jsonlibrary() to Plug.Parsers as jsondecoder. 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 decodingintegerdigitlimit (1024 by default) before conversion.

Proof of concept

1. Build body = "[" <> Enum.mapjoin(1..7, ",", fn -> String.duplicate("9", 1000000) end) <> "]", a body of about 7 MB. 2. JSON.decode(body) returns {:ok, ...} after about one second of CPU. 3. JSON.decode(String.duplicate("9", 1000000)) takes 100 to 300 times as long as JSON.decode("\"" <> String.duplicate("9", 1000000) <> "\""). 4. JSON.decode(String.duplicate("9", 1400000)) raises SystemLimitError.

Impact

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.

Workarounds

Decode untrusted JSON with an integer decoder that rejects long digit runs before conversion:

elixir defmodule SafeJSON do def decode!(binary) do case JSON.decode(binary, :ok, integer: &safeinteger/1) do {decoded, :ok, ""} -> decoded -> raise ArgumentError, "invalid JSON" end end

defp safeinteger(digits) when bytesize(digits) <= 1024, do: String.tointeger(digits) defp safeinteger(digits), do: raise(ArgumentError, "integer too long") end

In Phoenix, pass SafeJSON to Plug.Parsers as jsondecoder instead of Phoenix.jsonlibrary().

Other sources

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:binarytointeger/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.

— NVD

Affected Software

3 affected components
ubuntu/elixir*
ubuntu/elixir*
ubuntu/elixir*

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade elixir to a version that resolves this vulnerability.

    Fixed in 1.18.6
  2. Upgrade

    Upgrade elixir to a version that resolves this vulnerability.

    Fixed in 1.19.7
  3. Upgrade

    Upgrade elixir to a version that resolves this vulnerability.

    Fixed in 1.20.5
  4. Configuration

    Pass SafeJSON to Plug.Parsers as json_decoder instead of Phoenix.json_library(); configure its integer decoder to reject digit runs longer than 1024 before conversion.

    Phoenix Plug.Parsers json_decoder = SafeJSON

Event History

Oct 30, 2026
CVE Published
via OSV·12:00 AM
Data Sourced
via OSV·12:00 AM
DescriptionSeverityAffected Software
CVE Published
via MITRE·08:57 AM
Data Sourced
via MITRE·08:57 AM
DescriptionWeakness
Data Sourced
via OSV·08:57 AM
Weakness
Data Sourced
via NVD·09:17 AM
DescriptionSeverityWeakness

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203