Skip to content

Commit

Permalink
feat(events): streamline event data decoding types
Browse files Browse the repository at this point in the history
1. Reduce the type variance in data fields via hardcoding possible types
   decodable from the kernel.
2. Move data field decode types to the types/trace package
3. Make corresponding changes in eBPF and test code
  • Loading branch information
NDStrahilevitz committed Dec 26, 2024
1 parent 1840071 commit 8ff1b21
Show file tree
Hide file tree
Showing 12 changed files with 1,856 additions and 1,926 deletions.
143 changes: 52 additions & 91 deletions pkg/bufferdecoder/eventsreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,6 @@ import (
"github.com/aquasecurity/tracee/types/trace"
)

// argType is an enum that encodes the argument types that the BPF program may write to the shared buffer
// argument types should match defined values in ebpf code
type ArgType uint8

const (
noneT ArgType = iota
intT
uintT
longT
ulongT
offT
modeT
devT
sizeT
pointerT
strT
strArrT
sockAddrT
bytesT
u16T
credT
intArr2T
uint64ArrT
u8T
timespecT
)

// These types don't match the ones defined in the ebpf code since they are not being used by syscalls arguments.
// They have their own set of value to avoid collision in the future.
const (
argsArrT ArgType = iota + 0x80
boolT
)

