golang/go

path/filepath: endless loop in EvalSymlinks because it resolves a link to the same path it asked for

ericwj opened this issue · 13 comments

What version of Go are you using (go version)?

1.14.4

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

Windows

What did you do?

$vhd = New-VHD -Path Test.vhdx -SizeBytes 3MB -Fixed
$vhd | Mount-VHD
$vhd = Get-VHD Test.vhdx # refresh IsAttached etc
$vhd | Get-Disk | Initialize-Disk -PartitionStyle GPT
$part = $vhd | Get-Disk | New-Partition -UseMaximumSize -AssignDriveLetter:$false
$vol = $part | Format-Volume -FileSystem NTFS
mkdir Test
$part | Add-PartitionAccessPath -AccessPath "$PWD\Test"
package main

import ("path/filepath"; "fmt")

func main() {
   p, err := filepath.EvalSymlinks("Test")
   if err != nil { panic(err) }
   fmt.Println(p)
}

What did you expect to see?

"$PWD\Test"

What did you see instead?

EvalSymlinks: too many links

#39786

The code path also is not using go API surface or private internal code paths that public go API surface uses to do path math and gets it wrong.

@ericwj thank you for creating this issue. I think this issue is a duplicate of #39786. Same reproduction steps as per @networkimprov (see #39786 (comment)). No?

Alex

Not really, the code paths are different. This one fails for links to a target volume that does have a DOS device name (C:\EmptyDir) while the other one only fails for links to a target volume that doesn't (\\?\Volume{guid}).

I think the confusion is that the other issue was filed with the error that occurs here, while in the description it sais that the situation had been that there was no DOS device name. But then you don't get the 'too many links' error.

Not really,

If this is not a dup of #39786. I am happy to investigate this separately. But I cannot run your steps. See #39786 (comment) for details.

Alex

This failure mode can be reproduced without Hyper-V with the code snippet down below. In fact, it reproduces on all OSes, not only on Windows. Note that my reproducer is quite stupid and probably not something that someone would do, @ericwj's one interesting to demonstrate a real use case.

package main

import (
	"fmt"
	"os"
	"path/filepath"
)

func main() {
	err := os.Symlink("./self", "./self")
	if err != nil {
		panic(err)
	}
	p, err := filepath.EvalSymlinks("./self")
	if err != nil {
		panic(err)
	}
	fmt.Println(p)
}

Change https://go.dev/cl/453915 mentions this issue: path/filepath: EvalSymlinks should detect cycle to itself

neild commented

In the example of ln -s self self (producing a symlink from self to itself), the current behavior is correct: The target of the link cannot be resolved, because there is an infinite cycle of links. We can see this without involving Go at all:

$ ln self self
$ ls -l self
lrwxr-xr-x  1 dneil  wheel  4 Nov 30 10:15 self -> self
$ cat self
cat: self: Too many levels of symbolic links

I'm not familiar with the Windows filesystem operations in the original example in this issue, so I'm not certain what the correct behavior there is.

@neild in your example, ls -l self can resolve the link, the last column contains self -> self. cat can't print its content, but that's expected. If ls was to use Go's filepath.EvalSymlinks, it would report an error instead of resolving the link.

neild commented

The equivalent to what ls -l self is doing is os.Lstat.

filepath.EvalSymlinks evaluates all symlinks within a path, returning an equivalent path that contains no symlinks. In the case of a link cycle, there is no such path and EvalSymlinks returns an error.

filepath.EvalSymlinks evaluates all symlinks within a path, returning an equivalent path that contains no symlinks. In the case of a link cycle, there is no such path and EvalSymlinks returns an error.

My bad, I thought EvalSymlinks was another thing. Then I don't know how to reproduce this issue without using a virtual hard drive.

Change https://go.dev/cl/516695 mentions this issue: filepath: EvalSymlinks to not follow mount points on Windows

Change https://go.dev/cl/536655 mentions this issue: os,path/filepath: treat all non-symlink reparse points as irregular files