Skip to content

Use resolved runsc executable path for nixos compatibility#13100

Open
0kenx wants to merge 2 commits intogoogle:masterfrom
0kenx:sigid/runsc-real-exe-path
Open

Use resolved runsc executable path for nixos compatibility#13100
0kenx wants to merge 2 commits intogoogle:masterfrom
0kenx:sigid/runsc-real-exe-path

Conversation

@0kenx
Copy link
Copy Markdown

@0kenx 0kenx commented May 6, 2026

Summary

Use specutils.ExePath instead of /proc/self/exe when runsc re-execs itself inside a new user namespace, and resolve ExePath to the real executable path at startup with a conservative fallback to /proc/self/exe.

Why

Some host-side execute allowlist policies cannot safely authorize procfs magic-link execution through /proc/self/exe. Resolving the real executable path keeps helper and user-namespace re-execs on the same concrete binary path that the supervisor already authorized.

Changes

  • Replace the direct /proc/self/exe re-exec in MaybeRunAsRoot with ExePath.
  • Initialize ExePath from os.Executable(), resolving symlinks when possible.
  • Fall back to /proc/self/exe if resolution fails, returns a non-absolute path, points at a directory, or points at a non-executable file.

Validation

  • Ran gofmt on changed files.
  • Ran git diff --check.

0kenx added 2 commits May 6, 2026 18:32
Signed-off-by: 0kenx <km@nxfi.app>
Signed-off-by: 0kenx <km@nxfi.app>
@google-cla
Copy link
Copy Markdown

google-cla Bot commented May 6, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@EtiennePerot
Copy link
Copy Markdown
Collaborator

Some host-side execute allowlist policies cannot safely authorize procfs magic-link execution through /proc/self/exe.

Can you elaborate here?

There is a tradeoff here, which is that the path at specutils.ExePath may have changed between the time runsc started and the time it re-execs. There have been attacks on this path during the container startup process, by making container startup hooks act in unexpected ways. IMO /proc/self/exe is the safer thing to do. I am surprised that an allowlist policy wouldn't allow it, since that seems like the safest way for a binary to re-exec itself.

@0kenx
Copy link
Copy Markdown
Author

0kenx commented May 7, 2026

There is a tradeoff here, which is that the path at specutils.ExePath may have changed between the time runsc started and the time it re-execs.

That's an interesting tradeoff. The issue here is specific to the supervisor execute policy we install before handing control to rootless runsc.

That policy is a Landlock LANDLOCK_ACCESS_FS_EXECUTE allowlist over a private staged runsc directory. The reason for avoiding the original package path is on Nixos, the real installed binary may live under /nix/store, and allowing that root would be broader than the private staged directory.

@EtiennePerot
Copy link
Copy Markdown
Collaborator

In that case, can you make this an explicit configuration flag (like --self_path=...) instead of this?
That way the default behavior remains /proc/self/exe, and the responsibility of ensuring that this path is safe and doesn't get clobbered inherently becomes the runsc caller's responsibility.

In addition, I worry that PR as-is would cause runsc boot to automatically execute the stat syscall, which may or may not be allowed per the seccomp policy (I'm not 100% sure but I think setting --directfs=false would forbid it), and cause it to get killed for seccomp violation as a result. By making it a string flag, there is no syscall on process init.

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