github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/osutil/osutil_linux.go (about) 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 package osutil 5 6 import ( 7 "fmt" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "sync" 14 "syscall" 15 "time" 16 17 "golang.org/x/sys/unix" 18 ) 19 20 func creationTime(fi os.FileInfo) time.Time { 21 st := fi.Sys().(*syscall.Stat_t) 22 return time.Unix(int64(st.Ctim.Sec), int64(st.Ctim.Nsec)) // nolint: unconvert 23 } 24 25 // RemoveAll is similar to os.RemoveAll, but can handle more cases. 26 func RemoveAll(dir string) error { 27 files, _ := os.ReadDir(dir) 28 for _, f := range files { 29 name := filepath.Join(dir, f.Name()) 30 if f.IsDir() { 31 RemoveAll(name) 32 } 33 unix.Unmount(name, unix.MNT_FORCE) 34 } 35 if err := os.RemoveAll(dir); err != nil { 36 removeImmutable(dir) 37 return os.RemoveAll(dir) 38 } 39 return nil 40 } 41 42 func SystemMemorySize() uint64 { 43 var info syscall.Sysinfo_t 44 syscall.Sysinfo(&info) 45 return uint64(info.Totalram) // nolint:unconvert 46 } 47 48 func removeImmutable(fname string) error { 49 // Reset FS_XFLAG_IMMUTABLE/FS_XFLAG_APPEND. 50 fd, err := syscall.Open(fname, syscall.O_RDONLY, 0) 51 if err != nil { 52 return err 53 } 54 defer syscall.Close(fd) 55 return unix.IoctlSetPointerInt(fd, unix.FS_IOC_SETFLAGS, 0) 56 } 57 58 func Sandbox(cmd *exec.Cmd, user, net bool) error { 59 enabled, uid, gid, err := initSandbox() 60 if err != nil || !enabled { 61 return err 62 } 63 if cmd.SysProcAttr == nil { 64 cmd.SysProcAttr = new(syscall.SysProcAttr) 65 } 66 if net { 67 cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC | 68 syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID 69 } 70 if user { 71 cmd.SysProcAttr.Credential = &syscall.Credential{ 72 Uid: uid, 73 Gid: gid, 74 } 75 } 76 return nil 77 } 78 79 func SandboxChown(file string) error { 80 enabled, uid, gid, err := initSandbox() 81 if err != nil || !enabled { 82 return err 83 } 84 return os.Chown(file, int(uid), int(gid)) 85 } 86 87 var ( 88 sandboxOnce sync.Once 89 sandboxEnabled = true 90 sandboxUsername = "syzkaller" 91 sandboxUID = ^uint32(0) 92 sandboxGID = ^uint32(0) 93 ) 94 95 func initSandbox() (bool, uint32, uint32, error) { 96 sandboxOnce.Do(func() { 97 if syscall.Getuid() != 0 || os.Getenv("CI") != "" || os.Getenv("SYZ_DISABLE_SANDBOXING") == "yes" { 98 sandboxEnabled = false 99 return 100 } 101 uid, err := usernameToID("-u") 102 if err != nil { 103 return 104 } 105 gid, err := usernameToID("-g") 106 if err != nil { 107 return 108 } 109 sandboxUID = uid 110 sandboxGID = gid 111 }) 112 if sandboxEnabled && sandboxUID == ^uint32(0) { 113 return false, 0, 0, fmt.Errorf("user %q is not found, can't sandbox command", sandboxUsername) 114 } 115 return sandboxEnabled, sandboxUID, sandboxGID, nil 116 } 117 118 func usernameToID(what string) (uint32, error) { 119 out, err := RunCmd(time.Minute, "", "id", what, sandboxUsername) 120 if err != nil { 121 return 0, err 122 } 123 str := strings.Trim(string(out), " \t\n") 124 id, err := strconv.ParseUint(str, 10, 32) 125 if err != nil { 126 return 0, err 127 } 128 return uint32(id), nil 129 } 130 131 func setPdeathsig(cmd *exec.Cmd, hardKill bool) { 132 if cmd.SysProcAttr == nil { 133 cmd.SysProcAttr = new(syscall.SysProcAttr) 134 } 135 if hardKill { 136 cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL 137 } else { 138 cmd.SysProcAttr.Pdeathsig = syscall.SIGTERM 139 } 140 // We will kill the whole process group. 141 cmd.SysProcAttr.Setpgid = true 142 } 143 144 func killPgroup(cmd *exec.Cmd) { 145 syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) 146 } 147 148 func prolongPipe(r, w *os.File) { 149 for sz := 128 << 10; sz <= 2<<20; sz *= 2 { 150 syscall.Syscall(syscall.SYS_FCNTL, w.Fd(), syscall.F_SETPIPE_SZ, uintptr(sz)) 151 } 152 }