{
  "parent_claim_id": "CVE-2026-XRING",
  "parent_root_cause": "xqc_ring_mem_resize() both-truncated branch sizes the first block of the ORIGINAL ring buffer with the NEW capacity: ori_sz1 = mcap - soffset_ori (should be rmem->capacity - soffset_ori). Produces a heap OOB read of (mcap - rmem->capacity) bytes and a size_t underflow (rmem->used - ori_sz1) into the third memcpy, crashing the process.",
  "parent_sink": "src/common/utils/ringmem/xqc_ring_mem.c:129 (ori_sz1 definition) and :139 (OOB-read memcpy in the new_sz1 < ori_sz1 sub-branch)",
  "parent_entrypoint": "QPACK encoder-stream Set Dynamic Table Capacity instruction -> xqc_decoder_set_dtable_cap -> xqc_dtable_set_capacity -> xqc_ring_mem_resize",
  "variant_candidates": [
    {
      "id": "P2",
      "description": "alternate wrap state: 59x Insert(x:y) instead of 61, leaving sidx=118 (vs 122) before the grow; same mcap=128, same final Insert(AAAAA:BBBBB), SetCap=65",
      "same_root_cause": true,
      "same_sink": true,
      "same_entrypoint": true,
      "distinct_data_path": true,
      "vulnerable_reproduced": true,
      "vulnerable_evidence": "ASAN heap-buffer-overflow READ in xqc_ring_mem_resize:139; crash exit 134 (logs/vuln_variant/vuln_P2_server.stderr)",
      "fixed_blocked": true,
      "fixed_evidence": "ASAN-clean, server alive (logs/vuln_variant/fixed_P2_server.stderr = 0 bytes; result ALIVE)",
      "equivalence_reason": "Identical buggy ori_sz1 line and identical both-truncated sub-branch B; only the ring wrap offset differs. Same root cause, same sink, same fix covers it."
    },
    {
      "id": "P3",
      "description": "alternate grow factor: 123x Insert(x:y) leaving sidx=246, then SetCap=200 -> mcap=256 (vs 128); OOB read scales to 192 bytes (mcap - rmem->capacity = 256 - 64)",
      "same_root_cause": true,
      "same_sink": true,
      "same_entrypoint": true,
      "distinct_data_path": true,
      "vulnerable_reproduced": true,
      "vulnerable_evidence": "ASAN heap-buffer-overflow READ of size 192 in xqc_ring_mem_resize:139; crash exit 134 (logs/vuln_variant/vuln_P3_server.stderr)",
      "fixed_blocked": true,
      "fixed_evidence": "ASAN-clean, server alive (logs/vuln_variant/fixed_P3_server.stderr = 0 bytes; result ALIVE)",
      "equivalence_reason": "Identical buggy ori_sz1 line and sub-branch B; only mcap (grow factor) differs. The OOB-read size scaling with (mcap - rmem->capacity) is the direct signature of the mcap-vs-rmem->capacity confusion. Same root cause, same sink, same fix (rmem->capacity is mcap-independent) covers it."
    },
    {
      "id": "encoder-SETTINGS-path",
      "description": "alternate ENTRY POINT: encoder dtable resize via peer 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)",
      "same_root_cause": true,
      "same_sink": true,
      "same_entrypoint": false,
      "distinct_data_path": true,
      "vulnerable_reproduced": false,
      "vulnerable_evidence": "not triggerable: at SETTINGS-exchange time the encoder dtable is empty (rmem->used==0), so xqc_ring_mem_resize skips the copy block (if (rmem->used != 0)); SETTINGS can be sent only once per connection, so the both-truncated grow precondition (wrapped non-empty ring + grow) cannot be met",
      "fixed_blocked": true,
      "fixed_evidence": "covered by the fix (fix is inside xqc_ring_mem_resize, applies to all callers); not separately runtime-tested because the bug precondition is unreachable on this path",
      "equivalence_reason": "Reaches the same xqc_ring_mem_resize sink via a different caller, but the both-truncated copy block is never executed (rmem->used==0). Distinct entry point, same sink, covered by fix, not a triggerable variant."
    }
  ],
  "overall_equivalence": "P2 and P3 are alternate data paths (different ring offsets / grow factors) to the SAME root cause and SAME sink; both reproduce on the vulnerable build and are blocked by the one-line fix. They are NOT bypasses (the fix covers them). The encoder SETTINGS path is a distinct entry point to the same sink but is not triggerable for the memory-safety bug. No candidate is a distinct uncovered variant or a bypass.",
  "fix_completeness": "complete",
  "fix_completeness_reason": "The single ori_sz1 line is the only capacity-confusion defect; it is consumed by both sub-branches of the only reachable (both-truncated) case; the fix is inside the function so it covers every caller; the other resize branches and xqc_ring_mem_copy are correct; sub-branch A is provably unreachable. ASAN confirms the fixed build is clean across P1/P2/P3.",
  "negative_result": true,
  "bypass": false
}
