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.
25 // chtmpdir changes the working directory to a new temporary directory and
26 // provides a cleanup function. Used when PWD is read-only.
27 func chtmpdir(t
*testing
.T
) func() {
28 oldwd
, err
:= os
.Getwd()
30 t
.Fatalf("chtmpdir: %v", err
)
32 d
, err
:= ioutil
.TempDir("", "test")
34 t
.Fatalf("chtmpdir: %v", err
)
36 if err
:= os
.Chdir(d
); err
!= nil {
37 t
.Fatalf("chtmpdir: %v", err
)
40 if err
:= os
.Chdir(oldwd
); err
!= nil {
41 t
.Fatalf("chtmpdir: %v", err
)
47 func touch(t
*testing
.T
, name
string) {
48 f
, err
:= os
.Create(name
)
52 if err
:= f
.Close(); err
!= nil {
58 _AT_SYMLINK_NOFOLLOW
= 0x100
65 func TestFaccessat(t
*testing
.T
) {
69 err
:= syscall
.Faccessat(_AT_FDCWD
, "file1", _R_OK
, 0)
71 t
.Errorf("Faccessat: unexpected error: %v", err
)
74 err
= syscall
.Faccessat(_AT_FDCWD
, "file1", _R_OK
, 2)
75 if err
!= syscall
.EINVAL
{
76 t
.Errorf("Faccessat: unexpected error: %v, want EINVAL", err
)
79 err
= syscall
.Faccessat(_AT_FDCWD
, "file1", _R_OK
, _AT_EACCESS
)
81 t
.Errorf("Faccessat: unexpected error: %v", err
)
84 err
= os
.Symlink("file1", "symlink1")
89 err
= syscall
.Faccessat(_AT_FDCWD
, "symlink1", _R_OK
, _AT_SYMLINK_NOFOLLOW
)
91 t
.Errorf("Faccessat SYMLINK_NOFOLLOW: unexpected error %v", err
)
94 // We can't really test _AT_SYMLINK_NOFOLLOW, because there
95 // doesn't seem to be any way to change the mode of a symlink.
96 // We don't test _AT_EACCESS because such tests are only
97 // meaningful if run as root.
99 err
= syscall
.Fchmodat(_AT_FDCWD
, "file1", 0, 0)
101 t
.Errorf("Fchmodat: unexpected error %v", err
)
104 err
= syscall
.Faccessat(_AT_FDCWD
, "file1", _F_OK
, _AT_SYMLINK_NOFOLLOW
)
106 t
.Errorf("Faccessat: unexpected error: %v", err
)
109 err
= syscall
.Faccessat(_AT_FDCWD
, "file1", _R_OK
, _AT_SYMLINK_NOFOLLOW
)
110 if err
!= syscall
.EACCES
{
111 if syscall
.Getuid() != 0 {
112 t
.Errorf("Faccessat: unexpected error: %v, want EACCES", err
)
117 func TestFchmodat(t
*testing
.T
) {
121 os
.Symlink("file1", "symlink1")
123 err
:= syscall
.Fchmodat(_AT_FDCWD
, "symlink1", 0444, 0)
125 t
.Fatalf("Fchmodat: unexpected error: %v", err
)
128 fi
, err
:= os
.Stat("file1")
133 if fi
.Mode() != 0444 {
134 t
.Errorf("Fchmodat: failed to change mode: expected %v, got %v", 0444, fi
.Mode())
137 err
= syscall
.Fchmodat(_AT_FDCWD
, "symlink1", 0444, _AT_SYMLINK_NOFOLLOW
)
138 if err
!= syscall
.EOPNOTSUPP
{
139 t
.Fatalf("Fchmodat: unexpected error: %v, expected EOPNOTSUPP", err
)
143 func TestMain(m
*testing
.M
) {
144 if os
.Getenv("GO_DEATHSIG_PARENT") == "1" {
146 } else if os
.Getenv("GO_DEATHSIG_CHILD") == "1" {
148 } else if os
.Getenv("GO_SYSCALL_NOERROR") == "1" {
155 func TestLinuxDeathSignal(t
*testing
.T
) {
156 if os
.Getuid() != 0 {
157 t
.Skip("skipping root only test")
160 // Copy the test binary to a location that a non-root user can read/execute
161 // after we drop privileges
162 tempDir
, err
:= ioutil
.TempDir("", "TestDeathSignal")
164 t
.Fatalf("cannot create temporary directory: %v", err
)
166 defer os
.RemoveAll(tempDir
)
167 os
.Chmod(tempDir
, 0755)
169 tmpBinary
:= filepath
.Join(tempDir
, filepath
.Base(os
.Args
[0]))
171 src
, err
:= os
.Open(os
.Args
[0])
173 t
.Fatalf("cannot open binary %q, %v", os
.Args
[0], err
)
177 dst
, err
:= os
.OpenFile(tmpBinary
, os
.O_WRONLY|os
.O_CREATE|os
.O_TRUNC
, 0755)
179 t
.Fatalf("cannot create temporary binary %q, %v", tmpBinary
, err
)
181 if _
, err
:= io
.Copy(dst
, src
); err
!= nil {
182 t
.Fatalf("failed to copy test binary to %q, %v", tmpBinary
, err
)
186 t
.Fatalf("failed to close test binary %q, %v", tmpBinary
, err
)
189 cmd
:= exec
.Command(tmpBinary
)
190 cmd
.Env
= []string{"GO_DEATHSIG_PARENT=1"}
191 chldStdin
, err
:= cmd
.StdinPipe()
193 t
.Fatalf("failed to create new stdin pipe: %v", err
)
195 chldStdout
, err
:= cmd
.StdoutPipe()
197 t
.Fatalf("failed to create new stdout pipe: %v", err
)
199 cmd
.Stderr
= os
.Stderr
204 t
.Fatalf("failed to start first child process: %v", err
)
207 chldPipe
:= bufio
.NewReader(chldStdout
)
209 if got
, err
:= chldPipe
.ReadString('\n'); got
== "start\n" {
210 syscall
.Kill(cmd
.Process
.Pid
, syscall
.SIGTERM
)
213 time
.Sleep(5 * time
.Second
)
218 if got
, err
= chldPipe
.ReadString('\n'); got
!= want
{
219 t
.Fatalf("expected %q, received %q, %v", want
, got
, err
)
222 t
.Fatalf("did not receive start from child, received %q, %v", got
, err
)
226 func deathSignalParent() {
227 cmd
:= exec
.Command(os
.Args
[0])
228 cmd
.Env
= []string{"GO_DEATHSIG_CHILD=1"}
230 cmd
.Stdout
= os
.Stdout
231 attrs
:= syscall
.SysProcAttr
{
232 Pdeathsig
: syscall
.SIGUSR1
,
233 // UID/GID 99 is the user/group "nobody" on RHEL/Fedora and is
235 Credential
: &syscall
.Credential
{Uid
: 99, Gid
: 99},
237 cmd
.SysProcAttr
= &attrs
241 fmt
.Fprintf(os
.Stderr
, "death signal parent error: %v\n", err
)
248 func deathSignalChild() {
249 c
:= make(chan os
.Signal
, 1)
250 signal
.Notify(c
, syscall
.SIGUSR1
)
258 buf
:= make([]byte, 32)
261 // We expected to be signaled before stdin closed
262 fmt
.Println("not ok")
266 func TestParseNetlinkMessage(t
*testing
.T
) {
267 for i
, b
:= range [][]byte{
268 {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,
269 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,
270 1, 127, 0, 0, 1, 230, 228, 31, 144, 32, 186, 155, 211, 185, 151, 209, 179, 128, 24, 1, 86,
271 53, 119, 0, 0, 1, 1, 8, 10, 0, 17, 234, 12, 0, 17, 189, 126, 107, 106, 108, 107, 106, 13, 10,
273 {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,
274 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,
275 1, 127, 0, 0, 1, 237, 206, 31, 144, 73, 197, 128, 65, 250, 60, 192, 97, 128, 24, 1, 86, 253, 21, 0,
276 0, 1, 1, 8, 10, 0, 51, 106, 89, 0, 51, 102, 198, 108, 104, 106, 108, 107, 104, 108, 107, 104, 10,
278 {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,
279 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,
280 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,
281 10, 0, 52, 198, 200, 0, 51, 135, 232, 101, 115, 97, 103, 103, 10,
284 m
, err
:= syscall
.ParseNetlinkMessage(b
)
285 if err
!= syscall
.EINVAL
{
286 t
.Errorf("#%d: got %v; want EINVAL", i
, err
)
289 t
.Errorf("#%d: got %v; want nil", i
, m
)
294 func TestSyscallNoError(t
*testing
.T
) {
295 // On Linux there are currently no syscalls which don't fail and return
296 // a value larger than 0xfffffffffffff001 so we could test RawSyscall
297 // vs. RawSyscallNoError on 64bit architectures.
298 if unsafe
.Sizeof(uintptr(0)) != 4 {
299 t
.Skip("skipping on non-32bit architecture")
302 // See https://golang.org/issue/35422
303 // On MIPS, Linux returns whether the syscall had an error in a separate
304 // register (R7), not using a negative return value as on other
306 if runtime
.GOARCH
== "mips" || runtime
.GOARCH
== "mipsle" {
307 t
.Skipf("skipping on %s", runtime
.GOARCH
)
310 if os
.Getuid() != 0 {
311 t
.Skip("skipping root only test")
314 if syscall
.Sys_GETEUID
== 0 {
315 t
.Skip("skipping because there is no geteuid system call")
318 if runtime
.GOOS
== "android" {
319 t
.Skip("skipping on rooted android, see issue 27364")
322 // Copy the test binary to a location that a non-root user can read/execute
323 // after we drop privileges
324 tempDir
, err
:= ioutil
.TempDir("", "TestSyscallNoError")
326 t
.Fatalf("cannot create temporary directory: %v", err
)
328 defer os
.RemoveAll(tempDir
)
329 os
.Chmod(tempDir
, 0755)
331 tmpBinary
:= filepath
.Join(tempDir
, filepath
.Base(os
.Args
[0]))
333 src
, err
:= os
.Open(os
.Args
[0])
335 t
.Fatalf("cannot open binary %q, %v", os
.Args
[0], err
)
339 dst
, err
:= os
.OpenFile(tmpBinary
, os
.O_WRONLY|os
.O_CREATE|os
.O_TRUNC
, 0755)
341 t
.Fatalf("cannot create temporary binary %q, %v", tmpBinary
, err
)
343 if _
, err
:= io
.Copy(dst
, src
); err
!= nil {
344 t
.Fatalf("failed to copy test binary to %q, %v", tmpBinary
, err
)
348 t
.Fatalf("failed to close test binary %q, %v", tmpBinary
, err
)
351 uid
:= uint32(0xfffffffe)
352 err
= os
.Chown(tmpBinary
, int(uid
), -1)
354 t
.Fatalf("failed to chown test binary %q, %v", tmpBinary
, err
)
357 err
= os
.Chmod(tmpBinary
, 0755|os
.ModeSetuid
)
359 t
.Fatalf("failed to set setuid bit on test binary %q, %v", tmpBinary
, err
)
362 cmd
:= exec
.Command(tmpBinary
)
363 cmd
.Env
= []string{"GO_SYSCALL_NOERROR=1"}
365 out
, err
:= cmd
.CombinedOutput()
367 t
.Fatalf("failed to start first child process: %v", err
)
370 got
:= strings
.TrimSpace(string(out
))
371 want
:= strconv
.FormatUint(uint64(uid
)+1, 10) + " / " +
372 strconv
.FormatUint(uint64(-uid
), 10) + " / " +
373 strconv
.FormatUint(uint64(uid
), 10)
375 if filesystemIsNoSUID(tmpBinary
) {
376 t
.Skip("skipping test when temp dir is mounted nosuid")
378 // formatted so the values are aligned for easier comparison
379 t
.Errorf("expected %s,\ngot %s", want
, got
)
383 // filesystemIsNoSUID reports whether the filesystem for the given
384 // path is mounted nosuid.
385 func filesystemIsNoSUID(path
string) bool {
386 var st syscall
.Statfs_t
387 if syscall
.Statfs(path
, &st
) != nil {
390 return st
.Flags
&syscall
.MS_NOSUID
!= 0
393 func syscallNoError() {
394 // Test that the return value from SYS_GETEUID32 (which cannot fail)
395 // doesn't get treated as an error (see https://golang.org/issue/22924)
396 euid1
, _
, e
:= syscall
.RawSyscall(syscall
.Sys_GETEUID
, 0, 0, 0)
397 euid2
, _
:= syscall
.RawSyscallNoError(syscall
.Sys_GETEUID
, 0, 0, 0)
399 fmt
.Println(uintptr(euid1
), "/", int(e
), "/", uintptr(euid2
))