1 | /* |
2 | * Copyright © 2012 Collabora, Ltd. |
3 | * Copyright © 2012 Intel Corporation |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining |
6 | * a copy of this software and associated documentation files (the |
7 | * "Software"), to deal in the Software without restriction, including |
8 | * without limitation the rights to use, copy, modify, merge, publish, |
9 | * distribute, sublicense, and/or sell copies of the Software, and to |
10 | * permit persons to whom the Software is furnished to do so, subject to |
11 | * the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice (including the |
14 | * next paragraph) shall be included in all copies or substantial |
15 | * portions of the Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
24 | * SOFTWARE. |
25 | */ |
26 | #include "../config.h" |
27 | |
28 | #define _GNU_SOURCE |
29 | |
30 | #include <stdlib.h> |
31 | #include <stdint.h> |
32 | #include <assert.h> |
33 | #include <sys/types.h> |
34 | #include <sys/socket.h> |
35 | #include <sys/stat.h> |
36 | #include <unistd.h> |
37 | #include <dlfcn.h> |
38 | #include <errno.h> |
39 | #include <stdarg.h> |
40 | #include <fcntl.h> |
41 | #include <stdio.h> |
42 | #include <sys/epoll.h> |
43 | |
44 | #include "wayland-private.h" |
45 | #include "test-runner.h" |
46 | #include "wayland-os.h" |
47 | |
48 | static int fall_back; |
49 | |
50 | /* Play nice with sanitizers |
51 | * |
52 | * Sanitizers need to intercept syscalls in the compiler run-time library. As |
53 | * this isn't a separate ELF object, the usual dlsym(RTLD_NEXT) approach won't |
54 | * work: there can only be one function named "socket" etc. To support this, the |
55 | * sanitizer library names its interceptors with the prefix __interceptor_ ("__" |
56 | * being reserved for the implementation) and then weakly aliases it to the real |
57 | * function. The functions we define below will override the weak alias, and we |
58 | * can call them by the __interceptor_ name directly. This allows the sanitizer |
59 | * to do its work before calling the next version of the function via dlsym. |
60 | * |
61 | * However! We also don't know which of these functions the sanitizer actually |
62 | * wants to override, so we have to declare our own weak symbols for |
63 | * __interceptor_ and check at run time if they linked to anything or not. |
64 | */ |
65 | |
66 | #define DECL(ret_type, func, ...) \ |
67 | ret_type __interceptor_ ## func(__VA_ARGS__) __attribute__((weak)); \ |
68 | static ret_type (*real_ ## func)(__VA_ARGS__); \ |
69 | static int wrapped_calls_ ## func; |
70 | |
71 | #define REAL(func) (__interceptor_ ## func) ? \ |
72 | __interceptor_ ## func : \ |
73 | (typeof(&__interceptor_ ## func))dlsym(RTLD_NEXT, #func) |
74 | |
75 | DECL(int, socket, int, int, int); |
76 | DECL(int, fcntl, int, int, ...); |
77 | DECL(ssize_t, recvmsg, int, struct msghdr *, int); |
78 | DECL(int, epoll_create1, int); |
79 | |
80 | static void |
81 | init_fallbacks(int do_fallbacks) |
82 | { |
83 | fall_back = do_fallbacks; |
84 | real_socket = REAL(socket); |
85 | real_fcntl = REAL(fcntl); |
86 | real_recvmsg = REAL(recvmsg); |
87 | real_epoll_create1 = REAL(epoll_create1); |
88 | } |
89 | |
90 | __attribute__ ((visibility("default" ))) int |
91 | socket(int domain, int type, int protocol) |
92 | { |
93 | wrapped_calls_socket++; |
94 | |
95 | if (fall_back && (type & SOCK_CLOEXEC)) { |
96 | errno = EINVAL; |
97 | return -1; |
98 | } |
99 | |
100 | return real_socket(domain, type, protocol); |
101 | } |
102 | |
103 | __attribute__ ((visibility("default" ))) int |
104 | (fcntl)(int fd, int cmd, ...) |
105 | { |
106 | va_list ap; |
107 | int arg; |
108 | int has_arg; |
109 | |
110 | wrapped_calls_fcntl++; |
111 | |
112 | if (fall_back && (cmd == F_DUPFD_CLOEXEC)) { |
113 | errno = EINVAL; |
114 | return -1; |
115 | } |
116 | switch (cmd) { |
117 | case F_DUPFD_CLOEXEC: |
118 | case F_DUPFD: |
119 | case F_SETFD: |
120 | va_start(ap, cmd); |
121 | arg = va_arg(ap, int); |
122 | has_arg = 1; |
123 | va_end(ap); |
124 | break; |
125 | case F_GETFD: |
126 | has_arg = 0; |
127 | break; |
128 | default: |
129 | fprintf(stderr, format: "Unexpected fctnl cmd %d\n" , cmd); |
130 | abort(); |
131 | } |
132 | |
133 | if (has_arg) { |
134 | return real_fcntl(fd, cmd, arg); |
135 | } |
136 | return real_fcntl(fd, cmd); |
137 | } |
138 | |
139 | __attribute__ ((visibility("default" ))) ssize_t |
140 | recvmsg(int sockfd, struct msghdr *msg, int flags) |
141 | { |
142 | wrapped_calls_recvmsg++; |
143 | |
144 | if (fall_back && (flags & MSG_CMSG_CLOEXEC)) { |
145 | errno = EINVAL; |
146 | return -1; |
147 | } |
148 | |
149 | return real_recvmsg(sockfd, msg, flags); |
150 | } |
151 | |
152 | __attribute__ ((visibility("default" ))) int |
153 | epoll_create1(int flags) |
154 | { |
155 | wrapped_calls_epoll_create1++; |
156 | |
157 | if (fall_back) { |
158 | wrapped_calls_epoll_create1++; /* epoll_create() not wrapped */ |
159 | errno = EINVAL; |
160 | return -1; |
161 | } |
162 | |
163 | return real_epoll_create1(flags); |
164 | } |
165 | |
166 | static void |
167 | do_os_wrappers_socket_cloexec(int n) |
168 | { |
169 | int fd; |
170 | int nr_fds; |
171 | |
172 | nr_fds = count_open_fds(); |
173 | |
174 | /* simply create a socket that closes on exec */ |
175 | fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, protocol: 0); |
176 | assert(fd >= 0); |
177 | |
178 | /* |
179 | * Must have 2 calls if falling back, but must also allow |
180 | * falling back without a forced fallback. |
181 | */ |
182 | assert(wrapped_calls_socket > n); |
183 | |
184 | exec_fd_leak_check(nr_expected_fds: nr_fds); |
185 | } |
186 | |
187 | TEST(os_wrappers_socket_cloexec) |
188 | { |
189 | /* normal case */ |
190 | init_fallbacks(do_fallbacks: 0); |
191 | do_os_wrappers_socket_cloexec(n: 0); |
192 | } |
193 | |
194 | TEST(os_wrappers_socket_cloexec_fallback) |
195 | { |
196 | /* forced fallback */ |
197 | init_fallbacks(do_fallbacks: 1); |
198 | do_os_wrappers_socket_cloexec(n: 1); |
199 | } |
200 | |
201 | static void |
202 | do_os_wrappers_dupfd_cloexec(int n) |
203 | { |
204 | int base_fd; |
205 | int fd; |
206 | int nr_fds; |
207 | |
208 | nr_fds = count_open_fds(); |
209 | |
210 | base_fd = socket(PF_LOCAL, SOCK_STREAM, protocol: 0); |
211 | assert(base_fd >= 0); |
212 | |
213 | fd = wl_os_dupfd_cloexec(fd: base_fd, minfd: 13); |
214 | assert(fd >= 13); |
215 | |
216 | close(fd: base_fd); |
217 | |
218 | /* |
219 | * Must have 4 calls if falling back, but must also allow |
220 | * falling back without a forced fallback. |
221 | */ |
222 | assert(wrapped_calls_fcntl > n); |
223 | |
224 | exec_fd_leak_check(nr_expected_fds: nr_fds); |
225 | } |
226 | |
227 | TEST(os_wrappers_dupfd_cloexec) |
228 | { |
229 | init_fallbacks(do_fallbacks: 0); |
230 | do_os_wrappers_dupfd_cloexec(n: 0); |
231 | } |
232 | |
233 | TEST(os_wrappers_dupfd_cloexec_fallback) |
234 | { |
235 | init_fallbacks(do_fallbacks: 1); |
236 | do_os_wrappers_dupfd_cloexec(n: 3); |
237 | } |
238 | |
239 | struct marshal_data { |
240 | struct wl_connection *read_connection; |
241 | struct wl_connection *write_connection; |
242 | int s[2]; |
243 | uint32_t read_mask; |
244 | uint32_t write_mask; |
245 | union { |
246 | int h[3]; |
247 | } value; |
248 | int nr_fds_begin; |
249 | int nr_fds_conn; |
250 | int wrapped_calls; |
251 | }; |
252 | |
253 | static void |
254 | setup_marshal_data(struct marshal_data *data) |
255 | { |
256 | assert(socketpair(AF_UNIX, |
257 | SOCK_STREAM | SOCK_CLOEXEC, 0, data->s) == 0); |
258 | |
259 | data->read_connection = wl_connection_create(fd: data->s[0]); |
260 | assert(data->read_connection); |
261 | |
262 | data->write_connection = wl_connection_create(fd: data->s[1]); |
263 | assert(data->write_connection); |
264 | } |
265 | |
266 | static void |
267 | marshal_demarshal(struct marshal_data *data, |
268 | void (*func)(void), int size, const char *format, ...) |
269 | { |
270 | struct wl_closure *closure; |
271 | static const int opcode = 4444; |
272 | static struct wl_object sender = { NULL, NULL, 1234 }; |
273 | struct wl_message message = { "test" , format, NULL }; |
274 | struct wl_map objects; |
275 | struct wl_object object = { NULL, &func, 1234 }; |
276 | va_list ap; |
277 | uint32_t msg[1] = { 1234 }; |
278 | |
279 | va_start(ap, format); |
280 | closure = wl_closure_vmarshal(sender: &sender, opcode, ap, message: &message); |
281 | va_end(ap); |
282 | |
283 | assert(closure); |
284 | assert(wl_closure_send(closure, data->write_connection) == 0); |
285 | wl_closure_destroy(closure); |
286 | assert(wl_connection_flush(data->write_connection) == size); |
287 | |
288 | assert(wl_connection_read(data->read_connection) == size); |
289 | |
290 | wl_map_init(map: &objects, WL_MAP_SERVER_SIDE); |
291 | object.id = msg[0]; |
292 | closure = wl_connection_demarshal(connection: data->read_connection, |
293 | size, objects: &objects, message: &message); |
294 | assert(closure); |
295 | wl_closure_invoke(closure, flags: WL_CLOSURE_INVOKE_SERVER, target: &object, opcode: 0, data); |
296 | wl_closure_destroy(closure); |
297 | } |
298 | |
299 | static void |
300 | validate_recvmsg_h(struct marshal_data *data, |
301 | struct wl_object *object, int fd1, int fd2, int fd3) |
302 | { |
303 | struct stat buf1, buf2; |
304 | |
305 | assert(fd1 >= 0); |
306 | assert(fd2 >= 0); |
307 | assert(fd3 >= 0); |
308 | |
309 | assert(fd1 != data->value.h[0]); |
310 | assert(fd2 != data->value.h[1]); |
311 | assert(fd3 != data->value.h[2]); |
312 | |
313 | assert(fstat(fd3, &buf1) == 0); |
314 | assert(fstat(data->value.h[2], &buf2) == 0); |
315 | assert(buf1.st_dev == buf2.st_dev); |
316 | assert(buf1.st_ino == buf2.st_ino); |
317 | |
318 | /* close the original file descriptors */ |
319 | close(fd: data->value.h[0]); |
320 | close(fd: data->value.h[1]); |
321 | close(fd: data->value.h[2]); |
322 | |
323 | /* the dup'd (received) fds should still be open */ |
324 | assert(count_open_fds() == data->nr_fds_conn + 3); |
325 | |
326 | /* |
327 | * Must have 2 calls if falling back, but must also allow |
328 | * falling back without a forced fallback. |
329 | */ |
330 | assert(wrapped_calls_recvmsg > data->wrapped_calls); |
331 | |
332 | if (data->wrapped_calls == 0 && wrapped_calls_recvmsg > 1) |
333 | printf(format: "recvmsg fell back unforced.\n" ); |
334 | |
335 | /* all fds opened during the test in any way should be gone on exec */ |
336 | exec_fd_leak_check(nr_expected_fds: data->nr_fds_begin); |
337 | } |
338 | |
339 | static void |
340 | do_os_wrappers_recvmsg_cloexec(int n) |
341 | { |
342 | struct marshal_data data; |
343 | |
344 | data.nr_fds_begin = count_open_fds(); |
345 | #if HAVE_BROKEN_MSG_CMSG_CLOEXEC |
346 | /* We call the fallback directly on FreeBSD versions with a broken |
347 | * MSG_CMSG_CLOEXEC, so we don't call the local recvmsg() wrapper. */ |
348 | data.wrapped_calls = 0; |
349 | #else |
350 | data.wrapped_calls = n; |
351 | #endif |
352 | |
353 | setup_marshal_data(&data); |
354 | data.nr_fds_conn = count_open_fds(); |
355 | |
356 | assert(pipe(data.value.h) >= 0); |
357 | |
358 | data.value.h[2] = open(file: "/dev/zero" , O_RDONLY); |
359 | assert(data.value.h[2] >= 0); |
360 | |
361 | marshal_demarshal(data: &data, func: (void *) validate_recvmsg_h, |
362 | size: 8, format: "hhh" , data.value.h[0], data.value.h[1], |
363 | data.value.h[2]); |
364 | } |
365 | |
366 | TEST(os_wrappers_recvmsg_cloexec) |
367 | { |
368 | init_fallbacks(do_fallbacks: 0); |
369 | do_os_wrappers_recvmsg_cloexec(n: 0); |
370 | } |
371 | |
372 | TEST(os_wrappers_recvmsg_cloexec_fallback) |
373 | { |
374 | init_fallbacks(do_fallbacks: 1); |
375 | do_os_wrappers_recvmsg_cloexec(n: 1); |
376 | } |
377 | |
378 | static void |
379 | do_os_wrappers_epoll_create_cloexec(int n) |
380 | { |
381 | int fd; |
382 | int nr_fds; |
383 | |
384 | nr_fds = count_open_fds(); |
385 | |
386 | fd = wl_os_epoll_create_cloexec(); |
387 | assert(fd >= 0); |
388 | |
389 | #ifdef EPOLL_CLOEXEC |
390 | assert(wrapped_calls_epoll_create1 == n); |
391 | #else |
392 | printf("No epoll_create1.\n" ); |
393 | #endif |
394 | |
395 | exec_fd_leak_check(nr_expected_fds: nr_fds); |
396 | } |
397 | |
398 | TEST(os_wrappers_epoll_create_cloexec) |
399 | { |
400 | init_fallbacks(do_fallbacks: 0); |
401 | do_os_wrappers_epoll_create_cloexec(n: 1); |
402 | } |
403 | |
404 | TEST(os_wrappers_epoll_create_cloexec_fallback) |
405 | { |
406 | init_fallbacks(do_fallbacks: 1); |
407 | do_os_wrappers_epoll_create_cloexec(n: 2); |
408 | } |
409 | |
410 | /* FIXME: add tests for wl_os_accept_cloexec() */ |
411 | |