blob: f4b30b1952d1cb8824b16eef1bc97e92b0cb1dfa [file] [log] [blame]
// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
// +build !appengine
package osutil
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
"unsafe"
)
// RemoveAll is similar to os.RemoveAll, but can handle more cases.
func RemoveAll(dir string) error {
files, _ := ioutil.ReadDir(dir)
for _, f := range files {
name := filepath.Join(dir, f.Name())
if f.IsDir() {
RemoveAll(name)
}
fn := []byte(name + "\x00")
syscall.Syscall(syscall.SYS_UMOUNT2, uintptr(unsafe.Pointer(&fn[0])), syscall.MNT_FORCE, 0)
}
return os.RemoveAll(dir)
}
func Sandbox(cmd *exec.Cmd, user, net bool) error {
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = new(syscall.SysProcAttr)
}
if net {
cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC |
syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID
}
if user {
enabled, uid, gid, err := initSandbox()
if err != nil {
return err
}
if enabled {
cmd.SysProcAttr.Credential = &syscall.Credential{
Uid: uid,
Gid: gid,
}
}
}
return nil
}
func SandboxChown(file string) error {
enabled, uid, gid, err := initSandbox()
if err != nil || !enabled {
return err
}
return os.Chown(file, int(uid), int(gid))
}
var (
sandboxOnce sync.Once
sandboxEnabled = true
sandboxUsername = "syzkaller"
sandboxUID = ^uint32(0)
sandboxGID = ^uint32(0)
)
func initSandbox() (bool, uint32, uint32, error) {
sandboxOnce.Do(func() {
if syscall.Getuid() != 0 || os.Getenv("SYZ_DISABLE_SANDBOXING") == "yes" {
sandboxEnabled = false
return
}
uid, err := usernameToID("-u")
if err != nil {
return
}
gid, err := usernameToID("-g")
if err != nil {
return
}
sandboxUID = uid
sandboxGID = gid
})
if sandboxEnabled && sandboxUID == ^uint32(0) {
return false, 0, 0, fmt.Errorf("user %q is not found, can't sandbox command", sandboxUsername)
}
return sandboxEnabled, sandboxUID, sandboxGID, nil
}
func usernameToID(what string) (uint32, error) {
out, err := RunCmd(time.Minute, "", "id", what, sandboxUsername)
if err != nil {
return 0, err
}
str := strings.Trim(string(out), " \t\n")
id, err := strconv.ParseUint(str, 10, 32)
if err != nil {
return 0, err
}
return uint32(id), nil
}
func setPdeathsig(cmd *exec.Cmd) {
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = new(syscall.SysProcAttr)
}
cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
}
func prolongPipe(r, w *os.File) {
for sz := 128 << 10; sz <= 2<<20; sz *= 2 {
syscall.Syscall(syscall.SYS_FCNTL, w.Fd(), syscall.F_SETPIPE_SZ, uintptr(sz))
}
}