carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package host
4
5import (
6 "github.com/carlosframework/platform/internal/release"
7 "os"
8 "os/exec"
9 "path/filepath"
10 "strconv"
11 "strings"
12 "testing"
13)
14
15func TestPlan14GenerationKernelRunningExecutable(t *testing.T) {
16 for _, change := range []string{"running", "deleted", "parent-renamed"} {
17 t.Run(change, func(t *testing.T) {
18 f := generationProc(t)
19 b := f.reservation.Binding
20 body, err := os.ReadFile("/bin/sleep")
21 if err != nil {
22 t.Fatal(err)
23 }
24 if err := os.WriteFile(b.Path, body, 0700); err != nil {
25 t.Fatal(err)
26 }
27 if err := os.Chmod(b.Path, 0700); err != nil {
28 t.Fatal(err)
29 }
30 cmd := exec.Command(b.Path, "60")
31 cmd.Dir = t.TempDir()
32 if err := cmd.Start(); err != nil {
33 t.Fatal(err)
34 }
35 joined := false
36 defer func() {
37 if !joined {
38 _ = cmd.Process.Kill()
39 _ = cmd.Wait()
40 }
41 }()
42 proc := filepath.Join("/proc", strconv.Itoa(cmd.Process.Pid))
43 raw, err := generationRead(proc + "/stat")
44 if err != nil {
45 t.Fatal(err)
46 }
47 start, err := generationStart(raw)
48 if err != nil {
49 t.Fatal(err)
50 }
51 // The copied binary kept the staged inode. Read the real proc link, not
52 // the fake launch fixture's claim that it observed a process.
53 generationRelink(t, proc+"/exe", f.task+"/exe")
54 switch change {
55 case "deleted":
56 if err := os.Remove(b.Path); err != nil {
57 t.Fatal(err)
58 }
59 case "parent-renamed":
60 if err := os.Rename(b.Tree, b.Tree+".old"); err != nil {
61 t.Fatal(err)
62 }
63 }
64 if err := generationLinkRef(proc+"/exe", b); err == nil {
65 t.Fatal("running executable not found")
66 }
67 if _, err := (generationKernel{proc: f.proc}).absent(t.Context(), f.reservation); err == nil {
68 t.Fatal("running tenant released")
69 }
70 actual := b
71 actual.ControllerPID = cmd.Process.Pid
72 actual.ControllerStart = start
73 actual.Target.BootID, err = (generationKernel{proc: "/proc"}).boot()
74 if err != nil {
75 t.Fatal(err)
76 }
77 if err := (generationKernel{proc: "/proc"}).controllerGone(t.Context(), actual); err == nil {
78 t.Fatal("live process reported joined")
79 }
80 if err := cmd.Process.Kill(); err != nil {
81 t.Fatal(err)
82 }
83 _ = cmd.Wait()
84 joined = true
85 if err := (generationKernel{proc: "/proc"}).controllerGone(t.Context(), actual); err != nil {
86 t.Fatal(err)
87 }
88 generationRelink(t, f.unrelated, f.task+"/exe")
89 _, err = (generationKernel{proc: f.proc}).absent(t.Context(), f.reservation)
90 if (err == nil) != (change != "parent-renamed") {
91 t.Fatalf("after join: %v", err)
92 }
93 })
94 }
95}
96
97func TestPlan14GenerationKernelCgroup(t *testing.T) {
98 for _, attack := range []string{"absent", "empty", "populated", "replaced", "unknown-child", "pending-present", "unreadable"} {
99 t.Run(attack, func(t *testing.T) {
100 f, d, _ := generationLaunchFixture(t)
101 if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil {
102 t.Fatal(err)
103 }
104 r := readGenerationLaunch(t, d)
105 boot, err := os.ReadFile("/proc/sys/kernel/random/boot_id")
106 if err != nil {
107 t.Fatal(err)
108 }
109 r.Binding.Target.BootID = strings.TrimSpace(string(boot))
110 root := t.TempDir()
111 group := filepath.Join(root, r.Process.Group)
112 if attack != "absent" {
113 if err := os.MkdirAll(group, 0700); err != nil {
114 t.Fatal(err)
115 }
116 id, err := stopIdentity(group)
117 if err != nil {
118 t.Fatal(err)
119 }
120 r.Process.GroupIdentity.Device, r.Process.GroupIdentity.Inode = id.Device, id.Inode
121 generationWrite(t, group+"/cgroup.events", "populated 0\n")
122 }
123 switch attack {
124 case "populated":
125 generationWrite(t, group+"/cgroup.events", "populated 1\n")
126 case "replaced":
127 r.Process.GroupIdentity.Inode++
128 case "unknown-child":
129 if err := os.Mkdir(group+"/child", 0700); err != nil {
130 t.Fatal(err)
131 }
132 case "pending-present":
133 r.Process = release.GenerationProcess{}
134 case "unreadable":
135 if err := os.Remove(group + "/cgroup.events"); err != nil {
136 t.Fatal(err)
137 }
138 }
139 err = generationGroupGone(t.Context(), root, r)
140 allow := attack == "absent" || attack == "empty"
141 if (err == nil) != allow {
142 t.Fatalf("group check=%v allow=%t", err, allow)
143 }
144 })
145 }
146}
147