Repository navigation
CUDA: fix MMQ out-of-bounds reads - #29953
Conversation
|
Checked on a GB10 (sm_121a, CUDA 13.0). src1_q8_1 in its own exact-size cudaMalloc (the pool alloc swapped for a cudaMalloc of the same size), compute-sanitizer memcheck, cases from the #27044 thread. Plain memcheck, q4_0, 512 experts, 10 used, m=640, n=100: master 1194 errors (b=0) / 274 (b=1), this PR 0 / 0. n=508 and the q4_K shapes are 0 on both. MUL_MAT + MUL_MAT_ID on CUDA0: 2243/2243. With ids_dst gets read up to J_best-1 ints past ne_get_rows too with exact-size allocs; padding it by J_best clears that. |
|
Thank you for checking. I agree with those fixes and pushed a corresponding patch, please re-test. |
|
Re-tested 20c3140 on the GB10, same harness (src1_q8_1, ids_src1, ids_dst at your ne_get_rows + J_best - 1, and expert_bounds each in an exact-size cudaMalloc, memcheck with Also swept q4_0/q4_K/q6_K/q8_0 x n in {9, 33, 65, 129, 257, 400} x m in {512, 576} (128 experts, 8 used), 2 seeds: 96/96 clean. MUL_MAT + MUL_MAT_ID on CUDA0: 2291/2291. |
20c3140 to
3070d92
Compare
|
@JohannesGaessler , @hclsys thanks for your work. Much appreciated. The over night agent came to the same conclusion as your corresponding patch. Checked #29953 with your corresponding patch, all passing:
All GPU runs used exact-size or guard-page allocations Since it is an MMQ padding issue, also tested on AMD ROCm platform (ggfx1151) and nvfp4 (sm_120->sma_120a). A potential test is proposed in How it works What it covers Limitations Links: Test cases: https://github.com/MaxusAI/ollama/blob/5273e495fef54972f5d265ee49699e3e39ff1e0d/docs/maxusai/tasks/mmq-tests.patch Disclosure Hope this helps. |
|
I re-tested |
| GGML_UNUSED_VARS(type, J, fallback, prec_src1); | ||
| } | ||
|
|
||
| // FIXME all of the host functions are missing prec_src1, this can lead to inconsitent behavior. |
There was a problem hiding this comment.
Sure, I can take care of this.
Upstream brings CUDA top-k/argsort/mmq fixes (PR ggml-org#28713, ggml-org#29953, ggml-org#30147, ggml-org#29453), a DFlash output-head fix (ggml-org#30111) and a UI fix (ggml-org#29668). Conflicts and resolution: - top-k.cu / argsort.cu: upstream reworked the top-k selection and the same radix path the fork added. Take upstream's shape-based dispatch and int64_t fixes, keep the fork workspace cap in the shared ggml_cuda_chunk_nrows (GGML_CUDA_ARGSORT_CHUNK_MB) and the fork benchmark-only GGML_CUDA_TOPK_IMPL selector used by bench-topk.py. - mmq.cu: upstream PR ggml-org#29953 fixes the same mul_mat_id padding over-read the fork patched in 1386ec2, but sizes the padding from the chosen J tile in both branches. Take upstream's version. - test-backend-ops.cpp: keep the fork GGML_TOPK_BENCH shapes and add upstream's chunk-spanning cases. Assisted-by: pi (deepseek-flash)
…-10-10) Conflicts: - ggml-backend.cpp: upstream moved input copies to ggml_backend_sched_copy_input with host weights last (ggml-org#29943); the GGML_SCHED_BATCH_INPUTS fast path now runs in the first loop before falling back to it. - ggml-cuda/fattn-common.cuh: launch_fattn takes upstream async_kv_preload, then our parallel_blocks_fixed (GQA-6 tile launcher updated). - ggml-cuda/fattn-mma-f16.cuh: upstream swizzle refactor (ggml-org#29612); its helpers take ncols2 like the rest of the fork's config getters. - ggml-cuda/gated_delta_net.cu: keep the fork's kernel and launch geometry (its columns_per_block template argument is not upstream's cols_per_warp, ggml-org#30087). - ggml-cuda/mmq.cu: upstream per-tile src1 padding (ggml-org#29953); ggml_cuda_mul_mat_q_src1_nbytes and the MMQ input cache use the worst-case padding so producers and projections with other tiles can share the buffer. - ggml-cuda/mmq-vec-dot.cuh: fork vec_dot kernels pass GGML_PREC_Q8 to the config helpers (ggml-org#30168). - ggml-cuda/mmvf.cu/.cuh: keep both includes and the HIP declarations, upstream warp_size argument. - ggml-cuda/rope.cu: upstream grid-stride rms_norm+mul+rope (ggml-org#28175) with the fork's M-RoPE theta (nchannels instead of gridDim.y) and rope_store_cast. - ggml-cuda/top-k.cu: upstream shape-based selection (ggml-org#28713), RDNA4 tournament kept for few long rows. - src/llama-batch.*: mixed token/embd batches (ggml-org#29622) copy embd rows per entry; other batches keep pointing at the ext rows. - src/llama-context.cpp: expert copy callback also on the second MTP scheduler. - src/llama-graph.cpp: build_rs keeps the GET_ROWS of the ubatch states (GDN fusions, deferred state) and gathers the extra states from the tail like upstream's custom getter path (ggml-org#29856).
The 39 upstream commits after halo-box/llama.cpp's final sync. Same rules as the previous merge: strix's side wins where the two collide, upstream's non-conflicting changes stay, and fixes are ported into strix's code. - server: upstream's final ggml-org#26004 (context checkpoints across slot save/restore) replaces strix's earlier port of it, keeping strix's on-device restore flags; test_slot_save.py follows upstream. - cuda: strix's top-k (HIP radix), gated delta net and argsort kernels kept whole; upstream's CCCL major-version guard fix (ggml-org#29453) applied to strix's argsort. MMQ: upstream's out-of-bounds fix and host-side tile choice (ggml-org#29953) with strix's RDNA3.5 tile overrides on top; strix's fused MMQ paths pad src1 for the largest tile instead of the removed get_J_max. strix's 128-column rms_norm fast path and two mmvf calls updated to upstream's new signatures (ggml-org#28175, warp_size). - vulkan: strix's flash_attn_cm2.comp and topk_nary_search.comp kept whole (upstream's sparse cm2 FA ggml-org#30003 stays out with the rest of sparse FA). - llama-context: strix's MTP draft vocabulary next to upstream's MoE cache. - graph: upstream's single recurrent-state gather (ggml-org#29856) is restored and combined with strix's in-place gated delta net: in-place capable consumers keep the full gather and get their state view next to the consumer whether or not the rows are in place, so in-place and gathered ubatches build the same graph; this fixes the realloc aborts under GGML_SCHED_DEBUG_REALLOC in both upstream's shared-seq test and strix's seq cp graph reuse test. - ggml-backend: alloc_buffer_n planning no longer flushes on zero-size tensors, which left views of a tensor larger than max_size uninitialized (caught by strix's test_view_init_after_max_size_split). - SYNC.md: this fork now syncs with ggml-org/llama.cpp directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Size the src1 padding from the tile width the kernel uses: for MUL_MAT_ID with broadcast src1 (ne11 == 1) the old padding was 0 while the tile width follows ne12, so the last tile read past the quantized src1 buffer. Also pad ids_dst for the unconditional loads. Hand port of fc9ce6b without the prec_src1 changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adapt upstream ggml-org#29953: pick J_best/nthreads on the host before buffer allocation, pad src1_q8_1 to a full J tile rounded to the load chunk, pad ids_dst to ne_get_rows + J_best - 1, drop ggml_cuda_mmq_get_J_max. Local scatter/dedup, expert_bounds and precision selection are preserved. Port the structural part of upstream ggml-org#30168 per user request: prec_src1 is an explicit parameter through the MMQ template chain (Q8 at call sites, Q4 in FP4 functions); GGML_PREC_Q8/Q4 added to enum ggml_prec. Config tables do not branch on it (the ggml-org#24364 prec policy is not ported), behavior and kernel dispatch on NVIDIA are unchanged. Verified: compute-sanitizer A/B (baseline reproduces OOB in dense, MoE and ids_dst cases on Ada/Ampere, candidate is clean 18/18 on both GPUs), test-backend-ops MUL_MAT/MUL_MAT_ID 2178/2178 on Ada and Ampere, GLM-5.3 IQ3_XXS model control with MTP gives byte-identical outputs.
Fixup to #29941 . See comments starting at #27044 (comment) .
I was unable to reproduce further issues locally but looking at the way I implemented the utility function
ggml_cuda_mmq_get_J_maxit is definitely wrong on master in a way that is consistent with the reported issues. On master the number of columns that are being checked for viable configs is being rounded down for the allocation but rounded up when selecting the actual config to run.The lowest-effort fix would be to modify
ggml_cuda_mmq_get_J_maxso that the rounding is consistent. However, the fact that the rounding can be inconsistent in the first place is a design flaw. So this PR instead moves the code for determining the optimal tile size forward and directly uses that to calculate how much padding is needed. This also plays nicer with recent efforts to modify the logic for selecting the optimal tile size for MoE models.The way
fallbackis being calculated on master can in principle also be inconsistent so I deduplicated it as well.Requirements