The Pedigree Project 0.1
process-memory-contract-test/main.c
1#define _GNU_SOURCE
2#include <poll.h>
3#include <signal.h>
4#include <string.h>
5#include <time.h>
6#include <unistd.h>
7
8#include "contract.h"
9#include <sys/prctl.h>
10#include <sys/wait.h>
11
12size_t pm_page;
13
14int64_t pm_now(void) {
15 struct timespec now;
16 return clock_gettime(CLOCK_MONOTONIC, &now) ? -1 : (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
17}
18int pm_reap(pid_t pid, int milliseconds) {
19 int64_t start = pm_now(), now, deadline = start + (int64_t)milliseconds * 1000000;
20 while (start >= 0 && (now = pm_now()) >= 0 && now < deadline) {
21 int status;
22 pid_t result = waitpid(pid, &status, WNOHANG);
23 if (result == pid)
24 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
25 if (result < 0 && errno != EINTR)
26 return -1;
27 struct timespec pause = {0, 5000000};
28 nanosleep(&pause, NULL);
29 }
30 kill(pid, SIGKILL);
31 while (waitpid(pid, NULL, 0) < 0 && errno == EINTR) {
32 }
33 return -1;
34}
35int pm_read(int fd, void* buffer, size_t length) {
36 unsigned char* bytes = buffer;
37 while (length) {
38 struct pollfd ready = {.fd = fd, .events = POLLIN};
39 int result;
40 do
41 result = poll(&ready, 1, 10000);
42 while (result < 0 && errno == EINTR);
43 if (result <= 0)
44 return -1;
45 ssize_t count = read(fd, bytes, length);
46 if (count < 0 && errno == EINTR)
47 continue;
48 if (count <= 0)
49 return -1;
50 bytes += count;
51 length -= count;
52 }
53 return 0;
54}
55int pm_write(int fd, const void* buffer, size_t length) {
56 const unsigned char* bytes = buffer;
57 while (length) {
58 ssize_t count = write(fd, bytes, length);
59 if (count < 0 && errno == EINTR)
60 continue;
61 if (count <= 0)
62 return -1;
63 bytes += count;
64 length -= count;
65 }
66 return 0;
67}
68int pm_send(int fd, char command) {
69 return pm_write(fd, &command, 1);
70}
71int pm_receive(int fd, char command) {
72 char actual;
73 return pm_read(fd, &actual, 1) || actual != command ? -1 : 0;
74}
75int pm_spawn(struct pm_peer* peer, int (*body)(int, int, void*), void* argument) {
76 int command[2], report[2];
77 if (pipe(command))
78 return -1;
79 if (pipe(report)) {
80 close(command[0]);
81 close(command[1]);
82 return -1;
83 }
84 pid_t pid = fork();
85 if (!pid) {
86 close(command[1]);
87 close(report[0]);
88 alarm(30);
89 int result = body(command[0], report[1], argument);
90 fflush(stdout);
91 fflush(stderr);
92 _exit(result ? 1 : 0);
93 }
94 close(command[0]);
95 close(report[1]);
96 if (pid < 0) {
97 close(command[1]);
98 close(report[0]);
99 return -1;
100 }
101 peer->pid = pid;
102 peer->command = command[1];
103 peer->report = report[0];
104 return 0;
105}
106int pm_join(struct pm_peer* peer) {
107 int result = pm_reap(peer->pid, 10000);
108 if (result)
109 fprintf(stderr, "cooperative child pid=%d status=%d\n", peer->pid, result);
110 peer->pid = -1;
111 if (peer->command >= 0)
112 close(peer->command);
113 if (peer->report >= 0)
114 close(peer->report);
115 peer->command = peer->report = -1;
116 return result;
117}
118void pm_cleanup(struct pm_peer* peer) {
119 if (peer->pid > 0) {
120 kill(peer->pid, SIGKILL);
121 pm_reap(peer->pid, 1000);
122 }
123 if (peer->command >= 0)
124 close(peer->command);
125 if (peer->report >= 0)
126 close(peer->report);
127 peer->pid = peer->command = peer->report = -1;
128}
129int pm_isolate(int (*body)(void)) {
130 pid_t pid = fork();
131 if (!pid) {
132 alarm(30);
133 int result = body();
134 fflush(stdout);
135 fflush(stderr);
136 _exit(result ? 1 : 0);
137 }
138 return pid > 0 ? pm_reap(pid, 32000) : -1;
139}
140ssize_t pm_copy(pid_t pid, void* local, const void* remote, size_t length, int write_remote) {
141 struct iovec here = {local, length}, there = {(void*)remote, length};
142 return write_remote ? process_vm_writev(pid, &here, 1, &there, 1, 0)
143 : process_vm_readv(pid, &here, 1, &there, 1, 0);
144}
145int pm_dumpable(int value) {
146 return prctl(PR_SET_DUMPABLE, (unsigned long)value, 0UL, 0UL, 0UL);
147}
148static int run(const char* name, int (*body)(void)) {
149 printf("PROCESS-MEMORY-CONTRACT: BEGIN %s\n", name);
150 fflush(stdout);
151 pid_t pid = fork();
152 if (!pid) {
153 alarm(40);
154 int result = body();
155 fflush(stdout);
156 fflush(stderr);
157 _exit(result ? 1 : 0);
158 }
159 int result = pid > 0 ? pm_reap(pid, 45000) : -1;
160 printf("PROCESS-MEMORY-CONTRACT: %s %s status=%d\n", result ? "FAIL" : "PASS", name, result);
161 fflush(stdout);
162 return result;
163}
164int main(int argc, char** argv) {
165 pm_page = (size_t)sysconf(_SC_PAGESIZE);
166 if (pm_page < 512 || pm_page > 65536 || (pm_page & (pm_page - 1)))
167 return 2;
168 signal(SIGPIPE, SIG_IGN);
169 if (argc > 1 && !strcmp(argv[1], "memory-exec"))
170 return pm_exec(argc, argv);
171 if (argc > 1 && !strcmp(argv[1], "fs-exec"))
172 return pm_fs_exec(argc, argv);
173 if (argc > 2)
174 return 2;
175 static const struct {
176 const char* name;
177 int (*body)(void);
178 } families[] = {{"copies", pm_copies},
179 {"mappings", pm_mappings},
180 {"lifetime", pm_lifetime},
181 {"credentials", pm_credentials},
182 {"filesystem", pm_filesystem}};
183 int found = 0;
184 for (size_t n = 0; n < sizeof(families) / sizeof(families[0]); ++n) {
185 if (argc == 2 && strcmp(argv[1], "all") && strcmp(argv[1], families[n].name))
186 continue;
187 found = 1;
188 if (run(families[n].name, families[n].body))
189 return 1;
190 }
191 if (!found)
192 return 2;
193 puts("PROCESS-MEMORY-CONTRACT: END PASS");
194 return 0;
195}