Home | History | Annotate | Download | only in osutil
      1 // Copyright 2017 syzkaller project authors. All rights reserved.
      2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
      3 
      4 // +build !appengine
      5 
      6 package osutil
      7 
      8 import (
      9 	"fmt"
     10 	"io/ioutil"
     11 	"os"
     12 	"os/exec"
     13 	"path/filepath"
     14 	"strconv"
     15 	"strings"
     16 	"sync"
     17 	"syscall"
     18 	"time"
     19 	"unsafe"
     20 )
     21 
     22 // RemoveAll is similar to os.RemoveAll, but can handle more cases.
     23 func RemoveAll(dir string) error {
     24 	files, _ := ioutil.ReadDir(dir)
     25 	for _, f := range files {
     26 		name := filepath.Join(dir, f.Name())
     27 		if f.IsDir() {
     28 			RemoveAll(name)
     29 		}
     30 		fn := []byte(name + "\x00")
     31 		syscall.Syscall(syscall.SYS_UMOUNT2, uintptr(unsafe.Pointer(&fn[0])), syscall.MNT_FORCE, 0)
     32 	}
     33 	return os.RemoveAll(dir)
     34 }
     35 
     36 func Sandbox(cmd *exec.Cmd, user, net bool) error {
     37 	if cmd.SysProcAttr == nil {
     38 		cmd.SysProcAttr = new(syscall.SysProcAttr)
     39 	}
     40 	if net {
     41 		cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC |
     42 			syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID
     43 	}
     44 	if user {
     45 		enabled, uid, gid, err := initSandbox()
     46 		if err != nil {
     47 			return err
     48 		}
     49 		if enabled {
     50 			cmd.SysProcAttr.Credential = &syscall.Credential{
     51 				Uid: uid,
     52 				Gid: gid,
     53 			}
     54 		}
     55 	}
     56 	return nil
     57 }
     58 
     59 func SandboxChown(file string) error {
     60 	enabled, uid, gid, err := initSandbox()
     61 	if err != nil || !enabled {
     62 		return err
     63 	}
     64 	return os.Chown(file, int(uid), int(gid))
     65 }
     66 
     67 var (
     68 	sandboxOnce     sync.Once
     69 	sandboxEnabled  = true
     70 	sandboxUsername = "syzkaller"
     71 	sandboxUID      = ^uint32(0)
     72 	sandboxGID      = ^uint32(0)
     73 )
     74 
     75 func initSandbox() (bool, uint32, uint32, error) {
     76 	sandboxOnce.Do(func() {
     77 		if syscall.Getuid() != 0 || os.Getenv("SYZ_DISABLE_SANDBOXING") == "yes" {
     78 			sandboxEnabled = false
     79 			return
     80 		}
     81 		uid, err := usernameToID("-u")
     82 		if err != nil {
     83 			return
     84 		}
     85 		gid, err := usernameToID("-g")
     86 		if err != nil {
     87 			return
     88 		}
     89 		sandboxUID = uid
     90 		sandboxGID = gid
     91 	})
     92 	if sandboxEnabled && sandboxUID == ^uint32(0) {
     93 		return false, 0, 0, fmt.Errorf("user %q is not found, can't sandbox command", sandboxUsername)
     94 	}
     95 	return sandboxEnabled, sandboxUID, sandboxGID, nil
     96 }
     97 
     98 func usernameToID(what string) (uint32, error) {
     99 	out, err := RunCmd(time.Minute, "", "id", what, sandboxUsername)
    100 	if err != nil {
    101 		return 0, err
    102 	}
    103 	str := strings.Trim(string(out), " \t\n")
    104 	id, err := strconv.ParseUint(str, 10, 32)
    105 	if err != nil {
    106 		return 0, err
    107 	}
    108 	return uint32(id), nil
    109 }
    110 
    111 func setPdeathsig(cmd *exec.Cmd) {
    112 	if cmd.SysProcAttr == nil {
    113 		cmd.SysProcAttr = new(syscall.SysProcAttr)
    114 	}
    115 	cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
    116 }
    117 
    118 func prolongPipe(r, w *os.File) {
    119 	for sz := 128 << 10; sz <= 2<<20; sz *= 2 {
    120 		syscall.Syscall(syscall.SYS_FCNTL, w.Fd(), syscall.F_SETPIPE_SZ, uintptr(sz))
    121 	}
    122 }
    123