#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <string.h>
#include <sys/epoll.h>
+#include <sys/mman.h>
#include <sys/un.h>
#ifdef HAVE_SYS_UCRED_H
#include <sys/ucred.h>
fd = accept(sockfd, addr, addrlen);
return set_cloexec_or_close(fd);
}
+
+/*
+ * Fallback function for operating systems that don't implement
+ * mremap(MREMAP_MAYMOVE).
+ */
+void *
+wl_os_mremap_maymove(int fd, void *old_data, ssize_t *old_size,
+ ssize_t new_size, int prot, int flags)
+{
+ void *result;
+ /*
+ * We could try mapping a new block immediately after the current one
+ * with MAP_FIXED, however that is not guaranteed to work and breaks
+ * on CHERI-enabled architectures since the data pointer will still
+ * have the bounds of the previous allocation. As this is not a
+ * performance-critical path, we always map a new region and copy the
+ * old data to the new region.
+ */
+ result = mmap(NULL, new_size, prot, flags, fd, 0);
+ if (result != MAP_FAILED) {
+ /* Copy the data over and unmap the old mapping. */
+ memcpy(result, old_data, *old_size);
+ if (munmap(old_data, *old_size) == 0) {
+ *old_size = 0; /* successfully unmapped old data. */
+ }
+ }
+ return result;
+}
#include <errno.h>
#include <fcntl.h>
+#include "wayland-os.h"
#include "wayland-util.h"
#include "wayland-private.h"
#include "wayland-server.h"
int internal_refcount;
int external_refcount;
char *data;
- int32_t size;
- int32_t new_size;
+ ssize_t size;
+ ssize_t new_size;
+ /* The following three fields are needed for mremap() emulation. */
+ int mmap_fd;
+ int mmap_flags;
+ int mmap_prot;
bool sigbus_is_impossible;
};
int fallback_mapping_used;
};
+static void *
+shm_pool_grow_mapping(struct wl_shm_pool *pool)
+{
+ void *data;
+
+#ifdef MREMAP_MAYMOVE
+ data = mremap(pool->data, pool->size, pool->new_size, MREMAP_MAYMOVE);
+#else
+ data = wl_os_mremap_maymove(pool->mmap_fd, pool->data, &pool->size,
+ pool->new_size, pool->mmap_prot,
+ pool->mmap_flags);
+ if (pool->size != 0) {
+ wl_resource_post_error(pool->resource,
+ WL_SHM_ERROR_INVALID_FD,
+ "leaked old mapping");
+ }
+#endif
+ return data;
+}
+
static void
shm_pool_finish_resize(struct wl_shm_pool *pool)
{
if (pool->size == pool->new_size)
return;
- data = mremap(pool->data, pool->size, pool->new_size, MREMAP_MAYMOVE);
+ data = shm_pool_grow_mapping(pool);
if (data == MAP_FAILED) {
wl_resource_post_error(pool->resource,
WL_SHM_ERROR_INVALID_FD,
return;
munmap(pool->data, pool->size);
+ close(pool->mmap_fd);
free(pool);
}
{
struct wl_shm_pool *pool;
int seals;
+ int prot;
+ int flags;
if (size <= 0) {
wl_resource_post_error(resource,
pool->external_refcount = 0;
pool->size = size;
pool->new_size = size;
- pool->data = mmap(NULL, size,
- PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ prot = PROT_READ | PROT_WRITE;
+ flags = MAP_SHARED;
+ pool->data = mmap(NULL, size, prot, flags, fd, 0);
if (pool->data == MAP_FAILED) {
- wl_resource_post_error(resource,
- WL_SHM_ERROR_INVALID_FD,
+ wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FD,
"failed mmap fd %d: %s", fd,
strerror(errno));
goto err_free;
}
- close(fd);
-
+ /* We may need to keep the fd, prot and flags to emulate mremap(). */
+ pool->mmap_fd = fd;
+ pool->mmap_prot = prot;
+ pool->mmap_flags = flags;
pool->resource =
wl_resource_create(client, &wl_shm_pool_interface, 1, id);
if (!pool->resource) {