inspektor-gadget

Форк
0
620 строк · 17.7 Кб
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
	"path"
24
	"path/filepath"
25
	"sort"
26
	"testing"
27
	"time"
28

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/open/tracer"
33
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/open/types"
34
	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
35
)
36

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

40
	utilstest.RequireRoot(t)
41

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

48
func TestOpenTracerStopIdempotent(t *testing.T) {
49
	t.Parallel()
50

51
	utilstest.RequireRoot(t)
52

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

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

60
func createTracer(
61
	t *testing.T, config *tracer.Config, callback func(*types.Event),
62
) *tracer.Tracer {
63
	t.Helper()
64

65
	tracer, err := tracer.NewTracer(config, nil, callback)
66
	if err != nil {
67
		t.Fatalf("Error creating tracer: %s", err)
68
	}
69
	t.Cleanup(tracer.Stop)
70

71
	return tracer
72
}
73

74
func generateRelativePathForAbsolutePath(t *testing.T, fileName string) string {
75
	// If the filename is relative, return it as is
76
	if !filepath.IsAbs(fileName) {
77
		return fileName
78
	}
79

80
	cwd, err := os.Getwd()
81
	if err != nil {
82
		t.Errorf("Error getting current working directory: %s", err)
83
	}
84

85
	relPath, err := filepath.Rel(cwd, fileName)
86
	if err != nil {
87
		t.Errorf("Error getting relative path: %s", err)
88
	}
89

90
	return relPath
91
}
92

93
func generateLongDirPath(t *testing.T) string {
94
	size := 64
95
	str := "abcdefgh"
96
	slice := make([]string, size)
97
	for i := 0; i < size; i++ {
98
		slice[i] = str
99
	}
100
	return path.Join("/tmp", path.Join(slice...))
101
}
102

103
func TestOpenTracer(t *testing.T) {
104
	t.Parallel()
105

106
	utilstest.RequireRoot(t)
107

108
	const unprivilegedUID = int(1435)
109
	const unprivilegedGID = int(6789)
110

111
	type testDefinition struct {
112
		getTracerConfig func(info *utilstest.RunnerInfo) *tracer.Config
113
		runnerConfig    *utilstest.RunnerConfig
114
		generateEvent   func() (int, error)
115
		validateEvent   func(*testing.T, *utilstest.RunnerInfo, int, []types.Event)
116
	}
117

118
	for name, test := range map[string]testDefinition{
119
		"captures_all_events_with_no_filters_configured": {
120
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
121
				return &tracer.Config{}
122
			},
123
			generateEvent: generateEvent,
124
			validateEvent: utilstest.ExpectAtLeastOneEvent(func(info *utilstest.RunnerInfo, fd int) *types.Event {
125
				return &types.Event{
126
					Event: eventtypes.Event{
127
						Type: eventtypes.NORMAL,
128
					},
129
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
130
					Pid:           uint32(info.Pid),
131
					Uid:           uint32(info.Uid),
132
					Comm:          info.Comm,
133
					Fd:            fd,
134
					Ret:           fd,
135
					Err:           0,
136
					Path:          "/dev/null",
137
					Flags:         []string{"O_RDONLY"},
138
					Mode:          "----------",
139
				}
140
			}),
141
		},
142
		"captures_no_events_with_no_matching_filter": {
143
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
144
				return &tracer.Config{
145
					MountnsMap: utilstest.CreateMntNsFilterMap(t, 0),
146
				}
147
			},
148
			generateEvent: generateEvent,
149
			validateEvent: utilstest.ExpectNoEvent[types.Event, int],
150
		},
151
		"captures_events_with_matching_filter": {
152
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
153
				return &tracer.Config{
154
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
155
				}
156
			},
157
			generateEvent: generateEvent,
158
			validateEvent: utilstest.ExpectOneEvent(func(info *utilstest.RunnerInfo, fd int) *types.Event {
159
				return &types.Event{
160
					Event: eventtypes.Event{
161
						Type: eventtypes.NORMAL,
162
					},
163
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
164
					Pid:           uint32(info.Pid),
165
					Uid:           uint32(info.Uid),
166
					Comm:          info.Comm,
167
					Fd:            fd,
168
					Ret:           fd,
169
					Err:           0,
170
					Path:          "/dev/null",
171
					FullPath:      "",
172
					Flags:         []string{"O_RDONLY"},
173
					Mode:          "----------",
174
				}
175
			}),
176
		},
