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