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

support 32-bit ARM (aarch32) syscalls #2898

Merged
merged 10 commits into from
Sep 19, 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
17 changes: 11 additions & 6 deletions .github/workflows/gotests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ jobs:
# renovate: datasource=golang-version depName=go
go-version: '1.22.7'

- name: Install dependencies x86
run: |
sudo apt-get update
sudo apt-get -y install libc6-dev-i386
if: ${{ matrix.os == 'ubuntu-20.04' }}

- name: Install dependencies
run: |
sudo apt-get update
Expand All @@ -46,6 +40,17 @@ jobs:
echo `which llc`
echo `clang --version`


- name: Install dependencies x86
run: |
sudo apt-get -y install libc6-dev-i386
if: ${{ matrix.os == 'ubuntu-20.04' }}
kkourt marked this conversation as resolved.
Show resolved Hide resolved

- name: Install dependencies ARM
run: |
sudo apt-get -y install gcc-arm-linux-gnueabihf
if: ${{ matrix.os == 'actuated-arm64-4cpu-8gb' }}

- name: Install bpftool
uses: mtardy/setup-bpftool@adeab4f9332cc28db56064a93911860d0775665b # v1.0.3
with:
Expand Down
21 changes: 19 additions & 2 deletions bpf/process/generic_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,8 @@ generic_process_event(void *ctx, struct bpf_map_def *heap_map,
return 0;
}

#if defined(__TARGET_ARCH_x86)
#define TS_COMPAT 0x0002

#ifdef __TARGET_ARCH_x86
FUNC_INLINE void
generic_setup_32bit_syscall(struct msg_generic_kprobe *e, u8 op)
{
Expand All @@ -140,6 +139,24 @@ generic_setup_32bit_syscall(struct msg_generic_kprobe *e, u8 op)
break;
}
}
#elif defined(__TARGET_ARCH_arm64)
#define TIF_32BIT 22
FUNC_INLINE void
generic_setup_32bit_syscall(struct msg_generic_kprobe *e, u8 op)
{
struct thread_info *info;
unsigned long flags;

switch (op) {
case MSG_OP_GENERIC_TRACEPOINT:
case MSG_OP_GENERIC_KPROBE:
info = (struct thread_info *)get_current_task();
probe_read(&flags, sizeof(flags), _(&info->flags));
e->sel.is32BitSyscall = flags & (1 << TIF_32BIT);
default:
break;
}
}
#else
#define generic_setup_32bit_syscall(e, op)
#endif
Expand Down
16 changes: 9 additions & 7 deletions contrib/tester-progs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,13 @@ PROGS = sigkill-tester \
bench-reader \
threads-exit \
enforcer-tester \
enforcer-tester-32 \
drop-privileges \
getcpu \
direct-write-tester \
change-capabilities \
user-stacktrace

# For now enforcer-tester is compiled to 32-bit only on x86_64 as we want
# to test 32-bit binaries and system calls compatibility layer.
ifeq ($(shell uname -m),x86_64)
PROGS += enforcer-tester-32
endif

all: $(PROGS)

Expand Down Expand Up @@ -78,10 +74,16 @@ uprobe-test-1: uprobe-test.c libuprobe.so
uprobe-test-2: uprobe-test-1
cp uprobe-test-1 uprobe-test-2

# -m32 is an x86_64 flag.
# NB(kkourt) we compile this as static to avoid the need for ia32 libs in VMs
# NB: compile the 32 bit version of enforcer-tester statically so that we don't
# need additional libraries in the VMs
enforcer-tester-32: enforcer-tester.c
ifeq ($(shell uname -m),x86_64)
$(GCC) -Wall -m32 -static $< -o $@
else ifeq ($(shell uname -m),aarch64)
arm-linux-gnueabihf-gcc -Wall -static $< -o $@
else
$(error unsupported arch)
endif

lseek-pipe: FORCE
go build -o lseek-pipe ./go/lseek-pipe
Expand Down
8 changes: 4 additions & 4 deletions docs/content/en/docs/concepts/tracing-policy/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,8 @@ spec:

Syscalls specified with `sys_` prefix are translated to their 64 bit equivalent function names.

It's possible to specify 32 bit syscall by using its full function name that
includes specific architecture native prefix (like `__ia32_` for `x86`):
It's possible to specify a syscall for an alternative ABI by using the ABI name as a prefix.
For example:

