The Pedigree Project 0.1
remap-file-pages-contract-test/fixture.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <grp.h>
4#include <poll.h>
5#include <signal.h>
6#include <stdlib.h>
7#include <string.h>
8#include <time.h>
9#include <unistd.h>
10
11#include "contract.h"
12#include <sys/mman.h>
13#include <sys/resource.h>
14#include <sys/stat.h>
15#include <sys/wait.h>
16
17static int64_t now(void) {
18 struct timespec time;
19 return clock_gettime(CLOCK_MONOTONIC, &time) ? -1
20 : (int64_t)time.tv_sec * 1000000000 + time.tv_nsec;
21}
22
23int rp_reap(pid_t child, int milliseconds) {
24 int64_t begin = now();
25 int64_t deadline = begin + (int64_t)milliseconds * 1000000;
26 while (begin >= 0 && (begin = now()) >= 0 && begin < deadline) {
27 int status;
28 pid_t result = waitpid(child, &status, WNOHANG);
29 if (result == child)
30 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
31 if (result < 0 && errno != EINTR)
32 return -1;
33 struct timespec pause = {0, 5000000};
34 nanosleep(&pause, NULL);
35 }
36 kill(child, SIGKILL);
37 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
38 }
39 return -1;
40}
41
42int rp_send(int fd, char byte) {
43 ssize_t result;
44 do {
45 result = write(fd, &byte, 1);
46 } while (result < 0 && errno == EINTR);
47 return result == 1 ? 0 : -1;
48}
49
50int rp_receive(int fd, char expected) {
51 int64_t begin = now(), deadline = begin + 5000000000;
52 while (begin >= 0 && (begin = now()) >= 0 && begin < deadline) {
53 struct pollfd watch = {.fd = fd, .events = POLLIN};
54 int result = poll(&watch, 1, 100);
55 if (result < 0 && errno == EINTR)
56 continue;
57 if (result < 0)
58 return -1;
59 if (!result)
60 continue;
61 char byte;
62 ssize_t amount = read(fd, &byte, 1);
63 if (amount < 0 && errno == EINTR)
64 continue;
65 if (amount == 1 && byte == expected)
66 return 0;
67 errno = EIO;
68 return -1;
69 }
70 errno = ETIMEDOUT;
71 return -1;
72}
73
74static void expected_fault(int signal) {
75 (void)signal;
76 _exit(0);
77}
78
79int rp_fault(void* address, int expected_signal, int write_access) {
80 pid_t child = fork();
81 if (child < 0)
82 return -1;
83 if (!child) {
84 alarm(3);
85 struct sigaction action = {.sa_handler = expected_fault};
86 sigemptyset(&action.sa_mask);
87 if (sigaction(expected_signal, &action, NULL))
88 _exit(2);
89 volatile unsigned char* byte = address;
90 if (write_access)
91 *byte = 0xb9;
92 else {
93 volatile unsigned char value = *byte;
94 (void)value;
95 }
96 _exit(1);
97 }
98 int result = rp_reap(child, 4000);
99 if (result)
100 fprintf(stderr, "REMAP-FILE-PAGES-CONTRACT: expected signal=%d address=%p write=%d status=%d\n",
101 expected_signal, address, write_access, result);
102 return result;
103}
104
105unsigned char rp_pattern(size_t offset) {
106 return (unsigned char)(0x31 + (offset / rp_page) * 23 + (offset % rp_page) * 37 +
107 ((offset % rp_page) >> 8) * 11);
108}
109
110int rp_matches(const volatile unsigned char* bytes, size_t offset, size_t length, int zero) {
111 for (size_t n = 0; n < length; ++n) {
112 unsigned char expected = zero ? 0 : rp_pattern(offset + n);
113 unsigned char actual = bytes[n];
114 if (actual != expected) {
115 fprintf(stderr, "REMAP-FILE-PAGES-CONTRACT: offset=%zu got=%u expected=%u\n", offset + n,
116 (unsigned)actual, (unsigned)expected);
117 return 0;
118 }
119 }
120 return 1;
121}
122
123int rp_contents(int fd, size_t offset, size_t length, int zero) {
124 unsigned char bytes[512];
125 while (length) {
126 size_t amount = length < sizeof(bytes) ? length : sizeof(bytes);
127 if (pread(fd, bytes, amount, offset) != (ssize_t)amount ||
128 !rp_matches(bytes, offset, amount, zero))
129 return -1;
130 offset += amount;
131 length -= amount;
132 }
133 return 0;
134}
135
136int rp_size(int fd, size_t size) {
137 struct stat metadata;
138 if (fstat(fd, &metadata))
139 return -1;
140 if (metadata.st_size == (off_t)size)
141 return 0;
142 fprintf(stderr, "REMAP-FILE-PAGES-CONTRACT: size=%lld expected=%zu\n",
143 (long long)metadata.st_size, size);
144 return -1;
145}
146
147int rp_resident(void* address, size_t pages, unsigned bits, const char* stage) {
148 unsigned char vector[8];
149 memset(vector, 0xa5, sizeof(vector));
150 if (pages > sizeof(vector) || mincore(address, pages * rp_page, vector)) {
151 fprintf(stderr, "REMAP-FILE-PAGES-CONTRACT: %s mincore errno=%d\n", stage, errno);
152 return -1;
153 }
154 for (size_t n = 0; n < pages; ++n) {
155 if ((vector[n] & 1) != ((bits >> n) & 1)) {
156 fprintf(stderr, "REMAP-FILE-PAGES-CONTRACT: %s page=%zu resident=%u expected=%u\n", stage, n,
157 vector[n], (bits >> n) & 1);
158 return -1;
159 }
160 }
161 return 0;
162}
163
164int rp_create(struct rp_file* file, int backend) {
165 static unsigned sequence;
166 file->fd = -1;
167 file->backend = backend;
168 file->path[0] = 0;
169 if (backend == RP_MEMFD)
170 file->fd = memfd_create("remap-file-pages", MFD_CLOEXEC | MFD_ALLOW_SEALING);
171 else {
172 snprintf(file->path, sizeof(file->path), "%s/remap-pages-%ld-%u",
173 backend == RP_RAMFS ? "/tmp" : "", (long)getpid(), ++sequence);
174 file->fd = open(file->path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
175 if (file->fd < 0) {
176 fprintf(stderr, "REMAP-FILE-PAGES-CONTRACT: create %s errno=%d\n", file->path, errno);
177 file->path[0] = 0;
178 }
179 }
180 if (file->fd < 0)
181 return -1;
182 unsigned char* bytes = malloc(rp_page);
183 int error = !bytes || ftruncate(file->fd, 8 * rp_page);
184 for (size_t offset = 0; !error && offset < 8 * rp_page; offset += rp_page) {
185 for (size_t n = 0; n < rp_page; ++n)
186 bytes[n] = rp_pattern(offset + n);
187 error = pwrite(file->fd, bytes, rp_page, offset) != (ssize_t)rp_page;
188 }
189 free(bytes);
190 if (error) {
191 int saved = errno;
192 rp_close(file);
193 errno = saved;
194 return -1;
195 }
196 return 0;
197}
198
199int rp_open_alias(const struct rp_file* file) {
200 if (file->backend == RP_EXT2) {
201 char alias[160];
202 snprintf(alias, sizeof(alias), "%s.alias", file->path);
203 if (link(file->path, alias))
204 return -1;
205 int fd = open(alias, O_RDWR | O_CLOEXEC);
206 int saved = errno;
207 if (unlink(alias)) {
208 saved = errno;
209 if (fd >= 0)
210 close(fd);
211 fd = -1;
212 }
213 errno = saved;
214 return fd;
215 }
216 return file->path[0] ? open(file->path, O_RDWR | O_CLOEXEC) : dup(file->fd);
217}
218
219void rp_close(struct rp_file* file) {
220 if (file->fd >= 0)
221 close(file->fd);
222 if (file->path[0])
223 unlink(file->path);
224 file->fd = -1;
225 file->path[0] = 0;
226}
227
228int rp_limit(size_t bytes) {
229 struct rlimit limit;
230 if (getrlimit(RLIMIT_MEMLOCK, &limit))
231 return -1;
232 limit.rlim_cur = bytes;
233 return setrlimit(RLIMIT_MEMLOCK, &limit);
234}
235
236int rp_unprivileged(void) {
237 if (!geteuid() && (setgroups(0, NULL) || setgid(65534) || setuid(65534)))
238 return -1;
239 if (geteuid())
240 return 0;
241 errno = EPERM;
242 return -1;
243}