Skip to content

Clamp derived sequence lengths and KV-cache index in CUDA GroupQueryAttention - #29240

Merged
jiafatom merged 5 commits into
microsoft:mainfrom
jiafatom:jiafatom/gqa-cache-index-bounds
Jun 24, 2026
Merged

Clamp derived sequence lengths and KV-cache index in CUDA GroupQueryAttention#29240
jiafatom merged 5 commits into
microsoft:mainfrom
jiafatom:jiafatom/gqa-cache-index-bounds

Conversation

@jiafatom

Copy link
Copy Markdown
Contributor

Description

The CUDA GroupQueryAttention kernel derives a KV-cache append offset from the seqlens_k input (past_seq_lens = (seqlens_k + 1) - sequence_length). On the CUDA EP seqlens_k is device-resident (only total_sequence_length is a CPU input), so the host-side range validation in the operator/helper is skipped. The device kernel UnpackRoPEAppend then guarded the cache store with only a one-sided upper bound (cache_s < max_seqlen), so an out-of-range seqlens_k could produce a negative offset that is sign-extended into the cache-index arithmetic.

The CPU operator already validates seqlens_k host-side; this change brings the CUDA path to parity by guarding on the device.

Changes

  • group_query_attention_impl.cu (GetSequenceLengths): clamp the negative case at the source so both total_seq_lens and the append offset past_seq_lens stay non-negative for all downstream consumers.
  • group_query_attention_qkv.cuh (UnpackRoPEAppend): make the KV-cache store bound two-sided (cache_s >= 0 && cache_s < max_seqlen), mirroring the existing position-index guard a few lines above. This also covers the fast-decode path, where past_seq_lens points directly at the raw input and bypasses GetSequenceLengths.
  • Added NegativeSeqlensK_CacheAppend_NoOOB_CUDA regression test exercising the KV-cache append path with an out-of-range seqlens_k (CUDA-guarded; skips when CUDA EP is unavailable).

Notes

  • The two-sided guard matches the pattern introduced for the rotary position index in Add validation of position_ids in RotaryEmbedding operators #27597.
  • CPU is unaffected (already validated host-side); WebGPU relies on the CPU-validated total_sequence_length. The CUDA implementation is shared with ROCm via hipify.
  • The regression is a device-memory write best observed under compute-sanitizer; the test asserts the run completes with finite outputs.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

jiafatom and others added 4 commits June 23, 2026 21:35
…ttention

On the CUDA EP the seqlens_k input is device-resident, so the host-side range
check in the operator is skipped. Guard the device path instead:

- GetSequenceLengths: clamp the negative case at the source so total_seq_lens
  and the KV append offset (past_seq_lens) stay non-negative.
- UnpackRoPEAppend: make the KV-cache store bound two-sided, mirroring the
  position-index guard in the same kernel. This also covers the fast-decode
  path, where past_seq_lens points directly at the raw input.

Add a CUDA regression test exercising the KV-cache append path with an
out-of-range seqlens_k.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…_sequence_length

Mark total_sequence_length as a graph initializer so shape inference computes
present_kv = max(past_seq_len, total_sequence_length), matching the declared
present_key/present_value outputs and resolving the model-build shape mismatch.

Use a controlled minimal underflow seqlens_k=0 (with sequence_length=2) so the
derived past length is -1 before neutralization, keeping all other internal
lengths in valid range and the output finite.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The negative seqlens_k under-specifies the kv length relative to the query, so
the resulting degenerate attention may legitimately yield non-finite values.
The regression point is that the cache append and attention complete without
indexing outside their buffers, so verify only that a correctly shaped result
is produced rather than asserting finiteness.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
output_size derives from constexpr locals, so it is a constant expression
usable inside the verifier lambda without capture. Capturing it triggers
-Werror=unused-lambda-capture under the Android clang build. Reference it
directly instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the CUDA (and hipified ROCm) GroupQueryAttention implementation against malformed seqlens_k values that can otherwise produce negative KV-cache append offsets and lead to out-of-bounds device writes. It brings the CUDA path closer to the CPU path’s safety guarantees by adding device-side clamping/guards and introduces a CUDA regression test for the scenario.

Changes:

  • Clamp derived sequence lengths in the CUDA GetSequenceLengths kernel so downstream offsets stay non-negative.
  • Add a two-sided bounds check for KV-cache writes in UnpackRoPEAppend (cache_s >= 0 && cache_s < max_seqlen).
  • Add a CUDA regression test covering negative seqlens_k in the KV-cache append path.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
onnxruntime/contrib_ops/cuda/bert/group_query_attention_impl.cu Clamps total_len/past_seq_lens to non-negative values in GetSequenceLengths to prevent negative derived offsets.
onnxruntime/contrib_ops/cuda/bert/group_query_attention_qkv.cuh Adds a lower-bound check to KV-cache store indices to prevent negative-index stores.
onnxruntime/test/contrib_ops/group_query_attention_op_test.cc Adds a CUDA regression test intended to catch the negative-seqlens_k KV-cache append OOB write.

Comment thread onnxruntime/test/contrib_ops/group_query_attention_op_test.cc Outdated
Reword the verifier comment to state explicitly that the degenerate attention
implied by the negative derived past length may yield non-finite outputs, which
is expected and intentionally not asserted; the test confirms completion with a
correctly shaped result and no out-of-bounds indexing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jiafatom
jiafatom merged commit eade9ea into microsoft:main Jun 24, 2026
86 checks passed
@jiafatom
jiafatom deleted the jiafatom/gqa-cache-index-bounds branch June 24, 2026 22:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants