Black Lives Matter. Support the Equal Justice Initiative.

Source file src/syscall/exec_libc.go

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

View as plain text