The Pedigree Project 0.1
execveat-syscalls.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "execveat-syscalls.h"
3#include "pedigree/kernel/process/Process.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/Processor.h"
6#include "pedigree/kernel/processor/ProcessorInformation.h"
7#include "pedigree/kernel/syscallError.h"
8#include "pedigree/kernel/utilities/StaticString.h"
9
10#include <fcntl.h>
11#include <limits.h>
12
13#include "PosixSubsystem.h"
14#include "file-syscalls.h"
16
17namespace {
18struct ExecResult {
19 ExecResult(int result)
20 : value(result),
21 error(result < 0 ? Processor::information().getCurrentThread()->getErrno() : 0) {}
22 int value;
23 int error;
24};
25
26bool snapshotArguments(const char** pointers, Vector<String>& output, size_t& remaining) {
27 uintptr_t cursor = reinterpret_cast<uintptr_t>(pointers);
28 while (cursor) {
29 const char* argument = nullptr;
30 if (!PosixSubsystem::copyFromUser(&argument, reinterpret_cast<void*>(cursor),
31 sizeof(argument))) {
32 SYSCALL_ERROR(BadAddress);
33 return false;
34 }
35 if (!argument)
36 return true;
37 if (remaining <= sizeof(uintptr_t)) {
38 SYSCALL_ERROR(TooBig);
39 return false;
40 }
41 remaining -= sizeof(uintptr_t);
42 String value;
43 const auto result = PosixSubsystem::copyUserString(argument, value, remaining);
44 if (result != PosixSubsystem::UserStringSuccess) {
45 syscallError(result == PosixSubsystem::UserStringBadAddress ? Error::BadAddress
46 : Error::TooBig);
47 return false;
48 }
49 remaining -= value.length() + 1;
50 output.pushBack(value);
51 if (cursor > ~uintptr_t(0) - sizeof(uintptr_t)) {
52 SYSCALL_ERROR(BadAddress);
53 return false;
54 }
55 cursor += sizeof(uintptr_t);
56 }
57 return true;
58}
59
60ExecResult execveat(int dirfd, const char* path, const char** argv, const char** env, int flags,
61 SyscallState& state) {
62 if (flags & ~(AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW)) {
63 SYSCALL_ERROR(InvalidArgument);
64 return -1;
65 }
66 String pathname;
67 const auto copied = PosixSubsystem::copyUserString(path, pathname, PATH_MAX);
68 if (copied != PosixSubsystem::UserStringSuccess) {
69 syscallError(copied == PosixSubsystem::UserStringBadAddress ? Error::BadAddress
70 : Error::NameTooLong);
71 return -1;
72 }
73 if (!pathname.length() && !(flags & AT_EMPTY_PATH)) {
74 SYSCALL_ERROR(DoesNotExist);
75 return -1;
76 }
77
78 auto* process = Processor::information().getCurrentThread()->getParent();
79 auto* subsystem = static_cast<PosixSubsystem*>(process->getSubsystem());
80 ResolvedPath targetLease;
81 DescriptorLease descriptor;
83 File* target = nullptr;
84 const bool absolute = pathname.length() && pathname[0] == '/';
85 const bool descriptorPath = !absolute && dirfd != AT_FDCWD;
86 if (descriptorPath) {
87 if (dirfd < 0 || !subsystem->acquireFileDescriptor(dirfd, descriptor)) {
88 SYSCALL_ERROR(BadFileDescriptor);
89 return -1;
90 }
91 target = descriptor->getFile();
92 start = descriptor->openingPath();
93 if (!target || (pathname.length() && (!target->isDirectory() || !start))) {
94 syscallError(pathname.length() ? Error::NotADirectory : Error::PermissionDenied);
95 return -1;
96 }
97 targetLease.retain(start);
98 } else if (!pathname.length()) {
99 auto context = process->acquireFilesystemContext();
101 if (!context || !context->snapshot(snapshot) || !snapshot.cwd) {
102 SYSCALL_ERROR(DoesNotExist);
103 return -1;
104 }
105 targetLease.retain(snapshot.cwd);
106 target = targetLease.get();
107 }
108
109 if (pathname.length()) {
110 String resolvedPath;
111 normalisePath(resolvedPath, pathname.cstr());
112 target = subsystem->findFileRetained(resolvedPath, targetLease, start);
113 }
114 if (!target) {
115 if (!Processor::information().getCurrentThread()->getErrno())
116 SYSCALL_ERROR(DoesNotExist);
117 return -1;
118 }
119 if ((flags & AT_SYMLINK_NOFOLLOW) && target->isSymlink()) {
120 SYSCALL_ERROR(LoopExists);
121 return -1;
122 }
123 if (target->isSymlink()) {
124 target = subsystem->followFile(targetLease);
125 if (!target)
126 return -1;
127 }
128 if (target->isDirectory() || target->isPipe() || target->isFifo() || target->isSocket()) {
129 SYSCALL_ERROR(PermissionDenied);
130 return -1;
131 }
132
133 String originalName(pathname);
134 if (descriptorPath) {
135 NormalStaticString prefix("/dev/fd/");
136 prefix.append(static_cast<size_t>(dirfd));
137 originalName.assign(prefix, prefix.length());
138 if (pathname.length()) {
139 originalName += "/";
140 originalName += pathname;
141 }
142 }
143 Vector<String> arguments, environment;
144 {
146 size_t remaining = PosixSubsystem::MaximumExecArgumentBytes - 2 * sizeof(uintptr_t);
147 if (originalName.length() >= remaining) {
148 SYSCALL_ERROR(TooBig);
149 return -1;
150 }
151 remaining -= originalName.length() + 1;
152 if (!snapshotArguments(argv, arguments, remaining) ||
153 !snapshotArguments(env, environment, remaining))
154 return -1;
155 }
156 const bool inaccessible = descriptorPath && (descriptor->fdflags & FD_CLOEXEC);
157 const auto opening = targetLease.path()
158 ? targetLease.path()
159 : (descriptor ? descriptor->openingPath() : FilesystemPathRef());
160 const bool invoked =
161 opening
162 ? subsystem->invoke(opening, originalName, arguments, environment, state, inaccessible)
163 : subsystem->invoke(target, originalName, arguments, environment, state, inaccessible);
164 return invoked ? 0 : -1;
165}
166
167} // namespace
168
169int posix_execveat(int dirfd, const char* path, const char** argv, const char** env, int flags,
170 SyscallState& state) {
171 const ExecResult result = execveat(dirfd, path, argv, env, flags, state);
172 syscallError(result.error);
173 return result.value;
174}
Memory-mapped file interface.
int fdflags
File descriptor flags (fcntl)
Definition File.h:75
virtual bool isSocket() const
Definition File.cc:816
virtual bool isSymlink()
Definition File.cc:800
virtual bool isDirectory()
Definition File.cc:804
virtual bool isFifo() const
Definition File.cc:812
virtual bool isPipe() const
Definition File.cc:808
static MemoryMapManager & instance()
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static UserStringResult copyUserString(const char *userString, String &copy, size_t maxLength)
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
A vector / dynamic array.
Definition Vector.h:33