The Pedigree Project 0.1
signalfd-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/TerminationDeferral.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/Processor.h"
6#include "pedigree/kernel/syscallError.h"
7
8#include <fcntl.h>
9
10#include "FileDescriptor.h"
11#include "PosixSubsystem.h"
12#include "signalfd-syscalls.h"
13
14namespace {
15constexpr uint64_t Unblockable = (uint64_t(1) << 8) | (uint64_t(1) << 18);
16constexpr int Nonblock = 0x800, CloseOnExec = 0x80000;
17struct SignalFdRecord {
18 uint32_t number;
19 int32_t error, code;
20 uint32_t pid, uid;
21 int32_t fd;
22 uint32_t timerId, band, overrun, trap;
23 int32_t status, integer;
24 uint64_t pointer, userTime, systemTime, address;
25 uint16_t addressLowBit, padding;
26 int32_t syscall;
27 uint64_t callAddress;
28 uint32_t architecture;
29 uint8_t reserved[28];
30};
31static_assert(sizeof(SignalFdRecord) == 128, "Linux signalfd_siginfo layout");
32static_assert(__builtin_offsetof(SignalFdRecord, pointer) == 48, "signalfd pointer offset");
33
34SignalFdRecord encode(const PendingSignalRecord& source) {
35 SignalFdRecord result = {};
36 result.number = source.number;
37 result.code = source.code;
38 result.pid = source.pid;
39 result.uid = source.uid;
40 if (source.code == -2) {
41 result.pid = 0;
42 result.uid = 0;
43 result.timerId = source.timerId;
44 result.overrun = source.overrun;
45 }
46 result.integer = source.value;
47 result.pointer = source.value;
48 result.status = source.status;
49 result.userTime = source.userTime;
50 result.systemTime = source.systemTime;
51 return result;
52}
53struct ScalarCopy {
54 uint8_t* destination;
55};
56bool scalarCopy(void* opaque, const void* data, size_t size) {
57 auto& cursor = *static_cast<ScalarCopy*>(opaque);
58 if (!PosixSubsystem::copyToUser(cursor.destination, data, size))
59 return false;
60 cursor.destination += size;
61 return true;
62}
63} // namespace
64
65class SignalFdState final : public ReadinessSource {
66 public:
67 explicit SignalFdState(uint64_t mask) : m_Mask(mask & ~Unblockable) {}
68 uint64_t mask() const {
69 return __atomic_load_n(&m_Mask, __ATOMIC_ACQUIRE);
70 }
71 void snapshot(uint64_t& mask, uint64_t& generation) {
72 LockGuard<Mutex> guard(m_Lock);
73 mask = m_Mask;
74 generation = m_Generation;
75 }
76 bool closed() const {
77 return __atomic_load_n(&m_Closed, __ATOMIC_ACQUIRE);
78 }
79 bool setMask(uint64_t mask) {
80 {
81 LockGuard<Mutex> guard(m_Lock);
82 if (m_Closed) {
83 SYSCALL_ERROR(BadFileDescriptor);
84 return false;
85 }
86 __atomic_store_n(&m_Mask, mask & ~Unblockable, __ATOMIC_RELEASE);
87 __atomic_add_fetch(&m_Generation, uint64_t(1), __ATOMIC_RELEASE);
88 }
89 notifyReadiness(ReadyRead);
90 return true;
91 }
92 bool addOwner() {
93 LockGuard<Mutex> guard(m_Lock);
94 if (m_Closed)
95 return false;
96 ++m_Owners;
97 return true;
98 }
99 void removeOwner() {
100 bool last = false;
101 {
102 LockGuard<Mutex> guard(m_Lock);
103 assert(m_Owners);
104 last = !--m_Owners;
105 if (last)
106 __atomic_store_n(&m_Closed, true, __ATOMIC_RELEASE);
107 }
108 if (last)
110 }
111
112 private:
113 Mutex m_Lock;
114 uint64_t m_Mask;
115 uint64_t m_Generation = 0;
116 size_t m_Owners = 0;
117 bool m_Closed = false;
118};
119
121 public:
122 SignalFdObserver(SignalFdView* view, bool maskChanged)
123 : m_View(view), m_MaskChanged(maskChanged) {}
124 void readinessChanged(ReadyMask) override {
125 m_View->changed(m_MaskChanged);
126 }
127
128 private:
129 SignalFdView* m_View;
130 bool m_MaskChanged;
131};
132
133SignalFdView::SignalFdView(const SharedPointer<SignalFdState>& state,
136 : m_State(state), m_Context(context), m_Binding(binding) {}
137SignalFdView::~SignalFdView() {
138 // Observer shims borrow this pointer only while their subscription admits a
139 // callback; drain them before releasing any view state.
140 m_MaskSubscription.reset();
141 m_PendingSubscription.reset();
143}
144bool SignalFdView::subscribe() {
145 return m_Context->subscribeReadiness(
146 ReadyRead | ReadyHangup,
148 m_PendingSubscription) &&
149 m_State->subscribeReadiness(
150 ReadyRead | ReadyHangup,
152 m_MaskSubscription);
153}
154void SignalFdView::changed(bool maskChanged) {
155 if (maskChanged)
156 m_Context->wake();
157 notifyReadiness(ReadyRead | ReadyHangup);
158}
159ReadyMask SignalFdView::queryReady() {
160 if (m_State->closed())
161 return ReadyInvalid | ReadyHangup;
162 return m_Context->query(m_Binding, m_State->mask());
163}
165 return generationsFor(m_Binding);
166}
167ReadyMask SignalFdView::queryCallerReady() {
168 auto binding = m_Context->bind(Processor::information().getCurrentThread());
169 if (!binding)
170 return ReadyNone;
171 if (m_State->closed())
172 return ReadyInvalid | ReadyHangup;
173 return m_Context->query(binding, m_State->mask());
174}
175ReadinessGenerations SignalFdView::callerReadinessGenerations() {
176 auto binding = m_Context->bind(Processor::information().getCurrentThread());
177 return binding ? generationsFor(binding) : ReadinessGenerations{};
178}
179ReadinessGenerations SignalFdView::generationsFor(
181 LockGuard<Mutex> guard(m_GenerationLock);
182 uint64_t mask, maskGeneration;
183 m_State->snapshot(mask, maskGeneration);
185 m_Context->query(binding, mask, &result);
186 // Switching masks can lower the selected pending count, so keep the two
187 // versions and caller identity separate instead of hiding a readiness edge.
188 if (m_GenerationBinding.get() != binding.get() || m_MaskGeneration != maskGeneration ||
189 m_PendingGeneration != result.read) {
190 m_GenerationBinding = binding;
191 m_MaskGeneration = maskGeneration;
192 m_PendingGeneration = result.read;
193 ++m_ReadGeneration;
194 }
195 result.read = m_ReadGeneration;
196 return result;
197}
198
199SignalFd::SignalFd(uint64_t mask) : m_State(new SignalFdState(mask)) {}
200SignalFd::~SignalFd() = default;
201bool SignalFd::setMask(uint64_t mask) {
202 return m_State->setMask(mask);
203}
204bool SignalFd::addDescriptorOwner() {
205 return m_State->addOwner();
206}
207void SignalFd::removeDescriptorOwner() {
208 m_State->removeOwner();
209}
210SharedPointer<SignalFdView> SignalFd::bindCaller() {
211 Thread* thread = Processor::information().getCurrentThread();
212 auto* owner = static_cast<PosixSubsystem*>(thread->getParent()->getSubsystem());
213 auto context = owner->pendingSignalContext();
214 auto binding = context->bind(thread);
215 if (!binding || m_State->closed())
217 SharedPointer<SignalFdView> view(new SignalFdView(m_State, context, binding));
218 if (!view->subscribe())
220 return view;
221}
222ssize_t SignalFd::readToUser(void* destination, size_t count, bool canBlock) {
223 ScalarCopy cursor{static_cast<uint8_t*>(destination)};
224 return readWithCopy(count, canBlock, scalarCopy, &cursor);
225}
226ssize_t SignalFd::readWithCopy(size_t count, bool canBlock, PosixDescriptorReadCopy copy,
227 void* opaque) {
228 TerminationDeferral termination;
229 if (count < sizeof(SignalFdRecord)) {
230 SYSCALL_ERROR(InvalidArgument);
231 return -1;
232 }
233 auto view = bindCaller();
234 if (!view) {
235 SYSCALL_ERROR(BadFileDescriptor);
236 return -1;
237 }
238 Thread* caller = Processor::information().getCurrentThread();
239 auto context = view->m_Context;
240 PendingSignalNotification notification(context);
241 LockGuard<Mutex> guard(context->lock);
242 size_t copied = 0;
243 while (count - copied >= sizeof(SignalFdRecord)) {
244 if (m_State->closed()) {
245 if (copied) {
246 caller->setErrno(0);
247 return copied;
248 }
249 SYSCALL_ERROR(BadFileDescriptor);
250 return -1;
251 }
252 PendingSignalReservation reservation;
253 if (reservation.reserve(caller, m_State->mask())) {
254 const PendingSignalRecord record = reservation.record();
255 const SignalFdRecord result = encode(record);
256 if (!copy(opaque, &result, sizeof(result))) {
257 if (copied) {
258 caller->setErrno(0);
259 return copied;
260 }
261 SYSCALL_ERROR(BadAddress);
262 return -1;
263 }
264 reservation.commit(record.overrun);
265 copied += sizeof(result);
266 continue;
267 }
268 if (copied) {
269 caller->setErrno(0);
270 return copied;
271 }
272 if (!canBlock) {
273 SYSCALL_ERROR(NoMoreProcesses);
274 return -1;
275 }
276 if (caller->hasEvents()) {
277 SYSCALL_ERROR(Interrupted);
278 return -1;
279 }
280 ConditionVariable::Error error = ConditionVariable::NoError;
281 if (!context->changed.wait(context->lock, error)) {
283 guard.disown();
284 SYSCALL_ERROR(Interrupted);
285 return -1;
286 }
287 }
288 caller->setErrno(0);
289 return copied;
290}
291
292int posix_signalfd(int fd, const uint64_t* mask, size_t size) {
293 return posix_signalfd4(fd, mask, size, 0);
294}
295int posix_signalfd4(int fd, const uint64_t* mask, size_t size, int flags) {
296 if (size != sizeof(uint64_t) || (flags & ~(Nonblock | CloseOnExec))) {
297 SYSCALL_ERROR(InvalidArgument);
298 return -1;
299 }
300 uint64_t interest = 0;
301 if (!PosixSubsystem::copyFromUser(&interest, mask, sizeof(interest))) {
302 SYSCALL_ERROR(BadAddress);
303 return -1;
304 }
305 if (fd != -1) {
306 DescriptorLease descriptor;
307 if (!acquireDescriptor(fd, descriptor)) {
308 SYSCALL_ERROR(BadFileDescriptor);
309 return -1;
310 }
311 auto signalFd = descriptor->getSignalFdImpl();
312 if (!signalFd) {
313 SYSCALL_ERROR(InvalidArgument);
314 return -1;
315 }
316 if (!signalFd->setMask(interest))
317 return -1;
318 Processor::information().getCurrentThread()->setErrno(0);
319 return fd;
320 }
321 const size_t number = getAvailableDescriptor();
322 FileDescriptor* descriptor =
323 new FileDescriptor(nullptr, 0, number, flags & CloseOnExec ? FD_CLOEXEC : 0,
324 O_RDWR | (flags & Nonblock ? O_NONBLOCK : 0));
325 descriptor->setSignalFdImpl(SharedPointer<SignalFd>(new SignalFd(interest)));
326 addDescriptor(number, descriptor);
327 Processor::information().getCurrentThread()->setErrno(0);
328 return number;
329}
static bool mutexAcquired(Error error)
Definition Mutex.h:56
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)
static ProcessorInformation & information()
void notifyReadiness(ReadyMask mask)
Definition Readiness.cc:201
void closeReadiness(ReadyMask mask=ReadyInvalid|ReadyHangup)
Definition Readiness.cc:208
MUST_USE_RESULT bool subscribeReadiness(ReadyMask interest, const SharedPointer< ReadinessObserver > &observer, ReadinessSubscription &subscription)
Definition Readiness.cc:181
T * get() const
void readinessChanged(ReadyMask) override
ReadinessGenerations readinessGenerations() override
void setErrno(size_t err)
Definition Thread.h:478
Process * getParent() const
Definition Thread.h:338