From: Vsevolod Stakhov Date: Thu, 29 Jan 2026 15:58:41 +0000 (+0000) Subject: [Fix] hs_helper: fix use-after-free in Redis async cache callbacks X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1799ceac70bd52cfb516b4ff8665c3db983409c5;p=thirdparty%2Frspamd.git [Fix] hs_helper: fix use-after-free in Redis async cache callbacks Remove dangerous ev_run(EVRUN_NOWAIT) calls from inside Redis callback chains in rspamd_hs_helper_mp_exists_cb and rspamd_hs_helper_remap_exists_cb. Calling ev_run() inside a callback can trigger Lua GC which may try to finalize lua_redis userdata while we're still processing the callback, causing lua_redis_gc to access already-freed memory. Also add missing REF_RELEASE calls at the end of both callbacks to properly release the reference from exists_async (matching the exists==true path). --- diff --git a/src/hs_helper.c b/src/hs_helper.c index f6923a028..f7bd5ed25 100644 --- a/src/hs_helper.c +++ b/src/hs_helper.c @@ -942,14 +942,19 @@ rspamd_hs_helper_mp_exists_cb(gboolean success, /* Need to compile+store */ rspamd_worker_set_busy(mpctx->worker, mpctx->ctx->event_loop, "compile multipattern"); - /* Flush the busy notification before blocking on compilation */ - ev_run(mpctx->ctx->event_loop, EVRUN_NOWAIT); - /* Use saved mp pointer - pending array may have been freed by ev_run */ + /* + * DO NOT call ev_run() here - we're inside a Redis callback chain and + * ev_run can trigger Lua GC which may try to finalize lua_redis userdata + * while we're still processing. The busy notification will be sent on + * the next event loop iteration after this callback returns. + */ mpctx->compile_cb_called = FALSE; REF_RETAIN(mpctx); rspamd_multipattern_compile_hs_to_cache_async(mp, mpctx->ctx->hs_dir, mpctx->ctx->event_loop, rspamd_hs_helper_mp_compiled_cb, mpctx); + /* Release the reference from exists_async callback */ + REF_RELEASE(mpctx); } static void @@ -1138,14 +1143,19 @@ rspamd_hs_helper_remap_exists_cb(gboolean success, /* Need to compile+store */ rspamd_worker_set_busy(rmctx->worker, rmctx->ctx->event_loop, "compile regexp map"); - /* Flush the busy notification before blocking on compilation */ - ev_run(rmctx->ctx->event_loop, EVRUN_NOWAIT); - /* Use saved re_map pointer - pending array may have been freed by ev_run */ + /* + * DO NOT call ev_run() here - we're inside a Redis callback chain and + * ev_run can trigger Lua GC which may try to finalize lua_redis userdata + * while we're still processing. The busy notification will be sent on + * the next event loop iteration after this callback returns. + */ rmctx->compile_cb_called = FALSE; REF_RETAIN(rmctx); rspamd_regexp_map_compile_hs_to_cache_async(re_map, rmctx->ctx->hs_dir, rmctx->ctx->event_loop, rspamd_hs_helper_remap_compiled_cb, rmctx); + /* Release the reference from exists_async callback */ + REF_RELEASE(rmctx); } static void