diff --git a/pkg/events/parse_args.go b/pkg/events/parse_args.go index c132f7bc851e..5f9f184101d9 100644 --- a/pkg/events/parse_args.go +++ b/pkg/events/parse_args.go @@ -13,20 +13,9 @@ import ( "github.com/aquasecurity/tracee/types/trace" ) -func emptyString(arg *trace.Argument) { - arg.Type = "string" - arg.Value = "" -} - -func parseOrEmptyString(arg *trace.Argument, sysArg parsers.SystemFunctionArgument, err error) { - emptyString(arg) - if err == nil { - arg.Value = sysArg.String() - } -} - func ParseArgs(event *trace.Event) error { for i := range event.Args { + // Convert uintptr to hex string if ptr, isUintptr := event.Args[i].Value.(uintptr); isUintptr { v := []byte{'0', 'x'} v = strconv.AppendUint(v, uint64(ptr), 16) @@ -38,258 +27,203 @@ func ParseArgs(event *trace.Event) error { case MemProtAlert: if alertArg := GetArg(event, "alert"); alertArg != nil { if alert, isUint32 := alertArg.Value.(uint32); isUint32 { - alertArg.Value = trace.MemProtAlert(alert).String() - alertArg.Type = "string" + parseMemProtAlert(alertArg, alert) } } if protArg := GetArg(event, "prot"); protArg != nil { if prot, isInt32 := protArg.Value.(int32); isInt32 { - mmapProtArgument := parsers.ParseMmapProt(uint64(prot)) - parseOrEmptyString(protArg, mmapProtArgument, nil) + parseMMapProt(protArg, uint64(prot)) } } if prevProtArg := GetArg(event, "prev_prot"); prevProtArg != nil { if prevProt, isInt32 := prevProtArg.Value.(int32); isInt32 { - mmapProtArgument := parsers.ParseMmapProt(uint64(prevProt)) - parseOrEmptyString(prevProtArg, mmapProtArgument, nil) + parseMMapProt(prevProtArg, uint64(prevProt)) } } case SysEnter, SysExit: if syscallArg := GetArg(event, "syscall"); syscallArg != nil { if id, isInt32 := syscallArg.Value.(int32); isInt32 { - if Core.IsDefined(ID(id)) { - eventDefinition := Core.GetDefinitionByID(ID(id)) - if eventDefinition.IsSyscall() { - syscallArg.Value = eventDefinition.GetName() - syscallArg.Type = "string" - } - } + parseSyscall(syscallArg, id) } } case CapCapable: if capArg := GetArg(event, "cap"); capArg != nil { if capability, isInt32 := capArg.Value.(int32); isInt32 { - capabilityFlagArgument, err := parsers.ParseCapability(uint64(capability)) - parseOrEmptyString(capArg, capabilityFlagArgument, err) + parseCapability(capArg, uint64(capability)) } } case SecurityMmapFile, DoMmap: if protArg := GetArg(event, "prot"); protArg != nil { if prot, isUint64 := protArg.Value.(uint64); isUint64 { - mmapProtArgument := parsers.ParseMmapProt(prot) - parseOrEmptyString(protArg, mmapProtArgument, nil) + parseMMapProt(protArg, prot) } } case Mmap, Mprotect, PkeyMprotect: if protArg := GetArg(event, "prot"); protArg != nil { if prot, isInt32 := protArg.Value.(int32); isInt32 { - mmapProtArgument := parsers.ParseMmapProt(uint64(prot)) - parseOrEmptyString(protArg, mmapProtArgument, nil) + parseMMapProt(protArg, uint64(prot)) } } case SecurityFileMprotect: if protArg := GetArg(event, "prot"); protArg != nil { if prot, isInt32 := protArg.Value.(int32); isInt32 { - mmapProtArgument := parsers.ParseMmapProt(uint64(prot)) - parseOrEmptyString(protArg, mmapProtArgument, nil) + parseMMapProt(protArg, uint64(prot)) } } if prevProtArg := GetArg(event, "prev_prot"); prevProtArg != nil { if prevProt, isInt32 := prevProtArg.Value.(int32); isInt32 { - mmapProtArgument := parsers.ParseMmapProt(uint64(prevProt)) - parseOrEmptyString(prevProtArg, mmapProtArgument, nil) + parseMMapProt(prevProtArg, uint64(prevProt)) } } case Ptrace: if reqArg := GetArg(event, "request"); reqArg != nil { if req, isInt64 := reqArg.Value.(int64); isInt64 { - ptraceRequestArgument, err := parsers.ParsePtraceRequestArgument(uint64(req)) - parseOrEmptyString(reqArg, ptraceRequestArgument, err) + parsePtraceRequestArgument(reqArg, uint64(req)) } } case Prctl: if optArg := GetArg(event, "option"); optArg != nil { if opt, isInt32 := optArg.Value.(int32); isInt32 { - prctlOptionArgument, err := parsers.ParsePrctlOption(uint64(opt)) - parseOrEmptyString(optArg, prctlOptionArgument, err) + parsePrctlOption(optArg, uint64(opt)) } } case Socketcall: if callArg := GetArg(event, "call"); callArg != nil { if call, isInt32 := callArg.Value.(int32); isInt32 { - socketcallArgument, err := parsers.ParseSocketcallCall(uint64(call)) - parseOrEmptyString(callArg, socketcallArgument, err) + parseSocketcallCall(callArg, uint64(call)) } } case Socket: if domArg := GetArg(event, "domain"); domArg != nil { if dom, isInt32 := domArg.Value.(int32); isInt32 { - socketDomainArgument, err := parsers.ParseSocketDomainArgument(uint64(dom)) - parseOrEmptyString(domArg, socketDomainArgument, err) + parseSocketDomainArgument(domArg, uint64(dom)) } } if typeArg := GetArg(event, "type"); typeArg != nil { if typ, isInt32 := typeArg.Value.(int32); isInt32 { - socketTypeArgument, err := parsers.ParseSocketType(uint64(typ)) - parseOrEmptyString(typeArg, socketTypeArgument, err) + parseSocketType(typeArg, uint64(typ)) } } case SecuritySocketCreate, SecuritySocketConnect: if domArg := GetArg(event, "family"); domArg != nil { if dom, isInt32 := domArg.Value.(int32); isInt32 { - socketDomainArgument, err := parsers.ParseSocketDomainArgument(uint64(dom)) - parseOrEmptyString(domArg, socketDomainArgument, err) + parseSocketDomainArgument(domArg, uint64(dom)) } } if typeArg := GetArg(event, "type"); typeArg != nil { if typ, isInt32 := typeArg.Value.(int32); isInt32 { - socketTypeArgument, err := parsers.ParseSocketType(uint64(typ)) - parseOrEmptyString(typeArg, socketTypeArgument, err) + parseSocketType(typeArg, uint64(typ)) } } case Access, Faccessat: if modeArg := GetArg(event, "mode"); modeArg != nil { if mode, isInt32 := modeArg.Value.(int32); isInt32 { - accessModeArgument, err := parsers.ParseAccessMode(uint64(mode)) - parseOrEmptyString(modeArg, accessModeArgument, err) + parseAccessMode(modeArg, uint64(mode)) } } case Execveat: if flagsArg := GetArg(event, "flags"); flagsArg != nil { if flags, isInt32 := flagsArg.Value.(int32); isInt32 { - execFlagArgument, err := parsers.ParseExecFlag(uint64(flags)) - parseOrEmptyString(flagsArg, execFlagArgument, err) + parseExecFlag(flagsArg, uint64(flags)) } } case Open, Openat, SecurityFileOpen: if flagsArg := GetArg(event, "flags"); flagsArg != nil { if flags, isInt32 := flagsArg.Value.(int32); isInt32 { - openFlagArgument, err := parsers.ParseOpenFlagArgument(uint64(flags)) - parseOrEmptyString(flagsArg, openFlagArgument, err) + parseOpenFlagArgument(flagsArg, uint64(flags)) } } case Mknod, Mknodat, Chmod, Fchmod, Fchmodat: if modeArg := GetArg(event, "mode"); modeArg != nil { if mode, isUint32 := modeArg.Value.(uint32); isUint32 { - inodeModeArgument, err := parsers.ParseInodeMode(uint64(mode)) - parseOrEmptyString(modeArg, inodeModeArgument, err) + parseInodeMode(modeArg, uint64(mode)) } } case SecurityInodeMknod: if modeArg := GetArg(event, "mode"); modeArg != nil { if mode, isUint16 := modeArg.Value.(uint16); isUint16 { - inodeModeArgument, err := parsers.ParseInodeMode(uint64(mode)) - parseOrEmptyString(modeArg, inodeModeArgument, err) + parseInodeMode(modeArg, uint64(mode)) } } case Clone: if flagsArg := GetArg(event, "flags"); flagsArg != nil { if flags, isUint64 := flagsArg.Value.(uint64); isUint64 { - cloneFlagArgument, err := parsers.ParseCloneFlags(uint64(flags)) - parseOrEmptyString(flagsArg, cloneFlagArgument, err) + parseCloneFlags(flagsArg, flags) } } case Bpf, SecurityBPF: if cmdArg := GetArg(event, "cmd"); cmdArg != nil { if cmd, isInt32 := cmdArg.Value.(int32); isInt32 { - bpfCommandArgument, err := parsers.ParseBPFCmd(uint64(cmd)) - parseOrEmptyString(cmdArg, bpfCommandArgument, err) + parseBPFCmd(cmdArg, uint64(cmd)) } } case SecurityKernelReadFile, SecurityPostReadFile: if typeArg := GetArg(event, "type"); typeArg != nil { if readFileId, isInt32 := typeArg.Value.(trace.KernelReadType); isInt32 { - emptyString(typeArg) + typeArg.Type = "string" typeArg.Value = readFileId.String() } } case SchedProcessExec: if modeArg := GetArg(event, "stdin_type"); modeArg != nil { if mode, isUint16 := modeArg.Value.(uint16); isUint16 { - inodeModeArgument, err := parsers.ParseInodeMode(uint64(mode)) - parseOrEmptyString(modeArg, inodeModeArgument, err) + parseInodeMode(modeArg, uint64(mode)) } } case DirtyPipeSplice: if modeArg := GetArg(event, "in_file_type"); modeArg != nil { if mode, isUint16 := modeArg.Value.(uint16); isUint16 { - inodeModeArgument, err := parsers.ParseInodeMode(uint64(mode)) - parseOrEmptyString(modeArg, inodeModeArgument, err) + parseInodeMode(modeArg, uint64(mode)) } } case SecuritySocketSetsockopt, Setsockopt, Getsockopt: if levelArg := GetArg(event, "level"); levelArg != nil { if level, isInt := levelArg.Value.(int32); isInt { - levelArgument, err := parsers.ParseSocketLevel(uint64(level)) - parseOrEmptyString(levelArg, levelArgument, err) + parseSocketLevel(levelArg, uint64(level)) } } if optionNameArg := GetArg(event, "optname"); optionNameArg != nil { if opt, isInt := optionNameArg.Value.(int32); isInt { - var optionNameArgument parsers.SocketOptionArgument - var err error - if ID(event.EventID) == Getsockopt { - optionNameArgument, err = parsers.ParseGetSocketOption(uint64(opt)) - } else { - optionNameArgument, err = parsers.ParseSetSocketOption(uint64(opt)) - } - parseOrEmptyString(optionNameArg, optionNameArgument, err) + parseGetSocketOption(optionNameArg, uint64(opt), ID(event.EventID)) } } case BpfAttach: if progTypeArg := GetArg(event, "prog_type"); progTypeArg != nil { if progType, isInt := progTypeArg.Value.(int32); isInt { - progTypeArgument, err := parsers.ParseBPFProgType(uint64(progType)) - parseOrEmptyString(progTypeArg, progTypeArgument, err) + parseBPFProgType(progTypeArg, uint64(progType)) } } if helpersArg := GetArg(event, "prog_helpers"); helpersArg != nil { if helpersList, isUintSlice := helpersArg.Value.([]uint64); isUintSlice { - parsedHelpersList, err := parseBpfHelpersUsage(helpersList) - if err != nil { - return err - } - helpersArg.Type = "const char**" - helpersArg.Value = parsedHelpersList + parseBpfHelpersUsage(helpersArg, helpersList) } } if attachTypeArg := GetArg(event, "attach_type"); attachTypeArg != nil { if attachType, isInt := attachTypeArg.Value.(int32); isInt { - attachTypestr, err := parseBpfAttachType(attachType) - emptyString(attachTypeArg) - if err == nil { - attachTypeArg.Value = attachTypestr - } + parseBpfAttachType(attachTypeArg, attachType) } } case SecurityBpfProg: if progTypeArg := GetArg(event, "type"); progTypeArg != nil { if progType, isInt := progTypeArg.Value.(int32); isInt { - progTypeArgument, err := parsers.ParseBPFProgType(uint64(progType)) - parseOrEmptyString(progTypeArg, progTypeArgument, err) + parseBPFProgType(progTypeArg, uint64(progType)) } } if helpersArg := GetArg(event, "helpers"); helpersArg != nil { if helpersList, isUintSlice := helpersArg.Value.([]uint64); isUintSlice { - parsedHelpersList, err := parseBpfHelpersUsage(helpersList) - if err != nil { - return err - } - helpersArg.Type = "const char**" - helpersArg.Value = parsedHelpersList + parseBpfHelpersUsage(helpersArg, helpersList) } } case SecurityPathNotify: if maskArg := GetArg(event, "mask"); maskArg != nil { if mask, isUint64 := maskArg.Value.(uint64); isUint64 { - fsNotifyMaskArgument := parsers.ParseFsNotifyMask(mask) - parseOrEmptyString(maskArg, fsNotifyMaskArgument, nil) + maskArg.Type = "string" + maskArg.Value = parsers.ParseFsNotifyMask(mask).String() } } if objTypeArg := GetArg(event, "obj_type"); objTypeArg != nil { if objType, isUint := objTypeArg.Value.(uint32); isUint { - objTypeArgument, err := parsers.ParseFsNotifyObjType(uint64(objType)) - parseOrEmptyString(objTypeArg, objTypeArgument, err) + parseFsNotifyObjType(objTypeArg, uint64(objType)) } } } @@ -343,39 +277,3 @@ func (arg CustomFunctionArgument) String() string { func (arg CustomFunctionArgument) Value() uint64 { return arg.val } - -func parseBpfHelpersUsage(helpersList []uint64) ([]string, error) { - var usedHelpers []string - - for i := 0; i < len(helpersList)*64; i++ { - if (helpersList[i/64] & (1 << (i % 64))) > 0 { - // helper number is used. get its name from libbpfgo - bpfHelper, err := parsers.ParseBPFFunc(uint64(i)) - if err != nil { - continue - } - usedHelpers = append(usedHelpers, bpfHelper.String()) - } - } - - return usedHelpers, nil -} - -func parseBpfAttachType(attachType int32) (string, error) { - switch attachType { - case 0: - return "raw_tracepoint", nil - case 1: - return "tracepoint", nil - case 2: - return "kprobe", nil - case 3: - return "kretprobe", nil - case 4: - return "uprobe", nil - case 5: - return "uretprobe", nil - default: - return "", errfmt.Errorf("unknown attach_type got from bpf_attach event") - } -} diff --git a/pkg/events/parse_args_bench_test.go b/pkg/events/parse_args_bench_test.go index dcaddd7fa6a2..ac10c8aa5cbf 100644 --- a/pkg/events/parse_args_bench_test.go +++ b/pkg/events/parse_args_bench_test.go @@ -1,6 +1,7 @@ package events import ( + "sync" "testing" "github.com/aquasecurity/tracee/types/trace" @@ -297,3 +298,20 @@ func BenchmarkParseArgs_Uintptr(b *testing.B) { } } } + +func Benchmark_parseSyscall(b *testing.B) { + for n := 0; n < b.N; n++ { + wg := sync.WaitGroup{} + wg.Add(10) + + for i := 0; i < 10; i++ { + syscallArg := &trace.Argument{ArgMeta: trace.ArgMeta{Name: "syscall"}, Value: int32(0)} + go func() { + defer wg.Done() + parseSyscall(syscallArg, 0) + }() + } + + wg.Wait() + } +} diff --git a/pkg/events/parse_args_helpers.go b/pkg/events/parse_args_helpers.go new file mode 100644 index 000000000000..8e0279b58406 --- /dev/null +++ b/pkg/events/parse_args_helpers.go @@ -0,0 +1,244 @@ +package events + +import ( + "github.com/aquasecurity/tracee/pkg/events/parsers" + "github.com/aquasecurity/tracee/pkg/logger" + "github.com/aquasecurity/tracee/types/trace" +) + +func parseMMapProt(arg *trace.Argument, prot uint64) { + mmapProtArgument := parsers.ParseMmapProt(prot) + arg.Type = "string" + arg.Value = mmapProtArgument.String() +} + +func parseSocketDomainArgument(arg *trace.Argument, domain uint64) { + arg.Type = "string" + socketDomainArgument, err := parsers.ParseSocketDomainArgument(domain) + if err != nil { + arg.Value = "" + return + } + arg.Value = socketDomainArgument.String() +} + +func parseSocketType(arg *trace.Argument, typ uint64) { + arg.Type = "string" + socketTypeArgument, err := parsers.ParseSocketType(typ) + if err != nil { + arg.Value = "" + return + } + arg.Value = socketTypeArgument.String() +} + +func parseInodeMode(arg *trace.Argument, mode uint64) { + arg.Type = "string" + inodeModeArgument, err := parsers.ParseInodeMode(mode) + if err != nil { + arg.Value = "" + return + } + arg.Value = inodeModeArgument.String() +} + +func parseBPFProgType(arg *trace.Argument, progType uint64) { + arg.Type = "string" + bpfProgTypeArgument, err := parsers.ParseBPFProgType(progType) + if err != nil { + arg.Value = "" + return + } + arg.Value = bpfProgTypeArgument.String() +} + +func parseCapability(arg *trace.Argument, capability uint64) { + arg.Type = "string" + capabilityFlagArgument, err := parsers.ParseCapability(capability) + if err != nil { + arg.Value = "" + return + } + arg.Value = capabilityFlagArgument.String() +} + +func parseMemProtAlert(arg *trace.Argument, alert uint32) { + arg.Type = "string" + arg.Value = trace.MemProtAlert(alert).String() +} + +func parseSyscall(arg *trace.Argument, id int32) { + // Bypass the lock contention accessing the read-only map directly, avoiding + // locking the map for reading. + // + // NOTE: This might cause data races in the future if the map is modified. + // One solution to keep better CPU time is to segregate the map into two maps: + // one for proper core (read-only) events and another for the dynamic events. + def, ok := CoreEvents[ID(id)] + if !ok || !def.IsSyscall() { + return + } + + arg.Type = "string" + arg.Value = def.GetName() +} + +func parsePtraceRequestArgument(arg *trace.Argument, req uint64) { + arg.Type = "string" + ptraceRequestArgument, err := parsers.ParsePtraceRequestArgument(req) + if err != nil { + arg.Value = "" + return + } + arg.Value = ptraceRequestArgument.String() +} + +func parsePrctlOption(arg *trace.Argument, opt uint64) { + arg.Type = "string" + prctlOptionArgument, err := parsers.ParsePrctlOption(opt) + if err != nil { + arg.Value = "" + return + } + arg.Value = prctlOptionArgument.String() +} + +func parseSocketcallCall(arg *trace.Argument, call uint64) { + arg.Type = "string" + socketcallArgument, err := parsers.ParseSocketcallCall(call) + if err != nil { + arg.Value = "" + return + } + arg.Value = socketcallArgument.String() +} + +func parseAccessMode(arg *trace.Argument, mode uint64) { + arg.Type = "string" + accessModeArgument, err := parsers.ParseAccessMode(mode) + if err != nil { + arg.Value = "" + return + } + arg.Value = accessModeArgument.String() +} + +func parseExecFlag(arg *trace.Argument, flags uint64) { + arg.Type = "string" + execFlagArgument, err := parsers.ParseExecFlag(flags) + if err != nil { + arg.Value = "" + return + } + arg.Value = execFlagArgument.String() +} + +func parseOpenFlagArgument(arg *trace.Argument, flags uint64) { + arg.Type = "string" + openFlagArgument, err := parsers.ParseOpenFlagArgument(flags) + if err != nil { + arg.Value = "" + return + } + arg.Value = openFlagArgument.String() +} + +func parseCloneFlags(arg *trace.Argument, flags uint64) { + arg.Type = "string" + cloneFlagArgument, err := parsers.ParseCloneFlags(flags) + if err != nil { + arg.Value = "" + return + } + arg.Value = cloneFlagArgument.String() +} + +func parseBPFCmd(arg *trace.Argument, cmd uint64) { + arg.Type = "string" + bpfCommandArgument, err := parsers.ParseBPFCmd(cmd) + if err != nil { + arg.Value = "" + return + } + arg.Value = bpfCommandArgument.String() +} + +func parseSocketLevel(arg *trace.Argument, level uint64) { + arg.Type = "string" + socketLevelArgument, err := parsers.ParseSocketLevel(level) + if err != nil { + arg.Value = "" + return + } + arg.Value = socketLevelArgument.String() +} + +func parseGetSocketOption(arg *trace.Argument, opt uint64, evtID ID) { + var optionNameArgument parsers.SocketOptionArgument + var err error + if evtID == Getsockopt { + optionNameArgument, err = parsers.ParseGetSocketOption(uint64(opt)) + } else { + optionNameArgument, err = parsers.ParseSetSocketOption(uint64(opt)) + } + arg.Type = "string" + if err == nil { + arg.Value = optionNameArgument.String() + } else { + arg.Value = "" + } +} + +func parseFsNotifyObjType(arg *trace.Argument, objType uint64) { + arg.Type = "string" + fsNotifyObjTypeArgument, err := parsers.ParseFsNotifyObjType(objType) + if err != nil { + arg.Value = "" + return + } + arg.Value = fsNotifyObjTypeArgument.String() +} +func parseBpfHelpersUsage(arg *trace.Argument, helpersList []uint64) { + var usedHelpers []string + + for i := 0; i < len(helpersList)*64; i++ { + if (helpersList[i/64] & (1 << (i % 64))) > 0 { + // helper number is used. get its name from libbpfgo + bpfHelper, err := parsers.ParseBPFFunc(uint64(i)) + if err != nil { + continue + } + usedHelpers = append(usedHelpers, bpfHelper.String()) + } + } + + arg.Type = "const char**" + arg.Value = usedHelpers +} + +func parseBpfAttachType(arg *trace.Argument, attachType int32) { + arg.Type = "string" + + var attTypeName string + + switch attachType { + case 0: + attTypeName = "raw_tracepoint" + case 1: + attTypeName = "tracepoint" + case 2: + attTypeName = "kprobe" + case 3: + attTypeName = "kretprobe" + case 4: + attTypeName = "uprobe" + case 5: + attTypeName = "uretprobe" + default: + arg.Value = "" + logger.Errorw("Unknown attach_type got from bpf_attach event") + return + } + + arg.Value = attTypeName +}