java-topology/defects/rustc/patch/rustc-0004-finalize-imports-ambiguity-errors-linear-scan.md

4.5 KiB
Raw Permalink Blame History

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: 10041007, 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>:

  1. Line 10041007 — closure ambiguity_errors_len filters and counts non-warning errors: errors.iter().filter(|e| e.warning.is_none()).count()
  2. Line 1007 — called once to capture prev_ambiguity_errors_len (before resolve_path)
  3. Line 1023 — called again to compute no_ambiguity (after resolve_path)
  4. Line 1232 — inside per_ns closure (runs 23 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.rs lines 10041007, 1023, 1232
  • compiler/rustc_resolve/src/lib.rs line 1279 (ambiguity_errors: Vec<AmbiguityError>)
  • compiler/rustc_resolve/src/lib.rs line 2134 (push site)