]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] util: harden shared memory mapping helpers
authorVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 25 Jul 2026 09:18:20 +0000 (10:18 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 25 Jul 2026 09:18:34 +0000 (10:18 +0100)
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.

src/libutil/util.c
src/libutil/util.h

index 1b514df24a3de8e93a198afe7700b9763f0356f8..af436417951cc984732bcd3fd04cac33948da6e7 100644 (file)
@@ -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;
        }
 
index 19052bc83be3ba1531be04a2e6502218a695be6b..6aa9170f2ae1621a4ef3539b399b89b766712b1e 100644 (file)
@@ -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,