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
6package osutil
7
8import (
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.
23func 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
36func 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
59func 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
67var (
68	sandboxOnce     sync.Once
69	sandboxEnabled  = true
70	sandboxUsername = "syzkaller"
71	sandboxUID      = ^uint32(0)
72	sandboxGID      = ^uint32(0)
73)
74
75func 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
98func 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
111func setPdeathsig(cmd *exec.Cmd) {
112	if cmd.SysProcAttr == nil {
113		cmd.SysProcAttr = new(syscall.SysProcAttr)
114	}
115	cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
116}
117
118func 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