Skip to content

Commit

Permalink
Tracing: Add UserspaceData flag to arg meta
Browse files Browse the repository at this point in the history
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 <kevin.sheldrake@isovalent.com>
  • Loading branch information
kevsecurity committed Mar 18, 2024
1 parent 2a409a9 commit 0462350
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 41 deletions.
21 changes: 15 additions & 6 deletions bpf/process/types/basic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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)
{
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
Expand Down
44 changes: 24 additions & 20 deletions pkg/sensors/tracing/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -41,37 +43,39 @@ 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
}
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 {
Expand Down
3 changes: 2 additions & 1 deletion pkg/sensors/tracing/generickprobe.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
31 changes: 22 additions & 9 deletions pkg/sensors/tracing/generictracepoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -235,18 +235,24 @@ 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]
if specArg.Index >= nfields {
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,
Expand All @@ -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,
Expand All @@ -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
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/sensors/tracing/genericuprobe.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/sensors/tracing/kprobe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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))
Expand Down

0 comments on commit 0462350

Please sign in to comment.