Skip to content

Commit dad18d7

Browse files
committed
all: use runtime.AddCleanup
Convert all uses of runtime.SetFinalizer to runtime.AddCleanup. The exception is the unsafe memory code, which cannot currently be converted due to fundamental differences with finalizers. Cleanups cannot carry references to the object in question, and cannot resurrect the object, meaning the original object's allocation can be used to service new allocations before the cleanup has run. In our case, this means bpf map memory gets zeroed by the allocator, which is obviously a show stopper. If we want to move off finalizers, we need to implement golang/go#70224 first and switch to that instead. Signed-off-by: Timo Beckers <[email protected]> Suggested-by: Lorenz Bauer <[email protected]>
1 parent 604453a commit dad18d7

File tree

14 files changed

+78
-54
lines changed

14 files changed

+78
-54
lines changed

btf/kernel.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,9 @@ func loadKernelSpec() (*Spec, error) {
151151
return nil, fmt.Errorf("load vmlinux: %w", err)
152152
}
153153

154-
runtime.SetFinalizer(spec.decoder.sharedBuf, func(_ *sharedBuf) {
155-
_ = unix.Munmap(raw)
156-
})
154+
runtime.AddCleanup(spec.decoder.sharedBuf, func(b []byte) {
155+
_ = unix.Munmap(b)
156+
}, raw)
157157

158158
return spec, nil
159159
}

internal/epoll/poller.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type Poller struct {
3434
eventMu sync.Mutex
3535
closeEvent *eventFd
3636
flushEvent *eventFd
37+
cleanup runtime.Cleanup
3738
}
3839

3940
func New() (_ *Poller, err error) {
@@ -75,7 +76,10 @@ func New() (_ *Poller, err error) {
7576
return nil, fmt.Errorf("add flush eventfd: %w", err)
7677
}
7778

78-
runtime.SetFinalizer(p, (*Poller).Close)
79+
p.cleanup = runtime.AddCleanup(p, func(raw int) {
80+
_ = unix.Close(raw)
81+
}, p.epollFd)
82+
7983
return p, nil
8084
}
8185

