GHSA-6cp8-v795-jr2j

Suggest an improvement
Source
https://github.com/advisories/GHSA-6cp8-v795-jr2j
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-6cp8-v795-jr2j/GHSA-6cp8-v795-jr2j.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-6cp8-v795-jr2j
Aliases
Published
2026-06-26T21:53:07Z
Modified
2026-06-26T22:00:09Z
Severity
  • 8.7 (High) CVSS_V4 - CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N CVSS Calculator
Summary
Hackney has an infinite loop on non-token byte at start of an Alt-Svc entry
Details

Summary

CVE-2026-47066 is an infinite loop (CWE-835) in hackney's Alt-Svc response header parser (src/hackney_altsvc.erl). When an HTTP server returns an Alt-Svc header whose value begins with a non-token byte (e.g. !, @, =, ;), the parser enters a tight tail-recursive loop that pins an Erlang scheduler at 100% CPU and permanently hangs the calling connection process. Because the parser is invoked synchronously on every HTTP response, any attacker-controlled origin can trigger the hang with a single-byte header value.

Details

1. Parser dispatch

parse_and_cache/3 is called inside the hackney connection process on each HTTP response. It collects all Alt-Svc header values via collect_altsvc_headers/1, concatenates them, and passes the result to parse/1, which calls parse_entries(Header, []).

2. Failed token consumption

parse_entries/2parse_entry/1parse_protocol/1parse_token(Data, <<>>). The function parse_token/2 pattern-matches leading bytes: alphanumeric, -, _, whitespace, and comma all have explicit clauses. Any other byte (e.g. !) falls through to the catch-all:

parse_token(Rest, <<>>) -> {undefined, Rest}.

This returns the input unchanged — no byte is consumed.

3. No-progress loop

parse_entry propagates {undefined, Rest} back to parse_entries/2, which calls skip_comma(Rest). Because the first byte is not ,, skip_comma also returns Rest unchanged. parse_entries then recurses with the identical buffer:

parse_entries(Data, Acc)  % Data identical to previous iteration

Erlang tail recursion never preempts on a pure CPU loop, so the scheduler is pinned and the process never yields or returns.

4. Root cause

parse_entries/2 has no guard that detects zero-byte progress after a failed parse_entry call and no fallback to advance past the offending byte.

PoC

  1. Start an HTTP server that responds with the header Alt-Svc: ! (any single non-token byte suffices).
  2. Issue any HTTP GET request via hackney to that server:
    hackney:request(get, "http://attacker.example/", [], <<>>, [])
    
  3. Observe that the call never returns; the Erlang scheduler hosting the connection process is pinned at 100% CPU indefinitely.

Alternatively, call the parser directly: hackney_altsvc:parse(<<"!">>) — the process hangs immediately.

Impact

Denial of service via unbounded CPU consumption. Any application using hackney 2.0.0-beta.1 through 4.0.0 that connects to attacker-controlled HTTP endpoints is affected. No authentication is required; a single response header byte is sufficient to hang the connection process. Fixed in hackney 4.0.1. CVSS v4.0 score: 8.7 (HIGH).

Resources

Database specific
{
    "cwe_ids":  [
        "CWE-835"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-06-26T21:53:07Z",
    "nvd_published_at":  "2026-05-25T15:16:21Z",
    "severity":  "HIGH"
}
References

Affected packages

Hex / hackney

Package

Name
hackney
Purl
pkg:hex/hackney

Affected ranges

Type
SEMVER
Events
Introduced
2.0.0
Fixed
4.0.1

Affected versions

2.*
2.0.0
2.0.1
3.*
3.0.0
3.0.1
3.0.2
3.0.3
3.1.0
3.1.1
3.1.2
3.2.0
3.2.1
4.*
4.0.0

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/06/GHSA-6cp8-v795-jr2j/GHSA-6cp8-v795-jr2j.json"