Skip to content

kernel: Implement setfsuid and setfsgid - #16309

Merged
copybara-service[bot] merged 2 commits into
masterfrom
test/cl1006036221
Oct 25, 2026
Merged

copybara-service[bot] merged 2 commits into
masterfrom
test/cl1006036221

Conversation

@copybara-service

Copy link
Copy Markdown

kernel: Implement setfsuid and setfsgid

Fixes #260

setfsuid(2) and setfsgid(2) return ENOSYS today. Setuid-root programs call them so that file access done while euid is still 0 is checked as the calling user. The one I hit is setuid bubblewrap, which gives up at startup:

bwrap: Unable to set fsuid: Function not implemented

Repro, with runsc started with --allow-suid. bwrap_suid.py:

import subprocess

subprocess.run(
    "export DEBIAN_FRONTEND=noninteractive && apt-get update -qq"
    " && apt-get install -qq -y bubblewrap >/dev/null && chmod u+s /usr/bin/bwrap",
    shell=True, check=True, stdin=subprocess.DEVNULL)

r = subprocess.run(
    ["bwrap", "--unshare-pid", "--die-with-parent", "--new-session",
     "--ro-bind", "/", "/", "--tmpfs", "/tmp", "--", "id", "-u"],
    user=1000, group=1000, extra_groups=[])
raise SystemExit(r.returncode)
docker run -i --rm --runtime=runsc \
    --cap-add SYS_ADMIN --cap-add NET_ADMIN --cap-add SYS_PTRACE \
    python:3.12-slim-bookworm python - < bwrap_suid.py

The script runs as root, installs bwrap with the setuid bit, and starts it as uid 1000. On runc (add --security-opt seccomp=unconfined --security-opt apparmor=unconfined) this prints 1000. On runsc master it fails with the error above, and with this change it prints 1000. The three caps are there because setuid bwrap raises them with capset() and fails if they are not in the bounding set.

The image is bookworm on purpose. bubblewrap 0.12.0 (Debian 13) dropped setuid support and uses unprivileged user namespaces instead, which already work on runsc without this change. This change is for images that still install an older bwrap setuid, like Debian 12 (0.8.0) or Ubuntu 22.04 (0.6.1).

Returning the real UID from a stub would get bwrap past this check too, since bwrap only uses fsuid to guard its own euid 0 phase. But the point of setfsuid is that file access afterwards runs with the caller's permissions, and with a stub every other user of it would quietly keep doing that access as root. So this follows Linux:

  • Credentials get FilesystemKUID/FilesystemKGID. They follow the effective IDs through set*id(2) and execve(2) and only setfsuid/setfsgid change them on their own, with the same return value, permission and CAP_FS_SET fixup rules as Linux (including SECBIT_NO_SETUID_FIXUP).
  • They are used where Linux uses fsuid/fsgid: DAC and ACL checks, group membership, owner checks, the owner of new inodes, mqueue and key permissions, FUSE request headers, xt_owner, and the fourth Uid/Gid column of /proc/[pid]/status. access(2) overrides only them, like access_override_creds().
  • SysV IPC, signals, SCM_CREDENTIALS, host UDS connect (so the host still sees the euid in SO_PEERCRED), the FUSE mount owner check, /proc/[pid] ownership and ptrace access checks keep using the same IDs as before, which matches Linux except for the FSCREDS case below.

About #13982: it adds PTRACE_MODE_FSCREDS. Linux compares the caller's fsuid/fsgid in that mode, but #13982 uses the effective IDs in canTraceCreds because gVisor had no filesystem IDs. Whichever of the two lands second should switch that to FilesystemKUID/FilesystemKGID. This change does not touch the ptrace checks.

Assisted-by: Cursor
Auto-fixed-by: [🤖] @EtiennePerot
FUTURE_COPYBARA_INTEGRATE_REVIEW=#15723 from cedriccchen:setfsuid cc78af9

setfsuid(2) and setfsgid(2) returned ENOSYS, so setuid-root programs
that switch the filesystem IDs before doing work for the real user,
such as setuid bubblewrap, failed with "Unable to set fsuid".

Add filesystem UID/GID fields to Credentials. They follow the effective
IDs through the set*id(2) family and execve(2); only setfsuid(2) and
setfsgid(2) set them independently. Like Linux, these calls always
return the previous ID, ignore unmapped IDs, and without
CAP_SETUID/CAP_SETGID accept only the real, effective, saved or current
filesystem ID. Moving the filesystem UID away from or back to the
namespace root drops or restores CAP_FS_SET in the effective set unless
SECBIT_NO_SETUID_FIXUP is set, and changing a filesystem ID resets
dumpability and the parent death signal. setresuid/setresgid calls that
change nothing return early without resetting the filesystem IDs, as in
Linux 6.3 and later.

Use the filesystem IDs where Linux does: DAC and POSIX ACL checks, group
membership, owner checks for chmod, chown, utimes and sticky deletion,
the owner of new inodes in tmpfs, gofer, overlay, FUSE, kernfs, pipefs,
devpts, cgroupfs and mqueue, mqueue and key permission checks, FUSE
request headers, netfilter owner matching, and the fourth Uid/Gid column
of /proc/[pid]/status. access(2) now overrides only the filesystem IDs,
like access_override_creds().

SysV IPC ownership, signals, SCM_CREDENTIALS and host socket connect
(which determines the host's SO_PEERCRED) keep the effective IDs, and
the FUSE mount owner check keeps the real, effective and saved IDs,
matching Linux. /proc/[pid] entries stay owned by the effective IDs, as
in task_dump_owner(), and /proc access checks still use the real IDs.

Fixes #260

Signed-off-by: Chen Hui <cedriccchen@tencent.com>
@copybara-service copybara-service Bot added the exported Issue was exported automatically label Oct 24, 2026
@copybara-service
copybara-service Bot force-pushed the test/cl1006036221 branch 5 times, most recently from fc3d6c3 to 7602e63 Compare October 25, 2026 05:59
PiperOrigin-RevId: 1006600450
@copybara-service
copybara-service Bot merged commit 46f1089 into master Oct 25, 2026
1 of 3 checks passed
@copybara-service
copybara-service Bot deleted the test/cl1006036221 branch October 25, 2026 06:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

exported Issue was exported automatically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support setfsuid, setfsgid

2 participants