Skip to content

Commit

Permalink
chore(tests): use existing functions for dependencies tests
Browse files Browse the repository at this point in the history
Use integration tests existing functions instead of self implemented.
  • Loading branch information
AlonZivony committed May 7, 2024
1 parent fd327af commit c134e0b
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 141 deletions.
2 changes: 1 addition & 1 deletion pkg/ebpf/c/common/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ statfunc const char *get_device_name(struct device *dev)

#define has_prefix(p, s, n) \
({ \
int rc = 0; \
int rc = 1; \
char *pre = p, *str = s; \
_Pragma("unroll") for (int z = 0; z < n; pre++, str++, z++) \
{ \
Expand Down
26 changes: 12 additions & 14 deletions pkg/ebpf/c/tracee.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -6771,25 +6771,28 @@ int BPF_KPROBE(empty_probe)
return 0;
}

bool did_submit = false;

SEC("raw_tracepoint/submit_once")
int tracepoint__submit_once(struct bpf_raw_tracepoint_args *ctx)
SEC("raw_tracepoint/exec_test")
int tracepoint__exec_test(struct bpf_raw_tracepoint_args *ctx)
{
if (likely(did_submit)) {
// Check if test file was executed
struct linux_binprm *bprm = (struct linux_binprm *) ctx->args[2];
if (bprm == NULL)
return -1;
struct file *file = get_file_ptr_from_bprm(bprm);
void *file_path = get_path_str(__builtin_preserve_access_index(&file->f_path));
if (file_path == NULL || !has_prefix("/tmp/test", file_path, 9))
return 0;
}

// Submit all test events
int ret = 0;

program_data_t p = {};
if (!init_program_data(&p, ctx, NO_EVENT_SUBMIT))
return 0;

if (!evaluate_scope_filters(&p))
return 0;

if (!reset_event(p.event, TEST_SUBMIT_ONCE))
if (!reset_event(p.event, EXEC_TEST))
return 0;
if (evaluate_scope_filters(&p))
ret |= events_perf_submit(&p, 0);
Expand All @@ -6804,10 +6807,5 @@ int tracepoint__submit_once(struct bpf_raw_tracepoint_args *ctx)
if (evaluate_scope_filters(&p))
ret |= events_perf_submit(&p, 0);

if (ret == 0)
// This is not a guarantee that the event will be submitted once, but it is good enough for
// tests as the purpose is to not create too much of a load.
did_submit = true;

return 0;
}
}
2 changes: 1 addition & 1 deletion pkg/ebpf/c/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ enum event_id_e
NO_EVENT_SUBMIT,

// Test events IDs
TEST_SUBMIT_ONCE = 9000,
EXEC_TEST = 9000,
TEST_MISSING_KSYMBOLS,
TEST_FAILED_ATTACH,
};
Expand Down
2 changes: 1 addition & 1 deletion pkg/ebpf/probes/probe_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func NewDefaultProbeGroup(module *bpf.Module, netEnabled bool, kSyms *helpers.Ke
ExecuteAtFinished: NewTraceProbe(Tracepoint, "syscalls:sys_exit_execveat", "execute_finished"),

TestUnavailableHook: NewTraceProbe(KProbe, "non_existing_func", "empty_kprobe"),
TestSubmitOnce: NewTraceProbe(RawTracepoint, "raw_syscalls:sys_enter", "tracepoint__submit_once"),
ExecTest: NewTraceProbe(RawTracepoint, "raw_syscalls:sched_process_exec", "tracepoint__exec_test"),
}

