Skip to content

REPRO-2026-00284: Verified Reproduction

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 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://www.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…
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).

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