sandbox/seccomp: import dot for syscall
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 1m55s
Test / Sandbox (race detector) (push) Successful in 3m7s
Test / Planterette (push) Successful in 3m31s
Test / Hakurei (race detector) (push) Successful in 4m19s
Test / Hakurei (push) Successful in 1m57s
Test / Flake checks (push) Successful in 1m11s

This significantly increases readability in some places.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-07-02 02:30:35 +09:00
parent 69bd581af7
commit a6887f7253
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
4 changed files with 71 additions and 104 deletions

View File

@ -11,7 +11,7 @@ import (
"os/exec"
"path"
"strconv"
"syscall"
. "syscall"
"time"
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
@ -119,11 +119,9 @@ func (p *Container) Start() error {
ctx, cancel := context.WithCancel(p.ctx)
p.cancel = cancel
var cloneFlags uintptr = syscall.CLONE_NEWIPC |
syscall.CLONE_NEWUTS |
syscall.CLONE_NEWCGROUP
var cloneFlags uintptr = CLONE_NEWIPC | CLONE_NEWUTS | CLONE_NEWCGROUP
if p.Flags&FAllowNet == 0 {
cloneFlags |= syscall.CLONE_NEWNET
cloneFlags |= CLONE_NEWNET
}
// map to overflow id to work around ownership checks
@ -146,17 +144,13 @@ func (p *Container) Start() error {
if p.Cancel != nil {
p.cmd.Cancel = func() error { return p.Cancel(p.cmd) }
} else {
p.cmd.Cancel = func() error { return p.cmd.Process.Signal(syscall.SIGTERM) }
p.cmd.Cancel = func() error { return p.cmd.Process.Signal(SIGTERM) }
}
p.cmd.Dir = "/"
p.cmd.SysProcAttr = &syscall.SysProcAttr{
Setsid: p.Flags&FAllowTTY == 0,
Pdeathsig: syscall.SIGKILL,
Cloneflags: cloneFlags |
syscall.CLONE_NEWUSER |
syscall.CLONE_NEWPID |
syscall.CLONE_NEWNS,
p.cmd.SysProcAttr = &SysProcAttr{
Setsid: p.Flags&FAllowTTY == 0,
Pdeathsig: SIGKILL,
Cloneflags: cloneFlags | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS,
// remain privileged for setup
AmbientCaps: []uintptr{CAP_SYS_ADMIN, CAP_SETPCAP},
@ -194,7 +188,7 @@ func (p *Container) Serve() error {
if p.Path != "" && !path.IsAbs(p.Path) {
p.cancel()
return msg.WrapErr(syscall.EINVAL,
return msg.WrapErr(EINVAL,
fmt.Sprintf("invalid executable path %q", p.Path))
}
@ -203,7 +197,7 @@ func (p *Container) Serve() error {
p.Path = os.Getenv("SHELL")
if !path.IsAbs(p.Path) {
p.cancel()
return msg.WrapErr(syscall.EBADE,
return msg.WrapErr(EBADE,
"no command specified and $SHELL is invalid")
}
p.name = path.Base(p.Path)
@ -220,8 +214,8 @@ func (p *Container) Serve() error {
err := setup.Encode(
&initParams{
p.Params,
syscall.Getuid(),
syscall.Getgid(),
Getuid(),
Getgid(),
len(p.ExtraFiles),
msg.IsVerbose(),
},

View File

@ -10,7 +10,7 @@ import (
"path"
"runtime"
"strconv"
"syscall"
. "syscall"
"time"
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
@ -97,9 +97,9 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
}
oldmask := syscall.Umask(0)
oldmask := Umask(0)
if params.Hostname != "" {
if err := syscall.Sethostname([]byte(params.Hostname)); err != nil {
if err := Sethostname([]byte(params.Hostname)); err != nil {
log.Fatalf("cannot set hostname: %v", err)
}
}
@ -107,9 +107,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
// cache sysctl before pivot_root
LastCap()
if err := syscall.Mount("", "/", "",
syscall.MS_SILENT|syscall.MS_SLAVE|syscall.MS_REC,
""); err != nil {
if err := Mount("", "/", "", MS_SILENT|MS_SLAVE|MS_REC, ""); err != nil {
log.Fatalf("cannot make / rslave: %v", err)
}
@ -126,9 +124,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
}
}
if err := syscall.Mount("rootfs", basePath, "tmpfs",
syscall.MS_NODEV|syscall.MS_NOSUID,
""); err != nil {
if err := Mount("rootfs", basePath, "tmpfs", MS_NODEV|MS_NOSUID, ""); err != nil {
log.Fatalf("cannot mount intermediate root: %v", err)
}
if err := os.Chdir(basePath); err != nil {
@ -138,9 +134,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
if err := os.Mkdir(sysrootDir, 0755); err != nil {
log.Fatalf("%v", err)
}
if err := syscall.Mount(sysrootDir, sysrootDir, "",
syscall.MS_SILENT|syscall.MS_MGC_VAL|syscall.MS_BIND|syscall.MS_REC,
""); err != nil {
if err := Mount(sysrootDir, sysrootDir, "", MS_SILENT|MS_MGC_VAL|MS_BIND|MS_REC, ""); err != nil {
log.Fatalf("cannot bind sysroot: %v", err)
}
@ -148,7 +142,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
log.Fatalf("%v", err)
}
// pivot_root uncovers basePath in hostDir
if err := syscall.PivotRoot(basePath, hostDir); err != nil {
if err := PivotRoot(basePath, hostDir); err != nil {
log.Fatalf("cannot pivot into intermediate root: %v", err)
}
if err := os.Chdir("/"); err != nil {
@ -167,19 +161,17 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
}
// setup requiring host root complete at this point
if err := syscall.Mount(hostDir, hostDir, "",
syscall.MS_SILENT|syscall.MS_REC|syscall.MS_PRIVATE,
""); err != nil {
if err := Mount(hostDir, hostDir, "", MS_SILENT|MS_REC|MS_PRIVATE, ""); err != nil {
log.Fatalf("cannot make host root rprivate: %v", err)
}
if err := syscall.Unmount(hostDir, syscall.MNT_DETACH); err != nil {
if err := Unmount(hostDir, MNT_DETACH); err != nil {
log.Fatalf("cannot unmount host root: %v", err)
}
{
var fd int
if err := IgnoringEINTR(func() (err error) {
fd, err = syscall.Open("/", syscall.O_DIRECTORY|syscall.O_RDONLY, 0)
fd, err = Open("/", O_DIRECTORY|O_RDONLY, 0)
return
}); err != nil {
log.Fatalf("cannot open intermediate root: %v", err)
@ -188,36 +180,36 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
log.Fatalf("%v", err)
}
if err := syscall.PivotRoot(".", "."); err != nil {
if err := PivotRoot(".", "."); err != nil {
log.Fatalf("cannot pivot into sysroot: %v", err)
}
if err := syscall.Fchdir(fd); err != nil {
if err := Fchdir(fd); err != nil {
log.Fatalf("cannot re-enter intermediate root: %v", err)
}
if err := syscall.Unmount(".", syscall.MNT_DETACH); err != nil {
if err := Unmount(".", MNT_DETACH); err != nil {
log.Fatalf("cannot unmount intemediate root: %v", err)
}
if err := os.Chdir("/"); err != nil {
log.Fatalf("%v", err)
}
if err := syscall.Close(fd); err != nil {
if err := Close(fd); err != nil {
log.Fatalf("cannot close intermediate root: %v", err)
}
}
if _, _, errno := syscall.Syscall(PR_SET_NO_NEW_PRIVS, 1, 0, 0); errno != 0 {
if _, _, errno := Syscall(PR_SET_NO_NEW_PRIVS, 1, 0, 0); errno != 0 {
log.Fatalf("prctl(PR_SET_NO_NEW_PRIVS): %v", errno)
}
if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0); errno != 0 {
if _, _, errno := Syscall(SYS_PRCTL, PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0); errno != 0 {
log.Fatalf("cannot clear the ambient capability set: %v", errno)
}
for i := uintptr(0); i <= LastCap(); i++ {
if params.Privileged && i == CAP_SYS_ADMIN {
continue
}
if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_CAPBSET_DROP, i, 0); errno != 0 {
if _, _, errno := Syscall(SYS_PRCTL, PR_CAPBSET_DROP, i, 0); errno != 0 {
log.Fatalf("cannot drop capability from bonding set: %v", errno)
}
}
@ -226,7 +218,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
if params.Privileged {
keep[capToIndex(CAP_SYS_ADMIN)] |= capToMask(CAP_SYS_ADMIN)
if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_SYS_ADMIN); errno != 0 {
if _, _, errno := Syscall(SYS_PRCTL, PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_SYS_ADMIN); errno != 0 {
log.Fatalf("cannot raise CAP_SYS_ADMIN: %v", errno)
}
}
@ -246,7 +238,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
// setup fd is placed before all extra files
extraFiles[i] = os.NewFile(uintptr(offsetSetup+i), "extra file "+strconv.Itoa(i))
}
syscall.Umask(oldmask)
Umask(oldmask)
cmd := exec.Command(params.Path)
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
@ -267,7 +259,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
type winfo struct {
wpid int
wstatus syscall.WaitStatus
wstatus WaitStatus
}
info := make(chan winfo, 1)
done := make(chan struct{})
@ -276,7 +268,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
var (
err error
wpid = -2
wstatus syscall.WaitStatus
wstatus WaitStatus
)
// keep going until no child process is left
@ -289,12 +281,12 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
info <- winfo{wpid, wstatus}
}
err = syscall.EINTR
for errors.Is(err, syscall.EINTR) {
wpid, err = syscall.Wait4(-1, &wstatus, 0, nil)
err = EINTR
for errors.Is(err, EINTR) {
wpid, err = Wait4(-1, &wstatus, 0, nil)
}
}
if !errors.Is(err, syscall.ECHILD) {
if !errors.Is(err, ECHILD) {
log.Println("unexpected wait4 response:", err)
}
@ -303,7 +295,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
// handle signals to dump withheld messages
sig := make(chan os.Signal, 2)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
signal.Notify(sig, SIGINT, SIGTERM)
// closed after residualProcessTimeout has elapsed after initial process death
timeout := make(chan struct{})

View File

@ -5,7 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
"syscall"
. "syscall"
"git.gensokyo.uk/security/hakurei/sandbox/vfs"
)
@ -17,8 +17,7 @@ func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) err
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 {
if err := Mount(source, target, "", MS_SILENT|MS_BIND|flags&MS_REC, ""); err != nil {
return wrapErrSuffix(err,
fmt.Sprintf("cannot mount %q on %q:", source, target))
}
@ -38,7 +37,7 @@ func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) err
{
var destFd int
if err := IgnoringEINTR(func() (err error) {
destFd, err = syscall.Open(targetFinal, O_PATH|syscall.O_CLOEXEC, 0)
destFd, err = Open(targetFinal, O_PATH|O_CLOEXEC, 0)
return
}); err != nil {
return wrapErrSuffix(err,
@ -46,7 +45,7 @@ func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) err
}
if v, err := os.Readlink(p.fd(destFd)); err != nil {
return wrapErrSelf(err)
} else if err = syscall.Close(destFd); err != nil {
} else if err = Close(destFd); err != nil {
return wrapErrSuffix(err,
fmt.Sprintf("cannot close %q:", targetFinal))
} else {
@ -54,11 +53,11 @@ func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) err
}
}
mf := syscall.MS_NOSUID | flags&syscall.MS_NODEV | flags&syscall.MS_RDONLY
mf := MS_NOSUID | flags&MS_NODEV | flags&MS_RDONLY
return hostProc.mountinfo(func(d *vfs.MountInfoDecoder) error {
n, err := d.Unfold(targetKFinal)
if err != nil {
if errors.Is(err, syscall.ESTALE) {
if errors.Is(err, ESTALE) {
return msg.WrapErr(err,
fmt.Sprintf("mount point %q never appeared in mountinfo", targetKFinal))
}
@ -69,13 +68,13 @@ func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) err
if err = remountWithFlags(n, mf); err != nil {
return err
}
if flags&syscall.MS_REC == 0 {
if flags&MS_REC == 0 {
return nil
}
for cur := range n.Collective() {
err = remountWithFlags(cur, mf)
if err != nil && !errors.Is(err, syscall.EACCES) {
if err != nil && !errors.Is(err, EACCES) {
return err
}
}
@ -91,9 +90,8 @@ func remountWithFlags(n *vfs.MountInfoNode, mf uintptr) error {
}
if kf&mf != mf {
return wrapErrSuffix(syscall.Mount("none", n.Clean, "",
syscall.MS_SILENT|syscall.MS_BIND|syscall.MS_REMOUNT|kf|mf,
""),
return wrapErrSuffix(
Mount("none", n.Clean, "", MS_SILENT|MS_BIND|MS_REMOUNT|kf|mf, ""),
fmt.Sprintf("cannot remount %q:", n.Clean))
}
return nil
@ -108,8 +106,8 @@ func mountTmpfs(fsname, name string, size int, perm os.FileMode) error {
if size > 0 {
opt += fmt.Sprintf(",size=%d", size)
}
return wrapErrSuffix(syscall.Mount(fsname, target, "tmpfs",
syscall.MS_NOSUID|syscall.MS_NODEV, opt),
return wrapErrSuffix(
Mount(fsname, target, "tmpfs", MS_NOSUID|MS_NODEV, opt),
fmt.Sprintf("cannot mount tmpfs on %q:", name))
}

View File

@ -9,7 +9,7 @@ import (
"path/filepath"
"slices"
"strings"
"syscall"
. "syscall"
"unsafe"
)
@ -46,8 +46,7 @@ const (
func (b *BindMountOp) early(*Params) error {
if !path.IsAbs(b.Source) {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("path %q is not absolute", b.Source))
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", b.Source))
}
if v, err := filepath.EvalSymlinks(b.Source); err != nil {
@ -66,14 +65,13 @@ func (b *BindMountOp) apply(*Params) error {
if b.SourceFinal == "\x00" {
if b.Flags&BindOptional == 0 {
// unreachable
return syscall.EBADE
return EBADE
}
return nil
}
if !path.IsAbs(b.SourceFinal) || !path.IsAbs(b.Target) {
return msg.WrapErr(syscall.EBADE,
"path is not absolute")
return msg.WrapErr(EBADE, "path is not absolute")
}
source := toHost(b.SourceFinal)
@ -91,12 +89,12 @@ func (b *BindMountOp) apply(*Params) error {
return err
}
var flags uintptr = syscall.MS_REC
var flags uintptr = MS_REC
if b.Flags&BindWritable == 0 {
flags |= syscall.MS_RDONLY
flags |= MS_RDONLY
}
if b.Flags&BindDevice == 0 {
flags |= syscall.MS_NODEV
flags |= MS_NODEV
}
return hostProc.bindMount(source, target, flags, b.SourceFinal == b.Target)
@ -125,16 +123,14 @@ func (p MountProcOp) apply(params *Params) error {
v := string(p)
if !path.IsAbs(v) {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("path %q is not absolute", v))
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", v))
}
target := toSysroot(v)
if err := os.MkdirAll(target, params.ParentPerm); err != nil {
return wrapErrSelf(err)
}
return wrapErrSuffix(syscall.Mount("proc", target, "proc",
syscall.MS_NOSUID|syscall.MS_NOEXEC|syscall.MS_NODEV, ""),
return wrapErrSuffix(Mount("proc", target, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, ""),
fmt.Sprintf("cannot mount proc on %q:", v))
}
@ -156,8 +152,7 @@ func (d MountDevOp) apply(params *Params) error {
v := string(d)
if !path.IsAbs(v) {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("path %q is not absolute", v))
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", v))
}
target := toSysroot(v)
@ -204,8 +199,7 @@ func (d MountDevOp) apply(params *Params) error {
}
}
if err := syscall.Mount("devpts", devPtsPath, "devpts",
syscall.MS_NOSUID|syscall.MS_NOEXEC,
if err := Mount("devpts", devPtsPath, "devpts", MS_NOSUID|MS_NOEXEC,
"newinstance,ptmxmode=0666,mode=620"); err != nil {
return wrapErrSuffix(err,
fmt.Sprintf("cannot mount devpts on %q:", devPtsPath))
@ -213,10 +207,7 @@ func (d MountDevOp) apply(params *Params) error {
if params.Flags&FAllowTTY != 0 {
var buf [8]byte
if _, _, errno := syscall.Syscall(
syscall.SYS_IOCTL, 1, syscall.TIOCGWINSZ,
uintptr(unsafe.Pointer(&buf[0])),
); errno == 0 {
if _, _, errno := Syscall(SYS_IOCTL, 1, TIOCGWINSZ, uintptr(unsafe.Pointer(&buf[0]))); errno == 0 {
consolePath := toSysroot(path.Join(v, "console"))
if err := ensureFile(consolePath, 0444, params.ParentPerm); err != nil {
return err
@ -255,16 +246,14 @@ func (m MountMqueueOp) apply(params *Params) error {
v := string(m)
if !path.IsAbs(v) {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("path %q is not absolute", v))
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", v))
}
target := toSysroot(v)
if err := os.MkdirAll(target, params.ParentPerm); err != nil {
return wrapErrSelf(err)
}
return wrapErrSuffix(syscall.Mount("mqueue", target, "mqueue",
syscall.MS_NOSUID|syscall.MS_NOEXEC|syscall.MS_NODEV, ""),
return wrapErrSuffix(Mount("mqueue", target, "mqueue", MS_NOSUID|MS_NOEXEC|MS_NODEV, ""),
fmt.Sprintf("cannot mount mqueue on %q:", v))
}
@ -288,12 +277,10 @@ type MountTmpfsOp struct {
func (t *MountTmpfsOp) early(*Params) error { return nil }
func (t *MountTmpfsOp) apply(*Params) error {
if !path.IsAbs(t.Path) {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("path %q is not absolute", t.Path))
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", t.Path))
}
if t.Size < 0 || t.Size > math.MaxUint>>1 {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("size %d out of bounds", t.Size))
return msg.WrapErr(EBADE, fmt.Sprintf("size %d out of bounds", t.Size))
}
return mountTmpfs("tmpfs", t.Path, t.Size, t.Perm)
}
@ -315,8 +302,7 @@ func (l *SymlinkOp) early(*Params) error {
if strings.HasPrefix(l[0], "*") {
l[0] = l[0][1:]
if !path.IsAbs(l[0]) {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("path %q is not absolute", l[0]))
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", l[0]))
}
if name, err := os.Readlink(l[0]); err != nil {
return wrapErrSelf(err)
@ -329,8 +315,7 @@ func (l *SymlinkOp) early(*Params) error {
func (l *SymlinkOp) apply(params *Params) error {
// symlink target is an arbitrary path value, so only validate link name here
if !path.IsAbs(l[1]) {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("path %q is not absolute", l[1]))
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", l[1]))
}
target := toSysroot(l[1])
@ -362,8 +347,7 @@ type MkdirOp struct {
func (m *MkdirOp) early(*Params) error { return nil }
func (m *MkdirOp) apply(*Params) error {
if !path.IsAbs(m.Path) {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("path %q is not absolute", m.Path))
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", m.Path))
}
if err := os.MkdirAll(toSysroot(m.Path), m.Perm); err != nil {
@ -391,8 +375,7 @@ type TmpfileOp struct {
func (t *TmpfileOp) early(*Params) error { return nil }
func (t *TmpfileOp) apply(params *Params) error {
if !path.IsAbs(t.Path) {
return msg.WrapErr(syscall.EBADE,
fmt.Sprintf("path %q is not absolute", t.Path))
return msg.WrapErr(EBADE, fmt.Sprintf("path %q is not absolute", t.Path))
}
var tmpPath string
@ -414,7 +397,7 @@ func (t *TmpfileOp) apply(params *Params) error {
} else if err = hostProc.bindMount(
tmpPath,
target,
syscall.MS_RDONLY|syscall.MS_NODEV,
MS_RDONLY|MS_NODEV,
false,
); err != nil {
return err