mirror of
https://github.com/moby/moby.git
synced 2026-08-03 22:51:03 +00:00
d/libn/i/nftables: free nft_ctx when unreachable
The libnftables context is C-allocated memory which is only freed when nftCtx.Close() is called. Attach a runtime cleanup so that a context which is dropped without being closed is freed once it becomes unreachable, instead of being leaked for the lifetime of the process. runtime.AddCleanup needs a pointer to a Go-heap object to attach to, so wrap the libnftables handle in a Go struct rather than type-defining the C struct. Keep the nftCtx reachable across the C calls in Apply(), and across Cleanup.Stop() in Close(): a receiver may otherwise become unreachable at its last mention, and Stop() has no effect once the cleanup has already been queued for execution. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/containerd/log"
|
||||
@@ -24,7 +25,13 @@ func preflight() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type nftCtx C.struct_nft_ctx
|
||||
// nftCtx owns a libnftables context. The context is freed by [nftCtx.Close],
|
||||
// or by a runtime cleanup if the nftCtx becomes unreachable without being
|
||||
// closed.
|
||||
type nftCtx struct {
|
||||
handle *C.struct_nft_ctx
|
||||
cleanup runtime.Cleanup
|
||||
}
|
||||
|
||||
// Apply calls libnftables to execute the nftables commands in nftCmd.
|
||||
func (h *nftCtx) Apply(ctx context.Context, nftCmd []byte) error {
|
||||
@@ -34,9 +41,12 @@ func (h *nftCtx) Apply(ctx context.Context, nftCmd []byte) error {
|
||||
cCmd := C.CString(string(nftCmd))
|
||||
defer C.free(unsafe.Pointer(cCmd))
|
||||
|
||||
ret := C.nft_run_cmd_from_buffer((*C.struct_nft_ctx)(h), cCmd)
|
||||
stdout := C.GoString(C.nft_ctx_get_output_buffer((*C.struct_nft_ctx)(h)))
|
||||
stderr := C.GoString(C.nft_ctx_get_error_buffer((*C.struct_nft_ctx)(h)))
|
||||
ret := C.nft_run_cmd_from_buffer(h.handle, cCmd)
|
||||
stdout := C.GoString(C.nft_ctx_get_output_buffer(h.handle))
|
||||
stderr := C.GoString(C.nft_ctx_get_error_buffer(h.handle))
|
||||
// Keep h reachable until libnftables is done with its context, so that the
|
||||
// cleanup can't free it out from under these calls.
|
||||
runtime.KeepAlive(h)
|
||||
if ret != 0 {
|
||||
return fmt.Errorf("libnftables: failed to apply commands (code %d), stderr: %s", int(ret), stderr)
|
||||
}
|
||||
@@ -60,9 +70,22 @@ func newNftCtx() (_ *nftCtx, retErr error) {
|
||||
if ret := C.nft_ctx_buffer_error(handle); ret != 0 {
|
||||
return nil, fmt.Errorf("libnftables: failed to set error buffer (code %d)", int(ret))
|
||||
}
|
||||
return (*nftCtx)(handle), nil
|
||||
h := &nftCtx{handle: handle}
|
||||
h.cleanup = runtime.AddCleanup(h, func(handle *C.struct_nft_ctx) {
|
||||
C.nft_ctx_free(handle)
|
||||
}, handle)
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// Close frees the libnftables context. It is idempotent, but h must not be used
|
||||
// for anything else after it's been closed.
|
||||
func (h *nftCtx) Close() {
|
||||
C.nft_ctx_free((*C.struct_nft_ctx)(h))
|
||||
h.cleanup.Stop()
|
||||
if h.handle != nil {
|
||||
C.nft_ctx_free(h.handle)
|
||||
h.handle = nil
|
||||
}
|
||||
// Stop only cancels the cleanup if h hasn't already become unreachable, so h
|
||||
// must be kept alive across the call to avoid a double free.
|
||||
runtime.KeepAlive(h)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user