fix: RISC-V test portability and exec fd-redirect argv[0] preservation#147
Merged
Conversation
…i tests RISC-V glibc and musl put the dynamic loader under /lib and have no /lib64 at all. These tests granted /lib64 as a mandatory read (a plain -r flag, a profile `read` entry, or an ffi fs_read call), so confinement aborted on such hosts and the tests failed even though the sandbox is fine. The builder-based tests already handled this with fs_read_if_exists; the CLI, profile, and ffi layers had no equivalent. Add /lib64 only when the host actually has it, mirroring fs_read_if_exists: - cli_test.rs: args_for_host() drops `-r /lib64` when /lib64 is absent. - profile_integration.rs: read_list() omits /lib64 from the TOML when absent. - ffi popen.rs/restore.rs: skip /lib64 in the fs_read loop when absent. - ffi handler_smoke.rs: gate the /lib64 fs_read on host presence. - examples/openat_audit.rs: fs_read -> fs_read_if_exists. On x86-64 (where /lib64 exists) the resulting arguments and policies are unchanged; all cli_test and profile_integration cases still pass.
…per install
Two RISC-V-only integration failures, unrelated to /lib64:
- test_seccomp_cow_statx_created_file hardcoded the x86-64 statx syscall
number (332) with only an aarch64 (291) special case. RISC-V uses the
asm-generic syscall table where statx is 291 (like aarch64), so the raw
syscall returned ENOSYS (errno 38). Match the file's own
`machine() in ('aarch64', 'riscv64')` convention already used at lines
282 and 342.
- test_chroot_* failed with ETXTBSY when build_test_rootfs fell back from
hard_link to fs::copy. That fallback fires when the temp rootfs lands on
a different filesystem than the compiled helper (e.g. /tmp on tmpfs);
the copy's writable fd can be inherited by a concurrent fork+exec in
another parallel test, leaving the helper open for write when it is
execve'd. Place the temp rootfs under CARGO_TARGET_TMPDIR (same
filesystem as the helper) so the atomic hard_link path is taken and no
writable fd is ever opened on the executable.
…th to /proc/self/fd/N Signed-off-by: Cong Wang <cwang@multikernel.io>
…tils portability Signed-off-by: Cong Wang <cwang@multikernel.io>
…ath rewrite Signed-off-by: Cong Wang <cwang@multikernel.io>
congwang-mk
force-pushed
the
fix/riscv-test-portability
branch
from
July 15, 2026 23:28
be654d7 to
a38646b
Compare
Signed-off-by: Cong Wang <cwang@multikernel.io>
Signed-off-by: Cong Wang <cwang@multikernel.io>
congwang-mk
force-pushed
the
fix/riscv-test-portability
branch
from
July 15, 2026 23:30
a38646b to
7d18c24
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes all of the RISC-V test failures, including one product bug that the RISC-V environment exposed but that exists on every architecture. Full suites verified on x86-64 (lib, integration, spec, ffi handler and C smoke tests); the argv[0] clobber reproduces on x86-64 and is regression-tested there.
1.
/lib64as an optional read grant (586b5cd, 3b98496)RISC-V glibc and musl put the dynamic loader under
/liband have no/lib64at all, so a mandatory/lib64read grant aborts confinement. The builder-based tests already usedfs_read_if_exists; the CLI, profile, and ffi layers had no equivalent:cli_test.rs:args_for_host()drops-r /lib64when absent.profile_integration.rs:read_list()omits/lib64from the TOML when absent.popen.rs/restore.rs/handler_smoke.rs: skip thefs_readwhen absent.examples/openat_audit.rs:fs_read->fs_read_if_exists.tests/c/handler_smoke.c(the pure-C smoke test) grantedfs_read("/lib64")unconditionally, sosandlock_run_with_handlersreturned NULL on riscv64. It now grants/lib64only whenaccess(2)finds it.On x86-64 (where
/lib64exists) the resulting arguments and policies are unchanged.2. statx syscall number + rootfs-helper ETXTBSY (8275206)
test_seccomp_cow_statx_created_filehardcoded the x86-64statxnumber (332) with only an aarch64 (291) special case; RISC-V uses the asm-generic table wherestatxis also 291, so the raw syscall returned ENOSYS. Use the file's existingmachine() in ('aarch64', 'riscv64')convention.test_chroot_*failed with ETXTBSY whenbuild_test_rootfsfell back fromhard_linktofs::copy(temp rootfs on a different filesystem than the compiled helper): the copy's writable fd can be inherited by a concurrent fork+exec in a parallel test, leaving the helper open for write when it isexecve'd. Place the temp rootfs underCARGO_TARGET_TMPDIR(same filesystem as the helper) so the atomichard_linkpath is taken.3. Exec fd-redirect clobbered argv[0]: product bug on all arches (73c4cf2, be654d7, 7d18c24)
The
coreutils: unknown program '3'failure was diagnosed on RISC-V hardware: Ubuntu rust-coreutils ships/bin/echoand/bin/catas uutils multicall binaries that dispatch onbasename(argv[0]).The COW and chroot exec handlers redirect an exec by rewriting the child's path buffer to
/proc/self/fd/N(a user-notif supervisor cannot change syscall registers). Shells pass the same buffer as the execve path andargv[0](dash execs./mwithpath == argv[0]), so the rewrite silently clobberedargv[0]on every architecture. GNU echo ignoresargv[0]and hid the bug; a multicall binary dispatches on it and fails with basename"3". Reproduced on x86-64 with an argv0-printing binary before fixing.The fix,
rewrite_exec_path_to_fd()inseccomp/notif.rs, plans the rewrite against the full argv and envp pointer arrays and relocates every string the write window touches, patching the affected pointer slots:All reads happen before any write, so relocated strings always carry their original bytes. String length is capped at the kernel's own
MAX_ARG_STRLEN; the 8-byte pointer-slot width is safe because the BPF filter kills non-native-arch syscalls before they reach the notif fd.Tests: 9 planner unit tests against fake child memory (aliased argv[0], packed argv[1], envp strings, tail overlap, window growth, array-in-window rejection, NULL arrays); a packed-argv integration test crafts the path and argv[1] in one buffer via raw
execve(2)and fails on the pre-fix code.4. Test portability against uutils coreutils (73c4cf2, 8d7b915)
test_seccomp_cow_exec_created_filecopiestests/rootfs-helperinstead of/bin/echo: a copied uutilsechodispatches onbasename(argv[0]) = "m"and fails even outside a sandbox. The helper is itself busybox-style argv[0]-dispatched, so the test both avoids depending on the host coreutils flavor and regression-tests the argv[0] preservation above.inject_bytes_delivers_synthetic_content_to_guestintercepts onlyopenat, but uutilscatstatx-es a path operand before opening it and failed on the host-nonexistent sentinel before the handler could fire. Feedcatvia stdin redirection so the intercepted open is the shell's bareopenat.