The Pedigree Project 0.1
resource-syscall-regressions.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/errors.h"
11#include "pedigree/kernel/process/Process.h"
12#include "pedigree/kernel/process/Thread.h"
13#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
14#include "pedigree/kernel/processor/Processor.h"
15#include "pedigree/kernel/processor/VirtualAddressSpace.h"
16
17#include "modules/subsys/posix/PosixProcess.h"
18#include "modules/subsys/posix/PosixSubsystem.h"
19#include "modules/subsys/posix/linux-resource-abi.h"
20#include "modules/subsys/posix/system-syscalls.h"
22#include <sys/prctl.h>
23#include <sys/resource.h>
24#include <sys/times.h>
25
26namespace {
27constexpr int PreservedErrno = 173;
28
29struct ResourceSyscallContext {
30 ResourceSyscallContext(Process* process, int foreignPid)
31 : process(process), foreignPid(foreignPid), passed(false), returned(0) {}
32
33 Process* process;
34 int foreignPid;
35 bool passed;
36 Atomic<size_t> returned;
37};
38
39int resourceSyscallWorker(void* parameter) {
40 ResourceSyscallContext* context = reinterpret_cast<ResourceSyscallContext*>(parameter);
41 Thread* thread = Processor::information().getCurrentThread();
42 const size_t pageSize = PhysicalMemoryManager::getPageSize();
43 uintptr_t address = 0;
44 if (!context->process->allocateUserRange(Process::UserRegion::Normal, pageSize, address)) {
45 context->returned += 1;
46 return 1;
47 }
48
49 uintptr_t mappedAddress = address;
51 mappedAddress, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write);
52 if (!mapping || mappedAddress != address) {
53 if (mapping) {
54 MemoryMapManager::instance().remove(mappedAddress, pageSize);
55 }
56 context->process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
57 context->returned += 1;
58 return 1;
59 }
60
61 struct rlimit* nativeLimit = reinterpret_cast<struct rlimit*>(address + 64);
62 LinuxRlimit64* linuxLimit = reinterpret_cast<LinuxRlimit64*>(address + 128);
63 struct tms* processTimes = reinterpret_cast<struct tms*>(address + 384);
64 struct rusage* usage = reinterpret_cast<struct rusage*>(address + 512);
65 LinuxRusage64* linuxUsage = reinterpret_cast<LinuxRusage64*>(address + 1024);
66 uint8_t* linuxUsageCanary = reinterpret_cast<uint8_t*>(linuxUsage + 1);
67 const uintptr_t kernelStart = Processor::information().getVirtualAddressSpace().getKernelStart();
68 struct rlimit* badNative = reinterpret_cast<struct rlimit*>(kernelStart);
69 LinuxRlimit64* badLinux = reinterpret_cast<LinuxRlimit64*>(kernelStart);
70 bool passed = true;
71
72 thread->setErrno(PreservedErrno);
73 passed &= posix_getrlimit(RLIMIT_CPU, nativeLimit) == 0 &&
74 nativeLimit->rlim_cur == RLIM_INFINITY && nativeLimit->rlim_max == RLIM_INFINITY &&
75 thread->getErrno() == PreservedErrno;
76
77 thread->setErrno(0);
78 passed &= posix_getrlimit(-1, nativeLimit) == -1 && thread->getErrno() == Error::InvalidArgument;
79 thread->setErrno(0);
80 passed &=
81 posix_getrlimit(RLIMIT_NOFILE, badNative) == -1 && thread->getErrno() == Error::BadAddress;
82 thread->setErrno(PreservedErrno);
83 passed &= posix_getrlimit(RLIMIT_RTPRIO, nativeLimit) == 0 && nativeLimit->rlim_cur == 0 &&
84 nativeLimit->rlim_max == 0 && thread->getErrno() == PreservedErrno;
85#ifdef RLIMIT_RTTIME
86 thread->setErrno(PreservedErrno);
87 passed &= posix_getrlimit(RLIMIT_RTTIME, nativeLimit) == 0 &&
88 nativeLimit->rlim_cur == RLIM_INFINITY && nativeLimit->rlim_max == RLIM_INFINITY &&
89 thread->getErrno() == PreservedErrno;
90#endif
91
92 thread->setErrno(PreservedErrno);
93 passed &= posix_prlimit64(0, RLIMIT_NOFILE, nullptr, linuxLimit) == 0 &&
94 linuxLimit->current == 16384 && linuxLimit->maximum == 16384 &&
95 thread->getErrno() == PreservedErrno;
96 thread->setErrno(PreservedErrno);
97 passed &= posix_prlimit64(static_cast<int>(context->process->getId()), RLIMIT_NOFILE, nullptr,
98 nullptr) == 0 &&
99 thread->getErrno() == PreservedErrno;
100
101 thread->setErrno(0);
102 passed &= posix_prlimit64(0, RLIMIT_NOFILE, badLinux, nullptr) == -1 &&
103 thread->getErrno() == Error::Unimplemented;
104 thread->setErrno(0);
105 passed &= posix_prlimit64(0, RLIMIT_NOFILE, linuxLimit, nullptr) == -1 &&
106 thread->getErrno() == Error::Unimplemented;
107 thread->setErrno(0);
108 passed &= posix_prlimit64(0, RLIMIT_NOFILE, nullptr, badLinux) == -1 &&
109 thread->getErrno() == Error::BadAddress;
110 thread->setErrno(0);
111 passed &= posix_prlimit64(-1, RLIMIT_NOFILE, nullptr, linuxLimit) == -1 &&
112 thread->getErrno() == Error::NoSuchProcess;
113 thread->setErrno(0);
114 passed &= posix_prlimit64(context->foreignPid, RLIMIT_NOFILE, nullptr, linuxLimit) == -1 &&
115 thread->getErrno() == Error::Unimplemented;
116 thread->setErrno(0);
117 passed &= posix_prlimit64(0, -1, nullptr, linuxLimit) == -1 &&
118 thread->getErrno() == Error::InvalidArgument;
119
120 thread->setErrno(0);
121 passed &=
122 posix_setrlimit(RLIMIT_NOFILE, badNative) == -1 && thread->getErrno() == Error::Unimplemented;
123 thread->setErrno(0);
124 passed &= posix_setrlimit(RLIMIT_NOFILE, nativeLimit) == -1 &&
125 thread->getErrno() == Error::Unimplemented;
126
127 thread->setErrno(PreservedErrno);
128 passed &= posix_membarrier(0, 0, 0) == 0 && thread->getErrno() == PreservedErrno;
129 thread->setErrno(0);
130 passed &= posix_membarrier(1, 0, 0) == -1 && thread->getErrno() == Error::InvalidArgument;
131 thread->setErrno(0);
132 passed &= posix_membarrier(0, 1, 0) == -1 && thread->getErrno() == Error::InvalidArgument;
133
134 constexpr Time::Timestamp clockTick = Time::Multiplier::Second / 100;
135 thread->publishTimeAccountingForHostedTest(73 * clockTick, 41 * clockTick);
136 context->process->publishTimeAccountingForHostedTest(235 * clockTick, 127 * clockTick);
137 Time::Timestamp expectedUser = context->process->getUserTime();
138 Time::Timestamp expectedKernel = context->process->getKernelTime();
139 const clock_t elapsedBefore = Time::getTicks() / clockTick;
140 thread->setErrno(PreservedErrno);
141 const clock_t elapsed = posix_times(processTimes);
142 const clock_t elapsedAfter = Time::getTicks() / clockTick;
143 passed &= elapsed >= elapsedBefore && elapsed <= elapsedAfter &&
144 processTimes->tms_utime == static_cast<clock_t>(expectedUser / clockTick) &&
145 processTimes->tms_stime == static_cast<clock_t>(expectedKernel / clockTick) &&
146 !processTimes->tms_cutime && !processTimes->tms_cstime &&
147 thread->getErrno() == PreservedErrno;
148 thread->setErrno(PreservedErrno);
149 passed &= posix_times(nullptr) >= 0 && thread->getErrno() == PreservedErrno;
150 thread->setErrno(0);
151 passed &= posix_times(reinterpret_cast<struct tms*>(kernelStart)) == -1 &&
152 thread->getErrno() == Error::BadAddress;
153
154 expectedUser = context->process->getUserTime();
155 expectedKernel = context->process->getKernelTime();
156 ByteSet(usage, 0xA5, sizeof(*usage));
157 thread->setErrno(PreservedErrno);
158 passed &=
159 posix_getrusage(RUSAGE_SELF, usage) == 0 &&
160 usage->ru_utime.tv_sec == static_cast<time_t>(expectedUser / Time::Multiplier::Second) &&
161 usage->ru_utime.tv_usec ==
162 static_cast<suseconds_t>((expectedUser % Time::Multiplier::Second) /
163 Time::Multiplier::Microsecond) &&
164 usage->ru_stime.tv_sec == static_cast<time_t>(expectedKernel / Time::Multiplier::Second) &&
165 usage->ru_stime.tv_usec ==
166 static_cast<suseconds_t>((expectedKernel % Time::Multiplier::Second) /
167 Time::Multiplier::Microsecond) &&
168 thread->getErrno() == PreservedErrno;
169 for (size_t i = sizeof(LinuxRusage64); i < sizeof(*usage); ++i) {
170 passed &= !reinterpret_cast<uint8_t*>(usage)[i];
171 }
172 ByteSet(usage, 0xA5, sizeof(*usage));
173 const Time::Timestamp threadUserBeforeNative = thread->getUserTime();
174 const Time::Timestamp threadKernelBeforeNative = thread->getKernelTime();
175 thread->setErrno(PreservedErrno);
176 const bool nativeThreadUsageSucceeded = posix_getrusage(RUSAGE_THREAD, usage) == 0;
177 const Time::Timestamp threadUserAfterNative = thread->getUserTime();
178 const Time::Timestamp threadKernelAfterNative = thread->getKernelTime();
179 const Time::Timestamp nativeThreadUser =
180 static_cast<Time::Timestamp>(usage->ru_utime.tv_sec) * Time::Multiplier::Second +
181 static_cast<Time::Timestamp>(usage->ru_utime.tv_usec) * Time::Multiplier::Microsecond;
182 const Time::Timestamp nativeThreadKernel =
183 static_cast<Time::Timestamp>(usage->ru_stime.tv_sec) * Time::Multiplier::Second +
184 static_cast<Time::Timestamp>(usage->ru_stime.tv_usec) * Time::Multiplier::Microsecond;
185 passed &= nativeThreadUsageSucceeded &&
186 nativeThreadUser >=
187 threadUserBeforeNative - (threadUserBeforeNative % Time::Multiplier::Microsecond) &&
188 nativeThreadUser <=
189 threadUserAfterNative - (threadUserAfterNative % Time::Multiplier::Microsecond) &&
190 nativeThreadKernel >= threadKernelBeforeNative -
191 (threadKernelBeforeNative % Time::Multiplier::Microsecond) &&
192 nativeThreadKernel <= threadKernelAfterNative -
193 (threadKernelAfterNative % Time::Multiplier::Microsecond) &&
194 thread->getErrno() == PreservedErrno;
195 for (size_t i = offsetof(struct rusage, ru_maxrss); i < sizeof(*usage); ++i) {
196 passed &= !reinterpret_cast<uint8_t*>(usage)[i];
197 }
198 struct rusage untouched = {};
199 ByteSet(&untouched, 0xA5, sizeof(untouched));
200 MemoryCopy(usage, &untouched, sizeof(untouched));
201 thread->setErrno(PreservedErrno);
202 passed &= posix_getrusage(RUSAGE_CHILDREN, usage) == 0 && !usage->ru_utime.tv_sec &&
203 !usage->ru_utime.tv_usec && !usage->ru_stime.tv_sec && !usage->ru_stime.tv_usec &&
204 thread->getErrno() == PreservedErrno;
205 for (size_t i = offsetof(struct rusage, ru_maxrss); i < sizeof(*usage); ++i) {
206 passed &= !reinterpret_cast<uint8_t*>(usage)[i];
207 }
208 thread->setErrno(0);
209 passed &= posix_getrusage(RUSAGE_SELF, reinterpret_cast<struct rusage*>(kernelStart)) == -1 &&
210 thread->getErrno() == Error::BadAddress;
211
212 ByteSet(linuxUsage, 0xA5, sizeof(*linuxUsage) + 32);
213 thread->setErrno(PreservedErrno);
214 passed &=
215 posix_linux_getrusage(RUSAGE_SELF, linuxUsage) == 0 &&
216 linuxUsage->userSeconds == static_cast<int64_t>(expectedUser / Time::Multiplier::Second) &&
217 linuxUsage->userMicroseconds ==
218 static_cast<int64_t>((expectedUser % Time::Multiplier::Second) /
219 Time::Multiplier::Microsecond) &&
220 linuxUsage->systemSeconds ==
221 static_cast<int64_t>(expectedKernel / Time::Multiplier::Second) &&
222 linuxUsage->systemMicroseconds ==
223 static_cast<int64_t>((expectedKernel % Time::Multiplier::Second) /
224 Time::Multiplier::Microsecond) &&
225 !linuxUsage->maximumResidentSetSize && !linuxUsage->involuntaryContextSwitches &&
226 thread->getErrno() == PreservedErrno;
227 for (size_t i = 0; i < 32; ++i) {
228 passed &= linuxUsageCanary[i] == 0xA5;
229 }
230 ByteSet(linuxUsage, 0xA5, sizeof(*linuxUsage) + 32);
231 const Time::Timestamp threadUserBeforeLinux = thread->getUserTime();
232 const Time::Timestamp threadKernelBeforeLinux = thread->getKernelTime();
233 thread->setErrno(PreservedErrno);
234 const bool linuxThreadUsageSucceeded = posix_linux_getrusage(RUSAGE_THREAD, linuxUsage) == 0;
235 const Time::Timestamp threadUserAfterLinux = thread->getUserTime();
236 const Time::Timestamp threadKernelAfterLinux = thread->getKernelTime();
237 const Time::Timestamp linuxThreadUser =
238 static_cast<Time::Timestamp>(linuxUsage->userSeconds) * Time::Multiplier::Second +
239 static_cast<Time::Timestamp>(linuxUsage->userMicroseconds) * Time::Multiplier::Microsecond;
240 const Time::Timestamp linuxThreadKernel =
241 static_cast<Time::Timestamp>(linuxUsage->systemSeconds) * Time::Multiplier::Second +
242 static_cast<Time::Timestamp>(linuxUsage->systemMicroseconds) * Time::Multiplier::Microsecond;
243 passed &= linuxThreadUsageSucceeded &&
244 linuxThreadUser >=
245 threadUserBeforeLinux - (threadUserBeforeLinux % Time::Multiplier::Microsecond) &&
246 linuxThreadUser <=
247 threadUserAfterLinux - (threadUserAfterLinux % Time::Multiplier::Microsecond) &&
248 linuxThreadKernel >= threadKernelBeforeLinux -
249 (threadKernelBeforeLinux % Time::Multiplier::Microsecond) &&
250 linuxThreadKernel <=
251 threadKernelAfterLinux - (threadKernelAfterLinux % Time::Multiplier::Microsecond) &&
252 thread->getErrno() == PreservedErrno;
253 for (size_t i = offsetof(LinuxRusage64, maximumResidentSetSize); i < sizeof(*linuxUsage); ++i) {
254 passed &= !reinterpret_cast<uint8_t*>(linuxUsage)[i];
255 }
256 for (size_t i = 0; i < 32; ++i) {
257 passed &= linuxUsageCanary[i] == 0xA5;
258 }
259 ByteSet(linuxUsage, 0xA5, sizeof(*linuxUsage) + 32);
260 thread->setErrno(PreservedErrno);
261 passed &= posix_linux_getrusage(RUSAGE_CHILDREN, linuxUsage) == 0 && !linuxUsage->userSeconds &&
262 !linuxUsage->userMicroseconds && !linuxUsage->systemSeconds &&
263 !linuxUsage->systemMicroseconds && thread->getErrno() == PreservedErrno;
264 for (size_t i = 0; i < sizeof(*linuxUsage); ++i) {
265 passed &= !reinterpret_cast<uint8_t*>(linuxUsage)[i];
266 }
267 for (size_t i = 0; i < 32; ++i) {
268 passed &= linuxUsageCanary[i] == 0xA5;
269 }
270 thread->setErrno(0);
271 passed &=
272 posix_linux_getrusage(RUSAGE_SELF, reinterpret_cast<LinuxRusage64*>(kernelStart)) == -1 &&
273 thread->getErrno() == Error::BadAddress;
274
275 char* requestedName = reinterpret_cast<char*>(address + 256);
276 char* returnedName = reinterpret_cast<char*>(address + 320);
277 StringCopy(requestedName, "0123456789abcdef-long");
278 ByteSet(returnedName, 0xA5, 16);
279 const String originalName = thread->getName();
280 thread->setErrno(PreservedErrno);
281 passed &= posix_prctl(PR_SET_NAME, reinterpret_cast<uint64_t>(requestedName), 0, 0, 0) == 0 &&
282 thread->getName().compare("0123456789abcde") && thread->getErrno() == PreservedErrno;
283 thread->setErrno(PreservedErrno);
284 passed &= posix_prctl(PR_GET_NAME, reinterpret_cast<uint64_t>(returnedName), 0, 0, 0) == 0 &&
285 !MemoryCompare(returnedName, "0123456789abcde", 15) && !returnedName[15] &&
286 thread->getErrno() == PreservedErrno;
287 thread->setErrno(0);
288 passed &= posix_prctl(-1, 0, 0, 0, 0) == -1 && thread->getErrno() == Error::InvalidArgument;
289 thread->setErrno(0);
290 passed &= posix_prctl(PR_SET_NAME, kernelStart, 0, 0, 0) == -1 &&
291 thread->getErrno() == Error::BadAddress && thread->getName().compare("0123456789abcde");
292 thread->setErrno(0);
293 passed &= posix_prctl(PR_GET_NAME, kernelStart, 0, 0, 0) == -1 &&
294 thread->getErrno() == Error::BadAddress;
295 thread->setName(originalName);
296
297 MemoryMapManager::instance().remove(address, pageSize);
298 context->process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
299 context->passed = passed;
300 context->returned += 1;
301 return passed ? 0 : 1;
302}
303
304bool resourceSyscallSemantics(Process* kernelProcess) {
305 Process* process = new Process(kernelProcess);
306 process->setSubsystem(new PosixSubsystem);
307 PosixProcess* foreignProcess = new PosixProcess(kernelProcess);
308 foreignProcess->setSubsystem(new PosixSubsystem);
309 foreignProcess->publish();
310
311 ResourceSyscallContext context(process, static_cast<int>(foreignProcess->getId()));
312 Thread* worker = new Thread(process, resourceSyscallWorker, &context, nullptr, false, true, true);
313 worker->setName("hosted resource syscall semantics");
314 const bool started = worker->start();
315 const bool joined = started && worker->joinForCompletion();
316 if (!started) {
317 delete worker;
318 }
319
320 const bool passed = started && joined && context.returned == 1 && context.passed;
321 delete foreignProcess;
322 delete process;
323 if (!passed) {
324 ERROR(
325 "HOSTED-SYSCALL-TEST: FAIL resource-syscall-semantics: "
326 "limits, accounting, fail-closed mutation, usercopy, membarrier, or prctl names regressed");
327 return false;
328 }
329
330 NOTICE("HOSTED-SYSCALL-TEST: PASS resource-syscall-semantics");
331 return true;
332}
333} // namespace
334
335bool runHostedResourceSyscallRegressions(Process* process) {
336 return resourceSyscallSemantics(process);
337}
Memory-mapped file interface.
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
size_t remove(uintptr_t base, size_t length)
static MemoryMapManager & instance()
size_t getId()
Definition Process.h:463
static ProcessorInformation & information()
bool compare(const char *s, size_t len) const
Definition String.cc:208
void setErrno(size_t err)
Definition Thread.h:478
size_t getErrno()
Definition Thread.h:473
Time::Timestamp getUserTime() const
Definition Thread.h:410