```yaml
spec:
Expand All @@ -535,7 +535,7 @@ spec:
type: "syscalls"
values:
- "sys_dup"
- "__ia32_sys_dup"
- "i386/sys_dup"
kkourt marked this conversation as resolved.
Show resolved Hide resolved
name: "another"
- "sys_open"
- "sys_close"
Expand Down Expand Up @@ -665,7 +665,7 @@ spec:
type: "syscalls"
values:
- "sys_dup"
- "__ia32_sys_dup"
- "i386/sys_dup"
tracepoints:
- subsystem: "raw_syscalls"
event: "sys_enter"
Expand Down
21 changes: 11 additions & 10 deletions pkg/arch/arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,19 @@ func AddSyscallPrefixTestHelper(t *testing.T, symbol string) string {
return syscallName
}

// CutSyscallPrefix removes a potential arch specific prefix from the symbol
// and returns true in second return argument if the prefix is 32 bits
func CutSyscallPrefix(symbol string) (string, bool) {
is32BitArch := func(arch string) bool {
return arch == "i386"
}
for arch, prefix := range supportedArchPrefix {
if strings.HasPrefix(symbol, prefix) {
return symbol[len(prefix):], is32BitArch(arch)
// CutSyscallPrefix removes a potential arch specific prefix from the symbol.
// If a prefix was removed, it returns the corresponding arch as a first argument.
func CutSyscallPrefix(symbol string) (arch string, name string) {
for a, p := range supportedArchPrefix {
if rest, ok := strings.CutPrefix(symbol, p); ok {
arch = a
name = rest
return
}
}
return symbol, false

name = symbol
return
}

func HasSyscallPrefix(symbol string) bool {
Expand Down
2 changes: 1 addition & 1 deletion pkg/encoder/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ func (p *CompactEncoder) EventToString(response *tetragon.GetEventsResponse) (st
return "", ErrMissingProcessInfo
}
processInfo, caps := p.Colorer.ProcessInfo(response.NodeName, kprobe.Process)
sc, _ := arch.CutSyscallPrefix(kprobe.FunctionName)
_, sc := arch.CutSyscallPrefix(kprobe.FunctionName)
switch sc {
case "sys_write":
event := p.Colorer.Blue.Sprintf("📝 %-7s", "write")
Expand Down
4 changes: 3 additions & 1 deletion pkg/observer/observertesthelper/observer_test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,9 @@ func loadObserver(tb testing.TB, ctx context.Context, base *sensors.Sensor,
if err := base.Load(option.Config.BpfDir); err != nil {
tb.Fatalf("Load base error: %s\n", err)
}
tb.Cleanup(func() {
base.Unload()
})
kkourt marked this conversation as resolved.
Show resolved Hide resolved

if tp != nil {
if err := observer.GetSensorManager().AddTracingPolicy(ctx, tp); err != nil {
Expand All @@ -433,7 +436,6 @@ func loadObserver(tb testing.TB, ctx context.Context, base *sensors.Sensor,

tb.Cleanup(func() {
observer.RemoveSensors(ctx)
base.Unload()
})
return nil
}
Expand Down
71 changes: 36 additions & 35 deletions pkg/sensors/tracing/enforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,47 +198,48 @@ func (kp *enforcerPolicy) createEnforcerSensor(
)

kh := &enforcerHandler{}

// get all the syscalls
for idx := range enforcer.Calls {
sym := enforcer.Calls[idx]
if strings.HasPrefix(sym, "list:") {
listName := sym[len("list:"):]

list := getList(listName, lists)
for _, call := range enforcer.Calls {
var symsToAdd []string
if isL, list := isList(call, lists); isL {
if list == nil {
return nil, fmt.Errorf("Error list '%s' not found", listName)
return nil, fmt.Errorf("Error list '%s' not found", call)
}

kh.syscallsSyms = append(kh.syscallsSyms, list.Values...)
continue
}

kh.syscallsSyms = append(kh.syscallsSyms, sym)
}

var err error

// fix syscalls
for idx, sym := range kh.syscallsSyms {
isPrefix := arch.HasSyscallPrefix(sym)
isSyscall := strings.HasPrefix(sym, "sys_")
isSecurity := strings.HasPrefix(sym, "security_")

if !isSyscall && !isSecurity && !isPrefix {
return nil, fmt.Errorf("enforcer sensor requires either syscall or security_ functions")
switch list.Type {
case "syscalls":
syms, err := getSyscallListSymbols(list)
if err != nil {
return nil, err
}
hasSyscall = true
// we know that this is a list of syscalls, so no need to check them
kh.syscallsSyms = append(kh.syscallsSyms, syms...)
continue
default:
// for everything else, we just append the symbols
symsToAdd = list.Values
}
} else {
symsToAdd = []string{call}
}

if isSyscall {
sym, err = arch.AddSyscallPrefix(sym)
if err != nil {
return nil, err
// check and add the rest of the symbols
for _, sym := range symsToAdd {
if arch.HasSyscallPrefix(sym) {
hasSyscall = true
} else if strings.HasPrefix(sym, "sys_") {
hasSyscall = true
var err error
sym, err = arch.AddSyscallPrefix(sym)
if err != nil {
return nil, err
}
} else if strings.HasPrefix(sym, "security_") {
hasSecurity = true
} else {
return nil, fmt.Errorf("enforcer sensor requires either syscall or security_ functions and symbol '%s' appears to be neither", sym)
}
kh.syscallsSyms[idx] = sym
kh.syscallsSyms = append(kh.syscallsSyms, sym)
}

hasSyscall = hasSyscall || isSyscall || isPrefix
hasSecurity = hasSecurity || isSecurity
}

// register enforcer sensor
Expand Down
Loading
Loading