Skip to content

quinn-proto: harden congestion arithmetic and resolve token cache panic surface#2706

Open
MerlijnW70 wants to merge 3 commits into
quinn-rs:mainfrom
MerlijnW70:fix/proto-stability
Open

quinn-proto: harden congestion arithmetic and resolve token cache panic surface#2706
MerlijnW70 wants to merge 3 commits into
quinn-rs:mainfrom
MerlijnW70:fix/proto-stability

Conversation

@MerlijnW70

Copy link
Copy Markdown

While reviewing the congestion control and token management logic, I
identified several scenarios — particularly under extreme ACK-aggregation
and token-replay conditions — where unwrap() calls and arithmetic
operations could trigger panics.

This PR implements hardening measures so the protocol degrades gracefully
rather than crashing under these edge cases:

Congestion control — replaced potentially overflowing arithmetic in
NewReno and BBR with saturating operations (saturating_add,
saturating_mul) on the ACK-driven window, byte counters, and
ack-aggregation state.

Token cache — improved safety in token_memory_cache.rs and
bloom_token_log.rs by recovering from a poisoned lock
(PoisonError::into_inner) instead of lock().unwrap(), so one panicking
caller can no longer poison the cache for the whole system.
BloomTokenLog additionally computes token expiry with
SystemTime::checked_add, rejecting an overflowing lifetime as token reuse
instead of panicking while the lock is held.

Regression tests — added 3 unit tests driving these paths with extreme
inputs (u64::MAX byte counts, an overflowing token lifetime) to verify
they are now contained and panic-free. Each test panicked prior to the fix.

This addresses the stability concerns outlined in #2702. The changes are
regression-tested and introduce no public API signature changes.

@djc

djc commented Jun 26, 2026

Copy link
Copy Markdown
Member

Please use three separate commits for this.

An extreme (forged or aggregated) ACK byte count could overflow the
congestion window, byte counters, and ack-aggregation state, panicking in
debug builds. Use saturating_add/saturating_mul on these paths; for all
realistic values the behaviour is unchanged.

Adds regression tests driving each controller with u64::MAX ACK byte counts.
A caller panicking while holding the cache mutex would poison it, after
which every later insert/take panicked on lock().unwrap(). Recover the
guard with PoisonError::into_inner instead: a poisoned lock only means a
previous holder panicked, and the cache state remains a valid token store.
check_and_insert computed issued + lifetime while holding the state mutex.
An extreme lifetime overflowed the SystemTime addition and panicked with
the guard held, poisoning the mutex so every later validation panicked on
lock(). Compute the expiry up front with SystemTime::checked_add and reject
an overflowing lifetime as token reuse, and recover a poisoned lock instead
of unwrapping it.

Adds a regression test using an overflowing token lifetime.
@MerlijnW70
MerlijnW70 force-pushed the fix/proto-stability branch from 5247ea0 to c41a271 Compare June 26, 2026 09:59
@MerlijnW70

Copy link
Copy Markdown
Author

Split into three commits as requested — one per subsystem (congestion arithmetic, token-cache lock recovery, BloomTokenLog lifetime), each carrying its own regression test.

@djc djc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks okay to me, thanks.

@djc

djc commented Jun 29, 2026

Copy link
Copy Markdown
Member

Are you interested in backporting these to the 0.11.x branch?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants