The Pedigree Project 0.1
queued-signal.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/Scheduler.h"
4#include "pedigree/kernel/process/TerminationDeferral.h"
5#include "pedigree/kernel/processor/Processor.h"
6#include "pedigree/kernel/syscallError.h"
7#include "pedigree/kernel/time/Time.h"
8#include "pedigree/kernel/utilities/List.h"
9
10#include <signal.h>
11
12#include "PosixProcess.h"
13#include "PosixSubsystem.h"
14#include "linux-wait-abi.h"
15#include "queued-signal.h"
16
17namespace {
18struct UserQuota {
19 int64_t uid;
20 size_t count;
21};
22Mutex quotaLock;
24constexpr size_t PendingLimit = 16;
25constexpr uint64_t Unblockable = (uint64_t(1) << (SIGKILL - 1)) | (uint64_t(1) << (SIGSTOP - 1));
26
27PosixSubsystem* subsystem(Process* process) {
28 return process && process->getType() == Process::Posix
29 ? static_cast<PosixSubsystem*>(process->getSubsystem())
30 : nullptr;
31}
32
33template <class T>
34T field(const LinuxQueuedSiginfo& info, size_t offset) {
35 T result;
36 MemoryCopy(&result, info.bytes + offset, sizeof(result));
37 return result;
38}
39template <class T>
40void field(LinuxQueuedSiginfo& info, size_t offset, T value) {
41 MemoryCopy(info.bytes + offset, &value, sizeof(value));
42}
43
44void cancelSource(Process* process, const void* source) {
45 for (size_t i = process->getNumThreads(); i > 0; --i) {
47 if (process->acquireThread(thread, i - 1))
48 thread->cullSignalSource(source);
49 }
50}
51} // namespace
52
54 public:
55 explicit SignalQueueReservation(UserQuota* quota) : m_Quota(quota) {}
57 // Event completion also runs with interrupts disabled. Retire only the
58 // count here; an admission in normal thread context reclaims idle nodes.
59 const size_t previous = __atomic_fetch_sub(&m_Quota->count, size_t(1), __ATOMIC_ACQ_REL);
60 assert(previous);
61 }
62 static SharedPointer<SignalQueueReservation> reserve(Process* process) {
63 LockGuard<Mutex> guard(quotaLock);
64 const int64_t uid = process->getUserId();
65 UserQuota* quota = nullptr;
66 for (auto it = quotas.begin(); it != quotas.end();) {
67 UserQuota* candidate = *it;
68 if (candidate->uid == uid) {
69 quota = candidate;
70 ++it;
71 } else if (!__atomic_load_n(&candidate->count, __ATOMIC_ACQUIRE)) {
72 it = quotas.erase(it);
73 delete candidate;
74 } else {
75 ++it;
76 }
77 }
78 if (!quota) {
79 quota = new UserQuota{uid, 0};
80 quotas.pushBack(quota);
81 }
82 if (__atomic_load_n(&quota->count, __ATOMIC_ACQUIRE) >= PendingLimit) {
83 SYSCALL_ERROR(NoMoreProcesses);
85 }
86 __atomic_add_fetch(&quota->count, size_t(1), __ATOMIC_ACQ_REL);
88 }
89
90 private:
91 UserQuota* m_Quota;
92};
93
95 public:
97 : m_Reservation(reservation), m_Generation(0), m_Done(false) {}
99 : m_Token(token), m_Generation(0), m_Done(false) {
100 LockGuard<Spinlock> guard(token->m_Lock);
101 m_Generation = token->m_Generation;
102 }
103 bool active() const override {
104 if (!m_Token)
105 return true;
106 LockGuard<Spinlock> guard(m_Token->m_Lock);
107 return m_Token->m_Active && m_Token->m_Generation == m_Generation;
108 }
109 bool timer() const override {
110 return bool(m_Token);
111 }
112 const void* source() const override {
113 return m_Token.get();
114 }
115 void timerInfo(int32_t& id, int32_t& overrun) const override {
116 if (!m_Token)
117 return;
118 LockGuard<Spinlock> guard(m_Token->m_Lock);
119 id = m_Token->timerId;
120 const uint64_t count = m_Token->m_Expirations;
121 overrun = count > 0x80000000ULL ? 0x7fffffff : (count ? count - 1 : 0);
122 }
123 void complete(bool delivered, int32_t overrun) override {
124 {
125 LockGuard<Spinlock> guard(m_Lock);
126 if (m_Done)
127 return;
128 m_Done = true;
129 }
130 if (!m_Token) {
131 m_Reservation.reset();
132 return;
133 }
134 LockGuard<Spinlock> guard(m_Token->m_Lock);
135 if (!m_Token->m_Active || m_Token->m_Generation != m_Generation)
136 return;
137 if (delivered) {
138 const uint64_t consumed = uint64_t(overrun) + 1;
139 m_Token->m_Expirations =
140 m_Token->m_Expirations > consumed ? m_Token->m_Expirations - consumed : 0;
141 m_Token->m_DeliveredOverrun = overrun;
142 } else if (overrun >= 0) {
143 // Ignored or discarded notifications retire their accumulated periods.
144 m_Token->m_Expirations = 0;
145 }
146 m_Token->m_Pending = false;
147 }
148
149 private:
152 uint64_t m_Generation;
153 Spinlock m_Lock;
154 bool m_Done;
155};
156
157SharedPointer<SignalEventState> posix_signal_reserve_queue(Process* process) {
158 auto reservation = SignalQueueReservation::reserve(process);
159 return reservation ? SharedPointer<SignalEventState>(new QueuedSignalState(reservation))
161}
162
163PosixTimerSignalToken::PosixTimerSignalToken()
164 : timerId(0),
165 m_Active(true),
166 m_Pending(false),
167 m_Generation(1),
168 m_Expirations(0),
169 m_DeliveredOverrun(0) {}
170PosixTimerSignalToken::~PosixTimerSignalToken() = default;
171bool PosixTimerSignalToken::addExpirations(uint64_t count) {
172 LockGuard<Spinlock> guard(m_Lock);
173 if (!m_Active)
174 return false;
175 m_Expirations = count > ~uint64_t(0) - m_Expirations ? ~uint64_t(0) : m_Expirations + count;
176 if (!m_Expirations || m_Pending)
177 return false;
178 m_Pending = true;
179 return true;
180}
181void PosixTimerSignalToken::queueFailed() {
182 LockGuard<Spinlock> guard(m_Lock);
183 m_Pending = false;
184}
185int PosixTimerSignalToken::getDeliveredOverrun() {
186 LockGuard<Spinlock> guard(m_Lock);
187 return m_DeliveredOverrun;
188}
189bool posix_signal_reserve_timer(Process* process, SharedPointer<PosixTimerSignalToken>& token) {
190 auto reservation = SignalQueueReservation::reserve(process);
191 if (!reservation)
192 return false;
193 token.reset(new PosixTimerSignalToken);
194 token->m_Reservation = reservation;
195 return true;
196}
197int posix_signal_queue_timer(Process* process, Thread* target, int signal, uint64_t value,
199 auto* owner = subsystem(process);
200 Process::ThreadLease selected;
201 const bool processDirected = !target;
202 if (!owner || (!target && !process->acquireProcessSignalThread(selected)))
203 return -1;
204 if (!target)
205 target = selected.get();
207 const auto result =
208 owner->queueSignalDelivery(target, signal, nullptr, -2, processDirected, value, state);
209 if (result == PosixSubsystem::SignalDeliveryResult::Ignored)
210 state->complete(false, 0);
211 return result == PosixSubsystem::SignalDeliveryResult::Queued ||
212 result == PosixSubsystem::SignalDeliveryResult::Ignored
213 ? 0
214 : -1;
215}
216void posix_signal_reset_timer(Process* process, const SharedPointer<PosixTimerSignalToken>& token) {
217 auto* owner = subsystem(process);
218 if (!owner || !token)
219 return;
220 PendingSignalNotification notification(owner->pendingSignalContext());
221 LockGuard<Mutex> pendingGuard(owner->pendingSignalLock());
222 {
223 LockGuard<Spinlock> guard(token->m_Lock);
224 ++token->m_Generation;
225 token->m_Expirations = 0;
226 token->m_DeliveredOverrun = 0;
227 token->m_Pending = false;
228 }
229 cancelSource(process, token.get());
230 owner->pendingSignalContext()->recordChange();
231}
232void posix_signal_cancel_timer(Process* process,
234 auto* owner = subsystem(process);
235 if (!owner || !token)
236 return;
237 PendingSignalNotification notification(owner->pendingSignalContext());
238 LockGuard<Mutex> pendingGuard(owner->pendingSignalLock());
239 {
240 LockGuard<Spinlock> guard(token->m_Lock);
241 token->m_Active = false;
242 ++token->m_Generation;
243 token->m_Expirations = 0;
244 token->m_Pending = false;
245 }
246 cancelSource(process, token.get());
247 token->m_Reservation.reset();
248 owner->pendingSignalContext()->recordChange();
249}
250
251int posix_rt_sigpending(uint64_t* signals, size_t size) {
252 if (size != sizeof(uint64_t)) {
253 SYSCALL_ERROR(InvalidArgument);
254 return -1;
255 }
256 Thread* current = Processor::information().getCurrentThread();
257 Process* process = current->getParent();
258 auto* owner = subsystem(process);
259 LockGuard<Mutex> guard(owner->pendingSignalLock());
260 uint64_t mask = current->pendingSignalMask();
261 for (size_t i = process->getNumThreads(); i > 0; --i) {
263 if (process->acquireThread(thread, i - 1) && thread.get() != current)
264 mask |= thread->pendingSignalMask(true);
265 }
266 mask &= current->getSignalMask();
267 if (!PosixSubsystem::copyToUser(signals, &mask, sizeof(mask))) {
268 SYSCALL_ERROR(BadAddress);
269 return -1;
270 }
271 return 0;
272}
273
274int posix_rt_sigtimedwait(const uint64_t* signals, LinuxQueuedSiginfo* info,
275 const LinuxKernelTimespec* timeout, size_t size) {
276 TerminationDeferral termination;
277 if (size != sizeof(uint64_t)) {
278 SYSCALL_ERROR(InvalidArgument);
279 return -1;
280 }
281 uint64_t mask = 0;
282 LinuxKernelTimespec requested = {};
283 if (!PosixSubsystem::copyFromUser(&mask, signals, sizeof(mask)) ||
284 (timeout && !PosixSubsystem::copyFromUser(&requested, timeout, sizeof(requested)))) {
285 SYSCALL_ERROR(BadAddress);
286 return -1;
287 }
288 if (timeout &&
289 (requested.tv_sec < 0 || requested.tv_nsec < 0 || requested.tv_nsec >= 1000000000)) {
290 SYSCALL_ERROR(InvalidArgument);
291 return -1;
292 }
293 Time::Timestamp remaining = Time::Infinity;
294 if (timeout) {
295 const uint64_t maximum = Time::Infinity - 1;
296 remaining = uint64_t(requested.tv_sec) > (maximum - requested.tv_nsec) / 1000000000
297 ? maximum
298 : uint64_t(requested.tv_sec) * 1000000000 + requested.tv_nsec;
299 }
300 mask &= ~Unblockable;
301 Thread* current = Processor::information().getCurrentThread();
302 Process* process = current->getParent();
303 auto* owner = subsystem(process);
304 PendingSignalNotification notification(owner->pendingSignalContext());
305 LockGuard<Mutex> guard(owner->pendingSignalLock());
306 current->setSynchronousSignalMask(mask);
307 struct Enrollment {
308 Thread* thread;
309 ~Enrollment() {
310 thread->setSynchronousSignalMask(0);
311 }
312 } enrollment{current};
313 while (true) {
314 PendingSignalReservation reservation;
315 if (reservation.reserve(current, mask)) {
316 const PendingSignalRecord record = reservation.record();
317 LinuxQueuedSiginfo result;
318 posix_signal_record_siginfo(record, result);
319 if (info && !PosixSubsystem::copyToUser(info, &result, sizeof(result))) {
320 SYSCALL_ERROR(BadAddress);
321 return -1;
322 }
323 reservation.commit(record.overrun);
324 return record.number;
325 }
326 if (!remaining) {
327 SYSCALL_ERROR(NoMoreProcesses);
328 return -1;
329 }
330 if (current->hasEvents()) {
331 SYSCALL_ERROR(Interrupted);
332 return -1;
333 }
334 ConditionVariable::Error error = ConditionVariable::NoError;
335 if (!owner->pendingSignalChanged().wait(owner->pendingSignalLock(), remaining, error)) {
337 guard.disown();
338 if (error == ConditionVariable::TimedOut)
339 SYSCALL_ERROR(NoMoreProcesses);
340 else
341 SYSCALL_ERROR(Interrupted);
342 return -1;
343 }
344 }
345}
346
347int posix_rt_sigqueueinfo(int pid, int signal, const LinuxQueuedSiginfo* info) {
348 TerminationDeferral termination;
349 if (pid <= 0 || signal <= 0 || signal > 64) {
350 SYSCALL_ERROR(InvalidArgument);
351 return -1;
352 }
353 LinuxQueuedSiginfo supplied = {};
354 if (!PosixSubsystem::copyFromUser(&supplied, info, sizeof(supplied))) {
355 SYSCALL_ERROR(BadAddress);
356 return -1;
357 }
358 // The public sigqueue protocol carries SI_QUEUE. Kernel-generated codes
359 // and timer identity are always constructed from trusted kernel state.
360 if (field<int32_t>(supplied, 8) != -1) {
361 SYSCALL_ERROR(InvalidArgument);
362 return -1;
363 }
365 if (!Scheduler::instance().acquireProcessByUserspaceId(target, pid) || !subsystem(target.get())) {
366 SYSCALL_ERROR(NoSuchProcess);
367 return -1;
368 }
369 auto* caller =
370 static_cast<PosixProcess*>(Processor::information().getCurrentThread()->getParent());
371 auto* recipient = static_cast<PosixProcess*>(target.get());
372 const int64_t real = caller->getUserId(), effective = caller->getEffectiveUserId();
373 const int64_t targetReal = recipient->getUserId(), saved = recipient->getSavedUserId();
374 if (effective != 0 &&
375 !((real >= 0 && (real == targetReal || real == saved)) ||
376 (effective >= 0 && (effective == targetReal || effective == saved))) &&
377 !(signal == SIGCONT && caller->sharesSession(*recipient))) {
378 SYSCALL_ERROR(NotEnoughPermissions);
379 return -1;
380 }
382 if (!recipient->acquireProcessSignalThread(thread)) {
383 SYSCALL_ERROR(NoSuchProcess);
384 return -1;
385 }
386 const auto result = subsystem(recipient)->queueSignalDelivery(
387 thread.get(), signal, nullptr, -1, true, field<uint64_t>(supplied, 24));
388 if (result == PosixSubsystem::SignalDeliveryResult::Full)
389 return -1;
390 if (result == PosixSubsystem::SignalDeliveryResult::Unavailable ||
391 result == PosixSubsystem::SignalDeliveryResult::Rejected) {
392 SYSCALL_ERROR(NoSuchProcess);
393 return -1;
394 }
395 return 0;
396}
static bool mutexAcquired(Error error)
Definition List.h:61
Iterator begin()
Definition List.h:122
Iterator end()
Definition List.h:132
Definition Mutex.h:56
int64_t getUserId() const final
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
MUST_USE_RESULT bool acquireProcessSignalThread(ThreadLease &lease)
Definition Process.cc:1270
size_t getNumThreads()
Definition Process.cc:1243
MUST_USE_RESULT bool acquireThread(ThreadLease &lease, size_t n)
Definition Process.cc:1248
virtual int64_t getUserId() const
Definition Process.cc:2051
static ProcessorInformation & information()
static Scheduler & instance()
Definition Scheduler.h:96
T * get() const
uint64_t getSignalMask()
Definition Thread.cc:2002
Process * getParent() const
Definition Thread.h:338
Iterator erase(Iterator &Iter)
Definition List.h:352
void pushBack(const T &value)
Definition List.h:216