Skip to content

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.

REPRO-2026-00284 Alibaba XQUIC · github Variant found Jul 11, 2026 .txt
Severity
HIGH
Confidence
HIGH
Reproduced in
20m 53s
Tool calls
242
Spend
$3.57
01 · Overview

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).

02 · Severity & CVSS

REPRO-2026-00284 Severity

REPRO-2026-00284 is rated high severity.

HIGH threat level

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

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
or 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
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for REPRO-2026-00284

Denial of service — reproduced
  • reached the target end-to-end
  • crash observed
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
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

Attack chain
  1. quic-go client dials XQUIC demo_server over QUIC/UDP
  2. HTTP3 control stream + QPACK encoder uni stream
  3. xqc_decoder processes Set Dynamic Table Capacity
  4. xqc_decoder_set_dtable_cap
  5. xqc_dtable_set_capacity
  6. xqc_ring_mem_resize both-truncated branch (ori_sz1 = mcap - soffset_ori, should be rmem
  7. capacity)
  8. 64-byte heap OOB read + size_t underflow into xqc_memcpy
  9. glibc _FORTIFY_SOURCE…
Variants tested

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 522 events · 242 tool calls · 21 min
21 minDuration
242Tool calls
121Reasoning steps
522Events
1Dead-ends
Agent activity over 21 min
Support
19
Hypothesis
2
Repro
281
Judge
24
Variant
192
0:0020:53

Root Cause and Exploit Chain for REPRO-2026-00284