177
		"test_flags_and_mode": {
178
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
179
				return &tracer.Config{
180
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
181
				}
182
			},
183
			generateEvent: func() (int, error) {
184
				filename := "/tmp/test_flags_and_mode"
185
				fd, err := unix.Open(filename, unix.O_CREAT|unix.O_RDWR, unix.S_IRWXU|unix.S_IRGRP|unix.S_IWGRP|unix.S_IXOTH)
186
				if err != nil {
187
					return 0, fmt.Errorf("opening file: %w", err)
188
				}
189

190
				defer os.Remove(filename)
191

192
				unix.Close(fd)
193

194
				return fd, nil
195
			},
196
			validateEvent: utilstest.ExpectOneEvent(func(info *utilstest.RunnerInfo, fd int) *types.Event {
197
				return &types.Event{
198
					Event: eventtypes.Event{
199
						Type: eventtypes.NORMAL,
200
					},
201
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
202
					Pid:           uint32(info.Pid),
203
					Uid:           uint32(info.Uid),
204
					Comm:          info.Comm,
205
					Fd:            fd,
206
					Ret:           fd,
207
					Err:           0,
208
					Path:          "/tmp/test_flags_and_mode",
209
					FullPath:      "",
210
					Flags:         []string{"O_RDWR", "O_CREAT"},
211
					FlagsRaw:      unix.O_CREAT | unix.O_RDWR,
212
					Mode:          "-rwxrw---x",
213
					ModeRaw:       unix.S_IRWXU | unix.S_IRGRP | unix.S_IWGRP | unix.S_IXOTH,
214
				}
215
			}),
216
		},
217
		"test_relative_path": {
218
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
219
				return &tracer.Config{
220
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
221
					FullPath:   true,
222
				}
223
			},
224
			generateEvent: func() (int, error) {
225
				relPath := generateRelativePathForAbsolutePath(t, "/tmp/test_relative_path")
226
				fd, err := unix.Open(relPath, unix.O_CREAT|unix.O_RDWR, unix.S_IRWXU|unix.S_IRGRP|unix.S_IWGRP|unix.S_IXOTH)
227
				if err != nil {
228
					return 0, fmt.Errorf("opening file: %w", err)
229
				}
230

231
				defer os.Remove(relPath)
232

233
				unix.Close(fd)
234

235
				return fd, nil
236
			},
237
			validateEvent: utilstest.ExpectOneEvent(func(info *utilstest.RunnerInfo, fd int) *types.Event {
238
				relPath := generateRelativePathForAbsolutePath(t, "/tmp/test_relative_path")
239
				return &types.Event{
240
					Event: eventtypes.Event{
241
						Type: eventtypes.NORMAL,
242
					},
243
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
244
					Pid:           uint32(info.Pid),
245
					Uid:           uint32(info.Uid),
246
					Comm:          info.Comm,
247
					Fd:            fd,
248
					Ret:           fd,
249
					Err:           0,
250
					Path:          relPath,
251
					FullPath:      "/tmp/test_relative_path",
252
					Flags:         []string{"O_RDWR", "O_CREAT"},
253
					FlagsRaw:      unix.O_CREAT | unix.O_RDWR,
254
					Mode:          "-rwxrw---x",
255
					ModeRaw:       unix.S_IRWXU | unix.S_IRGRP | unix.S_IWGRP | unix.S_IXOTH,
256
				}
257
			}),
258
		},
259
		"test_symbolic_links": {
260
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
261
				return &tracer.Config{
262
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
263
					FullPath:   true,
264
				}
265
			},
