summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/kernel')
-rw-r--r--pkg/sentry/kernel/task.go9
-rw-r--r--pkg/sentry/kernel/task_signals.go68
2 files changed, 55 insertions, 22 deletions
diff --git a/pkg/sentry/kernel/task.go b/pkg/sentry/kernel/task.go
index ae4fd7817..2f6f825ac 100644
--- a/pkg/sentry/kernel/task.go
+++ b/pkg/sentry/kernel/task.go
@@ -108,9 +108,12 @@ type Task struct {
// goroutine.
signalMask linux.SignalSet
- // FIXME: An equivalent to task_struct::real_blocked is needed
- // to prevent signals that are ignored, but transiently unblocked by
- // sigtimedwait(2), from being dropped in Task.sendSignalTimerLocked.
+ // If the task goroutine is currently executing Task.sigtimedwait,
+ // realSignalMask is the previous value of signalMask, which has temporarily
+ // been replaced by Task.sigtimedwait. Otherwise, realSignalMask is 0.
+ //
+ // realSignalMask is exclusive to the task goroutine.
+ realSignalMask linux.SignalSet
// If haveSavedSignalMask is true, savedSignalMask is the signal mask that
// should be applied after the task has either delivered one signal to a
diff --git a/pkg/sentry/kernel/task_signals.go b/pkg/sentry/kernel/task_signals.go
index 58a1bc0bd..afb010f60 100644
--- a/pkg/sentry/kernel/task_signals.go
+++ b/pkg/sentry/kernel/task_signals.go
@@ -19,6 +19,7 @@ package kernel
import (
"fmt"
"sync/atomic"
+ "time"
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
@@ -119,25 +120,11 @@ var UnblockableSignals = linux.MakeSignalSet(linux.SIGKILL, linux.SIGSTOP)
// StopSignals is the set of signals whose default action is SignalActionStop.
var StopSignals = linux.MakeSignalSet(linux.SIGSTOP, linux.SIGTSTP, linux.SIGTTIN, linux.SIGTTOU)
-// dequeueSignalLocked returns a pending unmasked signal. If there are no
-// pending unmasked signals, dequeueSignalLocked returns nil.
+// dequeueSignalLocked returns a pending signal that is *not* included in mask.
+// If there are no pending unmasked signals, dequeueSignalLocked returns nil.
//
// Preconditions: t.tg.signalHandlers.mu must be locked.
-func (t *Task) dequeueSignalLocked() *arch.SignalInfo {
- if info := t.pendingSignals.dequeue(t.signalMask); info != nil {
- return info
- }
- return t.tg.pendingSignals.dequeue(t.signalMask)
-}
-
-// TakeSignal returns a pending signal not blocked by mask. Signal handlers are
-// not affected. If there are no pending signals not blocked by mask,
-// TakeSignal returns a nil SignalInfo.
-func (t *Task) TakeSignal(mask linux.SignalSet) *arch.SignalInfo {
- t.tg.pidns.owner.mu.RLock()
- defer t.tg.pidns.owner.mu.RUnlock()
- t.tg.signalHandlers.mu.Lock()
- defer t.tg.signalHandlers.mu.Unlock()
+func (t *Task) dequeueSignalLocked(mask linux.SignalSet) *arch.SignalInfo {
if info := t.pendingSignals.dequeue(mask); info != nil {
return info
}
@@ -294,6 +281,49 @@ func (t *Task) SignalReturn(rt bool) (*SyscallControl, error) {
return ctrlResume, nil
}
+// Sigtimedwait implements the semantics of sigtimedwait(2).
+//
+// Preconditions: The caller must be running on the task goroutine. t.exitState
+// < TaskExitZombie.
+func (t *Task) Sigtimedwait(set linux.SignalSet, timeout time.Duration) (*arch.SignalInfo, error) {
+ // set is the set of signals we're interested in; invert it to get the set
+ // of signals to block.
+ mask := ^set &^ UnblockableSignals
+
+ t.tg.signalHandlers.mu.Lock()
+ defer t.tg.signalHandlers.mu.Unlock()
+ if info := t.dequeueSignalLocked(mask); info != nil {
+ return info, nil
+ }
+
+ if timeout == 0 {
+ return nil, syserror.EAGAIN
+ }
+
+ // Unblock signals we're waiting for. Remember the original signal mask so
+ // that Task.sendSignalTimerLocked doesn't discard ignored signals that
+ // we're temporarily unblocking.
+ t.realSignalMask = t.signalMask
+ t.setSignalMaskLocked(t.signalMask & mask)
+
+ // Wait for a timeout or new signal.
+ t.tg.signalHandlers.mu.Unlock()
+ _, err := t.BlockWithTimeout(nil, true, timeout)
+ t.tg.signalHandlers.mu.Lock()
+
+ // Restore the original signal mask.
+ t.setSignalMaskLocked(t.realSignalMask)
+ t.realSignalMask = 0
+
+ if info := t.dequeueSignalLocked(mask); info != nil {
+ return info, nil
+ }
+ if err == syserror.ETIMEDOUT {
+ return nil, syserror.EAGAIN
+ }
+ return nil, err
+}
+
// SendSignal sends the given signal to t.
//
// The following errors may be returned:
@@ -431,7 +461,7 @@ func (t *Task) sendSignalTimerLocked(info *arch.SignalInfo, group bool, timer *I
// Linux's kernel/signal.c:__send_signal() => prepare_signal() =>
// sig_ignored().
ignored := computeAction(sig, t.tg.signalHandlers.actions[sig]) == SignalActionIgnore
- if linux.SignalSetOf(sig)&t.signalMask == 0 && ignored && !t.hasTracer() {
+ if sigset := linux.SignalSetOf(sig); sigset&t.signalMask == 0 && sigset&t.realSignalMask == 0 && ignored && !t.hasTracer() {
t.Debugf("Discarding ignored signal %d", sig)
if timer != nil {
timer.signalRejectedLocked()
@@ -1010,7 +1040,7 @@ func (*runInterrupt) execute(t *Task) taskRunState {
}
// Are there signals pending?
- if info := t.dequeueSignalLocked(); info != nil {
+ if info := t.dequeueSignalLocked(t.signalMask); info != nil {
if linux.SignalSetOf(linux.Signal(info.Signo))&StopSignals != 0 && t.tg.groupStopPhase == groupStopNone {
// Indicate that we've dequeued a stop signal before
// unlocking the signal mutex; initiateGroupStop will check