VYPR
Unrated severityNVD Advisory· Published May 25, 2026

Infinite loop in Alt-Svc header parser in hackney

CVE-2026-47066

Description

Loop with Unreachable Exit Condition ('Infinite Loop') vulnerability in benoitc hackney allows Excessive Allocation. The Alt-Svc response header parser in src/hackney_altsvc.erl does not guarantee forward progress. When parse_token/2 receives a non-token, non-whitespace, non-comma byte (e.g. !, @, =, ;), it returns the input unchanged. skip_comma/1 also returns the buffer unchanged when the first byte is not a comma. parse_entries/2 then recurses with identical data, creating a tight infinite tail-recursive loop that pins a scheduler at 100% CPU. The calling process never returns.

The entry point parse_and_cache/3 is called synchronously in the connection process on every HTTP response. A single-byte Alt-Svc: ! response header is sufficient to trigger the hang; the header is fully controlled by any HTTP origin the client connects to.

This issue affects hackney: from 2.0.0-beta.1 before 4.0.1.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Infinite loop in hackney's Alt-Svc parser allows remote attackers to cause denial of service via a single malformed header byte.

Vulnerability

The Alt-Svc response header parser in src/hackney_altsvc.erl contains an infinite loop vulnerability (CWE-835). When parse_token/2 receives a non-token, non-whitespace, non-comma byte (e.g., !, @, =, ;), it returns the input unchanged. skip_comma/1 also returns the buffer unchanged when the first byte is not a comma. parse_entries/2 then recurses with identical data, creating a tight infinite tail-recursive loop that pins a scheduler at 100% CPU. The entry point parse_and_cache/3 is called synchronously on every HTTP response. This affects hackney versions from 2.0.0-beta.1 before 4.0.1 [1][2][3].

Exploitation

An attacker needs only the ability to serve an HTTP response with an Alt-Svc header whose value begins with a single non-token byte (e.g., Alt-Svc: !). No authentication or special network position is required; any HTTP origin the client connects to can trigger the hang. The attacker sends a crafted response, and the hackney client's connection process enters an infinite loop, never returning [3].

Impact

Successful exploitation results in a denial of service: the affected Erlang scheduler is pinned at 100% CPU, and the calling connection process hangs permanently. No data confidentiality or integrity is compromised. The CVSS v4 score is 8.7 (High) with vector CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H [2].

Mitigation

The vulnerability is fixed in hackney version 4.0.1, released on 2026-05-25. The fix introduces a seek_next_entry/1 function that skips past malformed entries to guarantee forward progress [4]. No workaround is available for earlier versions. The CVE is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog.

AI Insight generated on May 25, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2

Patches

1
e548aba1f97f

fix(security): stop Alt-Svc parser looping on non-token byte (GHSA-6cp8)