// readArgFromBuff read the next argument from the buffer.
// Return the index of the argument and the parsed argument.
func readArgFromBuff(id events.ID, ebpfMsgDecoder *EbpfDecoder, fields []trace.ArgMeta,
Expand All @@ -66,50 +32,53 @@ func readArgFromBuff(id events.ID, ebpfMsgDecoder *EbpfDecoder, fields []trace.A
return 0, arg, errfmt.Errorf("invalid arg index %d", argIdx)
}
arg.ArgMeta = fields[argIdx]
argType := GetFieldType(arg.Type)
decodeType := arg.DecodeAs
if decodeType == trace.NONE_T {
return 0, arg, errfmt.Errorf("arg \"%s\" from event %d: did not declare a decode type, this should not happen", arg.Name, id)
}

switch argType {
case u8T:
switch decodeType {
case trace.U8_T:
var data uint8
err = ebpfMsgDecoder.DecodeUint8(&data)
res = data
case u16T:
case trace.U16_T:
var data uint16
err = ebpfMsgDecoder.DecodeUint16(&data)
res = data
case intT:
case trace.INT_T:
var data int32
err = ebpfMsgDecoder.DecodeInt32(&data)
res = data
case uintT, devT, modeT:
case trace.UINT_T:
var data uint32
err = ebpfMsgDecoder.DecodeUint32(&data)
res = data
case longT:
case trace.LONG_T:
var data int64
err = ebpfMsgDecoder.DecodeInt64(&data)
res = data
case ulongT, offT, sizeT:
case trace.ULONG_T:
var data uint64
err = ebpfMsgDecoder.DecodeUint64(&data)
res = data
case boolT:
case trace.BOOL_T:
var data bool
err = ebpfMsgDecoder.DecodeBool(&data)
res = data
case pointerT:
case trace.POINTER_T:
var data uint64
err = ebpfMsgDecoder.DecodeUint64(&data)
res = uintptr(data)
case sockAddrT:
case trace.SOCK_ADDR_T:
res, err = readSockaddrFromBuff(ebpfMsgDecoder)
case credT:
case trace.CRED_T:
var data SlimCred
err = ebpfMsgDecoder.DecodeSlimCred(&data)
res = trace.SlimCred(data) // here we cast to trace.SlimCred to ensure we send the public interface and not bufferdecoder.SlimCred
case strT:
case trace.STR_T:
res, err = readStringFromBuff(ebpfMsgDecoder)
case strArrT:
case trace.STR_ARR_T:
// TODO optimization: create slice after getting arrLen
var ss []string
var arrLen uint8
Expand All @@ -125,7 +94,7 @@ func readArgFromBuff(id events.ID, ebpfMsgDecoder *EbpfDecoder, fields []trace.A
ss = append(ss, s)
}
res = ss
case argsArrT:
case trace.ARGS_ARR_T:
var ss []string
var arrLen uint32
var argNum uint32
Expand All @@ -150,7 +119,7 @@ func readArgFromBuff(id events.ID, ebpfMsgDecoder *EbpfDecoder, fields []trace.A
ss = append(ss, "?")
}
res = ss
case bytesT:
case trace.BYTES_T:
var size uint32
err = ebpfMsgDecoder.DecodeUint32(&size)
if err != nil {
Expand All @@ -161,21 +130,21 @@ func readArgFromBuff(id events.ID, ebpfMsgDecoder *EbpfDecoder, fields []trace.A
return uint(argIdx), arg, errfmt.Errorf("byte array size too big: %d", size)
}
res, err = ReadByteSliceFromBuff(ebpfMsgDecoder, int(size))
case intArr2T:
case trace.INT_ARR_2_T:
var intArray [2]int32
err = ebpfMsgDecoder.DecodeIntArray(intArray[:], 2)
if err != nil {
return uint(argIdx), arg, errfmt.Errorf("error reading int elements: %v", err)
}
res = intArray
case uint64ArrT:
case trace.UINT64_ARR_T:
ulongArray := make([]uint64, 0)
err := ebpfMsgDecoder.DecodeUint64Array(&ulongArray)
if err != nil {
return uint(argIdx), arg, errfmt.Errorf("error reading ulong elements: %v", err)
}
res = ulongArray
case timespecT:
case trace.TIMESPEC_T:
var sec int64
var nsec int64
err = ebpfMsgDecoder.DecodeInt64(&sec)
Expand All @@ -187,7 +156,7 @@ func readArgFromBuff(id events.ID, ebpfMsgDecoder *EbpfDecoder, fields []trace.A

default:
// if we don't recognize the arg type, we can't parse the rest of the buffer
return uint(argIdx), arg, errfmt.Errorf("error unknown arg type %v", argType)
return uint(argIdx), arg, errfmt.Errorf("error unknown arg type %v", decodeType)
}
if err != nil {
return uint(argIdx), arg, errfmt.WrapError(err)
Expand All @@ -196,53 +165,45 @@ func readArgFromBuff(id events.ID, ebpfMsgDecoder *EbpfDecoder, fields []trace.A
return uint(argIdx), arg, nil
}

func GetFieldType(fieldType string) ArgType {
func GetFieldType(fieldType string) trace.DecodeAs {
switch fieldType {
case "int", "pid_t", "uid_t", "gid_t", "mqd_t", "clockid_t", "const clockid_t", "key_t", "key_serial_t", "timer_t":
return intT
case "unsigned int", "u32":
return uintT
case "int":
return trace.INT_T
case "unsigned int":
return trace.UINT_T
case "long":
return longT
case "unsigned long", "u64":
return ulongT
return trace.LONG_T
case "unsigned long":
return trace.ULONG_T
case "u16":
return trace.U16_T
case "u8":
return trace.U8_T
case "bool":
return boolT
case "off_t", "loff_t":
return offT
case "mode_t":
return modeT
case "dev_t":
return devT
case "size_t":
return sizeT
case "void*", "const void*":
return pointerT
case "char*", "const char*":
return strT
return trace.BOOL_T
case "void*":
return trace.POINTER_T
case "char*":
return trace.STR_T
case "const char*const*": // used by execve(at) argv and env
return strArrT
case "const char**": // used by sched_process_exec argv and envp
return argsArrT
case "const struct sockaddr*", "struct sockaddr*":
return sockAddrT
return trace.STR_ARR_T
case "const char**": // used by sched_process_exec argv and env
return trace.ARGS_ARR_T
case "struct sockaddr*":
return trace.SOCK_ADDR_T
case "bytes":
return bytesT
return trace.BYTES_T
case "int[2]":
return intArr2T
return trace.INT_ARR_2_T
case "slim_cred_t":
return credT
case "umode_t":
return u16T
case "u8":
return u8T
return trace.CRED_T
case "unsigned long[]", "[]trace.HookedSymbolData":
return uint64ArrT
case "struct timespec*", "const struct timespec*":
return timespecT
return trace.UINT64_ARR_T
case "struct timespec*":
return trace.TIMESPEC_T
default:
// Default to pointer (printed as hex) for unsupported types
return pointerT
return trace.POINTER_T
}
}

Expand Down
32 changes: 16 additions & 16 deletions pkg/bufferdecoder/eventsreader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,71 +24,71 @@ func TestReadArgFromBuff(t *testing.T) {
input: []byte{0,
0xFF, 0xFF, 0xFF, 0xFF, // -1
},
fields: []trace.ArgMeta{{Type: "int", Name: "int0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.INT_T, Name: "int0"}},
expectedArg: int32(-1),
},
{
name: "uintT",
input: []byte{0,
0xFF, 0xFF, 0xFF, 0xFF, // 4294967295
},
fields: []trace.ArgMeta{{Type: "unsigned int", Name: "uint0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.UINT_T, Name: "uint0"}},
expectedArg: uint32(4294967295),
},
{
name: "longT",
input: []byte{0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // -1
},
fields: []trace.ArgMeta{{Type: "long", Name: "long0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.LONG_T, Name: "long0"}},
expectedArg: int64(-1),
},
{
name: "ulongT",
input: []byte{0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 18446744073709551615
},
fields: []trace.ArgMeta{{Type: "unsigned long", Name: "ulong0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.ULONG_T, Name: "ulong0"}},
expectedArg: uint64(18446744073709551615),
},
{
name: "modeT",
input: []byte{0,
0xB6, 0x11, 0x0, 0x0, // 0x000011B6 == 010666 == S_IFIFO|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
},
fields: []trace.ArgMeta{{Type: "mode_t", Name: "modeT0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.UINT_T, Name: "modeT0"}},
expectedArg: uint32(0x11b6),
},
{
name: "devT",
input: []byte{0,
0xFF, 0xFF, 0xFF, 0xFF, // 4294967295
},
fields: []trace.ArgMeta{{Type: "dev_t", Name: "devT0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.UINT_T, Name: "devT0"}},
expectedArg: uint32(4294967295),
},
{
name: "offT",
input: []byte{0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 18446744073709551615
},
fields: []trace.ArgMeta{{Type: "off_t", Name: "offT0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.ULONG_T, Name: "offT0"}},
expectedArg: uint64(18446744073709551615),
},
{
name: "loffT",
input: []byte{0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 18446744073709551615
},
fields: []trace.ArgMeta{{Type: "loff_t", Name: "loffT0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.ULONG_T, Name: "loffT0"}},
expectedArg: uint64(18446744073709551615),
},
{ // This is expected to fail. TODO: change pointer parsed type to uint64
name: "pointerT",
input: []byte{0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
},
fields: []trace.ArgMeta{{Type: "void*", Name: "pointer0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.POINTER_T, Name: "pointer0"}},
expectedArg: uintptr(0xFFFFFFFFFFFFFFFF),
},
{
Expand All @@ -97,7 +97,7 @@ func TestReadArgFromBuff(t *testing.T) {
16, 0, 0, 0, // len=16
47, 117, 115, 114, 47, 98, 105, 110, 47, 100, 111, 99, 107, 101, 114, 0, // /usr/bin/docker
},
fields: []trace.ArgMeta{{Type: "const char*", Name: "str0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.STR_T, Name: "str0"}},
expectedArg: "/usr/bin/docker",
},
{
Expand All @@ -109,7 +109,7 @@ func TestReadArgFromBuff(t *testing.T) {
7, 0, 0, 0, // len=7
100, 111, 99, 107, 101, 114, 0, // docker
},
fields: []trace.ArgMeta{{Type: "const char*const*", Name: "strArr0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.STR_ARR_T, Name: "strArr0"}},
expectedArg: []string{"/usr/bin", "docker"},
},
{
Expand All @@ -120,7 +120,7 @@ func TestReadArgFromBuff(t *testing.T) {
47, 117, 115, 114, 47, 98, 105, 110, 0, // /usr/bin
100, 111, 99, 107, 101, 114, 0, // docker
},
fields: []trace.ArgMeta{{Type: "const char**", Name: "argsArr0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.ARGS_ARR_T, Name: "argsArr0"}},
expectedArg: []string{"/usr/bin", "docker"},
},
{
Expand All @@ -131,7 +131,7 @@ func TestReadArgFromBuff(t *testing.T) {
0xFF, 0xFF, 0xFF, 0xFF, // sin_addr=255.255.255.255
0, 0, 0, 0, 0, 0, 0, 0, // padding[8]
},
fields: []trace.ArgMeta{{Type: "struct sockaddr*", Name: "sockAddr0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.SOCK_ADDR_T, Name: "sockAddr0"}},
expectedArg: map[string]string(map[string]string{"sa_family": "AF_INET", "sin_addr": "255.255.255.255", "sin_port": "65535"}),
},
{
Expand All @@ -140,7 +140,7 @@ func TestReadArgFromBuff(t *testing.T) {
1, 0, // sa_family=AF_UNIX
47, 116, 109, 112, 47, 115, 111, 99, 107, 101, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 101, 110, 0, 0, 0, // sun_path=/tmp/socket
},
fields: []trace.ArgMeta{{Type: "struct sockaddr*", Name: "sockAddr0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.SOCK_ADDR_T, Name: "sockAddr0"}},
expectedArg: map[string]string{"sa_family": "AF_UNIX", "sun_path": "/tmp/socket"},
},
{
Expand All @@ -153,15 +153,15 @@ func TestReadArgFromBuff(t *testing.T) {
input: []byte{0,
0, 0, 0, 1, // len=16777216
},
fields: []trace.ArgMeta{{Type: "const char*", Name: "str0"}},
fields: []trace.ArgMeta{{DecodeAs: trace.STR_T, Name: "str0"}},
expectedError: errors.New("string size too big: 16777216"),
},
{
name: "multiple fields",
input: []byte{1,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 18446744073709551615
},
fields: []trace.ArgMeta{{Type: "const char*", Name: "str0"}, {Type: "off_t", Name: "offT1"}},
fields: []trace.ArgMeta{{DecodeAs: trace.STR_T, Name: "str0"}, {DecodeAs: trace.ULONG_T, Name: "offT1"}},
expectedArg: uint64(18446744073709551615),
},
}
Expand Down
Loading

0 comments on commit 8ff1b21

Please sign in to comment.