zdd plan 2 final fix: ParentLimit refuses when there is no cgroup manager
With containment on and no cgroup manager, a flat or foreign ParentLimit answered nil through the manager's nil-safe SetParentLimit, so a swap could admit an overlap with no parent raised and no generation child to kill. The backend now refuses with cgroup.ErrNoDelegation; the manager's own nil-safe contract is kept for its other callers. Final review finding L2 (triage T10 nil-manager item).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 files changed,
+18
−0
internal/activator/containment.go+6 −0internal/activator/containment_test.go+12 −0
diff --git a/internal/activator/containment.go b/internal/activator/containment.go| index 42e4e3809..ec0a824ec 100644 |
| --- a/internal/activator/containment.go |
| +++ b/internal/activator/containment.go |
| @@ -160,6 +160,12 @@ func (b *ExecBackend) ParentLimit(ctx context.Context, rt registry.Route, l cgro |
| case LayoutFenced, LayoutConflict: |
| return fmt.Errorf("%s: refusing to set a parent limit: %s", rt.Host, p.reason) |
| } |
| + // The manager's SetParentLimit answers nil with no manager; here that |
| + // would admit an overlap with no parent raised and no generation child |
| + // to kill, so it refuses. |
| + if b.Cgroups == nil { |
| + return cgroup.ErrNoDelegation |
| + } |
| return b.Cgroups.SetParentLimit(rt.Host, l) |
| } |
diff --git a/internal/activator/containment_test.go b/internal/activator/containment_test.go| index 8885b523a..a66c53e47 100644 |
| --- a/internal/activator/containment_test.go |
| +++ b/internal/activator/containment_test.go |
| @@ -159,6 +159,18 @@ func TestParentLimitFlatWritesTheParent(t *testing.T) { |
| } |
| } |
| +// No cgroup manager (no delegation): there is no parent to raise, so a |
| +// flat ParentLimit refuses rather than let a swap admit an overlap with |
| +// nothing raised. |
| +func TestParentLimitFlatRefusesWithoutACgroupManager(t *testing.T) { |
| + b := testExecBackend(t) |
| + b.GenContainment = true |
| + b.Cgroups = nil |
| + if err := b.ParentLimit(context.Background(), registry.Route{Host: "h.example.com"}, cgroup.OverlapLimit(cgroup.TierDefault, cgroup.TierDefault)); !errors.Is(err, cgroup.ErrNoDelegation) { |
| + t.Fatalf("ParentLimit with no cgroup manager: %v, want cgroup.ErrNoDelegation", err) |
| + } |
| +} |
| + |
| // A fixture is not cgroup v2: KillGen must refuse rather than claim an |
| // empty child it never proved. |
| func TestKillGenFlatRefusesWithoutCgroupV2(t *testing.T) { |