Skip to content

Commit 501a663

Browse files
nixprimegvisor-bot
authored andcommitted
sentry/kernel: allow pending signal sets to be read without locking
PiperOrigin-RevId: 776801765
1 parent a2ad577 commit 501a663

File tree

3 files changed

+20
-16
lines changed

3 files changed

+20
-16
lines changed

pkg/sentry/kernel/pending_signals.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package kernel
1616

1717
import (
1818
"gvisor.dev/gvisor/pkg/abi/linux"
19+
"gvisor.dev/gvisor/pkg/atomicbitops"
1920
"gvisor.dev/gvisor/pkg/bits"
2021
)
2122

@@ -35,8 +36,7 @@ const (
3536
)
3637

3738
// pendingSignals holds a collection of pending signals. The zero value of
38-
// pendingSignals is a valid empty collection. pendingSignals is thread-unsafe;
39-
// users must provide synchronization.
39+
// pendingSignals is a valid empty collection.
4040
//
4141
// +stateify savable
4242
type pendingSignals struct {
@@ -45,11 +45,19 @@ type pendingSignals struct {
4545
// Note that signals is zero-indexed, but signal 1 is the first valid
4646
// signal, so signals[0] contains signals with signo 1 etc. This offset is
4747
// usually handled by using Signal.index().
48+
//
49+
// signals is protected by the signal mutex for the containing Task or
50+
// ThreadGroup.
4851
signals [linux.SignalMaximum]pendingSignalQueue `state:".([]savedPendingSignal)"`
4952

5053
// Bit i of pendingSet is set iff there is at least one signal with signo
5154
// i+1 pending.
52-
pendingSet linux.SignalSet `state:"manual"`
55+
//
56+
// pendingSet is accessed using atomic memory operations, and is protected
57+
// by the signal mutex (such that reading pendingSet is safe if either the
58+
// signal mutex is locked or if atomic memory operations are used, while
59+
// writing pendingSet requires both).
60+
pendingSet atomicbitops.Uint64 `state:"manual"`
5361
}
5462

5563
// pendingSignalQueue holds a pendingSignalList for a single signal number.
@@ -86,7 +94,7 @@ func (p *pendingSignals) enqueue(info *linux.SignalInfo, timer *IntervalTimer) b
8694
}
8795
q.pendingSignalList.PushBack(&pendingSignal{SignalInfo: info, timer: timer})
8896
q.length++
89-
p.pendingSet |= linux.SignalSetOf(sig)
97+
p.pendingSet.Store(p.pendingSet.RacyLoad() | uint64(linux.SignalSetOf(sig)))
9098
return true
9199
}
92100

@@ -103,7 +111,7 @@ func (p *pendingSignals) dequeue(mask linux.SignalSet) *linux.SignalInfo {
103111
// process, POSIX leaves it unspecified which is delivered first. Linux,
104112
// like many other implementations, gives priority to standard signals in
105113
// this case." - signal(7)
106-
lowestPendingUnblockedBit := bits.TrailingZeros64(uint64(p.pendingSet &^ mask))
114+
lowestPendingUnblockedBit := bits.TrailingZeros64(p.pendingSet.RacyLoad() &^ uint64(mask))
107115
if lowestPendingUnblockedBit >= linux.SignalMaximum {
108116
return nil
109117
}
@@ -119,7 +127,7 @@ func (p *pendingSignals) dequeueSpecific(sig linux.Signal) *linux.SignalInfo {
119127
q.pendingSignalList.Remove(ps)
120128
q.length--
121129
if q.length == 0 {
122-
p.pendingSet &^= linux.SignalSetOf(sig)
130+
p.pendingSet.Store(p.pendingSet.RacyLoad() &^ uint64(linux.SignalSetOf(sig)))
123131
}
124132
if ps.timer != nil {
125133
ps.timer.updateDequeuedSignalLocked(ps.SignalInfo)
@@ -137,5 +145,5 @@ func (p *pendingSignals) discardSpecific(sig linux.Signal) {
137145
}
138146
q.pendingSignalList.Reset()
139147
q.length = 0
140-
p.pendingSet &^= linux.SignalSetOf(sig)
148+
p.pendingSet.Store(p.pendingSet.RacyLoad() &^ uint64(linux.SignalSetOf(sig)))
141149
}

pkg/sentry/kernel/task_exit.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,12 @@ func (t *Task) killLocked() {
105105
//
106106
// Preconditions: The caller must be running on the task goroutine.
107107
func (t *Task) killed() bool {
108-
t.tg.signalHandlers.mu.Lock()
109-
defer t.tg.signalHandlers.mu.Unlock()
110-
return t.killedLocked()
108+
return linux.SignalSet(t.pendingSignals.pendingSet.Load())&linux.SignalSetOf(linux.SIGKILL) != 0
111109
}
112110

113111
// Preconditions: The signal mutex must be locked.
114112
func (t *Task) killedLocked() bool {
115-
return t.pendingSignals.pendingSet&linux.SignalSetOf(linux.SIGKILL) != 0
113+
return linux.SignalSet(t.pendingSignals.pendingSet.RacyLoad())&linux.SignalSetOf(linux.SIGKILL) != 0
116114
}
117115

118116
// PrepareExit indicates an exit with the given status.

pkg/sentry/kernel/task_signals.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,7 @@ func (tg *ThreadGroup) discardSpecificLocked(sig linux.Signal) {
146146

147147
// PendingSignals returns the set of pending signals.
148148
func (t *Task) PendingSignals() linux.SignalSet {
149-
sh := t.tg.signalLock()
150-
defer sh.mu.Unlock()
151-
return t.pendingSignals.pendingSet | t.tg.pendingSignals.pendingSet
149+
return linux.SignalSet(t.pendingSignals.pendingSet.Load() | t.tg.pendingSignals.pendingSet.Load())
152150
}
153151

154152
// deliverSignal delivers the given signal and returns the following run state.
@@ -612,7 +610,7 @@ func (t *Task) setSignalMaskLocked(mask linux.SignalSet) {
612610
// signal, but will no longer do so as a result of its new signal mask, so
613611
// we have to pick a replacement.
614612
blocked := mask &^ oldMask
615-
blockedGroupPending := blocked & t.tg.pendingSignals.pendingSet
613+
blockedGroupPending := blocked & linux.SignalSet(t.tg.pendingSignals.pendingSet.RacyLoad())
616614
if blockedGroupPending != 0 && t.interrupted() {
617615
linux.ForEachSignal(blockedGroupPending, func(sig linux.Signal) {
618616
if nt := t.tg.findSignalReceiverLocked(sig); nt != nil {
@@ -626,7 +624,7 @@ func (t *Task) setSignalMaskLocked(mask linux.SignalSet) {
626624
// the old mask, and at least one such signal is pending, we may now need
627625
// to handle that signal.
628626
unblocked := oldMask &^ mask
629-
unblockedPending := unblocked & (t.pendingSignals.pendingSet | t.tg.pendingSignals.pendingSet)
627+
unblockedPending := unblocked & linux.SignalSet(t.pendingSignals.pendingSet.RacyLoad()|t.tg.pendingSignals.pendingSet.RacyLoad())
630628
if unblockedPending != 0 {
631629
t.interruptSelf()
632630
}

0 commit comments

Comments
 (0)