#65 RateLimiter: per-key map never evicts, so source-IP rotation grows memory without bound
RateLimiter keeps a HashMap<String, Vec<u64>> and only prunes the entry for the key being checked:
fn check_at(&self, ip: &str, date: &str, server_salt: &str, now: u64) -> bool {
let key = salted_ip_hash(ip, date, server_salt);
let mut map = self.buckets.lock()...;
let entry = map.entry(key).or_default();
entry.retain(|&t| now.wrapping_sub(t) < 3600); // this key only
...
}
Keys are never removed and there is no global sweep, so every distinct source IP that ever submits adds a permanent entry. Two facts make this cheap to grow:
limiter.check()is called beforecheck_quota_with()and beforestore_submission, so a key is created and a timestamp recorded even when the POST is refused (quota full → 503).- Every request costs the attacker only a connection; source addresses are free on IPv6 (a single /64 is 2^64 addresses) and trivially varied.
Result: unbounded, monotonic memory growth on a public deployment; the process never returns the memory (HashMap never shrinks). A few million connections = hundreds of MB, and it is not reclaimable without a restart.
Fix direction:
- expiry sweep: on insert, drop entries whose newest timestamp is older than the window (or
map.retainwhenmap.len()crosses a threshold) - optionally cap the number of tracked keys; when full, fall back to a single shared bucket (fail closed) rather than growing
- move
limiter.checkafter the cheap rejections if you want refused submissions not to create keys (does not fix rotation, only slows it)
Acceptance:
- entries older than the window are evicted without a request from that IP
- a test inserts N distinct keys, advances the clock past the window, asserts the map shrinks
- map size has a documented bound
Uunknown ·
Fixed in 80497982-9d71-46ca-a8aa-5f4ded049800 (see PR body): bounded 64 connections with immediate 503 + 10s head deadline (#63), 4 concurrent upload-pack runs with 120s timeout and temp-file streaming (#64), 5-minute full-map sweep + 100k key cap in RateLimiter (#65).