From 046235085e0290eb13b05d0258e4ccdf1a18b9c4 Mon Sep 17 00:00:00 2001 From: Kevin Sheldrake Date: Tue, 12 Mar 2024 16:31:15 +0000 Subject: [PATCH] Tracing: Add UserspaceData flag to arg meta Add the UserspaceData flag to the meta value, taking into account the default value if it is missing from the arg in the policy. Modify how tracepoints use the meta value to permit adding this flag. Signed-off-by: Kevin Sheldrake --- bpf/process/types/basic.h | 21 +++++++---- pkg/sensors/tracing/args.go | 44 +++++++++++++----------- pkg/sensors/tracing/generickprobe.go | 3 +- pkg/sensors/tracing/generictracepoint.go | 31 ++++++++++++----- pkg/sensors/tracing/genericuprobe.go | 3 +- pkg/sensors/tracing/kprobe_test.go | 8 ++--- 6 files changed, 69 insertions(+), 41 deletions(-) diff --git a/bpf/process/types/basic.h b/bpf/process/types/basic.h index 6fc5b8a569e..6a622975080 100644 --- a/bpf/process/types/basic.h +++ b/bpf/process/types/basic.h @@ -47,7 +47,7 @@ enum { fd_ty = 17, /* const_buf_type is a type for buffers with static size that is passed - * in the meta argument + * in the meta argument's upper 16 bits */ const_buf_type = 18, bpf_attr_type = 19, @@ -631,9 +631,10 @@ copy_kernel_module(char *args, unsigned long arg) return sizeof(struct tg_kernel_module); } -#define ARGM_INDEX_MASK 0xf -#define ARGM_RETURN_COPY BIT(4) -#define ARGM_MAX_DATA BIT(5) +#define ARGM_INDEX_MASK 0xf +#define ARGM_RETURN_COPY BIT(4) +#define ARGM_MAX_DATA BIT(5) +#define ARGM_USERSPACE_DATA BIT(6) static inline __attribute__((always_inline)) bool hasReturnCopy(unsigned long argm) @@ -647,6 +648,12 @@ has_max_data(unsigned long argm) return (argm & ARGM_MAX_DATA) != 0; } +static inline __attribute__((always_inline)) bool +is_userspace_data(unsigned long argm) +{ + return (argm & ARGM_USERSPACE_DATA) != 0; +} + static inline __attribute__((always_inline)) unsigned long get_arg_meta(int meta, struct msg_generic_kprobe *e) { @@ -1609,7 +1616,8 @@ static inline __attribute__((always_inline)) size_t type_to_min_size(int type, case char_iovec: return 4; case const_buf_type: - return argm; + // For const_buf_type, the size is in the upper 16 bits of the meta argument. + return argm >> 16; case bpf_attr_type: return sizeof(struct bpf_info_type); case perf_event_type: @@ -2651,8 +2659,9 @@ read_call_arg(void *ctx, struct msg_generic_kprobe *e, int index, int type, size = copy_char_iovec(ctx, orig_off, arg, argm, e); break; case const_buf_type: { + // for const_buf_type the size is in the upper 16 bits of the meta argument // bound size to 1023 to help the verifier out - size = argm & 0x03ff; + size = (argm >> 16) & 0x03ff; probe_read(args, size, (char *)arg); break; } diff --git a/pkg/sensors/tracing/args.go b/pkg/sensors/tracing/args.go index 58444113b18..430a5a04a61 100644 --- a/pkg/sensors/tracing/args.go +++ b/pkg/sensors/tracing/args.go @@ -30,8 +30,10 @@ type argPrinter struct { } const ( - argReturnCopyBit = 1 << 4 - argMaxDataBit = 1 << 5 + argSizeArgIndexMask = int(0xf) + argReturnCopyBit = 1 << 4 + argMaxDataBit = 1 << 5 + argUserspaceDataBit = 1 << 6 ) func argReturnCopy(meta int) bool { @@ -41,17 +43,20 @@ func argReturnCopy(meta int) bool { // meta value format: // bits // -// 0-3 : SizeArgIndex -// 4 : ReturnCopy -// 5 : MaxData -func getMetaValue(arg *v1alpha1.KProbeArg) (int, error) { - var meta int +// 0-3 : SizeArgIndex +// 4 : ReturnCopy +// 5 : MaxData +// 6 : UserspaceData +// 7-15 : reserved +// 16-31 : size for const_buf +func getMetaValue(arg *v1alpha1.KProbeArg, userspaceDataDefault bool) (int, error) { + meta := 0 if arg.SizeArgIndex > 0 { if arg.SizeArgIndex > 15 { return 0, fmt.Errorf("invalid SizeArgIndex value (>15): %v", arg.SizeArgIndex) } - meta = int(arg.SizeArgIndex) + meta = meta | int(arg.SizeArgIndex) } if arg.ReturnCopy { meta = meta | argReturnCopyBit @@ -59,19 +64,18 @@ func getMetaValue(arg *v1alpha1.KProbeArg) (int, error) { if arg.MaxData { meta = meta | argMaxDataBit } - return meta, nil -} - -// getTracepointMetaArg is a temporary helper to find meta values while tracepoint -// converts into new CRD and config formats. -func getTracepointMetaValue(arg *v1alpha1.KProbeArg) int { - if arg.SizeArgIndex > 0 { - return int(arg.SizeArgIndex) - } - if arg.ReturnCopy { - return -1 + if arg.IsUserspaceData == nil { + // If not set in policy, use the default. + if userspaceDataDefault { + meta = meta | argUserspaceDataBit + } + } else { + // Otherwise, use the provided value. + if *arg.IsUserspaceData { + meta = meta | argUserspaceDataBit + } } - return 0 + return meta, nil } func getArg(r *bytes.Reader, a argPrinter) tracingapi.MsgGenericKprobeArg { diff --git a/pkg/sensors/tracing/generickprobe.go b/pkg/sensors/tracing/generickprobe.go index a4c30a51960..f154423fead 100644 --- a/pkg/sensors/tracing/generickprobe.go +++ b/pkg/sensors/tracing/generickprobe.go @@ -662,7 +662,8 @@ func addKprobe(funcName string, f *v1alpha1.KProbeSpec, in *addKprobeIn) (id idt logger.GetLogger().Warnf("maxData flag is ignored (supported from large programs)") } } - argMValue, err := getMetaValue(&a) + // For kprobes, args default to userspace memory for syscalls, and kernel memory otherwise. + argMValue, err := getMetaValue(&a, f.Syscall) if err != nil { return errFn(err) } diff --git a/pkg/sensors/tracing/generictracepoint.go b/pkg/sensors/tracing/generictracepoint.go index 2e8274195c4..ca129dc57db 100644 --- a/pkg/sensors/tracing/generictracepoint.go +++ b/pkg/sensors/tracing/generictracepoint.go @@ -219,10 +219,10 @@ func (out *genericTracepointArg) getGenericTypeId() (int, error) { if err != nil { return gt.GenericInvalidType, fmt.Errorf("failed to get size of array type %w", err) } - if out.MetaArg == 0 { - // set MetaArg equal to the number of bytes we need to copy - out.MetaArg = nbytes - } + // set MetaArg's upper half-word equal to the number of bytes we need to copy + out.MetaArg = out.MetaArg & 0xffff + out.MetaArg = out.MetaArg | (nbytes << 16) + return gt.GenericConstBuffer, nil case tracepoint.SizeTy: @@ -235,6 +235,7 @@ func (out *genericTracepointArg) getGenericTypeId() (int, error) { func buildGenericTracepointArgs(info *tracepoint.Tracepoint, specArgs []v1alpha1.KProbeArg) ([]genericTracepointArg, error) { ret := make([]genericTracepointArg, 0, len(specArgs)) nfields := uint32(len(info.Format.Fields)) + syscall := info.Subsys == "syscalls" || info.Subsys == "raw_syscalls" for argIdx := range specArgs { specArg := &specArgs[argIdx] @@ -242,11 +243,16 @@ func buildGenericTracepointArgs(info *tracepoint.Tracepoint, specArgs []v1alpha1 return nil, fmt.Errorf("tracepoint %s/%s has %d fields but field %d was requested", info.Subsys, info.Event, nfields, specArg.Index) } field := info.Format.Fields[specArg.Index] + // Syscall tracepoint arguments are in userspace memory. + metaTp, err := getMetaValue(specArg, syscall) + if err != nil { + return nil, fmt.Errorf("tracepoint %s/%s getMetaValue error: %w", info.Subsys, info.Event, err) + } ret = append(ret, genericTracepointArg{ CtxOffset: int(field.Offset), ArgIdx: uint32(argIdx), TpIdx: int(specArg.Index), - MetaTp: getTracepointMetaValue(specArg), + MetaTp: metaTp, nopTy: false, format: &field, genericTypeId: gt.GenericInvalidType, @@ -272,12 +278,16 @@ func buildGenericTracepointArgs(info *tracepoint.Tracepoint, specArgs []v1alpha1 } field := info.Format.Fields[tpIdx] argIdx := uint32(len(ret)) + metaArg := 0 + if syscall { + metaArg = argUserspaceDataBit + } ret = append(ret, genericTracepointArg{ CtxOffset: int(field.Offset), ArgIdx: argIdx, TpIdx: tpIdx, MetaTp: 0, - MetaArg: 0, + MetaArg: metaArg, nopTy: true, format: &field, genericTypeId: gt.GenericInvalidType, @@ -287,15 +297,18 @@ func buildGenericTracepointArgs(info *tracepoint.Tracepoint, specArgs []v1alpha1 for idx := 0; idx < len(ret); idx++ { meta := ret[idx].MetaTp - if meta == 0 || meta == -1 { + metaArgIndex := meta & argSizeArgIndexMask + + if metaArgIndex == 0 || (meta&argReturnCopyBit != 0) { ret[idx].MetaArg = meta continue } - a, err := getOrAppendMeta(meta) + a, err := getOrAppendMeta(metaArgIndex) if err != nil { return nil, err } - ret[idx].MetaArg = int(a.ArgIdx) + 1 + meta = meta & ^argSizeArgIndexMask + ret[idx].MetaArg = meta | (int(a.ArgIdx) + 1) } return ret, nil } diff --git a/pkg/sensors/tracing/genericuprobe.go b/pkg/sensors/tracing/genericuprobe.go index e5969a8b311..23941a26237 100644 --- a/pkg/sensors/tracing/genericuprobe.go +++ b/pkg/sensors/tracing/genericuprobe.go @@ -244,7 +244,8 @@ func addUprobe(spec *v1alpha1.UProbeSpec, ids []idtable.EntryID, in *addUprobeIn if argType == gt.GenericInvalidType { return nil, fmt.Errorf("Arg(%d) type '%s' unsupported", i, a.Type) } - argMValue, err := getMetaValue(&a) + // For uprobes, args default to userspace memory. + argMValue, err := getMetaValue(&a, true) if err != nil { return nil, err } diff --git a/pkg/sensors/tracing/kprobe_test.go b/pkg/sensors/tracing/kprobe_test.go index 15456cffd2e..ad91d7fb009 100644 --- a/pkg/sensors/tracing/kprobe_test.go +++ b/pkg/sensors/tracing/kprobe_test.go @@ -4390,7 +4390,7 @@ func testMaxData(t *testing.T, data []byte, checker *ec.UnorderedEventChecker, c func TestKprobeWriteMaxDataTrunc(t *testing.T) { if !kernels.MinKernelVersion("5.3.0") { - t.Skip("TestCopyFd requires at least 5.3.0 version") + t.Skip("TestKprobeWriteMaxDataTrunc requires at least 5.3.0 version") } _, fd2, fdString := createTestFile(t) myPid := observertesthelper.GetMyPid() @@ -4453,7 +4453,7 @@ spec: func TestKprobeWriteMaxData(t *testing.T) { if !kernels.MinKernelVersion("5.3.0") { - t.Skip("TestCopyFd requires at least 5.3.0 version") + t.Skip("TestKprobeWriteMaxData requires at least 5.3.0 version") } _, fd2, fdString := createTestFile(t) myPid := observertesthelper.GetMyPid() @@ -4512,7 +4512,7 @@ spec: func TestKprobeWriteMaxDataFull(t *testing.T) { if !kernels.MinKernelVersion("5.3.0") { - t.Skip("TestCopyFd requires at least 5.3.0 version") + t.Skip("TestKprobeWriteMaxDataFull requires at least 5.3.0 version") } _, fd2, fdString := createTestFile(t) myPid := observertesthelper.GetMyPid() @@ -5783,7 +5783,7 @@ spec: func TestKprobeListSyscallDupsRange(t *testing.T) { if !kernels.MinKernelVersion("5.3.0") { - t.Skip("TestCopyFd requires at least 5.3.0 version") + t.Skip("TestKprobeListSyscallDupsRange requires at least 5.3.0 version") } myPid := observertesthelper.GetMyPid() pidStr := strconv.Itoa(int(myPid))