Source file
src/syscall/exec_pdeathsig_test.go
1
2
3
4
5
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
26
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
33
34
35
36
37 tempDir := t.TempDir()
38 os.Chmod(tempDir, 0755)
39
40 tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0]))
41
42 src, err := os.Open(os.Args[0])
43 if err != nil {
44 t.Fatalf("cannot open binary %q, %v", os.Args[0], err)
45 }
46 defer src.Close()
47
48 dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
49 if err != nil {
50 t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err)
51 }
52 if _, err := io.Copy(dst, src); err != nil {
53 t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err)
54 }
55 err = dst.Close()
56 if err != nil {
57 t.Fatalf("failed to close test binary %q, %v", tmpBinary, err)
58 }
59
60 cmd := testenv.Command(t, tmpBinary)
61 cmd.Env = append(cmd.Environ(), "GO_DEATHSIG_PARENT=1")
62 chldStdin, err := cmd.StdinPipe()
63 if err != nil {
64 t.Fatalf("failed to create new stdin pipe: %v", err)
65 }
66 chldStdout, err := cmd.StdoutPipe()
67 if err != nil {
68 t.Fatalf("failed to create new stdout pipe: %v", err)
69 }
70 stderr := new(strings.Builder)
71 cmd.Stderr = stderr
72
73 err = cmd.Start()
74 defer func() {
75 chldStdin.Close()
76 cmd.Wait()
77 if stderr.Len() > 0 {
78 t.Logf("stderr:\n%s", stderr)
79 }
80 }()
81 if err != nil {
82 t.Fatalf("failed to start first child process: %v", err)
83 }
84
85 chldPipe := bufio.NewReader(chldStdout)
86
87 if got, err := chldPipe.ReadString('\n'); got == "start\n" {
88 syscall.Kill(cmd.Process.Pid, syscall.SIGTERM)
89
90 want := "ok\n"
91 if got, err = chldPipe.ReadString('\n'); got != want {
92 t.Fatalf("expected %q, received %q, %v", want, got, err)
93 }
94 } else if got == "skip\n" {
95 t.Skipf("skipping: parent could not run child program as selected user")
96 } else {
97 t.Fatalf("did not receive start from child, received %q, %v", got, err)
98 }
99 }
100
101 func deathSignalParent() {
102 var (
103 u *user.User
104 err error
105 )
106 if os.Getuid() == 0 {
107 tryUsers := []string{"nobody"}
108 if testenv.Builder() != "" {
109 tryUsers = append(tryUsers, "gopher")
110 }
111 for _, name := range tryUsers {
112 u, err = user.Lookup(name)
113 if err == nil {
114 break
115 }
116 fmt.Fprintf(os.Stderr, "Lookup(%q): %v\n", name, err)
117 }
118 }
119 if u == nil {
120
121
122
123 u, err = user.Current()
124 if err != nil {
125 fmt.Fprintln(os.Stderr, err)
126 os.Exit(1)
127 }
128 }
129
130 uid, err := strconv.ParseUint(u.Uid, 10, 32)
131 if err != nil {
132 fmt.Fprintf(os.Stderr, "invalid UID: %v\n", err)
133 os.Exit(1)
134 }
135 gid, err := strconv.ParseUint(u.Gid, 10, 32)
136 if err != nil {
137 fmt.Fprintf(os.Stderr, "invalid GID: %v\n", err)
138 os.Exit(1)
139 }
140
141 cmd := exec.Command(os.Args[0])
142 cmd.Env = append(os.Environ(),
143 "GO_DEATHSIG_PARENT=",
144 "GO_DEATHSIG_CHILD=1",
145 )
146 cmd.Stdin = os.Stdin
147 cmd.Stdout = os.Stdout
148 attrs := syscall.SysProcAttr{
149 Pdeathsig: syscall.SIGUSR1,
150 Credential: &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)},
151 }
152 cmd.SysProcAttr = &attrs
153
154 fmt.Fprintf(os.Stderr, "starting process as user %q\n", u.Username)
155 if err := cmd.Start(); err != nil {
156 fmt.Fprintln(os.Stderr, err)
157 if testenv.SyscallIsNotSupported(err) {
158 fmt.Println("skip")
159 os.Exit(0)
160 }
161 os.Exit(1)
162 }
163 cmd.Wait()
164 os.Exit(0)
165 }
166
167 func deathSignalChild() {
168 c := make(chan os.Signal, 1)
169 signal.Notify(c, syscall.SIGUSR1)
170 go func() {
171 <-c
172 fmt.Println("ok")
173 os.Exit(0)
174 }()
175 fmt.Println("start")
176
177 buf := make([]byte, 32)
178 os.Stdin.Read(buf)
179
180
181 fmt.Println("not ok")
182 os.Exit(1)
183 }
184
View as plain text