Skip to content

gofer: Ensure mount flags are applied for bind mounts #11866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions runsc/boot/vfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,14 @@ func setupContainerVFS(ctx context.Context, info *containerInfo, mntr *container
return fmt.Errorf("failed to create device files: %w", err)
}

if err := mntr.k.VFS().MkdirAllAt(
ctx, procArgs.WorkingDirectory, mnsRoot, rootCreds,
&vfs.MkdirOptions{Mode: 0755}, true, /* mustBeDir */
); err != nil {
return fmt.Errorf("failed to create process working directory %q: %w",
procArgs.WorkingDirectory, err)
}

// We are executing a file directly. Do not resolve the executable path.
if procArgs.File != nil {
return nil
Expand Down
60 changes: 47 additions & 13 deletions runsc/cmd/gofer.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,18 +485,6 @@ func (g *Gofer) setupRootFS(spec *specs.Spec, conf *config.Config, goferToHostRP
g.setupDev(spec, conf, root, procPath)
}

// Create working directory if needed.
if spec.Process.Cwd != "" {
dst, err := resolveSymlinks(root, spec.Process.Cwd)
if err != nil {
return fmt.Errorf("resolving symlinks to %q: %v", spec.Process.Cwd, err)
}
log.Infof("Create working directory %q if needed", spec.Process.Cwd)
if err := os.MkdirAll(dst, 0755); err != nil {
return fmt.Errorf("creating working directory %q: %v", spec.Process.Cwd, err)
}
}

// Check if root needs to be remounted as readonly.
if rootfsConf.ShouldUseLisafs() && (spec.Root.Readonly || rootfsConf.ShouldUseOverlayfs()) {
// If root is a mount point but not read-only, we can change mount options
Expand Down Expand Up @@ -526,7 +514,7 @@ func (g *Gofer) setupRootFS(spec *specs.Spec, conf *config.Config, goferToHostRP
// setupMounts bind mounts all mounts specified in the spec in their correct
// location inside root. It will resolve relative paths and symlinks. It also
// creates directories as needed.
func (g *Gofer) setupMounts(conf *config.Config, mounts []specs.Mount, root, procPath string, goferToHostRPC *urpc.Client) error {
func (g *Gofer) setupMounts(conf *config.Config, mounts []specs.Mount, root, procPath string, goferToHostRPC *urpc.Client) (retErr error) {
mountIdx := 1 // First index is for rootfs.
for _, m := range mounts {
if !specutils.IsGoferMount(m) {
Expand Down Expand Up @@ -569,6 +557,52 @@ func (g *Gofer) setupMounts(conf *config.Config, mounts []specs.Mount, root, pro
return fmt.Errorf("mounting %+v: %v", m, err)
}

dstFD, err := unix.Open(dst, unix.O_PATH|unix.O_CLOEXEC, 0)
if err != nil {
return fmt.Errorf("Open(%s, _, _): %w", dst, err)
}
defer unix.Close(dstFD)
// Apply mount options after creating all mount points.
// Otherwise they can be remounted into read-only.
defer func(dstFD int, flags uint32, dst string) {
path := fmt.Sprintf("/proc/self/fd/%d", dstFD)
// The gofer process doesn't execute anything nativly.
flags |= unix.MS_NOSUID

statfs := unix.Statfs_t{}
if err := unix.Statfs(path, &statfs); err != nil {
retErr = fmt.Errorf("stat dst: %q", dst)
return
}
lockedFlags := uint32(0)
for _, f := range []struct {
st, ms int
}{
// MS_NOSUID are always set.
{unix.ST_RDONLY, unix.MS_RDONLY},
{unix.ST_NOEXEC, unix.MS_NOEXEC},
{unix.ST_NODEV, unix.MS_NODEV},
{unix.ST_NOATIME, unix.MS_NOATIME},
{unix.ST_NODIRATIME, unix.MS_NODIRATIME},
{unix.ST_RELATIME, unix.MS_RELATIME},
} {
if int(statfs.Flags)&f.st == f.st {
lockedFlags |= uint32(f.ms)
}
}
if lockedFlags&unix.MS_NOATIME|unix.MS_RELATIME == 0 {
lockedFlags |= unix.MS_STRICTATIME
}

// The previous SafeSetupAndMount creates a new bind-mount, but
// it doesn't change mount flags. A separate MS_BIND|MS_REMOUNT
// has to be done to apply the mount options.
if err := unix.Mount("", path, "", uintptr(flags|lockedFlags|unix.MS_REMOUNT), ""); err != nil {
retErr = fmt.Errorf("mount dst: %q, flags: %#x, err: %v", dst, flags, err)
return
}
}(dstFD, flags, dst)

// Set propagation options that cannot be set together with other options.
flags = specutils.PropOptionsToFlags(m.Options)
if flags != 0 {
Expand Down
2 changes: 1 addition & 1 deletion runsc/specutils/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func optionsToFlags(opts []string, source map[string]mapping) uint32 {
if m.set {
rv |= m.val
} else {
rv ^= m.val
rv &= ^m.val
}
}
}
Expand Down
Loading