The Pedigree Project 0.1
transfer-contract-test/offsets.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/sendfile.h>
8
9static int combinations(int kind) {
10 int failed = 0, alias = -1;
11 struct tf_file input = {.fd = -1}, output = {.fd = -1};
12 CHECK(!tf_create(&input, "/tmp", 128, 31) && !tf_create(&output, "/tmp", 128, 73));
13 CHECK((alias = dup(input.fd)) >= 0);
14 for (int explicit_input = 0; explicit_input < 2; ++explicit_input) {
15 for (int explicit_output = 0; explicit_output < (kind == TF_COPY_RANGE ? 2 : 1);
16 ++explicit_output) {
17 off_t from = 3, to = 11;
18 const off_t source = explicit_input ? from : 9, destination = explicit_output ? to : 17;
19 CHECK(lseek(input.fd, 9, SEEK_SET) == 9 && lseek(output.fd, 17, SEEK_SET) == 17);
20 CHECK(tf_copy(kind, alias, explicit_input ? &from : NULL, output.fd,
21 explicit_output ? &to : NULL, 25) == 25);
22 CHECK(from == (explicit_input ? 28 : 3) && to == (explicit_output ? 36 : 11));
23 CHECK(lseek(input.fd, 0, SEEK_CUR) == (explicit_input ? 9 : 34));
24 CHECK(lseek(output.fd, 0, SEEK_CUR) == (explicit_output ? 17 : 42));
25 CHECK(!tf_verify(output.fd, destination, 25, source, 31));
26 }
27 }
28out:
29 if (alias >= 0)
30 close(alias);
31 tf_destroy(&output);
32 tf_destroy(&input);
33 return failed;
34}
35static int same_file(void) {
36 int failed = 0, alias = -1, separate = -1;
37 struct tf_file file = {.fd = -1};
38 unsigned char before[64], after[64];
39 CHECK(!tf_create(&file, "/tmp", 64, 43));
40 CHECK((alias = dup(file.fd)) >= 0 && (separate = open(file.path, O_RDWR)) >= 0);
41 CHECK(lseek(file.fd, 7, SEEK_SET) == 7);
42 CHECK(sendfile(alias, file.fd, NULL, 9) == 9);
43 CHECK(lseek(file.fd, 0, SEEK_CUR) == 16 && lseek(alias, 0, SEEK_CUR) == 16);
44 CHECK(!tf_verify(file.fd, 0, 64, 0, 43));
45 CHECK(copy_file_range(file.fd, NULL, alias, NULL, 9, 0) == -1 && errno == EINVAL);
46 CHECK(lseek(alias, 0, SEEK_CUR) == 16);
47 CHECK(copy_file_range(file.fd, NULL, alias, NULL, 0, 0) == 0);
48 off_t from = 0, to = 32;
49 CHECK(copy_file_range(file.fd, &from, separate, &to, 16, 0) == 16);
50 CHECK(from == 16 && to == 48 && !tf_verify(file.fd, 32, 16, 0, 43));
51 CHECK(lseek(file.fd, 0, SEEK_CUR) == 16 && lseek(separate, 0, SEEK_CUR) == 0);
52 CHECK(pread(file.fd, before, sizeof(before), 0) == sizeof(before));
53 from = 0;
54 to = 8;
55 CHECK(copy_file_range(file.fd, &from, separate, &to, 16, 0) == -1 && errno == EINVAL);
56 CHECK(from == 0 && to == 8);
57 CHECK(pread(file.fd, after, sizeof(after), 0) == sizeof(after) &&
58 !memcmp(before, after, sizeof(before)));
59 /* The available input is sixteen bytes, adjacent to the destination range. */
60 from = 48;
61 to = 32;
62 CHECK(copy_file_range(file.fd, &from, alias, &to, 40, 0) == 16);
63 CHECK(from == 64 && to == 48 && !tf_verify(file.fd, 32, 16, 48, 43));
64 CHECK(lseek(file.fd, 0, SEEK_CUR) == 16);
65 CHECK(lseek(file.fd, 64, SEEK_SET) == 64);
66 CHECK(copy_file_range(file.fd, NULL, alias, NULL, 4, 0) == 0);
67out:
68 if (separate >= 0)
69 close(separate);
70 if (alias >= 0)
71 close(alias);
72 tf_destroy(&file);
73 return failed;
74}
75static int invalid_ranges(void) {
76 int failed = 0;
77 struct tf_file input = {.fd = -1}, output = {.fd = -1};
78 const size_t page = sysconf(_SC_PAGESIZE);
79 void* denied = MAP_FAILED;
80 CHECK(!tf_create(&input, "/tmp", 64, 19) && !tf_create(&output, "/tmp", 64, 61));
81 denied = mmap(NULL, page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
82 CHECK(denied != MAP_FAILED);
83 for (int kind = 0; kind < 2; ++kind) {
84 off_t offset = -2;
85 CHECK(tf_copy(kind, input.fd, &offset, output.fd, NULL, 1) == -1 && errno == EINVAL);
86 CHECK(offset == -2);
87 CHECK(tf_copy(kind, input.fd, denied, output.fd, NULL, 1) == -1 && errno == EFAULT);
88 }
89 off_t from = 0;
90 CHECK(copy_file_range(input.fd, &from, output.fd, denied, 1, 0) == -1 && errno == EFAULT);
91 CHECK(from == 0);
92 off_t to = -2;
93 CHECK(copy_file_range(input.fd, NULL, output.fd, &to, 1, 0) == -1 && errno == EINVAL);
94 CHECK(to == -2);
95 to = 1;
96 CHECK(copy_file_range(input.fd, NULL, output.fd, &to, SIZE_MAX, 0) == -1 && errno == EOVERFLOW);
97 CHECK(to == 1);
98 from = 1;
99 CHECK(copy_file_range(input.fd, &from, output.fd, NULL, SIZE_MAX, 0) == -1 && errno == EOVERFLOW);
100 CHECK(from == 1);
101 from = INT64_MAX - 7;
102 CHECK(sendfile(output.fd, input.fd, &from, 16) == -1 && errno == EINVAL);
103 CHECK(from == INT64_MAX - 7);
104 CHECK(copy_file_range(input.fd, &from, output.fd, NULL, 16, 0) == 0);
105 CHECK(from == INT64_MAX - 7);
106 CHECK(copy_file_range(input.fd, NULL, output.fd, NULL, 1, 1) == -1 && errno == EINVAL);
107 CHECK(copy_file_range(input.fd, NULL, output.fd, NULL, 0, 1) == -1 && errno == EINVAL);
108 CHECK(!tf_verify(output.fd, 0, 64, 0, 61));
109 CHECK(lseek(input.fd, 0, SEEK_CUR) == 0 && lseek(output.fd, 0, SEEK_CUR) == 0);
110out:
111 if (denied != MAP_FAILED)
112 munmap(denied, page);
113 tf_destroy(&output);
114 tf_destroy(&input);
115 return failed;
116}
117static int late_copyout(void) {
118 int failed = 0;
119 struct tf_file input = {.fd = -1}, output = {.fd = -1};
120 const size_t page = sysconf(_SC_PAGESIZE);
121 off_t* readonly = MAP_FAILED;
122 CHECK(!tf_create(&input, "/tmp", 64, 17) && !tf_create(&output, "/tmp", 128, 91));
123 readonly = mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
124 CHECK(readonly != MAP_FAILED);
125 *readonly = 8;
126 CHECK(!mprotect(readonly, page, PROT_READ));
127 CHECK(lseek(input.fd, 13, SEEK_SET) == 13);
128 CHECK(sendfile(output.fd, input.fd, readonly, 4) == -1 && errno == EFAULT);
129 CHECK(*readonly == 8 && lseek(input.fd, 0, SEEK_CUR) == 13 && lseek(output.fd, 0, SEEK_CUR) == 4);
130 CHECK(!tf_verify(output.fd, 0, 4, 8, 17));
131 off_t to = 24;
132 CHECK(copy_file_range(input.fd, readonly, output.fd, &to, 4, 0) == -1 && errno == EFAULT);
133 CHECK(to == 28 && *readonly == 8 && !tf_verify(output.fd, 24, 4, 8, 17));
134 CHECK(lseek(output.fd, 0, SEEK_CUR) == 4);
135 off_t from = 3;
136 CHECK(copy_file_range(input.fd, &from, output.fd, readonly, 4, 0) == -1 && errno == EFAULT);
137 CHECK(from == 7 && *readonly == 8 && !tf_verify(output.fd, 8, 4, 3, 17));
138 CHECK(copy_file_range(input.fd, readonly, output.fd, NULL, 4, 0) == -1 && errno == EFAULT);
139 CHECK(lseek(output.fd, 0, SEEK_CUR) == 8 && !tf_verify(output.fd, 4, 4, 8, 17));
140 CHECK(copy_file_range(input.fd, NULL, output.fd, readonly, 4, 0) == -1 && errno == EFAULT);
141 CHECK(lseek(input.fd, 0, SEEK_CUR) == 17 && !tf_verify(output.fd, 8, 4, 13, 17));
142 CHECK(copy_file_range(input.fd, readonly, output.fd, NULL, 0, 0) == 0);
143 CHECK(sendfile(output.fd, input.fd, readonly, 0) == -1 && errno == EFAULT);
144 CHECK(sendfile(-1, input.fd, readonly, 1) == -1 && errno == EFAULT);
145 CHECK(copy_file_range(input.fd, readonly, output.fd, NULL, 0, 1) == -1 && errno == EINVAL);
146 CHECK(!mprotect(readonly, page, PROT_READ | PROT_WRITE));
147 *readonly = 64;
148 CHECK(!mprotect(readonly, page, PROT_READ));
149 CHECK(copy_file_range(input.fd, readonly, output.fd, NULL, 4, 0) == 0);
150 CHECK(sendfile(output.fd, input.fd, readonly, 4) == -1 && errno == EFAULT);
151 CHECK(*readonly == 64 && lseek(input.fd, 0, SEEK_CUR) == 17 &&
152 lseek(output.fd, 0, SEEK_CUR) == 8);
153out:
154 if (readonly != MAP_FAILED)
155 munmap(readonly, page);
156 tf_destroy(&output);
157 tf_destroy(&input);
158 return failed;
159}
160int transfer_offsets(void) {
161 return combinations(TF_SENDFILE) || combinations(TF_COPY_RANGE) || same_file() ||
162 invalid_ranges() || late_copyout();
163}