The Pedigree Project 0.1
membership.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <pthread.h>
4#include <sched.h>
5#include <signal.h>
6#include <string.h>
7#include <unistd.h>
8
9#include "contract.h"
10#include <sys/mman.h>
11
12static int sharing_child(int command, int report, void* argument) {
13 int original = *(int*)argument;
14 if (ns_expect("initial", "initial-domain") || ns_send(report, 'R') || ns_receive(command, 'M') ||
15 ns_set("shared", "shared-domain") || ns_send(report, 'M') || ns_receive(command, 'U') ||
16 unshare(CLONE_NEWUTS) || ns_expect("shared", "shared-domain") ||
17 ns_set("private", "private-domain") || ns_send(report, 'U') || ns_receive(command, 'J') ||
18 setns(original, 0) || ns_expect("shared", "shared-domain") ||
19 ns_set("joined", "joined-domain") || ns_send(report, 'J'))
20 return 1;
21 return 0;
22}
23
24static int fork_membership(void) {
25 int failed = 0, original = -1;
26 struct ns_peer child = NS_PEER_INITIALIZER;
27 CHECK(ns_set("initial", "initial-domain") == 0);
28 original = open("/proc/thread-self/ns/uts", O_RDONLY | O_CLOEXEC);
29 CHECK(original >= 0);
30 CHECK(ns_spawn(&child, sharing_child, &original) == 0);
31 CHECK(ns_receive(child.report, 'R') == 0);
32 CHECK(ns_send(child.command, 'M') == 0 && ns_receive(child.report, 'M') == 0);
33 CHECK(ns_expect("shared", "shared-domain") == 0);
34 CHECK(ns_send(child.command, 'U') == 0 && ns_receive(child.report, 'U') == 0);
35 CHECK(ns_expect("shared", "shared-domain") == 0);
36 CHECK(ns_send(child.command, 'J') == 0 && ns_receive(child.report, 'J') == 0);
37 CHECK(ns_expect("joined", "joined-domain") == 0);
38 CHECK(ns_join(&child) == 0);
39out:
40 ns_cleanup(&child);
41 if (original >= 0)
42 close(original);
43 return failed;
44}
45
46static void* inherited_thread(void* argument) {
47 const char* expected = argument;
48 return (void*)(intptr_t)ns_expect(expected, expected);
49}
50static int inherited_child(int command, int report, void* argument) {
51 (void)command;
52 (void)report;
53 return ns_expect(argument, argument);
54}
55
57 int command[2], report[2];
58 int original;
59 int result;
60};
61static void* separate_thread(void* argument) {
62 struct thread_membership* state = argument;
63 struct ns_peer child = NS_PEER_INITIALIZER;
64 pthread_t descendant;
65 void* result = NULL;
66 state->result = 1;
67 if (ns_expect("leader", "leader") || unshare(CLONE_NEWUTS) || ns_set("worker", "worker") ||
68 ns_send(state->report[1], 'B') || ns_receive(state->command[0], 'I'))
69 return NULL;
70 if (pthread_create(&descendant, NULL, inherited_thread, "worker") ||
71 pthread_join(descendant, &result) || result || ns_spawn(&child, inherited_child, "worker") ||
72 ns_join(&child))
73 goto out;
74 if (ns_send(state->report[1], 'I') || ns_receive(state->command[0], 'J') ||
75 setns(state->original, CLONE_NEWUTS) || ns_expect("leader", "leader") ||
76 ns_set("rejoined", "rejoined") || ns_send(state->report[1], 'J'))
77 goto out;
78 state->result = 0;
79out:
80 ns_cleanup(&child);
81 return NULL;
82}
83
84static int thread_memberships(void) {
85 int failed = 0, started = 0;
86 struct thread_membership state = {.command = {-1, -1}, .report = {-1, -1}, .original = -1};
87 pthread_t worker, descendant;
88 void* result = NULL;
89 CHECK(ns_set("leader", "leader") == 0);
90 state.original = open("/proc/thread-self/ns/uts", O_RDONLY | O_CLOEXEC);
91 CHECK(state.original >= 0);
92 CHECK(pipe(state.command) == 0 && pipe(state.report) == 0);
93 CHECK(pthread_create(&worker, NULL, separate_thread, &state) == 0);
94 started = 1;
95 CHECK(ns_receive(state.report[0], 'B') == 0);
96 CHECK(ns_expect("leader", "leader") == 0);
97 CHECK(pthread_create(&descendant, NULL, inherited_thread, "leader") == 0);
98 CHECK(pthread_join(descendant, &result) == 0 && !result);
99 CHECK(ns_send(state.command[1], 'I') == 0 && ns_receive(state.report[0], 'I') == 0);
100 CHECK(ns_expect("leader", "leader") == 0);
101 CHECK(ns_send(state.command[1], 'J') == 0 && ns_receive(state.report[0], 'J') == 0);
102 CHECK(ns_expect("rejoined", "rejoined") == 0);
103out:
104 if (failed && state.command[1] >= 0) {
105 close(state.command[1]);
106 state.command[1] = -1;
107 }
108 if (started && (pthread_join(worker, NULL) || state.result))
109 failed = 1;
110 for (int i = 0; i < 2; ++i) {
111 if (state.command[i] >= 0)
112 close(state.command[i]);
113 if (state.report[i] >= 0)
114 close(state.report[i]);
115 }
116 if (state.original >= 0)
117 close(state.original);
118 return failed;
119}
120
122 struct ns_identity original;
123 int report;
124};
125static int new_uts_child(void* argument) {
126 struct clone_state* state = argument;
127 struct ns_identity own;
128 if (ns_expect("clone-parent", "clone-parent") ||
129 ns_path_identity("/proc/thread-self/ns/uts", &own) || ns_same(own, state->original) ||
130 ns_set("clone-child", "clone-child") || ns_send(state->report, 'C'))
131 return 1;
132 return 0;
133}
134static int clone_membership(void) {
135 int failed = 0, report[2] = {-1, -1};
136 pid_t child = -1;
137 const size_t length = ns_page * 16;
138 void* stack = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
139 struct clone_state state;
140 CHECK(stack != MAP_FAILED);
141 CHECK(ns_set("clone-parent", "clone-parent") == 0);
142 CHECK(ns_path_identity("/proc/thread-self/ns/uts", &state.original) == 0);
143 CHECK(pipe(report) == 0);
144 state.report = report[1];
145 child = clone(new_uts_child, (char*)stack + length, CLONE_NEWUTS | SIGCHLD, &state);
146 CHECK(child > 0);
147 CHECK(ns_receive(report[0], 'C') == 0);
148 CHECK(ns_expect("clone-parent", "clone-parent") == 0);
149 int status = ns_reap(child, 10000);
150 child = -1;
151 CHECK(status == 0);
152out:
153 if (child > 0) {
154 kill(child, SIGKILL);
155 ns_reap(child, 1000);
156 }
157 for (int i = 0; i < 2; ++i)
158 if (report[i] >= 0)
159 close(report[i]);
160 if (stack != MAP_FAILED)
161 munmap(stack, length);
162 return failed;
163}
164
165int ns_membership(void) {
166 return fork_membership() || thread_memberships() || clone_membership();
167}