From fa2aebcfa1b7cdfd7ad844b794f05888520ef937 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Wed, 15 Jan 2025 13:55:26 +0000 Subject: [PATCH] bpf recorder: load bpf program on startup to win race with container startup This commit makes it so that the BPF program is loaded (and relocated) on profile recorder startup. When we then want to start the recording, we only need to attach the programs. This fixes the flakiness in #2667 and #2668 where program loading is taking so long that we miss container startup. --- internal/pkg/cli/recorder/impl.go | 13 +- internal/pkg/cli/recorder/recorder.go | 5 +- .../cli/recorder/recorderfakes/fake_impl.go | 113 ++++++++----- .../bpfrecorder/bpf_program_collection.go | 80 +++++++++ .../pkg/daemon/bpfrecorder/bpfrecorder.go | 159 +++++++++++------- .../bpfrecorder/bpfrecorder_apparmor.go | 35 +++- .../daemon/bpfrecorder/bpfrecorder_seccomp.go | 17 +- 7 files changed, 306 insertions(+), 116 deletions(-) create mode 100644 internal/pkg/daemon/bpfrecorder/bpf_program_collection.go diff --git a/internal/pkg/cli/recorder/impl.go b/internal/pkg/cli/recorder/impl.go index 92bbcb8981..e4a33a91d0 100644 --- a/internal/pkg/cli/recorder/impl.go +++ b/internal/pkg/cli/recorder/impl.go @@ -43,7 +43,8 @@ type defaultImpl struct{} //counterfeiter:generate . impl type impl interface { LoadBpfRecorder(*bpfrecorder.BpfRecorder) error - UnloadBpfRecorder(*bpfrecorder.BpfRecorder) + StartBpfRecording(*bpfrecorder.BpfRecorder) error + StopBpfRecording(*bpfrecorder.BpfRecorder) error BPFLSMEnabled() bool CommandRun(*command.Command) (uint32, error) CommandWait(*command.Command) error @@ -62,11 +63,15 @@ type impl interface { } func (*defaultImpl) LoadBpfRecorder(b *bpfrecorder.BpfRecorder) error { - return b.Load(true) + return b.Load() } -func (*defaultImpl) UnloadBpfRecorder(b *bpfrecorder.BpfRecorder) { - b.Unload() +func (*defaultImpl) StartBpfRecording(b *bpfrecorder.BpfRecorder) error { + return b.StartRecording() +} + +func (*defaultImpl) StopBpfRecording(b *bpfrecorder.BpfRecorder) error { + return b.StopRecording() } func (*defaultImpl) BPFLSMEnabled() bool { diff --git a/internal/pkg/cli/recorder/recorder.go b/internal/pkg/cli/recorder/recorder.go index e71f565277..91f95cf43f 100644 --- a/internal/pkg/cli/recorder/recorder.go +++ b/internal/pkg/cli/recorder/recorder.go @@ -96,7 +96,10 @@ func (r *Recorder) Run() error { if err := r.LoadBpfRecorder(r.bpfRecorder); err != nil { return fmt.Errorf("load: %w", err) } - defer r.UnloadBpfRecorder(r.bpfRecorder) + if err := r.StartBpfRecording(r.bpfRecorder); err != nil { + return fmt.Errorf("record: %w", err) + } + defer r.StopBpfRecording(r.bpfRecorder) var mntns uint32 if r.options.noProcStart { diff --git a/internal/pkg/cli/recorder/recorderfakes/fake_impl.go b/internal/pkg/cli/recorder/recorderfakes/fake_impl.go index 6f884cff32..7944dc1f69 100644 --- a/internal/pkg/cli/recorder/recorderfakes/fake_impl.go +++ b/internal/pkg/cli/recorder/recorderfakes/fake_impl.go @@ -190,6 +190,17 @@ type FakeImpl struct { printObjReturnsOnCall map[int]struct { result1 error } + StartBpfRecordingStub func(*bpfrecorder.BpfRecorder) error + startBpfRecordingMutex sync.RWMutex + startBpfRecordingArgsForCall []struct { + arg1 *bpfrecorder.BpfRecorder + } + startBpfRecordingReturns struct { + result1 error + } + startBpfRecordingReturnsOnCall map[int]struct { + result1 error + } SyscallsGetValueStub func(*bpfrecorder.BpfRecorder, uint32) ([]byte, error) syscallsGetValueMutex sync.RWMutex syscallsGetValueArgsForCall []struct { @@ -215,11 +226,6 @@ type FakeImpl struct { syscallsIteratorReturnsOnCall map[int]struct { result1 *libbpfgo.BPFMapIterator } - UnloadBpfRecorderStub func(*bpfrecorder.BpfRecorder) - unloadBpfRecorderMutex sync.RWMutex - unloadBpfRecorderArgsForCall []struct { - arg1 *bpfrecorder.BpfRecorder - } WaitForPidExitStub func(*bpfrecorder.BpfRecorder, context.Context, uint32) error waitForPidExitMutex sync.RWMutex waitForPidExitArgsForCall []struct { @@ -1017,6 +1023,67 @@ func (fake *FakeImpl) PrintObjReturnsOnCall(i int, result1 error) { }{result1} } +func (fake *FakeImpl) StartBpfRecording(arg1 *bpfrecorder.BpfRecorder) error { + fake.startBpfRecordingMutex.Lock() + ret, specificReturn := fake.startBpfRecordingReturnsOnCall[len(fake.startBpfRecordingArgsForCall)] + fake.startBpfRecordingArgsForCall = append(fake.startBpfRecordingArgsForCall, struct { + arg1 *bpfrecorder.BpfRecorder + }{arg1}) + stub := fake.StartBpfRecordingStub + fakeReturns := fake.startBpfRecordingReturns + fake.recordInvocation("StartBpfRecording", []interface{}{arg1}) + fake.startBpfRecordingMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeImpl) StartBpfRecordingCallCount() int { + fake.startBpfRecordingMutex.RLock() + defer fake.startBpfRecordingMutex.RUnlock() + return len(fake.startBpfRecordingArgsForCall) +} + +func (fake *FakeImpl) StartBpfRecordingCalls(stub func(*bpfrecorder.BpfRecorder) error) { + fake.startBpfRecordingMutex.Lock() + defer fake.startBpfRecordingMutex.Unlock() + fake.StartBpfRecordingStub = stub +} + +func (fake *FakeImpl) StartBpfRecordingArgsForCall(i int) *bpfrecorder.BpfRecorder { + fake.startBpfRecordingMutex.RLock() + defer fake.startBpfRecordingMutex.RUnlock() + argsForCall := fake.startBpfRecordingArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeImpl) StartBpfRecordingReturns(result1 error) { + fake.startBpfRecordingMutex.Lock() + defer fake.startBpfRecordingMutex.Unlock() + fake.StartBpfRecordingStub = nil + fake.startBpfRecordingReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeImpl) StartBpfRecordingReturnsOnCall(i int, result1 error) { + fake.startBpfRecordingMutex.Lock() + defer fake.startBpfRecordingMutex.Unlock() + fake.StartBpfRecordingStub = nil + if fake.startBpfRecordingReturnsOnCall == nil { + fake.startBpfRecordingReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.startBpfRecordingReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakeImpl) SyscallsGetValue(arg1 *bpfrecorder.BpfRecorder, arg2 uint32) ([]byte, error) { fake.syscallsGetValueMutex.Lock() ret, specificReturn := fake.syscallsGetValueReturnsOnCall[len(fake.syscallsGetValueArgsForCall)] @@ -1143,38 +1210,6 @@ func (fake *FakeImpl) SyscallsIteratorReturnsOnCall(i int, result1 *libbpfgo.BPF }{result1} } -func (fake *FakeImpl) UnloadBpfRecorder(arg1 *bpfrecorder.BpfRecorder) { - fake.unloadBpfRecorderMutex.Lock() - fake.unloadBpfRecorderArgsForCall = append(fake.unloadBpfRecorderArgsForCall, struct { - arg1 *bpfrecorder.BpfRecorder - }{arg1}) - stub := fake.UnloadBpfRecorderStub - fake.recordInvocation("UnloadBpfRecorder", []interface{}{arg1}) - fake.unloadBpfRecorderMutex.Unlock() - if stub != nil { - fake.UnloadBpfRecorderStub(arg1) - } -} - -func (fake *FakeImpl) UnloadBpfRecorderCallCount() int { - fake.unloadBpfRecorderMutex.RLock() - defer fake.unloadBpfRecorderMutex.RUnlock() - return len(fake.unloadBpfRecorderArgsForCall) -} - -func (fake *FakeImpl) UnloadBpfRecorderCalls(stub func(*bpfrecorder.BpfRecorder)) { - fake.unloadBpfRecorderMutex.Lock() - defer fake.unloadBpfRecorderMutex.Unlock() - fake.UnloadBpfRecorderStub = stub -} - -func (fake *FakeImpl) UnloadBpfRecorderArgsForCall(i int) *bpfrecorder.BpfRecorder { - fake.unloadBpfRecorderMutex.RLock() - defer fake.unloadBpfRecorderMutex.RUnlock() - argsForCall := fake.unloadBpfRecorderArgsForCall[i] - return argsForCall.arg1 -} - func (fake *FakeImpl) WaitForPidExit(arg1 *bpfrecorder.BpfRecorder, arg2 context.Context, arg3 uint32) error { fake.waitForPidExitMutex.Lock() ret, specificReturn := fake.waitForPidExitReturnsOnCall[len(fake.waitForPidExitArgsForCall)] @@ -1267,12 +1302,12 @@ func (fake *FakeImpl) Invocations() map[string][][]interface{} { defer fake.notifyMutex.RUnlock() fake.printObjMutex.RLock() defer fake.printObjMutex.RUnlock() + fake.startBpfRecordingMutex.RLock() + defer fake.startBpfRecordingMutex.RUnlock() fake.syscallsGetValueMutex.RLock() defer fake.syscallsGetValueMutex.RUnlock() fake.syscallsIteratorMutex.RLock() defer fake.syscallsIteratorMutex.RUnlock() - fake.unloadBpfRecorderMutex.RLock() - defer fake.unloadBpfRecorderMutex.RUnlock() fake.waitForPidExitMutex.RLock() defer fake.waitForPidExitMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} diff --git a/internal/pkg/daemon/bpfrecorder/bpf_program_collection.go b/internal/pkg/daemon/bpfrecorder/bpf_program_collection.go new file mode 100644 index 0000000000..72baacd0a5 --- /dev/null +++ b/internal/pkg/daemon/bpfrecorder/bpf_program_collection.go @@ -0,0 +1,80 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bpfrecorder + +import ( + "fmt" + bpf "github.com/aquasecurity/libbpfgo" +) + +type bpfProgram struct { + name string + prog *bpf.BPFProg + link *bpf.BPFLink +} + +// This struct holds a set of bpf programs so that they be quickly attached and detached. +// We want to minimize the time that takes because it races with container startup, +// and we want to make sure that we catch the entire container lifecycle. +type bpfProgramCollection struct { + programs []bpfProgram +} + +func newProgramCollection(module *bpf.Module, programNames []string) (*bpfProgramCollection, error) { + programs := make([]bpfProgram, len(programNames)) + for i, name := range programNames { + prog, err := module.GetProgram(name) + if err != nil { + return nil, fmt.Errorf("get bpf program %s: %w", name, err) + } + programs[i] = bpfProgram{ + name: name, + prog: prog, + link: nil, + } + } + return &bpfProgramCollection{ + programs: programs, + }, nil +} + +func (b *bpfProgramCollection) attachAll() error { + var err error + for _, prog := range b.programs { + if prog.link != nil { + continue + } + prog.link, err = prog.prog.AttachGeneric() + if err != nil { + return fmt.Errorf("attach bpf program %s: %w", prog.name, err) + } + } + return err +} + +func (b *bpfProgramCollection) detachAll() error { + for _, prog := range b.programs { + if prog.link == nil { + continue + } + if err := prog.link.Destroy(); err != nil { + return fmt.Errorf("detach bpf program %s: %w", prog.name, err) + } + prog.link = nil + } + return nil +} diff --git a/internal/pkg/daemon/bpfrecorder/bpfrecorder.go b/internal/pkg/daemon/bpfrecorder/bpfrecorder.go index 01bf62dcb5..b66fc8cb36 100644 --- a/internal/pkg/daemon/bpfrecorder/bpfrecorder.go +++ b/internal/pkg/daemon/bpfrecorder/bpfrecorder.go @@ -86,10 +86,11 @@ type BpfRecorder struct { nodeName string clientset *kubernetes.Clientset excludeMountNamespace uint32 - loadUnloadMutex sync.RWMutex + attachUnattachMutex sync.RWMutex metricsClient apimetrics.Metrics_BpfIncClient programName string module *bpf.Module + bpfPrograms *bpfProgramCollection AppArmor *AppArmorRecorder Seccomp *SeccompRecorder @@ -127,7 +128,7 @@ func New(programName string, logger logr.Logger, recordSeccomp, recordAppArmor b ), mntnsToContainerIDMap: bimap.New[uint32, string](), containerIDToProfileMap: bimap.New[string, string](), - loadUnloadMutex: sync.RWMutex{}, + attachUnattachMutex: sync.RWMutex{}, programName: programName, AppArmor: appArmor, Seccomp: seccomp, @@ -209,11 +210,18 @@ func (b *BpfRecorder) Run() error { } b.logger.Info("Got system mount namespace: " + strconv.FormatUint(uint64(b.excludeMountNamespace), 10)) - b.logger.Info("Doing BPF load/unload self-test") - if err := b.Load(false); err != nil { - return fmt.Errorf("load self-test: %w", err) + b.logger.Info("Loading BPF program") + if err := b.Load(); err != nil { + return fmt.Errorf("bpf load: %w", err) + } + + b.logger.Info("Doing BPF start/stop self-test") + if err := b.StartRecording(); err != nil { + return fmt.Errorf("StartRecording self-test: %w", err) + } + if err := b.StopRecording(); err != nil { + return fmt.Errorf("StopRecording self-test: %w", err) } - b.Unload() b.logger.Info("Starting GRPC API server") grpcServer := grpc.NewServer( @@ -273,8 +281,8 @@ func (b *BpfRecorder) Start( if b.startRequests == 0 { b.logger.Info("Starting bpf recorder") //nolint:contextcheck // no context intended here - if err := b.Load(true); err != nil { - return nil, fmt.Errorf("load bpf: %w", err) + if err := b.StartRecording(); err != nil { + return nil, fmt.Errorf("start recording: %w", err) } } else { b.logger.Info("bpf recorder already running") @@ -295,7 +303,9 @@ func (b *BpfRecorder) Stop( atomic.AddInt64(&b.startRequests, -1) if b.startRequests == 0 { b.logger.Info("Stopping bpf recorder") - b.Unload() + if err := b.StopRecording(); err != nil { + return nil, fmt.Errorf("start recording: %w", err) + } } else { b.logger.Info("Not stopping because another recording is in progress") } @@ -318,9 +328,9 @@ func (b *BpfRecorder) SyscallsForProfile( if err != nil { return nil, err } - b.loadUnloadMutex.RLock() + b.attachUnattachMutex.RLock() syscalls, err := b.Seccomp.PopSyscalls(b, mntns) - b.loadUnloadMutex.RUnlock() + b.attachUnattachMutex.RUnlock() if err != nil { b.logger.Error(err, "Failed to get syscalls for mntns", "mntns", mntns) return nil, err @@ -353,9 +363,9 @@ func (b *BpfRecorder) ApparmorForProfile( if err != nil { return nil, err } - b.loadUnloadMutex.RLock() + b.attachUnattachMutex.RLock() apparmor := b.AppArmor.GetAppArmorProcessed(mntns) - b.loadUnloadMutex.RUnlock() + b.attachUnattachMutex.RUnlock() return &api.ApparmorResponse{ Files: &api.ApparmorResponse_Files{ AllowedExecutables: apparmor.FileProcessed.AllowedExecutables, @@ -421,8 +431,12 @@ var baseHooks = []string{ "sched_process_exit", } -// Load prestarts the bpf recorder. -func (b *BpfRecorder) Load(startEventProcessor bool) (err error) { +// Load loads the BPF code, does relocations, and gets references to the programs we want to attach. +// We try to front load as much work as possible so that starting a recording is quick. +// Recorder start races with container initialization, so we can't spend too much time then. +// +// Unloading is currently done implicitly on process exit. +func (b *BpfRecorder) Load() (err error) { var module *bpf.Module b.logger.Info("Loading bpf module") @@ -450,6 +464,26 @@ func (b *BpfRecorder) Load(startEventProcessor bool) (err error) { return fmt.Errorf("load bpf module: %w", err) } b.module = module + programs, err := newProgramCollection(module, baseHooks) + if err != nil { + return err + } + b.bpfPrograms = programs + if b.AppArmor != nil { + if err := b.AppArmor.Load(b); err != nil { + // Only log an error here, if Apparmor cannot be loaded. This is because it is + // enabled by default, and there are Linux distributions which either do not + // support Apparmor or BPF LSM is not yet available. + // + // see also https://github.com/kubernetes-sigs/security-profiles-operator/issues/2384 + b.logger.Error(err, "load AppArmor bpf hooks") + } + } + if b.Seccomp != nil { + if err := b.Seccomp.Load(b); err != nil { + return err + } + } if b.programName != "" { programName := []byte(filepath.Base(b.programName)) @@ -482,28 +516,6 @@ func (b *BpfRecorder) Load(startEventProcessor bool) (err error) { } } - b.logger.Info("Attach all bpf programs") - for _, hook := range baseHooks { - if err := b.attachBpfProgram(hook); err != nil { - return fmt.Errorf("attach bpf program: %w", err) - } - } - if b.AppArmor != nil { - if err := b.AppArmor.Load(b); err != nil { - // Only log an error here, if Apparmor cannot be loaded. This is because it is - // enabled by default, and there are Linux distributions which either do not - // support Apparmor or BPF LSM is not yet available. - // - // see also https://github.com/kubernetes-sigs/security-profiles-operator/issues/2384 - b.logger.Error(err, "Loading bpf program") - } - } - if b.Seccomp != nil { - if err := b.Seccomp.Load(b); err != nil { - return err - } - } - const timeout = 300 events := make(chan []byte) ringbuf, err := b.InitRingBuf( @@ -516,23 +528,59 @@ func (b *BpfRecorder) Load(startEventProcessor bool) (err error) { } b.PollRingBuffer(ringbuf, timeout) - if startEventProcessor { - go b.processEvents(events) - } + go b.processEvents(events) b.logger.Info("BPF module successfully loaded.") return nil } -func (b *BpfRecorder) attachBpfProgram(name string) error { - program, err := b.GetProgram(b.module, name) - if err != nil { - return fmt.Errorf("get %s bpf program: %w", name, err) +func (b *BpfRecorder) StartRecording() (err error) { + b.logger.Info("Attach all bpf programs...") + b.attachUnattachMutex.Lock() + defer b.attachUnattachMutex.Unlock() + + if err := b.bpfPrograms.attachAll(); err != nil { + return fmt.Errorf("attach base hooks: %w", err) } - if _, err := b.AttachGeneric(program); err != nil { - return fmt.Errorf("attach %s bpf program: %w", name, err) + if b.AppArmor != nil { + if err := b.AppArmor.StartRecording(b); err != nil { + // Only log an error here, if Apparmor cannot be loaded. This is because it is + // enabled by default, and there are Linux distributions which either do not + // support Apparmor or BPF LSM is not yet available. + // + // see also https://github.com/kubernetes-sigs/security-profiles-operator/issues/2384 + b.logger.Error(err, "attach AppArmor bpf hooks") + } + } + if b.Seccomp != nil { + if err := b.Seccomp.StartRecording(b); err != nil { + return err + } } - b.logger.Info("Attached bpf program " + name + ".") + b.logger.Info("Attached all bpf programs.") + + return nil +} + +func (b *BpfRecorder) StopRecording() error { + b.logger.Info("Stop BPF recording") + b.attachUnattachMutex.Lock() + defer b.attachUnattachMutex.Unlock() + + if err := b.bpfPrograms.detachAll(); err != nil { + return fmt.Errorf("detach base hooks: %w", err) + } + if b.Seccomp != nil { + if err := b.Seccomp.StopRecording(b); err != nil { + return err + } + } + if b.AppArmor != nil { + if err := b.AppArmor.StopRecording(b); err != nil { + return err + } + } + // XXX: It may be useful to clear out all existing maps here. return nil } @@ -880,23 +928,6 @@ func (b *BpfRecorder) findProfileForContainerID(id string) (string, error) { return "", fmt.Errorf("container ID not found: %s", id) } -// Unload can be used to reset the bpf recorder. -func (b *BpfRecorder) Unload() { - b.logger.Info("Unloading bpf module") - b.loadUnloadMutex.Lock() - b.CloseModule(b.module) - - if b.Seccomp != nil { - b.Seccomp.Unload() - } - if b.AppArmor != nil { - b.AppArmor.Unload() - } - - os.RemoveAll(b.btfPath) - b.loadUnloadMutex.Unlock() -} - // When running outside of Kubernetes as spoc, we have the use case of waiting for a specific PID to exit. func (b *BpfRecorder) WaitForPidExit(ctx context.Context, pid uint32) error { d, _ := b.recordedExits.LoadOrStore(pid, make(chan bool)) diff --git a/internal/pkg/daemon/bpfrecorder/bpfrecorder_apparmor.go b/internal/pkg/daemon/bpfrecorder/bpfrecorder_apparmor.go index 4d787942dd..7db5887146 100644 --- a/internal/pkg/daemon/bpfrecorder/bpfrecorder_apparmor.go +++ b/internal/pkg/daemon/bpfrecorder/bpfrecorder_apparmor.go @@ -72,6 +72,7 @@ type AppArmorRecorder struct { recordedFiles map[mntnsID]map[string]*fileAccess lockRecordedFiles sync.Mutex + bpfPrograms *bpfProgramCollection } type fileAccess struct { @@ -114,19 +115,36 @@ func newAppArmorRecorder(logger logr.Logger, programName string) *AppArmorRecord } } -func (*AppArmorRecorder) Load(b *BpfRecorder) error { +func (b *AppArmorRecorder) Load(r *BpfRecorder) error { if !BPFLSMEnabled() { return errors.New("BPF LSM is not enabled for this kernel") } - for _, hook := range appArmorHooks { - if err := b.attachBpfProgram(hook); err != nil { - return err - } + programs, err := newProgramCollection(r.module, appArmorHooks) + if err != nil { + return fmt.Errorf("load apparmor hooks: %w", err) } + b.bpfPrograms = programs return nil } -func (b *AppArmorRecorder) Unload() { +func (b *AppArmorRecorder) StartRecording(r *BpfRecorder) error { + return b.bpfPrograms.attachAll() +} + +func (b *AppArmorRecorder) StopRecording(r *BpfRecorder) (err error) { + err = b.bpfPrograms.detachAll() + + b.lockRecordedSocketsUse.Lock() + defer b.lockRecordedSocketsUse.Unlock() + b.lockRecordedCapabilities.Lock() + defer b.lockRecordedCapabilities.Unlock() + b.lockRecordedFiles.Lock() + defer b.lockRecordedFiles.Unlock() + + clear(b.recordedSocketsUse) + clear(b.recordedCapabilities) + clear(b.recordedFiles) + return err } func (b *AppArmorRecorder) handleFileEvent(fileEvent *bpfEvent) { @@ -262,6 +280,11 @@ func (b *AppArmorRecorder) GetAppArmorProcessed(mntns uint32) BpfAppArmorProcess } processed.Capabilities = b.processCapabilities(mid) + // Clean up the recorded data after processing to avoid keeping global state. + delete(b.recordedSocketsUse, mid) + delete(b.recordedFiles, mid) + delete(b.recordedCapabilities, mid) + return processed } diff --git a/internal/pkg/daemon/bpfrecorder/bpfrecorder_seccomp.go b/internal/pkg/daemon/bpfrecorder/bpfrecorder_seccomp.go index 78dc1e9acd..edb5be02a0 100644 --- a/internal/pkg/daemon/bpfrecorder/bpfrecorder_seccomp.go +++ b/internal/pkg/daemon/bpfrecorder/bpfrecorder_seccomp.go @@ -23,6 +23,7 @@ import ( "fmt" "sort" "strconv" + "unsafe" bpf "github.com/aquasecurity/libbpfgo" "github.com/go-logr/logr" @@ -52,8 +53,20 @@ func (s *SeccompRecorder) Load(b *BpfRecorder) error { return nil } -func (s *SeccompRecorder) Unload() { - s.syscalls = nil +func (s *SeccompRecorder) StartRecording(b *BpfRecorder) error { + // This uses one of the base hooks, no need to attach here. + return nil +} + +func (s *SeccompRecorder) StopRecording(b *BpfRecorder) error { + it := s.syscalls.Iterator() + for it.Next() { + key := it.Key() + if err := s.syscalls.DeleteKey(unsafe.Pointer(&key[0])); err != nil { + return fmt.Errorf("failed to clean up syscalls map: %w", err) + } + } + return nil } func (s *SeccompRecorder) PopSyscalls(b *BpfRecorder, mntns uint32) ([]string, error) {