diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-03-23 05:27:57 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-03-23 05:27:57 +0900 |
| commit | c6381932684a45aff6c187d53e5568bf52444457 (patch) | |
| tree | b837c39d86d8b65973a6b088987cbb507028b9fd /sandbox/mount.go | |
| parent | 8c3a81788188bbafbd824abbd1efe91e79a5e21e (diff) | |
sandbox: apply vfs options to bind mounts
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'sandbox/mount.go')
| -rw-r--r-- | sandbox/mount.go | 92 |
1 files changed, 86 insertions, 6 deletions
diff --git a/sandbox/mount.go b/sandbox/mount.go index bb5d6135..6683aaec 100644 --- a/sandbox/mount.go +++ b/sandbox/mount.go @@ -1,22 +1,102 @@ package sandbox import ( + "errors" "fmt" "os" + "path/filepath" "syscall" + + "git.gensokyo.uk/security/fortify/sandbox/vfs" ) func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) error { - var mf uintptr = syscall.MS_SILENT | syscall.MS_BIND - mf |= flags & syscall.MS_REC if eq { - msg.Verbosef("resolved %q flags %#x", target, mf) + msg.Verbosef("resolved %q flags %#x", target, flags) } else { - msg.Verbosef("resolved %q on %q flags %#x", source, target, mf) + msg.Verbosef("resolved %q on %q flags %#x", source, target, flags) + } + + if err := syscall.Mount(source, target, "", + syscall.MS_SILENT|syscall.MS_BIND|flags&syscall.MS_REC, ""); err != nil { + return wrapErrSuffix(err, + fmt.Sprintf("cannot mount %q on %q:", source, target)) + } + + var targetFinal string + if v, err := filepath.EvalSymlinks(target); err != nil { + return msg.WrapErr(err, err.Error()) + } else { + targetFinal = v + if targetFinal != target { + msg.Verbosef("target resolves to %q", targetFinal) + } + } + + // final target path according to the kernel through proc + var targetKFinal string + { + var destFd int + if err := IgnoringEINTR(func() (err error) { + destFd, err = syscall.Open(targetFinal, O_PATH|syscall.O_CLOEXEC, 0) + return + }); err != nil { + return wrapErrSuffix(err, + fmt.Sprintf("cannot open %q:", targetFinal)) + } + if v, err := os.Readlink(p.fd(destFd)); err != nil { + return msg.WrapErr(err, err.Error()) + } else if err = syscall.Close(destFd); err != nil { + return wrapErrSuffix(err, + fmt.Sprintf("cannot close %q:", targetFinal)) + } else { + targetKFinal = v + } } - return wrapErrSuffix(syscall.Mount(source, target, "", mf, ""), - fmt.Sprintf("cannot mount %q on %q:", source, target)) + mf := syscall.MS_NOSUID | flags&syscall.MS_NODEV | flags&syscall.MS_RDONLY + return hostProc.mountinfo(func(d *vfs.MountInfoDecoder) error { + n, err := d.Unfold(targetKFinal) + if err != nil { + if errors.Is(err, syscall.ESTALE) { + return msg.WrapErr(err, + fmt.Sprintf("mount point %q never appeared in mountinfo", targetKFinal)) + } + return wrapErrSuffix(err, + "cannot unfold mount hierarchy:") + } + + if err = remountWithFlags(n, mf); err != nil { + return err + } + if flags&syscall.MS_REC == 0 { + return nil + } + + for cur := range n.Collective() { + err = remountWithFlags(cur, mf) + if err != nil && !errors.Is(err, syscall.EACCES) { + return err + } + } + + return nil + }) +} + +func remountWithFlags(n *vfs.MountInfoNode, mf uintptr) error { + kf, unmatched := n.Flags() + if len(unmatched) != 0 { + msg.Verbosef("unmatched vfs options: %q", unmatched) + } + + if kf&mf != mf { + return wrapErrSuffix(syscall.Mount("none", n.Clean, "", + syscall.MS_SILENT|syscall.MS_BIND|syscall.MS_REMOUNT|kf|mf, + ""), + fmt.Sprintf("cannot remount %q:", n.Clean)) + } + return nil } func mountTmpfs(fsname, name string, size int, perm os.FileMode) error { |