https://github.com/benoitc/hackneyBenoit ChesneauMay 20, 2026via body-scan
2 files changed · +28 4
  • src/hackney_altsvc.erl+16 3 modified
    @@ -225,12 +225,25 @@ is_h3_protocol(_) -> false.
     parse_entries(<<>>, Acc) ->
         lists:reverse(Acc);
     parse_entries(Data, Acc) ->
    -    {Entry, Rest} = parse_entry(skip_ws(Data)),
    +    Stripped = skip_ws(Data),
    +    {Entry, Rest} = parse_entry(Stripped),
    +    %% GHSA-6cp8: ensure forward progress. A leading non-token byte
    +    %% (`!`, `@`, `=`, `;`, `.`, ...) makes parse_entry return
    +    %% {undefined, Rest=Stripped}, which would otherwise loop forever.
    +    %% Skip to the next ',' so at most one malformed entry is dropped.
    +    Rest1 = case Entry of
    +                undefined when Rest =:= Stripped -> seek_next_entry(Stripped);
    +                _ -> Rest
    +            end,
         case Entry of
    -        undefined -> parse_entries(skip_comma(Rest), Acc);
    -        _ -> parse_entries(skip_comma(Rest), [Entry | Acc])
    +        undefined -> parse_entries(skip_comma(Rest1), Acc);
    +        _ -> parse_entries(skip_comma(Rest1), [Entry | Acc])
         end.
     
    +seek_next_entry(<<>>) -> <<>>;
    +seek_next_entry(<<$,, _/binary>> = Data) -> Data;
    +seek_next_entry(<<_, Rest/binary>>) -> seek_next_entry(Rest).
    +
     %% Parse a single Alt-Svc entry: protocol="host:port"; ma=N
     parse_entry(<<>>) ->
         {undefined, <<>>};
    
  • test/hackney_altsvc_tests.erl+12 1 modified
    @@ -40,7 +40,9 @@ parse_test_() ->
                     {"parse h3 with ma parameter", fun test_parse_h3_ma/0},
                     {"parse multiple entries", fun test_parse_multiple/0},
                     {"parse clear", fun test_parse_clear/0},
    -                {"parse h3-29 variant", fun test_parse_h3_variant/0}
    +                {"parse h3-29 variant", fun test_parse_h3_variant/0},
    +                {"GHSA-6cp8: non-token lead byte terminates",
    +                 fun test_parse_non_token_lead_byte/0}
                 ]
             }
         }.
    @@ -65,6 +67,15 @@ test_parse_h3_variant() ->
         Result = hackney_altsvc:parse(<<"h3-29=\":443\"">>),
         ?assertEqual([{h3, same, 443, 86400}], Result).
     
    +%% GHSA-6cp8: a leading non-token byte used to spin parse_entries/2 forever.
    +%% The parser must terminate, and must still recover entries that follow a
    +%% malformed one.
    +test_parse_non_token_lead_byte() ->
    +    [?assertEqual([], hackney_altsvc:parse(B))
    +     || B <- [<<"!">>, <<"@">>, <<"=">>, <<";">>, <<".">>]],
    +    ?assertEqual([{h3, same, 443, 86400}],
    +                 hackney_altsvc:parse(<<"!, h3=\":443\"">>)).
    +
     %%====================================================================
     %% Cache Tests
     %%====================================================================
    

Vulnerability mechanics

Root cause

"Missing forward-progress guard in parse_entries/2 allows infinite tail recursion when parse_token/2 returns input unchanged for non-token bytes."

Attack vector

An attacker controls an HTTP origin that the hackney client connects to. The attacker's server responds to any HTTP request with a single-byte `Alt-Svc: !` response header (any non-token byte such as `!`, `@`, `=`, `;`, or `.` suffices) [ref_id=1]. The `parse_and_cache/3` entry point is called synchronously in the connection process on every HTTP response, so the malformed header triggers the infinite loop immediately [ref_id=1]. The Erlang tail recursion pins a scheduler at 100% CPU and the calling process never returns, effectively hanging the connection permanently [CWE-835] [ref_id=1].

Affected code

The vulnerable code is in `src/hackney_altsvc.erl`, specifically the `parse_entries/2` function. When `parse_entry/1` returns `{undefined, Rest}` with `Rest` equal to the input (because `parse_token/2` has a catch-all clause that returns the input unchanged for non-token bytes like `!`, `@`, `=`, `;`, `.`), `parse_entries/2` calls `skip_comma/1` which also returns the buffer unchanged when the first byte is not a comma. The function then tail-recurses with identical data, creating an infinite loop [patch_id=2473710] [ref_id=1].

What the fix does

The patch modifies `parse_entries/2` in `src/hackney_altsvc.erl` to detect when `parse_entry` consumed zero bytes (i.e., `Entry` is `undefined` and `Rest` equals the stripped input) [patch_id=2473710]. In that case, it calls the new `seek_next_entry/1` helper, which scans forward to the next `,` byte, guaranteeing forward progress and dropping at most one malformed entry [patch_id=2473710]. The new test `test_parse_non_token_lead_byte/0` verifies that single-byte non-token inputs terminate cleanly and that valid entries following a malformed one are still parsed correctly [patch_id=2473710] [ref_id=2].

Preconditions

  • networkThe attacker must control an HTTP origin that the hackney client connects to.
  • inputThe attacker's server must return an Alt-Svc response header beginning with a non-token byte (e.g., !, @, =, ;, .).
  • authNo authentication or special configuration is required; the parser is invoked on every HTTP response by default.

Reproduction

Start an HTTP server that responds with the header `Alt-Svc: !` (any single non-token byte suffices). Issue any HTTP GET request via hackney to that server: `hackney:request(get, "http://attacker.example/", [], [], [])`. Observe that the call never returns and the Erlang scheduler is pinned at 100% CPU [ref_id=1].

Generated on May 25, 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.