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