diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-10-06 22:23:20 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-10-06 23:09:19 +0900 |
| commit | a9e2749f6654d0aa07b274a45c9177d10323f80a (patch) | |
| tree | 291f5b23a67af036cbe5d374b3d3fb0240fc438c /test/sharefs | |
| parent | 19f36491f2e2a5029ac396c10408d653cad6c81b (diff) | |
internal/testsuite: move from test
This structure is a lot less clumsy than the old nix-centric layout.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'test/sharefs')
| -rw-r--r-- | test/sharefs/main.go | 119 | ||||
| -rw-r--r-- | test/sharefs/raceattr.go | 122 |
2 files changed, 0 insertions, 241 deletions
diff --git a/test/sharefs/main.go b/test/sharefs/main.go deleted file mode 100644 index 80e6ab09..00000000 --- a/test/sharefs/main.go +++ /dev/null @@ -1,119 +0,0 @@ -//go:build testsuite - -// The sharefs test program checks cli behaviour and exercises the filesystem -// implemented by cmd/sharefs using fs_mark. -package main - -import ( - "errors" - "log" - "os" - "os/exec" - "strings" - "syscall" - - "hakurei.app/test/internal/testsuite" -) - -// checkBadOpts invokes cmd/sharefs with the specified options and compares -// the resulting error message. -func checkBadOpts(cred *syscall.Credential, opts, want string) { - var buf strings.Builder - buf.Grow(len(want)) - - cmd := exec.Command( - "sharefs", - "-f", - "-o", "source=/etc,"+opts, - "/mnt", - ) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGKILL, - Credential: cred, - } - cmd.Stderr = &buf - err := cmd.Run() - if err == nil { - log.Fatalf("opts=%q, unexpected success", opts) - } - if e, ok := errors.AsType[*exec.ExitError](err); !ok { - log.Fatal(err) - } else if !e.Exited() { - log.Fatal(e) - } - - if got := buf.String(); got != want { - log.Fatalf("opts=%q\n\t got:%q\n\twant:%q", opts, got, want) - } -} - -func main() { - go testsuite.ReceiveSignals() - - cred := syscall.Credential{Uid: 1000, Gid: 100} - if err := os.Mkdir("result", 0755); err != nil { - log.Fatal(err) - } - - done := make(chan struct{}) - go func() { - defer close(done) - - testsuite.MustRun( - nil, nil, - "fs_mark", - "-v", - "-d", "/sdcard/fs_mark", - "-l", "result/fs_mark.log", - ) - }() - - log.Println("checking malformed setuid/setgid representation") - checkBadOpts(&cred, "setuid=ff", "sharefs: invalid value for option setuid\n") - checkBadOpts(&cred, "setgid=ff", "sharefs: invalid value for option setgid\n") - - log.Println("checking bounds check for setuid/setgid") - checkBadOpts(&cred, "setuid=0", "sharefs: invalid value for option setuid\n") - checkBadOpts(&cred, "setgid=0", "sharefs: invalid value for option setgid\n") - checkBadOpts(&cred, "setuid=-1", "sharefs: invalid value for option setuid\n") - checkBadOpts(&cred, "setgid=-1", "sharefs: invalid value for option setgid\n") - - log.Println("checking non-root setuid/setgid") - checkBadOpts(&cred, "setuid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n") - checkBadOpts(&cred, "setgid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n") - checkBadOpts(&cred, "setuid=1023,setgid=1023", "sharefs: setuid and setgid has no effect when not starting as root\n") - checkBadOpts(&cred, "mkdir", "sharefs: mkdir has no effect when not starting as root\n") - - log.Println("checking root without setuid/setgid") - checkBadOpts(nil, "allow_other", "sharefs: setuid and setgid must not be 0\n") - checkBadOpts(nil, "setuid=1023", "sharefs: setuid and setgid must not be 0\n") - checkBadOpts(nil, "setgid=1023", "sharefs: setuid and setgid must not be 0\n") - - log.Println("verifying mount point") - if err := os.Remove("/mnt"); err != nil { - log.Fatal(err) - } - - log.Println("checking unprivileged mount/unmount") - testsuite.MustRun(&cred, nil, "mkdir", "/tmp/sdcard", "/tmp/persistent") - testsuite.MustRun(&cred, nil, "sharefs", "-o", "source=/tmp/persistent", "/tmp/sdcard") - testsuite.MustRun(&cred, nil, "touch", "/tmp/sdcard/check") - testsuite.MustRun(&cred, nil, "umount", "/tmp/sdcard") - testsuite.MustRun(&cred, nil, "rm", "/tmp/persistent/check") - testsuite.MustRun(&cred, nil, "rmdir", "/tmp/sdcard", "/tmp/persistent") - - log.Println("waiting for fs_mark to complete") - <-done - - const backingDir = "/var/lib/sdcard" - sharefsCred := syscall.Credential{Uid: 1023, Gid: 1023} - log.Println("checking permissions") - testsuite.MustRun(&sharefsCred, nil, "touch", backingDir+"/fs_mark/.check") - testsuite.MustRun(&sharefsCred, nil, "rm", backingDir+"/fs_mark/.check") - testsuite.MustRun(&cred, nil, "rm", "-rf", "/sdcard/fs_mark") - if _, err := os.ReadDir(backingDir + "/fs_mark"); err == nil { - log.Fatal("fs_mark directory was not removed") - } else if !errors.Is(err, os.ErrNotExist) { - log.Fatal(err) - } -} diff --git a/test/sharefs/raceattr.go b/test/sharefs/raceattr.go deleted file mode 100644 index 412cb2b3..00000000 --- a/test/sharefs/raceattr.go +++ /dev/null @@ -1,122 +0,0 @@ -//go:build raceattr - -// The raceattr program reproduces vfs inode file attribute race. -// -// Even though libfuse high-level API presents the address of a struct stat -// alongside struct fuse_context, file attributes are actually inherent to the -// inode, instead of the specific call from userspace. The kernel implementation -// in fs/fuse/xattr.c appears to make stale data in the inode (set by a previous -// call) impossible or very unlikely to reach userspace via the stat family of -// syscalls. However, when using default_permissions to have the VFS check -// permissions, this race still happens, despite the resulting struct stat being -// correct when overriding the check via capabilities otherwise. -// -// This program reproduces the failure, but because of its continuous nature, it -// is provided independent of the vm integration test suite. -package main - -import ( - "context" - "flag" - "log" - "os" - "os/signal" - "runtime" - "sync" - "sync/atomic" - "syscall" -) - -func newStatAs( - ctx context.Context, cancel context.CancelFunc, - n *atomic.Uint64, ok *atomic.Bool, - uid uint32, pathname string, - continuous bool, -) func() { - return func() { - runtime.LockOSThread() - defer cancel() - - if _, _, errno := syscall.Syscall( - syscall.SYS_SETUID, uintptr(uid), - 0, 0, - ); errno != 0 { - cancel() - log.Printf("cannot set uid to %d: %s", uid, errno) - } - - var stat syscall.Stat_t - for { - if ctx.Err() != nil { - return - } - - if err := syscall.Lstat(pathname, &stat); err != nil { - // SHAREFS_PERM_DIR not world executable, or - // SHAREFS_PERM_REG not world readable - if !continuous { - cancel() - } - ok.Store(true) - log.Printf("uid %d: %v", uid, err) - } else if stat.Uid != uid { - // appears to be unreachable - if !continuous { - cancel() - } - ok.Store(true) - log.Printf("got uid %d instead of %d", stat.Uid, uid) - } - n.Add(1) - } - } -} - -func main() { - log.SetFlags(0) - log.SetPrefix("raceattr: ") - - p := flag.String("target", "/sdcard/raceattr", "pathname of test file") - u0 := flag.Int("uid0", 1<<10-1, "first uid") - u1 := flag.Int("uid1", 1<<10-2, "second uid") - count := flag.Int("count", 1, "threads per uid") - continuous := flag.Bool("continuous", false, "keep running even after reproduce") - flag.Parse() - - if os.Geteuid() != 0 { - log.Fatal("this program must run as root") - } - - ctx, cancel := signal.NotifyContext( - context.Background(), - syscall.SIGINT, - syscall.SIGTERM, - syscall.SIGHUP, - ) - - if err := os.WriteFile(*p, nil, 0); err != nil { - log.Fatal(err) - } - - var ( - wg sync.WaitGroup - - n atomic.Uint64 - ok atomic.Bool - ) - - if *count < 1 { - *count = 1 - } - for range *count { - wg.Go(newStatAs(ctx, cancel, &n, &ok, uint32(*u0), *p, *continuous)) - if *u1 >= 0 { - wg.Go(newStatAs(ctx, cancel, &n, &ok, uint32(*u1), *p, *continuous)) - } - } - - wg.Wait() - if !*continuous && ok.Load() { - log.Printf("reproduced after %d calls", n.Load()) - } -} |
