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