3.8 KiB
actix-web — CWE-407 Disclosure Brief
2026-03-27 · Patch available — awaiting upstream merge
Finding
Two O(n²) defects in actix-web's WebSocket extension deduplication and protocol negotiation. Both patched. Patches ready for upstream review.
The Defects
actix-web-0001 (PATCHED — HIGH): actix-http/src/ws/mod.rs
// update_unique() — per WebSocket upgrade response:
fn update_unique(vec: &mut Vec<ExtensionParam>, params: Vec<ExtensionParam>) {
for param in params {
if !vec.contains(¶m) { // O(n) Vec::contains() per param
vec.push(param);
}
}
}
// O(n²) total — called per WebSocket response extension
Vec::contains() performs O(n) scan per param in update_unique(). For n extension params: O(n²) total. Measured ratio: 300×.
actix-web-0002 (PATCHED — HIGH): actix-http/src/ws/codec.rs
// ws_protocol_negotiate() — per WebSocket upgrade:
fn ws_protocol_negotiate(requested: &[&str], supported: &[&str]) -> Option<String> {
for proto in requested {
if supported.contains(proto) { // O(P) Vec::contains() per requested protocol
return Some(proto.to_string());
}
}
None
}
// O(R×P) per WS upgrade — requested × supported protocols
Vec::contains() O(P) scan per requested protocol in ws_protocol_negotiate(). O(R × P) per WebSocket upgrade. Fix: HashSet. Measured ratio: 50×.
Complexity Proof
For n=300 extension parameters per WebSocket upgrade:
- O(n²) = 90,000 comparisons per upgrade
- Fixed:
HashSetshadow → O(n) - 300× measured ratio.
actix-web-0002: For R=50 requested protocols, P=50 supported protocols:
- Per upgrade: O(R×P) = 2,500 comparisons
- Fixed:
HashSet<&str>from supported → O(R) - 50× measured ratio.
Impact
All actix-web applications using WebSocket connections. actix-web-0001 (update_unique()) fires during extension negotiation in the WebSocket handshake response construction. actix-web-0002 (ws_protocol_negotiate()) fires on every WebSocket upgrade with protocol negotiation. Applications with many WebSocket clients doing simultaneous upgrades (real-time applications, chat servers, collaborative tools) hit both defects on every connection. actix-web is the most popular Rust web framework with one of the highest performance benchmarks.
The Fix
Add a HashSet shadow alongside the Vec:
// Before
fn update_unique(vec: &mut Vec<ExtensionParam>, params: Vec<ExtensionParam>) {
for param in params {
if !vec.contains(¶m) { vec.push(param); } // O(n)
}
}
// After
// CWE-407 fix: HashSet shadow for O(1) dedup instead of O(n) Vec::contains.
fn update_unique(vec: &mut Vec<ExtensionParam>, params: Vec<ExtensionParam>) {
let mut seen: HashSet<&ExtensionParam> = vec.iter().collect();
for param in params {
if seen.insert(¶m) {
vec.push(param);
}
}
}
actix-web-0002: Build HashSet<&str> from supported protocols before the loop:
// Before
if supported.contains(proto) { ... } // O(P) Vec scan
// After
// CWE-407 fix: HashSet for O(1) contains() instead of O(P) Vec::contains scan.
let supported_set: HashSet<&&str> = supported.iter().collect();
if supported_set.contains(proto) { ... }
Patch
defects/actix-web/patch/actix-web-0001-ws-update-unique-hashset.patch
defects/actix-web/patch/actix-web-0002-ws-protocol-negotiate-hashset.patch
What We Ask
- Confirm receipt and assign a GitHub Security Advisory or issue reference.
- Validate the patch against your WebSocket and extension negotiation test suite.
- Assess CVE eligibility — 300× overhead on every WebSocket upgrade response.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
Contact: see cover email. This brief is confidential until coordinated disclosure.