266
			generateEvent: func() (int, error) {
267
				// Create a symbolic link to /dev/null
268
				err := os.Symlink("/dev/null", "/tmp/test_symbolic_links")
269
				if err != nil {
270
					return 0, fmt.Errorf("creating symbolic link: %w", err)
271
				}
272
				defer os.Remove("/tmp/test_symbolic_links")
273

274
				// Open the symbolic link
275
				fd, err := unix.Open("/tmp/test_symbolic_links", unix.O_RDONLY, 0)
276
				if err != nil {
277
					return 0, fmt.Errorf("opening file: %w", err)
278
				}
279
				defer unix.Close(fd)
280

281
				return fd, nil
282
			},
283
			validateEvent: utilstest.ExpectOneEvent(func(info *utilstest.RunnerInfo, fd int) *types.Event {
284
				return &types.Event{
285
					Event: eventtypes.Event{
286
						Type: eventtypes.NORMAL,
287
					},
288
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
289
					Pid:           uint32(info.Pid),
290
					Uid:           uint32(info.Uid),
291
					Comm:          info.Comm,
292
					Fd:            fd,
293
					Ret:           fd,
294
					Err:           0,
295
					Path:          "/tmp/test_symbolic_links",
296
					FullPath:      "/dev/null",
297
					Flags:         []string{"O_RDONLY"},
298
					Mode:          "----------",
299
				}
300
			}),
301
		},
302
		"test_long_path": {
303
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
304
				return &tracer.Config{
305
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
306
					FullPath:   true,
307
				}
308
			},
309
			generateEvent: func() (int, error) {
310
				dirPath := generateLongDirPath(t)
311
				err := os.MkdirAll(generateLongDirPath(t), 0o755)
312
				if err != nil {
313
					return 0, fmt.Errorf("creating directory: %w", err)
314
				}
315
				fd, err := unix.Open(path.Join(dirPath, "test_long_path"), unix.O_CREAT|unix.O_RDWR, unix.S_IRWXU|unix.S_IRGRP|unix.S_IWGRP|unix.S_IXOTH)
316
				if err != nil {
317
					return 0, fmt.Errorf("opening file: %w", err)
318
				}
319

320
				defer os.RemoveAll(path.Join(dirPath, "test_long_path"))
321

322
				unix.Close(fd)
323

324
				return fd, nil
325
			},
326
			validateEvent: utilstest.ExpectOneEvent(func(info *utilstest.RunnerInfo, fd int) *types.Event {
327
				longPath := path.Join(generateLongDirPath(t), "test_long_path")
328
				return &types.Event{
329
					Event: eventtypes.Event{
330
						Type: eventtypes.NORMAL,
331
					},
332
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
333
					Pid:           uint32(info.Pid),
334
					Uid:           uint32(info.Uid),
335
					Comm:          info.Comm,
336
					Fd:            fd,
337
					Ret:           fd,
338
					Err:           0,
339
					Path:          longPath[:254],
340
					FullPath:      longPath,
341
					Flags:         []string{"O_RDWR", "O_CREAT"},
342
					FlagsRaw:      unix.O_CREAT | unix.O_RDWR,
343
					Mode:          "-rwxrw---x",
344
					ModeRaw:       unix.S_IRWXU | unix.S_IRGRP | unix.S_IWGRP | unix.S_IXOTH,
345
				}
346
			}),
347
		},
348
		"test_prefix_on_directory": {
349
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
350
				return &tracer.Config{
351
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
352
					Prefixes:   []string{"/tmp/foo"},
353
				}
354
			},
355
			generateEvent: func() (int, error) {
356
				err := os.Mkdir("/tmp/foo", 0o750)
357
				if err != nil {
358
					return 0, fmt.Errorf("creating directory: %w", err)
359
				}
360
				defer os.RemoveAll("/tmp/foo")
361

362
				fd, err := unix.Open("/tmp/foo/bar.test", unix.O_RDONLY|unix.O_CREAT, 0)
363
				if err != nil {
364
					return 0, fmt.Errorf("opening file: %w", err)
365
				}
366
				defer unix.Close(fd)
367

368
				badfd, err := unix.Open("/tmp/quux.test", unix.O_RDONLY|unix.O_CREAT, 0)
369
				if err != nil {
370
					return 0, fmt.Errorf("opening file: %w", err)
371
				}
372
				defer unix.Close(badfd)
373

374
				return fd, nil
375
			},
