diff --git a/contrib/tester-progs/.gitignore b/contrib/tester-progs/.gitignore index d09711bef2d..1f01243d393 100644 --- a/contrib/tester-progs/.gitignore +++ b/contrib/tester-progs/.gitignore @@ -23,3 +23,4 @@ drop-privileges change-capabilities direct-write-tester user-stacktrace +pause diff --git a/contrib/tester-progs/Makefile b/contrib/tester-progs/Makefile index 7f1d761d63b..841d30cd68a 100644 --- a/contrib/tester-progs/Makefile +++ b/contrib/tester-progs/Makefile @@ -24,7 +24,8 @@ PROGS = sigkill-tester \ getcpu \ direct-write-tester \ change-capabilities \ - user-stacktrace + user-stacktrace \ + pause all: $(PROGS) diff --git a/contrib/tester-progs/pause.c b/contrib/tester-progs/pause.c new file mode 100644 index 00000000000..f69f614af26 --- /dev/null +++ b/contrib/tester-progs/pause.c @@ -0,0 +1,7 @@ +#include + +int main(int argc, char** argv) +{ + pause(); + return 0; +} diff --git a/pkg/sensors/exec/exit_test.go b/pkg/sensors/exec/exit_test.go index aad003de5f7..f2219354625 100644 --- a/pkg/sensors/exec/exit_test.go +++ b/pkg/sensors/exec/exit_test.go @@ -8,6 +8,7 @@ import ( "fmt" "os/exec" "sync" + "syscall" "testing" "time" @@ -20,6 +21,7 @@ import ( tus "github.com/cilium/tetragon/pkg/testutils/sensors" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "golang.org/x/sys/unix" ) func TestExit(t *testing.T) { @@ -241,3 +243,57 @@ func TestExitCode(t *testing.T) { err = jsonchecker.JsonTestCheck(t, checker) assert.NoError(t, err) } + +// TestExitSignal tests whether we properly return the exit signal of the process. +// see: tester-progs/pause.c for the program we use to test this. +// +// The program will: +// - Pause until it receives a signal +// +// In our test we check whether the observed exit signal equals the real exit signal. +func TestExitSignal(t *testing.T) { + var doneWG, readyWG sync.WaitGroup + defer doneWG.Wait() + + ctx, cancel := context.WithTimeout(context.Background(), tus.Conf().CmdWaitTime) + defer cancel() + + obs, err := observertesthelper.GetDefaultObserver(t, ctx, tus.Conf().TetragonLib, observertesthelper.WithMyPid()) + if err != nil { + t.Fatalf("Failed to run observer: %s", err) + } + observertesthelper.LoopEvents(ctx, t, &doneWG, &readyWG, obs) + readyWG.Wait() + + checker := ec.NewOrderedEventChecker() + testExitSignalBinary := testutils.RepoRootPath("contrib/tester-progs/pause") + + for sig := 1; sig <= 15; sig++ { + signal := syscall.Signal(sig) + expectedSignal := unix.SignalName(signal) + cmd := exec.CommandContext(ctx, testExitSignalBinary) + + if err := cmd.Start(); err != nil { + t.Fatalf("failed to execute the test binary with signal %q: %s", expectedSignal, err) + } + + if err := cmd.Process.Signal(signal); err != nil { + t.Fatalf("failed to send signal %q to the test binary: %s", expectedSignal, err) + } + + if err := cmd.Wait(); err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + if got := exitErr.Sys().(syscall.WaitStatus).Signal(); got != signal { + t.Errorf("unexpected: wanted signal %q, execution returned %q", signal, got) + } + } + } + + checker.AddChecks( + ec.NewProcessExitChecker(fmt.Sprintf("exitSignal=%s", expectedSignal)).WithSignal(sm.Full(expectedSignal)), + ) + } + + err = jsonchecker.JsonTestCheck(t, checker) + assert.NoError(t, err) +}