summary refs log tree commit diff
path: root/ripple/website/pages/syscalls.adoc
blob: b717feb2b0b782f81f18bcd0e64ce11d521d820f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// SPDX-FileCopyrightText: edef <edef@unfathomable.blue>
// SPDX-License-Identifier: LicenseRef-NONE

= TODO

These have various sub-cases that vary significantly in complexity.

== mmap

== mprotect

== munmap

= Trivial

== arch_prctl(ARCH_SET_FS, ptr)

This is just `SETFS`.
Safe to pass through, but also trivial to implement.

== ioctl(fd, TCGETS, …)

gcc likes doing these on various fds, but `return -ENOTTY;` is enough of an implementation.

== close(fd)

Close a file descriptor.

== getrusage(RUSAGE_SELF, ptr)

gcc is rather eager to know how long it's taking.
We can probably stub this out safely.

== sysinfo(ptr)

I'm honestly not sure why gcc reads this, but we can probably stub it out.

== umask(mask)

Thread-unsafe, but we should probably bail if this gets invoked while multithreaded.
We just `mode &= ~mask` any filesystem call, and that's the only place this matters.

== prlimit(0, resource, NULL, ptr)

Retrieve a resource limit.
We can stub all of these out.

=== prlimit64(0, RLIMIT_STACK, NULL, ptr)

Retrieve the maximum stack size, presumably specifically of the main thread.

=== prlimit64(0, RLIMIT_AS, NULL, ptr)

Retrieve the maximum virtual memory size.

=== prlimit64(0, RLIMIT_RSS, NULL, ptr)

The maximum amount of resident memory.

=== prlimit64(0, RLIMIT_NOFILE, NULL, ptr)

The maximum amount of file descriptors.

= Easy

== brk

Should be fairly trivial.
`validate_prctl_map_addr` documents the requirements on address space layout.

== exit_group(status)

Kill the process, aka thread group.
Straightforward enough, although we do need to emit `SIGCHLD` appropriately.

== fcntl(fd, F_GETFD)

Grab the current flags for an fd.
Used only immediately preceding `fcntl(fd, F_SETFD, FD_CLOEXEC)`.

== fcntl(fd, F_SETFD, FD_CLOEXEC)

Set the `CLOEXEC` flag.

= This is where they start to trick you

== set_robust_list

The code for this itself isn't complex (we just store a pointer), but we have to actually do futex signalling on process death and execve.
Technically trivial if `futex` is never called.

== set_tid_address
The code for this itself isn't complex (we just store a pointer), but we have to actually do futex signalling on thread termination.
Technically trivial if `futex` is never called.

== getcwd(buf, len)

Read the current working directory path into a buffer.
Equivalent to `readlink("/proc/self/cwd", buf, len)`.
Probably bail if anything funny happens.

== readlink(path, buf, len)

Read the target of a symlink.

== unlink(path)

Remove a directory entry that is itself not a directory.
Equivalent to `unlinkat(AT_FDCWD, path, 0)`.

== chmod(path, mode)

Change the mode of an inode.

== pipe2(fds_out, flags)

Technically easy, but almost certainly being invoked for imminent IPC.

== read(fd, ptr, len)

Read bytes from a file descriptor.
Hard if it's IPC, easier if it's filesystem I/O.

== lseek(fd, offset, SEEK_SET)

Reposition the file offset, relative to the start of the file.

== lseek(fd, offset, SEEK_CUR)

Reposition the file offset, relative to the current offset.

== pread64(fd, buf, len, offset)

Equivalent to atomic `lseek(fd, offset, SEEK_SET); read(fd, buf, len)`, but without the side effects on the file descriptor offset.

== write(fd, ptr, len)

Write bytes to a file descriptor.
Hard if it's IPC, easier if it's filesystem I/O.

== execve(pathname, argv, envp)

Replace the current process image with another, loaded from a file.
Technically easy, modulo `O_CLOEXEC` handling, but almost certainly spells imminent IPC.

== access(path, mode)

Equivalent to atomic `int fd = open(path, mode); if (fd >= 0) close(fd);`, except if `eugid != ugid`.
Build systems are rarely setuid, so this distinction seems ok to bail on.

== openat(AT_FDCWD, path, flags, mode)

Filesystem I/O.

= This one's hard

== newfstatat(dirfd, name, stat_out, flag)

Filesystem I/O, primarily complicated by mtime and inode numbers.

== vfork()

Strictly less troubling than fork, but this spells imminent IPC.

== wait4(wstatus, 0, NULL)

Pure IPC.
This probably isn't much trouble if nothing unusual happens, however.

== rt_sigaction

Signals are complicated, underspecified, *and* IPC.
This is going to hurt.

== rt_sigprocmask

Signal mask manipulation is a doozy, but unblocking signals can deliver queued signals…