VYPR
Medium severityNVD Advisory· Published Jun 9, 2026· Updated Jun 9, 2026

CVE-2026-49762

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

2

Patches

1
c64417d72fd5

Limit version numbers to 14 bytes

https://github.com/elixir-lang/elixirJosé ValimJun 9, 2026via nvd-ref
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

4

News mentions

0

No linked articles in our index yet.