Skip to content

fix(build): rebuild lsp_all.o when its included headers change#662

Open
KerseyFabrications wants to merge 1 commit into
DeusData:mainfrom
KerseyFabrications:fix/rust-lsp-call-push-overread
Open

fix(build): rebuild lsp_all.o when its included headers change#662
KerseyFabrications wants to merge 1 commit into
DeusData:mainfrom
KerseyFabrications:fix/rust-lsp-call-push-overread

Conversation

@KerseyFabrications

Copy link
Copy Markdown
Contributor

Fixes #661

Problem

On an incremental build, make can link a stale lsp_all.o. ASan surfaces it as a stack-buffer-underflow during Rust-LSP tests:

ERROR: AddressSanitizer: stack-buffer-underflow ... in __interceptor_memcpy
  #1 cbm_calls_push internal/cbm/cbm.c:128
  #2 rust_inject_syn_call internal/cbm/lsp/rust_lsp.c:3366
  ...
  'call' (line 3363) <== Memory access at offset 0 partially underflows this variable

Root cause

lsp_all.o is a unity object that #includes every lsp/*.c resolver; those include cbm.h, and CBMCall is copied by value across the lsp→pipeline boundary (cbm_calls_push). Its Make rule depended only on lsp_all.c, not on cbm.h. After CBMCall gained bool is_method (#477), incremental builds kept a stale lsp_all.o compiled against the old 304-byte layout, while the rest of the tree recompiled against the new 312-byte layout — an ODR mismatch, so the by-value struct copy under-reads the shorter stale struct.

Clean builds (CI) always recompile both sides together, so the layouts match and CI never sees it. It only bites incremental developer builds after the struct changed.

Fix

Add an LSP_UNITY_DEPS prerequisite list (lsp/*.c, lsp/*.h, lsp/generated/*.c, internal/cbm/*.h) to both lsp_all.o and prod_lsp_all.o, so the unity object rebuilds when any included header or unity source changes. Wildcard-based, so new LSP resolvers (e.g. an added perl_lsp.c) are covered automatically. Explicit prerequisites rather than -MMD auto-deps, to match the Makefile's existing style.

Verification

  • touch internal/cbm/cbm.hlsp_all.o now rebuilds (it didn't before); no-op rebuild is a clean no-op.
  • Clean build + full ASan test suite green; rustlsp_dbg_macro_inner_call no longer aborts.

lsp_all.o is a unity object that #includes every lsp/*.c resolver; those include
cbm.h, and CBMCall is copied BY VALUE across the lsp->pipeline boundary
(cbm_calls_push). Its Make rule depended only on lsp_all.c, not on the headers it
pulls in. So after CBMCall gained a field (is_method, DeusData#477) an incremental build
kept a stale lsp_all.o with the old struct layout while the rest of the tree
recompiled against the new one -- an ODR mismatch ASan flags as a
stack-buffer-underflow in cbm_calls_push (the by-value copy under-reads the
shorter stale struct). Clean builds (CI) always match, so it only bites
incremental developer builds.

Add an LSP_UNITY_DEPS prerequisite (lsp/*.c + lsp/*.h + lsp/generated/*.c +
internal/cbm/*.h) to lsp_all.o and prod_lsp_all.o so the object rebuilds when any
included header or unity source changes. Wildcard-based, so new LSP resolvers are
covered automatically; explicit because this Makefile has no -MMD auto-deps.

Verified: touching internal/cbm/cbm.h now rebuilds lsp_all.o (was stale before);
clean build + full ASan suite green, rustlsp_dbg_macro_inner_call no longer aborts.

Signed-off-by: Kris Kersey <kris@kerseyfabrications.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lsp_all.o not rebuilt when cbm.h changes → stale-object ODR mismatch (ASan stack-buffer-underflow in cbm_calls_push)

1 participant