diff options
Diffstat (limited to 'test/internal/testsuite')
| -rw-r--r-- | test/internal/testsuite/ptrace.go | 136 | ||||
| -rw-r--r-- | test/internal/testsuite/ptrace_test.go | 13 | ||||
| -rw-r--r-- | test/internal/testsuite/testsuite.go | 4 | ||||
| -rw-r--r-- | test/internal/testsuite/testsuite_root.go | 14 |
4 files changed, 163 insertions, 4 deletions
diff --git a/test/internal/testsuite/ptrace.go b/test/internal/testsuite/ptrace.go new file mode 100644 index 00000000..4fcf1508 --- /dev/null +++ b/test/internal/testsuite/ptrace.go @@ -0,0 +1,136 @@ +package testsuite + +import ( + "crypto/sha512" + "encoding/hex" + "errors" + "fmt" + "os" + "syscall" + "unsafe" +) + +const ( + // _PTRACE_ATTACH attaches to the process specified in pid. + _PTRACE_ATTACH = 16 + // _PTRACE_DETACH restarts the stopped tracee as for PTRACE_CONT, but first + // detaches from it. + _PTRACE_DETACH = 17 + + // _PTRACE_SECCOMP_GET_FILTER allows the tracer to dump the tracee's classic + // BPF filters. + _PTRACE_SECCOMP_GET_FILTER = 0x420c +) + +// ptrace wraps the ptrace syscall. +func ptrace( + op uintptr, + pid, addr int, + data unsafe.Pointer, +) (r uintptr, errno syscall.Errno) { + r, _, errno = syscall.Syscall6( + syscall.SYS_PTRACE, + op, + uintptr(pid), + uintptr(addr), + uintptr(data), + 0, 0, + ) + return +} + +// ptraceAttach attaches to the process referred to by pid. +func ptraceAttach(pid int) error { + if _, errno := ptrace(_PTRACE_ATTACH, pid, 0, nil); errno != 0 { + return os.NewSyscallError("PTRACE_ATTACH", errno) + } + + var status syscall.WaitStatus + for { + if _, err := syscall.Wait4( + pid, + &status, + syscall.WALL, + nil, + ); err != nil { + if errors.Is(err, syscall.EINTR) { + continue + } + return os.NewSyscallError("wait4", err) + } + break + } + + return nil +} + +// ptraceDetach detaches from the attached process referred to by pid. +func ptraceDetach(pid int) error { + if _, errno := ptrace(_PTRACE_DETACH, pid, 0, nil); errno != 0 { + return os.NewSyscallError("PTRACE_DETACH", errno) + } + return nil +} + +// getFilter dumps the specified tracee's cBPF filter at the specified index +// and returns the resulting payload. T must be eight bytes long and must not +// contain pointers. +func getFilter(pid, index int) ([]syscall.SockFilter, error) { + var buf []syscall.SockFilter + if n, errno := ptrace( + _PTRACE_SECCOMP_GET_FILTER, + pid, index, nil, + ); errno != 0 { + return nil, os.NewSyscallError("PTRACE_SECCOMP_GET_FILTER", errno) + } else { + buf = make([]syscall.SockFilter, n) + } + if _, errno := ptrace( + _PTRACE_SECCOMP_GET_FILTER, + pid, index, unsafe.Pointer(&buf[0]), + ); errno != 0 { + return nil, os.NewSyscallError("PTRACE_SECCOMP_GET_FILTER", errno) + } + return buf, nil +} + +// CheckFilter checks the process at pid to have its first filter's contents +// match the sha512 checksum specified in hexadecimal string representation. +func CheckFilter(pid, index int, sum string) (err error) { + if err = ptraceAttach(pid); err != nil { + return + } + defer func() { + if detachErr := ptraceDetach(pid); err == nil { + err = detachErr + } + }() + + var ( + buf []syscall.SockFilter + want []byte + ) + + if want, err = hex.DecodeString(sum); err != nil { + return + } + + h := sha512.New() + if buf, err = getFilter(pid, index); err != nil { + return + } else { + h.Write(unsafe.Slice( + (*byte)(unsafe.Pointer(&buf[0])), + uintptr(len(buf))*unsafe.Sizeof(buf[0]), + )) + } + + if got := h.Sum(nil); string(got) != string(want) { + return fmt.Errorf( + "bad filter\n\t got: %s\n\twant: %s", + hex.EncodeToString(got), + sum, + ) + } + return +} diff --git a/test/internal/testsuite/ptrace_test.go b/test/internal/testsuite/ptrace_test.go new file mode 100644 index 00000000..eaaed131 --- /dev/null +++ b/test/internal/testsuite/ptrace_test.go @@ -0,0 +1,13 @@ +package testsuite + +import ( + "syscall" + "testing" + "unsafe" +) + +func TestBlockSize(t *testing.T) { + if sz := unsafe.Sizeof(syscall.SockFilter{}); sz != 8 { + t.Fatalf("invalid filter block size %d", sz) + } +} diff --git a/test/internal/testsuite/testsuite.go b/test/internal/testsuite/testsuite.go index 9233818d..2173509d 100644 --- a/test/internal/testsuite/testsuite.go +++ b/test/internal/testsuite/testsuite.go @@ -13,10 +13,6 @@ import ( func init() { log.SetFlags(0) log.SetPrefix("testsuite: ") - - if os.Geteuid() != 0 { - log.Fatal("this program must run as root") - } } // ReceiveSignals blocks until a termination signal arrives, and terminates. diff --git a/test/internal/testsuite/testsuite_root.go b/test/internal/testsuite/testsuite_root.go new file mode 100644 index 00000000..830e272c --- /dev/null +++ b/test/internal/testsuite/testsuite_root.go @@ -0,0 +1,14 @@ +//go:build testsuite + +package testsuite + +import ( + "log" + "os" +) + +func init() { + if os.Geteuid() != 0 { + log.Fatal("this program must run as root") + } +} |
