xref: /aosp_15_r20/external/libcap/psx/psx_test.go (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1package psx
2
3import (
4	"runtime"
5	"sync"
6	"syscall"
7	"testing"
8)
9
10func TestSyscall3(t *testing.T) {
11	want := syscall.Getpid()
12	if got, _, err := Syscall3(syscall.SYS_GETPID, 0, 0, 0); err != 0 {
13		t.Errorf("failed to get PID via libpsx: %v", err)
14	} else if int(got) != want {
15		t.Errorf("pid mismatch: got=%d want=%d", got, want)
16	}
17	if got, _, err := Syscall3(syscall.SYS_CAPGET, 0, 0, 0); err != 14 {
18		t.Errorf("malformed capget returned %d: %v (want 14: %v)", err, err, syscall.Errno(14))
19	} else if ^got != 0 {
20		t.Errorf("malformed capget did not return -1, got=%d", got)
21	}
22}
23
24func TestSyscall6(t *testing.T) {
25	want := syscall.Getpid()
26	if got, _, err := Syscall6(syscall.SYS_GETPID, 0, 0, 0, 0, 0, 0); err != 0 {
27		t.Errorf("failed to get PID via libpsx: %v", err)
28	} else if int(got) != want {
29		t.Errorf("pid mismatch: got=%d want=%d", got, want)
30	}
31	if got, _, err := Syscall6(syscall.SYS_CAPGET, 0, 0, 0, 0, 0, 0); err != 14 {
32		t.Errorf("malformed capget errno %d: %v (want 14: %v)", err, err, syscall.Errno(14))
33	} else if ^got != 0 {
34		t.Errorf("malformed capget did not return -1, got=%d", got)
35	}
36}
37
38// killAThread locks the goroutine to a thread and exits. This has the
39// effect of making the go runtime terminate the thread.
40func killAThread(c <-chan struct{}) {
41	runtime.LockOSThread()
42	<-c
43}
44
45// Test state is mirrored as expected.
46func TestShared(t *testing.T) {
47	const prGetKeepCaps = 7
48	const prSetKeepCaps = 8
49
50	var wg sync.WaitGroup
51
52	newTracker := func() chan<- uintptr {
53		ch := make(chan uintptr)
54		go func() {
55			runtime.LockOSThread()
56			defer wg.Done()
57			tid := syscall.Gettid()
58			for {
59				if _, ok := <-ch; !ok {
60					break
61				}
62				val, ok := <-ch
63				if !ok {
64					break
65				}
66				got, _, e := Syscall3(syscall.SYS_PRCTL, prGetKeepCaps, 0, 0)
67				if e != 0 {
68					t.Fatalf("[%d] psx:prctl(GET_KEEPCAPS) ?= %d failed: %v", tid, val, syscall.Errno(e))
69				}
70				if got != val {
71					t.Errorf("[%d] bad keepcaps value: got=%d, want=%d", tid, got, val)
72				}
73				if _, ok := <-ch; !ok {
74					break
75				}
76			}
77		}()
78		return ch
79	}
80
81	var tracked []chan<- uintptr
82	for i := 0; i <= 10; i++ {
83		val := uintptr(i & 1)
84		if _, _, e := Syscall3(syscall.SYS_PRCTL, prSetKeepCaps, val, 0); e != 0 {
85			t.Fatalf("[%d] psx:prctl(SET_KEEPCAPS, %d) failed: %v", i, i&1, syscall.Errno(e))
86		}
87		wg.Add(1)
88		tracked = append(tracked, newTracker())
89		for _, ch := range tracked {
90			ch <- 2   // start serialization.
91			ch <- val // definitely written after change.
92			ch <- 3   // end serialization.
93		}
94	}
95	for _, ch := range tracked {
96		close(ch)
97	}
98	wg.Wait()
99}
100
101// Test to confirm no regression against:
102//
103//	https://github.com/golang/go/issues/42494
104func TestThreadChurn(t *testing.T) {
105	const prSetKeepCaps = 8
106
107	for j := 0; j < 4; j++ {
108		kill := (j & 1) != 0
109		sysc := (j & 2) != 0
110		t.Logf("[%d] testing kill=%v, sysc=%v", j, kill, sysc)
111		for i := 50; i > 0; i-- {
112			if kill {
113				c := make(chan struct{})
114				go killAThread(c)
115				close(c)
116			}
117			if sysc {
118				if _, _, e := Syscall3(syscall.SYS_PRCTL, prSetKeepCaps, uintptr(i&1), 0); e != 0 {
119					t.Fatalf("[%d] psx:prctl(SET_KEEPCAPS, %d) failed: %v", i, i&1, syscall.Errno(e))
120				}
121			}
122		}
123		t.Logf("[%d] PASSED kill=%v, sysc=%v", j, kill, sysc)
124	}
125}
126