The Pedigree Project 0.1
clone3-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "clone3-syscalls.h"
3#include "pedigree/kernel/processor/Processor.h"
4#include "pedigree/kernel/processor/ProcessorInformation.h"
5#include "pedigree/kernel/processor/VirtualAddressSpace.h"
6#include "pedigree/kernel/syscallError.h"
7
8#include <sched.h>
9#include <signal.h>
10
11#include "PosixSubsystem.h"
12#include "linux-clone-abi.h"
13#include "system-syscalls.h"
14
15namespace {
16bool linuxUserRange(uint64_t address, uint64_t length) {
17 auto& space = Processor::information().getVirtualAddressSpace();
18 uint64_t limit = space.getKernelStart();
19 if (limit > 0x0000800000000000ULL)
20 limit = 0x0000800000000000ULL;
21 return address >= space.getUserStart() && address < limit && length <= limit - address;
22}
23} // namespace
24
25long posix_clone3(SyscallState& state, const LinuxCloneArgs* userArgs, size_t size) {
26 if (size > LinuxCloneAbi::MaximumSize) {
27 SYSCALL_ERROR(TooBig);
28 return -1;
29 }
30 if (size < LinuxCloneAbi::Version0Size) {
31 SYSCALL_ERROR(InvalidArgument);
32 return -1;
33 }
34 const uintptr_t userAddress = reinterpret_cast<uintptr_t>(userArgs);
35 if (!linuxUserRange(userAddress, size)) {
36 SYSCALL_ERROR(BadAddress);
37 return -1;
38 }
39
40 // Linux accepts future versions only when every unknown byte is zero.
41 // Read the extension in bounded pieces rather than consuming a kernel page.
42 for (size_t offset = sizeof(LinuxCloneArgs); offset < size;) {
43 uint8_t extension[64];
44 size_t count = size - offset;
45 if (count > sizeof(extension))
46 count = sizeof(extension);
47 if (!PosixSubsystem::copyFromUser(extension,
48 reinterpret_cast<const void*>(userAddress + offset), count)) {
49 SYSCALL_ERROR(BadAddress);
50 return -1;
51 }
52 for (size_t n = 0; n < count; ++n) {
53 if (extension[n]) {
54 SYSCALL_ERROR(TooBig);
55 return -1;
56 }
57 }
58 offset += count;
59 }
60
61 LinuxCloneArgs args{};
62 const size_t knownSize = size < sizeof(args) ? size : sizeof(args);
63 if (!PosixSubsystem::copyFromUser(&args, userArgs, knownSize)) {
64 SYSCALL_ERROR(BadAddress);
65 return -1;
66 }
67
68 constexpr uint64_t AllowedFlags =
69 CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VFORK | CLONE_THREAD |
70 CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID |
71 CLONE_CHILD_SETTID | CLONE_NEWUTS | CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWPID |
72 CLONE_NEWIPC | CLONE_NEWNET | LinuxCloneAbi::ClearSighand;
73 const bool clearSignalHandlers = args.flags & LinuxCloneAbi::ClearSighand;
74 const bool thread = args.flags & CLONE_THREAD;
75 if ((args.flags & ~AllowedFlags) || args.pidfd || args.set_tid || args.set_tid_size ||
76 args.cgroup || (clearSignalHandlers && (args.flags & (CLONE_SIGHAND | CLONE_THREAD))) ||
77 args.exit_signal != static_cast<uint64_t>(thread ? 0 : SIGCHLD)) {
78 SYSCALL_ERROR(InvalidArgument);
79 return -1;
80 }
81
82 const uint64_t maximumPointer = ~uintptr_t(0);
83 if (((args.flags & CLONE_PARENT_SETTID) && args.parent_tid > maximumPointer) ||
84 ((args.flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) &&
85 args.child_tid > maximumPointer)) {
86 SYSCALL_ERROR(BadAddress);
87 return -1;
88 }
89 if ((args.flags & CLONE_SETTLS) && args.tls > maximumPointer) {
90 SYSCALL_ERROR(NotEnoughPermissions);
91 return -1;
92 }
93
94 uintptr_t stackTop = 0;
95 if (args.stack || args.stack_size) {
96 // The extent may include a guard page below the usable stack.
97 if (!args.stack || !args.stack_size || !linuxUserRange(args.stack, args.stack_size)) {
98 SYSCALL_ERROR(InvalidArgument);
99 return -1;
100 }
101 stackTop = static_cast<uintptr_t>(args.stack + args.stack_size);
102 }
103
104 const unsigned long flags =
105 static_cast<unsigned long>((args.flags & ~LinuxCloneAbi::ClearSighand) | args.exit_signal);
106 return posix_clone(state, flags, reinterpret_cast<void*>(stackTop),
107 reinterpret_cast<int*>(static_cast<uintptr_t>(args.parent_tid)),
108 reinterpret_cast<int*>(static_cast<uintptr_t>(args.child_tid)),
109 static_cast<unsigned long>(args.tls), true, clearSignalHandlers);
110}
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static ProcessorInformation & information()