CVE-2026-49762
Description
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 1.5.0 before 1.20.1.
Affected products
2Patches
1c64417d72fd5Limit version numbers to 14 bytes
2 files changed · +32 −1
lib/elixir/lib/version.ex+18 −1 modified@@ -18,12 +18,16 @@ defmodule Version do MAJOR.MINOR.PATCH + Each numeric component is limited to at most 14 digits. + Pre-releases are supported by optionally appending a hyphen and a series of period-separated identifiers immediately following the patch version. Identifiers consist of only ASCII alphanumeric characters and hyphens (`[0-9A-Za-z-]`): "1.0.0-alpha.3" + Numeric pre-release identifiers are also limited to at most 14 digits. + Build information can be added by appending a plus sign and a series of dot-separated identifiers immediately following the patch or pre-release version. Identifiers consist of only ASCII alphanumeric characters and hyphens (`[0-9A-Za-z-]`): @@ -520,6 +524,8 @@ defmodule Version do defmodule Parser do @moduledoc false + @max_numeric_component_digits 14 + operators = [ {">=", :>=}, {"<=", :<=}, @@ -621,7 +627,9 @@ defmodule Version do defp require_digits(nil), do: :error defp require_digits(string) do - if leading_zero?(string), do: :error, else: parse_digits(string, "") + if leading_zero?(string) or byte_size(string) > @max_numeric_component_digits, + do: :error, + else: parse_digits(string, "") end defp leading_zero?(<<?0, _, _::binary>>), do: true @@ -649,6 +657,11 @@ defmodule Version do end end + defp convert_parts_to_integer([part | rest], acc) + when byte_size(part) > @max_numeric_component_digits do + if all_digits?(part), do: :error, else: convert_parts_to_integer(rest, [part | acc]) + end + defp convert_parts_to_integer([part | rest], acc) do case parse_digits(part, "") do {:ok, integer} -> @@ -667,6 +680,10 @@ defmodule Version do {:ok, Enum.reverse(acc)} end + defp all_digits?(<<char, rest::binary>>) when char in ?0..?9, do: all_digits?(rest) + defp all_digits?(<<>>), do: true + defp all_digits?(_other), do: false + defp valid_identifier?(<<char, rest::binary>>) when char in ?0..?9 when char in ?a..?z
lib/elixir/test/elixir/version_test.exs+14 −0 modified@@ -84,9 +84,15 @@ defmodule VersionTest do assert {:ok, %Version{major: 1, minor: 4, patch: 5, pre: [6, 7, "eight"]}} = Version.parse("1.4.5-6.7.eight") + assert {:ok, %Version{major: 99_999_999_999_999, minor: 0, patch: 0}} = + Version.parse("99999999999999.0.0") + assert {:ok, %Version{major: 1, minor: 4, patch: 5, pre: ["6-g3318bd5"]}} = Version.parse("1.4.5-6-g3318bd5+ignore") + assert {:ok, %Version{major: 1, minor: 0, patch: 0, pre: ["100000000000000-alpha"]}} = + Version.parse("1.0.0-100000000000000-alpha") + assert Version.parse("foobar") == :error assert Version.parse("2") == :error assert Version.parse("2.") == :error @@ -105,6 +111,13 @@ defmodule VersionTest do assert Version.parse("02.3.0") == :error assert Version.parse("0. 0.0") == :error assert Version.parse("0.1.0-&&pre") == :error + assert Version.parse("100000000000000.0.0") == :error + assert Version.parse("1.100000000000000.0") == :error + assert Version.parse("1.0.100000000000000") == :error + assert Version.parse("1.0.0-100000000000000") == :error + + assert Version.parse("1.0.0+100000000000000") == + {:ok, %Version{major: 1, minor: 0, patch: 0, build: "100000000000000"}} end test "to_string/1" do @@ -338,6 +351,7 @@ defmodule VersionTest do assert Version.parse_requirement("1.2.3 and or 4.5.6") == :error assert Version.parse_requirement(">= 1") == :error assert Version.parse_requirement("1.2.3 >=") == :error + assert Version.parse_requirement("100000000000000.0.0") == :error end test "inspect/1" do
Vulnerability mechanics
Root cause
"The version parser converts numeric version components to integers without bounding their length, leading to resource exhaustion."
Attack vector
An attacker controls a version string and passes it to public entry points such as Version.parse/1, Version.parse!/1, Version.match?/3, Version.compare/2, or Version.parse_requirement/1. This can occur with untrusted input like HTTP parameters, dependency manifest fields, or package metadata. A single large all-digit component forces a super-linear integer conversion that consumes excessive CPU and memory, potentially crashing the calling process [ref_id=1]. No authentication is required.
Affected code
The vulnerability resides in the Elixir standard library's Version module, specifically within the 'Elixir.Version.Parser':parse_digits/2 routine and related functions. The patch modifies the `require_digits/1` function to include a check for `byte_size(string) > @max_numeric_component_digits` and introduces the `@max_numeric_component_digits` constant, limiting numeric components to 14 digits [patch_id=5357536].
What the fix does
The patch introduces a limit of 14 digits for numeric components in version strings, including major, minor, patch, and pre-release identifiers. This is enforced by checking the byte size of the string representation of numeric components before attempting to parse them into integers. By bounding the length of these numeric components, the patch prevents the super-linear conversion process that could lead to CPU and memory exhaustion [patch_id=5357536].
Preconditions
- inputAn attacker must control a version string with a single, large all-digit component.
Generated on Jun 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.