mirror of https://github.com/golang/go.git
[release-branch.go1.24] os: don't follow symlinks on Windows when O_CREATE|O_EXCL
(This cherry-pick includes both CL 672396 and CL 676655.) 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. For #73702 Fixed #73720 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> Reviewed-on: https://go-review.googlesource.com/c/go/+/677215 Reviewed-by: Michael Knyszek <mknyszek@google.com> TryBot-Bypass: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
parent
a31c931adf
commit
9f9cf28f8f
|
|
@ -88,6 +88,7 @@ func Openat(dirfd syscall.Handle, name string, flag int, perm uint32) (_ syscall
|
|||
switch {
|
||||
case flag&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
|
||||
disposition = FILE_CREATE
|
||||
options |= FILE_OPEN_REPARSE_POINT // don't follow symlinks
|
||||
case flag&syscall.O_CREAT == syscall.O_CREAT:
|
||||
disposition = FILE_OPEN_IF
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -2223,6 +2223,31 @@ func TestFilePermissions(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestOpenFileCreateExclDanglingSymlink(t *testing.T) {
|
||||
testMaybeRooted(t, func(t *testing.T, r *Root) {
|
||||
const link = "link"
|
||||
if err := Symlink("does_not_exist", link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var f *File
|
||||
var err error
|
||||
if r == nil {
|
||||
f, err = OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444)
|
||||
} else {
|
||||
f, err = r.OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444)
|
||||
}
|
||||
if err == nil {
|
||||
f.Close()
|
||||
}
|
||||
if !errors.Is(err, ErrExist) {
|
||||
t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL = %v, want ErrExist", err)
|
||||
}
|
||||
if _, err := Stat(link); err == nil {
|
||||
t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL created a file")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestFileRDWRFlags tests the O_RDONLY, O_WRONLY, and O_RDWR flags.
|
||||
func TestFileRDWRFlags(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
|
|
|
|||
|
|
@ -376,20 +376,6 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
|
|||
if flag&O_CLOEXEC == 0 {
|
||||
sa = makeInheritSa()
|
||||
}
|
||||
// We don't use CREATE_ALWAYS, because when opening a file with
|
||||
// FILE_ATTRIBUTE_READONLY these will replace an existing file
|
||||
// with a new, read-only one. See https://go.dev/issue/38225.
|
||||
//
|
||||
// Instead, we ftruncate the file after opening when O_TRUNC is set.
|
||||
var createmode uint32
|
||||
switch {
|
||||
case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||
createmode = CREATE_NEW
|
||||
case flag&O_CREAT == O_CREAT:
|
||||
createmode = OPEN_ALWAYS
|
||||
default:
|
||||
createmode = OPEN_EXISTING
|
||||
}
|
||||
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
|
||||
if perm&S_IWRITE == 0 {
|
||||
attrs = FILE_ATTRIBUTE_READONLY
|
||||
|
|
@ -404,6 +390,21 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
|
|||
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
|
||||
attrs |= _FILE_FLAG_WRITE_THROUGH
|
||||
}
|
||||
// We don't use CREATE_ALWAYS, because when opening a file with
|
||||
// FILE_ATTRIBUTE_READONLY these will replace an existing file
|
||||
// with a new, read-only one. See https://go.dev/issue/38225.
|
||||
//
|
||||
// Instead, we ftruncate the file after opening when O_TRUNC is set.
|
||||
var createmode uint32
|
||||
switch {
|
||||
case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||
createmode = CREATE_NEW
|
||||
attrs |= FILE_FLAG_OPEN_REPARSE_POINT // don't follow symlinks
|
||||
case flag&O_CREAT == O_CREAT:
|
||||
createmode = OPEN_ALWAYS
|
||||
default:
|
||||
createmode = OPEN_EXISTING
|
||||
}
|
||||
h, err := createFile(namep, access, sharemode, sa, createmode, attrs, 0)
|
||||
if h == InvalidHandle {
|
||||
if err == ERROR_ACCESS_DENIED && (flag&O_WRONLY != 0 || flag&O_RDWR != 0) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue