github.com/criyle/go-sandbox@v0.10.3/pkg/forkexec/fork_child_darwin.go (about)

     1  package forkexec
     2  
     3  import (
     4  	"syscall"
     5  	"unsafe"
     6  )
     7  
     8  // Reference to src/syscall/exec_darwin.go
     9  //go:norace
    10  func forkAndExecInChild(r *Runner, argv0 *byte, argv, env []*byte, workdir, profile *byte, p [2]int) (r1 uintptr, err1 syscall.Errno) {
    11  	var (
    12  		err2   syscall.Errno
    13  		errBuf *byte
    14  	)
    15  
    16  	// similar to exec_linux, avoid side effect by shuffling around
    17  	fd, nextfd := prepareFds(r.Files)
    18  	pipe := p[1]
    19  
    20  	// About to call fork.
    21  	// No more allocation or calls of non-assembly functions.
    22  	beforeFork()
    23  
    24  	// UnshareFlags (new namespaces) is activated by clone syscall
    25  	r1, _, err1 = rawSyscall(libc_fork_trampoline_addr, 0, 0, 0)
    26  	if err1 != 0 || r1 != 0 {
    27  		// in parent process, immediate return
    28  		return
    29  	}
    30  
    31  	// In child process
    32  	afterForkInChild()
    33  	// Notice: cannot call any GO functions beyond this point
    34  
    35  	// Close write end of pipe
    36  	if _, _, err1 = rawSyscall(libc_close_trampoline_addr, uintptr(p[0]), 0, 0); err1 != 0 {
    37  		goto childerror
    38  	}
    39  
    40  	// Set pg id
    41  	_, _, err1 = rawSyscall(libc_setpgid_trampoline_addr, 0, 0, 0)
    42  	if err1 != 0 {
    43  		goto childerror
    44  	}
    45  
    46  	// Pass 1 & pass 2 assigns fds for child process
    47  	// Pass 1: fd[i] < i => nextfd
    48  	if pipe < nextfd {
    49  		_, _, err1 = rawSyscall(libc_dup2_trampoline_addr, uintptr(pipe), uintptr(nextfd), 0)
    50  		if err1 != 0 {
    51  			goto childerror
    52  		}
    53  		rawSyscall(libc_fcntl_trampoline_addr, uintptr(nextfd), syscall.F_SETFD, syscall.FD_CLOEXEC)
    54  		pipe = nextfd
    55  		nextfd++
    56  	}
    57  	for i := 0; i < len(fd); i++ {
    58  		if fd[i] >= 0 && fd[i] < int(i) {
    59  			// Avoid fd rewrite
    60  			if nextfd == pipe {
    61  				nextfd++
    62  			}
    63  			_, _, err1 = rawSyscall(libc_dup2_trampoline_addr, uintptr(fd[i]), uintptr(nextfd), 0)
    64  			if err1 != 0 {
    65  				goto childerror
    66  			}
    67  			rawSyscall(libc_fcntl_trampoline_addr, uintptr(nextfd), syscall.F_SETFD, syscall.FD_CLOEXEC)
    68  			// Set up close on exec
    69  			fd[i] = nextfd
    70  			nextfd++
    71  		}
    72  	}
    73  	// Pass 2: fd[i] => i
    74  	for i := 0; i < len(fd); i++ {
    75  		if fd[i] == -1 {
    76  			rawSyscall(libc_close_trampoline_addr, uintptr(i), 0, 0)
    77  			continue
    78  		}
    79  		if fd[i] == int(i) {
    80  			// dup2(i, i) will not clear close on exec flag, need to reset the flag
    81  			_, _, err1 = rawSyscall(libc_fcntl_trampoline_addr, uintptr(fd[i]), syscall.F_SETFD, 0)
    82  			if err1 != 0 {
    83  				goto childerror
    84  			}
    85  			continue
    86  		}
    87  		_, _, err1 = rawSyscall(libc_dup2_trampoline_addr, uintptr(fd[i]), uintptr(i), 0)
    88  		if err1 != 0 {
    89  			goto childerror
    90  		}
    91  	}
    92  
    93  	// chdir for child
    94  	if workdir != nil {
    95  		_, _, err1 = rawSyscall(libc_chdir_trampoline_addr, uintptr(unsafe.Pointer(workdir)), 0, 0)
    96  		if err1 != 0 {
    97  			goto childerror
    98  		}
    99  	}
   100  
   101  	// Set limit
   102  	for _, rlim := range r.RLimits {
   103  		_, _, err1 = rawSyscall(libc_setrlimit_trampoline_addr, uintptr(rlim.Res), uintptr(unsafe.Pointer(&rlim.Rlim)), 0)
   104  		if err1 != 0 {
   105  			if err1 == syscall.EINVAL && (rlim.Res == syscall.RLIMIT_DATA || rlim.Res == syscall.RLIMIT_AS) {
   106  				continue
   107  			}
   108  			goto childerror
   109  		}
   110  	}
   111  
   112  	// Load sandbox profile
   113  	if profile != nil {
   114  		r1, _, err1 = rawSyscall(libc_sandbox_init_trampoline_addr, uintptr(unsafe.Pointer(profile)), 0, uintptr(unsafe.Pointer(&errBuf)))
   115  		if err1 != 0 {
   116  			goto childerror
   117  		}
   118  		if r1 != 0 {
   119  			err1 = 253
   120  			goto childerror
   121  		}
   122  		rawSyscall(libc_sandbox_free_error_trampoline_addr, uintptr(unsafe.Pointer(errBuf)), 0, 0)
   123  	}
   124  
   125  	// Sync before exec
   126  	err2 = 0
   127  	r1, _, err1 = rawSyscall(libc_write_trampoline_addr, uintptr(pipe), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
   128  	if r1 == 0 || err1 != 0 {
   129  		goto childerror
   130  	}
   131  
   132  	r1, _, err1 = rawSyscall(libc_read_trampoline_addr, uintptr(pipe), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
   133  	if r1 == 0 || err1 != 0 {
   134  		goto childerror
   135  	}
   136  
   137  	// Time to exec.
   138  	_, _, err1 = rawSyscall(libc_execve_trampoline_addr,
   139  		uintptr(unsafe.Pointer(argv0)),
   140  		uintptr(unsafe.Pointer(&argv[0])),
   141  		uintptr(unsafe.Pointer(&env[0])))
   142  
   143  childerror:
   144  	// send error code on pipe
   145  	rawSyscall(libc_write_trampoline_addr, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
   146  	for {
   147  		rawSyscall(libc_exit_trampoline_addr, uintptr(err1+err2), 0, 0)
   148  	}
   149  }