xref: /aosp_15_r20/external/libcap/cap/syscalls.go (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1*2810ac1bSKiyoung Kimpackage cap
2*2810ac1bSKiyoung Kim
3*2810ac1bSKiyoung Kimimport (
4*2810ac1bSKiyoung Kim	"runtime"
5*2810ac1bSKiyoung Kim	"sync"
6*2810ac1bSKiyoung Kim	"syscall"
7*2810ac1bSKiyoung Kim
8*2810ac1bSKiyoung Kim	"kernel.org/pub/linux/libs/security/libcap/psx"
9*2810ac1bSKiyoung Kim)
10*2810ac1bSKiyoung Kim
11*2810ac1bSKiyoung Kim// multisc provides syscalls overridable for testing purposes that
12*2810ac1bSKiyoung Kim// support a single kernel security state for all OS threads.
13*2810ac1bSKiyoung Kim// We use this version when we are cgo compiling because
14*2810ac1bSKiyoung Kim// we need to manage the native C pthreads too.
15*2810ac1bSKiyoung Kimvar multisc = &syscaller{
16*2810ac1bSKiyoung Kim	w3: psx.Syscall3,
17*2810ac1bSKiyoung Kim	w6: psx.Syscall6,
18*2810ac1bSKiyoung Kim	r3: syscall.RawSyscall,
19*2810ac1bSKiyoung Kim	r6: syscall.RawSyscall6,
20*2810ac1bSKiyoung Kim}
21*2810ac1bSKiyoung Kim
22*2810ac1bSKiyoung Kim// singlesc provides a single threaded implementation. Users should
23*2810ac1bSKiyoung Kim// take care to ensure the thread is locked and marked nogc.
24*2810ac1bSKiyoung Kimvar singlesc = &syscaller{
25*2810ac1bSKiyoung Kim	w3: syscall.RawSyscall,
26*2810ac1bSKiyoung Kim	w6: syscall.RawSyscall6,
27*2810ac1bSKiyoung Kim	r3: syscall.RawSyscall,
28*2810ac1bSKiyoung Kim	r6: syscall.RawSyscall6,
29*2810ac1bSKiyoung Kim}
30*2810ac1bSKiyoung Kim
31*2810ac1bSKiyoung Kim// launchState is used to track which variant of the write syscalls
32*2810ac1bSKiyoung Kim// should execute.
33*2810ac1bSKiyoung Kimtype launchState int
34*2810ac1bSKiyoung Kim
35*2810ac1bSKiyoung Kim// these states are used to understand when a launch is in progress.
36*2810ac1bSKiyoung Kimconst (
37*2810ac1bSKiyoung Kim	launchIdle launchState = iota
38*2810ac1bSKiyoung Kim	launchActive
39*2810ac1bSKiyoung Kim	launchBlocked
40*2810ac1bSKiyoung Kim)
41*2810ac1bSKiyoung Kim
42*2810ac1bSKiyoung Kim// scwMu is used to fully serialize the write system calls. Note, this
43*2810ac1bSKiyoung Kim// would generally not be necessary, but in the case of Launch we get
44*2810ac1bSKiyoung Kim// into a situation where the launching thread is temporarily allowed
45*2810ac1bSKiyoung Kim// to deviate from the kernel state of the rest of the runtime and
46*2810ac1bSKiyoung Kim// allowing other threads to perform w* syscalls will potentially
47*2810ac1bSKiyoung Kim// interfere with the launching process. In pure Go binaries, this
48*2810ac1bSKiyoung Kim// will lead inevitably to a panic when the AllThreadsSyscall
49*2810ac1bSKiyoung Kim// discovers inconsistent thread state.
50*2810ac1bSKiyoung Kim//
51*2810ac1bSKiyoung Kim// scwMu protects scwTIDs and scwState
52*2810ac1bSKiyoung Kimvar scwMu sync.Mutex
53*2810ac1bSKiyoung Kim
54*2810ac1bSKiyoung Kim// scwTIDs holds the thread IDs of the threads that are executing a
55*2810ac1bSKiyoung Kim// launch it is empty when no launches are occurring.
56*2810ac1bSKiyoung Kimvar scwTIDs = make(map[int]bool)
57*2810ac1bSKiyoung Kim
58*2810ac1bSKiyoung Kim// scwState captures whether a launch is in progress or not.
59*2810ac1bSKiyoung Kimvar scwState = launchIdle
60*2810ac1bSKiyoung Kim
61*2810ac1bSKiyoung Kim// scwCond is used to announce when scwState changes to other
62*2810ac1bSKiyoung Kim// goroutines waiting for it to change.
63*2810ac1bSKiyoung Kimvar scwCond = sync.NewCond(&scwMu)
64*2810ac1bSKiyoung Kim
65*2810ac1bSKiyoung Kim// scwSetState blocks until a launch state change between states from
66*2810ac1bSKiyoung Kim// and to occurs. We use this for more context specific syscaller
67*2810ac1bSKiyoung Kim// use. In the case that the caller is requesting a launchActive ->
68*2810ac1bSKiyoung Kim// launchIdle transition they are declaring that tid is no longer
69*2810ac1bSKiyoung Kim// launching. If another thread is also launching the call will
70*2810ac1bSKiyoung Kim// complete, but the launchState will remain launchActive.
71*2810ac1bSKiyoung Kimfunc scwSetState(from, to launchState, tid int) {
72*2810ac1bSKiyoung Kim	scwMu.Lock()
73*2810ac1bSKiyoung Kim	for scwState != from {
74*2810ac1bSKiyoung Kim		if scwState == launchActive && from == launchIdle && to == launchActive {
75*2810ac1bSKiyoung Kim			break // This "transition" is also allowed.
76*2810ac1bSKiyoung Kim		}
77*2810ac1bSKiyoung Kim		scwCond.Wait()
78*2810ac1bSKiyoung Kim	}
79*2810ac1bSKiyoung Kim	if from == launchIdle && to == launchActive {
80*2810ac1bSKiyoung Kim		scwTIDs[tid] = true
81*2810ac1bSKiyoung Kim	} else if from == launchActive && to == launchIdle {
82*2810ac1bSKiyoung Kim		delete(scwTIDs, tid)
83*2810ac1bSKiyoung Kim		if len(scwTIDs) != 0 {
84*2810ac1bSKiyoung Kim			to = from // not actually idle
85*2810ac1bSKiyoung Kim		}
86*2810ac1bSKiyoung Kim	}
87*2810ac1bSKiyoung Kim	scwState = to
88*2810ac1bSKiyoung Kim	scwCond.Broadcast()
89*2810ac1bSKiyoung Kim	scwMu.Unlock()
90*2810ac1bSKiyoung Kim}
91*2810ac1bSKiyoung Kim
92*2810ac1bSKiyoung Kim// scwStateSC blocks until the current syscaller is available for
93*2810ac1bSKiyoung Kim// writes, and then marks launchBlocked. Use scwSetState to perform
94*2810ac1bSKiyoung Kim// the reverse transition (blocked->returned state value).
95*2810ac1bSKiyoung Kimfunc scwStateSC() (launchState, *syscaller) {
96*2810ac1bSKiyoung Kim	sc := multisc
97*2810ac1bSKiyoung Kim	scwMu.Lock()
98*2810ac1bSKiyoung Kim	for {
99*2810ac1bSKiyoung Kim		if scwState == launchIdle {
100*2810ac1bSKiyoung Kim			break
101*2810ac1bSKiyoung Kim		}
102*2810ac1bSKiyoung Kim		runtime.LockOSThread()
103*2810ac1bSKiyoung Kim		if scwState == launchActive && scwTIDs[syscall.Gettid()] {
104*2810ac1bSKiyoung Kim			sc = singlesc
105*2810ac1bSKiyoung Kim			// note, we don't runtime.UnlockOSThread()
106*2810ac1bSKiyoung Kim			// here because we have no reason to ever
107*2810ac1bSKiyoung Kim			// allow this thread to return to normal use -
108*2810ac1bSKiyoung Kim			// we need it dead before we can return to the
109*2810ac1bSKiyoung Kim			// launchIdle state.
110*2810ac1bSKiyoung Kim			break
111*2810ac1bSKiyoung Kim		}
112*2810ac1bSKiyoung Kim		runtime.UnlockOSThread()
113*2810ac1bSKiyoung Kim		scwCond.Wait()
114*2810ac1bSKiyoung Kim	}
115*2810ac1bSKiyoung Kim	old := scwState
116*2810ac1bSKiyoung Kim	scwState = launchBlocked
117*2810ac1bSKiyoung Kim	scwCond.Broadcast()
118*2810ac1bSKiyoung Kim	scwMu.Unlock()
119*2810ac1bSKiyoung Kim
120*2810ac1bSKiyoung Kim	return old, sc
121*2810ac1bSKiyoung Kim}
122