Skip to content

Commit

Permalink
syscall: handle undefined r2 value on linux-ppc64x
Browse files Browse the repository at this point in the history
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/<PID>/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 <[email protected]>
Reviewed-by: Emmanuel Odeke <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
TryBot-Result: Go Bot <[email protected]>
  • Loading branch information
AndrewGMorgan authored and ianlancetaylor committed Oct 29, 2020
1 parent 4fb4291 commit 3a819e8
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 31 deletions.
8 changes: 6 additions & 2 deletions src/syscall/syscall_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/syscall/syscall_linux_386.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions src/syscall/syscall_linux_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions src/syscall/syscall_linux_arm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions src/syscall/syscall_linux_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions src/syscall/syscall_linux_mips64x.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions src/syscall/syscall_linux_mipsx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions src/syscall/syscall_linux_ppc64x.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions src/syscall/syscall_linux_riscv64.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions src/syscall/syscall_linux_s390x.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
60 changes: 31 additions & 29 deletions src/syscall/syscall_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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")
}
Expand All @@ -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 {
Expand Down

0 comments on commit 3a819e8

Please sign in to comment.