Skip to content

Commit

Permalink
killer: add and use HasModifyReturnSyscall check
Browse files Browse the repository at this point in the history
Our detection code already has a function for detecting of fmod_ret is
supported named HasModifyReturn. If we want to load an fmod_ret program
in a syscall function, however, checking HasModifyReturn() is not
sufficient.

This is because in kernels where CONFIG_FUNCTION_ERROR_INJECTION is not
set, loading fmod_ret programs in syscalls is not supported.

So add a separate check, where we try to attach to a system call (we use
getcpu) instead of a security_ function and use it for feature detection
in the killer sensor.

Kernel check is:

```
static int check_attach_modify_return(unsigned long addr, const char *func_name)
{
	if (within_error_injection_list(addr) ||
	    !strncmp(SECURITY_PREFIX, func_name, sizeof(SECURITY_PREFIX) - 1))
		return 0;

	return -EINVAL;
}
```

And:

```
...

static inline bool within_error_injection_list(unsigned long addr)
{
	return false;
}

static inline int get_injectable_error_type(unsigned long addr)
{
	return -EOPNOTSUPP;
}

```

Signed-off-by: Kornilios Kourtis <kornilios@isovalent.com>
  • Loading branch information
kkourt authored and jrfastab committed Jan 17, 2024
1 parent ebed8cc commit 38bedec
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 35 deletions.
54 changes: 49 additions & 5 deletions pkg/bpf/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/features"
"github.com/cilium/ebpf/link"
"github.com/cilium/tetragon/pkg/arch"
"github.com/cilium/tetragon/pkg/logger"
"golang.org/x/sys/unix"
)

Expand All @@ -24,9 +26,10 @@ type Feature struct {
}

var (
kprobeMulti Feature
buildid Feature
modifyReturn Feature
kprobeMulti Feature
buildid Feature
modifyReturn Feature
modifyReturnSyscall Feature
)

func HasOverrideHelper() bool {
Expand Down Expand Up @@ -119,18 +122,59 @@ func detectModifyReturn() bool {
return true
}

func detectModifyReturnSyscall() bool {
sysGetcpu, err := arch.AddSyscallPrefix("sys_getcpu")
if err != nil {
return false
}
logger.GetLogger().Infof("probing detectModifyReturnSyscall using %s", sysGetcpu)
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
Name: "probe_sys_fmod_ret",
Type: ebpf.Tracing,
Instructions: asm.Instructions{
asm.Mov.Imm(asm.R0, 0),
asm.Return(),
},
AttachType: ebpf.AttachModifyReturn,
AttachTo: sysGetcpu,
License: "MIT",
})
if err != nil {
logger.GetLogger().WithError(err).Info("detectModifyReturnSyscall: failed to load")
return false
}
defer prog.Close()

link, err := link.AttachTracing(link.TracingOptions{
Program: prog,
})
if err != nil {
logger.GetLogger().WithError(err).Info("detectModifyReturnSyscall, failed to attach")
return false
}
link.Close()
return true
}

func HasModifyReturn() bool {
modifyReturn.init.Do(func() {
modifyReturn.detected = detectModifyReturn()
})
return modifyReturn.detected
}

func HasModifyReturnSyscall() bool {
modifyReturnSyscall.init.Do(func() {
modifyReturnSyscall.detected = detectModifyReturnSyscall()
})
return modifyReturnSyscall.detected
}

func HasProgramLargeSize() bool {
return features.HaveLargeInstructions() == nil
}

