The Pedigree Project 0.1
namespace-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "namespace-syscalls.h"
3#include "pedigree/kernel/Version.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/process/Uninterruptible.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/ProcessorInformation.h"
8#include "pedigree/kernel/syscallError.h"
9#include "pedigree/kernel/utilities/utility.h"
10
11#include "PosixProcess.h"
12#include "PosixSubsystem.h"
13#include "ipc-namespace.h"
15#include "modules/system/vfs/MountView.h"
16#include "namespace-file.h"
17#include "network-namespace.h"
18#include "sandbox-state.h"
19#include "sysv-semaphore-syscalls.h"
20#include "user-namespace.h"
21#include <sys/utsname.h>
22
23namespace {
24constexpr unsigned long NewMount = 0x20000, NewUts = 0x04000000, NewIpc = 0x08000000,
25 NewUser = 0x10000000, NewPid = 0x20000000, NewNet = 0x40000000;
26constexpr unsigned long KnownUnshare = 0x80 | 0x100 | 0x200 | 0x400 | 0x800 | 0x10000 | 0x20000 |
27 0x40000 | 0x02000000 | NewUts | 0x08000000 | 0x10000000 |
28 0x20000000 | 0x40000000;
29
30class NamespaceResult {
31 public:
32 NamespaceResult() : m_Thread(*Processor::information().getCurrentThread()) {}
33 ~NamespaceResult() {
34 m_Thread.setErrno(m_Error);
35 }
36 int finish(int value) {
37 m_Error = value < 0 ? m_Thread.getErrno() : 0;
38 return value;
39 }
40
41 private:
42 Thread& m_Thread;
43 size_t m_Error = 0;
44};
45
46bool administrative(const UserNamespaceRef& owner) {
47 if (!posix_namespace_capable(owner, PosixCapabilities::SysAdmin)) {
48 SYSCALL_ERROR(NotEnoughPermissions);
49 return false;
50 }
51 return true;
52}
53
55 Process* process = Processor::information().getCurrentThread()->getParent();
56 if (process->getType() != Process::Posix || !process->getSubsystem())
57 return {};
58 return static_cast<PosixSubsystem*>(process->getSubsystem())->namespaceContext();
59}
60
61int setName(const char* name, size_t suppliedLength, bool domain) {
62 NamespaceResult result;
63 Uninterruptible lifetime;
65 const int32_t length = static_cast<int32_t>(suppliedLength);
66 if (length < 0 || length > 64) {
67 SYSCALL_ERROR(InvalidArgument);
68 return result.finish(-1);
69 }
70 char bytes[64] = {};
71 if (length && !PosixSubsystem::copyFromUser(bytes, name, static_cast<size_t>(length))) {
72 SYSCALL_ERROR(BadAddress);
73 return result.finish(-1);
74 }
75 auto selected = context();
76 UtsRef space;
77 if (!selected || !selected->acquireThread(*Processor::information().getCurrentThread(), space))
78 return result.finish(posix_uts_error(UtsStatus::Missing));
79 if (!administrative(space->owner())) {
80 return result.finish(-1);
81 }
82 space->setName(domain, bytes, static_cast<size_t>(length));
83 return result.finish(0);
84}
85
86void copyField(char* output, size_t capacity, const char* source) {
87 size_t length = 0;
88 while (source[length] && length + 1 < capacity)
89 ++length;
90 MemoryCopy(output, source, length);
91}
92} // namespace
93
94int posix_sethostname(const char* name, size_t length) {
95 return setName(name, length, false);
96}
97int posix_setdomainname(const char* name, size_t length) {
98 return setName(name, length, true);
99}
100
101int posix_unshare(unsigned long flags) {
102 NamespaceResult result;
103 Uninterruptible lifetime;
104 if (flags & ~KnownUnshare) {
105 SYSCALL_ERROR(InvalidArgument);
106 return result.finish(-1);
107 }
108 if (flags & ~(NewMount | NewUts | NewUser | NewPid | NewIpc | NewNet)) {
109 SYSCALL_ERROR(OperationNotSupported);
110 return result.finish(-1);
111 }
112 if (!flags) {
113 return result.finish(0);
114 }
115 Thread& thread = *Processor::information().getCurrentThread();
116 Process* process = thread.getParent();
117 if ((flags & (NewMount | NewUser | NewPid)) && process->getNumThreads() != 1) {
118 SYSCALL_ERROR(InvalidArgument);
119 return result.finish(-1);
120 }
121 if (flags & NewPid) {
122 if (flags != NewPid) {
123 SYSCALL_ERROR(OperationNotSupported);
124 return result.finish(-1);
125 }
126 if (!posix_capable(PosixCapabilities::SysAdmin)) {
127 SYSCALL_ERROR(NotEnoughPermissions);
128 return result.finish(-1);
129 }
130 if (!process->pidNamespaceReady()) {
131 SYSCALL_ERROR(OutOfMemory);
132 return result.finish(-1);
133 }
134 const auto active = process->pidNamespace();
135 if (process->pidNamespaceForChildren().get() != active.get()) {
136 SYSCALL_ERROR(InvalidArgument);
137 return result.finish(-1);
138 }
139 if (active->depth() >= 32) {
140 SYSCALL_ERROR(NoSpaceLeftOnDevice);
141 return result.finish(-1);
142 }
144 if (!prepared) {
145 SYSCALL_ERROR(OutOfMemory);
146 return result.finish(-1);
147 }
148 if (!process->unsharePidNamespace(prepared)) {
149 SYSCALL_ERROR(InvalidArgument);
150 return result.finish(-1);
151 }
152 return result.finish(0);
153 }
154 TaskCredentialsRef credentials;
155 if ((flags & NewUser) && !posix_user_namespace_prepare(thread, credentials)) {
156 return result.finish(-1);
157 }
158 if (!(flags & NewUser) && !posix_capable(PosixCapabilities::SysAdmin)) {
159 SYSCALL_ERROR(NotEnoughPermissions);
160 return result.finish(-1);
161 }
162 const auto owner = credentials ? credentials->userNamespace : posix_user_namespace(thread);
163 FilesystemContextOwner filesystem;
164 auto previousFilesystem = process->acquireFilesystemContext();
165 if (flags & NewMount) {
166 auto* view = VfsMountView::fromContext(previousFilesystem);
167 if (!view ||
168 !view->forkNamespace(previousFilesystem, filesystem, owner ? owner->identity() : 0)) {
169 return result.finish(-1);
170 }
171 }
172 auto selected = context();
173 UtsRef source, replacement;
174 if (flags & NewUts) {
175 if (!selected || !selected->acquireThread(thread, source)) {
176 return result.finish(posix_uts_error(UtsStatus::Missing));
177 }
178 const auto status = posix_uts_copy(source, replacement, owner);
179 if (status != UtsStatus::Success) {
180 return result.finish(posix_uts_error(status));
181 }
182 }
183 NetworkNamespaceRef network;
184 if ((flags & NewNet) && !posix_network_namespace_prepare(owner, network)) {
185 return result.finish(-1);
186 }
188 if (flags & NewIpc) {
190 if (!ipc) {
191 SYSCALL_ERROR(OutOfMemory);
192 return result.finish(-1);
193 }
194 }
195 // Keep the original immutable task state until all fallible publication is done.
196 const auto previousState = thread.securityState();
197 Thread::SecurityStateRef preparedState;
198 if (!posix_sandbox_prepare_namespaces(thread, credentials, ipc, network, preparedState)) {
199 return result.finish(-1);
200 }
201 thread.setSecurityState(preparedState);
202 if (replacement && selected->replaceThread(thread, replacement) != UtsStatus::Success) {
203 thread.setSecurityState(previousState);
204 return result.finish(posix_uts_error(UtsStatus::Missing));
205 }
206 if (filesystem &&
207 !process->replaceFilesystemContext(pedigree_std::move(filesystem), previousFilesystem)) {
208 if (replacement) {
209 selected->replaceThread(thread, source);
210 }
211 thread.setSecurityState(previousState);
212 SYSCALL_ERROR(NoMoreProcesses);
213 return result.finish(-1);
214 }
215 if (ipc) {
216 posix_sem_thread_exit(&thread);
217 }
218 return result.finish(0);
219}
220
221int posix_setns(int fd, int type) {
222 NamespaceResult result;
223 Uninterruptible lifetime;
224 Process* process = Processor::information().getCurrentThread()->getParent();
225 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
226 DescriptorLease descriptor;
227 if (!subsystem || !subsystem->acquireFileDescriptor(fd, descriptor)) {
228 SYSCALL_ERROR(BadFileDescriptor);
229 return result.finish(-1);
230 }
231 UtsRef space;
232 if (!posix_uts_file_namespace(descriptor->getFile(), space) ||
233 (type && static_cast<unsigned int>(type) != NewUts)) {
234 SYSCALL_ERROR(InvalidArgument);
235 return result.finish(-1);
236 }
238 if (!administrative(space->owner())) {
239 return result.finish(-1);
240 }
241 auto selected = context();
242 if (!selected)
243 return result.finish(posix_uts_error(UtsStatus::Missing));
244 return result.finish(posix_uts_error(
245 selected->replaceThread(*Processor::information().getCurrentThread(), space)));
246}
247
248int posix_uname(struct utsname* user) {
249 NamespaceResult result;
250 Uninterruptible lifetime;
251 auto selected = context();
252 UtsRef space;
253 Thread& thread = *Processor::information().getCurrentThread();
254 if (!selected || !selected->acquireThread(thread, space))
255 return result.finish(posix_uts_error(UtsStatus::Missing));
256 const auto names = space->snapshot();
257 auto* subsystem = static_cast<PosixSubsystem*>(thread.getParent()->getSubsystem());
258 if (subsystem->getAbi() == PosixSubsystem::LinuxAbi) {
259 struct LinuxUtsname {
260 char fields[6][65];
261 } snapshot = {};
262 static_assert(sizeof(snapshot) == 390, "Linux utsname size");
263 copyField(snapshot.fields[0], 65, "Pedigree");
264 MemoryCopy(snapshot.fields[1], names.node, 65);
265 copyField(snapshot.fields[2], 65, "2.6.32-generic");
266 copyField(snapshot.fields[3], 65, g_pBuildRevision);
267#if ARM64
268 copyField(snapshot.fields[4], 65, "aarch64");
269#else
270 copyField(snapshot.fields[4], 65,
271 thread.executionPersonality().value() == ExecutionPersonality::Linux32
272 ? "i686"
273 : g_pBuildTarget);
274#endif
275 MemoryCopy(snapshot.fields[5], names.domain, 65);
276 if (!PosixSubsystem::copyToUser(user, &snapshot, sizeof(snapshot))) {
277 SYSCALL_ERROR(BadAddress);
278 return result.finish(-1);
279 }
280 } else {
281 struct utsname snapshot = {};
282 copyField(snapshot.sysname, sizeof(snapshot.sysname), "Pedigree");
283 MemoryCopy(snapshot.nodename, names.node,
284 sizeof(snapshot.nodename) < 65 ? sizeof(snapshot.nodename) : 65);
285 copyField(snapshot.release, sizeof(snapshot.release), g_pBuildRevision);
286 copyField(snapshot.version, sizeof(snapshot.version), "Foster");
287 copyField(snapshot.machine, sizeof(snapshot.machine),
288 thread.executionPersonality().value() == ExecutionPersonality::Linux32
289 ? "i686"
290 : g_pBuildTarget);
291 if (!PosixSubsystem::copyToUser(user, &snapshot, sizeof(snapshot))) {
292 SYSCALL_ERROR(BadAddress);
293 return result.finish(-1);
294 }
295 }
296 return result.finish(0);
297}
Memory-mapped file interface.
static MemoryMapManager & instance()
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)
Process * getParent()
Definition Process.h:620
size_t getNumThreads()
Definition Process.cc:1380
static ProcessorInformation & information()
static SharedPointer< T > tryAllocate(Args...)
T * get() const
Process * getParent() const
Definition Thread.h:340