CVE-2026-69664: Verified Reproduction
CVE-2026-69664: Erlang/OTP inets httpd parks request worker indefinitely on malformed chunk size sent after headers unauthenticated remote DoS
CVE-2026-69664 is verified against erlang/otp · github. Affected versions: OTP >= 18.1.4 < 27.3.4.17; >= 28.0 < 28.5.0.6; >= 29.0 < 29.0.6 (inets >= 6.0.3 < 9.3.2.7; >= 9.4 < 9.6.2.3; >= 9.7 < 9.7.2). Vulnerability class: DoS. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00370.
What Is CVE-2026-69664?
CVE-2026-69664 is a high-severity DoS vulnerability affecting erlang/otp OTP >= 18.1.4 < 27.3.4.17; >= 28.0 < 28.5.0.6; >= 29.0 < 29.0.6 (inets >= 6.0.3 < 9.3.2.7; >= 9.4 < 9.6.2.3; >= 9.7 < 9.7.2). Pruva has independently reproduced it and publishes a verified, runnable proof-of-concept (reproduction REPRO-2026-00370).
CVE-2026-69664 Severity
CVE-2026-69664 is rated high severity.
High — serious impact or readily exploitable. Prioritize remediation.
Affected erlang/otp Versions
erlang/otp · github versions OTP >= 18.1.4 < 27.3.4.17; >= 28.0 < 28.5.0.6; >= 29.0 < 29.0.6 (inets >= 6.0.3 < 9.3.2.7; >= 9.4 < 9.6.2.3; >= 9.7 < 9.7.2) are affected.
How to Reproduce CVE-2026-69664
pruva-verify REPRO-2026-00370 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00370/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-69664
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
HTTP/1.1 POST with Transfer-Encoding: chunked; headers (ending CRLFCRLF) in one TCP write, then a separate TCP write with non-hex chunk-size line 'ZZZ\r\n', socket held open with no further bytes
- inets httpd TCP listener
- httpd_manager accept
- httpd_request_handler handle_info({tcp,...}) bare-catch decoder continuation
- error tuple stored as NewMFA
- socket re-armed {active,once} with no timeout
- worker parked indefinitely; repeated across >max_clients (150) connections
- 503 heavy-load denial for legitimate clients
How the agent worked
Root Cause and Exploit Chain for CVE-2026-69664
In Erlang/OTP's built-in inets HTTP server (httpd), a request that uses
Transfer-Encoding: chunked and whose chunk-size line arrives in a TCP write
separate from the headers is decoded through a continuation invoked via a
bare catch in httpd_request_handler:handle_info/2. When the chunk-size
line is not valid hexadecimal (e.g. ZZZ\r\n), http_chunk:decode_size/4
throws {error, {chunk_size, Line}}; the bare catch flattens this throw
into a plain term that matches none of the error clauses, so it falls into the
catch-all NewMFA clause: the error tuple is stored as the next decoder
continuation and the socket is re-armed with {active, once}. Because the
request timeout was already cancelled when the headers were accepted and
minimum_bytes_per_second is disabled by default, no timer ever reclaims the
worker — it is parked indefinitely while the attacker keeps the TCP connection
open with zero further bytes. Repeating this across connections exhausts the
request-worker pool (max_clients, documented default 150) and denies service
to legitimate clients (CWE-772, Missing Release of Resource after Effective
Lifetime).
- Package/component affected:
lib/inetshttp_server/httpd_request_handler.erl(interaction withhttp_server/http_chunk.erldecode_size/4) - Affected versions: Erlang/OTP >= 18.1.4 < 27.3.4.17 | >= 28.0 < 28.5.0.6 |
= 29.0 < 29.0.6 (inets >= 6.0.3 < 9.3.2.7 | >= 9.4 < 9.6.2.3 | >= 9.7 < 9.7.2). Introduced by commit 77acb473d8f056f6f534395f131c6e45693797f0. Tested here: OTP-27 maintenance line, vulnerable commit
e9f49f57cef6e38fd13c4b0cee1eb5509ef471e8(parent of the fix; inets 9.3.2.6). - Risk level and consequences: High (CVSS 8.7, AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H).
Unauthenticated remote attacker, default configuration, availability-only
impact: each parked connection permanently consumes one request-handler
process and one socket until the attacker voluntarily disconnects; enough
such connections make the server answer every new client with
503 Service Unavailable("heavy load").
Impact Parity
- Disclosed/claimed maximum impact: Unauthenticated remote denial of service
against the default inets httpd configuration (
dos). - Reproduced impact from this run:
dos— full remote denial of service demonstrated end-to-end over the real TCP listener:- a single segmented request parks a
httpd_request_handlerprocess indefinitely (the same process pid observed alive with{message_queue_len,0},{status,waiting}and zero traffic for the full 165 s observation window, beyond the 150 s default keep-alive timeout; no response is ever sent to the parked socket); - a 300-connection exhaustion wave of identical parked requests fills the
worker pool at the documented
max_clientsdefault of 150 (confirmed against the server-side census before the probe is fired), after which fresh legitimateGETrequests are denied with503 Service Unavailable("heavy load"); the denial persists while the attacker holds the sockets (re-verified after a 25 s wait, census still pinned at 150) and disappears only after the attacker disconnects (census drops to 0, legit200); - the fixed build (fix commit
a3adf630...) responds400 Bad Requestimmediately on the exact same segmented input, closes the connection, reclaims the worker (census 0), and the same 300-connection wave cannot deny service to legitimate clients (200 throughout).
- a single segmented request parks a
- Parity:
full(claimed DoS fully demonstrated; no code execution was claimed or observed). - Not demonstrated: none — no impact beyond availability was claimed.
Root Cause
httpd_request_handler.erl, handle_info({Proto, Socket, Data}, ...) clause
(vulnerable code, line 241 of the tested checkout):
PROCESSED = (catch Module:Function([Data | Args])),
...
case PROCESSED of
{ok, Result} -> ...;
{error, {size_error, ...}, Version} -> ...;
{error, {version_error, ...}, Version} -> ...;
{error, {bad_request, ...}, Version} -> ...;
{http_chunk = Module, Function, Args} when ChunkState =/= undefined -> ...;
NewMFA ->
setopts(Socket, SockType, [{active, once}]),
{noreply, State#state{mfa = NewMFA}} %% <-- parks here
end
Flow for a chunked request:
- Headers arrive;
httpd_request:parse/2returns{ok, {continue, http_chunk, decode_size, Args}};handle_msg/2setsmfa = {http_chunk, decode_size, Args}after the request timeout was cancelled in the{ok, ...}branch (cancel_request_timeout/1). No new timer is armed;minimum_bytes_per_secondisfalseby default, sodata_receive_counter/2arms nothing either. - The chunk-size line arrives in a later TCP write, so
handle_info({tcp, Socket, Data}, ...)runs withModule = http_chunk, Function = decode_size. http_chunk:decode_size(<<"ZZZ\r\n">>, ...)throws{error, {chunk_size, Line}}for a non-hex chunk size.- The bare
catchconverts the throw into the plain term{error, {chunk_size, Line}}, which matches no clause of thecase(the{error, ...}clauses expect 3-tuples with aVersion), so the catch-allNewMFAclause stores the error tuple as the next continuation MFA and re-arms the socket with{active, once}. - The attacker sends no more bytes; no timeout exists; the worker waits
forever (only
tcp_closedwould free it). Each such connection leaks one handler process + one socket until client disconnect.
Note the contrast that proves segmentation matters: when the body arrives
together with the headers, handle_body/3 calls http_chunk:decode/...
inside a proper try ... catch and returns 400 Bad Request — that path was
never vulnerable. The trigger requires the chunk-size line to arrive in a
later write, exactly as the advisory states.
- Fix: OTP maintenance commits
a3adf63078438c86527d704e23282b7721d8ca12(OTP 27),df1a9ca4666e2fdfc44886bfaae76de086d803f6(OTP 28),bd4e74348c6be8a49f060da6fd48d43f3a960292(OTP 29). The fix wraps the decoder call ascatch throw:{error, Error} when Module =:= http_chunk ->(line 287 in the fixed tree) so a thrown chunk-decode error is routed to the error handling that sends400 Bad Requestand terminates the worker, plus a regression test inhttpd_SUITEfor the segmented-write case.
Reproduction Steps
- Script:
bundle/repro/reproduction_steps.sh(self-contained; run twice consecutively — both runs must exit 0). - What the script does:
- Resolves the prepared project cache (
bundle/project_cache_context.json); with no cached repo present it clones/fetches OTP fix commita3adf63078438c86527d704e23282b7721d8ca12(depth 2) fromhttps://github.com/erlang/otp.gitintobundle/artifacts/otp. - Builds the real Erlang/OTP from source at the vulnerable commit
e9f49f57cef6e38fd13c4b0cee1eb5509ef471e8(parent of the fix; the top-levelmaketolerates the wx-disableddebuggerfailure, thenmake local_setup+ targetedlib/stdlib/lib/inetsbuilds complete the system) and verifies the vulnerable checkout lacks the fix hunk (records the barecatchat line 241 inevidence/vuln_bare_catch.txt). - For each of two clean vulnerable attempts: starts a fresh
erlnode running the realinetshttpd on an ephemeral port with default configuration (keep_alive_timeout150 s andminimum_bytes_per_secondfalse left at their defaults on purpose;max_clientsset to its documented default of 150 —httpd_confonly persists the key when the user supplies it, and the manager'shandle_new_connection/4looks it up without a default, so an unset key means no bound at all), plus a 1 Hz server-side census ofhttpd_request_handlerprocesses. Thenattack_client.py: (a) healthchecks withGET(Connection: close), (b) opens a POST withTransfer-Encoding: chunked, sends headers in one TCP write, waits 0.3 s, sendsZZZ\r\nas a separate write, and holds the socket open with no further bytes for 165 s (beyond the 150 s default keep-alive timeout), (c) sends a legit request mid-park (server still appears healthy), (d) opens a 300-connection wave of identical parked requests, waits until the server-side census confirms the pool is actually full (>= 150 parked handlers) before probing (this removes the accept-backlog race where a probe fired while the pool was still filling could be served 200), then shows fresh legitimate requests are denied (503 Service Unavailable, "heavy load"), still denied after a 25 s wait, (e) disconnects the attacker and shows the server recovers (200, census back to 0). - Checks out the fix commit, rebuilds
lib/inetsin the same tree, verifies the fix hunk is present (line 287 recorded inevidence/fix_hunk.txt), and runs two clean fixed attempts with the same attack: the bad chunk-size line must produce400 Bad Request+ server close on the parked socket, census back to 0, and legit requests still served200during the full exhaustion wave. - Evaluates every attempt against the criteria above
(
evidence/summary.json,passmust be true), writesruntime_manifest.json, and exits 0 = confirmed / 1 = not reproduced.
- Resolves the prepared project cache (
- Expected evidence of reproduction (all produced by the script):
evidence/summary.jsonwithpass: trueand every per-attempt check true for two vulnerable and two fixed attempts;evidence/vulnerable_attempt{1,2}/census.log:PATH vsn=9.3.2.6with the inets beams loaded from the build tree, thenCENSUSlines showing one parked handler for the whole 165 s window and 150 parked handlers during the wave;evidence/vulnerable_attempt{1,2}/result.json:parked_socket_status: "timeout",parked_socket_recv: "",legit_after_exhaustion→HTTP/1.1 503 Service Unavailable,exhausted_recheck→ still 503,after_close_legit→ 200;evidence/fixed_attempt{1,2}/result.json:parked_socket_recvstarting withHTTP/1.1 400 Bad Request,parked_socket_status: "closed", census 0, legit 200 during the wave.
Evidence
- Environment: Linux x86_64, 4 cores; OTP built from source at
bundle/artifacts/otp(fallback location — the prepared project cache contained no repo); vulnerable checkoute9f49f57cef6e38fd13c4b0cee1eb5509ef471e8, fixed checkouta3adf63078438c86527d704e23282b7721d8ca12, both verified by hunk inspection before their respective attempts. - Primary logs and artifacts (all under
bundle/):bundle/logs/reproduction_steps.log— full transcript of the final passing run (run 2);bundle/logs/reproduction_steps_run1.log— the first consecutive passing run. Both end withRESULT: CVE-2026-69664 CONFIRMED on vulnerable build; fixed build fails closed.bundle/repro/evidence/summary.json— machine-readable pass/fail per attempt and per criterion (pass: true, 4/4 attempts pass).bundle/repro/evidence/vulnerable_attempt{1,2}/census.log— server-side process census, 1 Hz, 196 samples per attempt (PATH line + CENSUS lines; final entry after attacker disconnect shows count 0).bundle/repro/evidence/vulnerable_attempt{1,2}/result.json,client_output.log— client-side observations of each phase.bundle/repro/evidence/fixed_attempt{1,2}/...— same for the fixed build.bundle/repro/evidence/vuln_bare_catch.txt(241: PROCESSED = (catch Module:Function([Data | Args])),),fix_hunk.txt(287: catch throw:{error, Error} when Module =:= http_chunk ->) — source-line proof that the vulnerable tree has the barecatchand the fixed tree has the corrected clause.bundle/repro/runtime_manifest.json— runtime evidence manifest with per-artifact SHA-256 (all verified) and target identitygit:https://github.com/erlang/otp@e9f49f57cef6e38fd13c4b0cee1eb5509ef471e8(digestfac886b1b27ab179e45c1c7bf166b06ff9e1f3800dd1334f5f510e6a5cf67449).
- Key excerpts (from the final run's result.json files):
- Vulnerable, parked worker census (both attempts):
CENSUS ... 1 <0.NNN.0>:[{message_queue_len,0},{status,waiting}]at t=3/45/95/162 s — the same pid for the entire 165 s window;parked_socket_status: "timeout",parked_socket_recv: "". - Vulnerable, exhaustion:
parked=300/300; pool_full=True (census=150 ...);legit_after_exhaustion→HTTP/1.1 503 Service Unavailable(heavy load);exhausted_recheckafter 25 s → stillHTTP/1.1 503 Service Unavailable, census 150;after_close_legit→HTTP/1.1 200 OK, census 0. - Fixed:
parked_socket_recv=HTTP/1.1 400 Bad Request...,parked_socket_status: "closed", census count 0, andlegit_after_exhaustion→HTTP/1.1 200 OKduring the same wave.
- Vulnerable, parked worker census (both attempts):
- Note on version labels: the fixed checkout still reports
vsn=9.3.2.6in its PATH line because the maintenance fix commit does not bump the OTP/inets version file; the authoritative discriminators between the two builds are the recorded fix-hunk lines (241 barecatchvs 287catch throw:{error, Error}) and the behavioral divergence (indefinite park + 503 exhaustion vs immediate 400 + close + no denial).
Recommendations / Next Steps
- Upgrade guidance: upgrade to OTP 27.3.4.17 / 28.5.0.6 / 29.0.6
(inets 9.3.2.7 / 9.6.2.3 / 9.7.2) or later, which contain the
catch throw:{error, Error}fix. - Mitigations for interim: enable
minimum_bytes_per_secondin the httpd config so slow/stalled body receives are reaped; reduce exposure of inets httpd to untrusted networks. Be aware that leavingmax_clientsunset does not fall back to the documented default of 150 —httpd_confonly stores the value when the user supplies it, so an unset value effectively disables the connection cap (an even easier unbounded DoS); explicitly set it, ideally below 150. - Suggested fix approach: the merged fix wraps the decoder continuation
call so chunk-decode throws are converted into a proper
400 Bad Requestresponse and handler termination; keep that structure for every continuation invoked fromhandle_info. - Testing recommendations: the fix commit adds an
httpd_SUITEcase (invalid chunk size in a separate write). Keep regression coverage for the segmented-write case specifically — the single-write case was never vulnerable (handle_body/3already had a proper try/catch), so a test that coalesces headers and body will not catch this class of bug. Consider also auditing otherhandle_infocontinuation consumers for barecatchpatterns, asserting that a request timeout is always armed while a partially-received request is outstanding, and givinghandle_new_connection/4'smax_clientslookup the documented default.
Additional Notes
- Idempotency:
reproduction_steps.shis safe to re-run; it reuses the in-tree build underbundle/artifacts/otp, re-checks out each commit (git checkout -f), and incrementally rebuilds only the changedinetsmodules. Two consecutive full runs were executed in this session and both passed (exit 0,summary.jsonpass: true); transcripts:bundle/logs/reproduction_steps_run1.logandbundle/logs/reproduction_steps_run2.log. - Negative/scope controls included in the proof:
- fixed-commit build fails closed on the exact same segmented input (400 + close, worker reclaimed, no denial of service) — confirms the fix, not an environmental artifact;
- the single-write variant (headers +
ZZZ\r\nin one TCP write) returns 400 even on the vulnerable build, matching the advisory's note that thehandle_body/3path was never vulnerable (verified during development).
- Deterministic exhaustion probe: the attack client polls the server-side census until the pool is observed full (>= 150 handlers) before firing the post-exhaustion legitimate request; an earlier revision with a fixed 2-second sleep could, under scheduling pressure, fire the probe while the accept backlog was still draining and observe a misleading 200.
- Limitations: The DoS demonstration binds
max_clientsat its documented default (150); deployments that raised it need proportionally more parked connections, and deployments that left it unset are unbounded (see Recommendations). The census samples at 1 Hz from inside the Erlang node, so a handler that lived for only a fraction of a second could in principle be missed — not an issue here since the parked handler is verified alive across the whole 165 s observation window with the same pid. Background orchestration note: the full script takes ~10 minutes; execution environments with per-command time limits must run it detached (e.g.setsid nohup ... &).
CVE-2026-69664 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
ls /pruva/project-cache/ 2>/dev/null; ls /pruva/project-cache/repo-mirrors/ 2>/dev/null; cat /pruva/project-cache/.pruva/cache_manifest.json 2>/dev/null; ls bundle/repro bundle/logs/repro bundle/support 2>/dev/null; cat bundle/exploit_knowledge_preflight.json 2>/dev/null; cat bundle/learning/exploit_knowledge_preflight.json{"cwd":"/workspace","duration_ms":76,"exit_code":0,"stderr":"","stdout":"repo-mirrors\n{\n \"schema_version\": 1,\n \"entries\": [\n {\n \"path\": \"repo-mirrors\",\n \"reuse_class\": \"repo\",\n \"provenance\": \"run_generated\"\n }\n ]\n}bundle/logs/repro:\n\nbundle/repro:\n\nbundle/support:\n{\n \"schema_version\": 1,\n \"generated_from\": [\n \"bundle/claim_contract/report.json\",\n \"bundle/project_cache_context.json\"\n ],\n \"reference_only\": true,\n \"query\": {\n \"objective\": \"claim[0] surface=network_protocol trigger=network_protocol entrypoint_kind=tcp_peer expected_impact=dos\",\n \"primitive_kinds\": [],\n \"target\": {\n \"project_id\": \"82ee934f-5b47-4f99-8a8d-a9a9263441a5\",\n \"mitigations\": {\n \"aslr\": \"unknown\",\n \"pie\": \"unknown\",\n \"nx\": \"unknown\",\n \"stack_canary\": \"unknown\",\n \"relro\": \"unknown\"\n }\n },\n \"project_id\": \"82ee934f-5b47-4f99-8a8d-a9a9263441a5\",\n \"include_states\": [\n \"validated\",\n \"observed\",\n \"candidate\"\n ],\n \"limit\": 25\n },\n \"total_result_count\": 0,\n \"attached_result_… [truncated]ls -la /pruva/project-cache/repo-mirrors/; for d in /pruva/project-cache/repo-mirrors/*/; do echo "== $d"; git -C "$d" remote -v 2>/dev/null | head -2; git -C "$d" log --oneline -3 2>/dev/null; done{"cwd":"/workspace","duration_ms":77,"exit_code":128,"stderr":"","stdout":"total 0\ndrwx------ 2 vscode vscode 40 Sep 2 06:56 .\ndrwx------ 4 vscode vscode 80 Sep 2 06:56 ..\n== /pruva/project-cache/repo-mirrors/*/\n","success":false,"timeout_secs":120}Unknown error
which erl erlc docker podman gcc make perl python3; erl -noshell -eval 'io:format("~s~n",[erlang:system_info(otp_release)]), halt().' 2>&1 | head -2; nproc; df -h /pruva /tmp | tail -3; free -g | head -2/usr/bin/docker /usr/bin/gcc /usr/bin/make /usr/bin/perl /usr/bin/python3 pruva-command: 1: erl: not found 4 Filesystem Size Used Avail Use% Mounted on
cd /tmp && rm -rf otptest && git init -q otptest && cd otptest && git remote add origin https://github.com/erlang/otp.git && timeout 100 git fetch --depth 1 origin a3adf63078438c86527d704e23282b7721d8ca12 2>&1 | tail -3; echo "EXIT: $?"From https://github.com/erlang/otp * branch a3adf63078438c86527d704e23282b7721d8ca12 -> FETCH_HEAD EXIT: 0
docker info 2>&1 | head -5; echo ---; timeout 30 curl -s https://api.github.com/repos/erlang/otp/commits/a3adf63078438c86527d704e23282b7721d8ca12 2>&1 | head -30{"cwd":"/workspace","duration_ms":413,"exit_code":0,"stderr":"","stdout":"Client:\n Version: 29.1.3\n Context: default\n Debug Mode: false\n Plugins:\n---\n{\n \"sha\": \"a3adf63078438c86527d704e23282b7721d8ca12\",\n \"node_id\": \"C_kwDOAAW4j9oAKGEzYWRmNjMwNzg0MzhjODY1MjdkNzA0ZTIzMjgyYjc3MjFkOGNhMTI\",\n \"commit\": {\n \"author\": {\n \"name\": \"Lukas Backström\",\n \"email\": \"lukas@erlang.org\",\n \"date\": \"2026-06-01T08:26:29Z\"\n },\n \"committer\": {\n \"name\": \"Konrad Pietrzak\",\n \"email\": \"konrad@erlang.org\",\n \"date\": \"2026-08-21T14:21:26Z\"\n },\n \"message\": \"inets: Fix rejection of invalid chunk sizes\\n\\nBefore this fix the server would hang indefinitely without the need\\nto send any bytes to keep it running. This causes it to leak a process\\nwhich could be used to DOS the server.\",\n \"tree\": {\n \"sha\": \"90d23a65db45036bc7c393faa8546b01e9ea8cc9\",\n \"url\": \"https://api.github.com/repos/erlang/otp/git/trees/90d23a65db45036bc7c393faa8546b01e9ea8cc9\"\n },\n \"url\": \"https://api.github.com/repos/erlang/otp/git/commits/a3adf63078438c86527d704e23282b7721d8ca12\",\n \"c… [truncated]cd /tmp/otptest && git show FETCH_HEAD --stat | head -20 && git show FETCH_HEAD -- lib/inets | head -200{"cwd":"/workspace","duration_ms":700,"exit_code":0,"stderr":"","stdout":"commit a3adf63078438c86527d704e23282b7721d8ca12\nAuthor: Lukas Backström <lukas@erlang.org>\nDate: Mon Jun 1 10:26:29 2026 +0200\n\n inets: Fix rejection of invalid chunk sizes\n \n Before this fix the server would hang indefinitely without the need\n to send any bytes to keep it running. This causes it to leak a process\n which could be used to DOS the server.\n\n .devcontainer/devcontainer.json | 32 +\n .dir-locals.el | 14 +\n .editorconfig | 22 +\n .elp.toml | 16 +\n .gitattributes | 2 +\n .github/ISSUE_TEMPLATE/bug_report.md | 23 +\n .github/ISSUE_TEMPLATE/feature_request.md | 19 +\n .github/actions/build-base-image/action.yaml | 84 +\n .github/dockerfiles/Dockerfile.32-bit | 25 +\n .github/dockerfiles/Dockerfile.64-bit | 35 +\ncommit a3adf63078438c86527d704e23282b7721d8ca12\nAuthor: Lukas Backström <lukas@erlang.org>\nDate: Mon Jun 1 1… [truncated]Artifacts and Evidence for CVE-2026-69664
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-69664
FAQ: CVE-2026-69664
Is CVE-2026-69664 exploitable?
How severe is CVE-2026-69664?
What type of vulnerability is CVE-2026-69664?
Which versions of erlang/otp are affected by CVE-2026-69664?
How can I reproduce CVE-2026-69664?
Is the CVE-2026-69664 reproduction verified?
References for CVE-2026-69664
Authoritative sources for CVE-2026-69664 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.