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