wave17: 562/240 — vim/qemu/tcl/nodejs/bun/httpd-0002/systemd/emacs
This commit is contained in:
parent
cce7ec653a
commit
dde5ec97fb
13 changed files with 887 additions and 4 deletions
68
defects/bun/patch/bun-0001-bin-folders-dedup-set.md
Normal file
68
defects/bun/patch/bun-0001-bin-folders-dedup-set.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# bun-0001 — CWE-407 in dirInfoUncached bin_folders dedup
|
||||
|
||||
**Severity:** MEDIUM
|
||||
**File:** `src/resolver/resolver.zig`
|
||||
**Symbol:** `dirInfoUncached` — `for (bin_folders.constSlice()) |existing_folder|`
|
||||
|
||||
## Defect
|
||||
|
||||
During module resolution, `dirInfoCachedMaybeLog` walks the directory tree from
|
||||
the target path up to the filesystem root, calling `dirInfoUncached` for each
|
||||
uncached directory level (up to D levels).
|
||||
|
||||
Inside `dirInfoUncached`, when a `.bin` directory is found under `node_modules`,
|
||||
the path is deduplicated against a shared `bin_folders` array using a linear
|
||||
scan before appending:
|
||||
|
||||
```zig
|
||||
// Called inside while (queue_slice.len > 0) loop — D iterations
|
||||
fn dirInfoUncached(...) {
|
||||
// ...
|
||||
for (bin_folders.constSlice()) |existing_folder| { // O(B) scan
|
||||
if (strings.eql(existing_folder, bin_path)) {
|
||||
break :append_bin_dir;
|
||||
}
|
||||
}
|
||||
bin_folders.append(...); // B grows up to D
|
||||
}
|
||||
```
|
||||
|
||||
`bin_folders` is a shared array that accumulates all `.bin` folder paths found
|
||||
across the entire directory tree traversal. Each call to `dirInfoUncached`
|
||||
performs an O(B) scan where B grows with each new `.bin` directory found.
|
||||
With D directory levels each contributing one `.bin` entry, total dedup cost is
|
||||
**O(D²)**.
|
||||
|
||||
In a monorepo with deep nesting (D=50 directories, each with `node_modules/.bin`),
|
||||
this generates ~1,250 comparisons per `require()` call instead of ~50.
|
||||
|
||||
## Fix
|
||||
|
||||
Replace `bin_folders` linear scan with a `std.StringHashMap` or
|
||||
`std.BufSet` for O(1) membership:
|
||||
|
||||
```zig
|
||||
var bin_folders_set = std.StringHashMap(void).init(allocator);
|
||||
defer bin_folders_set.deinit();
|
||||
|
||||
// In the dedup check:
|
||||
const gop = try bin_folders_set.getOrPut(bin_path);
|
||||
if (!gop.found_existing) {
|
||||
bin_folders.append(stored_path) catch {};
|
||||
}
|
||||
```
|
||||
|
||||
## Complexity
|
||||
|
||||
| Scenario | Before | After |
|
||||
|---|---|---|
|
||||
| D=50 nested node_modules dirs | O(D²) = ~1,250 ops | O(D) = 50 ops |
|
||||
| D=200 deep monorepo | O(D²) = ~20,000 ops | O(D) = 200 ops |
|
||||
| Ratio at D=200 | ~100× overhead | 1× |
|
||||
|
||||
## Source location
|
||||
|
||||
`src/resolver/resolver.zig` — `fn dirInfoUncached`, in the
|
||||
`if (r.care_about_bin_folder)` / `append_bin_dir` blocks (~lines 4024–4073),
|
||||
called from within the `while (queue_slice.len > 0)` loop in
|
||||
`dirInfoCachedMaybeLog` (~line 2956).
|
||||
Loading…
Add table
Add a link
Reference in a new issue