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

View as plain text