carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "strings"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/rootstate"
11)
12
13func TestPlan14OperatorUnitPredecessorStop(t *testing.T) {
14 for _, attack := range []string{"none", "invocation", "cgroup", "inode", "dropin", "fragment", "missing property", "child remains", "lost stop"} {
15 t.Run(attack, func(t *testing.T) {
16 b, in, _ := plan14UnitBindingFixture(t, "console.service", "", "")
17 binding, err := b.establish(t.Context(), in.Host)
18 if err != nil {
19 t.Fatal(err)
20 }
21 invocation, group, state := "original", "/system.slice/console.service", "active"
22 id := rootstate.FileIdentity{Device: 1, Inode: 2}
23 populated, stops := true, 0
24 k := claimPredecessorKernel{group: func(string) (rootstate.FileIdentity, bool, error) { return id, populated, nil }}
25 k.ctl = func(_ context.Context, args ...string) (string, error) {
26 if args[0] == "stop" {
27 stops++
28 state = "inactive"
29 if attack != "child remains" {
30 populated = false
31 }
32 if attack == "lost stop" {
33 return "", os.ErrInvalid
34 }
35 return "", nil
36 }
37 out := fmt.Sprintf("ActiveState=%s\nInvocationID=%s\nControlGroup=%s\nFragmentPath=/etc/systemd/system/console.service\nDropInPaths=", state, invocation, group)
38 switch attack {
39 case "dropin":
40 out += "/etc/systemd/system/console.service.d/override.conf"
41 case "fragment":
42 out = strings.ReplaceAll(out, "/etc/systemd/system", "/run/systemd/system")
43 case "missing property":
44 out = strings.ReplaceAll(out, "\nDropInPaths=", "")
45 }
46 return out, nil
47 }
48 p, err := k.capture(t.Context(), binding)
49 if attack == "dropin" || attack == "fragment" || attack == "missing property" {
50 if err == nil || stops != 0 {
51 t.Fatal("bad definition accepted", err)
52 }
53 return
54 }
55 if err != nil {
56 t.Fatal(err)
57 }
58 switch attack {
59 case "invocation":
60 invocation = "successor"
61 case "cgroup":
62 group = "/system.slice/sibling.service"
63 case "inode":
64 id.Inode++
65 }
66 ctx, cancel := context.WithCancel(t.Context())
67 if attack == "child remains" {
68 original := k.ctl
69 k.ctl = func(ctx context.Context, args ...string) (string, error) {
70 out, e := original(ctx, args...)
71 if args[0] == "stop" {
72 cancel()
73 }
74 return out, e
75 }
76 }
77 defer cancel()
78 err = k.stop(ctx, p)
79 if attack == "none" {
80 if err != nil || stops != 1 {
81 t.Fatal(err, stops)
82 }
83 if err = k.stop(t.Context(), p); err != nil || stops != 1 {
84 t.Fatal("stop replay", err, stops)
85 }
86 } else if err == nil {
87 t.Fatal("unsafe stop accepted")
88 }
89 if (attack == "invocation" || attack == "cgroup" || attack == "inode") && stops != 0 {
90 t.Fatal("stopped successor")
91 }
92 if attack == "lost stop" {
93 if err = k.stop(t.Context(), p); err != nil || stops != 1 {
94 t.Fatal("lost stop replay", err, stops)
95 }
96 }
97 })
98 }
99}
100
101func TestPlan14PredecessorDeviceAcrossBoot(t *testing.T) {
102 p := claimUnitPredecessor{Unit: "console.service", Invocation: "before", Group: "system.slice/console.service", GroupIdentity: rootstate.FileIdentity{Device: 1, Inode: 2}, Boot: "boot-before"}
103 state := "inactive"
104 calls := 0
105 k := claimPredecessorKernel{
106 boot: func() (string, error) { return "boot-after", nil },
107 group: func(string) (rootstate.FileIdentity, bool, error) {
108 return rootstate.FileIdentity{Device: 8, Inode: 9}, state == "active", nil
109 },
110 ctl: func(_ context.Context, args ...string) (string, error) {
111 if args[0] != "show" {
112 calls++
113 return "", nil
114 }
115 return "ActiveState=" + state + "\nInvocationID=\nControlGroup=\nFragmentPath=/etc/systemd/system/console.service\nDropInPaths=\n", nil
116 },
117 }
118 if err := k.stop(t.Context(), p); err != nil {
119 t.Fatal("old boot empty group refused", err)
120 }
121 state = "active"
122 if err := k.stop(t.Context(), p); err == nil {
123 t.Fatal("old boot authorized stopping new population")
124 }
125 if calls != 0 {
126 t.Fatal("systemctl stop crossed a boot")
127 }
128}
129