inspektor-gadget

Форк
0
442 строки · 13.2 Кб
1
// Copyright 2022-2023 The Inspektor Gadget authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
//go:build linux
16
// +build linux
17

18
package tracer_test
19

20
import (
21
	"fmt"
22
	"os"
23
	"os/exec"
24
	"sort"
25
	"testing"
26
	"time"
27

28
	"github.com/stretchr/testify/require"
29
	"golang.org/x/sys/unix"
30

31
	utilstest "github.com/inspektor-gadget/inspektor-gadget/internal/test"
32
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/exec/tracer"
33
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/exec/types"
34
	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
35
)
36

37
func TestExecTracerCreate(t *testing.T) {
38
	t.Parallel()
39

40
	utilstest.RequireRoot(t)
41

42
	tracer := createTracer(t, &tracer.Config{}, func(*types.Event) {})
43
	require.NotNil(t, tracer, "Returned tracer was nil")
44
}
45

46
func TestExecTracerStopIdempotent(t *testing.T) {
47
	t.Parallel()
48

49
	utilstest.RequireRoot(t)
50

51
	tracer := createTracer(t, &tracer.Config{}, func(*types.Event) {})
52

53
	// Check that a double stop doesn't cause issues
54
	tracer.Stop()
55
	tracer.Stop()
56
}
57

58
func TestExecTracer(t *testing.T) {
59
	t.Parallel()
60

61
	utilstest.RequireRoot(t)
62

63
	const unprivilegedUID = int(1435)
64
	const unprivilegedGID = int(6789)
65

66
	manyArgs := []string{}
67
	// 19 is DEFAULT_MAXARGS - 1 (-1 because args[0] is on the first position).
68
	for i := 0; i < 19; i++ {
69
		manyArgs = append(manyArgs, "/dev/null")
70
	}
71

72
	cwd, err := os.Getwd()
73
	require.Nil(t, err, "Failed to get current working directory: %s", err)
74

75
	type testDefinition struct {
76
		shouldSkip      func(t *testing.T)
77
		getTracerConfig func(info *utilstest.RunnerInfo) *tracer.Config
78
		runnerConfig    *utilstest.RunnerConfig
79
		generateEvent   func() (int, error)
80
		validateEvent   func(t *testing.T, info *utilstest.RunnerInfo, catPid int, events []types.Event)
81
	}
82

83
	loginuid := utilstest.ReadFileAsUint32(t, "/proc/self/loginuid")
84
	sessionid := utilstest.ReadFileAsUint32(t, "/proc/self/sessionid")
85

86
	for name, test := range map[string]testDefinition{
87
		"captures_all_events_with_no_filters_configured": {
88
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
89
				return &tracer.Config{}
90
			},
91
			generateEvent: generateEvent,
92
			validateEvent: utilstest.ExpectAtLeastOneEvent(func(info *utilstest.RunnerInfo, catPid int) *types.Event {
93
				return &types.Event{
94
					Event: eventtypes.Event{
95
						Type: eventtypes.NORMAL,
96
					},
97
					Pid:           uint32(catPid),
98
					Ppid:          uint32(info.Pid),
99
					Uid:           uint32(info.Uid),
100
					LoginUid:      loginuid,
101
					SessionId:     sessionid,
102
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
103
					Retval:        0,
104
					Comm:          "cat",
105
					Args:          []string{"/bin/cat", "/dev/null"},
106
				}
107
			}),
108
		},
109
		"captures_no_events_with_no_matching_filter": {
110
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
111
				return &tracer.Config{
112
					MountnsMap: utilstest.CreateMntNsFilterMap(t, 0),
113
				}
114
			},
115
			generateEvent: generateEvent,
116
			validateEvent: utilstest.ExpectNoEvent[types.Event, int],
117
		},
118
		"captures_events_with_matching_filter": {
119
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
120
				return &tracer.Config{
121
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
122
				}
123
			},
124
			generateEvent: generateEvent,
125
			validateEvent: utilstest.ExpectOneEvent(func(info *utilstest.RunnerInfo, catPid int) *types.Event {
126
				return &types.Event{
127
					Event: eventtypes.Event{
128
						Type: eventtypes.NORMAL,
129
					},
130
					Pid:           uint32(catPid),
131
					Ppid:          uint32(info.Pid),
132
					Uid:           uint32(info.Uid),
133
					LoginUid:      loginuid,
134
					SessionId:     sessionid,
135
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
136
					Retval:        0,
137
					Comm:          "cat",
138
					Args:          []string{"/bin/cat", "/dev/null"},
139
				}
140
			}),
141
		},
142
		"event_has_UID_and_GID_of_user_generating_event": {
143
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
144
				return &tracer.Config{
145
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
146
				}
147
			},
148
			runnerConfig: &utilstest.RunnerConfig{
149
				Uid: unprivilegedUID,
150
				Gid: unprivilegedGID,
151
			},
152
			generateEvent: generateEvent,
