REPRO-2026-00284: Verified Repro With Script Download
REPRO-2026-00284: XRING: Alibaba XQUIC QPACK ring-buffer resize underflow
REPRO-2026-00284 is verified against Alibaba XQUIC · github. Affected versions: All XQUIC versions through v1.9.4 (since first public release Jan 2022). This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00284.
What Is REPRO-2026-00284?
REPRO-2026-00284 documents "XRING", a high-severity denial-of-service vulnerability in Alibaba's XQUIC HTTP/3 library, caused by a ring-buffer resize underflow in the QPACK dynamic-table implementation. It affects all published XQUIC versions through v1.9.4. Pruva reproduced it (reproduction REPRO-2026-00284).
REPRO-2026-00284 Severity
REPRO-2026-00284 is rated high severity.
High — serious impact or readily exploitable. Prioritize remediation.
Affected Alibaba XQUIC Versions
Alibaba XQUIC · github versions All XQUIC versions through v1.9.4 (since first public release Jan 2022) are affected.
How to Reproduce REPRO-2026-00284
pruva-verify REPRO-2026-00284 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00284/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for REPRO-2026-00284
- reached the target end-to-end
- crash observed
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
spec-compliant QPACK encoder stream instructions (Set Dynamic Table Capacity=64, 61x Insert x:y, Insert AAAAA:BBBBB, Set Dynamic Table Capacity=65) sent over a QUIC/HTTP3 unidirectional encoder stream by an unauthenticated remote client
- quic-go client dials XQUIC demo_server over QUIC/UDP
- HTTP3 control stream + QPACK encoder uni stream
- xqc_decoder processes Set Dynamic Table Capacity
- xqc_decoder_set_dtable_cap
- xqc_dtable_set_capacity
- xqc_ring_mem_resize both-truncated branch (ori_sz1 = mcap - soffset_ori, should be rmem
- capacity)
- 64-byte heap OOB read + size_t underflow into xqc_memcpy
- glibc _FORTIFY_SOURCE…
Variant/bypass analysis of CVE-2026-XRING (Alibaba XQUIC xqc_ring_mem_resize both-truncated ori_sz1 capacity confusion). Tested 3 materially different QPACK encoder-stream payloads (P1 original PoC mcap=128/sidx=122; P2 alternate wrap state mcap=128/sidx=118; P3 alternate grow factor mcap=256/sidx=246) against ASAN bu…
How the agent worked
Root Cause and Exploit Chain for REPRO-2026-00284
Alibaba XQUIC (all versions through v1.9.4, latest at time of disclosure) contains a
remote, unauthenticated denial-of-service vulnerability in its HTTP/3 QPACK dynamic
table implementation. The bug is in xqc_ring_mem_resize() (src/common/utils/ringmem/xqc_ring_mem.c).
When the QPACK dynamic-table ring buffer has wrapped (data is split across the end and
the beginning of the buffer — the "both-truncated" resize case) and the QPACK decoder
receives a Set Dynamic Table Capacity instruction that grows the table, the resize
code sizes the first block of the original buffer using the new capacity (mcap)
instead of the old capacity (rmem->capacity). This produces a heap out-of-bounds
read of the old buffer followed by a size_t underflow (rmem->used - ori_sz1) into the
third xqc_memcpy, which glibc _FORTIFY_SOURCE aborts (*** buffer overflow detected ***: terminated). A remote, unauthenticated QUIC/HTTP3 client can trigger this with
spec-compliant QPACK encoder-stream instructions, crashing the server process.
- Package/component affected:
xquic—src/common/utils/ringmem/xqc_ring_mem.c(xqc_ring_mem_resize), reached viaxqc_dtable_set_capacity(src/http3/qpack/dtable/xqc_dtable.c) ←xqc_decoder_set_dtable_cap(src/http3/qpack/xqc_decoder.c) when the decoder processes aSet Dynamic Table Capacityinstruction on the QPACK encoder stream. - Affected versions: All published XQUIC versions through v1.9.4. The disclosure
states no upstream patch is available. Reproduced here against XQUIC v1.9.4
(commit
96155cf, tagv1.9.4). - Risk level / consequences: High. Remote, unauthenticated, deterministic process crash (DoS) of any XQUIC HTTP/3 endpoint that allows the QPACK dynamic table (default configuration). The OOB read + underflowed memcpy also imply potential heap memory-safety impact beyond a simple crash, although only the crash (DoS) is claimed and reproduced.
Impact Parity
- Disclosed/claimed maximum impact: Remote unauthenticated crash (DoS); the blog
additionally describes a 64-byte heap OOB read and a
size_tunderflow intomemcpy. No code execution is claimed. - Reproduced impact from this run: Deterministic remote crash of the real XQUIC
demo_server(HTTP/3 over QUIC/UDP) via a realquic-goclient sending spec-compliant QPACK encoder-stream instructions. Observed: server process terminated with exit code134(SIGABRT) and glibc message*** buffer overflow detected ***: terminated(the_FORTIFY_SOURCEabort of the underflowedmemcpy). 2/2 vulnerable attempts crashed; 2/2 negative-control (one-line-fix) attempts stayed alive. - Parity:
fullfor the claimed DoS (remote unauthenticated crash) via the claimed network protocol surface. - Not demonstrated: Code execution / arbitrary heap write. Only the crash (DoS) and the underlying OOB-read + underflow symptom are demonstrated, matching the claim.
Root Cause
In xqc_ring_mem_resize(xqc_ring_mem_t *rmem, size_t cap), when cap > rmem->capacity
the function allocates a new power-of-two buffer mcap = xqc_pow2_upper(cap) and copies
the used bytes from the old buffer into the new one. The copy has four shape cases. In
the "both-truncated" case (data wraps in both the old and the new buffer,
i.e. soffset_ori >= eoffset_ori and soffset_new >= eoffset_new):
size_t new_sz1 = mcap - soffset_new; /* size of first block in new buffer */
size_t ori_sz1 = mcap - soffset_ori; /* BUG: uses NEW cap; should be rmem->capacity */
soffset_ori is computed with the old mask (rmem->sidx & rmem->mask), so it is an
offset inside the old buffer of size rmem->capacity. The first block of the old buffer
that must be copied therefore has size rmem->capacity - soffset_ori, not
mcap - soffset_ori. Because mcap > rmem->capacity, ori_sz1 is too large. In the
"first block of new buffer is smaller than original buffer" sub-branch this causes:
xqc_memcpy(buf, rmem->buf + soffset_ori + new_sz1, ori_sz1 - new_sz1)— readsori_sz1 - new_sz1bytes starting past the end of the old buffer (64-byte OOB read in the reproduced scenario);xqc_memcpy(buf + ori_sz1 - new_sz1, rmem->buf, rmem->used - ori_sz1)—rmem->used - ori_sz1underflows (unsigned), producing a hugesize_t, so glibc's__memcpy_chk(_FORTIFY_SOURCE) aborts the process.
Concrete reproduced arithmetic (v1.9.4, payload from FoxIO xring-poc):
initial Set Dynamic Table Capacity = 64 → rmem->capacity = 64 (mask 63). 61×
Insert "x":"y" then Insert "AAAAA":"BBBBB" evict entries and wrap the 64-byte ring,
leaving used = 10, sidx = 122 (soffset_ori = 122 & 63 = 58), eidx = 132
(eoffset_ori = 132 & 63 = 4): old buffer is truncated. Set Dynamic Table Capacity = 65
→ xqc_ring_mem_resize(rmem, 65): mcap = xqc_pow2_upper(65) = 128, soffset_new = 122 & 127 = 122, eoffset_new = 132 & 127 = 4: new buffer also truncated → "both-truncated"
branch. new_sz1 = 128 - 122 = 6; buggy ori_sz1 = 128 - 58 = 70 (correct: 64 - 58 = 6). new_sz1 < ori_sz1 → reads 70 - 6 = 64 bytes OOB, then used(10) - 70 underflows
→ memcpy_chk abort (exit 134).
Fix (no upstream patch exists; applied here as the negative control):
- size_t ori_sz1 = mcap - soffset_ori; /* size of first block in original buffer */
+ size_t ori_sz1 = rmem->capacity - soffset_ori; /* size of first block in original buffer */
With this one-line change the identical malicious payload no longer crashes the server (verified: 2/2 fixed attempts stay alive).
Reproduction Steps
- Script:
bundle/repro/reproduction_steps.sh(self-contained; reuses the durable project cache when present, otherwise clones/builds XQUIC v1.9.4 + Tongsuo 8.4-stable- a
quic-goclient from source).
- a
- What it does:
- Locates the durable project cache via
bundle/project_cache_context.json; uses prebuiltdemo_server_vuln(XQUIC v1.9.4,-Werrorremoved, vulnerable line intact) anddemo_server_fixed(same tree with the one-lineori_sz1fix) plus a prebuiltquic-goclient that reproduces the FoxIO XRING payload. - Generates a self-signed EC cert, starts the real XQUIC
demo_server(HTTP/3 over QUIC/UDP on127.0.0.1:8443), then runs thequic-goclient which performs a real QUIC/TLS handshake, opens the HTTP/3 control stream (0x00 0x04 0x00) and the QPACK encoder unidirectional stream (0x02+ payload), and sends:Set Dynamic Table Capacity=64, 61×Insert "x":"y",Insert "AAAAA":"BBBBB",Set Dynamic Table Capacity=65. - Runs 2 vulnerable attempts and 2 fixed attempts with per-attempt logs, then
writes
bundle/repro/runtime_manifest.json.
- Locates the durable project cache via
- Expected evidence: vulnerable server exits
134(SIGABRT) with stderr*** buffer overflow detected ***: terminated; fixed server stays alive. Script exits0when vulnerable crashes ≥1/2 and fixed alive ≥1/2.
Evidence
bundle/repro/reproduction_steps.sh— the reproducer.bundle/repro/runtime_manifest.json— runtime evidence manifest (entrypoint_kind=tcp_peer,service_started=true,healthcheck_passed=true,target_path_reached=true, proof artifact list).bundle/logs/repro_run1.log,bundle/logs/repro_run2.log— full logs of two consecutive successful runs.bundle/logs/vuln_attempt{1,2}_server.stderr— contain*** buffer overflow detected ***: terminated.bundle/logs/vuln_attempt{1,2}_result.txt—RESULT: server CRASHED exit=134.bundle/logs/fixed_attempt{1,2}_result.txt—RESULT: server ALIVE (no crash).bundle/logs/vuln_attempt{1,2}_client.stdout—payload len 260/payload sent.
Key excerpts (run 1):
[vuln#1] server pid=22035 listening on udp/8443
[vuln#1] RESULT: server CRASHED exit=134
[vuln#2] server pid=22062 listening on udp/8443
[vuln#2] RESULT: server CRASHED exit=134
[fixed#1] server pid=22089 listening on udp/8443
[fixed#1] RESULT: server ALIVE (no crash)
[fixed#2] server pid=22112 listening on udp/8443
[fixed#2] RESULT: server ALIVE (no crash)
[*] vulnerable crashes: 2 / 2
[*] fixed alive (no crash): 2 / 2
[*] glibc _FORTIFY_SOURCE abort observed: 2 / 2
Server stderr (vulnerable): *** buffer overflow detected ***: terminated
Environment: Ubuntu 26.04 LTS, GCC 15.2.0, XQUIC v1.9.4 (commit 96155cf), Tongsuo
8.4-stable (BabaSSL) as the QUIC TLS backend, quic-go v0.60.0 client, Go 1.26.5,
glibc _FORTIFY_SOURCE enabled in the Release build. No sanitizers used for the
primary proof (sanitizer_used=false); the crash is the native product-visible abort.
Recommendations / Next Steps
- Fix: In
xqc_ring_mem_resize, change the both-truncated branch to size the original-buffer block with the old capacity:ori_sz1 = rmem->capacity - soffset_ori;. (This is the negative-control patch applied and verified here.) Audit the other resize sub-branches for the same old-vs-new-capacity confusion and add unit tests covering wrapped-ring grow scenarios. - Mitigation (deploy now): Advertise
SETTINGS_QPACK_MAX_TABLE_CAPACITY = 0to disable the QPACK dynamic table, preventing the decoder from ever creating/growing the ring memory that contains the bug. - Upgrade guidance: No patched upstream release exists at disclosure time; users should apply the one-line fix or the mitigation until an official patch is published.
- Testing: Add regression tests that fill the QPACK dynamic table until the ring
wraps, then grow via
Set Dynamic Table Capacity, asserting no OOB read / underflow (e.g., under ASAN/UBSAN and with_FORTIFY_SOURCE).
Additional Notes
- Idempotency: Confirmed — the script was run twice consecutively; both runs exited
0with identical results (vuln 2/2 crash, fixed 2/2 alive, fortify abort 2/2). - Surface match: The claim surface is
network_protocol/required_entrypoint_kind=tcp_peer. The proof drives a real QUIC/HTTP3 peer over UDP (aquic-goclient dialing the real XQUICdemo_server), exercising the real QPACK decoder encoder-stream path end-to-end. QUIC uses UDP datagrams rather than a TCP byte stream, but the peer/listener boundary and protocol exchange are real. - No sanitizers in the primary proof: The crash is glibc
_FORTIFY_SOURCEaborting the underflowedmemcpyin a normal Release build — a product-visible native crash, not an ASAN/UBSAN report. - Claimed vs observed impact: Only DoS (crash) is claimed and reproduced. The
underlying 64-byte OOB read +
size_tunderflow suggest possible heap memory-safety impact beyond DoS, but code execution was not claimed and is not demonstrated here. - Limitations: Reproduced against the
demo_serversample shipped with XQUIC; any XQUIC-based HTTP/3 server exposing the QPACK decoder to remote clients is expected to be similarly affected (default dynamic-table configuration).
Variant Analysis & Alternative Triggers for REPRO-2026-00284
No bypass and no distinct uncovered alternate trigger was found. The root cause is a single
old-vs-new-capacity confusion in xqc_ring_mem_resize() (src/common/utils/ringmem/xqc_ring_mem.c:129):
the "both-truncated" resize branch sizes the first block of the original ring buffer with the
new capacity (mcap) instead of the old capacity (rmem->capacity), producing a heap
out-of-bounds read and a size_t underflow into memcpy. The one-line fix
(ori_sz1 = rmem->capacity - soffset_ori) corrects that single computation, which is consumed by
both sub-branches of the only reachable case and applies to every caller. This stage confirmed
empirically (ASAN, both vulnerable and fixed builds) that the fix eliminates the OOB read across
multiple materially different ring offsets and grow factors, and statically that there is no second
independent capacity-confusion bug, no other attacker-controlled resize trigger, and no unreachable
sub-branch that the fix misses. Result: fix is complete; no bypass.
Fix Coverage / Assumptions
- Invariant the fix relies on:
rmem->capacity/rmem->maskstill describe the old buffer during the copy (they are overwritten only after the copy block), so sizing the original-buffer block withrmem->capacity - soffset_oriis correct. - Code paths explicitly covered: the both-truncated branch of
xqc_ring_mem_resize, both itsnew_sz1 >= ori_sz1andnew_sz1 < ori_sz1sub-branches (they share the singleori_sz1line), and therefore every caller — the decoderSet Dynamic Table Capacitypath (the only attacker trigger) and the encoder SETTINGS/app-API path. - What the fix does NOT cover: nothing. The other three copy cases (continuous-new via the
xqc_ring_mem_copyhelper; continuous-old/truncated-new; and the dead sub-branch A) were audited and are correct; none contains a second capacity confusion. There is no gap.
Variant / Alternate Trigger
Three payloads were tested (entry point identical for all: a remote unauthenticated QUIC/HTTP3
client opening the HTTP/3 control stream + QPACK encoder unidirectional stream and sending
spec-compliant encoder-stream instructions to the XQUIC demo_server):
| Payload | Instructions | mcap | ring sidx | Role |
|---|---|---|---|---|
| P1 | SetCap64, 61×Ins x:y, Ins AAAAA:BBBBB, SetCap65 |
128 | 122 | original PoC |
| P2 | SetCap64, 59×Ins x:y, Ins AAAAA:BBBBB, SetCap65 |
128 | 118 | alternate wrap state |
| P3 | SetCap64, 123×Ins x:y, Ins AAAAA:BBBBB, SetCap200 |
256 | 246 | alternate grow factor |
P2 and P3 are materially different data paths (different ring offsets and, for P3, a different grow
factor / different mcap) that still reach the same sink:
xqc_h3_stream_process_uni → xqc_qpack_process_encoder → xqc_qpack_on_encoder_ins
(XQC_INS_TYPE_ENC_SET_DTABLE_CAP) → xqc_decoder_set_dtable_cap (xqc_decoder.c:243) →
xqc_dtable_set_capacity (xqc_dtable.c:602) → xqc_ring_mem_resize both-truncated branch
(xqc_ring_mem.c:129/139).
A second, distinct entry point to the same sink exists — the encoder dtable resize triggered by a
remote peer's SETTINGS_QPACK_MAX_TABLE_CAPACITY (xqc_h3_conn.c:722 → xqc_qpack_set_dtable_cap
→ xqc_encoder_set_dtable_cap → xqc_dtable_set_capacity → xqc_ring_mem_resize). It is covered
by the fix (the fix is in the function) but is not triggerable for the bug: at SETTINGS-exchange
time the encoder dtable is empty (rmem->used == 0), so xqc_ring_mem_resize skips the copy block
entirely, and SETTINGS can be sent only once. The both-truncated grow precondition cannot be met.
Other encoder-stream instructions (INSERT_NAME_REF, INSERT_LITERAL, DUPLICATE) evict entries
but never call xqc_ring_mem_resize, so they cannot reach the sink.
- Package/component affected:
xquic—src/common/utils/ringmem/xqc_ring_mem.c(xqc_ring_mem_resize), reached via the QPACK decoder dynamic-table capacity path. - Affected versions (as tested): XQUIC v1.9.4, commit
96155cffbde7f062fe45ac3f6899f47e25709d30(latest release at 2026-07-09; no newer/fixed release exists). Fixed target = v1.9.4 + the one-lineori_sz1patch. - Risk level / consequences: High. Remote, unauthenticated, deterministic process crash (DoS)
of any XQUIC HTTP/3 endpoint with the dynamic table enabled (default). Underlying defect is a
heap OOB read (size scales with
mcap - rmem->capacity) plus asize_tunderflow intomemcpy.
Impact Parity
- Disclosed/claimed maximum impact: remote unauthenticated crash (DoS); the blog also describes
a 64-byte heap OOB read and a
size_tunderflow intomemcpy. No code execution claimed. - Reproduced impact from this variant run: ASAN
heap-buffer-overflow READinxqc_ring_mem_resize:139on all three payloads against the vulnerable build (READ of size 64 for mcap=128; READ of size 192 for mcap=256), followed by process abort (exit 134). The fixed build is ASAN-clean and stays alive on all three. The OOB-read size scaling withmcap - rmem->capacitydirectly confirms themcap-vs-rmem->capacityroot cause. - Parity:
fullfor the claimed DoS + the disclosed OOB read, via the claimed network surface, across multiple alternate data paths; andfullfor "the fix closes the memory-safety defect" (no residual ASAN finding on the fixed build). - Not demonstrated: code execution / arbitrary heap write. Only the OOB read + crash (DoS) are demonstrated, matching the claim. No bypass (fixed build never crashes).
Root Cause
In the both-truncated resize case, soffset_ori = rmem->sidx & rmem->mask is an offset inside the
old buffer (size rmem->capacity), so the first block of the old buffer that must be copied has
size rmem->capacity - soffset_ori. The buggy code computes ori_sz1 = mcap - soffset_ori (using
the new capacity), making ori_sz1 too large by mcap - rmem->capacity. In sub-branch B
(new_sz1 < ori_sz1, the only reachable sub-branch — see patch_analysis.md for the reachability
proof) this causes:
xqc_memcpy(buf, rmem->buf + soffset_ori + new_sz1, ori_sz1 - new_sz1)— readsmcap - rmem->capacitybytes past the old buffer (the ASANREAD of size 64/192);xqc_memcpy(buf + ori_sz1 - new_sz1, rmem->buf, rmem->used - ori_sz1)—rmem->used - ori_sz1underflows (unsigned) → glibc_FORTIFY_SOURCEaborts (exit 134) in a non-ASAN build.
The fix replaces mcap with rmem->capacity in the ori_sz1 computation. No fix commit exists
upstream; the fix is the locally-applied one-line patch on v1.9.4.
Reproduction Steps
- Script:
bundle/vuln_variant/reproduction_steps.sh(self-contained; reuses the durable project cache, otherwise clones/builds XQUIC v1.9.4 + Tongsuo 8.4-stable + a parameterizedquic-goclient from source, with AddressSanitizer). - What it does: builds ASAN
demo_serverfor the vulnerable tree (line intact) and the fixed tree (one-line patch), then runs P1/P2/P3 against each server, capturing ASAN output and crash state, and writesbundle/vuln_variant/runtime_manifest.json. - Expected evidence: vulnerable server emits ASAN
heap-buffer-overflow READatxqc_ring_mem_resize:139and crashes (exit 134) on all 3 payloads; fixed server is ASAN-clean and alive on all 3. Script exits1(no bypass). A bypass would exit0.
Evidence
bundle/vuln_variant/reproduction_steps.sh— the variant reproducer (run 3×, all exit 1).bundle/vuln_variant/runtime_manifest.json— runtime evidence (sanitizer_used=true,vulnerable_crashes=3,fixed_bypasses=0, per-payload metadata, proof-artifact list).bundle/logs/vuln_variant/variant_matrix_summary.txt— the 3×2 result matrix.bundle/logs/vuln_variant/vuln_P{1,2,3}_server.stderr— ASANheap-buffer-overflow READreports atxqc_ring_mem_resize:139(size 64 for P1/P2; size 192 for P3), region0 bytes after 64-byte regionallocated byxqc_ring_mem_resize:97(the old ring buffer).bundle/logs/vuln_variant/fixed_P{1,2,3}_server.stderr— 0 bytes (ASAN-clean, server alive).bundle/logs/vuln_variant/{vuln,fixed}_P{1,2,3}_result.txt—CRASHED exit=134vsALIVE.bundle/logs/vuln_variant/analysis_notes.md— static entry-point / sub-branch reachability audit.bundle/logs/vuln_variant/source_revision_summary.txt,vulnerable_version.txt,fixed_version.txt,latest_version.txt— exact tested source revisions.
Key ASAN excerpt (vuln-P1):
==24138==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7bd837fea620 ...
READ of size 64 at 0x7bd837fea620 thread T0
#0 memcpy
#1 xqc_ring_mem_resize xqc_ring_mem.c:139
#2 xqc_dtable_set_capacity xqc_dtable.c:602
#3 xqc_decoder_set_dtable_cap xqc_decoder.c:243
#4 xqc_qpack_on_encoder_ins xqc_qpack.c:261 (XQC_INS_TYPE_ENC_SET_DTABLE_CAP)
0x...a620 is located 0 bytes after 64-byte region [0x...a5e0,0x...a620)
allocated by ... xqc_ring_mem_resize xqc_ring_mem.c:97 (old ring buffer from initial SetCap=64)
Environment: Ubuntu 26.04, GCC 15.2.0 with -fsanitize=address, XQUIC v1.9.4 (96155cf),
Tongsuo 8.4-stable (BabaSSL) QUIC TLS backend, quic-go v0.60.0 client, Go 1.26.5.
Recommendations / Next Steps
- Ship the one-line fix upstream (
ori_sz1 = rmem->capacity - soffset_ori) and cut a patched release; none exists today (v1.9.4 is latest). The fix is complete as-is — no additional code path needs closing. - Add regression tests that fill the QPACK dynamic table until the ring wraps, then grow via
Set Dynamic Table Capacityacross multiplemcapvalues (128, 256, …), asserting no OOB read / underflow under ASAN/UBSAN and_FORTIFY_SOURCE. Cover both decoder (encoder-stream SetCap) and the encoder SETTINGS path for defense-in-depth. - Deploy-time mitigation (now): advertise
SETTINGS_QPACK_MAX_TABLE_CAPACITY = 0to disable the QPACK dynamic table, preventing the decoder from ever creating/growing the buggy ring memory. - Optional hardening: in
xqc_ring_mem_resize, derive all "original buffer" sizes fromrmem->capacity/rmem->maskand addassert(soffset_ori < rmem->capacity)style bounds checks so any future old/new confusion fails closed.
Additional Notes
- Idempotency: confirmed —
reproduction_steps.shwas run 3 consecutive times; all exited1with an identical 3×2 matrix (vuln 3/3 ASAN crash, fixed 3/3 ASAN-clean/alive). - Bounded search: the variant search is bounded by the code structure — there is exactly one
attacker-controlled resize trigger (decoder
Set Dynamic Table Capacity), exactly one reachable sub-branch of the both-truncated case (sub-branch B; sub-branch A is provably unreachable for any legal grow), and exactly one buggy line. The encoder SETTINGS path is a real but non-triggerable alternate entry point. Additional "attempts" would be the same sink under different byte values (same root cause, same fix), so the search was stopped at 3 meaningfully distinct data paths. - Limitations: reproduced against the
demo_serversample; any XQUIC HTTP/3 server exposing the QPACK decoder to remote clients is expected to be similarly affected. Only DoS + the OOB read are demonstrated; code execution is not claimed and not shown.
REPRO-2026-00284 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.
which cmake gcc g++ make python3 jq git curl 2>/dev/null; echo "---"; ls -la /data/pruva/project-cache/ab38e70b-91c6-4571-9872-2e6f134461d6/ 2>&1/usr/bin/cmake /usr/bin/gcc /usr/bin/g++ /usr/bin/make /usr/bin/python3 /usr/bin/jq /usr/local/bin/git /usr/bin/curl
Artifacts and Evidence for REPRO-2026-00284
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix REPRO-2026-00284
FAQ: REPRO-2026-00284
How does the XRING QPACK resize attack work?
How severe is XRING (REPRO-2026-00284)?
How can I reproduce REPRO-2026-00284?
References for REPRO-2026-00284
Authoritative sources for REPRO-2026-00284 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.