The Pedigree Project 0.1
trace-signals.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/Thread.h"
4#include "pedigree/kernel/processor/state.h"
5#include "pedigree/kernel/utilities/lib.h"
6
7#include <signal.h>
8
9#include "PosixSubsystem.h"
10#include "queued-signal.h"
11#include "trace-state.h"
12
13namespace {
14void ignoreTraceSignal(int) {}
15bool stopping(int signal) {
16 return signal == 19 || signal == 20 || signal == 21 || signal == 22;
17}
18TraceSignalInfo signalInfo(SignalEvent& event) {
20 record.number = event.getNumber();
21 record.code = event.getSignalCode();
22 record.pid = event.getSenderProcess();
23 record.uid = event.getSenderUser();
24 record.value = event.getSignalValue();
25 record.status = event.childStatus();
26 record.userTime = event.childUserTime();
27 record.systemTime = event.childSystemTime();
28 event.timerInfo(record.timerId, record.overrun);
29 LinuxQueuedSiginfo encoded;
30 posix_signal_record_siginfo(record, encoded);
31 TraceSignalInfo info;
32 MemoryCopy(&info, &encoded, sizeof(info));
33 return info;
34}
35void releaseRelation(void* value) {
36 static_cast<TraceRelationRef*>(value)->reset();
37}
38void complete(SignalEvent& event) {
39 int32_t timer = 0, overrun = 0;
40 event.timerInfo(timer, overrun);
41 event.completeSignalDelivery(overrun);
42}
43} // namespace
44
45TraceStatus PosixSubsystem::prepareTraceSignal(Thread& thread, int signal, int32_t pid,
46 uint32_t uid, bool inheritReservation,
49 {
51 auto* handler = m_SignalHandlers.lookup(signal);
52 if (!handler)
53 return TraceStatus::Missing;
54 if (handler->pEvent)
56 static_cast<SignalEvent*>(handler->pEvent->cloneForDelivery()));
57 else if (handler->type == 2)
59 reinterpret_cast<uintptr_t>(ignoreTraceSignal), signal, ~0UL, uint64_t(0), true, true,
60 Event::HandlerPrivilege::Kernel, SignalEvent::DeliveryDisposition::DefaultAction);
61 }
62 if (!prepared)
63 return TraceStatus::NoMemory;
64 if (signal >= 35 && !inheritReservation) {
65 auto reservation = posix_signal_reserve_queue(thread.getParent());
66 if (!reservation)
67 return TraceStatus::Full;
68 prepared.get()->setDeliveryState(reservation);
69 }
70 prepared.get()->setSignalOrigin(0, pid, uid);
71 prepared.get()->setProcessDirected(false);
72 result = pedigree_std::move(prepared);
73 return TraceStatus::Success;
74}
75
76bool PosixSubsystem::publishTraceSignal(Thread& thread, UniquePointer<SignalEvent>& prepared,
77 SignalEvent* source) {
78 if (!prepared)
79 return false;
80 PendingSignalNotification notification(m_PendingSignals);
81 LockGuard<Mutex> pending(m_PendingSignals->lock);
82 SignalEvent* replacement = prepared.get();
83 const int signal = replacement->getNumber();
84 if (source && source->hasDeliveryState())
85 source->transferDeliveryStateTo(*replacement);
86 if (signal == 18) {
87 constexpr int stops[] = {19, 20, 21, 22};
88 for (int stop : stops)
89 thread.cullSignalEvent(stop);
90 thread.getParent()->resume();
91 } else if (stopping(signal)) {
92 thread.cullSignalEvent(18);
93 }
94 replacement->setContinuationEpoch(thread.getParent()->getContinuationEpoch());
95 replacement->setTraceBypass(!(thread.getSignalMask() & (uint64_t(1) << (signal - 1))));
96 if (!thread.sendEvent(replacement))
97 return false;
98 prepared.releaseOwnership();
99 m_PendingSignals->recordChange(signal, &thread, false);
100 return true;
101}
102
103Subsystem::UserReturnEventResult PosixSubsystem::userReturnEvent(Thread& thread, Event& event,
104 UserReturnFrame& frame) {
105 if (getAbi() != LinuxAbi || !event.isSignalEvent() || event.getNumber() == 9)
106 return UserReturnEventResult::Deliver;
107 auto& signal = static_cast<SignalEvent&>(event);
108 if (signal.consumeTraceBypass())
109 return UserReturnEventResult::Deliver;
110 TraceRelationRef relation;
111 Thread::StackDiscardScope lifetime(releaseRelation, &relation);
112 if (!m_TraceContext.acquireIncoming(relation))
113 return UserReturnEventResult::Deliver;
114 if (!signal.deliveryActive())
115 return UserReturnEventResult::Consumed;
116 const int number = signal.getNumber();
117 auto decision = relation->stop(thread, frame, PosixTraceRelation::StopKind::Signal, number,
118 signalInfo(signal), 0, signal.hasDeliveryState());
119 Thread::StackDiscardScope preparedLifetime(
120 [](void* value) { static_cast<UniquePointer<SignalEvent>*>(value)->reset(); },
121 &decision.prepared);
122 if (decision.terminal) {
123 if (thread.getUnwindState() == Thread::Continue)
124 thread.deferSignalExit(9);
125 return UserReturnEventResult::Terminal;
126 }
127 if (!decision.signal) {
128 complete(signal);
129 return UserReturnEventResult::Consumed;
130 }
131 SignalDisposition disposition;
132 if (decision.signal == number && stopping(number) && getSignalDisposition(number, disposition) &&
133 disposition.type == 1) {
134 const size_t epoch = signal.getContinuationEpoch();
135 if (thread.getParent()->getContinuationEpoch() == epoch && !decision.detached) {
136 decision = relation->stop(thread, frame, PosixTraceRelation::StopKind::Group, number,
137 TraceSignalInfo{}, epoch);
138 if (decision.terminal) {
139 if (thread.getUnwindState() == Thread::Continue)
140 thread.deferSignalExit(9);
141 return UserReturnEventResult::Terminal;
142 }
143 } else if (decision.detached) {
144 thread.getParent()->suspendIfContinuationEpoch(number, epoch);
145 }
146 complete(signal);
147 return UserReturnEventResult::Consumed;
148 }
149 if (decision.signal == number)
150 return UserReturnEventResult::Deliver;
151 if (!publishTraceSignal(thread, decision.prepared, &signal))
152 return UserReturnEventResult::Terminal;
153 return UserReturnEventResult::Consumed;
154}
155
156Subsystem::UserReturnResult PosixSubsystem::userReturnCheckpoint(Thread& thread,
157 UserReturnFrame& frame) {
158 TraceRelationRef relation;
159 Thread::StackDiscardScope lifetime(releaseRelation, &relation);
160 if (!m_TraceContext.acquireIncoming(relation) || !relation->takeExecTrap())
161 return UserReturnResult::Continue;
162 TraceSignalInfo info;
163 int32_t number = 5, code = 0;
164 MemoryCopy(reinterpret_cast<uint8_t*>(&info), &number, 4);
165 MemoryCopy(reinterpret_cast<uint8_t*>(&info) + 8, &code, 4);
166 auto decision = relation->stop(thread, frame, PosixTraceRelation::StopKind::Exec, 5, info);
167 Thread::StackDiscardScope preparedLifetime(
168 [](void* value) { static_cast<UniquePointer<SignalEvent>*>(value)->reset(); },
169 &decision.prepared);
170 if (decision.terminal) {
171 if (thread.getUnwindState() == Thread::Continue)
172 thread.deferSignalExit(9);
173 return UserReturnResult::Terminal;
174 }
175 if (decision.signal && !publishTraceSignal(thread, decision.prepared, nullptr))
176 return UserReturnResult::Terminal;
177 return UserReturnResult::Continue;
178}
179
180bool PosixSubsystem::traceException(Thread& thread, int& signal, InterruptState& state,
181 ExceptionType exception, uintptr_t address,
182 uintptr_t errorCode) {
183 auto* frame = thread.currentUserReturnFrame();
184 TraceRelationRef relation;
185 Thread::StackDiscardScope lifetime(releaseRelation, &relation);
186 if (!frame || !m_TraceContext.acquireIncoming(relation))
187 return false;
188 SignalEvent source(0, signal);
189 int32_t code = 128;
190 switch (exception) {
191 case PageFault:
192 code = errorCode & 1 ? 2 : 1;
193 break;
194 case InvalidOpcode:
195 code = 1;
196 break;
197 case GeneralProtectionFault:
198 case FileMappingFault:
199 code = 2;
200 break;
201 case DivideByZero:
202 code = 1;
203 break;
204 case FpuError:
205 case SpecialFpuError:
206 code = 7;
207 break;
208 default:
209 break;
210 }
211 source.setSignalOrigin(code, 0, 0);
212 auto info = signalInfo(source);
213 const uintptr_t fault = exception == PageFault || exception == FileMappingFault
214 ? address
215 : state.getInstructionPointer();
216 ByteSet(reinterpret_cast<uint8_t*>(&info) + 16, 0, sizeof(info) - 16);
217 MemoryCopy(reinterpret_cast<uint8_t*>(&info) + 16, &fault, sizeof(fault));
218 auto decision =
219 relation->stop(thread, *frame, PosixTraceRelation::StopKind::Signal, signal, info);
220 Thread::StackDiscardScope preparedLifetime(
221 [](void* value) { static_cast<UniquePointer<SignalEvent>*>(value)->reset(); },
222 &decision.prepared);
223 if (decision.terminal) {
224 if (thread.getUnwindState() == Thread::Continue)
225 thread.deferSignalExit(9);
226 return true;
227 }
228 if (!decision.signal)
229 return true;
230 if (decision.signal != signal) {
231 if (!publishTraceSignal(thread, decision.prepared, nullptr) &&
233 thread.deferSignalExit(9);
234 return true;
235 }
236 SignalDisposition disposition;
237 if (getSignalDisposition(signal, disposition) && disposition.type == 0)
238 return false;
239 // A default synchronous fault terminates at this return boundary without
240 // manufacturing a second pending delivery and a second trace stop.
241 if (disposition.type == 1)
242 thread.deferSignalExit(signal);
243 return true;
244}
Definition Event.h:49
virtual bool isSignalEvent() const
Definition Event.h:244
virtual size_t getNumber()=0
Abi getAbi() const
UnlikelyLock m_SignalHandlersLock
PosixTraceContext m_TraceContext
bool getSignalDisposition(size_t sig, SignalDisposition &disposition, bool beginDelivery=false)
void suspendIfContinuationEpoch(int stopSignal, size_t continuationEpoch)
Definition Process.cc:1896
void resume()
Definition Process.cc:1996
size_t getContinuationEpoch()
Definition Process.cc:1991
virtual size_t getNumber()
void setSignalOrigin(int32_t signalCode, int32_t senderProcess, uint32_t senderUser)
void setContinuationEpoch(size_t continuationEpoch)
@ Continue
No unwind necessary, carry on as normal.
Definition Thread.h:500
uint64_t getSignalMask()
Definition Thread.cc:1933
void deferSignalExit(int signal)
Definition Thread.cc:3582
UnwindType getUnwindState()
Definition Thread.h:518
Process * getParent() const
Definition Thread.h:325
void cullSignalEvent(size_t signalNumber)
Definition Thread.cc:2168
bool sendEvent(Event *pEvent)
Definition Thread.cc:1089
T * releaseOwnership() noexcept
Definition Pointers.h:106
static UniquePointer< T > adopt(T *pointer)
Definition Pointers.h:101