Skip to content

Commit feddee9

Browse files
nixprimegvisor-bot
authored andcommitted
kernel: add ThreadGroup.SigsegvLock/Unlock
PiperOrigin-RevId: 826271472
1 parent 47b0f21 commit feddee9

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

pkg/sentry/kernel/task_run.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,17 @@ func (app *runApp) execute(t *Task) taskRunState {
308308
}
309309
}
310310

311+
if sig == linux.SIGSEGV && t.tg.sigsegvLockCount.Load() != 0 {
312+
t.tg.signalHandlers.mu.Lock()
313+
if t.tg.sigsegvLockCount.Load() != 0 {
314+
t.Infof("Pausing execution due to SigsegvLock")
315+
t.beginInternalStopLocked((*sigsegvLockStop)(nil))
316+
t.tg.signalHandlers.mu.Unlock()
317+
return (*runApp)(nil)
318+
}
319+
t.tg.signalHandlers.mu.Unlock()
320+
}
321+
311322
// Faults are common, log only at debug level.
312323
t.Debugf("Unhandled user fault: addr=%x ip=%x access=%v sig=%v err=%v", addr, t.Arch().IP(), at, sig, err)
313324
t.DebugDumpState()

pkg/sentry/kernel/thread_group.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)