376
			validateEvent: utilstest.ExpectOneEvent(func(info *utilstest.RunnerInfo, fd int) *types.Event {
377
				return &types.Event{
378
					Event: eventtypes.Event{
379
						Type: eventtypes.NORMAL,
380
					},
381
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
382
					Pid:           uint32(info.Pid),
383
					Uid:           uint32(info.Uid),
384
					Comm:          info.Comm,
385
					Fd:            fd,
386
					Ret:           fd,
387
					Err:           0,
388
					Path:          "/tmp/foo/bar.test",
389
					Flags:         []string{"O_RDONLY", "O_CREAT"},
390
					FlagsRaw:      unix.O_RDONLY | unix.O_CREAT,
391
					Mode:          "----------",
392
				}
393
			}),
394
		},
395
		"test_prefix_on_file": {
396
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
397
				return &tracer.Config{
398
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
399
					Prefixes:   []string{"/tmp/foo.test"},
400
				}
401
			},
402
			generateEvent: func() (int, error) {
403
				fd, err := unix.Open("/tmp/foo.test", unix.O_RDONLY|unix.O_CREAT, 0)
404
				if err != nil {
405
					return 0, fmt.Errorf("opening file: %w", err)
406
				}
407
				defer unix.Close(fd)
408

409
				badfd, err := unix.Open("/tmp/quux.test", unix.O_RDONLY|unix.O_CREAT, 0)
410
				if err != nil {
411
					return 0, fmt.Errorf("opening file: %w", err)
412
				}
413
				defer unix.Close(badfd)
414

415
				return fd, nil
416
			},
417
			validateEvent: utilstest.ExpectOneEvent(func(info *utilstest.RunnerInfo, fd int) *types.Event {
418
				return &types.Event{
419
					Event: eventtypes.Event{
420
						Type: eventtypes.NORMAL,
421
					},
422
					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
423
					Pid:           uint32(info.Pid),
424
					Uid:           uint32(info.Uid),
425
					Comm:          info.Comm,
426
					Fd:            fd,
427
					Ret:           fd,
428
					Err:           0,
429
					Path:          "/tmp/foo.test",
430
					Flags:         []string{"O_RDONLY", "O_CREAT"},
431
					FlagsRaw:      unix.O_RDONLY | unix.O_CREAT,
432
					Mode:          "----------",
433
				}
434
			}),
435
		},
436
		"event_has_UID_and_GID_of_user_generating_event": {
437
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
438
				return &tracer.Config{
439
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
440
				}
441
			},
442
			runnerConfig: &utilstest.RunnerConfig{
443
				Uid: unprivilegedUID,
444
				Gid: unprivilegedGID,
445
			},
446
			generateEvent: generateEvent,
447
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ int, events []types.Event) {
448
				if len(events) != 1 {
449
					t.Fatalf("One event expected")
450
				}
451

452
				utilstest.Equal(t, uint32(info.Uid), events[0].Uid,
453
					"Captured event has bad UID")
454

455
				utilstest.Equal(t, uint32(info.Gid), events[0].Gid,
456
					"Captured event event has bad GID")
457
			},
458
		},
459
		"event_has_correct_error": {
460
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
461
				return &tracer.Config{
462
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
463
				}
464
			},
465
			generateEvent: func() (int, error) {
466
				_, err := unix.Open("non-existing-file", 0, 0)
467
				if err == nil {
468
					return 0, fmt.Errorf("error was expected")
469
				}
470

471
				return -1, nil
472
			},
473
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ int, events []types.Event) {
474
				if len(events) != 1 {
475
					t.Fatalf("One event expected")
476
				}
477

478
				utilstest.Equal(t, int(unix.ENOENT), events[0].Err,
479
					"Captured event has bad Err")
480
			},
481
		},
482
		"event_has_correct_ret_on_error": {
483
			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
484
				return &tracer.Config{
485
					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
486
				}
487
			},
488
			generateEvent: func() (int, error) {
489
				_, err := unix.Open("non-existing-file", 0, 0)
490
				if err == nil {
491
					return 0, fmt.Errorf("error was expected")
492
				}
493

494
				return -1, nil
495
			},