Versions: All published XQUIC versions through v1.9.4. The disclosure

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: xquicsrc/common/utils/ringmem/xqc_ring_mem.c (xqc_ring_mem_resize), reached via xqc_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 a Set Dynamic Table Capacity instruction 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, tag v1.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_t underflow into memcpy. 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 real quic-go client sending spec-compliant QPACK encoder-stream instructions. Observed: server process terminated with exit code 134 (SIGABRT) and glibc message *** buffer overflow detected ***: terminated (the _FORTIFY_SOURCE abort of the underflowed memcpy). 2/2 vulnerable attempts crashed; 2/2 negative-control (one-line-fix) attempts stayed alive.
  • Parity: full for 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:

  1. xqc_memcpy(buf, rmem->buf + soffset_ori + new_sz1, ori_sz1 - new_sz1) — reads ori_sz1 - new_sz1 bytes starting past the end of the old buffer (64-byte OOB read in the reproduced scenario);
  2. xqc_memcpy(buf + ori_sz1 - new_sz1, rmem->buf, rmem->used - ori_sz1)rmem->used - ori_sz1 underflows (unsigned), producing a huge size_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 = 64rmem->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 = 65xqc_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

  1. 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-go client from source).
  2. What it does:
    • Locates the durable project cache via bundle/project_cache_context.json; uses prebuilt demo_server_vuln (XQUIC v1.9.4, -Werror removed, vulnerable line intact) and demo_server_fixed (same tree with the one-line ori_sz1 fix) plus a prebuilt quic-go client that reproduces the FoxIO XRING payload.
    • Generates a self-signed EC cert, starts the real XQUIC demo_server (HTTP/3 over QUIC/UDP on 127.0.0.1:8443), then runs the quic-go client 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.
  3. Expected evidence: vulnerable server exits 134 (SIGABRT) with stderr *** buffer overflow detected ***: terminated; fixed server stays alive. Script exits 0 when 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.txtRESULT: server CRASHED exit=134.
  • bundle/logs/fixed_attempt{1,2}_result.txtRESULT: server ALIVE (no crash).
  • bundle/logs/vuln_attempt{1,2}_client.stdoutpayload 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 = 0 to 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 0 with 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 (a quic-go client dialing the real XQUIC demo_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_SOURCE aborting the underflowed memcpy in 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_t underflow suggest possible heap memory-safety impact beyond DoS, but code execution was not claimed and is not demonstrated here.
  • Limitations: Reproduced against the demo_server sample 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

Versions: versions (as tested): XQUIC v1.9.4, commit 96155cffbde7f062fe45ac3f6899f47e25709d30

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->mask still describe the old buffer during the copy (they are overwritten only after the copy block), so sizing the original-buffer block with rmem->capacity - soffset_ori is correct.
  • Code paths explicitly covered: the both-truncated branch of xqc_ring_mem_resize, both its new_sz1 >= ori_sz1 and new_sz1 < ori_sz1 sub-branches (they share the single ori_sz1 line), and therefore every caller — the decoder Set Dynamic Table Capacity path (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_copy helper; 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_unixqc_qpack_process_encoderxqc_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:722xqc_qpack_set_dtable_capxqc_encoder_set_dtable_capxqc_dtable_set_capacityxqc_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: xquicsrc/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-line ori_sz1 patch.
  • 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 a size_t underflow into memcpy.

Impact Parity

  • Disclosed/claimed maximum impact: remote unauthenticated crash (DoS); the blog also describes a 64-byte heap OOB read and a size_t underflow into memcpy. No code execution claimed.
  • Reproduced impact from this variant run: ASAN heap-buffer-overflow READ in xqc_ring_mem_resize:139 on 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 with mcap - rmem->capacity directly confirms the mcap-vs-rmem->capacity root cause.
  • Parity: full for the claimed DoS + the disclosed OOB read, via the claimed network surface, across multiple alternate data paths; and full for "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:

  1. xqc_memcpy(buf, rmem->buf + soffset_ori + new_sz1, ori_sz1 - new_sz1) — reads mcap - rmem->capacity bytes past the old buffer (the ASAN READ of size 64/192);
  2. xqc_memcpy(buf + ori_sz1 - new_sz1, rmem->buf, rmem->used - ori_sz1)rmem->used - ori_sz1 underflows (unsigned) → glibc _FORTIFY_SOURCE aborts (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

  1. 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 parameterized quic-go client from source, with AddressSanitizer).
  2. What it does: builds ASAN demo_server for 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 writes bundle/vuln_variant/runtime_manifest.json.
  3. Expected evidence: vulnerable server emits ASAN heap-buffer-overflow READ at xqc_ring_mem_resize:139 and crashes (exit 134) on all 3 payloads; fixed server is ASAN-clean and alive on all 3. Script exits 1 (no bypass). A bypass would exit 0.

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 — ASAN heap-buffer-overflow READ reports at xqc_ring_mem_resize:139 (size 64 for P1/P2; size 192 for P3), region 0 bytes after 64-byte region allocated by xqc_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.txtCRASHED exit=134 vs ALIVE.
  • 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 Capacity across multiple mcap values (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 = 0 to 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 from rmem->capacity/rmem->mask and add assert(soffset_ori < rmem->capacity) style bounds checks so any future old/new confusion fails closed.

Additional Notes

  • Idempotency: confirmed — reproduction_steps.sh was run 3 consecutive times; all exited 1 with 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_server sample; 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.

Event 1/40
0:001:17
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-XRING · REPRO-20
0:02
0:05
web search
0:06
web search
0:07
0:12
0:13
0:14
web search
0:22
0:23
0:32
0:46
0:46
extract_facts
no facts extracted
0:47
0:47
0:47
supportrepro
1:09
1:11
1:11
1:11
1:12
1:12
1:12
1:14
1:14
1:14
1:16
1:16
$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
1:17

Artifacts and Evidence for REPRO-2026-00284

Scripts, logs, diffs, and output captured during the reproduction.

bundle/artifact_promotion_manifest.json16.7 KB
bundle/artifact_promotion_report.json16.7 KB
bundle/vuln_variant/source_identity.json2.6 KB
bundle/vuln_variant/root_cause_equivalence.json4.6 KB
bundle/repro/reproduction_steps.sh14.4 KB
bundle/repro/runtime_manifest.json1.1 KB
bundle/repro/validation_verdict.json1.2 KB
bundle/repro/rca_report.md10.9 KB
bundle/logs/vuln_attempt1_server.stderr0.0 KB
bundle/logs/repro_run1.log1.9 KB
bundle/logs/vuln_attempt1_client.stdout0.0 KB
bundle/logs/vuln_attempt1_result.txt0.1 KB
bundle/logs/fixed_attempt1_server.stderr0.0 KB
bundle/logs/fixed_attempt1_client.stdout0.0 KB
bundle/logs/fixed_attempt1_result.txt0.1 KB
bundle/logs/vuln_attempt2_server.stderr0.0 KB
bundle/logs/vuln_attempt2_client.stdout0.0 KB
bundle/logs/vuln_attempt2_result.txt0.1 KB
bundle/logs/fixed_attempt2_server.stderr0.0 KB
bundle/logs/fixed_attempt2_client.stdout0.0 KB
bundle/logs/fixed_attempt2_result.txt0.1 KB
bundle/vuln_variant/reproduction_steps.sh16.5 KB
bundle/logs/vuln_variant/variant_matrix_summary.txt0.4 KB
bundle/logs/vuln_variant/vuln_P1_server.stderr9.0 KB
bundle/logs/vuln_variant/vuln_P3_server.stderr9.0 KB
bundle/logs/vuln_variant/fixed_P1_server.stderr0.0 KB
bundle/vuln_variant/validation_verdict.json4.4 KB
bundle/vuln_variant/variant_manifest.json5.0 KB
bundle/vuln_variant/patch_analysis.md9.0 KB
bundle/vuln_variant/rca_report.md11.3 KB
bundle/vuln_variant/runtime_manifest.json2.2 KB
bundle/logs/vuln_variant/vuln_P1_client.stdout0.1 KB
bundle/logs/vuln_variant/vuln_P1_result.txt0.1 KB
bundle/logs/vuln_variant/fixed_P1_client.stdout0.1 KB
bundle/logs/vuln_variant/fixed_P1_result.txt0.1 KB
bundle/logs/vuln_variant/vuln_P2_server.stderr9.0 KB
bundle/logs/vuln_variant/vuln_P2_client.stdout0.1 KB
bundle/logs/vuln_variant/vuln_P2_result.txt0.1 KB
bundle/logs/vuln_variant/fixed_P2_server.stderr0.0 KB
bundle/logs/vuln_variant/fixed_P2_client.stdout0.1 KB
bundle/logs/vuln_variant/fixed_P2_result.txt0.1 KB
bundle/logs/vuln_variant/vuln_P3_client.stdout0.1 KB
bundle/logs/vuln_variant/vuln_P3_result.txt0.1 KB
bundle/logs/vuln_variant/fixed_P3_server.stderr0.0 KB
bundle/logs/vuln_variant/fixed_P3_client.stdout0.1 KB
bundle/logs/vuln_variant/fixed_P3_result.txt0.1 KB
08 · How to Fix

How to Fix REPRO-2026-00284

Coming soon

Step-by-step mitigation and hardening guidance for REPRO-2026-00284 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: REPRO-2026-00284

How does the XRING QPACK resize attack work?

A remote, unauthenticated QUIC/HTTP3 client sends spec-compliant QPACK encoder-stream instructions that first fill the dynamic table until the ring buffer wraps, then send a 'Set Dynamic Table Capacity' instruction that grows the table. The resulting size miscalculation triggers glibc's _FORTIFY_SOURCE check, which aborts the process ('*** buffer overflow detected ***: terminated'), crashing the server.

How severe is XRING (REPRO-2026-00284)?

It is rated high severity: a remote, unauthenticated denial-of-service that crashes the server process. No CVSS score has been assigned.

How can I reproduce REPRO-2026-00284?

Download the verified script from this page and run it in an isolated environment against XQUIC v1.9.4 or earlier. It drives the HTTP/3 QPACK decoder through the wrapped-buffer resize path with a crafted Set Dynamic Table Capacity instruction and shows the server process aborting under _FORTIFY_SOURCE.
11 · References

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.