github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/syscall/exec_pdeathsig_test.go (about) 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 //go:build freebsd || linux 6 7 package syscall_test 8 9 import ( 10 "bufio" 11 "fmt" 12 "internal/testenv" 13 "io" 14 "os" 15 "os/exec" 16 "os/signal" 17 "os/user" 18 "path/filepath" 19 "strconv" 20 "strings" 21 "syscall" 22 "testing" 23 ) 24 25 // TestDeathSignalSetuid verifies that a command run with a different UID still 26 // receives PDeathsig; it is a regression test for https://go.dev/issue/9686. 27 func TestDeathSignalSetuid(t *testing.T) { 28 if testing.Short() { 29 t.Skipf("skipping test that copies its binary into temp dir") 30 } 31 32 // Copy the test binary to a location that another user can read/execute 33 // after we drop privileges. 34 // 35 // TODO(bcmills): Why do we believe that another users will be able to 36 // execute a binary in this directory? (It could be mounted noexec.) 37 tempDir, err := os.MkdirTemp("", "TestDeathSignal") 38 if err != nil { 39 t.Fatalf("cannot create temporary directory: %v", err) 40 } 41 defer os.RemoveAll(tempDir) 42 os.Chmod(tempDir, 0755) 43 44 tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0])) 45 46 src, err := os.Open(os.Args[0]) 47 if err != nil { 48 t.Fatalf("cannot open binary %q, %v", os.Args[0], err) 49 } 50 defer src.Close() 51 52 dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) 53 if err != nil { 54 t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err) 55 } 56 if _, err := io.Copy(dst, src); err != nil { 57 t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err) 58 } 59 err = dst.Close() 60 if err != nil { 61 t.Fatalf("failed to close test binary %q, %v", tmpBinary, err) 62 } 63 64 cmd := testenv.Command(t, tmpBinary) 65 cmd.Env = append(cmd.Environ(), "GO_DEATHSIG_PARENT=1") 66 chldStdin, err := cmd.StdinPipe() 67 if err != nil { 68 t.Fatalf("failed to create new stdin pipe: %v", err) 69 } 70 chldStdout, err := cmd.StdoutPipe() 71 if err != nil { 72 t.Fatalf("failed to create new stdout pipe: %v", err) 73 } 74 stderr := new(strings.Builder) 75 cmd.Stderr = stderr 76 77 err = cmd.Start() 78 defer func() { 79 chldStdin.Close() 80 cmd.Wait() 81 if stderr.Len() > 0 { 82 t.Logf("stderr:\n%s", stderr) 83 } 84 }() 85 if err != nil { 86 t.Fatalf("failed to start first child process: %v", err) 87 } 88 89 chldPipe := bufio.NewReader(chldStdout) 90 91 if got, err := chldPipe.ReadString('\n'); got == "start\n" { 92 syscall.Kill(cmd.Process.Pid, syscall.SIGTERM) 93 94 want := "ok\n" 95 if got, err = chldPipe.ReadString('\n'); got != want { 96 t.Fatalf("expected %q, received %q, %v", want, got, err) 97 } 98 } else if got == "skip\n" { 99 t.Skipf("skipping: parent could not run child program as selected user") 100 } else { 101 t.Fatalf("did not receive start from child, received %q, %v", got, err) 102 } 103 } 104 105 func deathSignalParent() { 106 var ( 107 u *user.User 108 err error 109 ) 110 if os.Getuid() == 0 { 111 tryUsers := []string{"nobody"} 112 if testenv.Builder() != "" { 113 tryUsers = append(tryUsers, "gopher") 114 } 115 for _, name := range tryUsers { 116 u, err = user.Lookup(name) 117 if err == nil { 118 break 119 } 120 fmt.Fprintf(os.Stderr, "Lookup(%q): %v\n", name, err) 121 } 122 } 123 if u == nil { 124 // If we couldn't find an unprivileged user to run as, try running as 125 // the current user. (Empirically this still causes the call to Start to 126 // fail with a permission error if running as a non-root user on Linux.) 127 u, err = user.Current() 128 if err != nil { 129 fmt.Fprintln(os.Stderr, err) 130 os.Exit(1) 131 } 132 } 133 134 uid, err := strconv.ParseUint(u.Uid, 10, 32) 135 if err != nil { 136 fmt.Fprintf(os.Stderr, "invalid UID: %v\n", err) 137 os.Exit(1) 138 } 139 gid, err := strconv.ParseUint(u.Gid, 10, 32) 140 if err != nil { 141 fmt.Fprintf(os.Stderr, "invalid GID: %v\n", err) 142 os.Exit(1) 143 } 144 145 cmd := exec.Command(os.Args[0]) 146 cmd.Env = append(os.Environ(), 147 "GO_DEATHSIG_PARENT=", 148 "GO_DEATHSIG_CHILD=1", 149 ) 150 cmd.Stdin = os.Stdin 151 cmd.Stdout = os.Stdout 152 attrs := syscall.SysProcAttr{ 153 Pdeathsig: syscall.SIGUSR1, 154 Credential: &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}, 155 } 156 cmd.SysProcAttr = &attrs 157 158 fmt.Fprintf(os.Stderr, "starting process as user %q\n", u.Username) 159 if err := cmd.Start(); err != nil { 160 fmt.Fprintln(os.Stderr, err) 161 if testenv.SyscallIsNotSupported(err) { 162 fmt.Println("skip") 163 os.Exit(0) 164 } 165 os.Exit(1) 166 } 167 cmd.Wait() 168 os.Exit(0) 169 } 170 171 func deathSignalChild() { 172 c := make(chan os.Signal, 1) 173 signal.Notify(c, syscall.SIGUSR1) 174 go func() { 175 <-c 176 fmt.Println("ok") 177 os.Exit(0) 178 }() 179 fmt.Println("start") 180 181 buf := make([]byte, 32) 182 os.Stdin.Read(buf) 183 184 // We expected to be signaled before stdin closed 185 fmt.Println("not ok") 186 os.Exit(1) 187 }