core: build and run on musl targets - #224
Merged
Merged
Conversation
The libc crate types ptrace requests as c_uint on glibc but c_int on musl, ioctl requests as c_ulong versus c_int, and msg_controllen as size_t versus u32. The explicit glibc-shaped casts made a musl build fail with 19 type errors, so users wanting a static binary for Alpine had to fall back to a glibc base image. Let the constants carry their per-target type, cast ioctl requests to libc::Ioctl, and fold the one helper that named the request type into a PTRACE_CONT-only resume so no code has to spell the type at all. The statx test constant is spelled locally because the libc crate only exports it for glibc. Signed-off-by: Cong Wang <cwang@multikernel.io>
musl defines O_SEARCH as O_PATH and folds it into O_ACCMODE, and std's OpenOptions masks custom flags with !O_ACCMODE. On musl the Landlock rule open therefore lost O_PATH and became a real O_RDONLY open, so a read rule on a FIFO blocked child setup forever. Calling open(2) directly keeps O_PATH on every libc. Signed-off-by: Cong Wang <cwang@multikernel.io>
The scan for argv/envp strings that could run into the execve path buffer read the whole span from each candidate up to the buffer, then looked for a NUL. glibc's brk heap keeps that span mapped, but musl's allocator places allocations in separate mmap chunks with unmapped pages between them, so the read hit a hole and the child's execve failed with EFAULT under chroot. Check each page for a NUL before reading the next, which also matches what the kernel would copy. Signed-off-by: Cong Wang <cwang@multikernel.io>
congwang-mk
force-pushed
the
musl-build
branch
from
September 12, 2026 01:03
f9b5809 to
b6043bd
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.
cargo build --target x86_64-unknown-linux-muslfailed with 19 type errors, so anyone wanting a static binary for a small Alpine image had to fall back to a glibc base. The errors come from the libc crate typing the bindings differently per target, not from anything a build flag can change. Fixing the build then exposed two runtime bugs that only show on musl.core: build on musl targets. The libc crate types the ptrace request as
c_uinton glibc andc_inton musl, ioctl requests asc_ulongversusc_int, andmsg_controllenassize_tversusu32. The explicit glibc-shaped casts are dropped so the constants carry their per-target type, ioctl requests cast tolibc::Ioctl, and the one helper that names the ptrace request type sits behind a cfg alias. A test spelledSTATX_BASIC_STATSlocally because the libc crate only exports it for glibc.landlock: open rule paths with open(2), not OpenOptions. musl defines
O_SEARCHasO_PATHand folds it intoO_ACCMODE, and std'sOpenOptionsmasks custom flags with!O_ACCMODE. On musl the Landlock rule open silently lostO_PATHand became a realO_RDONLYopen, so a read rule on a FIFO blocked child setup forever. Callingopen(2)directly keepsO_PATHon every libc.seccomp: stop the exec rewrite scan at the first NUL. The scan for argv/envp strings that could run into the execve path buffer read the whole span up to the buffer before looking for a NUL. glibc's brk heap keeps that span mapped, but musl's allocator places allocations in separate mmap chunks with unmapped pages between them, so the read hit a hole and the child's execve failed with EFAULT under chroot (19 of 20 runs). Checking each page for a NUL before reading the next also matches what the kernel would copy.
Verified on x86_64: the musl CLI is a static-pie binary that passes
sandlock checkand enforces Landlock; the full workspace suite on musl and on gnu produce identical results, with the only failures being the environment-dependent tests addressed in #223 plus one hardlink test that fails the same way on main here.Not covered: the FFI cdylib is not produced for musl unless built with
-C target-feature=-crt-static, and no musl target is added to the release matrix yet.🤖 Generated with Claude Code