From d1b1145cace8b968307f9311ff611e4bb810710c Mon Sep 17 00:00:00 2001 From: "Andrew G. Morgan" Date: Mon, 9 Dec 2019 21:50:16 -0800 Subject: syscall: support POSIX semantics for Linux syscalls This change adds two new methods for invoking system calls under Linux: syscall.AllThreadsSyscall() and syscall.AllThreadsSyscall6(). These system call wrappers ensure that all OSThreads mirror a common system call. The wrappers serialize execution of the runtime to ensure no race conditions where any Go code observes a non-atomic OS state change. As such, the syscalls have higher runtime overhead than regular system calls, and only need to be used where such thread (or 'm' in the parlance of the runtime sources) consistency is required. The new support is used to enable these functions under Linux: syscall.Setegid(), syscall.Seteuid(), syscall.Setgroups(), syscall.Setgid(), syscall.Setregid(), syscall.Setreuid(), syscall.Setresgid(), syscall.Setresuid() and syscall.Setuid(). They work identically to their glibc counterparts. Extensive discussion of the background issue addressed in this patch can be found here: https://github.com/golang/go/issues/1435 In the case where cgo is used, the C runtime can launch pthreads that are not managed by the Go runtime. As such, the added syscall.AllThreadsSyscall*() return ENOTSUP when cgo is enabled. However, for the 9 syscall.Set*() functions listed above, when cgo is active, these functions redirect to invoke their C.set*() equivalents in glibc, which wraps the raw system calls with a nptl:setxid fixup mechanism. This achieves POSIX semantics for these functions in the combined Go and C runtime. As a side note, the glibc/nptl:setxid support (2019-11-30) does not extend to all security related system calls under Linux so using native Go (CGO_ENABLED=0) and these AllThreadsSyscall*()s, where needed, will yield more well defined/consistent behavior over all threads of a Go program. That is, using the syscall.AllThreadsSyscall*() wrappers for things like setting state through SYS_PRCTL and SYS_CAPSET etc. Fixes #1435 Change-Id: Ib1a3e16b9180f64223196a32fc0f9dce14d9105c Reviewed-on: https://go-review.googlesource.com/c/go/+/210639 Trust: Emmanuel Odeke Trust: Ian Lance Taylor Trust: Michael Pratt Run-TryBot: Emmanuel Odeke Reviewed-by: Michael Pratt Reviewed-by: Austin Clements --- src/syscall/syscall_linux.go | 247 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 237 insertions(+), 10 deletions(-) (limited to 'src/syscall/syscall_linux.go') diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go index 07fe6a6c2b..54e5cfc2f2 100644 --- a/src/syscall/syscall_linux.go +++ b/src/syscall/syscall_linux.go @@ -271,16 +271,37 @@ func Getgroups() (gids []int, err error) { return } +var cgo_libc_setgroups unsafe.Pointer // non-nil if cgo linked. + func Setgroups(gids []int) (err error) { - if len(gids) == 0 { - return setgroups(0, nil) + n := uintptr(len(gids)) + if n == 0 { + if cgo_libc_setgroups == nil { + if _, _, e1 := AllThreadsSyscall(_SYS_setgroups, 0, 0, 0); e1 != 0 { + err = errnoErr(e1) + } + return + } + if ret := cgocaller(cgo_libc_setgroups, 0, 0); ret != 0 { + err = errnoErr(Errno(ret)) + } + return } a := make([]_Gid_t, len(gids)) for i, v := range gids { a[i] = _Gid_t(v) } - return setgroups(len(a), &a[0]) + if cgo_libc_setgroups == nil { + if _, _, e1 := AllThreadsSyscall(_SYS_setgroups, n, uintptr(unsafe.Pointer(&a[0])), 0); e1 != 0 { + err = errnoErr(e1) + } + return + } + if ret := cgocaller(cgo_libc_setgroups, n, uintptr(unsafe.Pointer(&a[0]))); ret != 0 { + err = errnoErr(Errno(ret)) + } + return } type WaitStatus uint32 @@ -957,17 +978,223 @@ func Getpgrp() (pid int) { //sysnb Setsid() (pid int, err error) //sysnb Settimeofday(tv *Timeval) (err error) -// issue 1435. -// On linux Setuid and Setgid only affects the current thread, not the process. -// This does not match what most callers expect so we must return an error -// here rather than letting the caller think that the call succeeded. +// allThreadsCaller holds the input and output state for performing a +// allThreadsSyscall that needs to synchronize all OS thread state. Linux +// generally does not always support this natively, so we have to +// manipulate the runtime to fix things up. +type allThreadsCaller struct { + // arguments + trap, a1, a2, a3, a4, a5, a6 uintptr + + // return values (only set by 0th invocation) + r1, r2 uintptr + + // err is the error code + err Errno +} + +// doSyscall is a callback for executing a syscall on the current m +// (OS thread). +//go:nosplit +//go:norace +func (pc *allThreadsCaller) doSyscall(initial bool) bool { + r1, r2, err := RawSyscall(pc.trap, pc.a1, pc.a2, pc.a3) + if initial { + pc.r1 = r1 + pc.r2 = r2 + pc.err = err + } else if pc.r1 != r1 || pc.r2 != r2 || pc.err != err { + panic("AllThreadsSyscall results differ between threads; runtime corrupted") + } + return err == 0 +} + +// doSyscall6 is a callback for executing a syscall6 on the current m +// (OS thread). +//go:nosplit +//go:norace +func (pc *allThreadsCaller) doSyscall6(initial bool) bool { + r1, r2, err := RawSyscall6(pc.trap, pc.a1, pc.a2, pc.a3, pc.a4, pc.a5, pc.a6) + if initial { + pc.r1 = r1 + pc.r2 = r2 + pc.err = err + } else if pc.r1 != r1 || pc.r2 != r2 || pc.err != err { + panic("AllThreadsSyscall6 results differ between threads; runtime corrupted") + } + return err == 0 +} + +// Provided by runtime.syscall_runtime_doAllThreadsSyscall which +// serializes the world and invokes the fn on each OS thread (what the +// runtime refers to as m's). Once this function returns, all threads +// are in sync. +func runtime_doAllThreadsSyscall(fn func(bool) bool) + +// AllThreadsSyscall performs a syscall on each OS thread of the Go +// runtime. It first invokes the syscall on one thread. Should that +// invocation fail, it returns immediately with the error status. +// Otherwise, it invokes the syscall on all of the remaining threads +// in parallel. It will terminate the program if it observes any +// invoked syscall's return value differs from that of the first +// invocation. +// +// AllThreadsSyscall is intended for emulating simultaneous +// process-wide state changes that require consistently modifying +// per-thread state of the Go runtime. +// +// AllThreadsSyscall is unaware of any threads that are launched +// explicitly by cgo linked code, so the function always returns +// ENOTSUP in binaries that use cgo. +//go:uintptrescapes +func AllThreadsSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { + if cgo_libc_setegid != nil { + return minus1, minus1, ENOTSUP + } + pc := &allThreadsCaller{ + trap: trap, + a1: a1, + a2: a2, + a3: a3, + } + runtime_doAllThreadsSyscall(pc.doSyscall) + r1 = pc.r1 + r2 = pc.r2 + err = pc.err + return +} -func Setuid(uid int) (err error) { - return EOPNOTSUPP +// AllThreadsSyscall6 is like AllThreadsSyscall, but extended to six +// arguments. +//go:uintptrescapes +func AllThreadsSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) { + if cgo_libc_setegid != nil { + return minus1, minus1, ENOTSUP + } + pc := &allThreadsCaller{ + trap: trap, + a1: a1, + a2: a2, + a3: a3, + a4: a4, + a5: a5, + a6: a6, + } + runtime_doAllThreadsSyscall(pc.doSyscall6) + r1 = pc.r1 + r2 = pc.r2 + err = pc.err + return +} + +// linked by runtime.cgocall.go +//go:uintptrescapes +func cgocaller(unsafe.Pointer, ...uintptr) uintptr + +var cgo_libc_setegid unsafe.Pointer // non-nil if cgo linked. + +const minus1 = ^uintptr(0) + +func Setegid(egid int) (err error) { + if cgo_libc_setegid == nil { + if _, _, e1 := AllThreadsSyscall(SYS_SETRESGID, minus1, uintptr(egid), minus1); e1 != 0 { + err = errnoErr(e1) + } + } else if ret := cgocaller(cgo_libc_setegid, uintptr(egid)); ret != 0 { + err = errnoErr(Errno(ret)) + } + return +} + +var cgo_libc_seteuid unsafe.Pointer // non-nil if cgo linked. + +func Seteuid(euid int) (err error) { + if cgo_libc_seteuid == nil { + if _, _, e1 := AllThreadsSyscall(SYS_SETRESUID, minus1, uintptr(euid), minus1); e1 != 0 { + err = errnoErr(e1) + } + } else if ret := cgocaller(cgo_libc_seteuid, uintptr(euid)); ret != 0 { + err = errnoErr(Errno(ret)) + } + return } +var cgo_libc_setgid unsafe.Pointer // non-nil if cgo linked. + func Setgid(gid int) (err error) { - return EOPNOTSUPP + if cgo_libc_setgid == nil { + if _, _, e1 := AllThreadsSyscall(sys_SETGID, uintptr(gid), 0, 0); e1 != 0 { + err = errnoErr(e1) + } + } else if ret := cgocaller(cgo_libc_setgid, uintptr(gid)); ret != 0 { + err = errnoErr(Errno(ret)) + } + return +} + +var cgo_libc_setregid unsafe.Pointer // non-nil if cgo linked. + +func Setregid(rgid, egid int) (err error) { + if cgo_libc_setregid == nil { + if _, _, e1 := AllThreadsSyscall(sys_SETREGID, uintptr(rgid), uintptr(egid), 0); e1 != 0 { + err = errnoErr(e1) + } + } else if ret := cgocaller(cgo_libc_setregid, uintptr(rgid), uintptr(egid)); ret != 0 { + err = errnoErr(Errno(ret)) + } + return +} + +var cgo_libc_setresgid unsafe.Pointer // non-nil if cgo linked. + +func Setresgid(rgid, egid, sgid int) (err error) { + if cgo_libc_setresgid == nil { + if _, _, e1 := AllThreadsSyscall(sys_SETRESGID, uintptr(rgid), uintptr(egid), uintptr(sgid)); e1 != 0 { + err = errnoErr(e1) + } + } else if ret := cgocaller(cgo_libc_setresgid, uintptr(rgid), uintptr(egid), uintptr(sgid)); ret != 0 { + err = errnoErr(Errno(ret)) + } + return +} + +var cgo_libc_setresuid unsafe.Pointer // non-nil if cgo linked. + +func Setresuid(ruid, euid, suid int) (err error) { + if cgo_libc_setresuid == nil { + if _, _, e1 := AllThreadsSyscall(sys_SETRESUID, uintptr(ruid), uintptr(euid), uintptr(suid)); e1 != 0 { + err = errnoErr(e1) + } + } else if ret := cgocaller(cgo_libc_setresuid, uintptr(ruid), uintptr(euid), uintptr(suid)); ret != 0 { + err = errnoErr(Errno(ret)) + } + return +} + +var cgo_libc_setreuid unsafe.Pointer // non-nil if cgo linked. + +func Setreuid(ruid, euid int) (err error) { + if cgo_libc_setreuid == nil { + if _, _, e1 := AllThreadsSyscall(sys_SETREUID, uintptr(ruid), uintptr(euid), 0); e1 != 0 { + err = errnoErr(e1) + } + } else if ret := cgocaller(cgo_libc_setreuid, uintptr(ruid), uintptr(euid)); ret != 0 { + err = errnoErr(Errno(ret)) + } + return +} + +var cgo_libc_setuid unsafe.Pointer // non-nil if cgo linked. + +func Setuid(uid int) (err error) { + if cgo_libc_setuid == nil { + if _, _, e1 := AllThreadsSyscall(sys_SETUID, uintptr(uid), 0, 0); e1 != 0 { + err = errnoErr(e1) + } + } else if ret := cgocaller(cgo_libc_setuid, uintptr(uid)); ret != 0 { + err = errnoErr(Errno(ret)) + } + return } //sys Setpriority(which int, who int, prio int) (err error) -- cgit v1.3 From 3a819e8998af1db3bdd34eb2ab059a3c534c6def Mon Sep 17 00:00:00 2001 From: "Andrew G. Morgan" Date: Wed, 28 Oct 2020 13:35:57 -0700 Subject: syscall: handle undefined r2 value on linux-ppc64x This change fixes two failng tests on linux-ppc64x: - TestAllThreadsSyscall() exposed a real bug in the ppc64x support: - It turns out that the r2 syscall return value is not defined on all architectures. Notably linux-ppc64x so address that by introducing a private architectural constant in the syscall package, archHonorsR2: true if r2 has a determanistic value. - TestSetuidEtc() was sensitive to /proc//status content: - The amount of padding space has changed with kernel vintage. - Stress testing revealed a race with /proc files disappearing. Fixes #42178 Change-Id: Ie6fc0b8f2f94a409ac0e5756e73bfce113274709 Reviewed-on: https://go-review.googlesource.com/c/go/+/266202 Run-TryBot: Ian Lance Taylor Reviewed-by: Emmanuel Odeke Reviewed-by: Ian Lance Taylor TryBot-Result: Go Bot --- src/syscall/syscall_linux.go | 8 +++-- src/syscall/syscall_linux_386.go | 6 ++++ src/syscall/syscall_linux_amd64.go | 6 ++++ src/syscall/syscall_linux_arm.go | 6 ++++ src/syscall/syscall_linux_arm64.go | 6 ++++ src/syscall/syscall_linux_mips64x.go | 6 ++++ src/syscall/syscall_linux_mipsx.go | 6 ++++ src/syscall/syscall_linux_ppc64x.go | 6 ++++ src/syscall/syscall_linux_riscv64.go | 6 ++++ src/syscall/syscall_linux_s390x.go | 6 ++++ src/syscall/syscall_linux_test.go | 60 +++++++++++++++++++----------------- 11 files changed, 91 insertions(+), 31 deletions(-) (limited to 'src/syscall/syscall_linux.go') diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go index 54e5cfc2f2..3041f6f8fc 100644 --- a/src/syscall/syscall_linux.go +++ b/src/syscall/syscall_linux.go @@ -1003,7 +1003,9 @@ func (pc *allThreadsCaller) doSyscall(initial bool) bool { pc.r1 = r1 pc.r2 = r2 pc.err = err - } else if pc.r1 != r1 || pc.r2 != r2 || pc.err != err { + } else if pc.r1 != r1 || (archHonorsR2 && pc.r2 != r2) || pc.err != err { + print("trap:", pc.trap, ", a123=[", pc.a1, ",", pc.a2, ",", pc.a3, "]\n") + print("results: got {r1=", r1, ",r2=", r2, ",err=", err, "}, want {r1=", pc.r1, ",r2=", pc.r2, ",r3=", pc.err, "}\n") panic("AllThreadsSyscall results differ between threads; runtime corrupted") } return err == 0 @@ -1019,7 +1021,9 @@ func (pc *allThreadsCaller) doSyscall6(initial bool) bool { pc.r1 = r1 pc.r2 = r2 pc.err = err - } else if pc.r1 != r1 || pc.r2 != r2 || pc.err != err { + } else if pc.r1 != r1 || (archHonorsR2 && pc.r2 != r2) || pc.err != err { + print("trap:", pc.trap, ", a123456=[", pc.a1, ",", pc.a2, ",", pc.a3, ",", pc.a4, ",", pc.a5, ",", pc.a6, "]\n") + print("results: got {r1=", r1, ",r2=", r2, ",err=", err, "}, want {r1=", pc.r1, ",r2=", pc.r2, ",r3=", pc.err, "}\n") panic("AllThreadsSyscall6 results differ between threads; runtime corrupted") } return err == 0 diff --git a/src/syscall/syscall_linux_386.go b/src/syscall/syscall_linux_386.go index dd5f2735d8..ed52647403 100644 --- a/src/syscall/syscall_linux_386.go +++ b/src/syscall/syscall_linux_386.go @@ -6,6 +6,12 @@ package syscall import "unsafe" +// archHonorsR2 captures the fact that r2 is honored by the +// runtime.GOARCH. Syscall conventions are generally r1, r2, err := +// syscall(trap, ...). Not all architectures define r2 in their +// ABI. See "man syscall". +const archHonorsR2 = true + const _SYS_setgroups = SYS_SETGROUPS32 func setTimespec(sec, nsec int64) Timespec { diff --git a/src/syscall/syscall_linux_amd64.go b/src/syscall/syscall_linux_amd64.go index 5518f44a07..5df3f796d1 100644 --- a/src/syscall/syscall_linux_amd64.go +++ b/src/syscall/syscall_linux_amd64.go @@ -4,6 +4,12 @@ package syscall +// archHonorsR2 captures the fact that r2 is honored by the +// runtime.GOARCH. Syscall conventions are generally r1, r2, err := +// syscall(trap, ...). Not all architectures define r2 in their +// ABI. See "man syscall". +const archHonorsR2 = true + const _SYS_setgroups = SYS_SETGROUPS //sys Dup2(oldfd int, newfd int) (err error) diff --git a/src/syscall/syscall_linux_arm.go b/src/syscall/syscall_linux_arm.go index 61133a59fb..4a3729f898 100644 --- a/src/syscall/syscall_linux_arm.go +++ b/src/syscall/syscall_linux_arm.go @@ -6,6 +6,12 @@ package syscall import "unsafe" +// archHonorsR2 captures the fact that r2 is honored by the +// runtime.GOARCH. Syscall conventions are generally r1, r2, err := +// syscall(trap, ...). Not all architectures define r2 in their +// ABI. See "man syscall". [EABI assumed.] +const archHonorsR2 = true + const _SYS_setgroups = SYS_SETGROUPS32 func setTimespec(sec, nsec int64) Timespec { diff --git a/src/syscall/syscall_linux_arm64.go b/src/syscall/syscall_linux_arm64.go index 16382102c8..f575c84c93 100644 --- a/src/syscall/syscall_linux_arm64.go +++ b/src/syscall/syscall_linux_arm64.go @@ -6,6 +6,12 @@ package syscall import "unsafe" +// archHonorsR2 captures the fact that r2 is honored by the +// runtime.GOARCH. Syscall conventions are generally r1, r2, err := +// syscall(trap, ...). Not all architectures define r2 in their +// ABI. See "man syscall". +const archHonorsR2 = true + const _SYS_setgroups = SYS_SETGROUPS func EpollCreate(size int) (fd int, err error) { diff --git a/src/syscall/syscall_linux_mips64x.go b/src/syscall/syscall_linux_mips64x.go index 4986baa319..ab25b7be6f 100644 --- a/src/syscall/syscall_linux_mips64x.go +++ b/src/syscall/syscall_linux_mips64x.go @@ -7,6 +7,12 @@ package syscall +// archHonorsR2 captures the fact that r2 is honored by the +// runtime.GOARCH. Syscall conventions are generally r1, r2, err := +// syscall(trap, ...). Not all architectures define r2 in their +// ABI. See "man syscall". +const archHonorsR2 = true + const _SYS_setgroups = SYS_SETGROUPS //sys Dup2(oldfd int, newfd int) (err error) diff --git a/src/syscall/syscall_linux_mipsx.go b/src/syscall/syscall_linux_mipsx.go index 5126b0e43c..377946fc92 100644 --- a/src/syscall/syscall_linux_mipsx.go +++ b/src/syscall/syscall_linux_mipsx.go @@ -9,6 +9,12 @@ package syscall import "unsafe" +// archHonorsR2 captures the fact that r2 is honored by the +// runtime.GOARCH. Syscall conventions are generally r1, r2, err := +// syscall(trap, ...). Not all architectures define r2 in their +// ABI. See "man syscall". +const archHonorsR2 = true + const _SYS_setgroups = SYS_SETGROUPS func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) diff --git a/src/syscall/syscall_linux_ppc64x.go b/src/syscall/syscall_linux_ppc64x.go index bb2d904b5f..45bf667407 100644 --- a/src/syscall/syscall_linux_ppc64x.go +++ b/src/syscall/syscall_linux_ppc64x.go @@ -7,6 +7,12 @@ package syscall +// archHonorsR2 captures the fact that r2 is honored by the +// runtime.GOARCH. Syscall conventions are generally r1, r2, err := +// syscall(trap, ...). Not all architectures define r2 in their +// ABI. See "man syscall". +const archHonorsR2 = false + const _SYS_setgroups = SYS_SETGROUPS //sys Dup2(oldfd int, newfd int) (err error) diff --git a/src/syscall/syscall_linux_riscv64.go b/src/syscall/syscall_linux_riscv64.go index aa1014f8ae..2a0fe64d25 100644 --- a/src/syscall/syscall_linux_riscv64.go +++ b/src/syscall/syscall_linux_riscv64.go @@ -6,6 +6,12 @@ package syscall import "unsafe" +// archHonorsR2 captures the fact that r2 is honored by the +// runtime.GOARCH. Syscall conventions are generally r1, r2, err := +// syscall(trap, ...). Not all architectures define r2 in their +// ABI. See "man syscall". +const archHonorsR2 = true + const _SYS_setgroups = SYS_SETGROUPS func EpollCreate(size int) (fd int, err error) { diff --git a/src/syscall/syscall_linux_s390x.go b/src/syscall/syscall_linux_s390x.go index dc97d5c65a..0f6f6277bb 100644 --- a/src/syscall/syscall_linux_s390x.go +++ b/src/syscall/syscall_linux_s390x.go @@ -6,6 +6,12 @@ package syscall import "unsafe" +// archHonorsR2 captures the fact that r2 is honored by the +// runtime.GOARCH. Syscall conventions are generally r1, r2, err := +// syscall(trap, ...). Not all architectures define r2 in their +// ABI. See "man syscall". +const archHonorsR2 = true + const _SYS_setgroups = SYS_SETGROUPS //sys Dup2(oldfd int, newfd int) (err error) diff --git a/src/syscall/syscall_linux_test.go b/src/syscall/syscall_linux_test.go index 0742ef5b07..41ae8cc5a1 100644 --- a/src/syscall/syscall_linux_test.go +++ b/src/syscall/syscall_linux_test.go @@ -410,9 +410,6 @@ const ( // syscalls that execute on all OSThreads - with which to support // POSIX semantics for security state changes. func TestAllThreadsSyscall(t *testing.T) { - if runtime.GOARCH == "ppc64" { - t.Skip("skipping on linux/ppc64; see issue #42178") - } if _, _, err := syscall.AllThreadsSyscall(syscall.SYS_PRCTL, PR_SET_KEEPCAPS, 0, 0); err == syscall.ENOTSUP { t.Skip("AllThreadsSyscall disabled with cgo") } @@ -544,7 +541,7 @@ func TestAllThreadsSyscall(t *testing.T) { // compareStatus is used to confirm the contents of the thread // specific status files match expectations. func compareStatus(filter, expect string) error { - expected := filter + "\t" + expect + expected := filter + expect pid := syscall.Getpid() fs, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/task", pid)) if err != nil { @@ -553,14 +550,22 @@ func compareStatus(filter, expect string) error { for _, f := range fs { tf := fmt.Sprintf("/proc/%s/status", f.Name()) d, err := ioutil.ReadFile(tf) + if os.IsNotExist(err) { + // We are racing against threads dying, which + // is out of our control, so ignore the + // missing file and skip to the next one. + continue + } if err != nil { return fmt.Errorf("unable to read %q: %v", tf, err) } lines := strings.Split(string(d), "\n") for _, line := range lines { + // Different kernel vintages pad differently. + line = strings.TrimSpace(line) if strings.HasPrefix(line, filter) { if line != expected { - return fmt.Errorf("%s %s (bad)\n", tf, line) + return fmt.Errorf("%q got:%q want:%q (bad)\n", tf, line, expected) } break } @@ -580,9 +585,6 @@ func compareStatus(filter, expect string) error { // the syscalls. Care should be taken to mirror any enhancements to // this test here in that file too. func TestSetuidEtc(t *testing.T) { - if runtime.GOARCH == "ppc64" { - t.Skip("skipping on linux/ppc64; see issue #42178") - } if syscall.Getuid() != 0 { t.Skip("skipping root only test") } @@ -591,34 +593,34 @@ func TestSetuidEtc(t *testing.T) { fn func() error filter, expect string }{ - {call: "Setegid(1)", fn: func() error { return syscall.Setegid(1) }, filter: "Gid:", expect: "0\t1\t0\t1"}, - {call: "Setegid(0)", fn: func() error { return syscall.Setegid(0) }, filter: "Gid:", expect: "0\t0\t0\t0"}, + {call: "Setegid(1)", fn: func() error { return syscall.Setegid(1) }, filter: "Gid:", expect: "\t0\t1\t0\t1"}, + {call: "Setegid(0)", fn: func() error { return syscall.Setegid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, - {call: "Seteuid(1)", fn: func() error { return syscall.Seteuid(1) }, filter: "Uid:", expect: "0\t1\t0\t1"}, - {call: "Setuid(0)", fn: func() error { return syscall.Setuid(0) }, filter: "Uid:", expect: "0\t0\t0\t0"}, + {call: "Seteuid(1)", fn: func() error { return syscall.Seteuid(1) }, filter: "Uid:", expect: "\t0\t1\t0\t1"}, + {call: "Setuid(0)", fn: func() error { return syscall.Setuid(0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"}, - {call: "Setgid(1)", fn: func() error { return syscall.Setgid(1) }, filter: "Gid:", expect: "1\t1\t1\t1"}, - {call: "Setgid(0)", fn: func() error { return syscall.Setgid(0) }, filter: "Gid:", expect: "0\t0\t0\t0"}, + {call: "Setgid(1)", fn: func() error { return syscall.Setgid(1) }, filter: "Gid:", expect: "\t1\t1\t1\t1"}, + {call: "Setgid(0)", fn: func() error { return syscall.Setgid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, - {call: "Setgroups([]int{0,1,2,3})", fn: func() error { return syscall.Setgroups([]int{0, 1, 2, 3}) }, filter: "Groups:", expect: "0 1 2 3 "}, - {call: "Setgroups(nil)", fn: func() error { return syscall.Setgroups(nil) }, filter: "Groups:", expect: " "}, - {call: "Setgroups([]int{0})", fn: func() error { return syscall.Setgroups([]int{0}) }, filter: "Groups:", expect: "0 "}, + {call: "Setgroups([]int{0,1,2,3})", fn: func() error { return syscall.Setgroups([]int{0, 1, 2, 3}) }, filter: "Groups:", expect: "\t0 1 2 3"}, + {call: "Setgroups(nil)", fn: func() error { return syscall.Setgroups(nil) }, filter: "Groups:", expect: ""}, + {call: "Setgroups([]int{0})", fn: func() error { return syscall.Setgroups([]int{0}) }, filter: "Groups:", expect: "\t0"}, - {call: "Setregid(101,0)", fn: func() error { return syscall.Setregid(101, 0) }, filter: "Gid:", expect: "101\t0\t0\t0"}, - {call: "Setregid(0,102)", fn: func() error { return syscall.Setregid(0, 102) }, filter: "Gid:", expect: "0\t102\t102\t102"}, - {call: "Setregid(0,0)", fn: func() error { return syscall.Setregid(0, 0) }, filter: "Gid:", expect: "0\t0\t0\t0"}, + {call: "Setregid(101,0)", fn: func() error { return syscall.Setregid(101, 0) }, filter: "Gid:", expect: "\t101\t0\t0\t0"}, + {call: "Setregid(0,102)", fn: func() error { return syscall.Setregid(0, 102) }, filter: "Gid:", expect: "\t0\t102\t102\t102"}, + {call: "Setregid(0,0)", fn: func() error { return syscall.Setregid(0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, - {call: "Setreuid(1,0)", fn: func() error { return syscall.Setreuid(1, 0) }, filter: "Uid:", expect: "1\t0\t0\t0"}, - {call: "Setreuid(0,2)", fn: func() error { return syscall.Setreuid(0, 2) }, filter: "Uid:", expect: "0\t2\t2\t2"}, - {call: "Setreuid(0,0)", fn: func() error { return syscall.Setreuid(0, 0) }, filter: "Uid:", expect: "0\t0\t0\t0"}, + {call: "Setreuid(1,0)", fn: func() error { return syscall.Setreuid(1, 0) }, filter: "Uid:", expect: "\t1\t0\t0\t0"}, + {call: "Setreuid(0,2)", fn: func() error { return syscall.Setreuid(0, 2) }, filter: "Uid:", expect: "\t0\t2\t2\t2"}, + {call: "Setreuid(0,0)", fn: func() error { return syscall.Setreuid(0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"}, - {call: "Setresgid(101,0,102)", fn: func() error { return syscall.Setresgid(101, 0, 102) }, filter: "Gid:", expect: "101\t0\t102\t0"}, - {call: "Setresgid(0,102,101)", fn: func() error { return syscall.Setresgid(0, 102, 101) }, filter: "Gid:", expect: "0\t102\t101\t102"}, - {call: "Setresgid(0,0,0)", fn: func() error { return syscall.Setresgid(0, 0, 0) }, filter: "Gid:", expect: "0\t0\t0\t0"}, + {call: "Setresgid(101,0,102)", fn: func() error { return syscall.Setresgid(101, 0, 102) }, filter: "Gid:", expect: "\t101\t0\t102\t0"}, + {call: "Setresgid(0,102,101)", fn: func() error { return syscall.Setresgid(0, 102, 101) }, filter: "Gid:", expect: "\t0\t102\t101\t102"}, + {call: "Setresgid(0,0,0)", fn: func() error { return syscall.Setresgid(0, 0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, - {call: "Setresuid(1,0,2)", fn: func() error { return syscall.Setresuid(1, 0, 2) }, filter: "Uid:", expect: "1\t0\t2\t0"}, - {call: "Setresuid(0,2,1)", fn: func() error { return syscall.Setresuid(0, 2, 1) }, filter: "Uid:", expect: "0\t2\t1\t2"}, - {call: "Setresuid(0,0,0)", fn: func() error { return syscall.Setresuid(0, 0, 0) }, filter: "Uid:", expect: "0\t0\t0\t0"}, + {call: "Setresuid(1,0,2)", fn: func() error { return syscall.Setresuid(1, 0, 2) }, filter: "Uid:", expect: "\t1\t0\t2\t0"}, + {call: "Setresuid(0,2,1)", fn: func() error { return syscall.Setresuid(0, 2, 1) }, filter: "Uid:", expect: "\t0\t2\t1\t2"}, + {call: "Setresuid(0,0,0)", fn: func() error { return syscall.Setresuid(0, 0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"}, } for i, v := range vs { -- cgit v1.3