fuzzy_lua_ping_storage selected an upstream from rule->read_servers
without checking the result, then dereferenced the NULL pointer in
rspamd_upstream_addr_next(). With the new deferred-DNS upstream layer
this becomes reachable in normal operation (every upstream still
pending), and was already reachable before whenever the alive list
was empty.
Audit of other rspamd_upstream_get / _forced / _except / _token_bucket
call sites in C/C++ (rspamd_proxy.c, libserver/dns.c,
fuzzy_backend_redis.c, http/http_connection.c, libstat http_backend,
the other fuzzy_check sites) confirms they already guard the result
with `if (up)` or a `while (up = ...)` loop; only this site was
unchecked.
Return (false, "no fuzzy storage upstream available for rule X") to
the Lua caller instead of crashing.
else {
struct upstream *selected = rspamd_upstream_get(rule_found->read_servers,
RSPAMD_UPSTREAM_ROUND_ROBIN, NULL, 0);
+ if (selected == NULL) {
+ lua_pushboolean(L, FALSE);
+ lua_pushfstring(L, "no fuzzy storage upstream available for rule %s",
+ rule_found->name);
+ return 2;
+ }
addr = rspamd_upstream_addr_next(selected);
}