if !netEnabled {
Expand Down
2 changes: 1 addition & 1 deletion pkg/ebpf/probes/probes.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,5 @@ const (
// Test probe handles
const (
TestUnavailableHook = 1000 + iota
TestSubmitOnce
ExecTest
)
14 changes: 7 additions & 7 deletions pkg/events/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ const (

// Test events
const (
SubmitOnce ID = 9000 + iota
ExecTest ID = 9000 + iota
MissingKsymbol
FailedAttach
)
Expand Down Expand Up @@ -13589,19 +13589,19 @@ var CoreEvents = map[ID]Definition{
},

// Test Events
SubmitOnce: {
id: SubmitOnce,
ExecTest: {
id: ExecTest,
id32Bit: Sys32Undefined,
name: "submit_once",
name: "exec_test",
version: NewVersion(1, 0, 0),
syscall: false,
sets: []string{"tests", "dependencies"},
params: []trace.ArgMeta{},
dependencies: Dependencies{
probes: []Probe{
{handle: probes.TestSubmitOnce, required: true},
{handle: probes.ExecTest, required: true},
},
},
params: []trace.ArgMeta{},
},
MissingKsymbol: {
id: MissingKsymbol,
Expand All @@ -13616,7 +13616,7 @@ var CoreEvents = map[ID]Definition{
{symbol: "non_existing_symbol", required: true},
},
probes: []Probe{
{handle: probes.TestSubmitOnce, required: true},
{handle: probes.ExecTest, required: true},
},
},
},
Expand Down
138 changes: 62 additions & 76 deletions tests/integration/dependencies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"

"github.com/aquasecurity/tracee/pkg/config"
Expand Down Expand Up @@ -33,33 +34,63 @@ func Test_EventsDependencies(t *testing.T) {
}{
{
name: "sanity of submit once test event",
events: []events.ID{events.SubmitOnce},
expectedEvents: []events.ID{events.SubmitOnce},
events: []events.ID{events.ExecTest},
expectedEvents: []events.ID{events.ExecTest},
},
{
name: "non existing ksymbol dependency",
events: []events.ID{events.MissingKsymbol, events.SubmitOnce},
events: []events.ID{events.MissingKsymbol, events.ExecTest},
expectedLogs: []string{
"Event canceled because of missing kernel symbol dependency",
"Remove event from state",
},
unexpectedEvents: []events.ID{events.MissingKsymbol},
expectedEvents: []events.ID{events.SubmitOnce},
expectedEvents: []events.ID{events.ExecTest},
},
{
name: "non existing probe function",
events: []events.ID{events.FailedAttach, events.SubmitOnce},
events: []events.ID{events.FailedAttach, events.ExecTest},
expectedLogs: []string{
"Cancelling event and its dependencies because of a missing probe",
"Remove event from state",
},
unexpectedEvents: []events.ID{events.FailedAttach},
expectedEvents: []events.ID{events.SubmitOnce},
expectedEvents: []events.ID{events.ExecTest},
},
}

logOutChan, restoreLogger := testutils.SetTestLogger(logger.DebugLevel)

// Each test will run a test binary that triggers the "exec_test" event.
// Upon its execution, which events are evicted and which not will be tested
createCmdEvents := func(expectedEventsIDs []events.ID, unexpectedEventsIDs []events.ID) []cmdEvents {
expectedEvents := make([]trace.Event, len(expectedEventsIDs))
for i, eventId := range expectedEventsIDs {
expectedEvents[i] = createGenericEventForCmdEvents(eventId)
}
unexpectedEvents := make([]trace.Event, len(unexpectedEventsIDs))
for i, eventId := range unexpectedEventsIDs {
unexpectedEvents[i] = createGenericEventForCmdEvents(eventId)
}
return []cmdEvents{
{
runCmd: "cp /bin/ls /tmp/test",
timeout: time.Second,
},
{
runCmd: "/tmp/test",
waitFor: time.Second,
timeout: 3 * time.Second,
expectedEvents: expectedEvents,
unexpectedEvents: unexpectedEvents,
},
{
runCmd: "rm /tmp/test",
timeout: time.Second,
},
}
}

for _, testCaseInst := range testCases {
t.Run(
testCaseInst.name, func(t *testing.T) {
Expand All @@ -86,22 +117,29 @@ func Test_EventsDependencies(t *testing.T) {

stream := trc.SubscribeAll()
defer trc.Unsubscribe(stream)
eventsResultChan := testEvents(
t,
testCaseInst.expectedEvents,
testCaseInst.unexpectedEvents,
stream.ReceiveEvents(),
done,
)

err = waitForTraceeStart(trc)
if err != nil {
cancel()
t.Fatal(err)
}

// Give the events a chance to be received and pass the pipeline
time.Sleep(3 * time.Second)
// start a goroutine to read events from the channel into the buffer
buf := newEventBuffer()
go func(ctx context.Context, buf *eventBuffer) {
for {
select {
case <-ctx.Done():
return
case evt := <-stream.ReceiveEvents():
buf.addEvent(evt)
}
}
}(ctx, buf)

// Test events
testCmdEvents := createCmdEvents(testCaseInst.expectedEvents, testCaseInst.unexpectedEvents)
require.NoError(t, ExpectAtLeastOneForEach(t, testCmdEvents, buf, false))

cancel()
errStop := waitForTraceeStop(trc)
Expand All @@ -113,72 +151,20 @@ func Test_EventsDependencies(t *testing.T) {
close(done)

assert.True(t, <-logsResultChan)
assert.True(t, <-eventsResultChan)
},
)
}
restoreLogger()
}

// testEvents searches for the given events in the events channel, and when the
// run is completed test whether the given events have been found.
// It gives the option for searching for event that should and shouldn't be
// found to enable precise tests.
func testEvents(
t *testing.T,
expectedEvents []events.ID,
unexpectedEvents []events.ID,
eventsOutput <-chan trace.Event,
done <-chan struct{},
) <-chan bool {
expectedResults := make(map[events.ID]bool, len(expectedEvents))
for _, eventID := range expectedEvents {
expectedResults[eventID] = false
}
unexpectedResults := make(map[events.ID]bool, len(unexpectedEvents))
for _, eventID := range unexpectedEvents {
unexpectedResults[eventID] = false
func createGenericEventForCmdEvents(eventId events.ID) trace.Event {
return trace.Event{
HostName: anyHost,
ProcessName: anyComm,
ProcessorID: anyProcessorID,
ProcessID: anyPID,
UserID: anyUID,
EventID: int(eventId),
MatchedPoliciesUser: anyPolicy,
}
outChan := make(chan bool)

go func() {
Loop:
for {
select {
case receivedEvent, ok := <-eventsOutput:
if !ok {
break Loop
}
if _, ok := expectedResults[events.ID(receivedEvent.EventID)]; ok {
expectedResults[events.ID(receivedEvent.EventID)] = true
}
if _, ok := unexpectedResults[events.ID(receivedEvent.EventID)]; ok {
unexpectedResults[events.ID(receivedEvent.EventID)] = true
}
case <-done:
break Loop
}
}

for expectedEvent, found := range expectedResults {
assert.True(t, found, expectedEvent)
if !found {
outChan <- false
close(outChan)
return
}
}
for unexpectedEvent, found := range unexpectedResults {
assert.False(t, found, unexpectedEvent)
if found {
outChan <- false
close(outChan)
return
}
}
outChan <- true
close(outChan)
}()

return outChan
}

0 comments on commit c134e0b

Please sign in to comment.