1 /*
2 * Copyright © 2008 Kristian Høgsberg
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * Authors:
26 * Kristian Høgsberg <krh@bitplanet.net>
27 * Benjamin Franzke <benjaminfranzke@googlemail.com>
28 *
29 */
30
31 #define _GNU_SOURCE
32
33 #include "config.h"
34
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <string.h>
40 #include <sys/mman.h>
41 #include <unistd.h>
42 #include <assert.h>
43 #include <signal.h>
44 #include <pthread.h>
45 #include <errno.h>
46 #include <fcntl.h>
47
48 #include "wayland-util.h"
49 #include "wayland-private.h"
50 #include "wayland-server.h"
51
52 /* This once_t is used to synchronize installing the SIGBUS handler
53 * and creating the TLS key. This will be done in the first call
54 * wl_shm_buffer_begin_access which can happen from any thread */
55 static pthread_once_t wl_shm_sigbus_once = PTHREAD_ONCE_INIT;
56 static pthread_key_t wl_shm_sigbus_data_key;
57 static struct sigaction wl_shm_old_sigbus_action;
58
59 struct wl_shm_pool {
60 struct wl_resource *resource;
61 int internal_refcount;
62 int external_refcount;
63 char *data;
64 int32_t size;
65 int32_t new_size;
66 bool sigbus_is_impossible;
67 };
68
69 struct wl_shm_buffer {
70 struct wl_resource *resource;
71 int32_t width, height;
72 int32_t stride;
73 uint32_t format;
74 int offset;
75 struct wl_shm_pool *pool;
76 };
77
78 struct wl_shm_sigbus_data {
79 struct wl_shm_pool *current_pool;
80 int access_count;
81 int fallback_mapping_used;
82 };
83
84 static void
shm_pool_finish_resize(struct wl_shm_pool * pool)85 shm_pool_finish_resize(struct wl_shm_pool *pool)
86 {
87 void *data;
88
89 if (pool->size == pool->new_size)
90 return;
91
92 data = mremap(pool->data, pool->size, pool->new_size, MREMAP_MAYMOVE);
93 if (data == MAP_FAILED) {
94 wl_resource_post_error(pool->resource,
95 WL_SHM_ERROR_INVALID_FD,
96 "failed mremap");
97 return;
98 }
99
100 pool->data = data;
101 pool->size = pool->new_size;
102 }
103
104 static void
shm_pool_unref(struct wl_shm_pool * pool,bool external)105 shm_pool_unref(struct wl_shm_pool *pool, bool external)
106 {
107 if (external) {
108 pool->external_refcount--;
109 if (pool->external_refcount == 0)
110 shm_pool_finish_resize(pool);
111 } else {
112 pool->internal_refcount--;
113 }
114
115 if (pool->internal_refcount + pool->external_refcount)
116 return;
117
118 munmap(pool->data, pool->size);
119 free(pool);
120 }
121
122 static void
destroy_buffer(struct wl_resource * resource)123 destroy_buffer(struct wl_resource *resource)
124 {
125 struct wl_shm_buffer *buffer = wl_resource_get_user_data(resource);
126
127 if (buffer->pool)
128 shm_pool_unref(buffer->pool, false);
129 free(buffer);
130 }
131
132 static void
shm_buffer_destroy(struct wl_client * client,struct wl_resource * resource)133 shm_buffer_destroy(struct wl_client *client, struct wl_resource *resource)
134 {
135 wl_resource_destroy(resource);
136 }
137
138 static const struct wl_buffer_interface shm_buffer_interface = {
139 shm_buffer_destroy
140 };
141
142 static bool
format_is_supported(struct wl_client * client,uint32_t format)143 format_is_supported(struct wl_client *client, uint32_t format)
144 {
145 struct wl_display *display = wl_client_get_display(client);
146 struct wl_array *formats;
147 uint32_t *p;
148
149 switch (format) {
150 case WL_SHM_FORMAT_ARGB8888:
151 case WL_SHM_FORMAT_XRGB8888:
152 return true;
153 default:
154 formats = wl_display_get_additional_shm_formats(display);
155 wl_array_for_each(p, formats)
156 if (*p == format)
157 return true;
158 }
159
160 return false;
161 }
162
163 static void
shm_pool_create_buffer(struct wl_client * client,struct wl_resource * resource,uint32_t id,int32_t offset,int32_t width,int32_t height,int32_t stride,uint32_t format)164 shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource,
165 uint32_t id, int32_t offset,
166 int32_t width, int32_t height,
167 int32_t stride, uint32_t format)
168 {
169 struct wl_shm_pool *pool = wl_resource_get_user_data(resource);
170 struct wl_shm_buffer *buffer;
171
172 if (!format_is_supported(client, format)) {
173 wl_resource_post_error(resource,
174 WL_SHM_ERROR_INVALID_FORMAT,
175 "invalid format 0x%x", format);
176 return;
177 }
178
179 if (offset < 0 || width <= 0 || height <= 0 || stride < width ||
180 INT32_MAX / stride <= height ||
181 offset > pool->size - stride * height) {
182 wl_resource_post_error(resource,
183 WL_SHM_ERROR_INVALID_STRIDE,
184 "invalid width, height or stride (%dx%d, %u)",
185 width, height, stride);
186 return;
187 }
188
189 buffer = malloc(sizeof *buffer);
190 if (buffer == NULL) {
191 wl_client_post_no_memory(client);
192 return;
193 }
194
195 buffer->width = width;
196 buffer->height = height;
197 buffer->format = format;
198 buffer->stride = stride;
199 buffer->offset = offset;
200 buffer->pool = pool;
201 pool->internal_refcount++;
202
203 buffer->resource =
204 wl_resource_create(client, &wl_buffer_interface, 1, id);
205 if (buffer->resource == NULL) {
206 wl_client_post_no_memory(client);
207 shm_pool_unref(pool, false);
208 free(buffer);
209 return;
210 }
211
212 wl_resource_set_implementation(buffer->resource,
213 &shm_buffer_interface,
214 buffer, destroy_buffer);
215 }
216
217 static void
destroy_pool(struct wl_resource * resource)218 destroy_pool(struct wl_resource *resource)
219 {
220 struct wl_shm_pool *pool = wl_resource_get_user_data(resource);
221
222 shm_pool_unref(pool, false);
223 }
224
225 static void
shm_pool_destroy(struct wl_client * client,struct wl_resource * resource)226 shm_pool_destroy(struct wl_client *client, struct wl_resource *resource)
227 {
228 wl_resource_destroy(resource);
229 }
230
231 static void
shm_pool_resize(struct wl_client * client,struct wl_resource * resource,int32_t size)232 shm_pool_resize(struct wl_client *client, struct wl_resource *resource,
233 int32_t size)
234 {
235 struct wl_shm_pool *pool = wl_resource_get_user_data(resource);
236
237 if (size < pool->size) {
238 wl_resource_post_error(resource,
239 WL_SHM_ERROR_INVALID_FD,
240 "shrinking pool invalid");
241 return;
242 }
243
244 pool->new_size = size;
245
246 /* If the compositor has taken references on this pool it
247 * may be caching pointers into it. In that case we
248 * defer the resize (which may move the entire mapping)
249 * until the compositor finishes dereferencing the pool.
250 */
251 if (pool->external_refcount == 0)
252 shm_pool_finish_resize(pool);
253 }
254
255 static const struct wl_shm_pool_interface shm_pool_interface = {
256 shm_pool_create_buffer,
257 shm_pool_destroy,
258 shm_pool_resize
259 };
260
261 static void
shm_create_pool(struct wl_client * client,struct wl_resource * resource,uint32_t id,int fd,int32_t size)262 shm_create_pool(struct wl_client *client, struct wl_resource *resource,
263 uint32_t id, int fd, int32_t size)
264 {
265 struct wl_shm_pool *pool;
266 int seals;
267
268 if (size <= 0) {
269 wl_resource_post_error(resource,
270 WL_SHM_ERROR_INVALID_STRIDE,
271 "invalid size (%d)", size);
272 goto err_close;
273 }
274
275 pool = malloc(sizeof *pool);
276 if (pool == NULL) {
277 wl_client_post_no_memory(client);
278 goto err_close;
279 }
280
281 #ifdef HAVE_MEMFD_CREATE
282 seals = fcntl(fd, F_GET_SEALS);
283 if (seals == -1)
284 seals = 0;
285 pool->sigbus_is_impossible = (seals & F_SEAL_SHRINK) ? true : false;
286 #else
287 pool->sigbus_is_impossible = false;
288 #endif
289
290 pool->internal_refcount = 1;
291 pool->external_refcount = 0;
292 pool->size = size;
293 pool->new_size = size;
294 pool->data = mmap(NULL, size,
295 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
296 if (pool->data == MAP_FAILED) {
297 wl_resource_post_error(resource,
298 WL_SHM_ERROR_INVALID_FD,
299 "failed mmap fd %d: %s", fd,
300 strerror(errno));
301 goto err_free;
302 }
303 close(fd);
304
305 pool->resource =
306 wl_resource_create(client, &wl_shm_pool_interface, 1, id);
307 if (!pool->resource) {
308 wl_client_post_no_memory(client);
309 munmap(pool->data, pool->size);
310 free(pool);
311 return;
312 }
313
314 wl_resource_set_implementation(pool->resource,
315 &shm_pool_interface,
316 pool, destroy_pool);
317
318 return;
319
320 err_free:
321 free(pool);
322 err_close:
323 close(fd);
324 }
325
326 static const struct wl_shm_interface shm_interface = {
327 shm_create_pool
328 };
329
330 static void
bind_shm(struct wl_client * client,void * data,uint32_t version,uint32_t id)331 bind_shm(struct wl_client *client,
332 void *data, uint32_t version, uint32_t id)
333 {
334 struct wl_resource *resource;
335 struct wl_display *display = wl_client_get_display(client);
336 struct wl_array *additional_formats;
337 uint32_t *p;
338
339 resource = wl_resource_create(client, &wl_shm_interface, 1, id);
340 if (!resource) {
341 wl_client_post_no_memory(client);
342 return;
343 }
344
345 wl_resource_set_implementation(resource, &shm_interface, data, NULL);
346
347 wl_shm_send_format(resource, WL_SHM_FORMAT_ARGB8888);
348 wl_shm_send_format(resource, WL_SHM_FORMAT_XRGB8888);
349
350 additional_formats = wl_display_get_additional_shm_formats(display);
351 wl_array_for_each(p, additional_formats)
352 wl_shm_send_format(resource, *p);
353 }
354
355 WL_EXPORT int
wl_display_init_shm(struct wl_display * display)356 wl_display_init_shm(struct wl_display *display)
357 {
358 if (!wl_global_create(display, &wl_shm_interface, 1, NULL, bind_shm))
359 return -1;
360
361 return 0;
362 }
363
364 WL_EXPORT struct wl_shm_buffer *
wl_shm_buffer_get(struct wl_resource * resource)365 wl_shm_buffer_get(struct wl_resource *resource)
366 {
367 if (resource == NULL)
368 return NULL;
369
370 if (wl_resource_instance_of(resource, &wl_buffer_interface,
371 &shm_buffer_interface))
372 return wl_resource_get_user_data(resource);
373 else
374 return NULL;
375 }
376
377 WL_EXPORT int32_t
wl_shm_buffer_get_stride(struct wl_shm_buffer * buffer)378 wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer)
379 {
380 return buffer->stride;
381 }
382
383
384 /** Get a pointer to the memory for the SHM buffer
385 *
386 * \param buffer The buffer object
387 *
388 * Returns a pointer which can be used to read the data contained in
389 * the given SHM buffer.
390 *
391 * As this buffer is memory-mapped, reading from it may generate
392 * SIGBUS signals. This can happen if the client claims that the
393 * buffer is larger than it is or if something truncates the
394 * underlying file. To prevent this signal from causing the compositor
395 * to crash you should call wl_shm_buffer_begin_access and
396 * wl_shm_buffer_end_access around code that reads from the memory.
397 *
398 * \memberof wl_shm_buffer
399 */
400 WL_EXPORT void *
wl_shm_buffer_get_data(struct wl_shm_buffer * buffer)401 wl_shm_buffer_get_data(struct wl_shm_buffer *buffer)
402 {
403 assert(buffer->pool);
404
405 if (!buffer->pool)
406 return NULL;
407
408 if (buffer->pool->external_refcount &&
409 (buffer->pool->size != buffer->pool->new_size))
410 wl_log("Buffer address requested when its parent pool "
411 "has an external reference and a deferred resize "
412 "pending.\n");
413 return buffer->pool->data + buffer->offset;
414 }
415
416 WL_EXPORT uint32_t
wl_shm_buffer_get_format(struct wl_shm_buffer * buffer)417 wl_shm_buffer_get_format(struct wl_shm_buffer *buffer)
418 {
419 return buffer->format;
420 }
421
422 WL_EXPORT int32_t
wl_shm_buffer_get_width(struct wl_shm_buffer * buffer)423 wl_shm_buffer_get_width(struct wl_shm_buffer *buffer)
424 {
425 return buffer->width;
426 }
427
428 WL_EXPORT int32_t
wl_shm_buffer_get_height(struct wl_shm_buffer * buffer)429 wl_shm_buffer_get_height(struct wl_shm_buffer *buffer)
430 {
431 return buffer->height;
432 }
433
434 /** Get a reference to a shm_buffer's shm_pool
435 *
436 * \param buffer The buffer object
437 *
438 * Returns a pointer to a buffer's shm_pool and increases the
439 * shm_pool refcount.
440 *
441 * The compositor must remember to call wl_shm_pool_unref when
442 * it no longer needs the reference to ensure proper destruction
443 * of the pool.
444 *
445 * \memberof wl_shm_buffer
446 * \sa wl_shm_pool_unref
447 */
448 WL_EXPORT struct wl_shm_pool *
wl_shm_buffer_ref_pool(struct wl_shm_buffer * buffer)449 wl_shm_buffer_ref_pool(struct wl_shm_buffer *buffer)
450 {
451 assert(buffer->pool->internal_refcount +
452 buffer->pool->external_refcount);
453
454 buffer->pool->external_refcount++;
455 return buffer->pool;
456 }
457
458 /** Unreference a shm_pool
459 *
460 * \param pool The pool object
461 *
462 * Drops a reference to a wl_shm_pool object.
463 *
464 * This is only necessary if the compositor has explicitly
465 * taken a reference with wl_shm_buffer_ref_pool(), otherwise
466 * the pool will be automatically destroyed when appropriate.
467 *
468 * \memberof wl_shm_pool
469 * \sa wl_shm_buffer_ref_pool
470 */
471 WL_EXPORT void
wl_shm_pool_unref(struct wl_shm_pool * pool)472 wl_shm_pool_unref(struct wl_shm_pool *pool)
473 {
474 shm_pool_unref(pool, true);
475 }
476
477 static void
reraise_sigbus(void)478 reraise_sigbus(void)
479 {
480 /* If SIGBUS is raised for some other reason than accessing
481 * the pool then we'll uninstall the signal handler so we can
482 * reraise it. This would presumably kill the process */
483 sigaction(SIGBUS, &wl_shm_old_sigbus_action, NULL);
484 raise(SIGBUS);
485 }
486
487 static void
sigbus_handler(int signum,siginfo_t * info,void * context)488 sigbus_handler(int signum, siginfo_t *info, void *context)
489 {
490 struct wl_shm_sigbus_data *sigbus_data =
491 pthread_getspecific(wl_shm_sigbus_data_key);
492 struct wl_shm_pool *pool;
493
494 if (sigbus_data == NULL) {
495 reraise_sigbus();
496 return;
497 }
498
499 pool = sigbus_data->current_pool;
500
501 /* If the offending address is outside the mapped space for
502 * the pool then the error is a real problem so we'll reraise
503 * the signal */
504 if (pool == NULL ||
505 (char *) info->si_addr < pool->data ||
506 (char *) info->si_addr >= pool->data + pool->size) {
507 reraise_sigbus();
508 return;
509 }
510
511 sigbus_data->fallback_mapping_used = 1;
512
513 /* This should replace the previous mapping */
514 if (mmap(pool->data, pool->size,
515 PROT_READ | PROT_WRITE,
516 MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS,
517 0, 0) == (void *) -1) {
518 reraise_sigbus();
519 return;
520 }
521 }
522
523 static void
destroy_sigbus_data(void * data)524 destroy_sigbus_data(void *data)
525 {
526 struct wl_shm_sigbus_data *sigbus_data = data;
527
528 free(sigbus_data);
529 }
530
531 static void
init_sigbus_data_key(void)532 init_sigbus_data_key(void)
533 {
534 struct sigaction new_action = {
535 .sa_sigaction = sigbus_handler,
536 .sa_flags = SA_SIGINFO | SA_NODEFER
537 };
538
539 sigemptyset(&new_action.sa_mask);
540
541 sigaction(SIGBUS, &new_action, &wl_shm_old_sigbus_action);
542
543 pthread_key_create(&wl_shm_sigbus_data_key, destroy_sigbus_data);
544 }
545
546 /** Mark that the given SHM buffer is about to be accessed
547 *
548 * \param buffer The SHM buffer
549 *
550 * An SHM buffer is a memory-mapped file given by the client.
551 * According to POSIX, reading from a memory-mapped region that
552 * extends off the end of the file will cause a SIGBUS signal to be
553 * generated. Normally this would cause the compositor to terminate.
554 * In order to make the compositor robust against clients that change
555 * the size of the underlying file or lie about its size, you should
556 * protect access to the buffer by calling this function before
557 * reading from the memory and call wl_shm_buffer_end_access
558 * afterwards. This will install a signal handler for SIGBUS which
559 * will prevent the compositor from crashing.
560 *
561 * After calling this function the signal handler will remain
562 * installed for the lifetime of the compositor process. Note that
563 * this function will not work properly if the compositor is also
564 * installing its own handler for SIGBUS.
565 *
566 * If a SIGBUS signal is received for an address within the range of
567 * the SHM pool of the given buffer then the client will be sent an
568 * error event when wl_shm_buffer_end_access is called. If the signal
569 * is for an address outside that range then the signal handler will
570 * reraise the signal which would will likely cause the compositor to
571 * terminate.
572 *
573 * It is safe to nest calls to these functions as long as the nested
574 * calls are all accessing the same buffer. The number of calls to
575 * wl_shm_buffer_end_access must match the number of calls to
576 * wl_shm_buffer_begin_access. These functions are thread-safe and it
577 * is allowed to simultaneously access different buffers or the same
578 * buffer from multiple threads.
579 *
580 * \memberof wl_shm_buffer
581 */
582 WL_EXPORT void
wl_shm_buffer_begin_access(struct wl_shm_buffer * buffer)583 wl_shm_buffer_begin_access(struct wl_shm_buffer *buffer)
584 {
585 struct wl_shm_pool *pool = buffer->pool;
586 struct wl_shm_sigbus_data *sigbus_data;
587
588 if (pool->sigbus_is_impossible)
589 return;
590
591 pthread_once(&wl_shm_sigbus_once, init_sigbus_data_key);
592
593 sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key);
594 if (sigbus_data == NULL) {
595 sigbus_data = zalloc(sizeof *sigbus_data);
596 if (sigbus_data == NULL)
597 return;
598
599 pthread_setspecific(wl_shm_sigbus_data_key, sigbus_data);
600 }
601
602 assert(sigbus_data->current_pool == NULL ||
603 sigbus_data->current_pool == pool);
604
605 sigbus_data->current_pool = pool;
606 sigbus_data->access_count++;
607 }
608
609 /** Ends the access to a buffer started by wl_shm_buffer_begin_access
610 *
611 * \param buffer The SHM buffer
612 *
613 * This should be called after wl_shm_buffer_begin_access once the
614 * buffer is no longer being accessed. If a SIGBUS signal was
615 * generated in-between these two calls then the resource for the
616 * given buffer will be sent an error.
617 *
618 * \memberof wl_shm_buffer
619 */
620 WL_EXPORT void
wl_shm_buffer_end_access(struct wl_shm_buffer * buffer)621 wl_shm_buffer_end_access(struct wl_shm_buffer *buffer)
622 {
623 struct wl_shm_pool *pool = buffer->pool;
624 struct wl_shm_sigbus_data *sigbus_data;
625
626 if (pool->sigbus_is_impossible)
627 return;
628
629 sigbus_data = pthread_getspecific(wl_shm_sigbus_data_key);
630 assert(sigbus_data && sigbus_data->access_count >= 1);
631
632 if (--sigbus_data->access_count == 0) {
633 if (sigbus_data->fallback_mapping_used) {
634 wl_resource_post_error(buffer->resource,
635 WL_SHM_ERROR_INVALID_FD,
636 "error accessing SHM buffer");
637 sigbus_data->fallback_mapping_used = 0;
638 }
639
640 sigbus_data->current_pool = NULL;
641 }
642 }
643
644 /** \cond */ /* Deprecated functions below. */
645
646 WL_EXPORT struct wl_shm_buffer *
wl_shm_buffer_create(struct wl_client * client,uint32_t id,int32_t width,int32_t height,int32_t stride,uint32_t format)647 wl_shm_buffer_create(struct wl_client *client,
648 uint32_t id, int32_t width, int32_t height,
649 int32_t stride, uint32_t format)
650 {
651 return NULL;
652 }
653
654 /** \endcond */
655
656 /* Functions at the end of this file are deprecated. Instead of adding new
657 * code here, add it before the comment above that states:
658 * Deprecated functions below.
659 */
660