1// Copyright 2019 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// +build libc_exec 6 7package main 8 9// #include <errno.h> 10// #include <stdlib.h> 11// #include <string.h> 12// #include <unistd.h> 13// 14// int libc_exec(const char *pathname, char *const argv[], char *const envp[]) { 15// if (execve(pathname, argv, envp) != 0) { 16// return errno; 17// } 18// return 0; 19//} 20import "C" 21import ( 22 "os/exec" 23 "unsafe" 24) 25 26// Replacement for syscall.Execve that uses the libc. 27// This allows tools that rely on intercepting syscalls via 28// LD_PRELOAD to work properly (e.g. gentoo sandbox). 29// Note that this changes the go binary to be a dynamically linked one. 30// See crbug.com/1000863 for details. 31// To use this version of exec, please add '-tags libc_exec' when building Go binary. 32// Without the tags, libc_exec.go will be used. 33 34func execCmd(env env, cmd *command) error { 35 freeList := []unsafe.Pointer{} 36 defer func() { 37 for _, ptr := range freeList { 38 C.free(ptr) 39 } 40 }() 41 42 goStrToC := func(goStr string) *C.char { 43 cstr := C.CString(goStr) 44 freeList = append(freeList, unsafe.Pointer(cstr)) 45 return cstr 46 } 47 48 goSliceToC := func(goSlice []string) **C.char { 49 // len(goSlice)+1 as the c array needs to be null terminated. 50 cArray := C.malloc(C.size_t(len(goSlice)+1) * C.size_t(unsafe.Sizeof(uintptr(0)))) 51 freeList = append(freeList, cArray) 52 53 // Convert the C array to a Go Array so we can index it. 54 // Note: Storing pointers to the c heap in go pointer types is ok 55 // (see https://golang.org/cmd/cgo/). 56 cArrayForIndex := (*[1<<30 - 1]*C.char)(cArray) 57 for i, str := range goSlice { 58 cArrayForIndex[i] = goStrToC(str) 59 } 60 cArrayForIndex[len(goSlice)] = nil 61 62 return (**C.char)(cArray) 63 } 64 65 execCmd := exec.Command(cmd.Path, cmd.Args...) 66 mergedEnv := mergeEnvValues(env.environ(), cmd.EnvUpdates) 67 if errno := C.libc_exec(goStrToC(execCmd.Path), goSliceToC(execCmd.Args), goSliceToC(mergedEnv)); errno != 0 { 68 return newErrorwithSourceLocf("exec error: %d", errno) 69 } 70 71 return nil 72} 73