From: Vsevolod Stakhov Date: Fri, 3 Oct 2025 21:25:02 +0000 (+0100) Subject: [CritFix] Prevent time_t overflow in HTTP map expires header processing X-Git-Tag: 3.13.2~6^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fb1a41b80cc01c90c0a0ab03b57d1e34e8c087bb;p=thirdparty%2Frspamd.git [CritFix] Prevent time_t overflow in HTTP map expires header processing Add validation to detect and reject absurdly invalid or overflow-inducing expires headers (>1 year in future). When expires header is invalid or causes overflow, properly call rspamd_http_map_process_next_check with expires=0 instead of setting map->next_check=0 which left stale overflow values. This prevents crashes and invalid scheduling like 'next check at Thu, 09 Nov 438498967' when servers send malformed Expires headers. --- diff --git a/src/libserver/maps/map.c b/src/libserver/maps/map.c index c7c5f9839f..817aedaf10 100644 --- a/src/libserver/maps/map.c +++ b/src/libserver/maps/map.c @@ -325,6 +325,7 @@ rspamd_http_map_process_next_check(struct rspamd_map *map, static const time_t max_expires_interval = 8 * 3600; /* 8 hours maximum */ static const time_t min_no_expires_interval = 10 * 60; /* 10 minutes minimum when no expires */ static const time_t liberal_mult = 10; /* Multiplier for liberal interval */ + static const time_t max_valid_time = 365 * 24 * 3600; /* 1 year max for overflow protection */ time_t next_check; /* @@ -332,6 +333,16 @@ rspamd_http_map_process_next_check(struct rspamd_map *map, * Server controls refresh rate via Expires header, client cannot override aggressively */ + /* Sanity check: detect overflow or absurdly invalid expires values */ + if (expires > now) { + /* Check if expires would be more than max_valid_time in future without overflow */ + if (expires > (time_t) (now + max_valid_time) || (expires - now) > max_valid_time) { + msg_warn_map("expires header is absurdly far in future (overflow?) for %s, ignoring", + bk->uri); + expires = 0; /* Treat as no expires header */ + } + } + if (expires > now) { /* Server provided an Expires header */ time_t expires_interval = expires - now; @@ -636,7 +647,11 @@ http_map_finish(struct rspamd_http_connection *conn, } else { msg_info_map("invalid expires header: %T, ignore it", expires_hdr); - map->next_check = 0; + /* Treat invalid expires as no expires header */ + map->next_check = rspamd_http_map_process_next_check(map, bk, msg->date, 0, + (time_t) map->poll_timeout, + etag_hdr != NULL, + msg->last_modified != 0); } } else if (etag_hdr != NULL || msg->last_modified != 0) { @@ -828,7 +843,11 @@ http_map_finish(struct rspamd_http_connection *conn, } else { msg_info_map("invalid expires header: %T, ignore it", expires_hdr); - map->next_check = 0; + /* Treat invalid expires as no expires header */ + map->next_check = rspamd_http_map_process_next_check(map, bk, msg->date, 0, + (time_t) map->poll_timeout, + cbd->data->etag != NULL, + msg->last_modified != 0); has_expires = false; } }