1 | /* |
2 | * Permission is hereby granted, free of charge, to any person obtaining |
3 | * a copy of this software and associated documentation files (the |
4 | * "Software"), to deal in the Software without restriction, including |
5 | * without limitation the rights to use, copy, modify, merge, publish, |
6 | * distribute, sublicense, and/or sell copies of the Software, and to |
7 | * permit persons to whom the Software is furnished to do so, subject to |
8 | * the following conditions: |
9 | * |
10 | * The above copyright notice and this permission notice (including the |
11 | * next paragraph) shall be included in all copies or substantial |
12 | * portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
15 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
16 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
17 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
18 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
19 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
21 | * SOFTWARE. |
22 | */ |
23 | |
24 | #include <stdlib.h> |
25 | #include <assert.h> |
26 | #include <errno.h> |
27 | #include <string.h> |
28 | #include <stdio.h> |
29 | #include <sys/socket.h> |
30 | #include <sys/types.h> |
31 | #include <sys/wait.h> |
32 | #include <sys/un.h> |
33 | #include <unistd.h> |
34 | |
35 | #include "wayland-client.h" |
36 | #include "wayland-os.h" |
37 | #include "wayland-server.h" |
38 | #include "test-runner.h" |
39 | |
40 | /* Paths longer than what the .sun_path array can contain must be rejected. |
41 | * This is a hard limitation of assigning a name to AF_UNIX/AF_LOCAL sockets. |
42 | * See `man 7 unix`. |
43 | */ |
44 | |
45 | static struct sockaddr_un example_sockaddr_un; |
46 | |
47 | #define TOO_LONG (1 + sizeof example_sockaddr_un.sun_path) |
48 | |
49 | /* Ensure the connection doesn't fail due to lack of XDG_RUNTIME_DIR. */ |
50 | static const char * |
51 | require_xdg_runtime_dir(void) |
52 | { |
53 | char *val = getenv(name: "XDG_RUNTIME_DIR" ); |
54 | assert(val && "set $XDG_RUNTIME_DIR to run this test" ); |
55 | |
56 | return val; |
57 | } |
58 | |
59 | TEST(socket_path_overflow_client_connect) |
60 | { |
61 | char path[TOO_LONG]; |
62 | struct wl_display *d; |
63 | |
64 | require_xdg_runtime_dir(); |
65 | |
66 | memset(s: path, c: 'a', n: sizeof path); |
67 | path[sizeof path - 1] = '\0'; |
68 | |
69 | d = wl_display_connect(name: path); |
70 | assert(d == NULL); |
71 | assert(errno == ENAMETOOLONG); |
72 | |
73 | /* This is useless, but prevents a warning about example_sockaddr_un |
74 | * being discarded from the compilation unit. */ |
75 | strcpy(dest: example_sockaddr_un.sun_path, src: "happy now clang?" ); |
76 | assert(example_sockaddr_un.sun_path[0] != '\0'); |
77 | } |
78 | |
79 | TEST(socket_path_overflow_server_create) |
80 | { |
81 | char path[TOO_LONG]; |
82 | struct wl_display *d; |
83 | int ret; |
84 | |
85 | require_xdg_runtime_dir(); |
86 | |
87 | memset(s: path, c: 'a', n: sizeof path); |
88 | path[sizeof path - 1] = '\0'; |
89 | |
90 | d = wl_display_create(); |
91 | assert(d != NULL); |
92 | |
93 | ret = wl_display_add_socket(display: d, name: path); |
94 | assert(ret < 0); |
95 | assert(errno == ENAMETOOLONG); |
96 | |
97 | wl_display_destroy(display: d); |
98 | } |
99 | |
100 | TEST(add_existing_socket) |
101 | { |
102 | char path[sizeof example_sockaddr_un.sun_path]; |
103 | const char *name = "wayland-test-0" ; |
104 | const char *xdg_runtime_dir; |
105 | struct wl_display *d; |
106 | int ret; |
107 | size_t len; |
108 | |
109 | xdg_runtime_dir = require_xdg_runtime_dir(); |
110 | |
111 | d = wl_display_create(); |
112 | assert(d != NULL); |
113 | |
114 | /* this one should be OK */ |
115 | ret = wl_display_add_socket(display: d, name); |
116 | assert(ret == 0); |
117 | |
118 | /* this one should fail */ |
119 | ret = wl_display_add_socket(display: d, name); |
120 | assert(ret < 0); |
121 | |
122 | /* the original socket should still exist. |
123 | * this was a bug introduced in e2c0d47b0c77f18cd90e9c6eabb358c4d89681c8 */ |
124 | len = snprintf(s: path, maxlen: sizeof example_sockaddr_un.sun_path, format: "%s/%s" , |
125 | xdg_runtime_dir, name); |
126 | assert(len < sizeof example_sockaddr_un.sun_path |
127 | && "Bug in test. Path too long" ); |
128 | |
129 | assert(access(path, F_OK) != -1); |
130 | |
131 | /* the original socket should still exist */ |
132 | ret = wl_display_add_socket(display: d, name); |
133 | assert(ret < 0); |
134 | |
135 | wl_display_destroy(display: d); |
136 | } |
137 | |
138 | TEST(add_socket_auto) |
139 | { |
140 | /* the number of auto sockets is currently 32, |
141 | * set in wayland-server.c. |
142 | */ |
143 | const int MAX_SOCKETS = 32; |
144 | |
145 | char path[sizeof example_sockaddr_un.sun_path]; |
146 | const char *name; |
147 | const char *xdg_runtime_dir; |
148 | struct wl_display *d; |
149 | int i; |
150 | size_t len; |
151 | |
152 | xdg_runtime_dir = require_xdg_runtime_dir(); |
153 | |
154 | d = wl_display_create(); |
155 | assert(d != NULL); |
156 | |
157 | for (i = 0; i <= MAX_SOCKETS; ++i) { |
158 | name = wl_display_add_socket_auto(display: d); |
159 | assert(name != NULL); |
160 | |
161 | len = snprintf(s: path, maxlen: sizeof example_sockaddr_un.sun_path, |
162 | format: "%s/%s" , xdg_runtime_dir, name); |
163 | assert(len < sizeof example_sockaddr_un.sun_path |
164 | && "Bug in test. Path too long" ); |
165 | |
166 | /* was the socket created correctly? */ |
167 | assert(access(path, F_OK) != -1); |
168 | |
169 | /* is the name sequential? */ |
170 | len = snprintf(s: path, maxlen: sizeof example_sockaddr_un.sun_path, |
171 | format: "wayland-%d" , i); |
172 | assert(strcmp(name, path) == 0); |
173 | } |
174 | |
175 | /* next addition should return NULL */ |
176 | name = wl_display_add_socket_auto(display: d); |
177 | assert(name == NULL); |
178 | |
179 | /* check if the socket was not deleted the last time */ |
180 | name = wl_display_add_socket_auto(display: d); |
181 | assert(name == NULL); |
182 | |
183 | wl_display_destroy(display: d); |
184 | } |
185 | |
186 | struct client_create_listener { |
187 | struct wl_listener listener; |
188 | struct wl_display *display; |
189 | }; |
190 | |
191 | struct client_destroy_listener { |
192 | struct wl_listener listener; |
193 | struct wl_display *display; |
194 | }; |
195 | |
196 | static void |
197 | client_destroy_notify(struct wl_listener *l, void *data) |
198 | { |
199 | struct client_destroy_listener *listener = |
200 | wl_container_of(l, listener, listener); |
201 | wl_display_terminate(display: listener->display); |
202 | free(ptr: listener); |
203 | } |
204 | |
205 | static void |
206 | client_create_notify(struct wl_listener *l, void *data) |
207 | { |
208 | struct wl_client *client = data; |
209 | struct client_create_listener *listener = |
210 | wl_container_of(l, listener, listener); |
211 | struct client_destroy_listener *destroy_listener = (struct client_destroy_listener *)malloc(size: sizeof *destroy_listener); |
212 | assert(destroy_listener != NULL); |
213 | destroy_listener->display = listener->display; |
214 | destroy_listener->listener.notify = client_destroy_notify; |
215 | wl_client_add_destroy_listener(client, listener: &destroy_listener->listener); |
216 | } |
217 | |
218 | TEST(absolute_socket_path) |
219 | { |
220 | struct wl_display *display; |
221 | struct client_create_listener client_create_listener; |
222 | struct sockaddr_un addr; |
223 | int fd; |
224 | socklen_t size; |
225 | const char *xdg_runtime_dir; |
226 | size_t len; |
227 | int ret; |
228 | pid_t pid; |
229 | |
230 | /* It's a little weird that this test about absolute socket paths |
231 | * uses XDG_RUNTIME_DIR, but that's the only location guaranteed |
232 | * by test-runner to be both writable and unique. This isn't |
233 | * really a problem; we'll just take care that the leaf-level |
234 | * filename used for the socket isn't anything that would |
235 | * accidentally be generated by a default usage of wl_display_connect(). */ |
236 | xdg_runtime_dir = require_xdg_runtime_dir(); |
237 | memset(s: &addr, c: 0, n: sizeof addr); |
238 | len = snprintf(s: addr.sun_path, maxlen: sizeof addr.sun_path, |
239 | format: "%s/%s" , xdg_runtime_dir, "wayland-absolute-0" ); |
240 | assert(len < sizeof addr.sun_path |
241 | && "Bug in test. Path too long" ); |
242 | |
243 | /* The path must not exist prior to binding. */ |
244 | assert(access(addr.sun_path, F_OK) == -1); |
245 | |
246 | size = offsetof (struct sockaddr_un, sun_path) + strlen(s: addr.sun_path); |
247 | addr.sun_family = AF_LOCAL; |
248 | fd = wl_os_socket_cloexec(PF_LOCAL, SOCK_STREAM, protocol: 0); |
249 | assert(fd >= 0 ); |
250 | ret = bind(fd: fd, addr: (struct sockaddr *) &addr, len: size); |
251 | assert(ret >= 0); |
252 | ret = listen(fd: fd, n: 128); |
253 | assert(ret >= 0); |
254 | |
255 | /* Start server display. Be careful (by avoiding wl_display_add_socket_auto() |
256 | * to offer only the absolutely qualified socket made above. */ |
257 | display = wl_display_create(); |
258 | assert(display != NULL); |
259 | client_create_listener.listener.notify = client_create_notify; |
260 | client_create_listener.display = display; |
261 | wl_display_add_client_created_listener(display, listener: &client_create_listener.listener); |
262 | ret = wl_display_add_socket_fd(display, sock_fd: fd); |
263 | assert(ret == 0); |
264 | |
265 | /* Execute client that connects to the absolutely qualified server socket path. */ |
266 | pid = fork(); |
267 | assert(pid != -1); |
268 | |
269 | if (pid == 0) { |
270 | ret = setenv(name: "WAYLAND_DISPLAY" , value: addr.sun_path, replace: 1); |
271 | assert(ret == 0); |
272 | struct wl_display *client_display = wl_display_connect(NULL); |
273 | assert(client_display != NULL); |
274 | ret = wl_display_roundtrip(display: client_display); |
275 | assert(ret != -1); |
276 | wl_display_disconnect(display: client_display); |
277 | exit(status: 0); |
278 | assert(false); |
279 | } |
280 | |
281 | wl_display_run(display); |
282 | ret = waitpid(pid: pid, NULL, options: 0); |
283 | assert(ret == pid); |
284 | |
285 | wl_display_destroy(display); |
286 | |
287 | ret = unlink(name: addr.sun_path); |
288 | assert(ret == 0); |
289 | } |
290 | |