The Pedigree Project 0.1
copyout.c
1#define _GNU_SOURCE
2#include <string.h>
3#include <unistd.h>
4
5#include "contract.h"
6#include <sys/mman.h>
7#include <sys/syscall.h>
8
9static int exited(struct cw_child* child, int status, struct rusage* usage, siginfo_t* info) {
10 memset(usage, 0xa5, sizeof(*usage));
11 return cw_spawn(child, -1, (uid_t)-1, status) || cw_send(child->command, 'B') ||
12 cw_receive(child->report, 'B') || cw_send(child->command, 'E') ||
13 cw_raw_waitid(P_PID, child->pid, info, WEXITED | WNOWAIT, usage)
14 ? -1
15 : 0;
16}
17
18static int fault_case(int use_wait4, int first_output, int no_wait) {
19 int failed = 0, status = 0x7777;
20 struct cw_child child = CW_CHILD_INIT;
21 struct rusage usage, expected;
22 siginfo_t info;
23 void* bad = mmap(NULL, cw_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
24 CHECK(bad != MAP_FAILED);
25 CHECK(exited(&child, 67, &expected, &info) == 0 && !cw_usage(&expected));
26 memset(&info, 0xa5, sizeof(info));
27 memset(&usage, 0xa5, sizeof(usage));
28 errno = 0;
29 long result;
30 if (use_wait4) {
31 result =
32 syscall(SYS_wait4, child.pid, first_output ? bad : &status, 0, first_output ? &usage : bad);
33 } else {
34 result = cw_raw_waitid(P_PID, child.pid, first_output ? &info : bad,
35 WEXITED | (no_wait ? WNOWAIT : 0), first_output ? bad : &usage);
36 }
37 CHECK(result == -1 && errno == EFAULT);
38 if (use_wait4) {
39 if (first_output)
40 CHECK(cw_bytes(&usage, sizeof(usage), 0xa5) == 0);
41 else
42 CHECK(WIFEXITED(status) && WEXITSTATUS(status) == 67);
43 } else if (first_output) {
44 CHECK(cw_bytes(&info, sizeof(info), 0xa5) == 0);
45 } else {
46 CHECK(memcmp(&usage, &expected, CW_RUSAGE_BYTES) == 0 && !cw_usage(&usage));
47 }
48 if (no_wait) {
49 CHECK(waitid(P_PID, child.pid, &info, WEXITED | WNOWAIT) == 0);
50 CHECK(cw_info(&info, child.pid, getuid(), CLD_EXITED, 67) == 0);
51 CHECK(wait4(child.pid, &status, 0, NULL) == child.pid && WEXITSTATUS(status) == 67);
52 } else {
53 CHECK(waitid(P_PID, child.pid, &info, WEXITED | WNOHANG) == -1 && errno == ECHILD);
54 CHECK(!cw_empty_info(&info));
55 }
56 child.pid = -1;
57out:
58 cw_cleanup(&child);
59 if (bad != MAP_FAILED)
60 munmap(bad, cw_page);
61 return failed;
62}
63
64static int optional_and_overlap(void) {
65 int failed = 0;
66 struct cw_child child = CW_CHILD_INIT;
67 struct rusage usage, expected_usage;
68 siginfo_t info, expected_info;
69 union {
70 struct rusage usage;
71 siginfo_t info;
72 int status;
73 } overlap, expected;
74 CHECK(exited(&child, 73, &expected_usage, &info) == 0);
75 CHECK(!cw_usage(&expected_usage));
76 CHECK(cw_info(&info, child.pid, getuid(), CLD_EXITED, 73) == 0);
77 memset(&usage, 0xa5, sizeof(usage));
78 CHECK(cw_raw_waitid(P_PID, child.pid, NULL, WEXITED | WNOWAIT, &usage) == 0);
79 CHECK(memcmp(&usage, &expected_usage, CW_RUSAGE_BYTES) == 0 && !cw_usage(&usage));
80
81 memset(&info, 0xa5, sizeof(info));
82 expected_info = info;
83 expected_info.si_signo = SIGCHLD;
84 expected_info.si_errno = 0;
85 expected_info.si_code = CLD_EXITED;
86 expected_info.si_pid = child.pid;
87 expected_info.si_uid = getuid();
88 expected_info.si_status = 73;
89 CHECK(waitid(P_PID, child.pid, &info, WEXITED | WNOWAIT) == 0);
90 CHECK(memcmp(&info, &expected_info, sizeof(info)) == 0);
91
92 memset(&overlap, 0xa5, sizeof(overlap));
93 memset(&expected, 0xa5, sizeof(expected));
94 memcpy(&expected, &expected_usage, CW_RUSAGE_BYTES);
95 expected.info.si_signo = SIGCHLD;
96 expected.info.si_errno = 0;
97 expected.info.si_code = CLD_EXITED;
98 expected.info.si_pid = child.pid;
99 expected.info.si_uid = getuid();
100 expected.info.si_status = 73;
101 CHECK(cw_raw_waitid(P_PID, child.pid, &overlap.info, WEXITED | WNOWAIT, &overlap.usage) == 0);
102 CHECK(memcmp(&overlap, &expected, CW_RUSAGE_BYTES) == 0);
103 CHECK(cw_bytes((const unsigned char*)&overlap + CW_RUSAGE_BYTES,
104 sizeof(overlap) - CW_RUSAGE_BYTES, 0xa5) == 0);
105
106 /* wait4's later rusage output overwrites an aliased status output. */
107 CHECK(syscall(SYS_wait4, child.pid, &overlap.status, 0, &overlap.usage) == child.pid);
108 CHECK(memcmp(&overlap.usage, &expected_usage, CW_RUSAGE_BYTES) == 0 && !cw_usage(&overlap.usage));
109 child.pid = -1;
110 cw_cleanup(&child);
111 CHECK(exited(&child, 74, &usage, &info) == 0);
112 CHECK(cw_raw_waitid(P_PID, child.pid, NULL, WEXITED, NULL) == 0);
113 CHECK(waitpid(child.pid, NULL, WNOHANG) == -1 && errno == ECHILD);
114 child.pid = -1;
115out:
116 cw_cleanup(&child);
117 return failed;
118}
119
120int cw_copyout(void) {
121 for (int no_wait = 0; no_wait < 2; ++no_wait)
122 for (int first = 0; first < 2; ++first)
123 if (fault_case(0, first, no_wait))
124 return 1;
125 for (int first = 0; first < 2; ++first)
126 if (fault_case(1, first, 0))
127 return 1;
128 return optional_and_overlap();
129}