Skip to content

Add user and workdir elfuse launch flags - #286

Open
henrybear327 wants to merge 2 commits into
sysprog21:mainfrom
henrybear327:oci/add_user_workdir_flag
Open

Add user and workdir elfuse launch flags#286
henrybear327 wants to merge 2 commits into
sysprog21:mainfrom
henrybear327:oci/add_user_workdir_flag

Conversation

@henrybear327

@henrybear327 henrybear327 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary by cubic

Adds --user UID[:GID] and --workdir DIR to select the guest identity and initial working directory, enforced in elfuse_launch so every launcher (including elfuse-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 match getuid/getgid, consumed once per launch, and cleared on early failures. Refuses --fakeroot with a non-root user; --fakeroot --user 0:0 is accepted. Name resolution remains in elfuse-oci.
  • --workdir: requires a guest-absolute path. Resolved under --sysroot after the casefold probe and must resolve inside the sysroot (carve‑out for --sysroot /); paths that only exist on the host are rejected. /dev/shm workdirs 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.
  • CLI/API: --help and usage include both flags. launch_args_t adds has_creds, uid, gid, and cwd_guest. New proc_set_initial_ids/proc_clear_initial_ids implement consume‑once staging. All failure paths, including early refusals, unlink a temp FUSE ELF.
  • Tests/docs: tests/test-launch-flags.sh covers parse/rejection, sysroot containment, root pairing with --fakeroot, and /dev/shm refusal. tests/test-identity-override-host.c pins consume‑once/clear semantics. docs/usage.md documents the flags and rules.

Written for commit 2c96a2f. Summary will update on new commits.

Review in cubic

cubic-dev-ai[bot]

This comment was marked as resolved.

jserv

This comment was marked as resolved.

@henrybear327
henrybear327 force-pushed the oci/add_user_workdir_flag branch from 9911813 to cd98b4e Compare August 12, 2026 14:35
@henrybear327
henrybear327 requested a review from jserv August 12, 2026 18:08
@henrybear327

This comment was marked as resolved.

@henrybear327
henrybear327 marked this pull request as draft August 12, 2026 18:09
@henrybear327
henrybear327 force-pushed the oci/add_user_workdir_flag branch 2 times, most recently from 20a5e74 to 238f4de Compare August 13, 2026 12:14
@henrybear327
henrybear327 marked this pull request as ready for review August 13, 2026 12:14
@henrybear327
henrybear327 marked this pull request as draft August 13, 2026 12:27
cubic-dev-ai[bot]

This comment was marked as resolved.

@henrybear327
henrybear327 marked this pull request as ready for review August 13, 2026 20:12
cubic-dev-ai[bot]

This comment was marked as resolved.

@henrybear327
henrybear327 marked this pull request as draft August 13, 2026 20:20
jserv

This comment was marked as resolved.

@henrybear327
henrybear327 force-pushed the oci/add_user_workdir_flag branch 2 times, most recently from 69f6ff1 to 4ed9d1b Compare August 14, 2026 11:34
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.
@henrybear327
henrybear327 force-pushed the oci/add_user_workdir_flag branch from 4ed9d1b to 5c797e3 Compare August 14, 2026 13:10
@henrybear327
henrybear327 marked this pull request as ready for review August 14, 2026 13:11
@henrybear327
henrybear327 requested a review from jserv August 14, 2026 13:11

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread src/core/launch.c
Comment thread src/main.c
const char *spec = argv[arg_start + 1];
char *end;
errno = 0;
unsigned long u = strtoul(spec, &end, 10);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants