...
Run Format

Source file src/syscall/exec_linux_test.go

Documentation: syscall

     1  // Copyright 2015 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 linux
     6  
     7  package syscall_test
     8  
     9  import (
    10  	"flag"
    11  	"fmt"
    12  	"internal/testenv"
    13  	"io"
    14  	"io/ioutil"
    15  	"os"
    16  	"os/exec"
    17  	"os/user"
    18  	"path/filepath"
    19  	"strconv"
    20  	"strings"
    21  	"syscall"
    22  	"testing"
    23  	"unsafe"
    24  )
    25  
    26  func isDocker() bool {
    27  	_, err := os.Stat("/.dockerenv")
    28  	return err == nil
    29  }
    30  
    31  func isLXC() bool {
    32  	return os.Getenv("container") == "lxc"
    33  }
    34  
    35  func skipInContainer(t *testing.T) {
    36  	if isDocker() {
    37  		t.Skip("skip this test in Docker container")
    38  	}
    39  	if isLXC() {
    40  		t.Skip("skip this test in LXC container")
    41  	}
    42  }
    43  
    44  // Check if we are in a chroot by checking if the inode of / is
    45  // different from 2 (there is no better test available to non-root on
    46  // linux).
    47  func isChrooted(t *testing.T) bool {
    48  	root, err := os.Stat("/")
    49  	if err != nil {
    50  		t.Fatalf("cannot stat /: %v", err)
    51  	}
    52  	return root.Sys().(*syscall.Stat_t).Ino != 2
    53  }
    54  
    55  func checkUserNS(t *testing.T) {
    56  	skipInContainer(t)
    57  	if _, err := os.Stat("/proc/self/ns/user"); err != nil {
    58  		if os.IsNotExist(err) {
    59  			t.Skip("kernel doesn't support user namespaces")
    60  		}
    61  		if os.IsPermission(err) {
    62  			t.Skip("unable to test user namespaces due to permissions")
    63  		}
    64  		t.Fatalf("Failed to stat /proc/self/ns/user: %v", err)
    65  	}
    66  	if isChrooted(t) {
    67  		// create_user_ns in the kernel (see
    68  		// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/user_namespace.c)
    69  		// forbids the creation of user namespaces when chrooted.
    70  		t.Skip("cannot create user namespaces when chrooted")
    71  	}
    72  	// On some systems, there is a sysctl setting.
    73  	if os.Getuid() != 0 {
    74  		data, errRead := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
    75  		if errRead == nil && data[0] == '0' {
    76  			t.Skip("kernel prohibits user namespace in unprivileged process")
    77  		}
    78  	}
    79  	// On Centos 7 make sure they set the kernel parameter user_namespace=1
    80  	// See issue 16283 and 20796.
    81  	if _, err := os.Stat("/sys/module/user_namespace/parameters/enable"); err == nil {
    82  		buf, _ := ioutil.ReadFile("/sys/module/user_namespace/parameters/enabled")
    83  		if !strings.HasPrefix(string(buf), "Y") {
    84  			t.Skip("kernel doesn't support user namespaces")
    85  		}
    86  	}
    87  	// When running under the Go continuous build, skip tests for
    88  	// now when under Kubernetes. (where things are root but not quite)
    89  	// Both of these are our own environment variables.
    90  	// See Issue 12815.
    91  	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
    92  		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
    93  	}
    94  }
    95  
    96  func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd {
    97  	checkUserNS(t)
    98  	cmd := exec.Command("whoami")
    99  	cmd.SysProcAttr = &syscall.SysProcAttr{
   100  		Cloneflags: syscall.CLONE_NEWUSER,
   101  		UidMappings: []syscall.SysProcIDMap{
   102  			{ContainerID: 0, HostID: uid, Size: 1},
   103  		},
   104  		GidMappings: []syscall.SysProcIDMap{
   105  			{ContainerID: 0, HostID: gid, Size: 1},
   106  		},
   107  		GidMappingsEnableSetgroups: setgroups,
   108  	}
   109  	return cmd
   110  }
   111  
   112  func testNEWUSERRemap(t *testing.T, uid, gid int, setgroups bool) {
   113  	cmd := whoamiCmd(t, uid, gid, setgroups)
   114  	out, err := cmd.CombinedOutput()
   115  	if err != nil {
   116  		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
   117  	}
   118  	sout := strings.TrimSpace(string(out))
   119  	want := "root"
   120  	if sout != want {
   121  		t.Fatalf("whoami = %q; want %q", out, want)
   122  	}
   123  }
   124  
   125  func TestCloneNEWUSERAndRemapRootDisableSetgroups(t *testing.T) {
   126  	if os.Getuid() != 0 {
   127  		t.Skip("skipping root only test")
   128  	}
   129  	testNEWUSERRemap(t, 0, 0, false)
   130  }
   131  
   132  func TestCloneNEWUSERAndRemapRootEnableSetgroups(t *testing.T) {
   133  	if os.Getuid() != 0 {
   134  		t.Skip("skipping root only test")
   135  	}
   136  	testNEWUSERRemap(t, 0, 0, true)
   137  }
   138  
   139  func TestCloneNEWUSERAndRemapNoRootDisableSetgroups(t *testing.T) {
   140  	if os.Getuid() == 0 {
   141  		t.Skip("skipping unprivileged user only test")
   142  	}
   143  	testNEWUSERRemap(t, os.Getuid(), os.Getgid(), false)
   144  }
   145  
   146  func TestCloneNEWUSERAndRemapNoRootSetgroupsEnableSetgroups(t *testing.T) {
   147  	if os.Getuid() == 0 {
   148  		t.Skip("skipping unprivileged user only test")
   149  	}
   150  	cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), true)
   151  	err := cmd.Run()
   152  	if err == nil {
   153  		t.Skip("probably old kernel without security fix")
   154  	}
   155  	if !os.IsPermission(err) {
   156  		t.Fatalf("Unprivileged gid_map rewriting with GidMappingsEnableSetgroups must fail")
   157  	}
   158  }
   159  
   160  func TestEmptyCredGroupsDisableSetgroups(t *testing.T) {
   161  	cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), false)
   162  	cmd.SysProcAttr.Credential = &syscall.Credential{}
   163  	if err := cmd.Run(); err != nil {
   164  		t.Fatal(err)
   165  	}
   166  }
   167  
   168  func TestUnshare(t *testing.T) {
   169  	skipInContainer(t)
   170  	// Make sure we are running as root so we have permissions to use unshare
   171  	// and create a network namespace.
   172  	if os.Getuid() != 0 {
   173  		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
   174  	}
   175  
   176  	// When running under the Go continuous build, skip tests for
   177  	// now when under Kubernetes. (where things are root but not quite)
   178  	// Both of these are our own environment variables.
   179  	// See Issue 12815.
   180  	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
   181  		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
   182  	}
   183  
   184  	path := "/proc/net/dev"
   185  	if _, err := os.Stat(path); err != nil {
   186  		if os.IsNotExist(err) {
   187  			t.Skip("kernel doesn't support proc filesystem")
   188  		}
   189  		if os.IsPermission(err) {
   190  			t.Skip("unable to test proc filesystem due to permissions")
   191  		}
   192  		t.Fatal(err)
   193  	}
   194  	if _, err := os.Stat("/proc/self/ns/net"); err != nil {
   195  		if os.IsNotExist(err) {
   196  			t.Skip("kernel doesn't support net namespace")
   197  		}
   198  		t.Fatal(err)
   199  	}
   200  
   201  	orig, err := ioutil.ReadFile(path)
   202  	if err != nil {
   203  		t.Fatal(err)
   204  	}
   205  	origLines := strings.Split(strings.TrimSpace(string(orig)), "\n")
   206  
   207  	cmd := exec.Command("cat", path)
   208  	cmd.SysProcAttr = &syscall.SysProcAttr{
   209  		Unshareflags: syscall.CLONE_NEWNET,
   210  	}
   211  	out, err := cmd.CombinedOutput()
   212  	if err != nil {
   213  		if strings.Contains(err.Error(), "operation not permitted") {
   214  			// Issue 17206: despite all the checks above,
   215  			// this still reportedly fails for some users.
   216  			// (older kernels?). Just skip.
   217  			t.Skip("skipping due to permission error")
   218  		}
   219  		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
   220  	}
   221  
   222  	// Check there is only the local network interface
   223  	sout := strings.TrimSpace(string(out))
   224  	if !strings.Contains(sout, "lo:") {
   225  		t.Fatalf("Expected lo network interface to exist, got %s", sout)
   226  	}
   227  
   228  	lines := strings.Split(sout, "\n")
   229  	if len(lines) >= len(origLines) {
   230  		t.Fatalf("Got %d lines of output, want <%d", len(lines), len(origLines))
   231  	}
   232  }
   233  
   234  func TestGroupCleanup(t *testing.T) {
   235  	if os.Getuid() != 0 {
   236  		t.Skip("we need root for credential")
   237  	}
   238  	cmd := exec.Command("id")
   239  	cmd.SysProcAttr = &syscall.SysProcAttr{
   240  		Credential: &syscall.Credential{
   241  			Uid: 0,
   242  			Gid: 0,
   243  		},
   244  	}
   245  	out, err := cmd.CombinedOutput()
   246  	if err != nil {
   247  		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
   248  	}
   249  	strOut := strings.TrimSpace(string(out))
   250  	expected := "uid=0(root) gid=0(root)"
   251  	// Just check prefix because some distros reportedly output a
   252  	// context parameter; see https://golang.org/issue/16224.
   253  	// Alpine does not output groups; see https://golang.org/issue/19938.
   254  	if !strings.HasPrefix(strOut, expected) {
   255  		t.Errorf("id command output: %q, expected prefix: %q", strOut, expected)
   256  	}
   257  }
   258  
   259  func TestGroupCleanupUserNamespace(t *testing.T) {
   260  	if os.Getuid() != 0 {
   261  		t.Skip("we need root for credential")
   262  	}
   263  	checkUserNS(t)
   264  	cmd := exec.Command("id")
   265  	uid, gid := os.Getuid(), os.Getgid()
   266  	cmd.SysProcAttr = &syscall.SysProcAttr{
   267  		Cloneflags: syscall.CLONE_NEWUSER,
   268  		Credential: &syscall.Credential{
   269  			Uid: uint32(uid),
   270  			Gid: uint32(gid),
   271  		},
   272  		UidMappings: []syscall.SysProcIDMap{
   273  			{ContainerID: 0, HostID: uid, Size: 1},
   274  		},
   275  		GidMappings: []syscall.SysProcIDMap{
   276  			{ContainerID: 0, HostID: gid, Size: 1},
   277  		},
   278  	}
   279  	out, err := cmd.CombinedOutput()
   280  	if err != nil {
   281  		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
   282  	}
   283  	strOut := strings.TrimSpace(string(out))
   284  
   285  	// Strings we've seen in the wild.
   286  	expected := []string{
   287  		"uid=0(root) gid=0(root) groups=0(root)",
   288  		"uid=0(root) gid=0(root) groups=0(root),65534(nobody)",
   289  		"uid=0(root) gid=0(root) groups=0(root),65534(nogroup)",
   290  		"uid=0(root) gid=0(root) groups=0(root),65534",
   291  		"uid=0(root) gid=0(root) groups=0(root),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody)", // Alpine; see https://golang.org/issue/19938
   292  	}
   293  	for _, e := range expected {
   294  		if strOut == e {
   295  			return
   296  		}
   297  	}
   298  	t.Errorf("id command output: %q, expected one of %q", strOut, expected)
   299  }
   300  
   301  // TestUnshareHelperProcess isn't a real test. It's used as a helper process
   302  // for TestUnshareMountNameSpace.
   303  func TestUnshareMountNameSpaceHelper(*testing.T) {
   304  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   305  		return
   306  	}
   307  	defer os.Exit(0)
   308  	if err := syscall.Mount("none", flag.Args()[0], "proc", 0, ""); err != nil {
   309  		fmt.Fprintf(os.Stderr, "unshare: mount %v failed: %v", os.Args, err)
   310  		os.Exit(2)
   311  	}
   312  }
   313  
   314  // Test for Issue 38471: unshare fails because systemd has forced / to be shared
   315  func TestUnshareMountNameSpace(t *testing.T) {
   316  	skipInContainer(t)
   317  	// Make sure we are running as root so we have permissions to use unshare
   318  	// and create a network namespace.
   319  	if os.Getuid() != 0 {
   320  		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
   321  	}
   322  
   323  	// When running under the Go continuous build, skip tests for
   324  	// now when under Kubernetes. (where things are root but not quite)
   325  	// Both of these are our own environment variables.
   326  	// See Issue 12815.
   327  	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
   328  		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
   329  	}
   330  
   331  	d, err := ioutil.TempDir("", "unshare")
   332  	if err != nil {
   333  		t.Fatalf("tempdir: %v", err)
   334  	}
   335  
   336  	cmd := exec.Command(os.Args[0], "-test.run=TestUnshareMountNameSpaceHelper", d)
   337  	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
   338  	cmd.SysProcAttr = &syscall.SysProcAttr{Unshareflags: syscall.CLONE_NEWNS}
   339  
   340  	o, err := cmd.CombinedOutput()
   341  	if err != nil {
   342  		if strings.Contains(err.Error(), ": permission denied") {
   343  			t.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o, err)
   344  		}
   345  		t.Fatalf("unshare failed: %s, %v", o, err)
   346  	}
   347  
   348  	// How do we tell if the namespace was really unshared? It turns out
   349  	// to be simple: just try to remove the directory. If it's still mounted
   350  	// on the rm will fail with EBUSY. Then we have some cleanup to do:
   351  	// we must unmount it, then try to remove it again.
   352  
   353  	if err := os.Remove(d); err != nil {
   354  		t.Errorf("rmdir failed on %v: %v", d, err)
   355  		if err := syscall.Unmount(d, syscall.MNT_FORCE); err != nil {
   356  			t.Errorf("Can't unmount %v: %v", d, err)
   357  		}
   358  		if err := os.Remove(d); err != nil {
   359  			t.Errorf("rmdir after unmount failed on %v: %v", d, err)
   360  		}
   361  	}
   362  }
   363  
   364  // Test for Issue 20103: unshare fails when chroot is used
   365  func TestUnshareMountNameSpaceChroot(t *testing.T) {
   366  	skipInContainer(t)
   367  	// Make sure we are running as root so we have permissions to use unshare
   368  	// and create a network namespace.
   369  	if os.Getuid() != 0 {
   370  		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
   371  	}
   372  
   373  	// When running under the Go continuous build, skip tests for
   374  	// now when under Kubernetes. (where things are root but not quite)
   375  	// Both of these are our own environment variables.
   376  	// See Issue 12815.
   377  	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
   378  		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
   379  	}
   380  
   381  	d, err := ioutil.TempDir("", "unshare")
   382  	if err != nil {
   383  		t.Fatalf("tempdir: %v", err)
   384  	}
   385  
   386  	// Since we are doing a chroot, we need the binary there,
   387  	// and it must be statically linked.
   388  	x := filepath.Join(d, "syscall.test")
   389  	cmd := exec.Command(testenv.GoToolPath(t), "test", "-c", "-o", x, "syscall")
   390  	cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
   391  	if o, err := cmd.CombinedOutput(); err != nil {
   392  		t.Fatalf("Build of syscall in chroot failed, output %v, err %v", o, err)
   393  	}
   394  
   395  	cmd = exec.Command("/syscall.test", "-test.run=TestUnshareMountNameSpaceHelper", "/")
   396  	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
   397  	cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: d, Unshareflags: syscall.CLONE_NEWNS}
   398  
   399  	o, err := cmd.CombinedOutput()
   400  	if err != nil {
   401  		if strings.Contains(err.Error(), ": permission denied") {
   402  			t.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o, err)
   403  		}
   404  		t.Fatalf("unshare failed: %s, %v", o, err)
   405  	}
   406  
   407  	// How do we tell if the namespace was really unshared? It turns out
   408  	// to be simple: just try to remove the executable. If it's still mounted
   409  	// on, the rm will fail. Then we have some cleanup to do:
   410  	// we must force unmount it, then try to remove it again.
   411  
   412  	if err := os.Remove(x); err != nil {
   413  		t.Errorf("rm failed on %v: %v", x, err)
   414  		if err := syscall.Unmount(d, syscall.MNT_FORCE); err != nil {
   415  			t.Fatalf("Can't unmount %v: %v", d, err)
   416  		}
   417  		if err := os.Remove(x); err != nil {
   418  			t.Fatalf("rm failed on %v: %v", x, err)
   419  		}
   420  	}
   421  
   422  	if err := os.Remove(d); err != nil {
   423  		t.Errorf("rmdir failed on %v: %v", d, err)
   424  	}
   425  }
   426  
   427  type capHeader struct {
   428  	version uint32
   429  	pid     int
   430  }
   431  
   432  type capData struct {
   433  	effective   uint32
   434  	permitted   uint32
   435  	inheritable uint32
   436  }
   437  
   438  const CAP_SYS_TIME = 25
   439  
   440  type caps struct {
   441  	hdr  capHeader
   442  	data [2]capData
   443  }
   444  
   445  func getCaps() (caps, error) {
   446  	var c caps
   447  
   448  	// Get capability version
   449  	if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(nil)), 0); errno != 0 {
   450  		return c, fmt.Errorf("SYS_CAPGET: %v", errno)
   451  	}
   452  
   453  	// Get current capabilities
   454  	if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(&c.data[0])), 0); errno != 0 {
   455  		return c, fmt.Errorf("SYS_CAPGET: %v", errno)
   456  	}
   457  
   458  	return c, nil
   459  }
   460  
   461  func mustSupportAmbientCaps(t *testing.T) {
   462  	var uname syscall.Utsname
   463  	if err := syscall.Uname(&uname); err != nil {
   464  		t.Fatalf("Uname: %v", err)
   465  	}
   466  	var buf [65]byte
   467  	for i, b := range uname.Release {
   468  		buf[i] = byte(b)
   469  	}
   470  	ver := string(buf[:])
   471  	if i := strings.Index(ver, "\x00"); i != -1 {
   472  		ver = ver[:i]
   473  	}
   474  	if strings.HasPrefix(ver, "2.") ||
   475  		strings.HasPrefix(ver, "3.") ||
   476  		strings.HasPrefix(ver, "4.1.") ||
   477  		strings.HasPrefix(ver, "4.2.") {
   478  		t.Skipf("kernel version %q predates required 4.3; skipping test", ver)
   479  	}
   480  }
   481  
   482  // TestAmbientCapsHelper isn't a real test. It's used as a helper process for
   483  // TestAmbientCaps.
   484  func TestAmbientCapsHelper(*testing.T) {
   485  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   486  		return
   487  	}
   488  	defer os.Exit(0)
   489  
   490  	caps, err := getCaps()
   491  	if err != nil {
   492  		fmt.Fprintln(os.Stderr, err)
   493  		os.Exit(2)
   494  	}
   495  	if caps.data[0].effective&(1<<uint(CAP_SYS_TIME)) == 0 {
   496  		fmt.Fprintln(os.Stderr, "CAP_SYS_TIME unexpectedly not in the effective capability mask")
   497  		os.Exit(2)
   498  	}
   499  }
   500  
   501  func TestAmbientCaps(t *testing.T) {
   502  	skipInContainer(t)
   503  	// Make sure we are running as root so we have permissions to use unshare
   504  	// and create a network namespace.
   505  	if os.Getuid() != 0 {
   506  		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
   507  	}
   508  	mustSupportAmbientCaps(t)
   509  
   510  	// When running under the Go continuous build, skip tests for
   511  	// now when under Kubernetes. (where things are root but not quite)
   512  	// Both of these are our own environment variables.
   513  	// See Issue 12815.
   514  	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
   515  		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
   516  	}
   517  
   518  	caps, err := getCaps()
   519  	if err != nil {
   520  		t.Fatal(err)
   521  	}
   522  
   523  	// Add CAP_SYS_TIME to the permitted and inheritable capability mask,
   524  	// otherwise we will not be able to add it to the ambient capability mask.
   525  	caps.data[0].permitted |= 1 << uint(CAP_SYS_TIME)
   526  	caps.data[0].inheritable |= 1 << uint(CAP_SYS_TIME)
   527  
   528  	if _, _, errno := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); errno != 0 {
   529  		t.Fatalf("SYS_CAPSET: %v", errno)
   530  	}
   531  
   532  	u, err := user.Lookup("nobody")
   533  	if err != nil {
   534  		t.Fatal(err)
   535  	}
   536  	uid, err := strconv.ParseInt(u.Uid, 0, 32)
   537  	if err != nil {
   538  		t.Fatal(err)
   539  	}
   540  	gid, err := strconv.ParseInt(u.Gid, 0, 32)
   541  	if err != nil {
   542  		t.Fatal(err)
   543  	}
   544  
   545  	// Copy the test binary to a temporary location which is readable by nobody.
   546  	f, err := ioutil.TempFile("", "gotest")
   547  	if err != nil {
   548  		t.Fatal(err)
   549  	}
   550  	defer os.Remove(f.Name())
   551  	defer f.Close()
   552  	e, err := os.Open(os.Args[0])
   553  	if err != nil {
   554  		t.Fatal(err)
   555  	}
   556  	defer e.Close()
   557  	if _, err := io.Copy(f, e); err != nil {
   558  		t.Fatal(err)
   559  	}
   560  	if err := f.Chmod(0755); err != nil {
   561  		t.Fatal(err)
   562  	}
   563  	if err := f.Close(); err != nil {
   564  		t.Fatal(err)
   565  	}
   566  
   567  	cmd := exec.Command(f.Name(), "-test.run=TestAmbientCapsHelper")
   568  	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
   569  	cmd.Stdout = os.Stdout
   570  	cmd.Stderr = os.Stderr
   571  	cmd.SysProcAttr = &syscall.SysProcAttr{
   572  		Credential: &syscall.Credential{
   573  			Uid: uint32(uid),
   574  			Gid: uint32(gid),
   575  		},
   576  		AmbientCaps: []uintptr{CAP_SYS_TIME},
   577  	}
   578  	if err := cmd.Run(); err != nil {
   579  		t.Fatal(err.Error())
   580  	}
   581  }
   582  

View as plain text