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