mirror of
https://github.com/moby/moby.git
synced 2026-08-03 06:30:59 +00:00
Table.Close() had a value receiver, so setting t.t = nil only modified the callee's copy of the handle. The caller's Table was left looking valid: t, _ := nftables.NewTable(...) t.Close() t.IsValid() // true Worse, Close() only dropped the nftables handle, and nftApply() opens a new one whenever it finds none. So Apply() and Reload() on a closed table silently reopened a handle and carried on updating the ruleset. At the root of it, a Table looked like a plain value but behaved like a reference, and nothing stopped it from being copied. So embed table in Table by value and hand out *Table instead. The Table/table split is still needed - table's fields have to be exported for text/template - but reference semantics are now visible at every call site, and they're enforced: because table contains a sync.Mutex, "go vet" reports both a copy of a Table and a method or function that takes one by value, so the shape of this bug is no longer expressible. Close() therefore can't invalidate the table by clearing a pointer, it has to record the state. Add a closed flag, and refuse to open a new nftables handle for a table that's been closed. Apply() checked neither for a closed table nor for a nil *Table, which would have panicked. It now reports an error, checking the closed state with applyLock held so that it can't race with Close(), and before the in-memory table is touched so that a rejected update isn't recorded as applied. The invalid table is now a nil *Table rather than a zero-value Table, which also removes the need for consumers to return an empty Table alongside an error. That made it obvious that the nftabler was leaking the table it had just created when it gave up on setting up IPv6, so close it. Signed-off-by: Cory Snider <csnider@mirantis.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>