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