Home | History | Annotate | Download | only in 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