github.com/LanceLRQ/deer-common@v0.0.9-0.20210319081233-e8222ac018a8/sandbox/forkexec/exec_darwin.go (about)

     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 darwin
     6  
     7  package forkexec
     8  
     9  import (
    10  	"syscall"
    11  	"unsafe"
    12  )
    13  
    14  // Find the entry point for f. See comments in runtime/proc.go for the
    15  // function of the same name.
    16  //go:nosplit
    17  func funcPC(f func()) uintptr {
    18  	return **(**uintptr)(unsafe.Pointer(&f))
    19  }
    20  
    21  type SysProcAttr struct {
    22  	Chroot     string              // Chroot.
    23  	Credential *syscall.Credential // Credential.
    24  	Ptrace     bool                // Enable tracing.
    25  	Setsid     bool                // Create session.
    26  	// Setpgid sets the process group ID of the child to Pgid,
    27  	// or, if Pgid == 0, to the new child's process ID.
    28  	Setpgid bool
    29  	// Setctty sets the controlling terminal of the child to
    30  	// file descriptor Ctty. Ctty must be a descriptor number
    31  	// in the child process: an index into ProcAttr.Files.
    32  	// This is only meaningful if Setsid is true.
    33  	Setctty bool
    34  	Noctty  bool // Detach fd 0 from controlling terminal
    35  	Ctty    int  // Controlling TTY fd
    36  	// Foreground places the child process group in the foreground.
    37  	// This implies Setpgid. The Ctty field must be set to
    38  	// the descriptor of the controlling TTY.
    39  	// Unlike Setctty, in this case Ctty must be a descriptor
    40  	// number in the parent process.
    41  	Foreground bool
    42  	Pgid       int        // Child's process group ID if Setpgid.
    43  	Rlimit     ExecRLimit // Set child's rlimit.
    44  }
    45  
    46  // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
    47  // If a dup or exec fails, write the errno error to pipe.
    48  // (Pipe is close-on-exec so if exec succeeds, it will be closed.)
    49  // In the child, this function must not acquire any locks, because
    50  // they might have been locked at the time of the fork. This means
    51  // no rescheduling, no malloc calls, and no new stack segments.
    52  // For the same reason compiler does not race instrument it.
    53  // The calls to rawSyscall are okay because they are assembly
    54  // functions that do not grow the stack.
    55  //go:norace
    56  func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err syscall.Errno) {
    57  	// Declare all variables at top in case any
    58  	// declarations require heap allocation (e.g., err1).
    59  	var (
    60  		r1     uintptr
    61  		err1   syscall.Errno
    62  		nextfd int
    63  		i      int
    64  	)
    65  
    66  	// Load rlimit options
    67  	rlimitOptions := GetRlimitOptions(&sys.Rlimit)
    68  
    69  	// guard against side effects of shuffling fds below.
    70  	// Make sure that nextfd is beyond any currently open files so
    71  	// that we can't run the risk of overwriting any of them.
    72  	fd := make([]int, len(attr.Files))
    73  	nextfd = len(attr.Files)
    74  	for i, ufd := range attr.Files {
    75  		if nextfd < int(ufd) {
    76  			nextfd = int(ufd)
    77  		}
    78  		fd[i] = int(ufd)
    79  	}
    80  	nextfd++
    81  
    82  	// About to call fork.
    83  	// No more allocation or calls of non-assembly functions.
    84  	runtime_BeforeFork()
    85  	r1, _, err1 = rawSyscall(funcPC(libc_fork_trampoline), 0, 0, 0)
    86  	if err1 != 0 {
    87  		runtime_AfterFork()
    88  		return 0, err1
    89  	}
    90  
    91  	if r1 != 0 {
    92  		// parent; return PID
    93  		runtime_AfterFork()
    94  		return int(r1), 0
    95  	}
    96  
    97  	// Fork succeeded, now in child.
    98  
    99  	runtime_AfterForkInChild()
   100  
   101  	// Enable tracing if requested.
   102  	if sys.Ptrace {
   103  		if err := ptrace(syscall.PTRACE_TRACEME, 0, 0, 0); err != nil {
   104  			err1 = err.(syscall.Errno)
   105  			goto childerror
   106  		}
   107  	}
   108  
   109  	// Session ID
   110  	if sys.Setsid {
   111  		_, _, err1 = rawSyscall(funcPC(libc_setsid_trampoline), 0, 0, 0)
   112  		if err1 != 0 {
   113  			goto childerror
   114  		}
   115  	}
   116  
   117  	// Set process group
   118  	if sys.Setpgid || sys.Foreground {
   119  		// Place child in process group.
   120  		_, _, err1 = rawSyscall(funcPC(libc_setpgid_trampoline), 0, uintptr(sys.Pgid), 0)
   121  		if err1 != 0 {
   122  			goto childerror
   123  		}
   124  	}
   125  
   126  	if sys.Foreground {
   127  		pgrp := sys.Pgid
   128  		if pgrp == 0 {
   129  			r1, _, err1 = rawSyscall(funcPC(libc_getpid_trampoline), 0, 0, 0)
   130  			if err1 != 0 {
   131  				goto childerror
   132  			}
   133  
   134  			pgrp = int(r1)
   135  		}
   136  
   137  		// Place process group in foreground.
   138  		_, _, err1 = rawSyscall(funcPC(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(syscall.TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
   139  		if err1 != 0 {
   140  			goto childerror
   141  		}
   142  	}
   143  
   144  	// Chroot
   145  	if chroot != nil {
   146  		_, _, err1 = rawSyscall(funcPC(libc_chroot_trampoline), uintptr(unsafe.Pointer(chroot)), 0, 0)
   147  		if err1 != 0 {
   148  			goto childerror
   149  		}
   150  	}
   151  
   152  	// User and groups
   153  	if cred := sys.Credential; cred != nil {
   154  		ngroups := uintptr(len(cred.Groups))
   155  		groups := uintptr(0)
   156  		if ngroups > 0 {
   157  			groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
   158  		}
   159  		if !cred.NoSetGroups {
   160  			_, _, err1 = rawSyscall(funcPC(libc_setgroups_trampoline), ngroups, groups, 0)
   161  			if err1 != 0 {
   162  				goto childerror
   163  			}
   164  		}
   165  		_, _, err1 = rawSyscall(funcPC(libc_setgid_trampoline), uintptr(cred.Gid), 0, 0)
   166  		if err1 != 0 {
   167  			goto childerror
   168  		}
   169  		_, _, err1 = rawSyscall(funcPC(libc_setuid_trampoline), uintptr(cred.Uid), 0, 0)
   170  		if err1 != 0 {
   171  			goto childerror
   172  		}
   173  	}
   174  
   175  	// Chdir
   176  	if dir != nil {
   177  		_, _, err1 = rawSyscall(funcPC(libc_chdir_trampoline), uintptr(unsafe.Pointer(dir)), 0, 0)
   178  		if err1 != 0 {
   179  			goto childerror
   180  		}
   181  	}
   182  
   183  	// Pass 1: look for fd[i] < i and move those up above len(fd)
   184  	// so that pass 2 won't stomp on an fd it needs later.
   185  	if pipe < nextfd {
   186  		_, _, err1 = rawSyscall(funcPC(libc_dup2_trampoline), uintptr(pipe), uintptr(nextfd), 0)
   187  		if err1 != 0 {
   188  			goto childerror
   189  		}
   190  		rawSyscall(funcPC(libc_fcntl_trampoline), uintptr(nextfd), syscall.F_SETFD, syscall.FD_CLOEXEC)
   191  		pipe = nextfd
   192  		nextfd++
   193  	}
   194  	for i = 0; i < len(fd); i++ {
   195  		if fd[i] >= 0 && fd[i] < int(i) {
   196  			if nextfd == pipe { // don't stomp on pipe
   197  				nextfd++
   198  			}
   199  			_, _, err1 = rawSyscall(funcPC(libc_dup2_trampoline), uintptr(fd[i]), uintptr(nextfd), 0)
   200  			if err1 != 0 {
   201  				goto childerror
   202  			}
   203  			rawSyscall(funcPC(libc_fcntl_trampoline), uintptr(nextfd), syscall.F_SETFD, syscall.FD_CLOEXEC)
   204  			fd[i] = nextfd
   205  			nextfd++
   206  		}
   207  	}
   208  
   209  	// Pass 2: dup fd[i] down onto i.
   210  	for i = 0; i < len(fd); i++ {
   211  		if fd[i] == -1 {
   212  			rawSyscall(funcPC(libc_close_trampoline), uintptr(i), 0, 0)
   213  			continue
   214  		}
   215  		if fd[i] == int(i) {
   216  			// dup2(i, i) won't clear close-on-exec flag on Linux,
   217  			// probably not elsewhere either.
   218  			_, _, err1 = rawSyscall(funcPC(libc_fcntl_trampoline), uintptr(fd[i]), syscall.F_SETFD, 0)
   219  			if err1 != 0 {
   220  				goto childerror
   221  			}
   222  			continue
   223  		}
   224  		// The new fd is created NOT close-on-exec,
   225  		// which is exactly what we want.
   226  		_, _, err1 = rawSyscall(funcPC(libc_dup2_trampoline), uintptr(fd[i]), uintptr(i), 0)
   227  		if err1 != 0 {
   228  			goto childerror
   229  		}
   230  	}
   231  
   232  	// By convention, we don't close-on-exec the fds we are
   233  	// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
   234  	// Programs that know they inherit fds >= 3 will need
   235  	// to set them close-on-exec.
   236  	for i = len(fd); i < 3; i++ {
   237  		rawSyscall(funcPC(libc_close_trampoline), uintptr(i), 0, 0)
   238  	}
   239  
   240  	// Detach fd 0 from tty
   241  	if sys.Noctty {
   242  		_, _, err1 = rawSyscall(funcPC(libc_ioctl_trampoline), 0, uintptr(syscall.TIOCNOTTY), 0)
   243  		if err1 != 0 {
   244  			goto childerror
   245  		}
   246  	}
   247  
   248  	// Set the controlling TTY to Ctty
   249  	if sys.Setctty {
   250  		_, _, err1 = rawSyscall(funcPC(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(syscall.TIOCSCTTY), 0)
   251  		if err1 != 0 {
   252  			goto childerror
   253  		}
   254  	}
   255  
   256  	// Set resource limitations
   257  	for _, rlimit := range rlimitOptions.Rlimits {
   258  		if !rlimit.Enable {
   259  			continue
   260  		}
   261  		_, _, err1 = rawSyscall(funcPC(libc_setrlimit_trampoline), uintptr(rlimit.Which), uintptr(unsafe.Pointer(&rlimit.RLim)), 0)
   262  		if err1 != 0 {
   263  			goto childerror
   264  		}
   265  	}
   266  
   267  	// Set real time limitation
   268  	if sys.Rlimit.RealTimeLimit > 0 {
   269  		_, _, err1 = syscall.RawSyscall(syscall.SYS_SETITIMER, ITIMER_REAL, uintptr(unsafe.Pointer(&rlimitOptions.ITimerValue)), 0)
   270  		if err1 != 0 {
   271  			goto childerror
   272  		}
   273  	}
   274  
   275  	// Time to exec.
   276  	_, _, err1 = rawSyscall(funcPC(libc_execve_trampoline),
   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  	rawSyscall(funcPC(libc_write_trampoline), uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
   284  	for {
   285  		rawSyscall(funcPC(libc_exit_trampoline), 253, 0, 0)
   286  	}
   287  }
   288  
   289  // Try to open a pipe with O_CLOEXEC set on both file descriptors.
   290  func forkExecPipe(p []int) error {
   291  	err := syscall.Pipe(p)
   292  	if err != nil {
   293  		return err
   294  	}
   295  	_, err = fcntl(p[0], syscall.F_SETFD, syscall.FD_CLOEXEC)
   296  	if err != nil {
   297  		return err
   298  	}
   299  	_, err = fcntl(p[1], syscall.F_SETFD, syscall.FD_CLOEXEC)
   300  	return err
   301  }