Skip to content

apply_logits_processors dereferences a cudaMalloc pointer on GB10: it gates on UnifiedMemory() where it needs DeviceMemoryIsHostAddressable() #1746

Description

@localai-bot

apply_logits_processors hands every ABI logits-processor callback a raw
cudaMalloc pointer on CUDA/GB10, because it gates the staging bounce on the
WIDE predicate. This is the #844 / #1435 / #960 bug class in a second location,
and the location that warns about it is in the same tree.

The chain

  • src/vllm/v1/sample/logits_processor/builtin.cpp:93 reads
    const bool unified = b.UnifiedMemory(); and on true does
    host = static_cast<float*>(logits.data); (:98), handing that pointer
    straight to the callback. The else arm stages down and back and is correct.
  • src/vt/cuda/cuda_backend.cu:363 constructs CudaBackend with
    caps.pageable_memory_access && caps.integrated, which is true on GB10.
  • src/vt/cuda/cuda_backend.cu:80-82 CudaBackend::Alloc is cudaMalloc.
    Measured: cudaMallocManaged appears 0 times in that file; cudaMalloc
    appears 4 (positive control).
  • CUDA never overrides DeviceMemoryIsHostAddressable(), so it is the base
    false at include/vt/backend.h:77. ROCm (rocm_backend.hip:371), Metal
    (metal_backend.mm:123) and Vulkan (vulkan_backend.cpp:135) all override it.

So on GB10 the predicate is true, the pointer is a plain device allocation, and
the callback dereferences it on the host.

The tree already documents this exact mistake

src/vt/op_provider.cpp:866-873:

THIS USED TO ASK UnifiedMemory(), AND THAT COST TWO CRASHES (#844, #1435).
The two properties are not the same [...] CUDA on GB10 reports unified memory
because host and device address the same physical RAM, yet a plain
cudaMalloc pointer is still not host-dereferenceable.

include/vt/backend.h:75-76 says a backend must OPT IN "because being wrong
here hands a device pointer to a host memcpy and segfaults".

The comment at builtin.cpp:90-91 asserts the opposite — "On a unified-memory
backend (CPU / GB10) logits.data IS host memory" — naming the one box
where it is false.

Why no test catches it

tests/vllm/v1/sample/test_logits_processors.cpp:32-33 runs only on
Device{kCPU,0}, where UnifiedMemory() is true and the pointer really is
host memory. The two predicates agree on CPU and disagree only on CUDA/GB10, so
a CPU-only suite cannot separate them. A red-first test needs a fake backend
reporting UnifiedMemory()==true and DeviceMemoryIsHostAddressable()==false,
asserting the staging arm is taken — that is reproducible on CPU and needs no GPU.

Impact

Blocks ENG-EXPERT-STREAM-DEVICE W0h (#1736), whose whole instrument is a
teacher-forcing logits processor run on both arms on dgx:gpu0; the CUDA arm
would SIGSEGV rather than produce a number. It is also a candidate cause for the
W0e completion-callback SIGSEGV currently recorded as "Still unexplained" in
.agents/specs/expert-stream-device-slots.md.

Any ABI client that installs vllm_logits_processor (ABI v8) on CUDA/GB10 is
affected, not only our harness.

Fix

builtin.cpp:93const bool unified = b.DeviceMemoryIsHostAddressable();
The staging path below is already correct, so this is a one-predicate change
plus the red-first test above. Found during the fresh review of PR #1738.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions