...
Run Format

Source file src/syscall/syscall_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  package syscall_test
     6  
     7  import (
     8  	"bufio"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"os/signal"
    15  	"path/filepath"
    16  	"runtime"
    17  	"strconv"
    18  	"strings"
    19  	"syscall"
    20  	"testing"
    21  	"time"
    22  )
    23  
    24  // chtmpdir changes the working directory to a new temporary directory and
    25  // provides a cleanup function. Used when PWD is read-only.
    26  func chtmpdir(t *testing.T) func() {
    27  	oldwd, err := os.Getwd()
    28  	if err != nil {
    29  		t.Fatalf("chtmpdir: %v", err)
    30  	}
    31  	d, err := ioutil.TempDir("", "test")
    32  	if err != nil {
    33  		t.Fatalf("chtmpdir: %v", err)
    34  	}
    35  	if err := os.Chdir(d); err != nil {
    36  		t.Fatalf("chtmpdir: %v", err)
    37  	}
    38  	return func() {
    39  		if err := os.Chdir(oldwd); err != nil {
    40  			t.Fatalf("chtmpdir: %v", err)
    41  		}
    42  		os.RemoveAll(d)
    43  	}
    44  }
    45  
    46  func touch(t *testing.T, name string) {
    47  	f, err := os.Create(name)
    48  	if err != nil {
    49  		t.Fatal(err)
    50  	}
    51  	if err := f.Close(); err != nil {
    52  		t.Fatal(err)
    53  	}
    54  }
    55  
    56  const (
    57  	_AT_SYMLINK_NOFOLLOW = 0x100
    58  	_AT_FDCWD            = -0x64
    59  	_AT_EACCESS          = 0x200
    60  	_F_OK                = 0
    61  	_R_OK                = 4
    62  )
    63  
    64  func TestFaccessat(t *testing.T) {
    65  	defer chtmpdir(t)()
    66  	touch(t, "file1")
    67  
    68  	err := syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, 0)
    69  	if err != nil {
    70  		t.Errorf("Faccessat: unexpected error: %v", err)
    71  	}
    72  
    73  	err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, 2)
    74  	if err != syscall.EINVAL {
    75  		t.Errorf("Faccessat: unexpected error: %v, want EINVAL", err)
    76  	}
    77  
    78  	err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, _AT_EACCESS)
    79  	if err != nil {
    80  		t.Errorf("Faccessat: unexpected error: %v", err)
    81  	}
    82  
    83  	err = os.Symlink("file1", "symlink1")
    84  	if err != nil {
    85  		t.Fatal(err)
    86  	}
    87  
    88  	err = syscall.Faccessat(_AT_FDCWD, "symlink1", _R_OK, _AT_SYMLINK_NOFOLLOW)
    89  	if err != nil {
    90  		t.Errorf("Faccessat SYMLINK_NOFOLLOW: unexpected error %v", err)
    91  	}
    92  
    93  	// We can't really test _AT_SYMLINK_NOFOLLOW, because there
    94  	// doesn't seem to be any way to change the mode of a symlink.
    95  	// We don't test _AT_EACCESS because such tests are only
    96  	// meaningful if run as root.
    97  
    98  	err = syscall.Fchmodat(_AT_FDCWD, "file1", 0, 0)
    99  	if err != nil {
   100  		t.Errorf("Fchmodat: unexpected error %v", err)
   101  	}
   102  
   103  	err = syscall.Faccessat(_AT_FDCWD, "file1", _F_OK, _AT_SYMLINK_NOFOLLOW)
   104  	if err != nil {
   105  		t.Errorf("Faccessat: unexpected error: %v", err)
   106  	}
   107  
   108  	err = syscall.Faccessat(_AT_FDCWD, "file1", _R_OK, _AT_SYMLINK_NOFOLLOW)
   109  	if err != syscall.EACCES {
   110  		if syscall.Getuid() != 0 {
   111  			t.Errorf("Faccessat: unexpected error: %v, want EACCES", err)
   112  		}
   113  	}
   114  }
   115  
   116  func TestFchmodat(t *testing.T) {
   117  	defer chtmpdir(t)()
   118  
   119  	touch(t, "file1")
   120  	os.Symlink("file1", "symlink1")
   121  
   122  	err := syscall.Fchmodat(_AT_FDCWD, "symlink1", 0444, 0)
   123  	if err != nil {
   124  		t.Fatalf("Fchmodat: unexpected error: %v", err)
   125  	}
   126  
   127  	fi, err := os.Stat("file1")
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  
   132  	if fi.Mode() != 0444 {
   133  		t.Errorf("Fchmodat: failed to change mode: expected %v, got %v", 0444, fi.Mode())
   134  	}
   135  
   136  	err = syscall.Fchmodat(_AT_FDCWD, "symlink1", 0444, _AT_SYMLINK_NOFOLLOW)
   137  	if err != syscall.EOPNOTSUPP {
   138  		t.Fatalf("Fchmodat: unexpected error: %v, expected EOPNOTSUPP", err)
   139  	}
   140  }
   141  
   142  func TestMain(m *testing.M) {
   143  	if os.Getenv("GO_DEATHSIG_PARENT") == "1" {
   144  		deathSignalParent()
   145  	} else if os.Getenv("GO_DEATHSIG_CHILD") == "1" {
   146  		deathSignalChild()
   147  	} else if os.Getenv("GO_SYSCALL_NOERROR") == "1" {
   148  		syscallNoError()
   149  	}
   150  
   151  	os.Exit(m.Run())
   152  }
   153  
   154  func TestLinuxDeathSignal(t *testing.T) {
   155  	if os.Getuid() != 0 {
   156  		t.Skip("skipping root only test")
   157  	}
   158  
   159  	// Copy the test binary to a location that a non-root user can read/execute
   160  	// after we drop privileges
   161  	tempDir, err := ioutil.TempDir("", "TestDeathSignal")
   162  	if err != nil {
   163  		t.Fatalf("cannot create temporary directory: %v", err)
   164  	}
   165  	defer os.RemoveAll(tempDir)
   166  	os.Chmod(tempDir, 0755)
   167  
   168  	tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0]))
   169  
   170  	src, err := os.Open(os.Args[0])
   171  	if err != nil {
   172  		t.Fatalf("cannot open binary %q, %v", os.Args[0], err)
   173  	}
   174  	defer src.Close()
   175  
   176  	dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
   177  	if err != nil {
   178  		t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err)
   179  	}
   180  	if _, err := io.Copy(dst, src); err != nil {
   181  		t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err)
   182  	}
   183  	err = dst.Close()
   184  	if err != nil {
   185  		t.Fatalf("failed to close test binary %q, %v", tmpBinary, err)
   186  	}
   187  
   188  	cmd := exec.Command(tmpBinary)
   189  	cmd.Env = []string{"GO_DEATHSIG_PARENT=1"}
   190  	chldStdin, err := cmd.StdinPipe()
   191  	if err != nil {
   192  		t.Fatalf("failed to create new stdin pipe: %v", err)
   193  	}
   194  	chldStdout, err := cmd.StdoutPipe()
   195  	if err != nil {
   196  		t.Fatalf("failed to create new stdout pipe: %v", err)
   197  	}
   198  	cmd.Stderr = os.Stderr
   199  
   200  	err = cmd.Start()
   201  	defer cmd.Wait()
   202  	if err != nil {
   203  		t.Fatalf("failed to start first child process: %v", err)
   204  	}
   205  
   206  	chldPipe := bufio.NewReader(chldStdout)
   207  
   208  	if got, err := chldPipe.ReadString('\n'); got == "start\n" {
   209  		syscall.Kill(cmd.Process.Pid, syscall.SIGTERM)
   210  
   211  		go func() {
   212  			time.Sleep(5 * time.Second)
   213  			chldStdin.Close()
   214  		}()
   215  
   216  		want := "ok\n"
   217  		if got, err = chldPipe.ReadString('\n'); got != want {
   218  			t.Fatalf("expected %q, received %q, %v", want, got, err)
   219  		}
   220  	} else {
   221  		t.Fatalf("did not receive start from child, received %q, %v", got, err)
   222  	}
   223  }
   224  
   225  func deathSignalParent() {
   226  	cmd := exec.Command(os.Args[0])
   227  	cmd.Env = []string{"GO_DEATHSIG_CHILD=1"}
   228  	cmd.Stdin = os.Stdin
   229  	cmd.Stdout = os.Stdout
   230  	attrs := syscall.SysProcAttr{
   231  		Pdeathsig: syscall.SIGUSR1,
   232  		// UID/GID 99 is the user/group "nobody" on RHEL/Fedora and is
   233  		// unused on Ubuntu
   234  		Credential: &syscall.Credential{Uid: 99, Gid: 99},
   235  	}
   236  	cmd.SysProcAttr = &attrs
   237  
   238  	err := cmd.Start()
   239  	if err != nil {
   240  		fmt.Fprintf(os.Stderr, "death signal parent error: %v\n", err)
   241  		os.Exit(1)
   242  	}
   243  	cmd.Wait()
   244  	os.Exit(0)
   245  }
   246  
   247  func deathSignalChild() {
   248  	c := make(chan os.Signal, 1)
   249  	signal.Notify(c, syscall.SIGUSR1)
   250  	go func() {
   251  		<-c
   252  		fmt.Println("ok")
   253  		os.Exit(0)
   254  	}()
   255  	fmt.Println("start")
   256  
   257  	buf := make([]byte, 32)
   258  	os.Stdin.Read(buf)
   259  
   260  	// We expected to be signaled before stdin closed
   261  	fmt.Println("not ok")
   262  	os.Exit(1)
   263  }
   264  
   265  func TestParseNetlinkMessage(t *testing.T) {
   266  	for i, b := range [][]byte{
   267  		{103, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 5, 8, 0, 3,
   268  			0, 8, 0, 6, 0, 0, 0, 0, 1, 63, 0, 10, 0, 69, 16, 0, 59, 39, 82, 64, 0, 64, 6, 21, 89, 127, 0, 0,
   269  			1, 127, 0, 0, 1, 230, 228, 31, 144, 32, 186, 155, 211, 185, 151, 209, 179, 128, 24, 1, 86,
   270  			53, 119, 0, 0, 1, 1, 8, 10, 0, 17, 234, 12, 0, 17, 189, 126, 107, 106, 108, 107, 106, 13, 10,
   271  		},
   272  		{106, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 3, 8, 0, 3,
   273  			0, 8, 0, 6, 0, 0, 0, 0, 1, 66, 0, 10, 0, 69, 0, 0, 62, 230, 255, 64, 0, 64, 6, 85, 184, 127, 0, 0,
   274  			1, 127, 0, 0, 1, 237, 206, 31, 144, 73, 197, 128, 65, 250, 60, 192, 97, 128, 24, 1, 86, 253, 21, 0,
   275  			0, 1, 1, 8, 10, 0, 51, 106, 89, 0, 51, 102, 198, 108, 104, 106, 108, 107, 104, 108, 107, 104, 10,
   276  		},
   277  		{102, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 11, 0, 1, 0, 0, 0, 0, 1, 8, 0, 3, 0,
   278  			8, 0, 6, 0, 0, 0, 0, 1, 62, 0, 10, 0, 69, 0, 0, 58, 231, 2, 64, 0, 64, 6, 85, 185, 127, 0, 0, 1, 127,
   279  			0, 0, 1, 237, 206, 31, 144, 73, 197, 128, 86, 250, 60, 192, 97, 128, 24, 1, 86, 104, 64, 0, 0, 1, 1, 8,
   280  			10, 0, 52, 198, 200, 0, 51, 135, 232, 101, 115, 97, 103, 103, 10,
   281  		},
   282  	} {
   283  		m, err := syscall.ParseNetlinkMessage(b)
   284  		if err != syscall.EINVAL {
   285  			t.Errorf("#%d: got %v; want EINVAL", i, err)
   286  		}
   287  		if m != nil {
   288  			t.Errorf("#%d: got %v; want nil", i, m)
   289  		}
   290  	}
   291  }
   292  
   293  func TestSyscallNoError(t *testing.T) {
   294  	// On Linux there are currently no syscalls which don't fail and return
   295  	// a value larger than 0xfffffffffffff001 so we could test RawSyscall
   296  	// vs. RawSyscallNoError on 64bit architectures.
   297  	if runtime.GOARCH != "386" && runtime.GOARCH != "arm" {
   298  		t.Skip("skipping on non-32bit architecture")
   299  	}
   300  
   301  	if os.Getuid() != 0 {
   302  		t.Skip("skipping root only test")
   303  	}
   304  
   305  	// Copy the test binary to a location that a non-root user can read/execute
   306  	// after we drop privileges
   307  	tempDir, err := ioutil.TempDir("", "TestSyscallNoError")
   308  	if err != nil {
   309  		t.Fatalf("cannot create temporary directory: %v", err)
   310  	}
   311  	defer os.RemoveAll(tempDir)
   312  	os.Chmod(tempDir, 0755)
   313  
   314  	tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0]))
   315  
   316  	src, err := os.Open(os.Args[0])
   317  	if err != nil {
   318  		t.Fatalf("cannot open binary %q, %v", os.Args[0], err)
   319  	}
   320  	defer src.Close()
   321  
   322  	dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
   323  	if err != nil {
   324  		t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err)
   325  	}
   326  	if _, err := io.Copy(dst, src); err != nil {
   327  		t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err)
   328  	}
   329  	err = dst.Close()
   330  	if err != nil {
   331  		t.Fatalf("failed to close test binary %q, %v", tmpBinary, err)
   332  	}
   333  
   334  	uid := uint32(0xfffffffe)
   335  	err = os.Chown(tmpBinary, int(uid), -1)
   336  	if err != nil {
   337  		t.Fatalf("failed to chown test binary %q, %v", tmpBinary, err)
   338  	}
   339  
   340  	err = os.Chmod(tmpBinary, 0755|os.ModeSetuid)
   341  	if err != nil {
   342  		t.Fatalf("failed to set setuid bit on test binary %q, %v", tmpBinary, err)
   343  	}
   344  
   345  	cmd := exec.Command(tmpBinary)
   346  	cmd.Env = []string{"GO_SYSCALL_NOERROR=1"}
   347  
   348  	out, err := cmd.CombinedOutput()
   349  	if err != nil {
   350  		t.Fatalf("failed to start first child process: %v", err)
   351  	}
   352  
   353  	got := strings.TrimSpace(string(out))
   354  	want := strconv.FormatUint(uint64(uid)+1, 10) + " / " +
   355  		strconv.FormatUint(uint64(-uid), 10) + " / " +
   356  		strconv.FormatUint(uint64(uid), 10)
   357  	if got != want {
   358  		t.Errorf("expected %s, got %s", want, got)
   359  	}
   360  }
   361  
   362  func syscallNoError() {
   363  	// Test that the return value from SYS_GETEUID32 (which cannot fail)
   364  	// doesn't get treated as an error (see https://golang.org/issue/22924)
   365  	euid1, _, e := syscall.RawSyscall(syscall.Sys_GETEUID, 0, 0, 0)
   366  	euid2, _ := syscall.RawSyscallNoError(syscall.Sys_GETEUID, 0, 0, 0)
   367  
   368  	fmt.Println(uintptr(euid1), "/", int(e), "/", uintptr(euid2))
   369  	os.Exit(0)
   370  }
   371  

View as plain text