The Pedigree Project 0.1
copies.c
1#define _GNU_SOURCE
2#include <limits.h>
3#include <string.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/mman.h>
8
9typedef ssize_t (*copy_fn)(pid_t, const struct iovec*, unsigned long, const struct iovec*,
10 unsigned long, unsigned long);
11
12static int scatter_target(int command, int report, void* argument) {
13 (void)argument;
14 int failed = 0;
15 unsigned char bytes[64];
16 for (size_t n = 0; n < sizeof(bytes); ++n)
17 bytes[n] = (unsigned char)(0x20 + n);
18 uintptr_t address = (uintptr_t)bytes;
19 CHECK(!pm_dumpable(1) && !pm_write(report, &address, sizeof(address)));
20 CHECK(!pm_receive(command, 'q'));
21 for (int n = 0; n < 5; ++n)
22 CHECK(bytes[3 + n] == (unsigned char)('A' + n));
23 for (int n = 0; n < 15; ++n)
24 CHECK(bytes[32 + n] == (unsigned char)('F' + n));
25 CHECK(bytes[2] == 0x22 && bytes[8] == 0x28 && bytes[47] == 0x4f);
26out:
27 return failed;
28}
29static int scatter(void) {
30 int failed = 0;
31 struct pm_peer peer = PM_PEER_INITIALIZER;
32 uintptr_t address;
33 unsigned char bytes[32], payload[20];
34 CHECK(!pm_spawn(&peer, scatter_target, NULL));
35 CHECK(!pm_read(peer.report, &address, sizeof(address)));
36 memset(bytes, 0, sizeof(bytes));
37 struct iovec local[] = {{NULL, 0}, {bytes, 7}, {bytes + 7, 13}};
38 struct iovec remote[] = {{(void*)(address + 3), 5}, {NULL, 0}, {(void*)(address + 32), 15}};
39 CHECK(process_vm_readv(peer.pid, local, 3, remote, 3, 0) == 20);
40 for (int n = 0; n < 5; ++n)
41 CHECK(bytes[n] == 0x23 + n);
42 for (int n = 0; n < 15; ++n)
43 CHECK(bytes[5 + n] == 0x40 + n);
44 for (int n = 0; n < 20; ++n)
45 payload[n] = (unsigned char)('A' + n);
46 struct iovec input[] = {{payload, 8}, {NULL, 0}, {payload + 8, 12}};
47 CHECK(process_vm_writev(peer.pid, input, 3, remote, 3, 0) == 20);
48 CHECK(process_vm_readv(peer.pid, local, 3, remote, 3, 0) == 20 && !memcmp(bytes, payload, 20));
49 CHECK(!pm_send(peer.command, 'q') && !pm_join(&peer));
50out:
51 pm_cleanup(&peer);
52 return failed;
53}
54static int admission(void) {
55 int failed = 0;
56 unsigned char source[16] = "cooperative", destination[16] = {0};
57 struct iovec local = {destination, sizeof(source)}, remote = {source, sizeof(source)};
58 struct iovec empty = {NULL, 0}, huge = {source, (size_t)SSIZE_MAX + 1};
59 void* bad = mmap(NULL, pm_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
60 CHECK(bad != MAP_FAILED);
61 copy_fn functions[] = {process_vm_readv, process_vm_writev};
62 for (size_t n = 0; n < 2; ++n) {
63 copy_fn copy = functions[n];
64 errno = 0;
65 CHECK(copy(0, NULL, 0, NULL, 0, 1) == -1 && errno == EINVAL);
66 errno = 0;
67 CHECK(copy(getpid(), &local, 1025, &remote, 1, 0) == -1 && errno == EINVAL);
68 errno = 0;
69 CHECK(copy(getpid(), bad, 1, &remote, 1, 0) == -1 && errno == EFAULT);
70 errno = 0;
71 CHECK(copy(getpid(), &huge, 1, &remote, 1, 0) == -1 && errno == EINVAL);
72 CHECK(copy(-1, NULL, 0, bad, ULONG_MAX, 0) == 0);
73 CHECK(copy(0, &empty, 1, bad, 1025, 0) == 0);
74 errno = 0;
75 CHECK(copy(getpid(), &local, 1, &remote, 1025, 0) == -1 && errno == EINVAL);
76 errno = 0;
77 CHECK(copy(0, &local, 1, bad, 1, 0) == -1 && errno == EFAULT);
78 errno = 0;
79 CHECK(copy(getpid(), &local, 1, &huge, 1, 0) == -1 && errno == EINVAL);
80 CHECK(copy(-1, &local, 1, &empty, 1, 0) == 0);
81 errno = 0;
82 CHECK(copy(0, &local, 1, &remote, 1, 0) == -1 && errno == ESRCH);
83 struct iovec inaccessible = {bad, 1};
84 errno = 0;
85 CHECK(copy(0, &inaccessible, 1, &remote, 1, 0) == -1 && errno == ESRCH);
86 }
87 CHECK(process_vm_readv(getpid(), &local, 1, &remote, 1, 0) == sizeof(source));
88 CHECK(!memcmp(source, destination, sizeof(source)));
89 memset(destination, 0x64, sizeof(destination));
90 CHECK(process_vm_writev(getpid(), &local, 1, &remote, 1, 0) == sizeof(source));
91 CHECK(!memcmp(source, destination, sizeof(source)));
92out:
93 if (bad != MAP_FAILED)
94 munmap(bad, pm_page);
95 return failed;
96}
97static int partial_pages(void) {
98 int failed = 0;
99 unsigned char *local = MAP_FAILED, *remote = MAP_FAILED;
100 local = mmap(NULL, 2 * pm_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
101 remote = mmap(NULL, 2 * pm_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
102 CHECK(local != MAP_FAILED && remote != MAP_FAILED);
103 memset(local, 0x35, 2 * pm_page);
104 memset(remote, 0x67, 2 * pm_page);
105 CHECK(!mprotect(remote + pm_page, pm_page, PROT_NONE));
106 CHECK(pm_copy(getpid(), local, remote, 2 * pm_page, 1) == (ssize_t)pm_page);
107 CHECK(remote[0] == 0x35 && remote[pm_page - 1] == 0x35);
108 memset(local, 0x89, 2 * pm_page);
109 CHECK(pm_copy(getpid(), local, remote, 2 * pm_page, 0) == (ssize_t)pm_page);
110 CHECK(local[0] == 0x35 && local[pm_page - 1] == 0x35 && local[pm_page] == 0x89);
111 CHECK(!mprotect(remote + pm_page, pm_page, PROT_READ | PROT_WRITE));
112 CHECK(remote[pm_page] == 0x67);
113 CHECK(!mprotect(local + pm_page, pm_page, PROT_NONE));
114 memset(remote, 0xab, pm_page);
115 CHECK(pm_copy(getpid(), local, remote, 2 * pm_page, 0) == (ssize_t)pm_page);
116 CHECK(local[0] == 0xab && local[pm_page - 1] == 0xab);
117 memset(local, 0xcd, pm_page);
118 CHECK(pm_copy(getpid(), local, remote, 2 * pm_page, 1) == (ssize_t)pm_page);
119 CHECK(remote[0] == 0xcd && remote[pm_page - 1] == 0xcd && remote[pm_page] == 0x67);
120 errno = 0;
121 CHECK(pm_copy(getpid(), local + pm_page, remote, 1, 1) == -1 && errno == EFAULT);
122out:
123 if (remote != MAP_FAILED)
124 munmap(remote, 2 * pm_page);
125 if (local != MAP_FAILED)
126 munmap(local, 2 * pm_page);
127 return failed;
128}
129int pm_copies(void) {
130 return admission() || scatter() || partial_pages();
131}