func LogFeatures() string {
return fmt.Sprintf("override_return: %t, buildid: %t, kprobe_multi: %t, fmodret: %t, signal: %t, large: %t",
HasOverrideHelper(), HasBuildId(), HasKprobeMulti(), HasModifyReturn(), HasSignalHelper(), HasProgramLargeSize())
return fmt.Sprintf("override_return: %t, buildid: %t, kprobe_multi: %t, fmodret: %t, fmodret_syscall: %t, signal: %t, large: %t",
HasOverrideHelper(), HasBuildId(), HasKprobeMulti(), HasModifyReturn(), HasModifyReturnSyscall(), HasSignalHelper(), HasProgramLargeSize())
}
4 changes: 2 additions & 2 deletions pkg/sensors/tracing/killer.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func selectOverrideMethod(specOpts *specOptions) (OverrideMethod, error) {
// by default, first try OverrideReturn and if this does not work try fmod_ret
if bpf.HasOverrideHelper() {
overrideMethod = OverrideMethodReturn
} else if bpf.HasModifyReturn() {
} else if bpf.HasModifyReturnSyscall() {
overrideMethod = OverrideMethodFmodRet
} else {
return OverrideMethodInvalid, fmt.Errorf("no override helper or mod_ret support: cannot load killer")
Expand All @@ -125,7 +125,7 @@ func selectOverrideMethod(specOpts *specOptions) (OverrideMethod, error) {
return OverrideMethodInvalid, fmt.Errorf("option override return set, but it is not supported")
}
case OverrideMethodFmodRet:
if !bpf.HasModifyReturn() {
if !bpf.HasModifyReturnSyscall() {
return OverrideMethodInvalid, fmt.Errorf("option fmod_ret set, but it is not supported")
}
}
Expand Down
22 changes: 3 additions & 19 deletions pkg/sensors/tracing/killer_amd64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"testing"

"github.com/cilium/tetragon/api/v1/tetragon"
"github.com/cilium/tetragon/pkg/bpf"
"github.com/cilium/tetragon/pkg/syscallinfo/i386"
"github.com/cilium/tetragon/pkg/testutils"

Expand All @@ -20,12 +19,7 @@ import (
)

func TestKillerOverride32(t *testing.T) {
if !bpf.HasOverrideHelper() && !bpf.HasModifyReturn() {
t.Skip("skipping killer test, neither bpf_override_return nor fmod_ret is available")
}
if !bpf.HasSignalHelper() {
t.Skip("skipping killer test, bpf_send_signal helper not available")
}
testKillerCheckSkip(t)

test := testutils.RepoRootPath("contrib/tester-progs/killer-tester-32")
yaml := NewKillerSpecBuilder("killer-override").
Expand Down Expand Up @@ -54,12 +48,7 @@ func TestKillerOverride32(t *testing.T) {
}

func TestKillerSignal32(t *testing.T) {
if !bpf.HasOverrideHelper() && !bpf.HasModifyReturn() {
t.Skip("skipping killer test, bpf_override_return helper not available")
}
if !bpf.HasSignalHelper() {
t.Skip("skipping killer test, bpf_send_signal helper not available")
}
testKillerCheckSkip(t)

test := testutils.RepoRootPath("contrib/tester-progs/killer-tester-32")
yaml := NewKillerSpecBuilder("killer-signal").
Expand Down Expand Up @@ -89,12 +78,7 @@ func TestKillerSignal32(t *testing.T) {
}

func TestKillerOverrideBothBits(t *testing.T) {
if !bpf.HasOverrideHelper() && !bpf.HasModifyReturn() {
t.Skip("skipping killer test, bpf_override_return helper not available")
}
if !bpf.HasSignalHelper() {
t.Skip("skipping killer test, bpf_send_signal helper not available")
}
testKillerCheckSkip(t)

test32 := testutils.RepoRootPath("contrib/tester-progs/killer-tester-32")
test64 := testutils.RepoRootPath("contrib/tester-progs/killer-tester")
Expand Down
20 changes: 11 additions & 9 deletions pkg/sensors/tracing/killer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ import (
"golang.org/x/sys/unix"
)

func testKillerCheckSkip(t *testing.T) {
if !bpf.HasSignalHelper() {
t.Skip("skipping killer test, bpf_send_signal helper not available")
}
if !bpf.HasOverrideHelper() && !bpf.HasModifyReturnSyscall() {
t.Skip("skipping test, neither bpf_override_return nor fmod_ret for syscalls is available")
}
}

func testKiller(t *testing.T, configHook string,
test string, test2 string,
checker *eventchecker.UnorderedEventChecker,
Expand Down Expand Up @@ -64,9 +73,7 @@ func testKiller(t *testing.T, configHook string,
}

func TestKillerOverride(t *testing.T) {
if !bpf.HasSignalHelper() {
t.Skip("skipping killer test, bpf_send_signal helper not available")
}
testKillerCheckSkip(t)

test := testutils.RepoRootPath("contrib/tester-progs/getcpu")
builder := func() *KillerSpecBuilder {
Expand Down Expand Up @@ -120,12 +127,7 @@ func TestKillerOverride(t *testing.T) {
}

func TestKillerSignal(t *testing.T) {
if !bpf.HasOverrideHelper() && !bpf.HasModifyReturn() {
t.Skip("skipping killer test, neither bpf_override_return nor fmod_ret is available")
}
if !bpf.HasSignalHelper() {
t.Skip("skipping killer test, bpf_send_signal helper not available")
}
testKillerCheckSkip(t)

test := testutils.RepoRootPath("contrib/tester-progs/killer-tester")

Expand Down

0 comments on commit 38bedec

Please sign in to comment.