exec_libc.mx raw

   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  
   9  package syscall
  10  
  11  import (
  12  	"runtime"
  13  	"unsafe"
  14  )
  15  
  16  type 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.
  40  func runtime_BeforeFork()
  41  func runtime_AfterFork()
  42  func runtime_AfterForkInChild()
  43  
  44  func chdir(path uintptr) (err Errno)
  45  func chroot1(path uintptr) (err Errno)
  46  func closeFD(fd uintptr) (err Errno)
  47  func dup2child(old uintptr, new uintptr) (val uintptr, err Errno)
  48  func execve(path uintptr, argv uintptr, envp uintptr) (err Errno)
  49  func exit(code uintptr)
  50  func fcntl1(fd uintptr, cmd uintptr, arg uintptr) (val uintptr, err Errno)
  51  func forkx(flags uintptr) (pid uintptr, err Errno)
  52  func getpid() (pid uintptr, err Errno)
  53  func ioctl(fd uintptr, req uintptr, arg uintptr) (err Errno)
  54  func setgid(gid uintptr) (err Errno)
  55  func setgroups1(ngid uintptr, gid uintptr) (err Errno)
  56  func setrlimit1(which uintptr, lim unsafe.Pointer) (err Errno)
  57  func setsid() (pid uintptr, err Errno)
  58  func setuid(uid uintptr) (err Errno)
  59  func setpgid(pid uintptr, pgid uintptr) (err Errno)
  60  func 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
  63  func 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
  81  func 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  
 309  childerror:
 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.
 318  func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
 319  	// Nothing to do.
 320  }
 321  
 322  func ioctlPtr(fd, req uintptr, arg unsafe.Pointer) (err Errno) {
 323  	return ioctl(fd, req, uintptr(arg))
 324  }
 325