Home | History | Annotate | Download | only in syscall
      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 dragonfly freebsd netbsd openbsd
      6 
      7 package syscall
      8 
      9 import (
     10 	"runtime"
     11 	"unsafe"
     12 )
     13 
     14 type SysProcAttr struct {
     15 	Chroot     string      // Chroot.
     16 	Credential *Credential // Credential.
     17 	Ptrace     bool        // Enable tracing.
     18 	Setsid     bool        // Create session.
     19 	Setpgid    bool        // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
     20 	Setctty    bool        // Set controlling terminal to fd Ctty
     21 	Noctty     bool        // Detach fd 0 from controlling terminal
     22 	Ctty       int         // Controlling TTY fd
     23 	Foreground bool        // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY)
     24 	Pgid       int         // Child's process group ID if Setpgid.
     25 }
     26 
     27 // Implemented in runtime package.
     28 func runtime_BeforeFork()
     29 func runtime_AfterFork()
     30 func runtime_AfterForkInChild()
     31 
     32 // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
     33 // If a dup or exec fails, write the errno error to pipe.
     34 // (Pipe is close-on-exec so if exec succeeds, it will be closed.)
     35 // In the child, this function must not acquire any locks, because
     36 // they might have been locked at the time of the fork. This means
     37 // no rescheduling, no malloc calls, and no new stack segments.
     38 // For the same reason compiler does not race instrument it.
     39 // The calls to RawSyscall are okay because they are assembly
     40 // functions that do not grow the stack.
     41 //go:norace
     42 func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
     43 	// Declare all variables at top in case any
     44 	// declarations require heap allocation (e.g., err1).
     45 	var (
     46 		r1, r2 uintptr
     47 		err1   Errno
     48 		nextfd int
     49 		i      int
     50 	)
     51 
     52 	// guard against side effects of shuffling fds below.
     53 	// Make sure that nextfd is beyond any currently open files so
     54 	// that we can't run the risk of overwriting any of them.
     55 	fd := make([]int, len(attr.Files))
     56 	nextfd = len(attr.Files)
     57 	for i, ufd := range attr.Files {
     58 		if nextfd < int(ufd) {
     59 			nextfd = int(ufd)
     60 		}
     61 		fd[i] = int(ufd)
     62 	}
     63 	nextfd++
     64 
     65 	darwin := runtime.GOOS == "darwin"
     66 
     67 	// About to call fork.
     68 	// No more allocation or calls of non-assembly functions.
     69 	runtime_BeforeFork()
     70 	r1, r2, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
     71 	if err1 != 0 {
     72 		runtime_AfterFork()
     73 		return 0, err1
     74 	}
     75 
     76 	// On Darwin:
     77 	//	r1 = child pid in both parent and child.
     78 	//	r2 = 0 in parent, 1 in child.
     79 	// Convert to normal Unix r1 = 0 in child.
     80 	if darwin && r2 == 1 {
     81 		r1 = 0
     82 	}
     83 
     84 	if r1 != 0 {
     85 		// parent; return PID
     86 		runtime_AfterFork()
     87 		return int(r1), 0
     88 	}
     89 
     90 	// Fork succeeded, now in child.
     91 
     92 	runtime_AfterForkInChild()
     93 
     94 	// Enable tracing if requested.
     95 	if sys.Ptrace {
     96 		_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
     97 		if err1 != 0 {
     98 			goto childerror
     99 		}
    100 	}
    101 
    102 	// Session ID
    103 	if sys.Setsid {
    104 		_, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
    105 		if err1 != 0 {
    106 			goto childerror
    107 		}
    108 	}
    109 
    110 	// Set process group
    111 	if sys.Setpgid || sys.Foreground {
    112 		// Place child in process group.
    113 		_, _, err1 = RawSyscall(SYS_SETPGID, 0, uintptr(sys.Pgid), 0)
    114 		if err1 != 0 {
    115 			goto childerror
    116 		}
    117 	}
    118 
    119 	if sys.Foreground {
    120 		pgrp := sys.Pgid
    121 		if pgrp == 0 {
    122 			r1, _, err1 = RawSyscall(SYS_GETPID, 0, 0, 0)
    123 			if err1 != 0 {
    124 				goto childerror
    125 			}
    126 
    127 			pgrp = int(r1)
    128 		}
    129 
    130 		// Place process group in foreground.
    131 		_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
    132 		if err1 != 0 {
    133 			goto childerror
    134 		}
    135 	}
    136 
    137 	// Chroot
    138 	if chroot != nil {
    139 		_, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
    140 		if err1 != 0 {
    141 			goto childerror
    142 		}
    143 	}
    144 
    145 	// User and groups
    146 	if cred := sys.Credential; cred != nil {
    147 		ngroups := uintptr(len(cred.Groups))
    148 		groups := uintptr(0)
    149 		if ngroups > 0 {
    150 			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
    151 		}
    152 		if !cred.NoSetGroups {
    153 			_, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0)
    154 			if err1 != 0 {
    155 				goto childerror
    156 			}
    157 		}
    158 		_, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0)
    159 		if err1 != 0 {
    160 			goto childerror
    161 		}
    162 		_, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0)
    163 		if err1 != 0 {
    164 			goto childerror
    165 		}
    166 	}
    167 
    168 	// Chdir
    169 	if dir != nil {
    170 		_, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
    171 		if err1 != 0 {
    172 			goto childerror
    173 		}
    174 	}
    175 
    176 	// Pass 1: look for fd[i] < i and move those up above len(fd)
    177 	// so that pass 2 won't stomp on an fd it needs later.
    178 	if pipe < nextfd {
    179 		_, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
    180 		if err1 != 0 {
    181 			goto childerror
    182 		}
    183 		RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
    184 		pipe = nextfd
    185 		nextfd++
    186 	}
    187 	for i = 0; i < len(fd); i++ {
    188 		if fd[i] >= 0 && fd[i] < int(i) {
    189 			if nextfd == pipe { // don't stomp on pipe
    190 				nextfd++
    191 			}
    192 			_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
    193 			if err1 != 0 {
    194 				goto childerror
    195 			}
    196 			RawSyscall(SYS_FCNTL, uintptr(nextfd), F_SETFD, FD_CLOEXEC)
    197 			fd[i] = nextfd
    198 			nextfd++
    199 		}
    200 	}
    201 
    202 	// Pass 2: dup fd[i] down onto i.
    203 	for i = 0; i < len(fd); i++ {
    204 		if fd[i] == -1 {
    205 			RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
    206 			continue
    207 		}
    208 		if fd[i] == int(i) {
    209 			// dup2(i, i) won't clear close-on-exec flag on Linux,
    210 			// probably not elsewhere either.
    211 			_, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_SETFD, 0)
    212 			if err1 != 0 {
    213 				goto childerror
    214 			}
    215 			continue
    216 		}
    217 		// The new fd is created NOT close-on-exec,
    218 		// which is exactly what we want.
    219 		_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0)
    220 		if err1 != 0 {
    221 			goto childerror
    222 		}
    223 	}
    224 
    225 	// By convention, we don't close-on-exec the fds we are
    226 	// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
    227 	// Programs that know they inherit fds >= 3 will need
    228 	// to set them close-on-exec.
    229 	for i = len(fd); i < 3; i++ {
    230 		RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
    231 	}
    232 
    233 	// Detach fd 0 from tty
    234 	if sys.Noctty {
    235 		_, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0)
    236 		if err1 != 0 {
    237 			goto childerror
    238 		}
    239 	}
    240 
    241 	// Set the controlling TTY to Ctty
    242 	if sys.Setctty {
    243 		_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
    244 		if err1 != 0 {
    245 			goto childerror
    246 		}
    247 	}
    248 
    249 	// Time to exec.
    250 	_, _, err1 = RawSyscall(SYS_EXECVE,
    251 		uintptr(unsafe.Pointer(argv0)),
    252 		uintptr(unsafe.Pointer(&argv[0])),
    253 		uintptr(unsafe.Pointer(&envv[0])))
    254 
    255 childerror:
    256 	// send error code on pipe
    257 	RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
    258 	for {
    259 		RawSyscall(SYS_EXIT, 253, 0, 0)
    260 	}
    261 }
    262