The Pedigree Project 0.1
memory-lock-contract-test/main.c
1#define _GNU_SOURCE
2#include <grp.h>
3#include <poll.h>
4#include <signal.h>
5#include <string.h>
6#include <time.h>
7#include <unistd.h>
8
9#include "contract.h"
10#include <sys/mman.h>
11#include <sys/resource.h>
12#include <sys/wait.h>
13
14size_t ml_page;
15
16static int64_t now(void) {
17 struct timespec time;
18 return clock_gettime(CLOCK_MONOTONIC, &time) ? -1
19 : (int64_t)time.tv_sec * 1000000000 + time.tv_nsec;
20}
21
22int ml_reap(pid_t child, int milliseconds) {
23 int64_t deadline = now() + (int64_t)milliseconds * 1000000;
24 while (now() < deadline) {
25 int status;
26 pid_t result = waitpid(child, &status, WNOHANG);
27 if (result == child)
28 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
29 if (result < 0 && errno != EINTR)
30 return -1;
31 struct timespec pause = {0, 5000000};
32 nanosleep(&pause, NULL);
33 }
34 kill(child, SIGKILL);
35 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
36 }
37 return -1;
38}
39
40int ml_send(int fd, char byte) {
41 ssize_t result;
42 do {
43 result = write(fd, &byte, 1);
44 } while (result < 0 && errno == EINTR);
45 return result == 1 ? 0 : -1;
46}
47
48int ml_receive(int fd, char expected) {
49 int64_t deadline = now() + 5000000000;
50 while (now() < deadline) {
51 struct pollfd watch = {.fd = fd, .events = POLLIN};
52 int result = poll(&watch, 1, 100);
53 if (result < 0 && errno == EINTR)
54 continue;
55 if (result < 0)
56 return -1;
57 if (!result)
58 continue;
59 char byte;
60 ssize_t amount = read(fd, &byte, 1);
61 if (amount < 0 && errno == EINTR)
62 continue;
63 if (amount == 1 && byte == expected)
64 return 0;
65 errno = EIO;
66 return -1;
67 }
68 errno = ETIMEDOUT;
69 return -1;
70}
71
72int ml_limit(size_t bytes) {
73 struct rlimit limit;
74 if (getrlimit(RLIMIT_MEMLOCK, &limit))
75 return -1;
76 limit.rlim_cur = bytes;
77 return setrlimit(RLIMIT_MEMLOCK, &limit);
78}
79
80int ml_unprivileged(void) {
81 if (!geteuid() && (setgroups(0, NULL) || setgid(65534) || setuid(65534)))
82 return -1;
83 if (geteuid())
84 return 0;
85 errno = EPERM;
86 return -1;
87}
88
89int ml_resident(void* address, size_t pages, unsigned bits, const char* stage) {
90 unsigned char vector[8];
91 memset(vector, 0xa5, sizeof(vector));
92 if (pages > sizeof(vector) || mincore(address, pages * ml_page, vector)) {
93 fprintf(stderr, "MEMORY-LOCK-CONTRACT: %s mincore failed errno=%d\n", stage, errno);
94 return -1;
95 }
96 for (size_t n = 0; n < pages; ++n) {
97 if ((vector[n] & 1) != ((bits >> n) & 1)) {
98 fprintf(stderr, "MEMORY-LOCK-CONTRACT: %s page=%zu resident=%u expected=%u\n", stage, n,
99 vector[n], (bits >> n) & 1);
100 return -1;
101 }
102 }
103 return 0;
104}
105
106static int run(const char* name, int (*function)(void)) {
107 printf("MEMORY-LOCK-CONTRACT: BEGIN %s\n", name);
108 fflush(stdout);
109 pid_t child = fork();
110 if (child < 0)
111 return -1;
112 if (!child) {
113 alarm(40);
114 int result = function();
115 fflush(stdout);
116 fflush(stderr);
117 _exit(result ? 1 : 0);
118 }
119 int result = ml_reap(child, 45000);
120 printf("MEMORY-LOCK-CONTRACT: %s %s status=%d\n", result ? "FAIL" : "PASS", name, result);
121 fflush(stdout);
122 return result;
123}
124
125int main(int argc, char** argv) {
126 ml_page = (size_t)sysconf(_SC_PAGESIZE);
127 if (ml_page < 512 || ml_page > 65536 || (ml_page & (ml_page - 1)))
128 return 2;
129 if (argc > 1 && !strcmp(argv[1], "memory-lock-exec"))
130 return ml_exec(argc, argv);
131 signal(SIGPIPE, SIG_IGN);
132 static const struct {
133 const char* name;
134 int (*function)(void);
135 } suites[] = {{"limits", ml_limits},
136 {"ranges", ml_ranges},
137 {"managed", ml_managed},
138 {"raw", ml_raw},
139 {"lifetime", ml_lifetime}};
140 int selected = 0;
141 for (size_t n = 0; n < sizeof(suites) / sizeof(suites[0]); ++n) {
142 if (argc > 1 && strcmp(argv[1], suites[n].name))
143 continue;
144 selected = 1;
145 if (run(suites[n].name, suites[n].function))
146 return 1;
147 }
148 if (!selected)
149 return 2;
150 puts("MEMORY-LOCK-CONTRACT: END PASS");
151 return 0;
152}