| 1 | //go:build linux |
| 2 | |
| 3 | package host |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| 7 | "os" |
| 8 | "path/filepath" |
| 9 | "testing" |
| 10 | ) |
| 11 | |
| 12 | func TestPlan14GenerationKernelRetainedNamespaceMount(t *testing.T) { |
| 13 | for _, fs := range []string{"tmpfs", "nsfs"} { |
| 14 | t.Run(fs, func(t *testing.T) { |
| 15 | f := generationProc(t) |
| 16 | b := f.reservation.Binding |
| 17 | mounts := fmt.Sprintf("1 0 %d:%d / / rw - tmpfs tmpfs rw\n", generationMajor(b.TreeIdentity.Device), generationMinor(b.TreeIdentity.Device)) |
| 18 | // A namespace bind mount survives its last task and descriptor. |
| 19 | // Its device differs from the release tree and its path names no build. |
| 20 | mounts += fmt.Sprintf("2 1 0:999 / /retained-namespace rw shared:7 - %s %s rw\n", fs, fs) |
| 21 | generationWrite(t, f.task+"/mountinfo", mounts) |
| 22 | k := generationKernel{proc: f.proc} |
| 23 | if _, err := k.absent(t.Context(), f.reservation); (err == nil) != (fs == "tmpfs") { |
| 24 | t.Errorf("retirement with %s mount: %v", fs, err) |
| 25 | } |
| 26 | tree := filepath.Dir(filepath.Dir(b.Tree)) |
| 27 | if _, _, err := k.census(t.Context(), tree); (err == nil) != (fs == "tmpfs") { |
| 28 | t.Errorf("completeness with %s mount: %v", fs, err) |
| 29 | } |
| 30 | }) |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | func TestPlan14GenerationKernelCensusUnknownReference(t *testing.T) { |
| 35 | for _, attack := range []string{"none", "unreserved-executable", "unreserved-sibling", "uncontained", "namespace"} { |
| 36 | t.Run(attack, func(t *testing.T) { |
| 37 | f := generationProc(t) |
| 38 | tree := filepath.Dir(filepath.Dir(f.reservation.Binding.Tree)) |
| 39 | other := filepath.Join(tree, "unreserved", "other") |
| 40 | if err := os.MkdirAll(filepath.Dir(other), 0700); err != nil { |
| 41 | t.Fatal(err) |
| 42 | } |
| 43 | generationWrite(t, other, "unknown build") |
| 44 | switch attack { |
| 45 | case "unreserved-executable": |
| 46 | generationRelink(t, other, f.task+"/exe") |
| 47 | case "unreserved-sibling": |
| 48 | generationLink(t, other, f.task+"/fd/7") |
| 49 | case "uncontained": |
| 50 | generationWrite(t, f.task+"/status", "Uid:\t1001\t1001\t1001\t1001\nKthread:\t0\n") |
| 51 | case "namespace": |
| 52 | generationRelink(t, "foreign", f.task+"/ns/mnt") |
| 53 | } |
| 54 | boot, digest, err := (generationKernel{proc: f.proc}).census(t.Context(), tree) |
| 55 | if attack == "none" { |
| 56 | if err != nil || boot == "" || len(digest) != 64 { |
| 57 | t.Fatalf("positive: %s %s %v", boot, digest, err) |
| 58 | } |
| 59 | } else if err == nil { |
| 60 | t.Fatal("unknown kernel obligation certified") |
| 61 | } |
| 62 | }) |
| 63 | } |
| 64 | } |
| 65 | |