mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
The previous fix put -include stdint.h on the lane's global SANITIZE line. That traded one vendored compile break for another: force-including a libc header ahead of every source file freezes glibc's feature-test macros before sqlite3.c can set _GNU_SOURCE for itself, and its view of libc loses MREMAP_MAYMOVE and nanosleep (17 errors on the x86-64 CI leg). The workaround only ever had one legitimate target -- the zstd amalgamation whose MEMORY_SANITIZER block lost its stdint re-include -- so it now rides a per-object hook (ZSTD_EXTRA_CFLAGS) that the MSan lane sets and every other build leaves empty. sqlite3.c compiles exactly as before in every lane. Verified locally that zstd compiles with the hook and the default rule stays untouched; the MSan leg itself is x86-64-only, so CI is its verification venue. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
144 lines
7.7 KiB
Bash
Executable File
144 lines
7.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# msan.sh — MemorySanitizer lane (stage 2: full coverage incl. the C++ paths).
|
|
#
|
|
# Runs inside the cbm-msan image (test-infrastructure/Dockerfile.msan), which
|
|
# provides MSan-instrumented libc++/libc++abi/libunwind and zlib in /opt/msan.
|
|
# Vendored C deps compile in-tree and are instrumented by the build itself.
|
|
#
|
|
# MSan detects uninitialized READS — the one memory-error class ASan/LSan and
|
|
# the clang-analyzer lane do not cover dynamically. halt_on_error stays ON:
|
|
# a finding is a bug (or an interceptor gap to triage), never board data.
|
|
#
|
|
# VENUES: the CI test-msan job (x86-64) is AUTHORITATIVE and runs with no
|
|
# exclusions. The local container is arm64, where deep-recursion suites
|
|
# overflow their thread stacks under instrumentation — MSan's shadow/stack
|
|
# handling is materially better supported on x86-64, so that limitation may
|
|
# be architectural, and the local ladder has no faithful x86-64 emulation to
|
|
# decide it. An accepted venue divergence for this lane specifically: the
|
|
# exclusions below are the LOCAL default only, and CI overrides them away.
|
|
#
|
|
# Usage: scripts/msan.sh [suite ...] (default: full suite)
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
cd "$ROOT"
|
|
|
|
MSAN_PREFIX="${MSAN_PREFIX:-/opt/msan}"
|
|
if [ ! -d "$MSAN_PREFIX/lib" ]; then
|
|
echo "FATAL: MSan-instrumented runtime not found at $MSAN_PREFIX (build test-infrastructure/Dockerfile.msan)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# -isystem: the image deliberately has no system zlib (so the instrumented
|
|
# one cannot be shadowed); its headers live under the MSan prefix.
|
|
# MSAN_ORIGINS: 2 = full origin chains (best reports, heaviest frames),
|
|
# 1 = immediate origin only, 0 = none. Detection is IDENTICAL at every level;
|
|
# only report quality differs, so lowering it is the first lever to try when
|
|
# frame inflation overflows a deep-recursion suite.
|
|
MSAN_ORIGINS="${MSAN_ORIGINS:-2}"
|
|
if [ "$MSAN_ORIGINS" = "0" ]; then
|
|
MSAN_ORIGIN_FLAG=""
|
|
else
|
|
MSAN_ORIGIN_FLAG="-fsanitize-memory-track-origins=$MSAN_ORIGINS"
|
|
fi
|
|
MSAN_SAN="-fsanitize=memory $MSAN_ORIGIN_FLAG -fno-omit-frame-pointer -isystem $MSAN_PREFIX/include"
|
|
# Scoped to the zstd object ONLY (see the ZSTD_EXTRA_CFLAGS note in
|
|
# Makefile.cbm): zstd's MSan block needs stdint.h that its amalgamation lost,
|
|
# but force-including it globally freezes glibc feature-test macros before
|
|
# sqlite3.c can set _GNU_SOURCE, breaking that compile instead.
|
|
ZSTD_EXTRA="-include stdint.h"
|
|
|
|
# Always clean: make does not encode flags into dependencies, so a build dir
|
|
# populated under different stdlib/sanitizer flags silently mixes objects
|
|
# (observed: a stage-1 probe's libstdc++ objects surviving into the libc++
|
|
# lane and producing an unattributable report). Correctness over speed here.
|
|
make -f Makefile.cbm clean-c BUILD_DIR=build/msan >/dev/null 2>&1 || true
|
|
|
|
make -j"$(nproc)" -f Makefile.cbm build/msan/test-runner \
|
|
CC=clang CXX=clang++ BUILD_DIR=build/msan \
|
|
SANITIZE="$MSAN_SAN" \
|
|
ZSTD_EXTRA_CFLAGS="$ZSTD_EXTRA" \
|
|
CXX_STDLIB_FLAGS="-stdlib=libc++ -nostdinc++ -isystem $MSAN_PREFIX/include/c++/v1" \
|
|
CXX_STDLIB="-L$MSAN_PREFIX/lib -Wl,-rpath,$MSAN_PREFIX/lib -lc++ -lc++abi"
|
|
|
|
export MSAN_OPTIONS="${MSAN_OPTIONS:-halt_on_error=1:print_stats=0}"
|
|
# Origin tracking inflates every frame; the grammar-corpus suites drive the
|
|
# deepest parser recursion in the tree. Raise what can be raised (main-thread
|
|
# stack via RLIMIT_STACK, worker stacks via the sanitized-build-only knob in
|
|
# cbm_thread_create) — see the exclusion note below for what this does NOT fix.
|
|
ulimit -s 262144 2>/dev/null || true
|
|
export CBM_THREAD_STACK_MB="${CBM_THREAD_STACK_MB:-256}"
|
|
export LD_LIBRARY_PATH="$MSAN_PREFIX/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
|
|
|
echo "=== MSan lane: $(clang --version | head -1) ==="
|
|
|
|
# KNOWN LOCAL (arm64) FAILURE — a bug to fix, NOT an accepted exclusion:
|
|
#
|
|
# grammar_regression, grammar_labels, pipeline each overflow their thread
|
|
# stack under instrumentation (in grammar_regression_all /
|
|
# grammar_label_goldens / githistory_coupling_limits_output). The lane runs
|
|
# them anyway by default and fails, because a sanitizer that skips code is
|
|
# asserting coverage it does not have.
|
|
#
|
|
# WHY (as far as it is understood): origin tracking inflates every frame, and
|
|
# these suites drive the deepest parser recursion in the tree. It looks like
|
|
# an INSTRUMENTATION limit rather than a product defect — the same corpora
|
|
# pass under ASan+UBSan on all three OS and in production builds — but that is
|
|
# a hypothesis, not a proven diagnosis.
|
|
#
|
|
# WHAT WAS TRIED:
|
|
# 1. RLIMIT_STACK 8→64→256 MiB. Verified applied in-container; no effect,
|
|
# the crash address barely moved. Stacks here are sized in code, so the
|
|
# rlimit never reaches them.
|
|
# 2. CBM_THREAD_STACK_MB (added to compat_thread.c for exactly this). First
|
|
# as a DEFAULT-only override — which silently did nothing, because
|
|
# worker_pool/runtime/main all pass an explicit size — then as a FLOOR on
|
|
# every thread, confirmed compiled in. Still overflows. That narrows it
|
|
# usefully: the crashing thread is not created through cbm_thread_create,
|
|
# and our tree has only one other pthread_create (daemon/bootstrap.c,
|
|
# unrelated), so the creator is most likely inside a vendored library or
|
|
# the test harness.
|
|
# 3. Clean-rebuild hygiene — which DID resolve a separate false report at
|
|
# preprocessor.cpp:168 (mixed libstdc++/libc++ objects).
|
|
#
|
|
# 4. MSAN_ORIGINS=1 (added above). Detection is identical at any origin
|
|
# level, so this is pure frame savings. It MOVED the wall rather than
|
|
# removing it: a full run at origins=1 got past both grammar suites and
|
|
# died later in githistory_coupling_limits_output, yet running the two
|
|
# grammar suites DIRECTLY at origins=1 still overflowed. Same flags, same
|
|
# binary, different outcome by run context — so the trigger is cumulative
|
|
# process state (thread ordinals T475 vs T486), not any one suite's depth.
|
|
#
|
|
# NEXT STEP: that context dependence is the real lead — it points at threads
|
|
# accumulating across a long-lived runner process rather than at recursion in
|
|
# any single suite. Find the creator of these threads (not cbm_thread_create,
|
|
# see 2 above) and give it a sanitized-build stack floor. Sharding the lane
|
|
# one suite per process would also sidestep it. Then delete this block.
|
|
# DEFAULT: EMPTY. A sanitizer lane covers the COMPLETE code or it is not a
|
|
# sanitizer lane — a pass over a subset asserts coverage it does not have.
|
|
# The suites below are known to overflow locally on arm64; that is a BUG TO
|
|
# FIX (see NEXT STEP), not a list to live with. MSAN_EXCLUDE exists only so
|
|
# an engineer can iterate on the rest while fixing it, e.g.
|
|
# MSAN_EXCLUDE="grammar_regression grammar_labels pipeline" scripts/msan.sh
|
|
MSAN_EXCLUDE="${MSAN_EXCLUDE:-}"
|
|
|
|
if [ "$#" -gt 0 ]; then
|
|
./build/msan/test-runner "$@"
|
|
elif [ -z "$MSAN_EXCLUDE" ]; then
|
|
./build/msan/test-runner
|
|
else
|
|
echo "WARNING: running a PARTIAL sanitizer lane — excluded: $MSAN_EXCLUDE" >&2
|
|
echo "WARNING: a green result here does NOT mean the tree is MSan-clean." >&2
|
|
excl_pattern="$(printf '%s\n' $MSAN_EXCLUDE | tr '\n' '|' | sed 's/|$//')"
|
|
suites="$(./build/msan/test-runner --list-suites | grep -Evx "$excl_pattern")"
|
|
# Fail loudly if an entry matched no suite: a typo (or a TEST name given
|
|
# where a SUITE name is required) would otherwise exclude nothing silently.
|
|
for e in $MSAN_EXCLUDE; do
|
|
./build/msan/test-runner --list-suites | grep -qx "$e" || {
|
|
echo "FATAL: MSAN_EXCLUDE entry '$e' is not a suite name" >&2; exit 1; }
|
|
done
|
|
echo "--- excluded: $MSAN_EXCLUDE (see the comment in $0) ---"
|
|
# shellcheck disable=SC2086 # deliberate word-splitting of the suite list
|
|
./build/msan/test-runner $suites
|
|
fi
|