The Pedigree Project 0.1
names.c
1#define _GNU_SOURCE
2#include <pthread.h>
3#include <sched.h>
4#include <string.h>
5#include <unistd.h>
6
7#include "contract.h"
8#include <sys/mman.h>
9#include <sys/utsname.h>
10
11_Static_assert(sizeof(struct utsname) == 390, "Linux utsname ABI");
12
13static int setter_contract(int domain) {
14 int failed = 0;
15 int (*setter)(const char*, size_t) = domain ? setdomainname : sethostname;
16 unsigned char expected[65] = {0};
17 char full[65];
18 memset(full, domain ? 'd' : 'h', sizeof(full));
19 void* mapping =
20 mmap(NULL, ns_page * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
21 struct utsname value;
22 CHECK(mapping != MAP_FAILED);
23 CHECK(mprotect((char*)mapping + ns_page, ns_page, PROT_NONE) == 0);
24 CHECK(setter(full, 64) == 0);
25 CHECK(uname(&value) == 0);
26 memcpy(expected, full, 64);
27 CHECK(!memcmp(domain ? value.domainname : value.nodename, expected, 65));
28 errno = 0;
29 CHECK(setter(full, 65) == -1 && errno == EINVAL);
30 errno = 0;
31 CHECK(setter(full, SIZE_MAX) == -1 && errno == EINVAL);
32 char* edge = (char*)mapping + ns_page - 2;
33 edge[0] = 'x';
34 edge[1] = 'y';
35 errno = 0;
36 CHECK(setter(edge, 4) == -1 && errno == EFAULT);
37 CHECK(uname(&value) == 0);
38 CHECK(!memcmp(domain ? value.domainname : value.nodename, expected, 65));
39
40 const unsigned char binary[] = {'a', 0, 'b', 0xff};
41 memset(expected, 0, sizeof(expected));
42 memcpy(expected, binary, sizeof(binary));
43 CHECK(setter((const char*)binary, sizeof(binary)) == 0);
44 CHECK(uname(&value) == 0);
45 CHECK(!memcmp(domain ? value.domainname : value.nodename, expected, 65));
46 /* musl's size_t argument reaches a Linux signed 32-bit length parameter. */
47 CHECK(setter(NULL, (size_t)UINT64_C(0x100000000)) == 0);
48 CHECK(setter(full, (size_t)UINT64_C(0x100000002)) == 0);
49 memset(expected, 0, sizeof(expected));
50 memcpy(expected, full, 2);
51 CHECK(uname(&value) == 0);
52 CHECK(!memcmp(domain ? value.domainname : value.nodename, expected, 65));
53 CHECK(setter(NULL, 0) == 0);
54 memset(expected, 0, sizeof(expected));
55 CHECK(uname(&value) == 0);
56 CHECK(!memcmp(domain ? value.domainname : value.nodename, expected, 65));
57out:
58 if (mapping != MAP_FAILED)
59 munmap(mapping, ns_page * 2);
60 return failed;
61}
62
64 pthread_barrier_t start;
65 char first[64], second[64];
66 int result;
67};
68static void* replace_names(void* argument) {
69 struct name_writer* state = argument;
70 pthread_barrier_wait(&state->start);
71 for (int i = 0; i < 128; ++i) {
72 if (sethostname(i & 1 ? state->first : state->second, 64)) {
73 state->result = 1;
74 break;
75 }
76 }
77 return NULL;
78}
79
80static int snapshots(void) {
81 int failed = 0, initialized = 0, started = 0;
82 pthread_t worker;
83 struct name_writer state = {0};
84 memset(state.first, 'a', sizeof(state.first));
85 memset(state.second, 'b', sizeof(state.second));
86 CHECK(ns_set("snapshot", "fixed-domain") == 0);
87 CHECK(sethostname(state.first, 64) == 0);
88 CHECK(pthread_barrier_init(&state.start, NULL, 2) == 0);
89 initialized = 1;
90 CHECK(pthread_create(&worker, NULL, replace_names, &state) == 0);
91 started = 1;
92 pthread_barrier_wait(&state.start);
93 for (int i = 0; i < 128; ++i) {
94 struct utsname value;
95 CHECK(uname(&value) == 0);
96 CHECK((!memcmp(value.nodename, state.first, 64) || !memcmp(value.nodename, state.second, 64)) &&
97 value.nodename[64] == 0);
98 CHECK(!strcmp(value.domainname, "fixed-domain"));
99 }
100out:
101 if (started && (pthread_join(worker, NULL) || state.result))
102 failed = 1;
103 if (initialized)
104 pthread_barrier_destroy(&state.start);
105 return failed;
106}
107
108int ns_names(void) {
109 int failed = 0;
110 struct utsname before, after;
111 unsigned char output[sizeof(struct utsname) + 2];
112 void* readonly = MAP_FAILED;
113 struct ns_identity first, second;
114 CHECK(uname(&before) == 0);
115 CHECK(ns_path_identity("/proc/thread-self/ns/uts", &first) == 0);
116 CHECK(unshare(0) == 0);
117 CHECK(ns_path_identity("/proc/thread-self/ns/uts", &second) == 0 && ns_same(first, second));
118 CHECK(setter_contract(0) == 0 && setter_contract(1) == 0);
119 CHECK(ns_set("alpha", "domain") == 0);
120 char host[3] = {'?', '?', '?'}, domain[7] = {0};
121 CHECK(gethostname(host, 2) == 0 && host[0] == 'a' && host[1] == 0 && host[2] == '?');
122 CHECK(gethostname(host + 2, 0) == 0 && host[2] == '?');
123 errno = 0;
124 CHECK(getdomainname(domain, 6) == -1 && errno == EINVAL);
125 errno = 0;
126 CHECK(getdomainname(domain, 0) == -1 && errno == EINVAL);
127 CHECK(getdomainname(domain, sizeof(domain)) == 0 && !strcmp(domain, "domain"));
128 memset(output, 0xcc, sizeof(output));
129 CHECK(uname((struct utsname*)(output + 1)) == 0);
130 CHECK(output[0] == 0xcc && output[sizeof(output) - 1] == 0xcc);
131 memcpy(&after, output + 1, sizeof(after));
132 CHECK(!memcmp(before.sysname, after.sysname, 65) && !memcmp(before.release, after.release, 65) &&
133 !memcmp(before.version, after.version, 65) && !memcmp(before.machine, after.machine, 65));
134 CHECK(ns_expect("alpha", "domain") == 0);
135 readonly = mmap(NULL, ns_page, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
136 CHECK(readonly != MAP_FAILED);
137 errno = 0;
138 CHECK(uname(readonly) == -1 && errno == EFAULT);
139 CHECK(ns_expect("alpha", "domain") == 0);
140 CHECK(snapshots() == 0);
141out:
142 if (readonly != MAP_FAILED)
143 munmap(readonly, ns_page);
144 return failed;
145}