diff --git a/pkg/ebpf/uprobes/attacher.go b/pkg/ebpf/uprobes/attacher.go index 36781ff80bdc2..8daa262056c3e 100644 --- a/pkg/ebpf/uprobes/attacher.go +++ b/pkg/ebpf/uprobes/attacher.go @@ -662,7 +662,7 @@ func (ua *UprobeAttacher) AttachPIDWithOptions(pid uint32, attachToLibs bool) er procInfo := NewProcInfo(ua.config.ProcRoot, pid) // Only compute the binary path if we are going to need it. It's better to do these two checks - // (which are cheak, the handlesExecutables function is cached) than to do the syscall + // (which are cheap, the handlesExecutables function is cached) than to do the syscall // every time var binPath string var err error @@ -679,21 +679,18 @@ func (ua *UprobeAttacher) AttachPIDWithOptions(pid uint32, attachToLibs bool) er if ua.handlesExecutables() { matchingRules := ua.getRulesForExecutable(binPath, procInfo) - if len(matchingRules) != 0 { registerCB, unregisterCB := ua.buildRegisterCallbacks(matchingRules, procInfo) err = ua.fileRegistry.Register(binPath, pid, registerCB, unregisterCB, utils.IgnoreCB) - if err != nil { - return err - } + // Do not return in case of error, as we still might want to attach to libraries } } if attachToLibs && ua.handlesLibraries() { - return ua.attachToLibrariesOfPID(pid) + err = errors.Join(err, ua.attachToLibrariesOfPID(pid)) } - return nil + return err } // DetachPID detaches the uprobes attached to a PID diff --git a/pkg/ebpf/uprobes/attacher_test.go b/pkg/ebpf/uprobes/attacher_test.go index 5edf30c1f4972..fa5740fe259c6 100644 --- a/pkg/ebpf/uprobes/attacher_test.go +++ b/pkg/ebpf/uprobes/attacher_test.go @@ -8,6 +8,7 @@ package uprobes import ( + "errors" "fmt" "os" "os/exec" @@ -63,6 +64,45 @@ func TestAttachPidExcludesInternal(t *testing.T) { require.ErrorIs(t, err, ErrInternalDDogProcessRejected) } +func TestAttachPidReadsSharedLibraries(t *testing.T) { + exe := "foobar" + pid := uint32(1) + libname := "/target/libssl.so" + maps := fmt.Sprintf("08048000-08049000 r-xp 00000000 03:00 8312 %s", libname) + procRoot := CreateFakeProcFS(t, []FakeProcFSEntry{{Pid: pid, Cmdline: exe, Command: exe, Exe: exe, Maps: maps}}) + config := AttacherConfig{ + ProcRoot: procRoot, + Rules: []*AttachRule{ + {LibraryNameRegex: regexp.MustCompile(`libssl\.so`), Targets: AttachToSharedLibraries}, + {Targets: AttachToExecutable}, + }, + SharedLibsLibset: sharedlibraries.LibsetCrypto, + EnableDetailedLogging: true, + } + + registry := &MockFileRegistry{} + // Force a failure on the Register call for the executable, to simulate a + // binary that doesn't have our desired functions to attach + registry.On("Register", exe, pid, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("cannot attach")) + + // Expect a call to Register for the library + registry.On("Register", libname, pid, mock.Anything, mock.Anything, mock.Anything).Return(nil) + + ua, err := NewUprobeAttacher(testModuleName, testAttacherName, config, &MockManager{}, nil, nil, newMockProcessMonitor()) + require.NoError(t, err) + require.NotNil(t, ua) + require.True(t, ua.handlesExecutables()) + require.True(t, ua.handlesLibraries()) + ua.fileRegistry = registry + + err = ua.AttachPIDWithOptions(pid, true) + require.Error(t, err) + + // We should get calls to Register both with the executable and the library + // name, even though the executable returns an error + registry.AssertExpectations(t) +} + func TestAttachPidExcludesSelf(t *testing.T) { config := AttacherConfig{ ExcludeTargets: ExcludeSelf, diff --git a/pkg/ebpf/uprobes/testutil.go b/pkg/ebpf/uprobes/testutil.go index cd4b2891d72ef..210f8f356960b 100644 --- a/pkg/ebpf/uprobes/testutil.go +++ b/pkg/ebpf/uprobes/testutil.go @@ -58,8 +58,8 @@ type MockFileRegistry struct { } // Register is a mock implementation of the FileRegistry.Register method. -func (m *MockFileRegistry) Register(namespacedPath string, pid uint32, activationCB, deactivationCB, _ utils.Callback) error { - args := m.Called(namespacedPath, pid, activationCB, deactivationCB) +func (m *MockFileRegistry) Register(namespacedPath string, pid uint32, activationCB, deactivationCB, ignoreCB utils.Callback) error { + args := m.Called(namespacedPath, pid, activationCB, deactivationCB, ignoreCB) return args.Error(0) }