Skip to content

fix(FIX-UNALIGNED-CONSUMERS-2540): read a borrowed 16-bit weight off a byte cursor, so the odd base costs nothing - #2581

Open
localai-org-maint-bot wants to merge 2 commits into
mainfrom
row/FIX-UNALIGNED-CONSUMERS-2540
Open

fix(FIX-UNALIGNED-CONSUMERS-2540): read a borrowed 16-bit weight off a byte cursor, so the odd base costs nothing#2581
localai-org-maint-bot wants to merge 2 commits into
mainfrom
row/FIX-UNALIGNED-CONSUMERS-2540

Conversation

@localai-org-maint-bot

Copy link
Copy Markdown
Collaborator

fix(FIX-UNALIGNED-CONSUMERS-2540): read a borrowed 16-bit weight off a byte cursor, so the odd base costs nothing

Closes #2540. Closes #2558. Closes #2578.

A safetensors payload starts at 8 + <JSON header length> and a header length is arbitrary, so a 16-bit weight begins on an ODD byte in roughly half of all checkpoints. That is an ordinary file. BorrowStTensorBytes hands those bytes to the kernel verbatim, which is what direct upload IS, and four CPU consumers then formed a const uint16_t* over the odd address:

Consumer Site Reached by
vt::cpu::WidenRowToF32 cpu_matmul_elem.cpp:577 the bf16 RMSNorm gamma of dots3_note and muse_glimmer (#2540)
HadRowBlock cpu_exl3_kernels.cpp:130 EXL3 suh / svh (#2558)
TileWord32 cpu_exl3_dequant.cpp:64 the EXL3 trellis, kI8 read as uint16_t (#2558)
WeightF32 qwen3_5.cpp:1028 a bf16 attention weight widened for the f32 kernels (#2578)

The load is undefined even where x86 executes it, and sanitize-cpu (address,undefined) aborts on it before reporting a single doctest assertion. All four now read through vt::LoadUnaligned off an unsigned char cursor — the shape vt::cpu::LoadF32 and cpu_layernorm.cpp already use, which at -O2 compiles to the load the raw index compiled to.

The trellis walk moves to byte arithmetic for the same reason: advancing a misaligned uint16_t* is undefined in its own right. Exl3TileCodeword, Exl3DecodeTile, Exl3ReconstructInner, Exl3DequantLinear and MoeGemm take a const void*, and the stride carries the sizeof(uint16_t) the pointer type used to supply. Every existing caller passes a uint16_t*, which converts implicitly, so no call site outside the repaired files changed. in, out and the activation keep their typed pointers: they are engine-allocated through std::aligned_alloc(64, ...), never a borrowed mapping, which is the polarity cpu_layernorm.cpp states.

Why the consumer and not the borrow

The alternative fix refuses to borrow a misaligned tensor, so the caller's MakeOwned + memcpy fallback copies into aligned memory. It works, and it switches direct upload off for every tensor in half of all checkpoints. Recomputed here from the direct-upload fixture at the base SHA: its header is 171 bytes, so the payload base is 179, and w (179), v (195) and f (211) are all odd. A producer-side gate therefore reds tests/vllm/test_load_direct_upload.cpp:142 (CHECK(w.bytes.borrowed())) and tests/vllm/models/test_qwen3_5_dense_load_residency.cpp:296. Both stay green here, and that is what separates the two fixes.

No alignment VT_CHECK is added to vt::Exl3Gemm either. After this change a misaligned operand is representable, so such a check would be the producer gate relocated one frame up: it would refuse the borrow path this change exists to preserve. A refusal must name something the kernel cannot do.

WeightF32 was not in scope when the work started. It is the same defect one frame further on, and it only became visible because -fno-sanitize-recover=all stops at the first report — repairing the EXL3 operands made test_qwen35_exl3 abort in WeightF32 on the very next run. Filed as #2578 and fixed here, because the target state is not reached without it. A grep finds 20 further sites of the shape in ten model files; whether any is reached with an odd base is UNMEASURED, so that is filed as #2579 and named under the spec's ## Owed rather than swept blind.

Evidence

Build: cmake -S . -B build-sanitize -DVLLM_CPP_BUILD_TESTS=ON -DVLLM_CPP_CUDA=OFF -DVLLM_CPP_SANITIZE='address,undefined', -j 4. Run: UBSAN_OPTIONS=print_stacktrace=1 ASAN_OPTIONS=detect_leaks=1:strict_string_checks=1 VT_POOL_BYPASS=1 ctest.

RED before, one site at a time. Each mutation restores only the pre-fix LOAD, leaving signatures and strides alone, so the red is the defect's and nothing else's; each asserts its own application, because a mutation that never applied reads as a pass.

Mutated site Reds Report
WidenRowToF32 test_muse_glimmer_text, test_dots3_note_attn, test_ops_rmsnorm_weight_dtype cpu_matmul_elem.cpp:598: load of misaligned address ...907 / ...7cd / ...081
HadRowBlock test_exl3_gemm, test_qwen35_exl3 cpu_exl3_kernels.cpp:142: load of misaligned address ...6ef
TileWord32 test_exl3_gemm, test_qwen35_exl3 cpu_exl3_dequant.cpp:78: load of misaligned address ...76b
WeightF32 test_qwen35_exl3 qwen3_5.cpp:1043: load of misaligned address ...cd3

GREEN after, on the restored tree (verified byte-identical to the fix): 11 of 11 pass, 0 misaligned address reports, 0 runtime error lines.

test_load_direct_upload            Passed
test_llama_embedding_fold          Passed
test_muse_glimmer_text             Passed
test_muse_glimmer_text_fallback    Passed
test_dots3_note_attn               Passed
test_openai_api_server             Passed
test_capi                          Passed
test_exl3_gemm                     Passed
test_qwen35_exl3                   Passed
test_ops_rmsnorm_weight_dtype      Passed
test_qwen3_5_dense_load_residency  Passed

scripts/agent-preflight.sh reports 0 gate failures with the same 5 environmental SKIPs the untouched base reports, and check-tree-compiles compiled 596 of 596 translation units in scope.

Tests

Two cases, each added to the suite that already owns its subject, so no tests/CMakeLists.txt row moves. Each REQUIREs the address parity it depends on before it asserts anything, so a case that quietly landed on an even address cannot read as a pass. Both compare BYTE-FOR-BYTE against the same operands at even addresses rather than within a tolerance: a byte cursor that lost its factor of two decodes a different weight, and a tolerance over a random-bit trellis could absorb that.

Spec: .agents/specs/unaligned-safetensors-consumers.md, committed before the implementation.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]

… safetensors bytes, not the borrow

A safetensors payload starts at `8 + <JSON header length>`, and a header length
is arbitrary, so a 16-bit weight begins on an odd byte in roughly half of all
checkpoints. That is an ordinary file. Three CPU consumers read those bytes
through a `const uint16_t*` and `-fsanitize=alignment` aborts on the load, which
is the second and third findings holding the `sanitize-cpu` lane red (#2540,
#2558).

The spec records why the fix goes in the consumer. Refusing to borrow a
misaligned tensor also takes the sanitizer green, and it switches direct upload
off for every tensor in half of all checkpoints: recomputed here from the
direct-upload fixture, whose 171-byte header puts `w` at 179, `v` at 195 and `f`
at 211, all odd. `vt::LoadUnaligned` costs nothing on that path, so the borrow
survives.

The bullet this row takes over is struck from the DEBTFIX spec's `## Owed`,
together with the guess it recorded. The root cause is not "wherever
`ResidentWeight` produced an odd `bytes.data()`"; it is the file, and a file is
not a defect.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
…a byte cursor, so the odd base costs nothing

Closes #2540. Closes #2558. Closes #2578.

`WidenRowToF32`, `HadRowBlock`, `TileWord32` and `WeightF32` each formed a `const uint16_t*`
over bytes the direct-upload borrow handed them straight out of an mmap. A
safetensors payload starts at `8 + <JSON header length>`, a header length is
arbitrary, and `vt::Exl3Gemm` types the trellis kI8, so an odd base is the
expected case rather than an exotic one. The load is undefined even where x86
executes it, and the `sanitize-cpu` lane aborts on it before reporting a single
assertion.

All four now read through `vt::LoadUnaligned` off an `unsigned char` cursor,
which is the shape `vt::cpu::LoadF32` and `cpu_layernorm.cpp` already use and
which compiles to the same instruction at `-O2`. The trellis walk moves to byte
arithmetic for the same reason: advancing a misaligned `uint16_t*` is undefined
in its own right, so `Exl3DecodeTile`, `Exl3ReconstructInner`,
`Exl3DequantLinear` and `MoeGemm` take a `const void*` and the stride carries
the `sizeof(uint16_t)` the pointer type used to supply. Every existing caller
passes a `uint16_t*`, which converts implicitly, so no call site changed.

`in`, `out` and the activation keep their typed pointers. They are
engine-allocated through `std::aligned_alloc(64, ...)`, never a borrowed file
mapping, which is the polarity `cpu_layernorm.cpp` states.

The alternative fix refuses the borrow. It works, and it turns direct upload off
for every tensor in half of all checkpoints -- the direct-upload fixture's
171-byte header puts all three of its tensors on odd addresses -- so it reds
`test_load_direct_upload` and `test_qwen3_5_dense_load_residency`. Both stay
green here, which is what separates the two fixes. No alignment `VT_CHECK` is
added to `vt::Exl3Gemm` for the same reason: after this change a misaligned
operand is representable, and a refusal must name something the kernel cannot
do.

`WeightF32` was not in the dispatch. It is the SAME defect one frame further
on, and it only became visible because `-fno-sanitize-recover=all` stops at the
first report: repairing the EXL3 operands made `test_qwen35_exl3` abort in
`WeightF32` on the very next run. It is filed as #2578 and fixed here, because
the target state is not reached without it. A grep finds 20 further sites of the
shape in ten model files; whether any is reached with an odd base is unmeasured,
so that is filed as #2579 and named under the spec's `## Owed` rather than swept
blind.

Two cases gate it, each in the suite that already owns its subject, and each
`REQUIRE`s the address parity it depends on before it asserts anything. They
compare byte-for-byte against the same operands at even addresses, because a
byte cursor that lost its factor of two decodes a different weight and a
tolerance over a random-bit trellis could absorb that.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5-1m [claude-code]
@localai-org-maint-bot

Copy link
Copy Markdown
Collaborator Author

Operator static review, recorded before the gate is rerun. I did not write this change.

Producer untouched — the criterion that matters

src/vllm/model_executor/models/qwen3_5_weights.cpp does not appear in the changed-file list at all, so BorrowStTensorBytes is genuinely unmodified and the zero-copy borrow survives. That is the difference from #2561, and it is checkable from the file list rather than from prose.

Pointer arithmetic verified by hand

The risk in this shape of change is a stride that looks right and is off by a factor of two. I re-derived every converted site against the arithmetic the const uint16_t* used to perform:

site before (uint16_t indexing) after (byte cursor) equal
TileWord32 tile[2*index], tile[2*index+1] → byte offsets 4*index, 4*index+2 tile + index*4, and + 2 yes
Exl3ReconstructInner trellis + (i*tiles_n+j) * (16*bits) words → * 2 bytes tile_bytes = 16*bits*sizeof(uint16_t), tw + (i*tiles_n+j)*tile_bytes yes
Exl3DequantLinear suh suh[i] → byte 2*i suh + i*2 yes
Exl3DequantLinear svh svh[j] → byte 2*j svh + j*2 yes

Exl3ReconstructInner keeps int64_t throughout, so the tile offset does not truncate on a large trellis.

On widening four signatures to const void*

This loses compile-time type checking: any pointer now converts implicitly, where previously only a uint16_t* did. I considered that a defect and concluded it is not, for one reason — const uint16_t* was overclaiming an alignment the data never had. vt::Exl3Gemm already states the trellis "travels as opaque i8 BYTES", so a 1-byte-aligned payload was being described by a type that asserts 2-byte alignment. const void* is the honest type for a borrowed kI8 payload. The header comment says so at each declaration.

The Exl3Gemm belts, correctly omitted

I agree with the decision not to port #2561's VT_CHECKs on suh/svh/trellis alignment. Once the consumers tolerate misalignment, a 2-byte-alignment assertion is #2561's producer gate moved one frame up: it would refuse ordinary checkpoints for a condition the kernel can now handle. A refusal must name something the code genuinely cannot do.

What is NOT verified here, and by what

This review is static. The functional claim — 11/11 green including test_load_direct_upload and test_qwen3_5_dense_load_residency, with zero misaligned address reports — rests on the implementer's local sanitizer run, and I am not treating that as the verdict. The sanitize-cpu (address,undefined) lane on this head is what settles it, and it has a precise predicate: the seven tests red tree-wide today (test_llama_embedding_fold, test_muse_glimmer_text, test_muse_glimmer_text_fallback, test_dots3_note_attn, test_openai_api_server, test_capi, test_qwen35_exl3) must all pass, and the two borrow assertions must not appear.

I confirmed that seven-test baseline independently on #2472's ASan lane earlier today: it failed on exactly those seven and nothing else.

Scope note: #2579 correctly leaves the twenty remaining casts of this shape unswept and owed. Changing unmeasured sites on the strength of a grep is how a sixth recurrence becomes a seventh.

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