Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

killer sensor: add support for fmod_ret #1953

Merged
merged 15 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions bpf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ PROCESS = bpf_execve_event.o bpf_execve_event_v53.o bpf_fork.o bpf_exit.o bpf_ge
bpf_multi_kprobe_v61.o bpf_multi_retkprobe_v61.o \
bpf_generic_uprobe_v61.o \
bpf_loader.o \
bpf_killer.o bpf_multi_killer.o
bpf_killer.o bpf_multi_killer.o bpf_fmodret_killer.o

CGROUP = bpf_cgroup_mkdir.o bpf_cgroup_rmdir.o bpf_cgroup_release.o
BPFTEST = bpf_lseek.o bpf_globals.o
Expand Down Expand Up @@ -75,6 +75,30 @@ objs/%.ll: $(ALIGNCHECKERDIR)%.c
$(DEPSDIR)%.d: $(ALIGNCHECKERDIR)%.c
$(CLANG) $(CLANG_FLAGS) -MM -MP -MT $(patsubst $(DEPSDIR)%.d, $(OBJSDIR)%.ll, $@) $< > $@


# Killer programs: bpf_killer, bpf_multi_killer, bpf_fmodret_killer

## bpf_killer: __BPF_OVERRIDE_RETURN, but no __MULTI_KPROBE
objs/bpf_killer.ll: process/bpf_killer.c
$(CLANG) $(CLANG_FLAGS) -D__BPF_OVERRIDE_RETURN -c $< -o $@

$(DEPSDIR)bpf_killer.d: process/bpf_killer.c
$(CLANG) $(CLANG_FLAGS) -D__BPF_OVERRIDE_RETURN -MM -MP -MT $(patsubst $(DEPSDIR)%.d, $(OBJSDIR)%.ll, $@) $< > $@

## bpf_multi_killer: __BPF_OVERRIDE_RETURN and __MULTI_KPROBE
objs/bpf_multi_killer.ll: process/bpf_killer.c
$(CLANG) $(CLANG_FLAGS) -D__BPF_OVERRIDE_RETURN -D__MULTI_KPROBE -c $< -o $@

$(DEPSDIR)/bpf_multi_killer.d: process/bpf_killer.c
$(CLANG) $(CLANG_FLAGS) -D__BPF_OVERRIDE_RETURN -D__MULTI_KPROBE -MM -MP -MT $(patsubst $(DEPSDIR)%.d, $(OBJSDIR)%.ll, $@) $< > $@

## bpf_fmodret_killer no bpf_override_return: we need fmod_ret
objs/bpf_fmodret_killer.ll: process/bpf_killer.c
$(CLANG) $(CLANG_FLAGS) -c $< -o $@

$(DEPSDIR)/bpf_fmodret_killer.d: process/bpf_killer.c
$(CLANG) $(CLANG_FLAGS) -MM -MP -MT $(patsubst $(DEPSDIR)%.d, $(OBJSDIR)%.ll, $@) $< > $@

# PROCESSDIR
objs/%.ll: $(PROCESSDIR)%.c
$(CLANG) $(CLANG_FLAGS) -c $< -o $@
Expand All @@ -88,11 +112,6 @@ objs/%_v53.ll:
$(DEPSDIR)%.d: $(PROCESSDIR)%.c
$(CLANG) $(CLANG_FLAGS) -MM -MP -MT $(patsubst $(DEPSDIR)%.d, $(OBJSDIR)%.ll, $@) $< > $@

objs/bpf_multi_killer.ll: process/bpf_killer.c
$(CLANG) $(CLANG_FLAGS) -D__LARGE_BPF_PROG -D__MULTI_KPROBE -c $< -o $@

$(DEPSDIR)/bpf_multi_killer.d: process/bpf_killer.c
$(CLANG) $(CLANG_FLAGS) -D__LARGE_BPF_PROG -D__MULTI_KPROBE -MM -MP -MT $(patsubst $(DEPSDIR)%.d, $(OBJSDIR)%.ll, $@) $< > $@

