aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/sandbox/assert.go
diff options
context:
space:
mode:
Diffstat (limited to 'test/sandbox/assert.go')
-rw-r--r--test/sandbox/assert.go272
1 files changed, 0 insertions, 272 deletions
diff --git a/test/sandbox/assert.go b/test/sandbox/assert.go
deleted file mode 100644
index 82c928b0..00000000
--- a/test/sandbox/assert.go
+++ /dev/null
@@ -1,272 +0,0 @@
-//go:build testtool
-
-/*
-Package sandbox provides utilities for checking sandbox outcome.
-
-This package must never be used outside integration tests, there is a much better native implementation of mountinfo
-in the public sandbox/vfs package. Files in this package are excluded by the build system to prevent accidental misuse.
-*/
-package sandbox
-
-import (
- "crypto/sha512"
- "encoding/hex"
- "encoding/json"
- "errors"
- "io/fs"
- "log"
- "net"
- "os"
- "path"
- "syscall"
-)
-
-var (
- assert = log.New(os.Stderr, "sandbox: ", 0)
- printfFunc = assert.Printf
- fatalfFunc = assert.Fatalf
-)
-
-func printf(format string, v ...any) { printfFunc(format, v...) }
-func fatalf(format string, v ...any) { fatalfFunc(format, v...) }
-
-type TestCase struct {
- Env []string `json:"env"`
- FS *FS `json:"fs"`
- Mount []*MountinfoEntry `json:"mount"`
- Seccomp bool `json:"seccomp"`
-
- TrySocket string `json:"try_socket,omitempty"`
- SocketAbstract bool `json:"socket_abstract,omitempty"`
- SocketPathname bool `json:"socket_pathname,omitempty"`
-}
-
-type T struct {
- FS fs.FS
-
- MountsPath string
-}
-
-func (t *T) MustCheckFile(wantFilePath string) {
- var want *TestCase
- mustDecode(wantFilePath, &want)
- t.MustCheck(want)
-}
-
-func mustAbs(s string) string {
- if !path.IsAbs(s) {
- fatalf("[FAIL] %q is not absolute", s)
- panic("unreachable")
- }
- return s
-}
-
-func (t *T) MustCheck(want *TestCase) {
- checkWritableDirPaths := []string{
- "/dev/shm",
- "/tmp",
- os.Getenv("XDG_RUNTIME_DIR"),
- }
- for _, a := range checkWritableDirPaths {
- pathname := path.Join(mustAbs(a), ".hakurei-check")
- if err := os.WriteFile(pathname, make([]byte, 1<<8), 0600); err != nil {
- fatalf("[FAIL] %s", err)
- } else if err = os.Remove(pathname); err != nil {
- fatalf("[FAIL] %s", err)
- } else {
- printf("[ OK ] %s is writable", a)
- }
- }
-
- if want.Env != nil {
- var (
- fail bool
- i int
- got string
- )
- for i, got = range os.Environ() {
- if i == len(want.Env) {
- fatalf("got more than %d environment variables", len(want.Env))
- }
- if got != want.Env[i] {
- fail = true
- printf("[FAIL] %s", got)
- } else {
- printf("[ OK ] %s", got)
- }
- }
-
- i++
- if i != len(want.Env) {
- fatalf("got %d environment variables, want %d", i, len(want.Env))
- }
-
- if fail {
- fatalf("[FAIL] some environment variables did not match")
- }
- } else {
- printf("[SKIP] skipping environ check")
- }
-
- if want.FS != nil && t.FS != nil {
- if err := want.FS.Compare(".", t.FS); err != nil {
- fatalf("%v", err)
- }
- } else {
- printf("[SKIP] skipping fs check")
- }
-
- if want.Mount != nil {
- var fail bool
- m := mustParseMountinfo(t.MountsPath)
- i := 0
- for ent := range m.Entries() {
- if i == len(want.Mount) {
- fatalf("got more than %d entries", i)
- }
- if !ent.EqualWithIgnore(want.Mount[i], "//ignore") {
- fail = true
- printf("[FAIL] %s", ent)
- } else {
- printf("[ OK ] %s", ent)
- }
-
- i++
- }
- if err := m.Err(); err != nil {
- fatalf("%v", err)
- }
-
- if i != len(want.Mount) {
- fatalf("got %d entries, want %d", i, len(want.Mount))
- }
-
- if fail {
- fatalf("[FAIL] some mount points did not match")
- }
- } else {
- printf("[SKIP] skipping mounts check")
- }
-
- if want.Seccomp {
- if trySyscalls() != nil {
- os.Exit(1)
- }
- } else {
- printf("[SKIP] skipping seccomp check")
- }
-
- if want.TrySocket != "" {
- abstractConn, abstractErr := net.Dial("unix", "@"+want.TrySocket)
- pathnameConn, pathnameErr := net.Dial("unix", want.TrySocket)
- ok := true
-
- if abstractErr == nil {
- if err := abstractConn.Close(); err != nil {
- ok = false
- log.Printf("Close: %v", err)
- }
- }
- if pathnameErr == nil {
- if err := pathnameConn.Close(); err != nil {
- ok = false
- log.Printf("Close: %v", err)
- }
- }
-
- abstractWantErr := error(syscall.EPERM)
- pathnameWantErr := error(syscall.ENOENT)
- if want.SocketAbstract {
- abstractWantErr = nil
- }
- if want.SocketPathname {
- pathnameWantErr = nil
- }
-
- if !errors.Is(abstractErr, abstractWantErr) {
- ok = false
- log.Printf("abstractErr: %v, want %v", abstractErr, abstractWantErr)
- }
- if !errors.Is(pathnameErr, pathnameWantErr) {
- ok = false
- log.Printf("pathnameErr: %v, want %v", pathnameErr, pathnameWantErr)
- }
-
- if !ok {
- os.Exit(1)
- }
- }
-}
-
-func MustCheckFilter(pid int, want string) {
- err := CheckFilter(pid, want)
- if err == nil {
- return
- }
-
- var perr *ptraceError
- if !errors.As(err, &perr) {
- fatalf("%s", err)
- }
- switch perr.op {
- case "PTRACE_ATTACH":
- fatalf("cannot attach to process %d: %v", pid, err)
- case "PTRACE_SECCOMP_GET_FILTER":
- if perr.errno == syscall.ENOENT {
- fatalf("seccomp filter not installed for process %d", pid)
- }
- fatalf("cannot get filter: %v", err)
- default:
- fatalf("cannot check filter: %v", err)
- }
-
- *(*int)(nil) = 0 // not reached
-}
-
-func CheckFilter(pid int, want string) error {
- if err := ptraceAttach(pid); err != nil {
- return err
- }
- defer func() {
- if err := ptraceDetach(pid); err != nil {
- printf("cannot detach from process %d: %v", pid, err)
- }
- }()
-
- h := sha512.New()
-
- if buf, err := getFilter[[8]byte](pid, 0); err != nil {
- return err
- } else {
- for _, b := range buf {
- h.Write(b[:])
- }
- }
-
- if got := hex.EncodeToString(h.Sum(nil)); got != want {
- printf("[FAIL] %s", got)
- return syscall.ENOTRECOVERABLE
- } else {
- printf("[ OK ] %s", got)
- return nil
- }
-}
-
-func mustDecode(wantFilePath string, v any) {
- if f, err := os.Open(wantFilePath); err != nil {
- fatalf("cannot open %q: %v", wantFilePath, err)
- } else if err = json.NewDecoder(f).Decode(v); err != nil {
- fatalf("cannot decode %q: %v", wantFilePath, err)
- } else if err = f.Close(); err != nil {
- fatalf("cannot close %q: %v", wantFilePath, err)
- }
-}
-
-func mustParseMountinfo(name string) *Mountinfo {
- m := NewMountinfo(name)
- if err := m.Parse(); err != nil {
- fatalf("%v", err)
- panic("unreachable")
- }
- return m
-}