]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] Fix use-after-free in pending regexp map and multipattern queues
authorVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 21 Mar 2026 13:53:12 +0000 (13:53 +0000)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 21 Mar 2026 13:53:12 +0000 (13:53 +0000)
When a map reloads while awaiting async hyperscan compilation, the old
re_map/mp is destroyed but rspamd_regexp_map_add_pending (and its
multipattern counterpart) blindly appended a new entry without removing
the stale one. The linear search in find_pending then returned the first
(dangling) pointer, causing a heap-buffer-overflow in rspamd_snprintf
when accessing re_map->re_digest.

Fix by replacing existing entries with the same name in-place instead of
appending duplicates.

src/libserver/maps/map_helpers.c
src/libutil/multipattern.c

index 5f6c27693080b57a89036b2d840a271ca40e9a00..f7b4cfbfcccc42762d0790ff0b8e39f9137d3054 100644 (file)
@@ -1898,6 +1898,24 @@ void rspamd_regexp_map_add_pending(struct rspamd_regexp_map_helper *re_map,
                                                                                  sizeof(struct rspamd_regexp_map_pending));
        }
 
+       /* Replace existing entry with the same name to avoid stale re_map pointers
+        * after map reload (the old re_map gets destroyed but the pending entry
+        * would still reference it, causing use-after-free) */
+       for (unsigned int i = 0; i < pending_regexp_maps->len; i++) {
+               struct rspamd_regexp_map_pending *existing;
+
+               existing = &g_array_index(pending_regexp_maps,
+                                                                 struct rspamd_regexp_map_pending, i);
+               if (strcmp(existing->name, name) == 0) {
+                       existing->re_map = re_map;
+                       rspamd_regexp_map_get_hash(re_map, existing->hash);
+
+                       msg_info_map("updated pending regexp map '%s' (%ud patterns) in compilation queue",
+                                                name, re_map->regexps ? re_map->regexps->len : 0);
+                       return;
+               }
+       }
+
        entry.re_map = re_map;
        entry.name = g_strdup(name);
        rspamd_regexp_map_get_hash(re_map, entry.hash);
index f84a495f008628d96bf745ac2aa55b8367f14672..3605f7c849f2dfcc1f0d72629e8dc16670eada43 100644 (file)
@@ -1322,6 +1322,24 @@ void rspamd_multipattern_add_pending(struct rspamd_multipattern *mp,
                                                                                   sizeof(struct rspamd_multipattern_pending));
        }
 
+       /* Replace existing entry with the same name to avoid stale mp pointers
+        * after reload (the old mp gets destroyed but the pending entry
+        * would still reference it, causing use-after-free) */
+       for (unsigned int i = 0; i < pending_compilations->len; i++) {
+               struct rspamd_multipattern_pending *existing;
+
+               existing = &g_array_index(pending_compilations,
+                                                                 struct rspamd_multipattern_pending, i);
+               if (strcmp(existing->name, name) == 0) {
+                       existing->mp = mp;
+                       rspamd_multipattern_get_hash(mp, existing->hash);
+
+                       msg_info("updated pending multipattern '%s' (%ud patterns) in compilation queue",
+                                        name, mp->cnt);
+                       return;
+               }
+       }
+
        entry.mp = mp;
        entry.name = g_strdup(name);
        rspamd_multipattern_get_hash(mp, entry.hash);