Skip to content

Fix File.cp_r/3 infinite loop with symlink cycles#15152

Merged
josevalim merged 1 commit intoelixir-lang:mainfrom
pnezis:fix-cp_r-symlink-cycle
Mar 9, 2026
Merged

Fix File.cp_r/3 infinite loop with symlink cycles#15152
josevalim merged 1 commit intoelixir-lang:mainfrom
pnezis:fix-cp_r-symlink-cycle

Conversation

@pnezis
Copy link
Copy Markdown
Contributor

@pnezis pnezis commented Mar 8, 2026

When dereference_symlinks: true was set and there was a symlink cycle (e.g., a -> b -> a), the function would infinitely recurse. This fix tracks visited paths using a MapSet and returns {:error, :eloop, path} when a cycle is detected.

Reproduce with:

iex> File.mkdir_p!("/tmp/src")
iex> :file.make_symlink("/tmp/src/b", "/tmp/src/a")
iex> :file.make_symlink("/tmp/src/a", "/tmp/src/b")
iex> File.cp_r("/tmp/src", "/tmp/dest", dereference_symlinks: true)

Comment thread lib/elixir/lib/file.ex Outdated
Comment thread lib/elixir/lib/file.ex
resolved = Path.expand(link, Path.dirname(src))

if MapSet.member?(visited, resolved) do
{:error, :eloop, src}
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.

eloop is a new code, right? It may be better to use one of the existing ones, such as einval.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is a standard posix error and I think the semantically correct for this case:

iex> :file.format_error(:eloop)
~c"too many levels of symbolic links"

@pnezis pnezis force-pushed the fix-cp_r-symlink-cycle branch from 6220d50 to 319c7de Compare March 9, 2026 05:27
When `dereference_symlinks: true` was set and there was a symlink cycle
(e.g., a -> b -> a), the function would infinitely recurse. This fix
tracks visited paths using a `MapSet` and returns `{:error, :eloop, path}`
when a cycle is detected.
@pnezis pnezis force-pushed the fix-cp_r-symlink-cycle branch from 319c7de to fcd9d7f Compare March 9, 2026 05:28
@josevalim josevalim merged commit c6820a1 into elixir-lang:main Mar 9, 2026
15 checks passed
@josevalim
Copy link
Copy Markdown
Member

💚 💙 💜 💛 ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants