github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadgets/trace/signal/tracer/tracer_test.go (about)

     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  	"syscall"
    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/signal/tracer"
    33  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/signal/types"
    34  	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
    35  )
    36  
    37  func TestSignalTracerCreate(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 TestSignalTracerStopIdempotent(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 TestSignalTracer(t *testing.T) {
    59  	t.Parallel()
    60  
    61  	utilstest.RequireRoot(t)
    62  
    63  	const unprivilegedUID = int(1435)
    64  	const unprivilegedGID = int(6789)
    65  
    66  	type testDefinition struct {
    67  		getTracerConfig func(info *utilstest.RunnerInfo) *tracer.Config
    68  		runnerConfig    *utilstest.RunnerConfig
    69  		signalToSend    syscall.Signal
    70  		generateEvent   func(syscall.Signal) (uint32, error)
    71  		validateEvent   func(t *testing.T, info *utilstest.RunnerInfo, childPid uint32, events []types.Event)
    72  	}
    73  
    74  	tests := map[string]testDefinition{
    75  		"captures_events_with_matching_filter": {
    76  			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
    77  				return &tracer.Config{
    78  					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
    79  				}
    80  			},
    81  			signalToSend:  syscall.SIGKILL,
    82  			generateEvent: generateEvent,
    83  			validateEvent: utilstest.ExpectOneEvent(func(info *utilstest.RunnerInfo, childPid uint32) *types.Event {
    84  				return &types.Event{
    85  					Event: eventtypes.Event{
    86  						Type: eventtypes.NORMAL,
    87  					},
    88  					Pid:           uint32(info.Pid),
    89  					Comm:          path.Base(os.Args[0]),
    90  					Signal:        unix.SignalName(syscall.SIGKILL),
    91  					TargetPid:     childPid,
    92  					Retval:        0,
    93  					Uid:           uint32(info.Uid),
    94  					Gid:           uint32(info.Gid),
    95  					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
    96  				}
    97  			}),
    98  		},
    99  		"event_has_UID_and_GID_of_user_generating_event": {
   100  			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
   101  				return &tracer.Config{
   102  					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
   103  				}
   104  			},
   105  			runnerConfig: &utilstest.RunnerConfig{
   106  				Uid: unprivilegedUID,
   107  				Gid: unprivilegedGID,
   108  			},
   109  			signalToSend:  syscall.SIGKILL,
   110  			generateEvent: generateEvent,
   111  			validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, _ uint32, events []types.Event) {
   112  				require.Len(t, events, 1, "One event expected")
   113  				require.Equal(t, uint32(info.Uid), events[0].Uid, "Event has bad UID")
   114  				require.Equal(t, uint32(info.Gid), events[0].Gid, "Event has bad GID")
   115  			},
   116  		},
   117  	}
   118  
   119  	for sig := syscall.SIGABRT; sig <= syscall.SIGXFSZ; sig++ {
   120  		signal := sig
   121  		tests[fmt.Sprintf("send_%s", unix.SignalName(signal))] = testDefinition{
   122  			getTracerConfig: func(info *utilstest.RunnerInfo) *tracer.Config {
   123  				return &tracer.Config{
   124  					MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
   125  				}
   126  			},
   127  			signalToSend:  signal,
   128  			generateEvent: generateEvent,
   129  			validateEvent: utilstest.ExpectAtLeastOneEvent(func(info *utilstest.RunnerInfo, childPid uint32) *types.Event {
   130  				return &types.Event{
   131  					Event: eventtypes.Event{
   132  						Type: eventtypes.NORMAL,
   133  					},
   134  					Pid:           uint32(info.Pid),
   135  					Comm:          path.Base(os.Args[0]),
   136  					Signal:        unix.SignalName(signal),
   137  					TargetPid:     childPid,
   138  					Retval:        0,
   139  					Uid:           uint32(info.Uid),
   140  					Gid:           uint32(info.Gid),
   141  					WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
   142  				}
   143  			}),
   144  		}
   145  	}
   146  
   147  	for name, test := range tests {
   148  		test := test
   149  
   150  		t.Run(name, func(t *testing.T) {
   151  			t.Parallel()
   152  
   153  			events := []types.Event{}
   154  			eventCallback := func(event *types.Event) {
   155  				// normalize
   156  				event.Timestamp = 0
   157  
   158  				events = append(events, *event)
   159  			}
   160  
   161  			runner := utilstest.NewRunnerWithTest(t, test.runnerConfig)
   162  
   163  			createTracer(t, test.getTracerConfig(runner.Info), eventCallback)
   164  
   165  			var childPid uint32
   166  
   167  			utilstest.RunWithRunner(t, runner, func() error {
   168  				var err error
   169  				childPid, err = test.generateEvent(test.signalToSend)
   170  				return err
   171  			})
   172  
   173  			// Give some time for the tracer to capture the events
   174  			time.Sleep(100 * time.Millisecond)
   175  
   176  			test.validateEvent(t, runner.Info, childPid, events)
   177  		})
   178  	}
   179  }
   180  
   181  func createTracer(
   182  	t *testing.T, config *tracer.Config, callback func(*types.Event),
   183  ) *tracer.Tracer {
   184  	t.Helper()
   185  
   186  	tracer, err := tracer.NewTracer(config, nil, callback)
   187  	require.Nil(t, err, "Error creating tracer: %s", err)
   188  	t.Cleanup(tracer.Stop)
   189  
   190  	return tracer
   191  }
   192  
   193  func generateEvent(signal syscall.Signal) (uint32, error) {
   194  	childPid, err := syscall.ForkExec("/bin/sleep", []string{"inf"}, nil)
   195  	if err != nil {
   196  		return 0, fmt.Errorf("spawning child process: %w", err)
   197  	}
   198  
   199  	// We only test kill and not tkill or tgkill as this is a pain to deal with
   200  	// pthread in golang.
   201  	err = syscall.Kill(childPid, signal)
   202  	if err != nil {
   203  		return 0, fmt.Errorf("sending signal %d to process %d: %w", signal, childPid, err)
   204  	}
   205  
   206  	return uint32(childPid), nil
   207  }