Add user and workdir elfuse launch flags - #286
Conversation
9911813 to
cd98b4e
Compare
This comment was marked as resolved.
This comment was marked as resolved.
20a5e74 to
238f4de
Compare
69f6ff1 to
4ed9d1b
Compare
An OCI image front end needs to set the guest identity and working directory without patching the runtime; both flags map onto launch_args_t fields and `elfuse-oci run` drives exactly this interface. The --user identity is staged before bring-up (proc_set_initial_ids) so the auxv AT_UID/AT_GID snapshot taken by build_linux_stack matches what getuid()/getgid() later report. proc_identity_init consumes the staged value, and the elfuse_launch fail path calls proc_clear_initial_ids: a bring-up that fails before proc_init would otherwise leave the value staged for the next launch in the same host process. --workdir rejects non-absolute paths up front instead of silently resolving them against the host cwd, and is applied by elfuse_launch after the casefold probe so the translation sees the sysroot's real case behavior. The resolved host path must sit inside the sysroot prefix: proc_resolve_sysroot_path falls back to the host spelling when the sysroot has no entry at the path, which is the overlay contract for guest syscalls but would start the guest in a same-named host directory here, so the launch refuses it. --fakeroot and a non-root --user are refused together. Fakeroot starts the guest as uid/gid 0 and uid_is_permitted() grants every id switch on that basis; a non-root --user would keep that grant while the guest reported an unprivileged uid, letting the guest call setuid(0) at will. The refusal lives in elfuse_launch beside the Rosetta GDB check, so every launcher inherits the privilege rule, and it exits through the shared fail unwind so a FUSE-materialized temp ELF is unlinked even when bring-up never starts. tests/test-launch-flags.sh covers the refusal, the parse rules, and the --workdir sysroot containment; tests/test-identity-override-host.c pins the staging consume-once and clear semantics as regression guards (the cross-launch leak needs two bring-ups in one host process, which no launcher performs). docs/usage.md documents both flags, the containment rule, and the two-sided root requirement behind the fakeroot refusal.
4ed9d1b to
5c797e3
Compare
There was a problem hiding this comment.
1 issue found across 10 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/main.c">
<violation number="1" location="src/main.c:384">
P2: `--user` accepts non-digit forms such as `+1000`, leading whitespace, and `-0` because `strtoul` accepts them. Validate each UID/GID component as one or more ASCII digits before conversion.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| const char *spec = argv[arg_start + 1]; | ||
| char *end; | ||
| errno = 0; | ||
| unsigned long u = strtoul(spec, &end, 10); |
There was a problem hiding this comment.
P2: --user accepts non-digit forms such as +1000, leading whitespace, and -0 because strtoul accepts them. Validate each UID/GID component as one or more ASCII digits before conversion.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/main.c, line 384:
<comment>`--user` accepts non-digit forms such as `+1000`, leading whitespace, and `-0` because `strtoul` accepts them. Validate each UID/GID component as one or more ASCII digits before conversion.</comment>
<file context>
@@ -368,6 +377,52 @@ int main(int argc, char **argv)
+ const char *spec = argv[arg_start + 1];
+ char *end;
+ errno = 0;
+ unsigned long u = strtoul(spec, &end, 10);
+ if (errno || end == spec || u > UINT32_MAX) {
+ log_error("invalid --user UID: %s", spec);
</file context>
path_translate_at redirects /dev/shm/<leaf> into the per-UID host backing dir and returns before sysroot resolution, so elfuse_launch ran only sys_chdir's real-directory branch for such a workdir. The plain chdir followed a symlink leaf that shm_open_leaf's O_NOFOLLOW fd refuses, and proc_cwd_refresh published the backing location, so getcwd reported /private/tmp/elfuse-shm-<uid>/<leaf> where a guest chdir into the same directory reports /dev/shm/<leaf>. Entering the leaf correctly needs that O_NOFOLLOW fd and the virtual-cwd publish, which would make launch.c a second holder of the never-follow duty dev_shm_resolve_path enumerates, for a workdir no image asks for. The flag refuses the path instead, on the same grounds guest_bootstrap_prepare refuses a /dev/shm ELF interpreter. The test creates the leaf first, so the case measures the refusal rather than a missing directory, and chmods the backing root because create_private_dir rejects a group or other permission bit. The test-launch-flags.sh header claimed every refusal happens before any VM exists. guest_bootstrap_prepare creates and initializes the VM before the workdir block runs, so the header now names both timings: --user before the VM is created, --workdir during bring-up.
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tests/test-launch-flags.sh">
<violation number="1" location="tests/test-launch-flags.sh:75">
P3: The comment claims the leaf must be created so the check 'measures the refusal, not a missing directory', but the rejection is independent of the leaf's existence. path_translate_at() marks is_dev_shm for any '/dev/shm/<leaf>' prefix, and elfuse_launch rejects on that flag alone without touching the leaf, so the refusal fires whether or not /tmp/elfuse-shm-$(id -u)/launch-flags-wd exists. The shm_wd mkdir and its trap cleanup are dead setup that also plants a test artifact inside the live runtime shm backing dir; drop them (and the chmod is only needed on the root dir you keep).</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| shm_root="/tmp/elfuse-shm-$(id -u)" | ||
| shm_wd="$shm_root/launch-flags-wd" | ||
| trap 'rm -rf "$scratch" "$shm_wd"' EXIT | ||
| mkdir -p "$scratch/sysroot/inroot" "$scratch/hostonly" "$shm_wd" |
There was a problem hiding this comment.
P3: The comment claims the leaf must be created so the check 'measures the refusal, not a missing directory', but the rejection is independent of the leaf's existence. path_translate_at() marks is_dev_shm for any '/dev/shm/' prefix, and elfuse_launch rejects on that flag alone without touching the leaf, so the refusal fires whether or not /tmp/elfuse-shm-$(id -u)/launch-flags-wd exists. The shm_wd mkdir and its trap cleanup are dead setup that also plants a test artifact inside the live runtime shm backing dir; drop them (and the chmod is only needed on the root dir you keep).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test-launch-flags.sh, line 75:
<comment>The comment claims the leaf must be created so the check 'measures the refusal, not a missing directory', but the rejection is independent of the leaf's existence. path_translate_at() marks is_dev_shm for any '/dev/shm/<leaf>' prefix, and elfuse_launch rejects on that flag alone without touching the leaf, so the refusal fires whether or not /tmp/elfuse-shm-$(id -u)/launch-flags-wd exists. The shm_wd mkdir and its trap cleanup are dead setup that also plants a test artifact inside the live runtime shm backing dir; drop them (and the chmod is only needed on the root dir you keep).</comment>
<file context>
@@ -66,8 +66,14 @@ check accept "--fakeroot with an explicit root --user" '' --fakeroot --user 0:0
+shm_root="/tmp/elfuse-shm-$(id -u)"
+shm_wd="$shm_root/launch-flags-wd"
+trap 'rm -rf "$scratch" "$shm_wd"' EXIT
+mkdir -p "$scratch/sysroot/inroot" "$scratch/hostonly" "$shm_wd"
+chmod 700 "$shm_root"
</file context>
Summary by cubic
Adds
--user UID[:GID]and--workdir DIRto select the guest identity and initial working directory, enforced inelfuse_launchso every launcher (includingelfuse-oci run) inherits consistent behavior. Previously guests always started with the built-in uid/gid and the host cwd; now callers can set both, with refusals that prevent privilege confusion and host-escape.--user: numeric-only; a bare UID sets GID=UID. Staged before bring-up so auxv AT_UID/AT_GID matchgetuid/getgid, consumed once per launch, and cleared on early failures. Refuses--fakerootwith a non-root user;--fakeroot --user 0:0is accepted. Name resolution remains inelfuse-oci.--workdir: requires a guest-absolute path. Resolved under--sysrootafter the casefold probe and must resolve inside the sysroot (carve‑out for--sysroot /); paths that only exist on the host are rejected./dev/shmworkdirs are refused to avoid following a symlink leaf and to keep guest-visible spelling stable. Cwd is set before the first guest instruction and path state is refreshed.--helpand usage include both flags.launch_args_taddshas_creds,uid,gid, andcwd_guest. Newproc_set_initial_ids/proc_clear_initial_idsimplement consume‑once staging. All failure paths, including early refusals, unlink a temp FUSE ELF.tests/test-launch-flags.shcovers parse/rejection, sysroot containment, root pairing with--fakeroot, and/dev/shmrefusal.tests/test-identity-override-host.cpins consume‑once/clear semantics.docs/usage.mddocuments the flags and rules.Written for commit 2c96a2f. Summary will update on new commits.