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