Problem
After appending a deferred packet, handle_query_or_defer always calls self._cancel_any_timers_for_addr(addr) and re-arms self._timers[addr] to fire 400–500 ms in the future. This is the assembly window for legitimate, fragmented TC queries (RFC 6762 §18.5), but it has no upper bound — a peer that streams TC packets faster than 400 ms keeps moving the deadline forward indefinitely, so the deferred list is never flushed. Combined with finding #1's lack of a per-list cap, this is what makes the memory-growth attack open-ended rather than self-limiting after ~500 ms.
Why This Matters
A correctly-behaving sender sends its fragmented TC pieces back-to-back; a malicious sender just keeps the stream open. Without a first-packet-arrival deadline, the listener has no way to decide "stop waiting and process what I have," so memory pressure compounds for as long as the attacker chooses.
Suggested Fix
Track the first TC-arrival time for each addr (e.g. alongside the list in _deferred), and on each new packet refuse to reset the timer further than first_arrival + _TC_DELAY_RANDOM_INTERVAL[1] into the future. When the deadline is hit, flush whatever is queued (or drop the queue) regardless of fresh arrivals. This preserves the legitimate fragment-reassembly window while bounding the worst case.
Details
|
|
| Severity |
🟡 Medium |
| Category |
dos |
| Location |
src/zeroconf/_listener.py:206-218, 220-223 |
| Effort |
⚡ Quick fix |
🤖 Created by Kōan from audit session
Problem
After appending a deferred packet,
handle_query_or_deferalways callsself._cancel_any_timers_for_addr(addr)and re-armsself._timers[addr]to fire 400–500 ms in the future. This is the assembly window for legitimate, fragmented TC queries (RFC 6762 §18.5), but it has no upper bound — a peer that streams TC packets faster than 400 ms keeps moving the deadline forward indefinitely, so the deferred list is never flushed. Combined with finding #1's lack of a per-list cap, this is what makes the memory-growth attack open-ended rather than self-limiting after ~500 ms.Why This Matters
A correctly-behaving sender sends its fragmented TC pieces back-to-back; a malicious sender just keeps the stream open. Without a first-packet-arrival deadline, the listener has no way to decide "stop waiting and process what I have," so memory pressure compounds for as long as the attacker chooses.
Suggested Fix
Track the first TC-arrival time for each addr (e.g. alongside the list in
_deferred), and on each new packet refuse to reset the timer further thanfirst_arrival + _TC_DELAY_RANDOM_INTERVAL[1]into the future. When the deadline is hit, flush whatever is queued (or drop the queue) regardless of fresh arrivals. This preserves the legitimate fragment-reassembly window while bounding the worst case.Details
src/zeroconf/_listener.py:206-218, 220-223🤖 Created by Kōan from audit session