From a009834155576356719c79512a73b1f9affacf7f Mon Sep 17 00:00:00 2001 From: Mike Perry Date: Tue, 3 Jun 2025 19:44:21 +0000 Subject: [PATCH] Add check of MaxHSDirCacheBytes every hsdesc upload --- src/feature/hs/hs_cache.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c index 08afc37364..66aab2a506 100644 --- a/src/feature/hs/hs_cache.c +++ b/src/feature/hs/hs_cache.c @@ -26,6 +26,11 @@ #include "feature/nodelist/networkstatus_st.h" +/** + * Spare room for 1000 descriptors when pruning cache to avoid thrashing + * and memory fragmentation. */ +#define HSCACHE_PRUNE_SPARE_ROOM (1000 * HS_DESC_MAX_LEN) + /* Total counter of the cache size. */ static size_t hs_cache_total_allocation = 0; @@ -148,6 +153,28 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc) tor_assert(desc); + /* Check if we've exceeded the MaxHSDirCacheBytes limit after adding + * this descriptor. If so, prune excess bytes leaving room for more. */ + const size_t max_cache_bytes = get_options()->MaxHSDirCacheBytes; + const size_t current_cache_bytes = hs_cache_get_total_allocation(); + if (max_cache_bytes > 0 && current_cache_bytes > max_cache_bytes) { + /* We prune only 1000 descriptors worth of memory here because + * pruning is an expensive O(n^2) option to keep finding lowest + * download count descs. */ + size_t bytes_to_remove = current_cache_bytes/2; + /* Ensure user didn't set a really low max hsdir cache vlue */ + if (HSCACHE_PRUNE_SPARE_ROOM < max_cache_bytes) { + bytes_to_remove = current_cache_bytes - + (max_cache_bytes - HSCACHE_PRUNE_SPARE_ROOM); + } + size_t removed = hs_cache_handle_oom(bytes_to_remove); + static ratelim_t hs_cache_oom_ratelim = RATELIM_INIT(600); + log_fn_ratelim(&hs_cache_oom_ratelim, LOG_NOTICE, LD_REND, + "HSDir cache exceeded limit (%zu > %zu bytes). " + "Pruned %zu bytes during an HS descriptor upload.", + current_cache_bytes, max_cache_bytes, removed); + } + /* Verify if we have an entry in the cache for that key and if yes, check * if we should replace it? */ cache_entry = lookup_v3_desc_as_dir(desc->key); @@ -170,9 +197,13 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc) hs_cache_decrement_allocation(cache_get_dir_entry_size(cache_entry)); cache_dir_desc_free(cache_entry); } + /* Store the descriptor we just got. We are sure here that either we * don't have the entry or we have a newer descriptor and the old one - * has been removed from the cache. */ + * has been removed from the cache. We do this *after* pruning + * other descriptors so that this descriptor is not immediately pruned, + * if new. This prevents probing to detect OOM threshholds via its + * absence. */ store_v3_desc_as_dir(desc); /* Update our total cache size with this entry for the OOM. This uses the -- 2.47.3