The Pedigree Project 0.1
pipe-transfer-contract-test/lifetime.c
1#define _GNU_SOURCE
2#include <poll.h>
3#include <signal.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/socket.h>
8
9static int queued_rights(void) {
10 int failed = 0, p[2] = {-1, -1}, pair[2] = {-1, -1}, gate[2] = {-1, -1}, alias = -1,
11 received = -1;
12 pid_t child = -1;
13 CHECK(!pipe(p) && !pipe(gate) && !socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
14 CHECK(!pt_fill(p[1], 16, 41) && (alias = dup(p[0])) >= 0);
15 CHECK((child = fork()) >= 0);
16 if (!child) {
17 alarm(8);
18 close(p[0]);
19 close(p[1]);
20 close(alias);
21 close(pair[0]);
22 close(gate[1]);
23 char byte;
24 if (read(gate[0], &byte, 1) != 1)
25 _exit(10);
26 int fd = pt_receive_fd(pair[1]), q[2];
27 if (fd < 0 || pipe(q) || splice(fd, NULL, q[1], NULL, 8, 0) != 8 || pt_read(q[0], 8, 0, 41) ||
28 pt_send_fd(pair[1], fd))
29 _exit(11);
30 close(fd);
31 _exit(0);
32 }
33 close(pair[1]);
34 pair[1] = -1;
35 close(gate[0]);
36 gate[0] = -1;
37 CHECK(!pt_send_fd(pair[0], alias));
38 CHECK(!close(p[0]) && !close(alias));
39 p[0] = alias = -1;
40 CHECK(write(gate[1], "g", 1) == 1);
41 CHECK((received = pt_receive_fd(pair[0])) >= 0);
42 CHECK(!pt_read(received, 8, 8, 41));
43 int status = pt_reap(child, 5000);
44 child = -1;
45 CHECK(!status);
46out:
47 if (child > 0) {
48 kill(child, SIGKILL);
49 pt_reap(child, 1000);
50 }
51 if (received >= 0)
52 close(received);
53 if (alias >= 0)
54 close(alias);
55 for (int n = 0; n < 2; ++n) {
56 if (p[n] >= 0)
57 close(p[n]);
58 if (pair[n] >= 0)
59 close(pair[n]);
60 if (gate[n] >= 0)
61 close(gate[n]);
62 }
63 return failed;
64}
65static int numeric_reuse(void) {
66 int failed = 0, p[2] = {-1, -1}, q[2] = {-1, -1}, pair[2] = {-1, -1}, replacement[2] = {-1, -1};
67 int input_alias = -1, output_alias = -1, running = 0;
68 struct pt_call call = {.kind = PT_SPLICE, .count = 32, .result = -2};
69 pthread_t worker;
70 CHECK(!pipe(p) && !pipe(q) && !socketpair(AF_UNIX, SOCK_STREAM, 0, pair) &&
71 !socketpair(AF_UNIX, SOCK_STREAM, 0, replacement));
72 CHECK(!pt_fill(p[1], 32, 59) && !pt_fill(q[1], 8, 83));
73 ssize_t capacity = pt_socket_full(pair[0]);
74 CHECK(capacity > 0 && (input_alias = dup(p[0])) >= 0 && (output_alias = dup(pair[0])) >= 0);
75 call.input = p[0];
76 call.output = pair[0];
77 CHECK(!pthread_create(&worker, NULL, pt_worker, &call));
78 running = 1;
79 CHECK(!pt_wait(&call.ready, 5000));
80 __atomic_store_n(&call.gate, 1, __ATOMIC_RELEASE);
81 CHECK(!pt_wait_readiness(p[0], POLLIN, 0));
82 CHECK(!__atomic_load_n(&call.done, __ATOMIC_ACQUIRE));
83 CHECK(dup2(q[0], p[0]) == p[0] && dup2(replacement[0], pair[0]) == pair[0]);
84 CHECK(!pt_read(pair[1], capacity, 0, PT_FILLER));
85 CHECK(!pt_join(worker, &call));
86 running = 0;
87 CHECK(call.result > 0 && call.result <= 32 && call.error == 0);
88 CHECK(!pt_read(pair[1], call.result, 0, 59));
89 CHECK(!pt_read(input_alias, 32 - call.result, call.result, 59));
90 CHECK(!pt_read(p[0], 8, 0, 83));
91 CHECK(!fcntl(replacement[1], F_SETFL, O_NONBLOCK));
92 char byte;
93 CHECK(read(replacement[1], &byte, 1) == -1 && errno == EAGAIN);
94out:
95 if (failed)
96 pt_diagnostic(&call);
97 if (running) {
98 shutdown(pair[1], SHUT_RD);
99 pt_join(worker, &call);
100 }
101 if (input_alias >= 0)
102 close(input_alias);
103 if (output_alias >= 0)
104 close(output_alias);
105 for (int n = 0; n < 2; ++n) {
106 if (p[n] >= 0)
107 close(p[n]);
108 if (q[n] >= 0)
109 close(q[n]);
110 if (pair[n] >= 0)
111 close(pair[n]);
112 if (replacement[n] >= 0)
113 close(replacement[n]);
114 }
115 return failed;
116}
117static int terminal_exit(int reservation) {
118 int failed = 0, p[2] = {-1, -1}, output[2] = {-1, -1};
119 pid_t child = -1;
120 ssize_t filler = 0;
121 CHECK(!pipe(p));
122 if (reservation) {
123 CHECK(!socketpair(AF_UNIX, SOCK_STREAM, 0, output) && !pt_fill(p[1], 32, 47));
124 CHECK((filler = pt_socket_full(output[1])) > 0);
125 } else
126 CHECK(!pipe(output));
127 CHECK((child = fork()) >= 0);
128 if (!child) {
129 alarm(8);
130 close(p[1]);
131 close(output[0]);
132 struct pt_call call = {.kind = reservation ? PT_SPLICE : PT_TEE,
133 .input = p[0],
134 .output = output[1],
135 .count = 32,
136 .result = -2};
137 pthread_t worker;
138 if (pthread_create(&worker, NULL, pt_worker, &call) || pt_wait(&call.ready, 3000))
139 _exit(20);
140 __atomic_store_n(&call.gate, 1, __ATOMIC_RELEASE);
141 if (reservation) {
142 if (pt_wait_readiness(p[0], POLLIN, 0) || __atomic_load_n(&call.done, __ATOMIC_ACQUIRE))
143 _exit(21);
144 } else if (pt_wait(&call.entered, 3000))
145 _exit(22);
146 /* Exit must retire any token or pair waiter admitted by the other thread. */
147 _exit(0);
148 }
149 close(p[0]);
150 p[0] = -1;
151 close(output[1]);
152 output[1] = -1;
153 int status = pt_reap(child, 5000);
154 child = -1;
155 CHECK(!status);
156 CHECK(write(p[1], "x", 1) == -1 && errno == EPIPE);
157 CHECK(!pt_read(output[0], filler, 0, PT_FILLER));
158 CHECK(!fcntl(output[0], F_SETFL, O_NONBLOCK));
159 char byte;
160 CHECK(read(output[0], &byte, 1) == 0);
161out:
162 if (child > 0) {
163 kill(child, SIGKILL);
164 pt_reap(child, 1000);
165 }
166 for (int n = 0; n < 2; ++n) {
167 if (p[n] >= 0)
168 close(p[n]);
169 if (output[n] >= 0)
170 close(output[n]);
171 }
172 return failed;
173}
174int pipe_transfer_lifetime(void) {
175 return queued_rights() || numeric_reuse() || terminal_exit(1) || terminal_exit(0);
176}