Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix char buff matchargs #770

Merged
merged 2 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions bpf/process/types/basic.h
Original file line number Diff line number Diff line change
Expand Up @@ -544,28 +544,28 @@ copy_char_buf(void *ctx, long off, unsigned long arg, int argm,
}

static inline __attribute__((always_inline)) long
filter_char_buf(struct selector_arg_filter *filter, char *args)
filter_char_buf(struct selector_arg_filter *filter, char *args, int value_off)
{
char *value = (char *)&filter->value;
long i, j = 0;

#pragma unroll
for (i = 0; i < MAX_MATCH_STRING_VALUES; i++) {
__u32 length;
int err, v, a, postoff = 0;
int err, a, postoff = 0;

/* filter->vallen is pulled from user input so we also need to
* ensure its bounded.
*/
asm volatile("%[j] &= 0xff;\n" ::[j] "+r"(j)
:);
length = *(__u32 *)&value[j];
asm volatile("%[length] &= 0x3f;\n" ::[length] "+r"(length)
asm volatile("%[length] &= 0xff;\n" ::[length] "+r"(length)
:);
v = (int)value[j];
a = (int)args[0];
// arg length is 4 bytes before the value data
a = *(int *)&args[value_off - 4];
if (filter->op == op_filter_eq) {
if (v != a)
if (length != a)
goto skip_string;
} else if (filter->op == op_filter_str_postfix) {
postoff = a - length;
Expand All @@ -580,7 +580,7 @@ filter_char_buf(struct selector_arg_filter *filter, char *args)
*/
asm volatile("%[j] &= 0xff;\n" ::[j] "+r"(j)
:);
err = cmpbytes(&value[j + 4], &args[4 + postoff], length);
err = cmpbytes(&value[j + 4], &args[value_off + postoff], length);
if (!err)
return 1;
skip_string:
Expand Down Expand Up @@ -1037,8 +1037,14 @@ selector_arg_offset(__u8 *f, struct msg_generic_kprobe *e, __u32 selidx)
pass = filter_file_buf(filter, args);
break;
case string_type:
/* for strings, we just encode the length */
pass = filter_char_buf(filter, args, 4);
break;
case char_buf:
pass = filter_char_buf(filter, args);
/* for buffers, we just encode the expected length and the
* length that was actually read (see: __copy_char_buf)
*/
pass = filter_char_buf(filter, args, 8);
break;
case s64_ty:
case u64_ty:
Expand Down
172 changes: 172 additions & 0 deletions pkg/sensors/tracing/selectors_char_buf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Tetragon

// NB: we are using a x64__ prefix. Should be easy to fix, but for now this
// test should only be run in amd64.
//go:build amd64 && linux
// +build amd64,linux

package tracing

import (
"context"
"fmt"
"testing"
"time"

"github.com/cilium/tetragon/pkg/api/tracingapi"
"github.com/cilium/tetragon/pkg/grpc/tracing"
"github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1"
"github.com/cilium/tetragon/pkg/logger"
"github.com/cilium/tetragon/pkg/observer"
"github.com/cilium/tetragon/pkg/reader/notify"
"github.com/cilium/tetragon/pkg/testutils"
"github.com/cilium/tetragon/pkg/testutils/perfring"
tus "github.com/cilium/tetragon/pkg/testutils/sensors"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)

func TestCharBufKprobe(t *testing.T) {
testutils.CaptureLog(t, logger.GetLogger().(*logrus.Logger))
ctx, cancel := context.WithTimeout(context.Background(), tus.Conf().CmdWaitTime)
defer cancel()

mypid := int(observer.GetMyPid())
t.Logf("filtering for my pid (%d)", mypid)

writeBufArgIdx := uint32(1)
writeSizeArgIdx := uint32(2)
writeBufArg := "pizzaisthebest"
call := "__x64_sys_write"
spec := &v1alpha1.TracingPolicySpec{
KProbes: []v1alpha1.KProbeSpec{{
Call: call,
Syscall: true,
Args: []v1alpha1.KProbeArg{{
Index: writeBufArgIdx,
Type: "char_buf",
SizeArgIndex: writeSizeArgIdx + 1,
}, {
Index: writeSizeArgIdx,
Type: "size_t",
}},
Selectors: []v1alpha1.KProbeSelector{{
MatchPIDs: []v1alpha1.PIDSelector{{
Operator: "In",
FollowForks: true,
Values: []uint32{uint32(mypid)},
}},
MatchArgs: []v1alpha1.ArgSelector{{
Index: writeBufArgIdx,
Operator: "Equal",
Values: []string{writeBufArg},
}},
}},
}},
}

loadGenericSensorTest(t, ctx, spec)
t0 := time.Now()
loadElapsed := time.Since(t0)
t.Logf("loading sensors took: %s\n", loadElapsed)

countPizza := 0
countOther := 0
eventFn := func(ev notify.Message) error {
if kpEvent, ok := ev.(*tracing.MsgGenericKprobeUnix); ok {
if kpEvent.FuncName != call {
return fmt.Errorf("unexpected kprobe event, func:%s", kpEvent.FuncName)
}
arg := string(kpEvent.Args[0].(tracingapi.MsgGenericKprobeArgBytes).Value)
if arg == writeBufArg {
countPizza++
} else {
countOther++
}
}
return nil
}

ops := func() {
unix.Write(-1, []byte(writeBufArg))
unix.Write(-1, []byte("unrelated string"))
}

perfring.RunTest(t, ctx, ops, eventFn)
require.Equal(t, 1, countPizza, "expected events with '%s'", writeBufArg)
require.Equal(t, 0, countOther, "unexexpected events")

}

func TestCharBufTracepoint(t *testing.T) {
testutils.CaptureLog(t, logger.GetLogger().(*logrus.Logger))
ctx, cancel := context.WithTimeout(context.Background(), tus.Conf().CmdWaitTime)
defer cancel()

mypid := int(observer.GetMyPid())
t.Logf("filtering for my pid (%d)", mypid)

writeBufArgIdx := uint32(6)
writeSizeArgIdx := uint32(7)
writeBufArg := "pizzaisthebest"
event := "sys_enter_write"
spec := &v1alpha1.TracingPolicySpec{
Tracepoints: []v1alpha1.TracepointSpec{{
Subsystem: "syscalls",
Event: event,
Args: []v1alpha1.KProbeArg{{
Index: writeBufArgIdx,
Type: "char_buf",
SizeArgIndex: writeSizeArgIdx + 1,
}, {
Index: writeSizeArgIdx,
Type: "size_t",
}},
Selectors: []v1alpha1.KProbeSelector{{
MatchPIDs: []v1alpha1.PIDSelector{{
Operator: "In",
FollowForks: true,
Values: []uint32{uint32(mypid)},
}},
MatchArgs: []v1alpha1.ArgSelector{{
Index: writeBufArgIdx,
Operator: "Equal",
Values: []string{writeBufArg},
}},
}},
}},
}

loadGenericSensorTest(t, ctx, spec)
t0 := time.Now()
loadElapsed := time.Since(t0)
t.Logf("loading sensors took: %s\n", loadElapsed)

countPizza := 0
countOther := 0
eventFn := func(ev notify.Message) error {
if tpEvent, ok := ev.(*tracing.MsgGenericTracepointUnix); ok {
if tpEvent.Event != event {
return fmt.Errorf("unexpected tracepoint event, %s:%s", tpEvent.Subsys, tpEvent.Event)
}
arg := string(tpEvent.Args[0].([]byte))
if arg == writeBufArg {
countPizza++
} else {
countOther++
}
}
return nil
}

ops := func() {
unix.Write(-1, []byte(writeBufArg))
unix.Write(-1, []byte("unrelated string"))
}

perfring.RunTest(t, ctx, ops, eventFn)
require.Equal(t, 1, countPizza, "expected events with '%s'", writeBufArg)
require.Equal(t, 0, countOther, "unexexpected events")
}