The Pedigree Project 0.1
process-memory-contract-test/lifetime.c
1#define _GNU_SOURCE
2#include <pthread.h>
3#include <stdlib.h>
4#include <string.h>
5#include <unistd.h>
6
7#include "contract.h"
8#include <sys/mman.h>
9
11 int command, report;
12};
14 pid_t tid;
15 uintptr_t address;
16};
17
18static void* target_thread(void* opaque) {
19 struct thread_arguments* args = opaque;
20 volatile unsigned char bytes[16] = {0x43};
21 struct thread_address address = {gettid(), (uintptr_t)bytes};
22 int failed = pm_write(args->report, &address, sizeof(address)) ||
23 pm_receive(args->command, 't') || bytes[0] != 0x54;
24 return (void*)(uintptr_t)failed;
25}
26static int thread_target(int command, int report, void* ignored) {
27 (void)ignored;
28 int failed = 0, live = 0;
29 pthread_t thread;
30 void* result;
31 volatile unsigned char main_byte = 0x65;
32 uintptr_t main_address = (uintptr_t)&main_byte;
33 struct thread_arguments args = {command, report};
34 CHECK(!pm_dumpable(1) && !pm_write(report, &main_address, sizeof(main_address)));
35 CHECK(!pthread_create(&thread, NULL, target_thread, &args));
36 live = 1;
37 CHECK(!pthread_join(thread, &result));
38 live = 0;
39 CHECK(result == NULL && !pm_send(report, 'd'));
40 CHECK(!pm_receive(command, 'q'));
41out:
42 if (live) {
43 /* Failure ends the isolated process, including the cooperating thread. */
44 return 1;
45 }
46 return failed;
47}
48static int nonleader(void) {
49 int failed = 0;
50 struct pm_peer peer = PM_PEER_INITIALIZER;
51 struct thread_address address;
52 uintptr_t main_address;
53 unsigned char byte;
54 CHECK(!pm_spawn(&peer, thread_target, NULL));
55 CHECK(!pm_read(peer.report, &main_address, sizeof(main_address)));
56 CHECK(!pm_read(peer.report, &address, sizeof(address)));
57 CHECK(address.tid > 0 && address.tid != peer.pid);
58 CHECK(pm_copy(address.tid, &byte, (void*)address.address, 1, 0) == 1 && byte == 0x43);
59 byte = 0x54;
60 CHECK(pm_copy(address.tid, &byte, (void*)address.address, 1, 1) == 1);
61 CHECK(!pm_send(peer.command, 't') && !pm_receive(peer.report, 'd'));
62 errno = 0;
63 CHECK(pm_copy(address.tid, &byte, (void*)address.address, 1, 0) == -1 && errno == ESRCH);
64 CHECK(pm_copy(peer.pid, &byte, (void*)main_address, 1, 0) == 1 && byte == 0x65);
65 CHECK(!pm_send(peer.command, 'q') && !pm_join(&peer));
66out:
67 pm_cleanup(&peer);
68 return failed;
69}
70static int image_target(int command, int report, void* ignored) {
71 (void)ignored;
72 int failed = 0;
73 unsigned char* bytes =
74 mmap(NULL, 3 * pm_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
75 CHECK(bytes != MAP_FAILED);
76 memset(bytes, 0x71, pm_page);
77 memset(bytes + pm_page, 0x82, pm_page);
78 memset(bytes + 2 * pm_page, 0x93, pm_page);
79 uintptr_t address = (uintptr_t)bytes;
80 CHECK(!pm_dumpable(1) && !pm_write(report, &address, sizeof(address)));
81 CHECK(!pm_receive(command, 'u') && !munmap(bytes + pm_page, pm_page));
82 CHECK(!pm_send(report, 'u') && !pm_receive(command, 'x'));
83 char input[24], output[24];
84 snprintf(input, sizeof(input), "%d", command);
85 snprintf(output, sizeof(output), "%d", report);
86 execl(PM_APP, PM_APP, "memory-exec", input, output, (char*)NULL);
87 CHECK(0);
88out:
89 if (bytes != MAP_FAILED)
90 munmap(bytes, 3 * pm_page);
91 return failed;
92}
93int pm_exec(int argc, char** argv) {
94 if (argc != 4)
95 return 2;
96 int failed = 0, command = atoi(argv[2]), report = atoi(argv[3]);
97 unsigned char* bytes =
98 mmap(NULL, pm_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
99 CHECK(bytes != MAP_FAILED);
100 memset(bytes, 0xe1, pm_page);
101 uintptr_t address = (uintptr_t)bytes;
102 CHECK(!pm_write(report, &address, sizeof(address)) && !pm_receive(command, 'q'));
103 CHECK(bytes[0] == 0xe2 && bytes[1] == 0xe1);
104out:
105 if (bytes != MAP_FAILED)
106 munmap(bytes, pm_page);
107 return failed;
108}
109static int images(void) {
110 int failed = 0;
111 struct pm_peer peer = PM_PEER_INITIALIZER;
112 uintptr_t address, replacement;
113 unsigned char* local =
114 mmap(NULL, 3 * pm_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
115 CHECK(local != MAP_FAILED && !pm_spawn(&peer, image_target, NULL));
116 CHECK(!pm_read(peer.report, &address, sizeof(address)));
117 CHECK(pm_copy(peer.pid, local, (void*)address, 3 * pm_page, 0) == (ssize_t)(3 * pm_page));
118 CHECK(local[0] == 0x71 && local[pm_page] == 0x82 && local[2 * pm_page] == 0x93);
119 CHECK(!pm_send(peer.command, 'u') && !pm_receive(peer.report, 'u'));
120 memset(local, 0xa4, 3 * pm_page);
121 CHECK(pm_copy(peer.pid, local, (void*)address, 3 * pm_page, 0) == (ssize_t)pm_page);
122 CHECK(local[0] == 0x71 && local[pm_page] == 0xa4 && local[2 * pm_page] == 0xa4);
123 errno = 0;
124 CHECK(pm_copy(peer.pid, local, (void*)(address + pm_page), 1, 1) == -1 && errno == EFAULT);
125 CHECK(pm_copy(peer.pid, local, (void*)(address + 2 * pm_page), 1, 0) == 1 && local[0] == 0x93);
126 CHECK(!pm_send(peer.command, 'x') && !pm_read(peer.report, &replacement, sizeof(replacement)));
127 CHECK(pm_copy(peer.pid, local, (void*)replacement, pm_page, 0) == (ssize_t)pm_page);
128 for (size_t n = 0; n < pm_page; ++n)
129 CHECK(local[n] == 0xe1);
130 local[0] = 0xe2;
131 CHECK(pm_copy(peer.pid, local, (void*)replacement, 1, 1) == 1);
132 pid_t departed = peer.pid;
133 CHECK(!pm_send(peer.command, 'q') && !pm_join(&peer));
134 errno = 0;
135 CHECK(pm_copy(departed, local, (void*)replacement, 1, 0) == -1 && errno == ESRCH);
136out:
137 pm_cleanup(&peer);
138 if (local != MAP_FAILED)
139 munmap(local, 3 * pm_page);
140 return failed;
141}
142int pm_lifetime(void) {
143 return nonleader() || images();
144}