The Pedigree Project 0.1
memfd-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/mman.h>
10#include <sys/socket.h>
11#include <sys/stat.h>
12#include <sys/wait.h>
13
14int64_t mf_now(void) {
15 struct timespec now;
16 return clock_gettime(CLOCK_MONOTONIC, &now) ? -1 : (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
17}
18void mf_pause(int milliseconds) {
19 struct timespec pause = {milliseconds / 1000, (milliseconds % 1000) * 1000000L};
20 while (nanosleep(&pause, &pause) && errno == EINTR) {
21 }
22}
23int mf_wait(volatile int* flag, int milliseconds) {
24 const int64_t until = mf_now() + (int64_t)milliseconds * 1000000;
25 while (!__atomic_load_n(flag, __ATOMIC_ACQUIRE)) {
26 if (mf_now() >= until)
27 return -1;
28 mf_pause(2);
29 }
30 return 0;
31}
32int mf_reap(pid_t child, int milliseconds) {
33 const int64_t until = mf_now() + (int64_t)milliseconds * 1000000;
34 while (mf_now() < until) {
35 int status;
36 pid_t result = waitpid(child, &status, WNOHANG);
37 if (result == child) {
38 int code = WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
39 if (code)
40 fprintf(stderr, "MEMFD-CONTRACT: child=%ld status=%d\n", (long)child, code);
41 return code;
42 }
43 if (result < 0 && errno != EINTR)
44 return -1;
45 mf_pause(5);
46 }
47 fprintf(stderr, "MEMFD-CONTRACT: child=%ld timeout\n", (long)child);
48 kill(child, SIGKILL);
49 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
50 }
51 return -1;
52}
53int mf_byte(int socket, char expected) {
54 struct pollfd entry = {.fd = socket, .events = POLLIN};
55 int result;
56 do
57 result = poll(&entry, 1, 5000);
58 while (result < 0 && errno == EINTR);
59 char byte;
60 return result > 0 && read(socket, &byte, 1) == 1 && byte == expected ? 0 : -1;
61}
62int mf_send_fd(int socket, int fd) {
63 char byte = 'f';
64 struct iovec vector = {&byte, 1};
65 union {
66 struct cmsghdr align;
67 char bytes[CMSG_SPACE(sizeof(int))];
68 } control = {0};
69 struct msghdr message = {.msg_iov = &vector,
70 .msg_iovlen = 1,
71 .msg_control = control.bytes,
72 .msg_controllen = sizeof(control.bytes)};
73 struct cmsghdr* item = CMSG_FIRSTHDR(&message);
74 item->cmsg_level = SOL_SOCKET;
75 item->cmsg_type = SCM_RIGHTS;
76 item->cmsg_len = CMSG_LEN(sizeof(int));
77 memcpy(CMSG_DATA(item), &fd, sizeof(fd));
78 return sendmsg(socket, &message, 0) == 1 ? 0 : -1;
79}
80int mf_receive_fd(int socket) {
81 char byte;
82 struct iovec vector = {&byte, 1};
83 union {
84 struct cmsghdr align;
85 char bytes[CMSG_SPACE(sizeof(int))];
86 } control = {0};
87 struct msghdr message = {.msg_iov = &vector,
88 .msg_iovlen = 1,
89 .msg_control = control.bytes,
90 .msg_controllen = sizeof(control.bytes)};
91 if (recvmsg(socket, &message, MSG_CMSG_CLOEXEC) != 1 || (message.msg_flags & MSG_CTRUNC))
92 return -1;
93 struct cmsghdr* item = CMSG_FIRSTHDR(&message);
94 if (!item || item->cmsg_level != SOL_SOCKET || item->cmsg_type != SCM_RIGHTS ||
95 item->cmsg_len != CMSG_LEN(sizeof(int)))
96 return -1;
97 int fd;
98 memcpy(&fd, CMSG_DATA(item), sizeof(fd));
99 return fd;
100}
101int mf_make(size_t bytes) {
102 int fd = memfd_create("contract", MFD_ALLOW_SEALING);
103 if (fd >= 0 && ftruncate(fd, bytes)) {
104 close(fd);
105 return -1;
106 }
107 return fd;
108}
109int mf_size(int fd, off_t expected) {
110 struct stat st;
111 return !fstat(fd, &st) && st.st_size == expected ? 0 : -1;
112}
113int mf_contents(int fd, off_t offset, const void* expected, size_t length) {
114 char bytes[128];
115 return length <= sizeof(bytes) && pread(fd, bytes, length, offset) == (ssize_t)length &&
116 !memcmp(bytes, expected, length)
117 ? 0
118 : -1;
119}
120static int run(const char* name, int (*test)(void)) {
121 printf("MEMFD-CONTRACT: BEGIN %s\n", name);
122 fflush(stdout);
123 pid_t child = fork();
124 if (child < 0)
125 return -1;
126 if (!child) {
127 alarm(40);
128 int result = test();
129 fflush(stdout);
130 fflush(stderr);
131 _exit(result ? 1 : 0);
132 }
133 const int status = mf_reap(child, 45000);
134 printf("MEMFD-CONTRACT: %s %s status=%d\n", status ? "FAIL" : "PASS", name, status);
135 fflush(stdout);
136 return status;
137}
138int main(int argc, char** argv) {
139 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
140 return 1;
141 if (argc > 1 && !strcmp(argv[1], "memfd-exec"))
142 return memfd_exec(argc, argv);
143 const struct {
144 const char* name;
145 int (*test)(void);
146 } suites[] = {{"creation", memfd_creation},
147 {"seals", memfd_seals},
148 {"mappings", memfd_mappings},
149 {"lifetime", memfd_lifetime},
150 {"races", memfd_races}};
151 int selected = 0;
152 for (unsigned n = 0; n < sizeof(suites) / sizeof(suites[0]); ++n) {
153 if (argc > 1 && strcmp(argv[1], suites[n].name))
154 continue;
155 selected = 1;
156 if (run(suites[n].name, suites[n].test))
157 return 1;
158 }
159 if (!selected)
160 return 2;
161 puts("MEMFD-CONTRACT: END PASS");
162 return 0;
163}