From 20fd3b3af974c8cb0b172a97d4f47ec9c5458566 Mon Sep 17 00:00:00 2001 From: Duncan McIntosh Date: Sat, 16 Oct 2021 20:28:34 -0400 Subject: [PATCH] wayland-shm: Check the size of sealed memory if ignoring SIGBUS handlers In 11623e8f, SIGBUS handlers aren't set if F_SEAL_SHRINK is configured on the memory. This helps avoid setting up handlers with cooperative clients; however, if an application gives an incorrect size, the compositor would access it anyways, figuring SIGBUS is impossible, and crash. This can be fixed by simply removing the seal-checking logic and always setting the signal handler. However, it seems that fstat can give the size of the memfd, so we can check that the size we were told is within the region. Since it's sealed to shrinking, it must never be shrunk in future, so we can really (hopefully) ignore SIGBUS. I was worried that fstat wasn't supported for this, but shm_overview(7) does mention that it is a possible use. The best solution would likely be avoiding SIGBUS entirely with MAP_NOSIGBUS, but that hasn't been merged yet and wouldn't help systems without it (e.g. with older kernels). A proof-of-concept of this crash is attached with the merge request. Running it with this patch gives an invalid-shm error, which is correct. Signed-off-by: Duncan McIntosh --- src/wayland-shm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wayland-shm.c b/src/wayland-shm.c index 27d5b00..63ac0d7 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -299,6 +300,7 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource, uint32_t id, int fd, int32_t size) { struct wl_shm_pool *pool; + struct stat statbuf; int seals; int prot; int flags; @@ -320,7 +322,11 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource, seals = fcntl(fd, F_GET_SEALS); if (seals == -1) seals = 0; - pool->sigbus_is_impossible = (seals & F_SEAL_SHRINK) ? true : false; + + if ((seals & F_SEAL_SHRINK) && fstat(fd, &statbuf) >= 0) + pool->sigbus_is_impossible = statbuf.st_size >= size; + else + pool->sigbus_is_impossible = false; #else pool->sigbus_is_impossible = false; #endif -- 2.7.4