1*760c253cSXin Li// Copyright 2019 The ChromiumOS Authors 2*760c253cSXin Li// Use of this source code is governed by a BSD-style license that can be 3*760c253cSXin Li// found in the LICENSE file. 4*760c253cSXin Li 5*760c253cSXin Li//go:build libc_exec 6*760c253cSXin Li// +build libc_exec 7*760c253cSXin Li 8*760c253cSXin Lipackage main 9*760c253cSXin Li 10*760c253cSXin Li// #include <errno.h> 11*760c253cSXin Li// #include <stdio.h> 12*760c253cSXin Li// #include <stdlib.h> 13*760c253cSXin Li// #include <string.h> 14*760c253cSXin Li// #include <unistd.h> 15*760c253cSXin Li// #include <sys/types.h> 16*760c253cSXin Li// #include <sys/wait.h> 17*760c253cSXin Li// 18*760c253cSXin Li// int libc_exec(const char *pathname, char *const argv[], char *const envp[]) { 19*760c253cSXin Li// // Since fork() brings us to one thread, we can only use async-signal-safe funcs below. 20*760c253cSXin Li// pid_t pid = fork(); 21*760c253cSXin Li// if (pid == 0) { 22*760c253cSXin Li// // crbug.com/1166017: we're (very rarely) getting ERESTARTSYS on some builders. 23*760c253cSXin Li// // Documentation indicates that this is a bug in the kernel. Work around it by 24*760c253cSXin Li// // retrying. 25 is an arbitrary retry number that Should Be Enough For Anyone(TM). 25*760c253cSXin Li// int i = 0; 26*760c253cSXin Li// for (; i < 25; i++) { 27*760c253cSXin Li// execve(pathname, argv, envp); 28*760c253cSXin Li// if (errno != 512) { 29*760c253cSXin Li// break; 30*760c253cSXin Li// } 31*760c253cSXin Li// // Sleep a bit. Not sure if this helps, but if the condition we're seeing is 32*760c253cSXin Li// // transient, it *hopefully* should. nanosleep isn't async-signal safe, so 33*760c253cSXin Li// // we have to live with sleep() 34*760c253cSXin Li// sleep(1); 35*760c253cSXin Li// } 36*760c253cSXin Li// fprintf(stderr, "exec failed (errno: %d)\n", errno); 37*760c253cSXin Li// _exit(1); 38*760c253cSXin Li// } 39*760c253cSXin Li// if (pid < 0) { 40*760c253cSXin Li// return errno; 41*760c253cSXin Li// } 42*760c253cSXin Li// 43*760c253cSXin Li// int wstatus; 44*760c253cSXin Li// pid_t waited = waitpid(pid, &wstatus, 0); 45*760c253cSXin Li// if (waited == -1) { 46*760c253cSXin Li// return errno; 47*760c253cSXin Li// } 48*760c253cSXin Li// exit(WEXITSTATUS(wstatus)); 49*760c253cSXin Li//} 50*760c253cSXin Liimport "C" 51*760c253cSXin Liimport ( 52*760c253cSXin Li "os/exec" 53*760c253cSXin Li "unsafe" 54*760c253cSXin Li) 55*760c253cSXin Li 56*760c253cSXin Li// Replacement for syscall.Execve that uses the libc. 57*760c253cSXin Li// This allows tools that rely on intercepting syscalls via 58*760c253cSXin Li// LD_PRELOAD to work properly (e.g. gentoo sandbox). 59*760c253cSXin Li// Note that this changes the go binary to be a dynamically linked one. 60*760c253cSXin Li// See crbug.com/1000863 for details. 61*760c253cSXin Li// To use this version of exec, please add '-tags libc_exec' when building Go binary. 62*760c253cSXin Li// Without the tags, libc_exec.go will not be used. 63*760c253cSXin Li 64*760c253cSXin Lifunc execCmd(env env, cmd *command) error { 65*760c253cSXin Li freeList := []unsafe.Pointer{} 66*760c253cSXin Li defer func() { 67*760c253cSXin Li for _, ptr := range freeList { 68*760c253cSXin Li C.free(ptr) 69*760c253cSXin Li } 70*760c253cSXin Li }() 71*760c253cSXin Li 72*760c253cSXin Li goStrToC := func(goStr string) *C.char { 73*760c253cSXin Li cstr := C.CString(goStr) 74*760c253cSXin Li freeList = append(freeList, unsafe.Pointer(cstr)) 75*760c253cSXin Li return cstr 76*760c253cSXin Li } 77*760c253cSXin Li 78*760c253cSXin Li goSliceToC := func(goSlice []string) **C.char { 79*760c253cSXin Li // len(goSlice)+1 as the c array needs to be null terminated. 80*760c253cSXin Li cArray := C.malloc(C.size_t(len(goSlice)+1) * C.size_t(unsafe.Sizeof(uintptr(0)))) 81*760c253cSXin Li freeList = append(freeList, cArray) 82*760c253cSXin Li 83*760c253cSXin Li // Convert the C array to a Go Array so we can index it. 84*760c253cSXin Li // Note: Storing pointers to the c heap in go pointer types is ok 85*760c253cSXin Li // (see https://golang.org/cmd/cgo/). 86*760c253cSXin Li cArrayForIndex := (*[1<<30 - 1]*C.char)(cArray) 87*760c253cSXin Li for i, str := range goSlice { 88*760c253cSXin Li cArrayForIndex[i] = goStrToC(str) 89*760c253cSXin Li } 90*760c253cSXin Li cArrayForIndex[len(goSlice)] = nil 91*760c253cSXin Li 92*760c253cSXin Li return (**C.char)(cArray) 93*760c253cSXin Li } 94*760c253cSXin Li 95*760c253cSXin Li execCmd := exec.Command(cmd.Path, cmd.Args...) 96*760c253cSXin Li mergedEnv := mergeEnvValues(env.environ(), cmd.EnvUpdates) 97*760c253cSXin Li if errno := C.libc_exec(goStrToC(execCmd.Path), goSliceToC(execCmd.Args), goSliceToC(mergedEnv)); errno != 0 { 98*760c253cSXin Li return newErrorwithSourceLocf("exec error: %d", errno) 99*760c253cSXin Li } 100*760c253cSXin Li 101*760c253cSXin Li return nil 102*760c253cSXin Li} 103