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:93 → const 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.
apply_logits_processorshands every ABI logits-processor callback a rawcudaMallocpointer on CUDA/GB10, because it gates the staging bounce on theWIDE 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:93readsconst bool unified = b.UnifiedMemory();and on true doeshost = static_cast<float*>(logits.data);(:98), handing that pointerstraight to the callback. The
elsearm stages down and back and is correct.src/vt/cuda/cuda_backend.cu:363constructsCudaBackendwithcaps.pageable_memory_access && caps.integrated, which is true on GB10.src/vt/cuda/cuda_backend.cu:80-82CudaBackend::AllociscudaMalloc.Measured:
cudaMallocManagedappears 0 times in that file;cudaMallocappears 4 (positive control).
DeviceMemoryIsHostAddressable(), so it is the basefalseatinclude/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:include/vt/backend.h:75-76says a backend must OPT IN "because being wronghere hands a device pointer to a host memcpy and segfaults".
The comment at
builtin.cpp:90-91asserts the opposite — "On a unified-memorybackend (CPU / GB10)
logits.dataIS host memory" — naming the one boxwhere it is false.
Why no test catches it
tests/vllm/v1/sample/test_logits_processors.cpp:32-33runs only onDevice{kCPU,0}, whereUnifiedMemory()is true and the pointer really ishost 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()==trueandDeviceMemoryIsHostAddressable()==false,asserting the staging arm is taken — that is reproducible on CPU and needs no GPU.
Impact
Blocks
ENG-EXPERT-STREAM-DEVICEW0h (#1736), whose whole instrument is ateacher-forcing logits processor run on both arms on
dgx:gpu0; the CUDA armwould 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 isaffected, not only our harness.
Fix
builtin.cpp:93→const 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.