Skip to content

vfs: prevent stack overflow in recursive readdir on circular symlinks#64149

Open
AkshatOP wants to merge 1 commit into
nodejs:mainfrom
AkshatOP:fix-vfs-readdir-circular-symlink
Open

vfs: prevent stack overflow in recursive readdir on circular symlinks#64149
AkshatOP wants to merge 1 commit into
nodejs:mainfrom
AkshatOP:fix-vfs-readdir-circular-symlink

Conversation

@AkshatOP

Copy link
Copy Markdown

MemoryProvider#readdirSync with recursive: true follows symlinks to directories during traversal but did not bound the number of symlinks followed along a branch. A circular symlink therefore caused unbounded recursion in the internal walk() helper until the call stack was exhausted, crashing the process with RangeError: Maximum call stack size exceeded. Both the synchronous and promise-based variants were affected. The existing kMaxSymlinkDepth guard in #lookupEntry did not help, because walk() resolved each symlink target with a fresh depth of zero.

Track the number of symlink hops along the current branch and stop recursing once it would exceed kMaxSymlinkDepth, mirroring the ELOOP guard in #lookupEntry and the behavior of the real filesystem, which follows directory symlinks until the OS symlink limit is reached. The entries themselves are still listed, so non-circular symlinks continue to be followed as before.

Fixes: #64148

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. vfs Issues and PRs related to the virtual filesystem subsystem. labels Jun 26, 2026
Comment thread lib/internal/vfs/providers/memory.js Outdated
const results = [];

const walk = (entry, currentPath, relativePath) => {
const walk = (entry, currentPath, relativePath, symlinkDepth) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of keeping this recursive, can it be refactored to be iterative to avoid the risk entirely?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alrightt... I switched #readdirRecursive to an iterative traversal with an explicit queue (same shape as fs.readdirSyncRecursive), so neither circular symlinks nor deeply nested trees can blow the call stack now.

I kept a per-entry symlink-hop counter bounded by kMaxSymlinkDepth, since the walk still follows directory symlinks and a cycle would otherwise grow the queue without end. It stops at the same point the real FS does (ELOOP) — the self-referential PoC returns the same 42 entries as fs.readdirSync(path, { recursive: true }). I went with the depth bound over a visited set to keep the output in parity with real fs, but happy to switch if you'd rather.

MemoryProvider#readdirSync with `recursive: true` follows symlinks to
directories during traversal. The traversal was recursive and unbounded,
so a circular symlink caused unbounded recursion in the internal walk()
helper until the call stack was exhausted, crashing the process with
`RangeError: Maximum call stack size exceeded`. Both the synchronous and
promise-based variants were affected.

Rewrite the traversal to be iterative, using an explicit queue instead
of recursion so deeply nested trees can no longer exhaust the call
stack.
Each queued directory carries the number of symlink hops taken to reach
it; a directory reached by following a symlink is not enqueued once that
count would exceed kMaxSymlinkDepth, mirroring the ELOOP guard in
#lookupEntry and the real filesystem, which follows directory symlinks
until the OS symlink limit is reached. Entries are still listed, so
non-circular symlinks continue to be followed as before.

Fixes: nodejs#64148
Signed-off-by: AkshatOP <hunterdevil0987@gmail.com>
@AkshatOP AkshatOP force-pushed the fix-vfs-readdir-circular-symlink branch from 62229c0 to 2e4e1be Compare June 27, 2026 03:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ci PRs that need a full CI run. vfs Issues and PRs related to the virtual filesystem subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

node:vfs MemoryProvider: readdirSync({recursive:true}) crashes via circular symlinks (stack overflow)

3 participants