brintos

brintos / hwjs-cc public Read only

0
0

wasmcc-glibc conftest fast path false-positives AC_CHECK_FUNCS(pledge) — glibc arm's import-witness allowlist too permissive #8

Closed jdbrinton opened this issue · 1 comment
jdbrinton jdbrinton commented

Incidental finding from tonight's linux-in-the-browser#85 investigation (agent for joel, 2026-07-14), while building xz 5.4.x from source with both driver arms:

wasmcc-glibc's conftest fast-link path answers yes to autoconf's AC_CHECK_FUNCS(pledge) (an OpenBSD-only syscall wrapper that glibc does not provide), so xz's configure set HAVE_PLEDGE and the build then failed at link/run until worked around with --enable-sandbox=no. The musl arm answers the same probe correctly (no).

Suspect: the glibc conftest symbol allowlist in bin/wasm-import-witness.mjs (or wherever the glibc arm's fast-path "function exists" decision is made) is too permissive for symbols glibc doesn't actually export.

Repro: wget any xz release tarball → ./configure with wasmcc-glibc env per docs/BUILDING.md → grep HAVE_PLEDGE config.h#define HAVE_PLEDGE 1 (wrong); same with the musl arm → undefined (right). Any AC_CHECK_FUNCS of a non-glibc symbol likely reproduces.

Class impact: silent misconfiguration of any autoconf package on the glibc arm — configure-time feature tests are load-bearing for the whole Yocto/meta-brintos pipeline.

jdbrinton jdbrinton commented

Fixed (agent for joel, 2026-07-15), 776bdec on joel-dev, pushed. Root cause was one step worse than filed: the glibc arm's WASMLD_HWJS_CONFTEST=1 fast path had no phantom-import witness at all — it linked probes with plain --allow-undefined (the old comment even admitted the ticket-#4 attestation was musl-arm only), and without --export-all the --no-entry probe image was fully GC'd, so there were zero imports to judge even in principle. Every function probe answered yes.

Fix mirrors the musl arm exactly: fast link adds -Wl,--export-all, then the image is attested by wasm-import-witness.mjs --mode=static against an allowlist of every symbol the real link resolves (glibc-bc pool + crt1 + coro substrate + libglibc_native.a + compiler-rt), cached at $GLIBC_BC_POOL/.conftest-resolvable-symbols.txt and rebuilt on provider mtime change. Archives kept off the fast link line (the #63 optimization) now speak through their llvm-nm defined-symbol lists instead of being dropped answer-blind. Probes stay ~0.1s; coroutinize never runs for conftests.

Validation: pledge → rc=1 "no" with attributed stderr (conftest probe references unresolvable symbol(s): env.pledge (function)) on both arms; clock_gettime/fork → yes on both; archive present/local/-lnosuch/-lm sin shapes all correct; test/conftest-archive-fastpath.sh GREEN+RED arms PASS; real glibc hello link+coroutinize smoke passes with the plugin rebuilt at 776bdec (provenance gate clean).

Known residual (pre-existing, BOTH arms, out of scope here): undefined data symbols still resolve silently to address 0 under --allow-undefined, so a probe taking the address of a nonexistent variable answers yes — function probes (the overwhelming autoconf shape) are now honest. Follow-up worth doing when convenient: re-run xz's configure on the glibc arm and drop the --enable-sandbox=no workaround. Closing.

jdbrinton closed this as completed