Source file src/syscall/exec_libc.go

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

View as plain text