153
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ int, events []types.Event) {
154
				require.Len(t, events, 1, "One event expected")
155
				require.Equal(t, uint32(info.Uid), events[0].Uid, "Event has bad UID")
156
				require.Equal(t, uint32(info.Gid), events[0].Gid, "Event has bad GID")
157
			},
158
		},
159
		"truncates_captured_args_in_trace_to_maximum_possible_length": {
160
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
161
				return &tracer.Config{
162
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
163
				}
164
			},
165
			generateEvent: func() (int, error) {
166
				args := append(manyArgs, "/dev/null")
167
				cmd := exec.Command("/bin/cat", args...)
168
				if err := cmd.Run(); err != nil {
169
					return 0, fmt.Errorf("running command: %w", err)
170
				}
171

172
				return cmd.Process.Pid, nil
173
			},
174
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ int, events []types.Event) {
175
				require.Len(t, events, 1, "One event expected")
176
				require.Equal(t, append([]string{"/bin/cat"}, manyArgs...), events[0].Args, "Event has bad args")
177
			},
178
		},
179
		"event_has_correct_cwd": {
180
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
181
				return &tracer.Config{
182
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
183
					GetCwd:     true,
184
				}
185
			},
186
			generateEvent: func() (int, error) {
187
				args := append(manyArgs, "/dev/null")
188
				cmd := exec.Command("/bin/cat", args...)
189
				if err := cmd.Run(); err != nil {
190
					return 0, fmt.Errorf("running command: %w", err)
191
				}
192

193
				return cmd.Process.Pid, nil
194
			},
195
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ int, events []types.Event) {
196
				require.Len(t, events, 1, "One event expected")
197
				require.Equal(t, events[0].Cwd, cwd, "Event has bad cwd")
198
			},
199
		},
200
		"event_failed": {
201
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
202
				return &tracer.Config{
203
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
204
				}
205
			},
206
			generateEvent: generateFailedEvent,
207
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ int, events []types.Event) {
208
				require.Len(t, events, 1, "One event expected")
209
				require.Equal(t, []string{"/bin/foobar"}, events[0].Args, "Event has bad args")
210
				require.NotEqual(t, int(0), events[0].Retval, "Event returns 0, while it should return an error code")
211
			},
212
		},
213
		"event_failed_ignored": {
214
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
215
				return &tracer.Config{
216
					MountnsMap:   utilstest.CreateMntNsFilterMap(t, info.MountNsID),
217
					IgnoreErrors: true,
218
				}
219
			},
220
			generateEvent: generateFailedEvent,
221
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ int, events []types.Event) {
222
				require.Len(t, events, 0, "Zero events expected")
223
			},
224
		},
225
		"event_from_non_main_thread_success": {
226
			shouldSkip: func(t *testing.T) {
227
				if _, err := exec.LookPath("python3"); err != nil {
228
					t.Skip("Python3 not found")
229
				}
230
			},
231
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
232
				return &tracer.Config{
233
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
234
				}
235
			},
236
			runnerConfig: &utilstest.RunnerConfig{
237
				Uid: unprivilegedUID,
238
				Gid: unprivilegedGID,
239
			},
240
			generateEvent: generateEventFromThread(true),
241
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ int, events []types.Event) {
242
				// python + cat
243
				require.Len(t, events, 2, "Two events expected")
244
				require.Equal(t, "python3", events[0].Comm, "Event has bad comm")
245
				require.Equal(t, 0, events[0].Retval, "Event has bad retval")
246
				require.Equal(t, "cat", events[1].Comm, "Event has bad comm")
247
				require.Equal(t, 0, events[1].Retval, "Event has bad retval")
248
			},
249
		},
250
		"event_from_non_main_thread_fail": {
251
			shouldSkip: func(t *testing.T) {
252
				if _, err := exec.LookPath("python3"); err != nil {
253
					t.Skip("Python3 not found")
254
				}
255
			},
256
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
257
				return &tracer.Config{
258
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
259
				}
260
			},
261
			runnerConfig: &utilstest.RunnerConfig{
262
				Uid: unprivilegedUID,
263
				Gid: unprivilegedGID,
264
			},
265
			generateEvent: generateEventFromThread(false),
266
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ int, events []types.Event) {
267
				// python + cat
268
				require.Len(t, events, 2, "Two events expected")
269
				require.Equal(t, "python3", events[0].Comm, "Event has bad comm")
270
				require.Equal(t, 0, events[0].Retval, "Event has bad retval")
271
				require.Equal(t, "python3", events[1].Comm, "Event has bad comm")
272
				require.Equal(t, -int(unix.ENOENT), events[1].Retval, "Event has bad retval")
273
			},
274
		},
