Skip to content

Commit adcad7b

Browse files
neildgopherbot
authored andcommitted
os: don't follow symlinks on Windows when O_CREATE|O_EXCL
Match standard Unix behavior: Symlinks are not followed when O_CREATE|O_EXCL is passed to open. Thanks to Junyoung Park and Dong-uk Kim of KAIST Hacking Lab for discovering this issue. Fixes #73702 Fixes CVE-2025-0913 Change-Id: Ieb46a6780c5e9a6090b09cd34290f04a8e3b0ca5 Reviewed-on: https://go-review.googlesource.com/c/go/+/672396 Auto-Submit: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Alan Donovan <adonovan@google.com>
1 parent 14fc54f commit adcad7b

File tree

3 files changed

+28
-1
lines changed

3 files changed

+28
-1
lines changed

src/internal/syscall/windows/at_windows.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func Openat(dirfd syscall.Handle, name string, flag uint64, perm uint32) (_ sysc
9494
switch {
9595
case flag&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
9696
disposition = FILE_CREATE
97+
options |= FILE_OPEN_REPARSE_POINT // don't follow symlinks
9798
case flag&syscall.O_CREAT == syscall.O_CREAT:
9899
disposition = FILE_OPEN_IF
99100
default:

src/os/os_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,6 +2299,31 @@ func TestFilePermissions(t *testing.T) {
22992299

23002300
}
23012301

2302+
func TestOpenFileCreateExclDanglingSymlink(t *testing.T) {
2303+
testMaybeRooted(t, func(t *testing.T, r *Root) {
2304+
const link = "link"
2305+
if err := Symlink("does_not_exist", link); err != nil {
2306+
t.Fatal(err)
2307+
}
2308+
var f *File
2309+
var err error
2310+
if r == nil {
2311+
f, err = OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o666)
2312+
} else {
2313+
f, err = r.OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o666)
2314+
}
2315+
if err == nil {
2316+
f.Close()
2317+
}
2318+
if !errors.Is(err, ErrExist) {
2319+
t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL = %v, want ErrExist", err)
2320+
}
2321+
if _, err := Stat(link); err == nil {
2322+
t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL created a file")
2323+
}
2324+
})
2325+
}
2326+
23022327
// TestFileRDWRFlags tests the O_RDONLY, O_WRONLY, and O_RDWR flags.
23032328
func TestFileRDWRFlags(t *testing.T) {
23042329
for _, test := range []struct {

src/syscall/syscall_windows.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,15 +403,16 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
403403
//
404404
// Instead, we ftruncate the file after opening when O_TRUNC is set.
405405
var createmode uint32
406+
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
406407
switch {
407408
case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
408409
createmode = CREATE_NEW
410+
attrs |= FILE_FLAG_OPEN_REPARSE_POINT // don't follow symlinks
409411
case flag&O_CREAT == O_CREAT:
410412
createmode = OPEN_ALWAYS
411413
default:
412414
createmode = OPEN_EXISTING
413415
}
414-
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
415416
if perm&S_IWRITE == 0 {
416417
attrs = FILE_ATTRIBUTE_READONLY
417418
}

0 commit comments

Comments
 (0)