gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "context" 20 "fmt" 21 "io/ioutil" 22 "os" 23 "os/exec" 24 "path/filepath" 25 "strconv" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/cenkalti/backoff" 31 "golang.org/x/sys/unix" 32 "gvisor.dev/gvisor/pkg/test/dockerutil" 33 "gvisor.dev/gvisor/pkg/test/testutil" 34 "gvisor.dev/gvisor/runsc/specutils" 35 ) 36 37 // TestDoKill checks that when "runsc do..." is killed, the sandbox process is 38 // also terminated. This ensures that parent death signal is propagate to the 39 // sandbox process correctly. 40 func TestDoKill(t *testing.T) { 41 // Make the sandbox process be reparented here when it's killed, so we can 42 // wait for it. 43 if err := unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); err != nil { 44 t.Fatalf("prctl(PR_SET_CHILD_SUBREAPER): %v", err) 45 } 46 47 cmd := exec.Command(specutils.ExePath, "do", "sleep", "10000") 48 buf := &bytes.Buffer{} 49 cmd.Stdout = buf 50 cmd.Stderr = buf 51 cmd.Start() 52 53 var pid int 54 findSandbox := func() error { 55 var err error 56 pid, err = sandboxPid(cmd.Process.Pid) 57 if err != nil { 58 return &backoff.PermanentError{Err: err} 59 } 60 if pid == 0 { 61 return fmt.Errorf("sandbox process not found") 62 } 63 return nil 64 } 65 if err := testutil.Poll(findSandbox, 10*time.Second); err != nil { 66 t.Fatalf("failed to find sandbox: %v", err) 67 } 68 t.Logf("Found sandbox, pid: %d", pid) 69 70 if err := cmd.Process.Kill(); err != nil { 71 t.Fatalf("failed to kill run process: %v", err) 72 } 73 cmd.Wait() 74 t.Logf("Parent process killed (%d). Output: %s", cmd.Process.Pid, buf.String()) 75 76 ch := make(chan struct{}) 77 go func() { 78 defer func() { ch <- struct{}{} }() 79 t.Logf("Waiting for sandbox process (%d) termination", pid) 80 if _, err := unix.Wait4(pid, nil, 0, nil); err != nil { 81 t.Errorf("error waiting for sandbox process (%d): %v", pid, err) 82 } 83 }() 84 select { 85 case <-ch: 86 // Done 87 case <-time.After(5 * time.Second): 88 t.Fatalf("timeout waiting for sandbox process (%d) to exit", pid) 89 } 90 } 91 92 // sandboxPid looks for the sandbox process inside the process tree starting 93 // from "pid". It returns 0 and no error if no sandbox process is found. It 94 // returns error if anything failed. 95 func sandboxPid(pid int) (int, error) { 96 cmd := exec.Command("pgrep", "-P", strconv.Itoa(pid)) 97 buf := &bytes.Buffer{} 98 cmd.Stdout = buf 99 if err := cmd.Start(); err != nil { 100 return 0, err 101 } 102 ps, err := cmd.Process.Wait() 103 if err != nil { 104 return 0, err 105 } 106 if ps.ExitCode() == 1 { 107 // pgrep returns 1 when no process is found. 108 return 0, nil 109 } 110 111 var children []int 112 for _, line := range strings.Split(buf.String(), "\n") { 113 if len(line) == 0 { 114 continue 115 } 116 child, err := strconv.Atoi(line) 117 if err != nil { 118 return 0, err 119 } 120 121 cmdline, err := ioutil.ReadFile(filepath.Join("/proc", line, "cmdline")) 122 if err != nil { 123 if os.IsNotExist(err) { 124 // Raced with process exit. 125 continue 126 } 127 return 0, err 128 } 129 args := strings.SplitN(string(cmdline), "\x00", 2) 130 if len(args) == 0 { 131 return 0, fmt.Errorf("malformed cmdline file: %q", cmdline) 132 } 133 // The sandbox process has the first argument set to "runsc-sandbox". 134 if args[0] == "runsc-sandbox" { 135 return child, nil 136 } 137 138 children = append(children, child) 139 } 140 141 // Sandbox process wasn't found, try another level down. 142 for _, pid := range children { 143 sand, err := sandboxPid(pid) 144 if err != nil { 145 return 0, err 146 } 147 if sand != 0 { 148 return sand, nil 149 } 150 // Not found, continue the search. 151 } 152 return 0, nil 153 } 154 155 // Tests that the sandbox process is running with no environment variables. We 156 // don't want to leak any env vars from the caller to the sandbox process. 157 func TestSandboxProcessEnv(t *testing.T) { 158 ctx := context.Background() 159 d := dockerutil.MakeContainer(ctx, t) 160 defer d.CleanUp(ctx) 161 162 runOpts := dockerutil.RunOpts{Image: "basic/alpine"} 163 if err := d.Spawn(ctx, runOpts, "sleep", "infinity"); err != nil { 164 t.Fatalf("docker run failed: %v", err) 165 } 166 167 pid, err := d.SandboxPid(ctx) 168 if err != nil { 169 t.Fatal(err) 170 } 171 got, err := os.ReadFile(fmt.Sprintf("/proc/%d/environ", pid)) 172 if err != nil { 173 t.Fatal(err) 174 } 175 if len(got) != 0 { 176 t.Errorf("sandbox process's environment is not empty: got %s", string(got)) 177 } 178 }