Skip to content

Commit

Permalink
restructure handling of i386 syscalls
Browse files Browse the repository at this point in the history
The previous implementation of i386 syscalls was using the fact that
(most) of the i386 syscalls are implemented using functions prefixed
by __ia32_sys. This, however, is not true for ARM where most system
calls have the same implementation for aarch32 and aarch64.

Morover, x86_64 has two 32-bit abis: i386 and x32, and we might,
eventually, want to add supoprt for the latter.

This patch reworks the 32-bit syscall code and instead uses the concept
of abis. Users can now use the abi/ prefix (e.g., i386/) to specify
system calls under a specific ABI.

Signed-off-by: Kornilios Kourtis <kornilios@isovalent.com>
  • Loading branch information
kkourt committed Sep 9, 2024
1 parent 94022ca commit b81ce08
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 60 deletions.
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"
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
11 changes: 4 additions & 7 deletions pkg/arch/arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,13 @@ func AddSyscallPrefixTestHelper(t *testing.T, symbol string) string {

// 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 {
func CutSyscallPrefix(symbol string) string {
for _, prefix := range supportedArchPrefix {
if strings.HasPrefix(symbol, prefix) {
return symbol[len(prefix):], is32BitArch(arch)
return symbol[len(prefix):]
}
}
return symbol, false
return symbol
}

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
6 changes: 3 additions & 3 deletions pkg/sensors/tracing/enforcer_amd64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestEnforcerOverride32(t *testing.T) {

test := testutils.RepoRootPath("contrib/tester-progs/enforcer-tester-32")
yaml := NewEnforcerSpecBuilder("enforcer-override").
WithSyscallList("__ia32_sys_prctl").
WithSyscallList("i386/sys_prctl").
WithMatchBinaries(test).
WithOverrideValue(-17). // EEXIST
MustYAML()
Expand Down Expand Up @@ -52,7 +52,7 @@ func TestEnforcerSignal32(t *testing.T) {

test := testutils.RepoRootPath("contrib/tester-progs/enforcer-tester-32")
yaml := NewEnforcerSpecBuilder("enforcer-signal").
WithSyscallList("__ia32_sys_prctl").
WithSyscallList("i386/sys_prctl").
WithMatchBinaries(test).
WithOverrideValue(-17). // EEXIST
WithKill(9). // SigKill
Expand Down Expand Up @@ -84,7 +84,7 @@ func TestEnforcerOverrideBothBits(t *testing.T) {
test64 := testutils.RepoRootPath("contrib/tester-progs/enforcer-tester")

yaml := NewEnforcerSpecBuilder("enforcer-override").
WithSyscallList("__ia32_sys_prctl", "sys_prctl").
WithSyscallList("i386/sys_prctl", "sys_prctl").
WithMatchBinaries(test32, test64).
WithOverrideValue(-17). // EEXIST
MustYAML()
Expand Down
54 changes: 44 additions & 10 deletions pkg/sensors/tracing/lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package tracing

import (
"fmt"
"runtime"
"strings"

"github.com/cilium/tetragon/pkg/arch"
Expand Down Expand Up @@ -124,6 +125,41 @@ type listReader struct {
lists []v1alpha1.ListSpec
}

func defaultABI() string {
switch runtime.GOARCH {
case "amd64":
return "x64"
case "arm64":
return "aarch64"
}

return ""
}

func parseSyscallValue(symbol string) (abi string, name string, err error) {
arr := strings.Split(symbol, "/")
abi = defaultABI()
switch len(arr) {
case 1:
// Original version of this code tried to determine the abi by looking at the
// prefix, so we maintain this behavior but only for x86 since this was the only
// architecture that was supported.
name = arch.CutSyscallPrefix(symbol)
if len(name) < len(symbol) {
prefix := symbol[0 : len(symbol)-len(name)]
if prefix == "i386" {
abi = "i386"
}
}
case 2:
abi = arr[0]
name = arr[1]
default:
err = fmt.Errorf("invalud syscall value: '%s'", symbol)
}
return
}

func (lr *listReader) Read(name string, ty uint32) ([]uint32, error) {
list := func() *v1alpha1.ListSpec {
for idx := range lr.lists {
Expand All @@ -150,19 +186,17 @@ func (lr *listReader) Read(name string, ty uint32) ([]uint32, error) {
)

for idx := range list.Values {
sc, is32 := arch.CutSyscallPrefix(list.Values[idx])
sc = strings.TrimPrefix(sc, "sys_")

if is32 {
id = syscallinfo.GetSyscallID32(sc)
} else {
id = syscallinfo.GetSyscallID(sc)
sc, abi, err := parseSyscallValue(list.Values[idx])
if err != nil {
return nil, err
}
sc = strings.TrimPrefix(sc, "sys_")

if id == -1 {
return []uint32{}, fmt.Errorf("failed list '%s' cannot translate syscall '%s'", name, sc)
id, err = syscallinfo.SyscallID(sc, abi)
if err != nil {
return nil, fmt.Errorf("failed list '%s' cannot translate syscall '%s: %w'", name, sc, err)
}
if is32 {
if abi == "i386" {
id |= Is32Bit
}
res = append(res, uint32(id))
Expand Down
37 changes: 4 additions & 33 deletions pkg/syscallinfo/syscallinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,6 @@ func init() {
}
}

var syscallIDs = func() map[string]int {
ret := make(map[string]int, len(syscallNames))
for k, v := range syscallNames {
ret[v] = k
}
return ret
}()

var syscallIDs32 = func() map[string]int {
ret := make(map[string]int, len(syscallNames32))
for k, v := range syscallNames32 {
ret[v] = k
}
return ret
}()

func SyscallsNames() []string {
ret := make([]string, 0, len(syscallNames))

Expand All @@ -60,24 +44,11 @@ func SyscallsNames() []string {
return ret
}

// GetSyscallID returns the id of a syscall based on its name
// SyscallID returns the id of a syscall based on its name and an ABI
// ABI may be empty, in which case the default ABI is used (e.g., x64 for x86_64)
// returns -1, if no system call was found
func GetSyscallID(sysName string) int {
k := fmt.Sprintf("sys_%s", sysName)
if id, ok := syscallIDs[k]; ok {
return id
}
return -1
}

// GetSyscallID returns the id of a syscall based on its name
// returns -1, if no system call was found
func GetSyscallID32(sysName string) int {
k := fmt.Sprintf("sys_%s", sysName)
if id, ok := syscallIDs32[k]; ok {
return id
}
return -1
func SyscallID(sysName string, abi string) (int, error) {
return syscallID(sysName, abi)
}

// GetSyscallName returns the name of a syscall based on its i d
Expand Down
39 changes: 38 additions & 1 deletion pkg/syscallinfo/syscallnames_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
package syscallinfo

import (
"fmt"

"github.com/cilium/tetragon/pkg/syscallinfo/i386"
"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -376,7 +378,7 @@ var syscallNames = map[int]string{
// unix.SYS_SET_MEMPOLICY_HOME_NODE: "sys_set_mempolicy_home_node",
}

var syscallNames32 = map[int]string{
var syscallNamesi386 = map[int]string{
i386.SYS_RESTART_SYSCALL: "sys_restart_syscall",
i386.SYS_EXIT: "sys_exit",
i386.SYS_FORK: "sys_fork",
Expand Down Expand Up @@ -820,3 +822,38 @@ var syscallNames32 = map[int]string{
i386.SYS_CACHESTAT: "sys_cachestat",
i386.SYS_FCHMODAT2: "sys_fchmodat2",
}

var syscallIDs = func() map[string]int {
ret := make(map[string]int, len(syscallNames))
for k, v := range syscallNames {
ret[v] = k
}
return ret
}()

var syscallIDs_i386 = func() map[string]int {
ret := make(map[string]int, len(syscallNamesi386))
for k, v := range syscallNamesi386 {
ret[v] = k
}
return ret
}()

func syscallID(name, abi string) (int, error) {
var ids map[string]int

switch abi {
case "", "x64":
ids = syscallIDs
case "i386":
ids = syscallIDs_i386
default:
return -1, fmt.Errorf("unknown abi: %s", abi)
}

k := fmt.Sprintf("sys_%s", name)
if id, ok := ids[k]; ok {
return id, nil
}
return -1, fmt.Errorf("syscall %s for abi %s was not found", name, abi)
}
29 changes: 28 additions & 1 deletion pkg/syscallinfo/syscallnames_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
package syscallinfo

import (
"fmt"

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

Expand Down Expand Up @@ -318,4 +320,29 @@ var syscallNames = map[int]string{
// unix.SYS_SET_MEMPOLICY_HOME_NODE: "sys_set_mempolicy_home_node",
}

var syscallNames32 = map[int]string{}
var syscallIDs = func() map[string]int {
ret := make(map[string]int, len(syscallNames))
for k, v := range syscallNames {
ret[v] = k
}
return ret
}()

func syscallID(name, abi string) (int, error) {
var ids map[string]int

switch abi {
case "", "aarch64":
ids = syscallIDs
case "aarch32":
return -1, fmt.Errorf("%s not yet supported", abi)
default:
return -1, fmt.Errorf("unknown abi: %s", abi)
}

k := fmt.Sprintf("sys_%s", name)
if id, ok := ids[k]; ok {
return id, nil
}
return -1, fmt.Errorf("syscall %s for abi %s was not found", name, abi)
}

0 comments on commit b81ce08

Please sign in to comment.