@@ -84,7 +88,7 @@ func New() (_ *Poller, err error) {
8488
// Interrupts any calls to Wait. Multiple calls to Close are valid, but subsequent
8589
// calls will return os.ErrClosed.
8690
func (p *Poller) Close() error {
87-
runtime.SetFinalizer(p, nil)
91+
p.cleanup.Stop()
8892

8993
// Interrupt Wait() via the closeEvent fd if it's currently blocked.
9094
if err := p.wakeWaitForClose(); err != nil {

internal/sys/fd.go

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,14 @@ const invalidFd = -1
2121
func newFD(value int) *FD {
2222
testmain.TraceFD(value, 1)
2323

24-
fd := &FD{value}
25-
runtime.SetFinalizer(fd, (*FD).finalize)
24+
fd := &FD{raw: value}
25+
fd.cleanup = runtime.AddCleanup(fd, func(raw int) {
26+
testmain.LeakFD(raw)
27+
_ = unix.Close(raw)
28+
}, fd.raw)
2629
return fd
2730
}
2831

29-
// finalize is set as the FD's runtime finalizer and
30-
// sends a leak trace before calling FD.Close().
31-
func (fd *FD) finalize() {
32-
if fd.raw == invalidFd {
33-
return
34-
}
35-
36-
testmain.LeakFD(fd.raw)
37-
38-
_ = fd.Close()
39-
}
40-
4132
func (fd *FD) String() string {
4233
return strconv.FormatInt(int64(fd.raw), 10)
4334
}
@@ -63,6 +54,6 @@ func (fd *FD) Disown() int {
6354
testmain.ForgetFD(value)
6455
fd.raw = invalidFd
6556

66-
runtime.SetFinalizer(fd, nil)
57+
fd.cleanup.Stop()
6758
return value
6859
}

internal/sys/fd_other.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ package sys
55
import (
66
"fmt"
77
"os"
8+
"runtime"
89

910
"github.com/cilium/ebpf/internal/unix"
1011
)
1112

1213
type FD struct {
13-
raw int
14+
raw int
15+
cleanup runtime.Cleanup
1416
}
1517

1618
// NewFD wraps a raw fd with a finalizer.

internal/sys/fd_windows.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package sys
33
import (
44
"fmt"
55
"os"
6+
"runtime"
67

78
"github.com/cilium/ebpf/internal"
89
"github.com/cilium/ebpf/internal/efw"
@@ -12,7 +13,8 @@ import (
1213
//
1314
// It is not equivalent to a real file descriptor or handle.
1415
type FD struct {
15-
raw int
16+
raw int
17+
cleanup runtime.Cleanup
1618
}
1719

1820
// NewFD wraps a raw fd with a finalizer.

internal/testutils/chan.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
// WaitChan waits for a value to be sent on a channel, or for a timeout to
99
// occur. If the timeout is reached, the test will fail.
1010
func WaitChan[T any](tb testing.TB, ch <-chan T, timeout time.Duration) {
11+
tb.Helper()
12+
1113
select {
1214
case <-ch:
1315
return

internal/tracefs/kprobe.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ type Event struct {
200200
group, name string
201201
// event id allocated by the kernel. 0 if the event has already been removed.
202202
id uint64
203+
204+
cleanup runtime.Cleanup
203205
}
204206

205207
// NewEvent creates a new ephemeral trace event.
@@ -316,8 +318,11 @@ func NewEvent(args ProbeArgs) (*Event, error) {
316318
return nil, fmt.Errorf("get trace event id: %w", err)
317319
}
318320

319-
evt := &Event{args.Type, args.Group, eventName, tid}
320-
runtime.SetFinalizer(evt, (*Event).Close)
321+
evt := &Event{typ: args.Type, group: args.Group, name: eventName, id: tid}
322+
evt.cleanup = runtime.AddCleanup(evt, func(*byte) {
323+
_ = removeEvent(args.Type, fmt.Sprintf("%s/%s", args.Group, eventName))
324+
}, nil)
325+
321326
return evt, nil
322327
}
323328

@@ -330,7 +335,7 @@ func (evt *Event) Close() error {
330335
}
331336

332337
evt.id = 0
333-
runtime.SetFinalizer(evt, nil)
338+
evt.cleanup.Stop()
334339
pe := fmt.Sprintf("%s/%s", evt.group, evt.name)
335340
return removeEvent(evt.typ, pe)
336341
}

memory.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ type Memory struct {
3838
b []byte
3939
ro bool
4040
heap bool
41+
42+
cleanup runtime.Cleanup
4143
}
4244

4345
func newMemory(fd, size int) (*Memory, error) {
@@ -62,20 +64,23 @@ func newMemory(fd, size int) (*Memory, error) {
6264
return nil, fmt.Errorf("setting up memory-mapped region: %w", err)
6365
}
6466

65-
mm := &Memory{
66-
b,
67-
ro,
68-
false,
69-
}
70-
runtime.SetFinalizer(mm, (*Memory).close)
67+
mm := &Memory{b: b, ro: ro, heap: false}
68+
mm.cleanup = runtime.AddCleanup(&mm, memoryCleanupFunc(), b)
7169

7270
return mm, nil
7371
}
7472

75-
func (mm *Memory) close() {
76-
if err := unix.Munmap(mm.b); err != nil {
77-
panic(fmt.Errorf("unmapping memory: %w", err))
73+
func memoryCleanupFunc() func([]byte) {
74+
return func(b []byte) {
75+
if err := unix.Munmap(b); err != nil {
76+
panic(fmt.Errorf("unmapping memory: %w", err))
77+
}
7878
}
79+
}
80+
81+
func (mm *Memory) close() {
82+
mm.cleanup.Stop()
83+
memoryCleanupFunc()(mm.b)
7984
mm.b = nil
8085
}
8186

memory_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"io"
55
"math"
66
"os"
7-
"runtime"
87
"testing"
98

109
"github.com/go-quicktest/qt"
@@ -88,9 +87,6 @@ func TestMemoryClose(t *testing.T) {
8887
mm, err := mustMmapableArray(t, 0).Memory()
8988
qt.Assert(t, qt.IsNil(err))
9089

91-
// Avoid unmap running twice.
92-
runtime.SetFinalizer(mm, nil)
93-
9490
// unmap panics if the operation fails.
9591
mm.close()
9692
}

memory_unsafe.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func newUnsafeMemory(fd, size int) (*Memory, error) {
8787
unsafe.Slice((*byte)(alloc), size),
8888
ro,
8989
true,
90+
runtime.Cleanup{},
9091
}
9192

9293
return mm, nil

0 commit comments

Comments
 (0)