diff --git a/.claude/blackboard.md b/.claude/blackboard.md index dcd0e27d..6e89e148 100644 --- a/.claude/blackboard.md +++ b/.claude/blackboard.md @@ -1,3 +1,9 @@ +## 2026-10-13 — `cargo-careful` red since #355: the libc probe was ASan's `malloc` + +- **Cause:** `cpu_guard::tests::an_address_in_libc_is_not_in_the_main_executable` used `malloc` as its libc address. CI's `cargo-careful` job runs `-Zcareful-sanitizer` (AddressSanitizer), which interposes `malloc` with an interceptor linked into the executable, so the address really is in the main image there. Red on every master merge from #355 (where the test landed) through #361; green at #354. +- **Change:** the test probes glibc's `gnu_get_libc_version`, which no sanitizer replaces. The guard itself is unchanged. +- **Measured here:** scratch crate with `-Zsanitizer=address`: `malloc` in main image `true`, `gnu_get_libc_version` `false`, own function `true` (plain build: false/false/true). `cargo +nightly careful test -Zcareful-sanitizer`: new test passes; with `malloc` restored it fails at `cpu_guard.rs:712`, as in CI. Plain build: 14/14 `cpu_guard` tests. + ## 2026-10-13 — CI compiles both production tiers: x86-64-v4 and Sapphire Rapids - **Change:** `tier4-avx512-check` in `ci.yaml` is now a two-entry matrix, `x86-64-v4` (`.cargo/config-v4.toml`) and `sapphirerapids` (`.cargo/config-avx512.toml`). Each entry runs both `cargo check` feature sets and the asm-only codegen witness. Neither is executed: GitHub's runners are mostly v3. `config-avx512.toml` gains `-Dwarnings`, matching `config-v4.toml`, so both entries gate warnings and the witness script (which word-splits `CARGO_ARGS`) needs no extra quoted `--config`. The job sets `CARGO_PROFILE_{DEV,TEST}_DEBUG=0`: with `--target`, build scripts take the profile's debug level. diff --git a/src/cpu_guard.rs b/src/cpu_guard.rs index e15e4549..7e737694 100644 --- a/src/cpu_guard.rs +++ b/src/cpu_guard.rs @@ -692,11 +692,16 @@ mod tests { assert!(guard_is_in_main_executable()); } - /// Can-stay-silent half: an address in a loaded system library (libc's - /// `malloc`) is not in the main executable. Without this the lookup - /// could be `true` everywhere and the library case would never happen. - /// Assumes libc is a shared object (the default dynamic linux-gnu - /// build): with `+crt-static`, `malloc` is inside the executable. + /// Can-stay-silent half: an address in a loaded system library (glibc's + /// `gnu_get_libc_version`) is not in the main executable. Without this + /// the lookup could be `true` everywhere and the library case would never + /// happen. Assumes libc is a shared object (the default dynamic linux-gnu + /// build): with `+crt-static`, libc is inside the executable. + /// + /// Not `malloc`: a sanitizer interposes it with its own copy linked into + /// the executable, so under `cargo careful -Zcareful-sanitizer` (ASan) + /// `malloc` really is in the main image. No sanitizer replaces + /// `gnu_get_libc_version`. #[cfg(all( not(miri), target_arch = "x86_64", @@ -707,9 +712,10 @@ mod tests { #[test] fn an_address_in_libc_is_not_in_the_main_executable() { extern "C" { - fn malloc(size: usize) -> *mut core::ffi::c_void; + fn gnu_get_libc_version() -> *const core::ffi::c_char; } - assert!(!main_image::contains(malloc as unsafe extern "C" fn(usize) -> *mut core::ffi::c_void as usize)); + let in_libc = gnu_get_libc_version as unsafe extern "C" fn() -> *const core::ffi::c_char as usize; + assert!(!main_image::contains(in_libc)); } #[test]