AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
execve.c
Go to the documentation of this file.
1
13
14#include <core/system.h>
15#include <core/string.h>
16#include <core/arch.h>
17#include <sys/proc.h>
18#include <sys/sched.h>
19#include <mm/pmap.h>
20#include <sys/binfmt.h>
21#include <sys/uaccess.h>
22#include <mm/mm.h>
23#include <aquila/fcntl.h>
24#include <aquila/auxv.h>
25
26/* the most that the arguments and the environment may take, strings and
27 * pointers together */
28#define EXEC_ARG_MAX (128 * 1024)
29
31static void exec_strings_free(char **strs, int count)
32{
33 if (!strs)
34 return;
35
36 for (int i = 0; i < count; ++i)
37 kfree(strs[i]);
38
39 kfree(strs);
40}
41
50static int exec_strings_get(char * const u_strs[], char *buf, size_t *total, int *count, char ***ref)
51{
52 int cnt = 0, err = 0;
53 char *u;
54
55 /* count the strings */
56 for (; u_strs; ++cnt) {
57 if (copyin(&u, &u_strs[cnt], sizeof(u)))
58 return -EFAULT;
59
60 if (!u)
61 break;
62
63 if ((cnt + 1) * sizeof(char *) > EXEC_ARG_MAX)
64 return -E2BIG;
65 }
66
67 char **strs = kmalloc((cnt + 1) * sizeof(char *), &M_BUFFER, M_ZERO);
68
69 if (!strs)
70 return -ENOMEM;
71
72 for (int i = 0; i < cnt; ++i) {
73 size_t room = EXEC_ARG_MAX - *total;
74 long len;
75
76 /* the array may have changed since it was counted */
77 if (copyin(&u, &u_strs[i], sizeof(u)) || !u) {
78 err = -EFAULT;
79 goto error;
80 }
81
82 if (!room || (len = copyinstr(buf, u, room)) == -ENAMETOOLONG) {
83 err = -E2BIG;
84 goto error;
85 }
86
87 if (len < 0) {
88 err = len;
89 goto error;
90 }
91
92 *total += len + 1 + sizeof(char *);
93
94 if (*total > EXEC_ARG_MAX) {
95 err = -E2BIG;
96 goto error;
97 }
98
99 if (!(strs[i] = kmalloc(len + 1, &M_BUFFER, 0))) {
100 err = -ENOMEM;
101 goto error;
102 }
103
104 memcpy(strs[i], buf, len + 1);
105 }
106
107 *count = cnt;
108 *ref = strs;
109 return 0;
110
111error:
112 exec_strings_free(strs, cnt);
113 return err;
114}
115
116/* how many interpreters in a row may be scripts themselves */
117#define EXEC_SCRIPT_DEPTH 4
118
125static int exec_script(struct proc *proc, const char *path, char **interp, char **arg)
126{
127 struct uio uio = PROC_UIO(proc);
128 struct vnode *vnode = NULL;
129 char buf[128];
130 int err;
131
132 *interp = *arg = NULL;
133
134 if ((err = vfs_lookup(path, &uio, &vnode)))
135 return err;
136
137 /* like any program, it must be a regular file that the process may
138 * execute */
139 if (!S_ISREG(vnode->mode))
140 err = -EACCES;
141 else
142 err = vfs_access(vnode, &uio, VFS_X);
143
144 ssize_t len = err ? 0 : vfs_read(vnode, 0, sizeof(buf) - 1, buf);
146
147 if (err)
148 return err;
149
150 if (len < 2 || buf[0] != '#' || buf[1] != '!')
151 return 0;
152
153 buf[len] = '\0';
154
155 char *end = buf + 2;
156 while (*end && *end != '\n')
157 ++end;
158
159 /* the whole line has to fit in the buffer */
160 if (!*end)
161 return -ENOEXEC;
162
163 *end = '\0';
164
165 char *p = buf + 2;
166 while (*p == ' ' || *p == '\t')
167 ++p;
168
169 char *name = p;
170 while (*p && *p != ' ' && *p != '\t')
171 ++p;
172
173 if (p == name)
174 return -ENOEXEC;
175
176 if (*p)
177 *p++ = '\0';
178
179 while (*p == ' ' || *p == '\t')
180 ++p;
181
182 /* trailing blanks aren't part of the argument */
183 while (end > p && (end[-1] == ' ' || end[-1] == '\t'))
184 *--end = '\0';
185
186 if (!(*interp = strdup(name)))
187 return -ENOMEM;
188
189 if (*p && !(*arg = strdup(p))) {
190 kfree(*interp);
191 *interp = NULL;
192 return -ENOMEM;
193 }
194
195 return 0;
196}
197
204static int exec_script_args(char *interp, char *arg, const char *path, char ***argp, int *argc)
205{
206 int count = 1 + !!arg + 1 + (*argc > 1 ? *argc - 1 : 0);
207 char **args = kmalloc((count + 1) * sizeof(char *), &M_BUFFER, M_ZERO);
208 char *script = strdup(path);
209
210 if (!args || !script) {
211 kfree(args);
212 kfree(script);
213 return -ENOMEM;
214 }
215
216 int i = 0;
217 args[i++] = interp;
218 if (arg)
219 args[i++] = arg;
220 args[i++] = script;
221
222 for (int j = 1; j < *argc; ++j)
223 args[i++] = (*argp)[j];
224
225 if (*argc)
226 kfree((*argp)[0]);
227 kfree(*argp);
228
229 *argp = args;
230 *argc = count;
231 return 0;
232}
233
251static uintptr_t exec_stack_build(struct proc *proc, int argc, char * const argv[], int envc, char * const envp[])
252{
253 /* what the program is told about itself (aquila/auxv.h); there are
254 * no separate effective IDs */
255 const uintptr_t auxv[][2] = {
256 {AT_PAGESZ, PAGE_SIZE},
257 {AT_ENTRY, proc->entry},
258 {AT_UID, proc->uid},
259 {AT_EUID, proc->uid},
260 {AT_GID, proc->gid},
261 {AT_EGID, proc->gid},
262 {AT_NULL, 0},
263 };
264 const int nauxv = sizeof(auxv) / sizeof(auxv[0]);
265
266 /* the strings go at the top; the caller bounds their total size
267 * (EXEC_ARG_MAX), well below the stack's */
268 size_t strings = 0;
269
270 for (int i = 0; i < argc; ++i)
271 strings += strlen(argv[i]) + 1;
272
273 for (int i = 0; i < envc; ++i)
274 strings += strlen(envp[i]) + 1;
275
276 char *str = (char *) (USER_STACK - strings);
277
278 /* the words below them: argc, the two arrays with their NULLs, and
279 * the auxiliary vector's pairs */
280 size_t words = 1 + (argc + 1) + (envc + 1) + 2 * nauxv;
281 uintptr_t sp = ((uintptr_t) str - words * sizeof(uintptr_t)) & ~(uintptr_t) 15;
282 uintptr_t *w = (uintptr_t *) sp;
283
284 *w++ = argc;
285
286 for (int i = 0; i < argc; ++i) {
287 strcpy(str, argv[i]);
288 *w++ = (uintptr_t) str;
289 str += strlen(argv[i]) + 1;
290 }
291
292 *w++ = 0;
293
294 for (int i = 0; i < envc; ++i) {
295 strcpy(str, envp[i]);
296 *w++ = (uintptr_t) str;
297 str += strlen(envp[i]) + 1;
298 }
299
300 *w++ = 0;
301
302 for (int i = 0; i < nauxv; ++i) {
303 *w++ = auxv[i][0];
304 *w++ = auxv[i][1];
305 }
306
307 return sp;
308}
309
313static void exec_start(struct proc *proc, int argc, char * const argv[], int envc, char * const envp[])
314{
315 struct thread *thread = (struct thread *) proc->threads.head->value;
316 uintptr_t sp = exec_stack_build(proc, argc, argv, envc, envp);
317
319}
320
332void init_execve(struct proc *proc, int argc, char * const argv[], int envc, char * const envp[])
333{
334 /* init's address space is made current, and init's thread the
335 * current thread, so that the page faults of the writes to its stack
336 * bring its pages in */
337 curthread = (struct thread *) proc->threads.head->value;
339
340 exec_start(proc, argc, argv, envc, envp);
341
342 curthread = NULL;
343}
344
358int proc_execve(struct thread *thread, const char *fn, char * const _argp[], char * const _envp[])
359{
360 int err = 0;
361 struct proc *proc = thread->owner;
362
363 int argc = 0, envc = 0;
364 size_t total = 0;
365
366 char **argp = NULL;
367 char **envp = NULL;
368
369 /* copy the arguments and the environment into the kernel */
370 char *buf = kmalloc(EXEC_ARG_MAX, &M_BUFFER, 0);
371
372 if (!buf)
373 return -ENOMEM;
374
375 if (!(err = exec_strings_get(_argp, buf, &total, &argc, &argp)))
376 err = exec_strings_get(_envp, buf, &total, &envc, &envp);
377
378 kfree(buf);
379
380 if (err)
381 goto free_resources;
382
383 /* a script runs as its interpreter */
384 for (int depth = 0;; ++depth) {
385 char *interp, *arg;
386
387 if ((err = exec_script(proc, fn, &interp, &arg)))
388 goto free_resources;
389
390 if (!interp)
391 break;
392
393 if (depth == EXEC_SCRIPT_DEPTH ||
394 (err = exec_script_args(interp, arg, fn, &argp, &argc))) {
395 kfree(interp);
396 kfree(arg);
397 err = err ? err : -ELOOP;
398 goto free_resources;
399 }
400
401 fn = interp;
402 }
403
404 if ((err = binfmt_load(proc, fn, NULL))) {
406 /* the failure came after the old program was destroyed:
407 * there is nothing to return to, so the process is killed */
408 exec_strings_free(argp, argc);
409 exec_strings_free(envp, envc);
410
411 proc->exit = PROC_EXIT(SIGSEGV, SIGSEGV);
413 arch_sleep();
414 }
415
416 goto free_resources;
417 }
418
419 thread->spawned = 0;
420
421 /* close the descriptors marked close-on-exec */
422 for (int fd = 0; fd < FDS_COUNT; ++fd)
423 if (proc_fd_file(proc, fd) && (proc->fds[fd].flags & FD_CLOEXEC))
425
426 /* caught signals go back to their default action, and ignored ones
427 * stay ignored (POSIX); the signal mask and the pending signals stay */
428 for (int i = 0; i <= SIG_MAX; ++i) {
429 if (proc->sigaction[i].sa_handler != SIG_IGN)
430 proc->sigaction[i].sa_handler = SIG_DFL;
431
432 proc->sigaction[i].sa_mask = 0;
433 proc->sigaction[i].sa_flags = 0;
434 }
435
436 exec_start(proc, argc, argp, envc, envp);
437
438free_resources:
439 exec_strings_free(argp, argc);
440 exec_strings_free(envp, envc);
441
442 return err;
443}
#define USER_STACK
Definition arch.h:60
#define PAGE_SIZE
Definition system.h:7
void arch_exec_start(struct thread *thread, uintptr_t entry, uintptr_t sp)
Sets up a thread to start a new program when it next returns to user mode: at its entry point,...
Definition execve.c:15
void arch_sleep(void)
Puts the current thread to sleep.
Definition sched.c:101
int binfmt_load(struct proc *proc, const char *path, struct proc **ref)
Loads a program into a process, for execve().
Definition binfmt.c:124
int vfs_close(struct vnode *vnode)
Drops a reference to a vnode.
Definition close.c:18
#define FDS_COUNT
Definition config.h:46
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
#define M_ZERO
Definition kvmem.h:26
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
void * memcpy(void *dst, const void *src, size_t n)
Copies n bytes; dst and src must not overlap.
Definition libc.c:11
int vfs_lookup(const char *path, struct uio *uio, struct vnode **ref)
Finds the vnode a path names.
Definition lookup.c:349
struct pmap * pmap_switch(struct pmap *pmap)
Switches this CPU to an address space's page tables.
Definition pmap.c:164
int proc_fd_close(struct proc *proc, int fd)
Closes a descriptor: it is free again, and its reference to the open file is dropped.
Definition proc.c:578
int proc_execve(struct thread *thread, const char *fn, char *const argv[], char *const env[])
Runs another program in a thread's process, for execve().
Definition execve.c:358
struct file * proc_fd_file(struct proc *proc, int fd)
Finds the open file a descriptor refers to.
Definition proc.c:560
#define PROC_EXIT(info, code)
Definition proc.h:207
void init_execve(struct proc *proc, int argc, char *const argv[], int envc, char *const envp[])
Sets up init, the first process, to start its program: its initial stack with its arguments and envir...
Definition execve.c:332
void proc_kill(struct proc *proc)
Ends a process: its threads end, its descriptors are closed, its memory is freed, its children go to ...
Definition proc.c:375
#define PROC_UIO(proc)
Definition proc.h:210
ssize_t vfs_read(struct vnode *vnode, off_t off, size_t size, void *buf)
Reads from a vnode into a kernel buffer.
Definition read.c:52
#define curthread
Definition sched.h:12
#define SIG_MAX
Definition signal.h:16
#define SIG_DFL
Definition signal.h:26
#define SIG_IGN
Definition signal.h:27
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
A file descriptor: the open file it refers to (fs/file.c, shared with the descriptors that dup() and ...
Definition proc.h:47
int flags
Definition proc.h:49
A process.
Definition proc.h:75
struct aquila_sigaction sigaction[SIG_MAX+1]
its signal actions
Definition proc.h:157
struct vm_space vm_space
its address space
Definition proc.h:129
int exit
its exit status (PROC_EXIT())
Definition proc.h:160
gid_t gid
Definition proc.h:119
struct fd * fds
its file descriptors (FDS_COUNT of them)
Definition proc.h:87
uid_t uid
its user and group IDs
Definition proc.h:118
struct queue threads
its threads
Definition proc.h:136
uintptr_t entry
its program's entry point
Definition proc.h:126
void * value
Definition queue.h:20
size_t count
Definition queue.h:30
struct qnode * head
Definition queue.h:31
A thread.
Definition thread.h:23
int spawned
whether it has run yet
Definition thread.h:87
struct proc * owner
its process
Definition thread.h:31
Who does a filesystem operation: where its paths start, and its credentials and umask.
Definition vfs.h:60
struct pmap * pmap
its page tables (the architecture's)
Definition vm.h:27
struct queue vm_entries
its mappings (map entries), in address order
Definition vm.h:30
A file in memory (a vnode): what its inode says, and the kernel's own state.
Definition vfs.h:113
void * p
the filesystem's own data
Definition vfs.h:130
mode_t mode
Definition vfs.h:118
static int exec_script(struct proc *proc, const char *path, char **interp, char **arg)
Reads the interpreter of a script, from "#!interpreter [argument]" on its first line.
Definition execve.c:125
#define EXEC_SCRIPT_DEPTH
Definition execve.c:117
static uintptr_t exec_stack_build(struct proc *proc, int argc, char *const argv[], int envc, char *const envp[])
Builds a new program's initial stack, at the top of its user stack, as the System V ABI has it on eve...
Definition execve.c:251
static void exec_strings_free(char **strs, int count)
Frees an array of count strings that exec_strings_get() made.
Definition execve.c:31
#define EXEC_ARG_MAX
Definition execve.c:28
static void exec_start(struct proc *proc, int argc, char *const argv[], int envc, char *const envp[])
Starts a process's thread at its program's entry point, with its initial stack built from argv and en...
Definition execve.c:313
static int exec_script_args(char *interp, char *arg, const char *path, char ***argp, int *argc)
Makes the arguments of a script's interpreter: the interpreter, its argument from the script if there...
Definition execve.c:204
static int exec_strings_get(char *const u_strs[], char *buf, size_t *total, int *count, char ***ref)
Copies a NULL-terminated array of strings (the arguments or the environment) from user memory into a ...
Definition execve.c:50
long int ssize_t
Definition types.h:12
int vfs_access(struct vnode *vnode, struct uio *uio, int mode)
Checks whether a user may access a vnode in a way: the owner's permissions apply to its owner,...
Definition vfs.c:114
#define VFS_X
Definition vfs.h:297