| // 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)) |
| } |
| } |