@@ -286,6 +286,11 @@ type ThreadGroup struct {
286286 execveCredsMutexMu sync.Mutex `state:"nosave"`
287287 execveCredsMutexLocked bool
288288 execveCredsMutexWaiters map [* Task ]struct {}
289+
290+ // sigsegvLockCount is the number of times SigsegvLock() has been called
291+ // without a matching call to SigsegvUnlock(). Decrementing
292+ // sigsegvLockCount to 0 requires that the signal mutex is locked.
293+ sigsegvLockCount atomicbitops.Int32
289294}
290295
291296// NewThreadGroup returns a new, empty thread group in PID namespace pidns. The
@@ -674,3 +679,38 @@ func (tg *ThreadGroup) Execed() bool {
674679 defer ts .mu .RUnlock ()
675680 return tg .execed
676681}
682+
683+ // SigsegvLock causes page faults that would result in SIGSEGV being sent to
684+ // tasks in tg to block without sending SIGSEGV. When SigsegvUnlock has been
685+ // called an equal number of times as SigsegvLock, application execution is
686+ // restarted, allowing the page fault to send SIGSEGV if it recurs.
687+ func (tg * ThreadGroup ) SigsegvLock () {
688+ tg .sigsegvLockCount .Add (1 )
689+ }
690+
691+ // SigsegvUnlock ends the effect of one preceding call to SigsegvLock.
692+ func (tg * ThreadGroup ) SigsegvUnlock () {
693+ sh := tg .signalLock ()
694+ defer sh .mu .Unlock ()
695+ if count := tg .sigsegvLockCount .Add (- 1 ); count == 0 {
696+ for t := tg .tasks .Front (); t != nil ; t = t .Next () {
697+ if _ , ok := t .stop .(* sigsegvLockStop ); ok {
698+ t .Infof ("Resuming execution due to SigsegvUnlock" )
699+ t .endInternalStopLocked ()
700+ }
701+ }
702+ } else if count < 0 {
703+ panic ("unlock of unlocked ThreadGroup.SigsegvLock" )
704+ }
705+ }
706+
707+ // sigsegvLockStop is a TaskStop entered when a task would raise SIGSEGV due to
708+ // an unhandled page fault, but ThreadGroup.SigsegvLock() is in effect.
709+ //
710+ // +stateify savable
711+ type sigsegvLockStop struct {}
712+
713+ // Killable implements TaskStop.Killable.
714+ func (* sigsegvLockStop ) Killable () bool {
715+ return true
716+ }
0 commit comments