aboutsummaryrefslogtreecommitdiff
path: root/src/syscall/syscall_linux_test.go
diff options
context:
space:
mode:
authorTobias Klauser <tklauser@distanz.ch>2017-12-18 11:59:26 +0100
committerTobias Klauser <tobias.klauser@gmail.com>2018-02-13 15:57:31 +0000
commit36951a9f638b03950b7413eee73bd0e3ccf47130 (patch)
tree0ca42679f668f22178b3ae520638a36d54cb58dd /src/syscall/syscall_linux_test.go
parentacd17e9b2b9740ea374ec18bcc7a4cd488eb534c (diff)
downloadgo-36951a9f638b03950b7413eee73bd0e3ccf47130.tar.xz
syscall: support syscalls without error return on Linux
Add the rawSyscallNoError wrapper function which is used for Linux syscalls that don't return an error and convert all applicable occurences of RawSyscall to use it instead. Fixes #22924 Change-Id: Iff1eddb54573d459faa01471f10398b3d38528dd Reviewed-on: https://go-review.googlesource.com/84485 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/syscall/syscall_linux_test.go')
-rw-r--r--src/syscall/syscall_linux_test.go86
1 files changed, 86 insertions, 0 deletions
diff --git a/src/syscall/syscall_linux_test.go b/src/syscall/syscall_linux_test.go
index 2c4d953561..becd267101 100644
--- a/src/syscall/syscall_linux_test.go
+++ b/src/syscall/syscall_linux_test.go
@@ -13,6 +13,9 @@ import (
"os/exec"
"os/signal"
"path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
"syscall"
"testing"
"time"
@@ -23,6 +26,8 @@ func TestMain(m *testing.M) {
deathSignalParent()
} else if os.Getenv("GO_DEATHSIG_CHILD") == "1" {
deathSignalChild()
+ } else if os.Getenv("GO_SYSCALL_NOERROR") == "1" {
+ syscallNoError()
}
os.Exit(m.Run())
@@ -166,3 +171,84 @@ func TestParseNetlinkMessage(t *testing.T) {
}
}
}
+
+func TestSyscallNoError(t *testing.T) {
+ // On Linux there are currently no syscalls which don't fail and return
+ // a value larger than 0xfffffffffffff001 so we could test RawSyscall
+ // vs. RawSyscallNoError on 64bit architectures.
+ if runtime.GOARCH != "386" && runtime.GOARCH != "arm" {
+ t.Skip("skipping on non-32bit architecture")
+ }
+
+ // TODO(tklauser) is this check enough? Otherwise test for being in a non-k8s
+ // Linux VM via testenv.Builder().
+ if os.Getuid() != 0 {
+ t.Skip("skipping root only test")
+ }
+
+ // Copy the test binary to a location that a non-root user can read/execute
+ // after we drop privileges
+ tempDir, err := ioutil.TempDir("", "TestSyscallNoError")
+ if err != nil {
+ t.Fatalf("cannot create temporary directory: %v", err)
+ }
+ defer os.RemoveAll(tempDir)
+ os.Chmod(tempDir, 0755)
+
+ tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0]))
+
+ src, err := os.Open(os.Args[0])
+ if err != nil {
+ t.Fatalf("cannot open binary %q, %v", os.Args[0], err)
+ }
+ defer src.Close()
+
+ dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
+ if err != nil {
+ t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err)
+ }
+ if _, err := io.Copy(dst, src); err != nil {
+ t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err)
+ }
+ err = dst.Close()
+ if err != nil {
+ t.Fatalf("failed to close test binary %q, %v", tmpBinary, err)
+ }
+
+ uid := uint32(0xfffffffe)
+ err = os.Chown(tmpBinary, int(uid), -1)
+ if err != nil {
+ t.Fatalf("failed to chown test binary %q, %v", tmpBinary, err)
+ }
+
+ err = os.Chmod(tmpBinary, 0755|os.ModeSetuid)
+ if err != nil {
+ t.Fatalf("failed to set setuid bit on test binary %q, %v", tmpBinary, err)
+ }
+
+ cmd := exec.Command(tmpBinary)
+ cmd.Env = []string{"GO_SYSCALL_NOERROR=1"}
+
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to start first child process: %v", err)
+ }
+
+ got := strings.TrimSpace(string(out))
+ want := strconv.FormatUint(uint64(uid)+1, 10) + " / " +
+ strconv.FormatUint(uint64(-uid), 10) + " / " +
+ strconv.FormatUint(uint64(uid), 10)
+ if got != want {
+ t.Errorf("expected %s, got %s", want, got)
+ }
+}
+
+func syscallNoError() {
+ // Test that the return value from SYS_GETEUID32 (which cannot fail)
+ // doesn't get treated as an error (see https://golang.org/issue/22924)
+ euid1, _, e := syscall.RawSyscall(syscall.Sys_GETEUID, 0, 0, 0)
+ euid2, _ := syscall.RawSyscallNoError(syscall.Sys_GETEUID, 0, 0, 0)
+
+ fmt.Println(uintptr(euid1), "/", int(e), "/", uintptr(euid2))
+ os.Exit(0)
+}