275
	} {
276
		test := test
277

278
		t.Run(name, func(t *testing.T) {
279
			t.Parallel()
280

281
			if test.shouldSkip != nil {
282
				test.shouldSkip(t)
283
			}
284

285
			events := []types.Event{}
286
			eventCallback := func(event *types.Event) {
287
				// normalize
288
				event.Timestamp = 0
289

290
				events = append(events, *event)
291
			}
292

293
			runner := utilstest.NewRunnerWithTest(t, test.runnerConfig)
294

295
			createTracer(t, test.getTracerConfig(runner.Info), eventCallback)
296

297
			var catPid int
298

299
			utilstest.RunWithRunner(t, runner, func() error {
300
				var err error
301
				catPid, err = test.generateEvent()
302
				return err
303
			})
304

305
			// Give some time for the tracer to capture the events
306
			time.Sleep(100 * time.Millisecond)
307

308
			test.validateEvent(t, runner.Info, catPid, events)
309
		})
310
	}
311
}
312

313
func TestExecTracerMultipleMntNsIDsFilter(t *testing.T) {
314
	t.Parallel()
315

316
	utilstest.RequireRoot(t)
317

318
	events := []types.Event{}
319
	eventCallback := func(event *types.Event) {
320
		// normalize
321
		event.Timestamp = 0
322

323
		events = append(events, *event)
324
	}
325

326
	// struct with only fields we want to check on this test
327
	type expectedEvent struct {
328
		mntNsID uint64
329
		catPid  int
330
	}
331

332
	const n int = 5
333
	runners := make([]*utilstest.Runner, n)
334
	expectedEvents := make([]expectedEvent, n)
335
	mntNsIDs := make([]uint64, n)
336

337
	for i := 0; i < n; i++ {
338
		runners[i] = utilstest.NewRunnerWithTest(t, nil)
339
		mntNsIDs[i] = runners[i].Info.MountNsID
340
		expectedEvents[i].mntNsID = runners[i].Info.MountNsID
341
	}
342

343
	// Filter events from all runners but last one
344
	config := &tracer.Config{
345
		MountnsMap: utilstest.CreateMntNsFilterMap(t, mntNsIDs[:n-1]...),
346
	}
347

348
	createTracer(t, config, eventCallback)
349

350
	for i := 0; i < n; i++ {
351
		utilstest.RunWithRunner(t, runners[i], func() error {
352
			var err error
353
			expectedEvents[i].catPid, err = generateEvent()
354
			return err
355
		})
356
	}
357

358
	// Give some time for the tracer to capture the events
359
	time.Sleep(100 * time.Millisecond)
360

361
	require.Len(t, events, n-1)
362

363
	// Pop last event since it shouldn't have been captured
364
	expectedEvents = expectedEvents[:n-1]
365

366
	// Order or events is not guaranteed, then we need to sort before comparing
367
	sort.Slice(expectedEvents, func(i, j int) bool {
368
		return expectedEvents[i].mntNsID < expectedEvents[j].mntNsID
369
	})
370
	sort.Slice(events, func(i, j int) bool {
371
		return events[i].WithMountNsID.MountNsID < events[j].WithMountNsID.MountNsID
372
	})
373

374
	for i := 0; i < n-1; i++ {
375
		require.Equal(t, expectedEvents[i].mntNsID, events[i].WithMountNsID.MountNsID,
376
			"Captured event has bad MountNsID")
377

378
		require.Equal(t, uint32(expectedEvents[i].catPid), events[i].Pid,
379
			"Captured event has bad PID")
380
	}
381
}
382

383
func createTracer(
384
	t *testing.T, config *tracer.Config, callback func(*types.Event),
385
) *tracer.Tracer {
386
	t.Helper()
387

388
	tracer, err := tracer.NewTracer(config, nil, callback)
389
	require.Nil(t, err, "Error creating tracer: %s", err)
390
	t.Cleanup(tracer.Stop)
391

392
	return tracer
393
}
394

395
// Function to generate an event used most of the times.
396
// Returns pid of executed process.
397
func generateEvent() (int, error) {
398
	cmd := exec.Command("/bin/cat", "/dev/null")
399
	if err := cmd.Run(); err != nil {
400
		return 0, fmt.Errorf("running command: %w", err)
401
	}
402

403
	return cmd.Process.Pid, nil
404
}
405

406
// Function to generate a failed event.
407
// Return 0 as no process is created.
408
func generateFailedEvent() (int, error) {
409
	// Ignore error since we want to capture a failed event
410
	exec.Command("/bin/foobar").Run()
411
	return 0, nil
412
}
413

414
// Function to generate an exec() event from a thread.
415
func generateEventFromThread(success bool) func() (int, error) {
416
	return func() (int, error) {
417
		bin := "/bin/cat"
418
		if !success {
419
			bin = "/bin/NONE"
420
		}
421
		script := fmt.Sprintf(`
422
import threading
423
import os
424

425
def exec():
426
    os.execve("%s", ["cat", "/dev/null"], {})
427

428
def main():
429
    thread = threading.Thread(target=exec)
430
    thread.start()
431
    thread.join()
432

433
if __name__ == "__main__":
434
    main()
435
`, bin)
436
		cmd := exec.Command("python3", "-c", script)
437
		if err := cmd.Run(); err != nil {
438
			return 0, fmt.Errorf("running command: %w", err)
439
		}
440
		return cmd.Process.Pid, nil
441
	}
442
}
443

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.