Source file src/syscall/exec_freebsd.go

     1  // Copyright 2021 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  package syscall
     6  
     7  import (
     8  	"runtime"
     9  	"unsafe"
    10  )
    11  
    12  type SysProcAttr struct {
    13  	Chroot     string      // Chroot.
    14  	Credential *Credential // Credential.
    15  	Ptrace     bool        // Enable tracing.
    16  	Setsid     bool        // Create session.
    17  	// Setpgid sets the process group ID of the child to Pgid,
    18  	// or, if Pgid == 0, to the new child's process ID.
    19  	Setpgid bool
    20  	// Setctty sets the controlling terminal of the child to
    21  	// file descriptor Ctty. Ctty must be a descriptor number
    22  	// in the child process: an index into ProcAttr.Files.
    23  	// This is only meaningful if Setsid is true.
    24  	Setctty bool
    25  	Noctty  bool // Detach fd 0 from controlling terminal
    26  	Ctty    int  // Controlling TTY fd
    27  	// Foreground places the child process group in the foreground.
    28  	// This implies Setpgid. The Ctty field must be set to
    29  	// the descriptor of the controlling TTY.
    30  	// Unlike Setctty, in this case Ctty must be a descriptor
    31  	// number in the parent process.
    32  	Foreground bool
    33  	Pgid       int    // Child's process group ID if Setpgid.
    34  	Pdeathsig  Signal // Signal that the process will get when its parent dies (Linux and FreeBSD only)
    35  }
    36  
    37  const (
    38  	_P_PID = 0
    39  
    40  	_PROC_PDEATHSIG_CTL = 11
    41  )
    42  
    43  // Implemented in runtime package.
    44  func runtime_BeforeFork()
    45  func runtime_AfterFork()
    46  func runtime_AfterForkInChild()
    47  
    48  // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
    49  // If a dup or exec fails, write the errno error to pipe.
    50  // (Pipe is close-on-exec so if exec succeeds, it will be closed.)
    51  // In the child, this function must not acquire any locks, because
    52  // they might have been locked at the time of the fork. This means
    53  // no rescheduling, no malloc calls, and no new stack segments.
    54  // For the same reason compiler does not race instrument it.
    55  // The calls to RawSyscall are okay because they are assembly
    56  // functions that do not grow the stack.
    57  //
    58  //go:norace
    59  func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
    60  	// Declare all variables at top in case any
    61  	// declarations require heap allocation (e.g., err1).
    62  	var (
    63  		r1     uintptr
    64  		err1   Errno
    65  		nextfd int
    66  		i      int
    67  	)
    68  
    69  	// Record parent PID so child can test if it has died.
    70  	ppid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
    71  
    72  	// guard against side effects of shuffling fds below.
    73  	// Make sure that nextfd is beyond any currently open files so
    74  	// that we can't run the risk of overwriting any of them.
    75  	fd := make([]int, len(attr.Files))
    76  	nextfd = len(attr.Files)
    77  	for i, ufd := range attr.Files {
    78  		if nextfd < int(ufd) {
    79  			nextfd = int(ufd)
    80  		}
    81  		fd[i] = int(ufd)
    82  	}
    83  	nextfd++
    84  
    85  	// About to call fork.
    86  	// No more allocation or calls of non-assembly functions.
    87  	runtime_BeforeFork()
    88  	r1, _, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
    89  	if err1 != 0 {
    90  		runtime_AfterFork()
    91  		return 0, err1
    92  	}
    93  
    94  	if r1 != 0 {
    95  		// parent; return PID
    96  		runtime_AfterFork()
    97  		return int(r1), 0
    98  	}
    99  
   100  	// Fork succeeded, now in child.
   101  
   102  	// Enable tracing if requested.
   103  	if sys.Ptrace {
   104  		_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
   105  		if err1 != 0 {
   106  			goto childerror
   107  		}
   108  	}
   109  
   110  	// Session ID
   111  	if sys.Setsid {
   112  		_, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
   113  		if err1 != 0 {
   114  			goto childerror
   115  		}
   116  	}
   117  
   118  	// Set process group
   119  	if sys.Setpgid || sys.Foreground {
   120  		// Place child in process group.
   121  		_, _, err1 = RawSyscall(SYS_SETPGID, 0, uintptr(sys.Pgid), 0)
   122  		if err1 != 0 {
   123  			goto childerror
   124  		}
   125  	}
   126  
   127  	if sys.Foreground {
   128  		// This should really be pid_t, however _C_int (aka int32) is
   129  		// generally equivalent.
   130  		pgrp := _C_int(sys.Pgid)
   131  		if pgrp == 0 {
   132  			r1, _, err1 = RawSyscall(SYS_GETPID, 0, 0, 0)
   133  			if err1 != 0 {
   134  				goto childerror
   135  			}
   136  
   137  			pgrp = _C_int(r1)
   138  		}
   139  
   140  		// Place process group in foreground.
   141  		_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
   142  		if err1 != 0 {
   143  			goto childerror
   144  		}
   145  	}
   146  
   147  	// Restore the signal mask. We do this after TIOCSPGRP to avoid
   148  	// having the kernel send a SIGTTOU signal to the process group.
   149  	runtime_AfterForkInChild()
   150  
   151  	// Chroot
   152  	if chroot != nil {
   153  		_, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
   154  		if err1 != 0 {
   155  			goto childerror
   156  		}
   157  	}
   158  
   159  	// User and groups
   160  	if cred := sys.Credential; cred != nil {
   161  		ngroups := uintptr(len(cred.Groups))
   162  		groups := uintptr(0)
   163  		if ngroups > 0 {
   164  			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
   165  		}
   166  		if !cred.NoSetGroups {
   167  			_, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0)
   168  			if err1 != 0 {
   169  				goto childerror
   170  			}
   171  		}
   172  		_, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0)
   173  		if err1 != 0 {
   174  			goto childerror
   175  		}
   176  		_, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0)
   177  		if err1 != 0 {
   178  			goto childerror
   179  		}
   180  	}
   181  
   182  	// Chdir
   183  	if dir != nil {
   184  		_, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
   185  		if err1 != 0 {
   186  			goto childerror
   187  		}
   188  	}
   189  
   190  	// Parent death signal
   191  	if sys.Pdeathsig != 0 {
   192  		switch runtime.GOARCH {
   193  		case "386", "arm":
   194  			_, _, err1 = RawSyscall6(SYS_PROCCTL, _P_PID, 0, 0, _PROC_PDEATHSIG_CTL, uintptr(unsafe.Pointer(&sys.Pdeathsig)), 0)
   195  		default:
   196  			_, _, err1 = RawSyscall6(SYS_PROCCTL, _P_PID, 0, _PROC_PDEATHSIG_CTL, uintptr(unsafe.Pointer(&sys.Pdeathsig)), 0, 0)
   197  		}
   198  		if err1 != 0 {
   199  			goto childerror
   200  		}
   201  
   202  		// Signal self if parent is already dead. This might cause a
   203  		// duplicate signal in rare cases, but it won't matter when
   204  		// using SIGKILL.
   205  		r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
   206  		if r1 != ppid {
   207  			pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
   208  			_, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
   209  			if err1 != 0 {
   210  				goto childerror
   211  			}
   212  		}
   213  	}
   214  
   215  	// Pass 1: look for fd[i] < i and move those up above len(fd)
   216  	// so that pass 2 won't stomp on an fd it needs later.
   217  	if pipe < nextfd {
   218  		_, _, err1 = RawSyscall(SYS_FCNTL, uintptr(pipe), F_DUP2FD_CLOEXEC, uintptr(nextfd))
   219  		if err1 != 0 {
   220  			goto childerror
   221  		}
   222  		pipe = nextfd
   223  		nextfd++
   224  	}
   225  	for i = 0; i < len(fd); i++ {
   226  		if fd[i] >= 0 && fd[i] < int(i) {
   227  			if nextfd == pipe { // don't stomp on pipe
   228  				nextfd++
   229  			}
   230  			_, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), F_DUP2FD_CLOEXEC, uintptr(nextfd))
   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  			RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
   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 = RawSyscall(SYS_FCNTL, 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 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(i), 0)
   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  		RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
   268  	}
   269  
   270  	// Detach fd 0 from tty
   271  	if sys.Noctty {
   272  		_, _, err1 = RawSyscall(SYS_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  		_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
   281  		if err1 != 0 {
   282  			goto childerror
   283  		}
   284  	}
   285  
   286  	// Time to exec.
   287  	_, _, err1 = RawSyscall(SYS_EXECVE,
   288  		uintptr(unsafe.Pointer(argv0)),
   289  		uintptr(unsafe.Pointer(&argv[0])),
   290  		uintptr(unsafe.Pointer(&envv[0])))
   291  
   292  childerror:
   293  	// send error code on pipe
   294  	RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
   295  	for {
   296  		RawSyscall(SYS_EXIT, 253, 0, 0)
   297  	}
   298  }
   299  

View as plain text