From: Vsevolod Stakhov Date: Sat, 25 Jul 2026 09:18:20 +0000 (+0100) Subject: [Fix] util: harden shared memory mapping helpers X-Git-Tag: 4.1.3~21 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=eb4ba26d62f96247e6343823ea543e23e38dfd1a;p=thirdparty%2Frspamd.git [Fix] util: harden shared memory mapping helpers rspamd_shmem_xmap() mapped whatever the name resolved to, so a fifo, a device or a directory ended up in mmap(2) with a meaningless size, and `*size` was left untouched on failure whilst rspamd_file_xmap(), its file counterpart, has always reported 0 for an empty object and (gsize)-1 for everything else. Reject non-regular and empty segments explicitly, guard the 32 bit address space overflow, keep the failing errno intact across close(2) and give `*size` the same meaning as in the file variant. rspamd_shmem_mkstemp() retried shm_open(2) forever on EEXIST: bound the loop so a permanently colliding namespace cannot spin a worker. --- diff --git a/src/libutil/util.c b/src/libutil/util.c index 1b514df24a..af43641795 100644 --- a/src/libutil/util.c +++ b/src/libutil/util.c @@ -1689,9 +1689,11 @@ void rspamd_uuid_v7_patch_uid(char uuid[37], const char *tag, gsize tag_len) int rspamd_shmem_mkstemp(char *pattern) { + static const unsigned int max_attempts = 1024; int fd = -1; char *nbuf, *xpos; gsize blen; + unsigned int i; xpos = strchr(pattern, 'X'); @@ -1705,14 +1707,20 @@ int rspamd_shmem_mkstemp(char *pattern) rspamd_strlcpy(nbuf, pattern, blen + 1); xpos = nbuf + (xpos - pattern); - for (;;) { + /* + * Bounded retry: a permanently colliding namespace (or a hostile + * neighbour recreating our names) must not spin here forever + */ + for (i = 0; i < max_attempts; i++) { rspamd_random_hex(xpos, blen - (xpos - nbuf)); fd = shm_open(nbuf, O_RDWR | O_EXCL | O_CREAT, 0600); if (fd != -1) { rspamd_strlcpy(pattern, nbuf, blen + 1); - break; + g_free(nbuf); + + return fd; } else if (errno != EEXIST) { g_free(nbuf); @@ -1722,8 +1730,9 @@ int rspamd_shmem_mkstemp(char *pattern) } g_free(nbuf); + errno = EEXIST; - return fd; + return -1; } void rspamd_ptr_array_free_hard(gpointer p) @@ -2046,13 +2055,15 @@ gpointer rspamd_shmem_xmap(const char *fname, unsigned int mode, gsize *size) { - int fd; + int fd, serrno; struct stat sb; gpointer map; g_assert(fname != NULL); g_assert(size != NULL); + *size = (gsize) -1; + #ifdef HAVE_SANE_SHMEM if (mode & PROT_WRITE) { fd = shm_open(fname, O_RDWR, 0); @@ -2074,15 +2085,44 @@ rspamd_shmem_xmap(const char *fname, unsigned int mode, } if (fstat(fd, &sb) == -1) { + serrno = errno; close(fd); + errno = serrno; + + return NULL; + } + + /* Only mappable objects are allowed here: no fifos, devices or dirs */ + if (!S_ISREG(sb.st_mode)) { + close(fd); + errno = EINVAL; + + return NULL; + } + + if (sb.st_size <= 0) { + close(fd); + *size = 0; + errno = EINVAL; + + return NULL; + } + + if ((uint64_t) sb.st_size > (uint64_t) G_MAXSIZE) { + /* Cannot be mapped on this platform (32 bits address space) */ + close(fd); + errno = EFBIG; return NULL; } map = mmap(NULL, sb.st_size, mode, MAP_SHARED, fd, 0); + serrno = errno; close(fd); if (map == MAP_FAILED) { + errno = serrno; + return NULL; } diff --git a/src/libutil/util.h b/src/libutil/util.h index 19052bc83b..6aa9170f2a 100644 --- a/src/libutil/util.h +++ b/src/libutil/util.h @@ -400,9 +400,9 @@ int rspamd_uuid_v7(char uuid_out[37], char *opt_uid_buf, gsize uid_buflen, doubl void rspamd_uuid_v7_patch_uid(char uuid[37], const char *tag, gsize tag_len); /** - * Returns + * Create an exclusively owned shared memory segment using a random name * @param pattern pattern to create (should end with some number of X symbols), modified by this function - * @return + * @return opened descriptor or -1 in case of error (errno is set accordingly) */ int rspamd_shmem_mkstemp(char *pattern); @@ -460,7 +460,8 @@ gpointer rspamd_file_xmap(const char *fname, unsigned int mode, gsize *size, * Map named shared memory segment * @param fname filename * @param mode mode to open - * @param size target size (must NOT be NULL) + * @param size target size (must NOT be NULL); it is set to 0 if the segment is + * empty and to `(gsize)-1` for any other error * @return pointer to memory (should be freed using munmap) or NULL in case of error */ gpointer rspamd_shmem_xmap(const char *fname, unsigned int mode,