$(DEPSDIR)%_v53.d:
$(CLANG) $(CLANG_FLAGS) -D__LARGE_BPF_PROG -MM -MP -MT $(patsubst $(DEPSDIR)%.d, $(OBJSDIR)%.ll, $@) $< > $@
Expand Down
4 changes: 2 additions & 2 deletions bpf/lib/bpf_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,13 @@ static inline __attribute__((always_inline)) struct execve_map_value *
event_find_curr(__u32 *ppid, bool *walked)
{
struct task_struct *task = (struct task_struct *)get_current_task();
__u32 pid = get_current_pid_tgid() >> 32;
struct execve_map_value *value = 0;
int i;
__u32 pid;

#pragma unroll
for (i = 0; i < 4; i++) {
probe_read(&pid, sizeof(pid), _(&task->tgid));
value = execve_map_get_noinit(pid);
if (value && value->key.ktime != 0)
break;
Expand All @@ -172,7 +173,6 @@ event_find_curr(__u32 *ppid, bool *walked)
probe_read(&task, sizeof(task), _(&task->real_parent));
if (!task)
break;
probe_read(&pid, sizeof(pid), _(&task->tgid));
}
*ppid = pid;
return value;
Expand Down
45 changes: 35 additions & 10 deletions bpf/process/bpf_killer.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@

char _license[] __attribute__((section("license"), used)) = "Dual BSD/GPL";

#ifdef __MULTI_KPROBE
#define MAIN "kprobe.multi/killer"
#else
#define MAIN "kprobe/killer"
#endif

__attribute__((section(MAIN), used)) int
killer(void *ctx)
static inline __attribute__((always_inline)) int
do_killer(void *ctx)
{
__u64 id = get_current_pid_tgid();
struct killer_data *data;
Expand All @@ -18,11 +12,42 @@ killer(void *ctx)
if (!data)
return 0;

if (data->error)
override_return(ctx, data->error);
if (data->signal)
send_signal(data->signal);

map_delete_elem(&killer_data, &id);
return data->error;
}

#if defined(__BPF_OVERRIDE_RETURN)

#ifdef __MULTI_KPROBE
#define MAIN "kprobe.multi/killer"
#else
#define MAIN "kprobe/killer"
#endif

__attribute__((section(MAIN), used)) int
multi_kprobe_killer(void *ctx)
{
long ret;

ret = do_killer(ctx);
if (ret)
override_return(ctx, ret);

return 0;
}

#else /* !__BPF_OVERRIDE_RETURN */

/* Putting security_task_prctl in here to pass contrib/verify/verify.sh test,
* in normal run the function is set by tetragon dynamically.
*/
__attribute__((section("fmod_ret/security_task_prctl"), used)) long
fmodret_killer(void *ctx)
{
return do_killer(ctx);
}

#endif
2 changes: 2 additions & 0 deletions contrib/tester-progs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ threads-tester
bench-reader
threads-exit
killer-tester
killer-tester-32
/getcpu
6 changes: 5 additions & 1 deletion contrib/tester-progs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ PROGS = sigkill-tester \
bench-reader \
threads-exit \
killer-tester \
drop-privileges
drop-privileges \
getcpu

# For now killer-tester is compiled to 32-bit only on x86_64 as we want
# to test 32-bit binaries and system calls compatibility layer.
Expand Down Expand Up @@ -79,6 +80,9 @@ killer-tester-32: killer-tester.c
lseek-pipe: FORCE
go build -o lseek-pipe ./go/lseek-pipe

getcpu: FORCE
go build -o getcpu ./go/getcpu

.PHONY: clean
clean:
rm -f $(PROGS)
Expand Down
22 changes: 22 additions & 0 deletions contrib/tester-progs/go/getcpu/getcpu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Tetragon

package main

import (
"os"
"unsafe"

"golang.org/x/sys/unix"
)

func main() {
var cpu, node int
_, _, err := unix.Syscall(
unix.SYS_GETCPU,
uintptr(unsafe.Pointer(&cpu)),
uintptr(unsafe.Pointer(&node)),
0,
)
os.Exit(int(err))
}
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())
}
36 changes: 36 additions & 0 deletions pkg/sensors/program/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,42 @@ func LoadMultiKprobeProgram(bpfDir, mapDir string, load *Program, verbose int) e
return loadProgram(bpfDir, []string{mapDir}, load, opts, verbose)
}

func LoadFmodRetProgram(bpfDir, mapDir string, load *Program, progName string, verbose int) error {
opts := &loadOpts{
attach: func(
coll *ebpf.Collection,
collSpec *ebpf.CollectionSpec,
prog *ebpf.Program,
spec *ebpf.ProgramSpec,
) (unloader.Unloader, error) {
linkFn := func() (link.Link, error) {
return link.AttachTracing(link.TracingOptions{
Program: prog,
})
}
lnk, err := linkFn()
if err != nil {
return nil, fmt.Errorf("attaching '%s' failed: %w", spec.Name, err)
}
return &unloader.RelinkUnloader{
UnloadProg: unloader.PinUnloader{Prog: prog}.Unload,
IsLinked: true,
Link: lnk,
RelinkFn: linkFn,
}, nil
},
open: func(coll *ebpf.CollectionSpec) error {
progSpec, ok := coll.Programs[progName]
if !ok {
return fmt.Errorf("progName %s not in collecition spec programs: %+v", progName, coll.Programs)
}
progSpec.AttachTo = load.Attach
return nil
},
}
return loadProgram(bpfDir, []string{mapDir}, load, opts, verbose)
}

func LoadTracingProgram(bpfDir, mapDir string, load *Program, verbose int) error {
opts := &loadOpts{
attach: TracingAttach(),
Expand Down
9 changes: 4 additions & 5 deletions pkg/sensors/tracing/generickprobe.go
Original file line number Diff line number Diff line change
Expand Up @@ -553,18 +553,17 @@ func createGenericKprobeSensor(
kprobes := spec.KProbes
lists := spec.Lists

options, err := getKprobeOptions(spec.Options)
specOpts, err := getSpecOptions(spec.Options)
if err != nil {
return nil, fmt.Errorf("failed to set options: %s", err)
return nil, fmt.Errorf("failed to get spec options: %s", err)
}

// use multi kprobe only if:
// - it's not disabled by spec option
// - it's not disabled by command line option
// - there's support detected
if !options.DisableKprobeMulti {
useMulti = !option.Config.DisableKprobeMulti &&
bpf.HasKprobeMulti()
if !specOpts.DisableKprobeMulti {
useMulti = !option.Config.DisableKprobeMulti && bpf.HasKprobeMulti()
}

if useMulti {
Expand Down
Loading
Loading