github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/sandbox/forkexec/exec_darwin.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build darwin 6 7 package forkexec 8 9 import ( 10 "syscall" 11 "unsafe" 12 ) 13 14 // Find the entry point for f. See comments in runtime/proc.go for the 15 // function of the same name. 16 //go:nosplit 17 func funcPC(f func()) uintptr { 18 return **(**uintptr)(unsafe.Pointer(&f)) 19 } 20 21 type SysProcAttr struct { 22 Chroot string // Chroot. 23 Credential *syscall.Credential // Credential. 24 Ptrace bool // Enable tracing. 25 Setsid bool // Create session. 26 // Setpgid sets the process group ID of the child to Pgid, 27 // or, if Pgid == 0, to the new child's process ID. 28 Setpgid bool 29 // Setctty sets the controlling terminal of the child to 30 // file descriptor Ctty. Ctty must be a descriptor number 31 // in the child process: an index into ProcAttr.Files. 32 // This is only meaningful if Setsid is true. 33 Setctty bool 34 Noctty bool // Detach fd 0 from controlling terminal 35 Ctty int // Controlling TTY fd 36 // Foreground places the child process group in the foreground. 37 // This implies Setpgid. The Ctty field must be set to 38 // the descriptor of the controlling TTY. 39 // Unlike Setctty, in this case Ctty must be a descriptor 40 // number in the parent process. 41 Foreground bool 42 Pgid int // Child's process group ID if Setpgid. 43 Rlimit ExecRLimit // Set child's rlimit. 44 } 45 46 // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child. 47 // If a dup or exec fails, write the errno error to pipe. 48 // (Pipe is close-on-exec so if exec succeeds, it will be closed.) 49 // In the child, this function must not acquire any locks, because 50 // they might have been locked at the time of the fork. This means 51 // no rescheduling, no malloc calls, and no new stack segments. 52 // For the same reason compiler does not race instrument it. 53 // The calls to rawSyscall are okay because they are assembly 54 // functions that do not grow the stack. 55 //go:norace 56 func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err syscall.Errno) { 57 // Declare all variables at top in case any 58 // declarations require heap allocation (e.g., err1). 59 var ( 60 r1 uintptr 61 err1 syscall.Errno 62 nextfd int 63 i int 64 ) 65 66 // Load rlimit options 67 rlimitOptions := GetRlimitOptions(&sys.Rlimit) 68 69 // guard against side effects of shuffling fds below. 70 // Make sure that nextfd is beyond any currently open files so 71 // that we can't run the risk of overwriting any of them. 72 fd := make([]int, len(attr.Files)) 73 nextfd = len(attr.Files) 74 for i, ufd := range attr.Files { 75 if nextfd < int(ufd) { 76 nextfd = int(ufd) 77 } 78 fd[i] = int(ufd) 79 } 80 nextfd++ 81 82 // About to call fork. 83 // No more allocation or calls of non-assembly functions. 84 runtime_BeforeFork() 85 r1, _, err1 = rawSyscall(funcPC(libc_fork_trampoline), 0, 0, 0) 86 if err1 != 0 { 87 runtime_AfterFork() 88 return 0, err1 89 } 90 91 if r1 != 0 { 92 // parent; return PID 93 runtime_AfterFork() 94 return int(r1), 0 95 } 96 97 // Fork succeeded, now in child. 98 99 runtime_AfterForkInChild() 100 101 // Enable tracing if requested. 102 if sys.Ptrace { 103 if err := ptrace(syscall.PTRACE_TRACEME, 0, 0, 0); err != nil { 104 err1 = err.(syscall.Errno) 105 goto childerror 106 } 107 } 108 109 // Session ID 110 if sys.Setsid { 111 _, _, err1 = rawSyscall(funcPC(libc_setsid_trampoline), 0, 0, 0) 112 if err1 != 0 { 113 goto childerror 114 } 115 } 116 117 // Set process group 118 if sys.Setpgid || sys.Foreground { 119 // Place child in process group. 120 _, _, err1 = rawSyscall(funcPC(libc_setpgid_trampoline), 0, uintptr(sys.Pgid), 0) 121 if err1 != 0 { 122 goto childerror 123 } 124 } 125 126 if sys.Foreground { 127 pgrp := sys.Pgid 128 if pgrp == 0 { 129 r1, _, err1 = rawSyscall(funcPC(libc_getpid_trampoline), 0, 0, 0) 130 if err1 != 0 { 131 goto childerror 132 } 133 134 pgrp = int(r1) 135 } 136 137 // Place process group in foreground. 138 _, _, err1 = rawSyscall(funcPC(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(syscall.TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp))) 139 if err1 != 0 { 140 goto childerror 141 } 142 } 143 144 // Chroot 145 if chroot != nil { 146 _, _, err1 = rawSyscall(funcPC(libc_chroot_trampoline), uintptr(unsafe.Pointer(chroot)), 0, 0) 147 if err1 != 0 { 148 goto childerror 149 } 150 } 151 152 // User and groups 153 if cred := sys.Credential; cred != nil { 154 ngroups := uintptr(len(cred.Groups)) 155 groups := uintptr(0) 156 if ngroups > 0 { 157 groups = uintptr(unsafe.Pointer(&cred.Groups[0])) 158 } 159 if !cred.NoSetGroups { 160 _, _, err1 = rawSyscall(funcPC(libc_setgroups_trampoline), ngroups, groups, 0) 161 if err1 != 0 { 162 goto childerror 163 } 164 } 165 _, _, err1 = rawSyscall(funcPC(libc_setgid_trampoline), uintptr(cred.Gid), 0, 0) 166 if err1 != 0 { 167 goto childerror 168 } 169 _, _, err1 = rawSyscall(funcPC(libc_setuid_trampoline), uintptr(cred.Uid), 0, 0) 170 if err1 != 0 { 171 goto childerror 172 } 173 } 174 175 // Chdir 176 if dir != nil { 177 _, _, err1 = rawSyscall(funcPC(libc_chdir_trampoline), uintptr(unsafe.Pointer(dir)), 0, 0) 178 if err1 != 0 { 179 goto childerror 180 } 181 } 182 183 // Pass 1: look for fd[i] < i and move those up above len(fd) 184 // so that pass 2 won't stomp on an fd it needs later. 185 if pipe < nextfd { 186 _, _, err1 = rawSyscall(funcPC(libc_dup2_trampoline), uintptr(pipe), uintptr(nextfd), 0) 187 if err1 != 0 { 188 goto childerror 189 } 190 rawSyscall(funcPC(libc_fcntl_trampoline), uintptr(nextfd), syscall.F_SETFD, syscall.FD_CLOEXEC) 191 pipe = nextfd 192 nextfd++ 193 } 194 for i = 0; i < len(fd); i++ { 195 if fd[i] >= 0 && fd[i] < int(i) { 196 if nextfd == pipe { // don't stomp on pipe 197 nextfd++ 198 } 199 _, _, err1 = rawSyscall(funcPC(libc_dup2_trampoline), uintptr(fd[i]), uintptr(nextfd), 0) 200 if err1 != 0 { 201 goto childerror 202 } 203 rawSyscall(funcPC(libc_fcntl_trampoline), uintptr(nextfd), syscall.F_SETFD, syscall.FD_CLOEXEC) 204 fd[i] = nextfd 205 nextfd++ 206 } 207 } 208 209 // Pass 2: dup fd[i] down onto i. 210 for i = 0; i < len(fd); i++ { 211 if fd[i] == -1 { 212 rawSyscall(funcPC(libc_close_trampoline), uintptr(i), 0, 0) 213 continue 214 } 215 if fd[i] == int(i) { 216 // dup2(i, i) won't clear close-on-exec flag on Linux, 217 // probably not elsewhere either. 218 _, _, err1 = rawSyscall(funcPC(libc_fcntl_trampoline), uintptr(fd[i]), syscall.F_SETFD, 0) 219 if err1 != 0 { 220 goto childerror 221 } 222 continue 223 } 224 // The new fd is created NOT close-on-exec, 225 // which is exactly what we want. 226 _, _, err1 = rawSyscall(funcPC(libc_dup2_trampoline), uintptr(fd[i]), uintptr(i), 0) 227 if err1 != 0 { 228 goto childerror 229 } 230 } 231 232 // By convention, we don't close-on-exec the fds we are 233 // started with, so if len(fd) < 3, close 0, 1, 2 as needed. 234 // Programs that know they inherit fds >= 3 will need 235 // to set them close-on-exec. 236 for i = len(fd); i < 3; i++ { 237 rawSyscall(funcPC(libc_close_trampoline), uintptr(i), 0, 0) 238 } 239 240 // Detach fd 0 from tty 241 if sys.Noctty { 242 _, _, err1 = rawSyscall(funcPC(libc_ioctl_trampoline), 0, uintptr(syscall.TIOCNOTTY), 0) 243 if err1 != 0 { 244 goto childerror 245 } 246 } 247 248 // Set the controlling TTY to Ctty 249 if sys.Setctty { 250 _, _, err1 = rawSyscall(funcPC(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(syscall.TIOCSCTTY), 0) 251 if err1 != 0 { 252 goto childerror 253 } 254 } 255 256 // Set resource limitations 257 for _, rlimit := range rlimitOptions.Rlimits { 258 if !rlimit.Enable { 259 continue 260 } 261 _, _, err1 = rawSyscall(funcPC(libc_setrlimit_trampoline), uintptr(rlimit.Which), uintptr(unsafe.Pointer(&rlimit.RLim)), 0) 262 if err1 != 0 { 263 goto childerror 264 } 265 } 266 267 // Set real time limitation 268 if sys.Rlimit.RealTimeLimit > 0 { 269 _, _, err1 = syscall.RawSyscall(syscall.SYS_SETITIMER, ITIMER_REAL, uintptr(unsafe.Pointer(&rlimitOptions.ITimerValue)), 0) 270 if err1 != 0 { 271 goto childerror 272 } 273 } 274 275 // Time to exec. 276 _, _, err1 = rawSyscall(funcPC(libc_execve_trampoline), 277 uintptr(unsafe.Pointer(argv0)), 278 uintptr(unsafe.Pointer(&argv[0])), 279 uintptr(unsafe.Pointer(&envv[0]))) 280 281 childerror: 282 // send error code on pipe 283 rawSyscall(funcPC(libc_write_trampoline), uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1)) 284 for { 285 rawSyscall(funcPC(libc_exit_trampoline), 253, 0, 0) 286 } 287 } 288 289 // Try to open a pipe with O_CLOEXEC set on both file descriptors. 290 func forkExecPipe(p []int) error { 291 err := syscall.Pipe(p) 292 if err != nil { 293 return err 294 } 295 _, err = fcntl(p[0], syscall.F_SETFD, syscall.FD_CLOEXEC) 296 if err != nil { 297 return err 298 } 299 _, err = fcntl(p[1], syscall.F_SETFD, syscall.FD_CLOEXEC) 300 return err 301 }