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  //
    79  //go:norace
    80  func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
    81  	// Declare all variables at top in case any
    82  	// declarations require heap allocation (e.g., err1).
    83  	var (
    84  		r1     uintptr
    85  		err1   Errno
    86  		nextfd int
    87  		i      int
    88  	)
    89  
    90  	// guard against side effects of shuffling fds below.
    91  	// Make sure that nextfd is beyond any currently open files so
    92  	// that we can't run the risk of overwriting any of them.
    93  	fd := make([]int, len(attr.Files))
    94  	nextfd = len(attr.Files)
    95  	for i, ufd := range attr.Files {
    96  		if nextfd < int(ufd) {
    97  			nextfd = int(ufd)
    98  		}
    99  		fd[i] = int(ufd)
   100  	}
   101  	nextfd++
   102  
   103  	// About to call fork.
   104  	// No more allocation or calls of non-assembly functions.
   105  	runtime_BeforeFork()
   106  	r1, err1 = forkx(0x1) // FORK_NOSIGCHLD
   107  	if err1 != 0 {
   108  		runtime_AfterFork()
   109  		return 0, err1
   110  	}
   111  
   112  	if r1 != 0 {
   113  		// parent; return PID
   114  		runtime_AfterFork()
   115  		return int(r1), 0
   116  	}
   117  
   118  	// Fork succeeded, now in child.
   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  	// Restore the signal mask. We do this after TIOCSPGRP to avoid
   156  	// having the kernel send a SIGTTOU signal to the process group.
   157  	runtime_AfterForkInChild()
   158  
   159  	// Chroot
   160  	if chroot != nil {
   161  		err1 = chroot1(uintptr(unsafe.Pointer(chroot)))
   162  		if err1 != 0 {
   163  			goto childerror
   164  		}
   165  	}
   166  
   167  	// User and groups
   168  	if cred := sys.Credential; cred != nil {
   169  		ngroups := uintptr(len(cred.Groups))
   170  		groups := uintptr(0)
   171  		if ngroups > 0 {
   172  			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
   173  		}
   174  		if !cred.NoSetGroups {
   175  			err1 = setgroups1(ngroups, groups)
   176  			if err1 != 0 {
   177  				goto childerror
   178  			}
   179  		}
   180  		err1 = setgid(uintptr(cred.Gid))
   181  		if err1 != 0 {
   182  			goto childerror
   183  		}
   184  		err1 = setuid(uintptr(cred.Uid))
   185  		if err1 != 0 {
   186  			goto childerror
   187  		}
   188  	}
   189  
   190  	// Chdir
   191  	if dir != nil {
   192  		err1 = chdir(uintptr(unsafe.Pointer(dir)))
   193  		if err1 != 0 {
   194  			goto childerror
   195  		}
   196  	}
   197  
   198  	// Pass 1: look for fd[i] < i and move those up above len(fd)
   199  	// so that pass 2 won't stomp on an fd it needs later.
   200  	if pipe < nextfd {
   201  		switch runtime.GOOS {
   202  		case "illumos":
   203  			_, err1 = fcntl1(uintptr(pipe), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
   204  		default:
   205  			_, err1 = dup2child(uintptr(pipe), uintptr(nextfd))
   206  			if err1 != 0 {
   207  				goto childerror
   208  			}
   209  			_, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
   210  		}
   211  		if err1 != 0 {
   212  			goto childerror
   213  		}
   214  		pipe = nextfd
   215  		nextfd++
   216  	}
   217  	for i = 0; i < len(fd); i++ {
   218  		if fd[i] >= 0 && fd[i] < int(i) {
   219  			if nextfd == pipe { // don't stomp on pipe
   220  				nextfd++
   221  			}
   222  			switch runtime.GOOS {
   223  			case "illumos":
   224  				_, err1 = fcntl1(uintptr(fd[i]), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
   225  			default:
   226  				_, err1 = dup2child(uintptr(fd[i]), uintptr(nextfd))
   227  				if err1 != 0 {
   228  					goto childerror
   229  				}
   230  				_, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
   231  			}
   232  			if err1 != 0 {
   233  				goto childerror
   234  			}
   235  			fd[i] = nextfd
   236  			nextfd++
   237  		}
   238  	}
   239  
   240  	// Pass 2: dup fd[i] down onto i.
   241  	for i = 0; i < len(fd); i++ {
   242  		if fd[i] == -1 {
   243  			close(uintptr(i))
   244  			continue
   245  		}
   246  		if fd[i] == int(i) {
   247  			// dup2(i, i) won't clear close-on-exec flag on Linux,
   248  			// probably not elsewhere either.
   249  			_, err1 = fcntl1(uintptr(fd[i]), F_SETFD, 0)
   250  			if err1 != 0 {
   251  				goto childerror
   252  			}
   253  			continue
   254  		}
   255  		// The new fd is created NOT close-on-exec,
   256  		// which is exactly what we want.
   257  		_, err1 = dup2child(uintptr(fd[i]), uintptr(i))
   258  		if err1 != 0 {
   259  			goto childerror
   260  		}
   261  	}
   262  
   263  	// By convention, we don't close-on-exec the fds we are
   264  	// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
   265  	// Programs that know they inherit fds >= 3 will need
   266  	// to set them close-on-exec.
   267  	for i = len(fd); i < 3; i++ {
   268  		close(uintptr(i))
   269  	}
   270  
   271  	// Detach fd 0 from tty
   272  	if sys.Noctty {
   273  		err1 = ioctl(0, uintptr(TIOCNOTTY), 0)
   274  		if err1 != 0 {
   275  			goto childerror
   276  		}
   277  	}
   278  
   279  	// Set the controlling TTY to Ctty
   280  	if sys.Setctty {
   281  		// On AIX, TIOCSCTTY is undefined
   282  		if TIOCSCTTY == 0 {
   283  			err1 = ENOSYS
   284  			goto childerror
   285  		}
   286  		err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
   287  		if err1 != 0 {
   288  			goto childerror
   289  		}
   290  	}
   291  
   292  	// Time to exec.
   293  	err1 = execve(
   294  		uintptr(unsafe.Pointer(argv0)),
   295  		uintptr(unsafe.Pointer(&argv[0])),
   296  		uintptr(unsafe.Pointer(&envv[0])))
   297  
   298  childerror:
   299  	// send error code on pipe
   300  	write1(uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
   301  	for {
   302  		exit(253)
   303  	}
   304  }
   305  

View as plain text