]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] hs_helper: fix use-after-free in Redis async cache callbacks
authorVsevolod Stakhov <vsevolod@rspamd.com>
Thu, 29 Jan 2026 15:58:41 +0000 (15:58 +0000)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Thu, 29 Jan 2026 15:58:41 +0000 (15:58 +0000)
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).

src/hs_helper.c

index f6923a0285fc123f493b565ba30fd42c2cfaae59..f7bd5ed25a84436f12f10720b9cf6f62738651b0 100644 (file)
@@ -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