Skip to content

DeepSeek V4 speculative decode SIGSEGV in ggml_visit_parents_graph after client_disconnect prefix cache restore #677

Description

@dpavlin

DeepSeek V4 speculative decode SIGSEGV in ggml_visit_parents_graph after client_disconnect prefix cache restore

Summary

When an in-flight generation is aborted mid-stream (e.g. client disconnect / manual cancellation during an extended reasoning turn), the active prefix cache slot is left in an unfinalized/dirty state. When a subsequent request matches the prefix and performs a cache restore (restore=true slot=N), speculative decoding occasionally crashes with SIGSEGV (Signal 11) in ggml_visit_parents_graph due to corrupt/stale tensor pointer traversal during draft graph verification.


Motivation & Symptoms

In production and interactive agent use (e.g. pi-coding-agent), long reasoning turns can occasionally loop or produce thousands of tokens. When a user manually interrupts generation, the next prompt sent to the server may stop abruptly mid-sentence because dflash_server crashes with SIGSEGV and dumps core.

Timeline from Server Logs (aimax:8087):

  1. Request A (chatcmpl_...0010) aborted mid-generation:
    Aug 28 10:55:02 aimax dflash_server[1862]: [server] chat START chatcmpl_...0010 format=chat stream=true prompt_tokens=11338 max_tokens=9218 tools=7
    Aug 28 10:59:36 aimax dflash_server[1862]: [server] client disconnected — generation aborted (prompt=11338 out=3867)
    Aug 28 10:59:36 aimax dflash_server[1862]: [server] chat DONE chatcmpl_...0010 ok=true in=11338 effective_in=11338 out=3868 273.7s 14.1 tok/s finish=client_disconnect restore=true slot=1 prefix_len=2162
    
  2. Request B (chatcmpl_...0011) restores slot 1:
    Aug 28 11:00:53 aimax dflash_server[1862]: [server] chat START chatcmpl_...0011 format=chat stream=true prompt_tokens=11382 max_tokens=4634 tools=7
    Aug 28 11:00:53 aimax dflash_server[1862]: [pc] lookup hit slot=1 prefix_len=2162 (of 11382 total)
    Aug 28 11:00:53 aimax dflash_server[1862]: [server] chat CACHE chatcmpl_...0011 restore=true slot=1 prefix_len=2162
    
  3. Crash at Speculative Decode Step 96:
    Aug 28 11:02:20 aimax dflash_server[1862]: [ds4-spec-t] step=96 q=4 acc=4 | draft=15.9 head=3.2 save=2.8 verify=142.4 ...
    Aug 28 11:06:20 aimax systemd[1]: lucebox.service: Main process exited, code=dumped, status=11/SEGV
    Aug 28 11:06:20 aimax systemd[1]: lucebox.service: Failed with result 'core-dump'.
    

Debugging Methodology on Memory-Constrained / UMA Systems (128 GB)

When debugging large LLM inference servers (e.g. DeepSeek V4 on 128 GB UMA where the process holds ~115–120 GB RAM):

  1. Avoid Live GDB Attachment to Coredump:
    • The coredump on disk is ~71.2 GB (/var/lib/systemd/coredump/core.dflash_server...zst).
    • Loading the coredump in GDB while lucebox.service is running (holding ~120 GB RAM) will immediately trigger the kernel OOM killer.
  2. Safe Core Extraction Procedure:
    # 1. Stop service to free physical RAM (~123 GB free)
    sudo systemctl stop lucebox.service
    
    # 2. Check metadata without loading full memory image
    coredumpctl info
    
    # 3. Extract backtrace in batch mode
    coredumpctl debug /home/dpavlin/lucebox/server/build-hip/dflash_server \
        --debugger-arguments='-batch -ex "info proc mappings" -ex "bt full" -ex "thread apply all bt"'
    
    # 4. Restart service
    sudo systemctl start lucebox.service
  3. Address Resolution & Disassembly:
    • Stack trace identified crash at 0x7fc67eb7f793 in libggml-base.so.0.9.11 (base 0x7fc67eb24000, offset 0x5b793).
    • addr2line -e libggml-base.so.0.9.11 0x5b793 -f -C:
      ggml_visit_parents_graph
      ggml.c:7670
      
    • Faulting instruction:
      0x5b793 <ggml_visit_parents_graph+0x2a3>: mov 0x98(%rcx), %rsi  ; reading node->src[k] from invalid pointer %rcx

Reproduction Observations & State

Preconditions Observed During Live Failure:

  • Speculative decoding active: DFLASH_DS4_SPEC=1 with DSpark drafter (DeepSeek-V4-Flash-DSpark-draft-Q4RMFP4-denseF16.gguf).
  • Prefix caching enabled: --prefix-cache-slots >= 1.
  • High-context multi-turn session with tools: ~11,382 prompt tokens.
  • Deep generation before abort: Request 0010 ran for ~4.5 minutes generating 3,868 speculative tokens before client disconnection.
  • Subsequent restore & deep verification: Request 0011 restored slot=1 (prefix_len=2162), prefilled 9,220 tokens suffix, and crashed at step 96 of speculative decode.

Note: Short synthetic scripts that abort after a few tokens do not reliably reproduce the crash because the memory corruption depends on deep speculative draft-tree mutations across thousands of tokens on the active slot.


Relationship to Prior Issues & PRs

  • PR #371 (fix(server): drop stale prefix-cache entries when a snapshot slot is reused): Addressed stale entries left behind when snapshots abort, but did not handle active in-flight slot rollback on client disconnect.
  • PR #231 (fix(dflash): abandoned stream drain): Fixed daemon pipe synchronization on disconnect, but did not invalidate dirty KV/speculative state in the restored slot.
  • PR #584 (Fix long-running SSE client disconnect during spec decode): Handled SSE disconnect timeouts during spec decode.

Proposed Remediation

In server/src/server/http_server.cpp (around line 4186):
When client_disconnected == true on a request using a prefix cache slot, explicitly invalidate the slot and purge its metadata so subsequent requests do not restore from unfinalized speculative state:

if (client_disconnected) {
    std::fprintf(stderr, "[server] client disconnected — generation aborted "
                 "(prompt=%zu out=%d)\n",
                 req.prompt_tokens.size(), completion_tokens);
    if (cache_slot >= 0) {
        backend->invalidate_slot(cache_slot);
        forget_inline_slot_metadata(cache_slot);
    }
}

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