496
			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ int, events []types.Event) {
497
				if len(events) != 1 {
498
					t.Fatalf("One event expected")
499
				}
500

501
				utilstest.Equal(t, -int(unix.ENOENT), events[0].Ret,
502
					"Captured event has bad Ret")
503
			},
504
		},
505
	} {
506
		test := test
507

508
		t.Run(name, func(t *testing.T) {
509
			t.Parallel()
510

511
			events := []types.Event{}
512
			eventCallback := func(event *types.Event) {
513
				// normalize
514
				event.Timestamp = 0
515

516
				events = append(events, *event)
517
			}
518

519
			runner := utilstest.NewRunnerWithTest(t, test.runnerConfig)
520

521
			createTracer(t, test.getTracerConfig(runner.Info), eventCallback)
522

523
			var fd int
524

525
			utilstest.RunWithRunner(t, runner, func() error {
526
				var err error
527
				fd, err = test.generateEvent()
528
				return err
529
			})
530

531
			// Give some time for the tracer to capture the events
532
			time.Sleep(100 * time.Millisecond)
533

534
			test.validateEvent(t, runner.Info, fd, events)
535
		})
536

537
	}
538
}
539

540
func TestOpenTracerMultipleMntNsIDsFilter(t *testing.T) {
541
	t.Parallel()
542

543
	utilstest.RequireRoot(t)
544

545
	events := []types.Event{}
546
	eventCallback := func(event *types.Event) {
547
		events = append(events, *event)
548
	}
549

550
	// struct with only fields we want to check on this test
551
	type expectedEvent struct {
552
		mntNsID uint64
553
		fd      int
554
	}
555

556
	const n int = 5
557
	runners := make([]*utilstest.Runner, n)
558
	expectedEvents := make([]expectedEvent, n)
559
	mntNsIDs := make([]uint64, n)
560

561
	for i := 0; i < n; i++ {
562
		runners[i] = utilstest.NewRunnerWithTest(t, nil)
563
		mntNsIDs[i] = runners[i].Info.MountNsID
564
		expectedEvents[i].mntNsID = runners[i].Info.MountNsID
565
	}
566

567
	// Filter events from all runners but last one
568
	config := &tracer.Config{
569
		MountnsMap: utilstest.CreateMntNsFilterMap(t, mntNsIDs[:n-1]...),
570
	}
571

572
	createTracer(t, config, eventCallback)
573

574
	for i := 0; i < n; i++ {
575
		utilstest.RunWithRunner(t, runners[i], func() error {
576
			var err error
577
			expectedEvents[i].fd, err = generateEvent()
578
			return err
579
		})
580
	}
581

582
	// Give some time for the tracer to capture the events
583
	time.Sleep(100 * time.Millisecond)
584

585
	if len(events) != n-1 {
586
		t.Fatalf("%d events were expected, %d found", n-1, len(events))
587
	}
588

589
	// Pop last event since it shouldn't have been captured
590
	expectedEvents = expectedEvents[:n-1]
591

592
	// Order or events is not guaranteed, then we need to sort before comparing
593
	sort.Slice(expectedEvents, func(i, j int) bool {
594
		return expectedEvents[i].mntNsID < expectedEvents[j].mntNsID
595
	})
596
	sort.Slice(events, func(i, j int) bool {
597
		return events[i].WithMountNsID.MountNsID < events[j].WithMountNsID.MountNsID
598
	})
599

600
	for i := 0; i < n-1; i++ {
601
		utilstest.Equal(t, expectedEvents[i].mntNsID, events[i].WithMountNsID.MountNsID,
602
			"Captured event has bad MountNsID")
603

604
		utilstest.Equal(t, expectedEvents[i].fd, events[i].Fd,
605
			"Captured event has bad fd")
606
	}
607
}
608

609
// Function to generate an event used most of the times.
610
// Returns fd of opened file.
611
func generateEvent() (int, error) {
612
	fd, err := unix.Open("/dev/null", 0, 0)
613
	if err != nil {
614
		return 0, fmt.Errorf("opening file: %w", err)
615
	}
616

617
	unix.Close(fd)
618

619
	return fd, nil
620
}
621

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

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

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

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