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