summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/kernel/pending_signals.go
blob: d8701f47ae5767196c26cb9fac76c1fd979d6180 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kernel

import (
	"gvisor.googlesource.com/gvisor/pkg/abi/linux"
	"gvisor.googlesource.com/gvisor/pkg/bits"
	"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
)

const (
	// stdSignalCap is the maximum number of instances of a given standard
	// signal that may be pending. ("[If] multiple instances of a standard
	// signal are delivered while that signal is currently blocked, then only
	// one instance is queued.") - signal(7)
	stdSignalCap = 1

	// rtSignalCap is the maximum number of instances of a given realtime
	// signal that may be pending.
	//
	// TODO: In Linux, the minimum signal queue size is
	// RLIMIT_SIGPENDING, which is by default max_threads/2.
	rtSignalCap = 32
)

// pendingSignals holds a collection of pending signals. The zero value of
// pendingSignals is a valid empty collection. pendingSignals is thread-unsafe;
// users must provide synchronization.
type pendingSignals struct {
	// signals contains all pending signals.
	//
	// Note that signals is zero-indexed, but signal 1 is the first valid
	// signal, so signals[0] contains signals with signo 1 etc. This offset is
	// usually handled by using Signal.index().
	signals [linux.SignalMaximum]pendingSignalQueue

	// Bit i of pendingSet is set iff there is at least one signal with signo
	// i+1 pending.
	pendingSet linux.SignalSet
}

// pendingSignalQueue holds a pendingSignalList for a single signal number.
type pendingSignalQueue struct {
	pendingSignalList
	length int
}

type pendingSignal struct {
	// pendingSignalEntry links into a pendingSignalList.
	pendingSignalEntry
	*arch.SignalInfo
}

// enqueue enqueues the given signal. enqueue returns true on success and false
// on failure (if the given signal's queue is full).
//
// Preconditions: info represents a valid signal.
func (p *pendingSignals) enqueue(info *arch.SignalInfo) bool {
	sig := linux.Signal(info.Signo)
	q := &p.signals[sig.Index()]
	if sig.IsStandard() {
		if q.length >= stdSignalCap {
			return false
		}
	} else if q.length >= rtSignalCap {
		return false
	}
	q.pendingSignalList.PushBack(&pendingSignal{SignalInfo: info})
	q.length++
	p.pendingSet |= linux.SignalSetOf(sig)
	return true
}

// dequeue dequeues and returns any pending signal not masked by mask. If no
// unmasked signals are pending, dequeue returns nil.
func (p *pendingSignals) dequeue(mask linux.SignalSet) *arch.SignalInfo {
	// "Real-time signals are delivered in a guaranteed order. Multiple
	// real-time signals of the same type are delivered in the order they were
	// sent. If different real-time signals are sent to a process, they are
	// delivered starting with the lowest-numbered signal. (I.e., low-numbered
	// signals have highest priority.) By contrast, if multiple standard
	// signals are pending for a process, the order in which they are delivered
	// is unspecified. If both standard and real-time signals are pending for a
	// process, POSIX leaves it unspecified which is delivered first. Linux,
	// like many other implementations, gives priority to standard signals in
	// this case." - signal(7)
	lowestPendingUnblockedBit := bits.TrailingZeros64(uint64(p.pendingSet &^ mask))
	if lowestPendingUnblockedBit >= linux.SignalMaximum {
		return nil
	}
	return p.dequeueSpecific(linux.Signal(lowestPendingUnblockedBit + 1))
}

func (p *pendingSignals) dequeueSpecific(sig linux.Signal) *arch.SignalInfo {
	q := &p.signals[sig.Index()]
	ps := q.pendingSignalList.Front()
	if ps == nil {
		return nil
	}
	q.pendingSignalList.Remove(ps)
	q.length--
	if q.length == 0 {
		p.pendingSet &^= linux.SignalSetOf(sig)
	}
	return ps.SignalInfo
}

// discardSpecific causes all pending signals with number sig to be discarded.
func (p *pendingSignals) discardSpecific(sig linux.Signal) {
	q := &p.signals[sig.Index()]
	q.pendingSignalList.Reset()
	q.length = 0
	p.pendingSet &^= linux.SignalSetOf(sig)
}