Skip to content

CUDA illegal memory access after long prefill with q4_0 K/V + FA (prism-b10770, regression from b10709) #346

Open

Description

@Powerful-Plantain

Build: prism-b10770-6684606 (commit 6684606ac), clean checkout, static
-DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=86 -DBUILD_SHARED_LIBS=OFF -DGGML_CUDA_FA_QUANTS=q4_0-q4_0,q8_0-q8_0,f16-f16,bf16-bf16
GPU: NVIDIA GeForce RTX 3070 Laptop GPU (sm_86, 8 GB), driver 595.91.07, CUDA 12.4, Ubuntu Server, GCC 15.2
Model: Ternary-Bonsai-2-27B-PTQ1_0.gguf (sha256 53107f530aa52eb0…)
Command:

llama-server -m Ternary-Bonsai-2-27B-PTQ1_0.gguf --host 127.0.0.1 --port 8081 -ngl 99 -c 65536 -np 1 -fa on -ctk q4_0 -ctv q4_0 --jinja --reasoning-budget 2048 --temp 1.0 --top-p 0.95 --top-k 20 --min-p 0.05 --threads 8

Default -b/-ub, no --spec-*, no drafter/MTP. Env: only CUDA_LAUNCH_BLOCKING=1; no other GGML_CUDA_* / LLAMA_ARG_* set (GGML_CUDA_FA_PREFILL_F16 from #330 is not in this tree).

Repro: single /v1/chat/completions request, needle-in-haystack prompt (code word at the start + repeated filler sentence + question at the end), max_tokens 3000.

Prompt ctx-checkpoints b10709 (9a9394a) b10770 (6684606ac)
~45K tokens default correct answer (prefill 271 t/s) crash right after prefill reaches 100%
~30K tokens default — crash right after prefill reaches 100%
~45K tokens 0 — correct answer (1 run)
~16K tokens default — no crash; 3000 tokens generated, empty content (reasoning budget not applied?)

With CUDA_LAUNCH_BLOCKING=1, the error surfaces in launch_fattn (fattn-common.cuh:1113) at the
cudaOccupancyMaxActiveBlocksPerMultiprocessor query, i.e. before the FA kernel launches — so the faulting
kernel appears to be one launched just before FA, at the transition from prefill (progress = 1.00) to the first
decode step. Without CUDA_LAUNCH_BLOCKING, the same crash reports at common.cuh:1905 (ggml_cuda_kernel_launch).

Short prompts and tool calling work on b10770, with a large speedup (decode 26 → 43 tok/s, prefill 271 → ~500 t/s).

Suspects from the changelog: #221 (in-place q4_0/q8_0 K/V FA), #312 (reuse evicted checkpoint buffers), #189, #318.

Note: the test server ran without --api-key-file on 127.0.0.1 only; the CORS warning in the log is expected.

Happy to run compute-sanitizer or test a branch if useful.

llama-server log — prism-b10770, 30K prompt, CUDA_LAUNCH_BLOCKING=1
0.01.556.460 I cmn  common_param: common_params_print_info: verbosity = 3 (adjust with the `-lv N` CLI arg)
0.01.556.698 W srv  llama_server: -----------------
0.01.556.699 W srv  llama_server: CORS is set to allow all origins ('*') and no API key is set
0.01.556.699 W srv  llama_server: this can be a security risk (cross-origin attacks)
0.01.556.699 W srv  llama_server: more info: https://github.com/ggml-org/llama.cpp/pull/25655
0.01.556.699 W srv  llama_server: -----------------
0.01.557.943 I srv    load_model: loading model '/srv/models/bonsai2-27b/Ternary-Bonsai-2-27B-PTQ1_0.gguf'
0.01.981.219 W common_fit_params: failed to fit params to free device memory: n_gpu_layers already set by user to 99, abort
0.03.288.518 I cmn          init: llama threadpool init, n_threads = 8
0.03.404.839 I srv    load_model: initializing, n_slots = 1, n_ctx_slot = 65536, kv_unified = 'false'
0.03.408.238 I srv          init: chat template supports preserving reasoning, consider enabling it via --reasoning-preserve
0.03.408.245 I srv  llama_server: model loaded
0.03.408.247 I srv  llama_server: listening on http://127.0.0.1:8081
0.04.038.511 I slot get_availabl: id  0 | task -1 | selected slot by LRU, t_last = -1
0.04.038.566 I slot launch_slot_: id  0 | task 0 | processing task, is_child = 0
0.07.811.803 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =   2090, progress = 0.07, t =   3.77 s / 553.90 tokens per second
0.11.470.420 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =   4138, progress = 0.14, t =   7.43 s / 556.79 tokens per second
0.15.232.956 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =   6186, progress = 0.20, t =  11.19 s / 552.60 tokens per second
0.19.104.219 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =   8234, progress = 0.27, t =  15.07 s / 546.54 tokens per second
0.23.078.968 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  10282, progress = 0.34, t =  19.04 s / 540.01 tokens per second
0.27.157.551 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  12330, progress = 0.41, t =  23.12 s / 533.33 tokens per second
0.31.342.018 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  14378, progress = 0.48, t =  27.30 s / 526.60 tokens per second
0.35.627.988 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  16426, progress = 0.54, t =  31.59 s / 519.98 tokens per second
0.40.020.194 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  18474, progress = 0.61, t =  35.98 s / 513.43 tokens per second
0.44.516.624 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  20522, progress = 0.68, t =  40.48 s / 506.99 tokens per second
0.49.111.772 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  22570, progress = 0.75, t =  45.07 s / 500.74 tokens per second
0.53.810.091 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  24618, progress = 0.82, t =  49.77 s / 494.62 tokens per second
0.58.612.051 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  26666, progress = 0.88, t =  54.57 s / 488.63 tokens per second
1.03.518.469 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  28714, progress = 0.95, t =  59.48 s / 482.75 tokens per second
1.05.834.579 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  29668, progress = 0.98, t =  61.80 s / 480.10 tokens per second
1.07.154.954 I slot print_timing: id  0 | task 0 | prompt processing, n_tokens =  30180, progress = 1.00, t =  63.12 s / 478.16 tokens per second
/opt/llama/prism-src/ggml/src/ggml-cuda/ggml-cuda.cu:108: CUDA error
1.07.250.129 E CUDA error: an illegal memory access was encountered
1.07.250.131 E   current device: 0, in function launch_fattn at /opt/llama/prism-src/ggml/src/ggml-cuda/template-instances/../fattn-common.cuh:1113
1.07.250.132 E   cudaOccupancyMaxActiveBlocksPerMultiprocessor(&max_blocks_per_sm, fattn_kernel, block_dim.x * block_dim.y * block_dim.z, nbytes_shared)
/opt/llama/prism-src/build/bin/llama-server(+0x1973356) [0x5dcfb5d68356]
/opt/llama/prism-src/build/bin/llama-server(+0x197394d) [0x5dcfb5d6894d]
/opt/llama/prism-src/build/bin/llama-server(+0x1973b30) [0x5dcfb5d68b30]
/opt/llama/prism-src/build/bin/llama-server(+0x9ec587) [0x5dcfb4de1587]
/opt/llama/prism-src/build/bin/llama-server(+0x17bcc30) [0x5dcfb5bb1c30]
/opt/llama/prism-src/build/bin/llama-server(+0x17bcfda) [0x5dcfb5bb1fda]
/opt/llama/prism-src/build/bin/llama-server(+0x9fc226) [0x5dcfb4df1226]
/opt/llama/prism-src/build/bin/llama-server(+0xa05305) [0x5dcfb4dfa305]
/opt/llama/prism-src/build/bin/llama-server(+0xa07ece) [0x5dcfb4dfcece]
/opt/llama/prism-src/build/bin/llama-server(+0x199725f) [0x5dcfb5d8c25f]
/opt/llama/prism-src/build/bin/llama-server(+0x689140) [0x5dcfb4a7e140]
/opt/llama/prism-src/build/bin/llama-server(+0x690e39) [0x5dcfb4a85e39]
/opt/llama/prism-src/build/bin/llama-server(+0x696516) [0x5dcfb4a8b516]
/opt/llama/prism-src/build/bin/llama-server(+0x697d52) [0x5dcfb4a8cd52]
/opt/llama/prism-src/build/bin/llama-server(+0x269739) [0x5dcfb465e739]
/opt/llama/prism-src/build/bin/llama-server(+0x21c159) [0x5dcfb4611159]
/opt/llama/prism-src/build/bin/llama-server(+0x27aabe) [0x5dcfb466fabe]
/opt/llama/prism-src/build/bin/llama-server(+0x27ce55) [0x5dcfb4671e55]
/opt/llama/prism-src/build/bin/llama-server(+0x21b316) [0x5dcfb4610316]
/opt/llama/prism-src/build/bin/llama-server(+0x1d1829) [0x5dcfb45c6829]
/opt/llama/prism-src/build/bin/llama-server(+0x1d37d7) [0x5dcfb45c87d7]
/usr/lib/x86_64-linux-gnu/libc.so.6(+0x2a601) [0x7fe26be2a601]
/usr/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x88) [0x7fe26be2a718]
/opt/llama/prism-src/build/bin/llama-server(+0x1c94d5) [0x5dcfb45be4d5]

