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