github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/root/runsc_test.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package root 16 17 import ( 18 "bytes" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "os/exec" 23 "path/filepath" 24 "strconv" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/cenkalti/backoff" 30 "golang.org/x/sys/unix" 31 "github.com/SagerNet/gvisor/pkg/test/testutil" 32 "github.com/SagerNet/gvisor/runsc/specutils" 33 ) 34 35 // TestDoKill checks that when "runsc do..." is killed, the sandbox process is 36 // also terminated. This ensures that parent death signal is propagate to the 37 // sandbox process correctly. 38 func TestDoKill(t *testing.T) { 39 // Make the sandbox process be reparented here when it's killed, so we can 40 // wait for it. 41 if err := unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); err != nil { 42 t.Fatalf("prctl(PR_SET_CHILD_SUBREAPER): %v", err) 43 } 44 45 cmd := exec.Command(specutils.ExePath, "do", "sleep", "10000") 46 buf := &bytes.Buffer{} 47 cmd.Stdout = buf 48 cmd.Stderr = buf 49 cmd.Start() 50 51 var pid int 52 findSandbox := func() error { 53 var err error 54 pid, err = sandboxPid(cmd.Process.Pid) 55 if err != nil { 56 return &backoff.PermanentError{Err: err} 57 } 58 if pid == 0 { 59 return fmt.Errorf("sandbox process not found") 60 } 61 return nil 62 } 63 if err := testutil.Poll(findSandbox, 10*time.Second); err != nil { 64 t.Fatalf("failed to find sandbox: %v", err) 65 } 66 t.Logf("Found sandbox, pid: %d", pid) 67 68 if err := cmd.Process.Kill(); err != nil { 69 t.Fatalf("failed to kill run process: %v", err) 70 } 71 cmd.Wait() 72 t.Logf("Parent process killed (%d). Output: %s", cmd.Process.Pid, buf.String()) 73 74 ch := make(chan struct{}) 75 go func() { 76 defer func() { ch <- struct{}{} }() 77 t.Logf("Waiting for sandbox process (%d) termination", pid) 78 if _, err := unix.Wait4(pid, nil, 0, nil); err != nil { 79 t.Errorf("error waiting for sandbox process (%d): %v", pid, err) 80 } 81 }() 82 select { 83 case <-ch: 84 // Done 85 case <-time.After(5 * time.Second): 86 t.Fatalf("timeout waiting for sandbox process (%d) to exit", pid) 87 } 88 } 89 90 // sandboxPid looks for the sandbox process inside the process tree starting 91 // from "pid". It returns 0 and no error if no sandbox process is found. It 92 // returns error if anything failed. 93 func sandboxPid(pid int) (int, error) { 94 cmd := exec.Command("pgrep", "-P", strconv.Itoa(pid)) 95 buf := &bytes.Buffer{} 96 cmd.Stdout = buf 97 if err := cmd.Start(); err != nil { 98 return 0, err 99 } 100 ps, err := cmd.Process.Wait() 101 if err != nil { 102 return 0, err 103 } 104 if ps.ExitCode() == 1 { 105 // pgrep returns 1 when no process is found. 106 return 0, nil 107 } 108 109 var children []int 110 for _, line := range strings.Split(buf.String(), "\n") { 111 if len(line) == 0 { 112 continue 113 } 114 child, err := strconv.Atoi(line) 115 if err != nil { 116 return 0, err 117 } 118 119 cmdline, err := ioutil.ReadFile(filepath.Join("/proc", line, "cmdline")) 120 if err != nil { 121 if os.IsNotExist(err) { 122 // Raced with process exit. 123 continue 124 } 125 return 0, err 126 } 127 args := strings.SplitN(string(cmdline), "\x00", 2) 128 if len(args) == 0 { 129 return 0, fmt.Errorf("malformed cmdline file: %q", cmdline) 130 } 131 // The sandbox process has the first argument set to "runsc-sandbox". 132 if args[0] == "runsc-sandbox" { 133 return child, nil 134 } 135 136 children = append(children, child) 137 } 138 139 // Sandbox process wasn't found, try another level down. 140 for _, pid := range children { 141 sand, err := sandboxPid(pid) 142 if err != nil { 143 return 0, err 144 } 145 if sand != 0 { 146 return sand, nil 147 } 148 // Not found, continue the search. 149 } 150 return 0, nil 151 }