4.5 KiB
UNDF: UNDF-2026-000000773
rustc-0004: CWE-407 — O(I×A) repeated Vec linear scan in finalize_imports
Severity: MEDIUM
CWE: CWE-407 (Algorithmic Complexity — Insufficient Control of Quadratic Complexity)
Target: rust-lang/rust (rustc)
File: compiler/rustc_resolve/src/imports.rs
Lines: 1004–1007, 1023, 1232
Status: PATCHED (unit test PASS)
Description
Resolver::finalize_imports iterates over every import in the crate and calls
finalize_import for each one. Inside finalize_import there are three
O(A) linear scans through self.ambiguity_errors: Vec<AmbiguityError>:
- Line 1004–1007 — closure
ambiguity_errors_lenfilters and counts non-warning errors:errors.iter().filter(|e| e.warning.is_none()).count() - Line 1007 — called once to capture
prev_ambiguity_errors_len(beforeresolve_path) - Line 1023 — called again to compute
no_ambiguity(afterresolve_path) - Line 1232 — inside
per_nsclosure (runs 2–3 times per import):this.ambiguity_errors.iter().any(|error| error.warning.is_none())
Total per finalize_imports pass: O(I × A) where I = number of imports,
A = length of ambiguity_errors.
// imports.rs:1004-1007 (inside finalize_import, called for each import)
let ambiguity_errors_len = |errors: &Vec<AmbiguityError<'_>>| {
errors.iter().filter(|error| error.warning.is_none()).count()
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ O(A) per call
};
let prev_ambiguity_errors_len = ambiguity_errors_len(&self.ambiguity_errors); // O(A)
// ... resolve_path() call ...
let no_ambiguity =
ambiguity_errors_len(&self.ambiguity_errors) == prev_ambiguity_errors_len; // O(A)
// imports.rs:1232 (inside per_ns closure, 2-3 times per import)
let has_ambiguity_error =
this.ambiguity_errors.iter().any(|error| error.warning.is_none()); // O(A)
At I=500 imports, A=200 ambiguity errors: ~700 × 200 = 140,000 comparisons instead of ~700 constant-time reads from a maintained counter.
Root Cause
ambiguity_errors is a plain Vec<AmbiguityError>. The code counts or
checks non-warning entries by scanning the entire vector on every call, rather
than maintaining a separate counter non_warning_ambiguity_error_count: usize
that is incremented/decremented when errors are pushed/popped.
Fix
Maintain non_warning_ambiguity_error_count: usize alongside ambiguity_errors.
Increment it in report_ambiguity_error when warning.is_none().
Replace all .iter().filter(|e| e.warning.is_none()).count() calls with a
single O(1) read of the counter.
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ ambiguity_errors field
ambiguity_errors: Vec<AmbiguityError<'ra>> = Vec::new(),
+ non_warning_ambiguity_error_count: usize = 0,
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ finalize_import — replace the closure and its three call sites
- let ambiguity_errors_len = |errors: &Vec<AmbiguityError<'_>>| {
- errors.iter().filter(|error| error.warning.is_none()).count()
- };
- let prev_ambiguity_errors_len = ambiguity_errors_len(&self.ambiguity_errors);
+ let prev_non_warning_ambiguity_count = self.non_warning_ambiguity_error_count;
// ...resolve_path...
- let no_ambiguity =
- ambiguity_errors_len(&self.ambiguity_errors) == prev_ambiguity_errors_len;
+ let no_ambiguity =
+ self.non_warning_ambiguity_error_count == prev_non_warning_ambiguity_count;
// ...inside per_ns closure...
- let has_ambiguity_error =
- this.ambiguity_errors.iter().any(|error| error.warning.is_none());
+ let has_ambiguity_error = this.non_warning_ambiguity_error_count > 0;
Increment site (in report_ambiguity_error or wherever errors are pushed):
self.ambiguity_errors.push(ambiguity_error);
if ambiguity_error.warning.is_none() {
self.non_warning_ambiguity_error_count += 1;
}
Complexity Before / After
| Scenario | Before | After |
|---|---|---|
| I imports, A ambiguity errors | O(I × A) | O(I) |
| I=500, A=200 | 140,000 ops | 500 ops |
| Ratio | — | 280x |
References
compiler/rustc_resolve/src/imports.rslines 1004–1007, 1023, 1232compiler/rustc_resolve/src/lib.rsline 1279 (ambiguity_errors: Vec<AmbiguityError>)compiler/rustc_resolve/src/lib.rsline 2134 (push site)