Repository navigation
Clang-built mcpp SIGSEGVs in ELF runtime inspection (validate_changed_artifacts); GCC-built does not #666
Description
Summary
A mcpp binary compiled by Clang 22.1.8 SIGSEGVs inside its own ELF runtime
inspector (mcpp::platform::elf::inspect_elf_runtime,
src/runtime/elf.cppm) when validating a build artifact. The same source,
compiled by GCC 16.1.0, does not crash on the same input. This is
pre-existing on main — reproduced with zero changes applied, at
2b42f744 — and is unrelated to the C-environment work in progress on
feat/c-environment / #667ish (found while verifying a release binary for
that PR).
Reproduction
git checkout 2b42f744 # main, unmodified
mcpp build --toolchain llvm@22.1.8 # build mcpp ITSELF with clang
Then use that freshly-built, Clang-compiled mcpp binary (not the one that
built it) to drive an ordinary build or test run. Two independent triggers
observed:
mcpp test test_targetside(or anymcpp test <pattern>) on this
repository itself.mcpp buildon a small fixture project with a dependency that has its own
build.mcpp(i.e. any project that exercises artifact validation after a
compile+link step).
Both SIGSEGV (rc=139, core dumped) with no further diagnostic printed.
Where it crashes
Program received signal SIGSEGV, Segmentation fault.
#0 std::__1::vector<unsigned char, ...>::__assign_with_sentinel<istreambuf_iterator<char>, istreambuf_iterator<char>>(...)
#1 mcpp::platform::elf::inspect_elf_runtime(std::filesystem::path const&)
#2 mcpp::platform::elf::resolve_runtime_closure(...)
#3 mcpp::build::runtime_validation::validate_changed_artifacts(...)
#4 mcpp::build::NinjaBackend::build(...)
#5 mcpp::build::run_tests(...) [or the plain-build call path]
#6 mcpp::cli::cmd_test(...)
...
inspect_elf_runtime reads the artifact via
reader.bytes.assign(std::istreambuf_iterator<char>(input), {})
(src/runtime/elf.cppm, three call sites: ~639, ~801, ~868) and the crash is
inside that vector::assign call itself — i.e. during the byte-by-byte read
of a just-built ELF file into a vector<unsigned char>.
The one variable that matters
Confirmed by building the same unmodified source two ways and testing
each resulting binary:
| mcpp binary built with | mcpp test test_targetside |
mcpp build on the artifact-validation fixture |
|---|---|---|
GCC 16.1.0 (mcpp build, default toolchain) |
passes (45/45) | passes |
Clang 22.1.8 (mcpp build --toolchain llvm@22.1.8) |
SIGSEGV | SIGSEGV |
Same machine, same source tree, same target/ inputs being validated —
only the compiler that built the mcpp binary differs.
CI coverage gap
ci-linux.yml's toolchain-cross job does self-host build mcpp with LLVM
(llvm@20.1.7, one minor version behind what I tested):
- name: "Toolchain: LLVM — build mcpp"
run: |
"$MCPP" toolchain install llvm 20.1.7
sed -i 's/^default = "gcc@16.1.0"/default = "llvm@20.1.7"/' mcpp.toml
"$MCPP" clean
"$MCPP" build 2>&1 | tee build.log; grep -q "Resolved llvm@20.1.7" build.logbut it only asserts that the build itself resolved LLVM and succeeded — it
never runs the resulting Clang-built mcpp binary against a second
project or test suite afterward. build-test, which does run mcpp test
and the per-subsystem suite, always does so with the GCC-built binary
(mcpp.toml's own [toolchain] default = "gcc@16.1.0"). So no CI job
exercises the combination that crashes: a Clang-built mcpp driving a real
build/test run. This looks like the reason it has gone unnoticed rather than
being new.
What I have not done
I have not attempted a fix — this was found incidentally while verifying a
release binary for unrelated work, and I don't have a diagnosis beyond the
above (it reads as heap corruption manifesting during an unrelated
reallocation, which would want a sanitizer run to actually localize, not
guesswork against elf.cppm's own bounds checks, which look correct on
inspection).
Suggested next step
A -fsanitize=address (or similar) build of mcpp under Clang, driven
through either repro above, should localize the actual out-of-bounds/UB
sooner than reading elf.cppm by eye.
Activity
Sunrisepeak commentedon Sep 17, 2026
Minimal repro, no fixture project needed: this repo's own
tests/unit/test_elf_runtime.cpp, built withmcpp test test_elf_runtime --toolchain llvm@22.1.8(test binary compiled by Clang 22.1.8, driven by a GCC-built mcpp), crashes the same way:(SIGSEGV surfaces here as
std::bad_alloc+ then a crash on the next test — samevector<unsigned char>allocation path as the original report.) Found running the full unit suite (mcpp test --toolchain llvm@22.1.8, driven by a GCC-built mcpp): 121/122 unit tests passed, this was the only failure. So this reproduces with no fixture project and no cross target at all — just this repo's own test suite, as long as the TEST BINARY (not necessarily the driver) is Clang-compiled. That's a narrower and more useful repro than my original one and probably the fastest path to a bisection or a sanitizer run.The C library layer declares the C environment it presents, the engin…