//go:build linux package instancectl import ( "context" "fmt" "net" "os" "path/filepath" "testing" "time" ) func TestPlan14ExecPeerNamespaceTransition(t *testing.T) { for _, attack := range []string{"none", "invocation", "group", "peer", "state"} { t.Run(attack, func(t *testing.T) { dir, err := os.MkdirTemp("", "ep") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) ln, err := net.Listen("unix", filepath.Join(dir, "s")) if err != nil { t.Fatal(err) } defer ln.Close() client, err := net.Dial("unix", filepath.Join(dir, "s")) if err != nil { t.Fatal(err) } defer client.Close() server, err := ln.Accept() if err != nil { t.Fatal(err) } defer server.Close() s := ClaimUnitStarted{Unit: "console.service", InvocationID: "inv", ControlGroup: "group", MainPID: os.Getpid() + 10000} calls := 0 u := ClaimUnits{Poll: time.Millisecond, Systemctl: func(context.Context, ...string) (string, error) { calls++ pid, inv, group, state := os.Getpid(), "inv", "/group", "active" if calls == 1 || attack == "peer" { pid += 10000 } switch attack { case "invocation": inv = "replacement" case "group": group = "/sibling" case "state": state = "failed" } return fmt.Sprintf("MainPID=%d\nInvocationID=%s\nControlGroup=%s\nActiveState=%s\n", pid, inv, group, state), nil }} ctx, cancel := context.WithTimeout(t.Context(), 50*time.Millisecond) defer cancel() err = u.pinExecPeer(ctx, server, &s) if attack == "none" { if err != nil || s.MainPID != os.Getpid() || calls < 2 { t.Fatal(s, calls, err) } } else if err == nil { t.Fatal("accepted changed identity", attack) } }) } }