//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())
	}
}
