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 aix || solaris
6
7// This file handles forkAndExecInChild function for OS using libc syscall like AIX or Solaris.
8
9package syscall
10
11import (
12	"runtime"
13	"unsafe"
14)
15
16type SysProcAttr struct {
17	Chroot     string      // Chroot.
18	Credential *Credential // Credential.
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
44func chdir(path uintptr) (err Errno)
45func chroot1(path uintptr) (err Errno)
46func closeFD(fd uintptr) (err Errno)
47func dup2child(old uintptr, new uintptr) (val uintptr, err Errno)
48func execve(path uintptr, argv uintptr, envp uintptr) (err Errno)
49func exit(code uintptr)
50func fcntl1(fd uintptr, cmd uintptr, arg uintptr) (val uintptr, err Errno)
51func forkx(flags uintptr) (pid uintptr, err Errno)
52func getpid() (pid uintptr, err Errno)
53func ioctl(fd uintptr, req uintptr, arg uintptr) (err Errno)
54func setgid(gid uintptr) (err Errno)
55func setgroups1(ngid uintptr, gid uintptr) (err Errno)
56func setrlimit1(which uintptr, lim unsafe.Pointer) (err Errno)
57func setsid() (pid uintptr, err Errno)
58func setuid(uid uintptr) (err Errno)
59func setpgid(pid uintptr, pgid uintptr) (err Errno)
60func write1(fd uintptr, buf uintptr, nbyte uintptr) (n uintptr, err Errno)
61
62// syscall defines this global on our behalf to avoid a build dependency on other platforms
63func init() {
64	execveLibc = execve
65}
66
67// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
68// If a dup or exec fails, write the errno error to pipe.
69// (Pipe is close-on-exec so if exec succeeds, it will be closed.)
70// In the child, this function must not acquire any locks, because
71// they might have been locked at the time of the fork. This means
72// no rescheduling, no malloc calls, and no new stack segments.
73//
74// We call hand-crafted syscalls, implemented in
75// ../runtime/syscall_solaris.go, rather than generated libc wrappers
76// because we need to avoid lazy-loading the functions (might malloc,
77// split the stack, or acquire mutexes). We can't call RawSyscall
78// because it's not safe even for BSD-subsystem calls.
79//
80//go:norace
81func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
82	// Declare all variables at top in case any
83	// declarations require heap allocation (e.g., err1).
84	var (
85		r1              uintptr
86		err1            Errno
87		nextfd          int
88		i               int
89		pgrp            _Pid_t
90		cred            *Credential
91		ngroups, groups uintptr
92	)
93
94	rlim := origRlimitNofile.Load()
95
96	// guard against side effects of shuffling fds below.
97	// Make sure that nextfd is beyond any currently open files so
98	// that we can't run the risk of overwriting any of them.
99	fd := make([]int, len(attr.Files))
100	nextfd = len(attr.Files)
101	for i, ufd := range attr.Files {
102		if nextfd < int(ufd) {
103			nextfd = int(ufd)
104		}
105		fd[i] = int(ufd)
106	}
107	nextfd++
108
109	// About to call fork.
110	// No more allocation or calls of non-assembly functions.
111	runtime_BeforeFork()
112	r1, err1 = forkx(0x1) // FORK_NOSIGCHLD
113	if err1 != 0 {
114		runtime_AfterFork()
115		return 0, err1
116	}
117
118	if r1 != 0 {
119		// parent; return PID
120		runtime_AfterFork()
121		return int(r1), 0
122	}
123
124	// Fork succeeded, now in child.
125
126	// Session ID
127	if sys.Setsid {
128		_, err1 = setsid()
129		if err1 != 0 {
130			goto childerror
131		}
132	}
133
134	// Set process group
135	if sys.Setpgid || sys.Foreground {
136		// Place child in process group.
137		err1 = setpgid(0, uintptr(sys.Pgid))
138		if err1 != 0 {
139			goto childerror
140		}
141	}
142
143	if sys.Foreground {
144		pgrp = _Pid_t(sys.Pgid)
145		if pgrp == 0 {
146			r1, err1 = getpid()
147			if err1 != 0 {
148				goto childerror
149			}
150
151			pgrp = _Pid_t(r1)
152		}
153
154		// Place process group in foreground.
155		err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
156		if err1 != 0 {
157			goto childerror
158		}
159	}
160
161	// Restore the signal mask. We do this after TIOCSPGRP to avoid
162	// having the kernel send a SIGTTOU signal to the process group.
163	runtime_AfterForkInChild()
164
165	// Chroot
166	if chroot != nil {
167		err1 = chroot1(uintptr(unsafe.Pointer(chroot)))
168		if err1 != 0 {
169			goto childerror
170		}
171	}
172
173	// User and groups
174	if cred = sys.Credential; cred != nil {
175		ngroups = uintptr(len(cred.Groups))
176		groups = uintptr(0)
177		if ngroups > 0 {
178			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
179		}
180		if !cred.NoSetGroups {
181			err1 = setgroups1(ngroups, groups)
182			if err1 != 0 {
183				goto childerror
184			}
185		}
186		err1 = setgid(uintptr(cred.Gid))
187		if err1 != 0 {
188			goto childerror
189		}
190		err1 = setuid(uintptr(cred.Uid))
191		if err1 != 0 {
192			goto childerror
193		}
194	}
195
196	// Chdir
197	if dir != nil {
198		err1 = chdir(uintptr(unsafe.Pointer(dir)))
199		if err1 != 0 {
200			goto childerror
201		}
202	}
203
204	// Pass 1: look for fd[i] < i and move those up above len(fd)
205	// so that pass 2 won't stomp on an fd it needs later.
206	if pipe < nextfd {
207		switch runtime.GOOS {
208		case "illumos", "solaris":
209			_, err1 = fcntl1(uintptr(pipe), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
210		default:
211			_, err1 = dup2child(uintptr(pipe), uintptr(nextfd))
212			if err1 != 0 {
213				goto childerror
214			}
215			_, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
216		}
217		if err1 != 0 {
218			goto childerror
219		}
220		pipe = nextfd
221		nextfd++
222	}
223	for i = 0; i < len(fd); i++ {
224		if fd[i] >= 0 && fd[i] < i {
225			if nextfd == pipe { // don't stomp on pipe
226				nextfd++
227			}
228			switch runtime.GOOS {
229			case "illumos", "solaris":
230				_, err1 = fcntl1(uintptr(fd[i]), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
231			default:
232				_, err1 = dup2child(uintptr(fd[i]), uintptr(nextfd))
233				if err1 != 0 {
234					goto childerror
235				}
236				_, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
237			}
238			if err1 != 0 {
239				goto childerror
240			}
241			fd[i] = nextfd
242			nextfd++
243		}
244	}
245
246	// Pass 2: dup fd[i] down onto i.
247	for i = 0; i < len(fd); i++ {
248		if fd[i] == -1 {
249			closeFD(uintptr(i))
250			continue
251		}
252		if fd[i] == i {
253			// dup2(i, i) won't clear close-on-exec flag on Linux,
254			// probably not elsewhere either.
255			_, err1 = fcntl1(uintptr(fd[i]), F_SETFD, 0)
256			if err1 != 0 {
257				goto childerror
258			}
259			continue
260		}
261		// The new fd is created NOT close-on-exec,
262		// which is exactly what we want.
263		_, err1 = dup2child(uintptr(fd[i]), uintptr(i))
264		if err1 != 0 {
265			goto childerror
266		}
267	}
268
269	// By convention, we don't close-on-exec the fds we are
270	// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
271	// Programs that know they inherit fds >= 3 will need
272	// to set them close-on-exec.
273	for i = len(fd); i < 3; i++ {
274		closeFD(uintptr(i))
275	}
276
277	// Detach fd 0 from tty
278	if sys.Noctty {
279		err1 = ioctl(0, uintptr(TIOCNOTTY), 0)
280		if err1 != 0 {
281			goto childerror
282		}
283	}
284
285	// Set the controlling TTY to Ctty
286	if sys.Setctty {
287		// On AIX, TIOCSCTTY is undefined
288		if TIOCSCTTY == 0 {
289			err1 = ENOSYS
290			goto childerror
291		}
292		err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
293		if err1 != 0 {
294			goto childerror
295		}
296	}
297
298	// Restore original rlimit.
299	if rlim != nil {
300		setrlimit1(RLIMIT_NOFILE, unsafe.Pointer(rlim))
301	}
302
303	// Time to exec.
304	err1 = execve(
305		uintptr(unsafe.Pointer(argv0)),
306		uintptr(unsafe.Pointer(&argv[0])),
307		uintptr(unsafe.Pointer(&envv[0])))
308
309childerror:
310	// send error code on pipe
311	write1(uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
312	for {
313		exit(253)
314	}
315}
316
317// forkAndExecFailureCleanup cleans up after an exec failure.
318func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
319	// Nothing to do.
320}
321
322func ioctlPtr(fd, req uintptr, arg unsafe.Pointer) (err Errno) {
323	return ioctl(fd, req, uintptr(arg))
324}
325