Summary
Configured plugin directories have a nominal 4,096-entry safety limit, but directory children are loaded completely before that limit is enforced.
Relevant paths:
Both loops increment entries when a queued path is processed. When that path is a directory, they call readdir(path) and push all returned children. A single directory with a very large number of entries is therefore fully allocated before any of those children reach the 4,096-entry check.
Cancellation is checked between queued paths but cannot interrupt the individual full-directory read.
Why this matters
A malicious or accidentally enormous configured plugin can cause significant memory consumption despite the presence of an apparent entry limit. ZIP plugins are checked entry-by-entry during extraction, so directory plugins currently have a weaker resource boundary than archive plugins.
Expected behavior
Traversal should reject the 4,097th discovered entry without first materializing every name in the containing directory, and cancellation should be observed during discovery.
Suggested direction
Use opendir() and its asynchronous iterator in both fingerprinting and copying. Increment and enforce the shared entry count as each child is yielded, check the abort signal inside the iterator loop, and preserve the existing symlink/identity checks.
Add a regression test with more than 4,096 children in one directory and a cancellation test that aborts during directory iteration.
Found by static audit of upstream main at 9c7634b.
Summary
Configured plugin directories have a nominal 4,096-entry safety limit, but directory children are loaded completely before that limit is enforced.
Relevant paths:
Both loops increment
entrieswhen a queued path is processed. When that path is a directory, they callreaddir(path)and push all returned children. A single directory with a very large number of entries is therefore fully allocated before any of those children reach the 4,096-entry check.Cancellation is checked between queued paths but cannot interrupt the individual full-directory read.
Why this matters
A malicious or accidentally enormous configured plugin can cause significant memory consumption despite the presence of an apparent entry limit. ZIP plugins are checked entry-by-entry during extraction, so directory plugins currently have a weaker resource boundary than archive plugins.
Expected behavior
Traversal should reject the 4,097th discovered entry without first materializing every name in the containing directory, and cancellation should be observed during discovery.
Suggested direction
Use
opendir()and its asynchronous iterator in both fingerprinting and copying. Increment and enforce the shared entry count as each child is yielded, check the abort signal inside the iterator loop, and preserve the existing symlink/identity checks.Add a regression test with more than 4,096 children in one directory and a cancellation test that aborts during directory iteration.
Found by static audit of upstream
mainat9c7634b.