Activity

  1. professorpalmer commented on Oct 10, 2026

    @professorpalmer

    A pointer for the bisect, from the details in the report: b10709 is good, b10770 crashes, and --ctx-checkpoints 0 avoids the crash.

    In prism-b10709-9a9394a..prism-b10770-6684606, one commit touches context checkpoints in the server:

    The others in that range that touch the server or speculative code are drafter changes (#334, #304), and this report runs without a drafter. So #312 is the first candidate: a reused checkpoint buffer that is smaller than the state written into it would fit an illegal access right after a long prompt, which is when checkpoints are created and evicted.

    For comparison: our fork (bonsai-ada-surgery, base before b10709) runs with up to 32 checkpoints per slot, plus checkpoints inside long messages, on 8 GB and 12 GB cards at 64k-262k. We have not seen this crash there. A revert of bf197c43d on b10770 would confirm or clear it.

  2. professorpalmer commented on Oct 10, 2026

    @professorpalmer

    Reproducing on an RTX 4070 (sm_89, 12 GB) with your command: prism-b10770-6684606 as is, then the same build with bf197c43d (#312) reverted. Results here within a few hours.

  3. professorpalmer commented on Oct 10, 2026

    @professorpalmer

    First result from the RTX 4070 (sm_89, 12 GB, CUDA 13, Windows): not reproduced with your command (-c 65536 -np 1 -fa on -ctk q4_0 -ctv q4_0 --jinja --reasoning-budget 2048 ..., default -b/-ub, no drafter), a needle prompt with the code word at the start, max_tokens 3000:

    Build ~28.5k-token prompt ~42.7k-token prompt
    prism-b10770-6684606 no error, server alive no error, server alive
    same with bf197c43d (#312) reverted no error, server alive no error, server alive

    Your 16k observation does reproduce here, though: with --reasoning-budget 2048 all 3000 tokens went to thinking and content was empty, on both builds and both prompts. So the budget is not applied on b10770 with this command. That is a separate problem from the crash.

    Differences from your machine: 12 GB vs 8 GB of VRAM, sm_89 vs sm_86, CUDA 13 vs 12.4. Next I hold ~4 GB of VRAM with a second process, so the server has about what an 8 GB card has, and run the same pair again. A compute-sanitizer --tool memcheck run on your machine would name the faulting kernel directly (it is slow, so the 30k prompt is enough).

  4. Powerful-Plantain commented on Oct 10, 2026

    @Powerful-Plantain
    Author

    Thanks for the pointer. Tested it: reverting bf197c43d (#312) does not fix the crash.

    git revert --no-commit bf197c43d on prism-b10770-6684606 (applies cleanly, only tools/server/server-context.cpp), clean rebuild, same flags, CUDA_LAUNCH_BLOCKING=1:

    Prompt b10770 b10770 − #312
    ~30K tokens crash crash — same illegal memory access, same location (launch_fattn, fattn-common.cuh:1113)
    ~45K tokens crash crash — same
    ~16K tokens empty content, finish_reason: length same

    So #312 is cleared. --ctx-checkpoints 0 still avoided the crash in my one run, so whatever faults seems to interact with checkpoint creation/restore rather than buffer reuse — on a hybrid model that would include the recurrent (GDN) state. Remaining candidates in range that touch this path on sm_86: #221, #318, #189, #216, #214.

    Happy to revert any of those individually, or run a git bisect across prism-b10709-9a9394a..prism-b10770-6684606 (~6 steps; each build takes ~18 min on this machine). Just say which you'd prefer.

  5. professorpalmer commented on Oct 10, 2026

    @professorpalmer

    Second round on the RTX 4070, with 4 GiB of VRAM held by a second process (7,755 MiB free for the server, about what your 8 GB card has), and prompts sized to yours:

    Build prompt result
    prism-b10770-6684606 28,484 / 42,704 tokens no error, server alive
    same, #312 reverted 28,484 / 42,704 tokens no error, server alive
    prism-b10770-6684606 30,176 / 44,954 tokens no error, server alive

    So on sm_89 / CUDA 13 / Windows it does not reproduce, with or without VRAM pressure, and with or without #312. What is left that only your machine has: sm_86 kernels, CUDA 12.4, Linux, a laptop GPU with the display possibly on it. Two runs on your side would locate it:

    1. compute-sanitizer --tool memcheck ./llama-server ... with the 30k prompt. It reports the faulting kernel and the address.
    2. If that is too slow: the same command with --ctx-checkpoints 0 crossed with -ub 256. Your table already shows that checkpoints 0 avoids the crash; -ub 256 changes how the last part of the prompt is split, which tells whether the crash depends on the batch shape at the end of the prompt.

    Scripts: bench/prism346_repro.sh and tools/vram-ballast/ in professorpalmer/bonsai-ada-surgery.

  6. Powerful-Plantain commented on Oct 10, 2026

    @Powerful-Plantain
    Author

    Thanks for testing on the 4070. Results from my machine (RTX 3070 Laptop, sm_86, CUDA 12.4, Linux):

    Environment: no display on the dGPU (nvidia-smi -q: Display Active: Disabled); the only process on the GPU is llama-server; compute-sanitizer is available.

    Matrix on prism-b10770-6684606, 30,184-token prompt, CUDA_LAUNCH_BLOCKING=1:

    ctx-checkpoints -ub -c result
    default 256 65536 crash (same location)
    0 512 65536 correct answer
    0 512 65536 correct answer (repeat)
    0 256 65536 correct answer
    default 512 32768 crash
    • Batch shape at the end of the prompt doesn't matter: -ub 256 crashes the same way.
    • Checkpoints are the switch: with --ctx-checkpoints 0, 4/4 runs pass (30K and 45K); with default checkpoints, every run ≥30K crashes (6/6, including the two with server : reuse the buffers of evicted prompt checkpoints #312 reverted).
    • Context size doesn't matter: it reproduces at -c 32768.

    Running compute-sanitizer --tool memcheck on the 30K prompt overnight (at -c 32768 for VRAM headroom); will post the faulting kernel and address.

  7. professorpalmer commented on Oct 10, 2026

    @professorpalmer

    Thanks, that matrix narrows it a lot: checkpoints on/off is the switch, and #312 is cleared (it crashes with #312 reverted).

    Why checkpoints matter here: with checkpoints on, the server processes the last 4 tokens of the prompt (and the last 4 + n_ubatch) as separate small batches, so it can save a checkpoint just before the end (checkpoint_offsets[] = {4 + n_ubatch, 4} in server-context.cpp). With --ctx-checkpoints 0 the last batch is a normal one. So the first new thing after "progress = 1.00" is a 4-query attention op over ~30k q4_0 cells, plus the recurrent-state copy for the checkpoint.

    Two more variants on the RTX 4070, same command, 30,176- and 44,954-token prompts, 4 GiB of VRAM held, same prompt split as yours (… 29,660 → 30,172, then the 4-token batch):

    Build result
    b10770 compiled for sm_86 only (your SASS) no error, server alive
    b10770 sm_89 with the scheduler told the GPU has 40 SMs (your card's count, so the stream-k split of the attention kernels is the same as yours) no error, server alive

    So neither the sm_86 code nor the 40-SM work split reproduces it here. What remains is specific to your machine: CUDA 12.4 / driver 595 on Linux. The sanitizer run will say which kernel it is. If it is not conclusive, a bisect on your side is short: prism-b10709..prism-b10770 is 52 first-parent commits (about 6 builds). The ones that touch this path, to try first as single reverts on b10770:

    Separate finding, also on b10770 here: --reasoning-budget 2048 is not applied with this command (all 3000 tokens are thinking, content empty), which matches your 16k observation.

  8. Powerful-Plantain commented on Oct 11, 2026

    @Powerful-Plantain
    Author

    memcheck found it. compute-sanitizer --tool memcheck on prism-b10770-6684606, 30,184-token prompt, -c 32768, default checkpoints:

    Invalid __global__ write of size 2 bytes
        at void dequantize_block_q4_0<__half>(const void *, T1 *, int)+0x4b0
        by thread (0,0,0) in block (43003,0,0)
        Address 0x79097465f702 is out of bounds
        and is 95,747 bytes after the nearest allocation at 0x79096a000000 of size 174,358,784 bytes
    Host backtrace at launch:
        dequantize_row_q4_0_cuda<__half>(void const*, __half*, long, CUstream_st*)
        launch_fattn<256, 8, 8>(...)
        ggml_cuda_flash_attn_ext_mma_f16_case<256, 256, 8, 8, (ggml_type)1>
        ggml_cuda_compute_forward
        ggml_cuda_graph_evaluate_and_capture
        ggml_backend_cuda_graph_compute
        ggml_backend_sched_compute_splits
    

    So the fault is the q4_0 → f16 conversion of K/V inside launch_fattn for the MMA f16 kernel (D=256, 8×8): the f16 destination is ~95 KB smaller than what's written into it. That's the earlier kernel the CUDA_LAUNCH_BLOCKING trace couldn't name.

    My guess (not verified): with checkpoints on, the end of the prompt is evaluated in a batch shape that routes this op to the convert-to-f16 path instead of #221's in-place quantized read, and the scratch reserved for that conversion (ggml_cuda_flash_attn_ext_get_alloc_size after #221) is sized for less than the ~30K-cell K/V view. With --ctx-checkpoints 0 the path isn't hit. #189's short-query routing may also be involved.

    Full memcheck log available if useful. Happy to test a fix branch on the same setup.

  9. professorpalmer commented on Oct 11, 2026

    @professorpalmer

    Thanks, the memcheck trace pinned it. Fix in #350.

    With q4_0/q8_0 K/V and 3-4 queries, the short-query MMA route from #189 converts K and V to f16 in the op's extra buffer. But get_alloc_size (from #221) reserved no f16 buffer when the in-place quantized kernel exists. So the f16 K was written past the allocation: your dequantize_block_q4_0 from launch_fattn, mma_f16_case<256, 256, 8, 8, F16>. Checkpoints evaluate the last 4 prompt tokens as their own batch, and that is why --ctx-checkpoints 0 avoids it.

    On my 4070 the 3-query D=256 q4_0 case trips a new assert without the fix and passes with it. The whole FLASH_ATTN_EXT suite passes 4007/4007. Could you run your crashing command on branch fix-346-short-query-scratch (PR #350)?

  10. Powerful-Plantain commented on Oct 12, 2026

    @Powerful-Plantain
    Author

    Tested on the machine from #346 (RTX 3070 Laptop, sm_86, CUDA 12.4, Linux), e48c865d1 on top of prism-b10772-e8fd209, same flags, default context checkpoints, CUDA_LAUNCH_BLOCKING=1. Fixed.

    Test b10770 b10772 + #350
    ~30K needle crash correct, stop
    ~45K needle crash correct, stop
    ~16K needle empty content, length correct, stop
    short prompt ok ok, decode 42.4 tok/s
    tool call ok ok (tool_calls)

    No illegal access, no assert, no FA fallback warning in any log; 7083 MiB loaded. Against b10709 at 45K: prefill 271 → 437 t/s, decode 15.6 → 21.1 tok/s.

    The 16K runaway answer is gone too. I can't tell whether that was this fix or #340/#347 in the base.

    Thanks for the quick turnaround. Happy to run compute-sanitizer on this build if you want a 0-error confirmation.

    Now running this build in production; a live 35K-context Hermes session is clean.

  11. professorpalmer commented on Oct 13, 2026

    @professorpalmer

    Thanks for the full test on the 3070. All five cases pass, with no assert and no fallback warning. That closes the crash for #350.

    • Yes, please run compute-sanitizer --tool memcheck on this build with the ~30K prompt, if it is easy for you. A 0-error result is a good record on cuda: reserve the f16 K/V scratch for the short-query MMA route (#346) #350.
    • The 16K runaway answer may be this fix too, but I have not proven it. At 16K the checkpoint also evaluates the last 4 tokens as their own batch, so the same route runs. A write past the f16 buffer can land in another live allocation without a crash. That gives a wrong answer instead of an illegal access.
  12. Powerful-Plantain commented on Oct 14, 2026

    @Powerful-Plantain
    Author

    memcheck on the same 3070 (sm_86, CUDA 12.4, Linux): 0 errors.

    Binary: the installed production build, b10773 = e48c865d1 on prism-b10772-e8fd209 (sha256 identical to the build that passed the earlier tests). compute-sanitizer --tool memcheck, 30,184-token prompt, default context checkpoints, -c 32768, q4_0 K/V, FA on — the same setup that reported Invalid __global__ write in dequantize_block_q4_0<__half> on b10770.

    b10770 b10773 (#350)
    memcheck invalid write, 95,747 B past the f16 buffer ERROR SUMMARY: 0 errors
    answer crash correct, stop

    (The log ends with a CUDA error after "Received second interrupt" — my test script sent a second stop signal before the slow sanitized shutdown finished. That's after the request completed and isn't related to the fix.)

    That's consistent with your explanation of the 16K runaway as the same overflow landing in a live allocation. I've been running this build in production since the 12th, including Hermes sessions past 35K context — no issues. Thanks again for the quick fix.

  13. professorpalmer commented on Oct 14, 2026

    @professorpalmer

    Thank you. 0 errors on the same setup that showed the invalid write (95,747 B past the f16 buffer) on b10770 is the clear record we wanted for #350.

    #350 now has a merge conflict with the current base. I am rebasing it today and will post the new commit there. The fix itself does not change.

  14. professorpalmer commented on Oct 14, 2026

    @professorpalmer

    Update: the fix for this crash is in the PrismML base now, through #355 (it keeps the quantized K/V type in the short-query route, so the conversion that wrote past the buffer does not happen). I closed #350 as superseded. On the current base, all 4335 FLASH_ATTN_EXT op tests pass on an RTX 4070, including the 3-4 query cases with q4_0 / q8_0. A build from the current prism branch includes the fix, so you no longer need the #350 patch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions