]> Cypherpunks repositories - gostls13.git/commitdiff
os: disallow Root.Remove(".") on Plan 9, js, and Windows
authorDamien Neil <dneil@google.com>
Thu, 5 Jun 2025 21:27:45 +0000 (14:27 -0700)
committerGopher Robot <gobot@golang.org>
Tue, 10 Jun 2025 23:37:33 +0000 (16:37 -0700)
Windows already forbids this, since removing the root causes a
sharing violation (can't delete the directory while the os.Root
has a handle open to it), but add a more explicit check for
attempts to delete "." and return EINVAL.

Note that this change to Windows doesn't affect operations like
Root.Remove("dir/."), since the path is cleaned into just "dir"
before attempting the deletion.

Fixes #73863

Change-Id: I0f45ccb6c9f171d3a52831632c134150388d77b6
Reviewed-on: https://go-review.googlesource.com/c/go/+/679377
Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/internal/syscall/windows/at_windows.go
src/os/root_noopenat.go

index 87e0195d30ece04efe3422f87ff07e21ffb3ecef..d48fce1c99dc3673b18da2bca542e1dbf06008b0 100644 (file)
@@ -192,6 +192,11 @@ func Mkdirat(dirfd syscall.Handle, name string, mode uint32) error {
 }
 
 func Deleteat(dirfd syscall.Handle, name string, options uint32) error {
+       if name == "." {
+               // NtOpenFile's documentation isn't explicit about what happens when deleting ".".
+               // Make this an error consistent with that of POSIX.
+               return syscall.EINVAL
+       }
        objAttrs := &OBJECT_ATTRIBUTES{}
        if err := objAttrs.init(dirfd, name); err != nil {
                return err
index c4929623c48f86ba106e5fd4d7ffc388beba79a1..59f1abe91b0b2e3dfa4fedab55d355a3ad34506b 100644 (file)
@@ -8,6 +8,7 @@ package os
 
 import (
        "errors"
+       "internal/filepathlite"
        "internal/stringslite"
        "sync/atomic"
        "syscall"
@@ -173,6 +174,12 @@ func rootRemove(r *Root, name string) error {
        if err := checkPathEscapesLstat(r, name); err != nil {
                return &PathError{Op: "removeat", Path: name, Err: err}
        }
+       if endsWithDot(name) {
+               // We don't want to permit removing the root itself, so check for that.
+               if filepathlite.Clean(name) == "." {
+                       return &PathError{Op: "removeat", Path: name, Err: errPathEscapes}
+               }
+       }
        if err := Remove(joinPath(r.root.name, name)); err != nil {
                return &PathError{Op: "removeat", Path: name, Err: underlyingError(err)}
        }