From: Vsevolod Stakhov Date: Sat, 21 Mar 2026 13:53:12 +0000 (+0000) Subject: [Fix] Fix use-after-free in pending regexp map and multipattern queues X-Git-Tag: 4.0.0~18 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cc373fbce58da3792bee4c24dbe77da898dcfe7f;p=thirdparty%2Frspamd.git [Fix] Fix use-after-free in pending regexp map and multipattern queues 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. --- diff --git a/src/libserver/maps/map_helpers.c b/src/libserver/maps/map_helpers.c index 5f6c276930..f7b4cfbfcc 100644 --- a/src/libserver/maps/map_helpers.c +++ b/src/libserver/maps/map_helpers.c @@ -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); diff --git a/src/libutil/multipattern.c b/src/libutil/multipattern.c index f84a495f00..3605f7c849 100644 --- a/src/libutil/multipattern.c +++ b/src/libutil/multipattern.c @@ -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);