Skip to content

x/sys/windows: GetNamedSecurityInfo crashes when GetNamedSecurityInfoW succeeds with NULL SECURITY_DESCRIPTOR #78396

@arieleiz

Description

@arieleiz

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

$ go version
go version go1.26.1 windows/amd64

Does this issue reproduce with the latest release?

Yes!

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

go env Output
$ go env
set AR=ar
set CC=gcc
set CGO_CFLAGS=-O2 -g
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-O2 -g
set CGO_ENABLED=0
set CGO_FFLAGS=-O2 -g
set CGO_LDFLAGS=-O2 -g
set CXX=g++
set GCCGO=gccgo
set GO111MODULE=
set GOAMD64=v1
set GOARCH=amd64
set GOAUTH=netrc
set GOBIN=
set GOCACHE=C:\Users\ariel\AppData\Local\go-build
set GOCACHEPROG=
set GODEBUG=
set GOENV=C:\Users\ariel\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFIPS140=off
set GOFLAGS=
set GOGCCFLAGS=-m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\ariel\AppData\Local\Temp\go-build322231695=/tmp/go-build -gno-record-gcc-switches
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMOD=D:\xm\gobug\go.mod
set GOMODCACHE=C:\Users\ariel\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\ariel\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=C:\Program Files\Go
set GOSUMDB=sum.golang.org
set GOTELEMETRY=local
set GOTELEMETRYDIR=C:\Users\ariel\AppData\Roaming\go\telemetry
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.26.1
set GOWORK=
set PKG_CONFIG=pkg-config

What did you do?

// Bug reproducer: golang.org/x/sys/windows.GetNamedSecurityInfo nil SD crash
//
// golang.org/x/sys v0.42.0 (and earlier) has a nil pointer dereference in
// windows.GetNamedSecurityInfo. The wrapper calls the Win32 API
// GetNamedSecurityInfoW, which can return ERROR_SUCCESS with a NULL security
// descriptor pointer - for example when querying DACL_SECURITY_INFORMATION on
// an SMB share whose DACL is inherited/absent. The wrapper then unconditionally
// calls winHeapSD.copySelfRelativeSecurityDescriptor(), which calls
// sd.Length() → getSecurityDescriptorLength(nil) → advapi32!GetSecurityDescriptorLength(NULL).
// On x86 (32-bit), this causes Exception 0xc0000005 (ACCESS_VIOLATION) at a
// kernel32/ntdll address because the Win32 function dereferences the NULL ptr.
// On amd64 it may also crash, but some builds happen to survive because the
// allocator sometimes places the SD at a non-null address even for empty DACLs.
//
// The crash is unrecoverable (Windows structured exception, not a Go panic),
// so there is no stack trace on stderr unless GOTRACEBACK=crash is set and the
// runtime manages to print before the process is torn down.
//
// Affected call chain in x/sys v0.42.0 (security_windows.go):
//
//   func GetNamedSecurityInfo(...) (sd *SECURITY_DESCRIPTOR, err error) {
//       var winHeapSD *SECURITY_DESCRIPTOR
//       err = getNamedSecurityInfo(..., &winHeapSD)  // may set winHeapSD = nil on success
//       if err != nil { return }
//       defer LocalFree(Handle(unsafe.Pointer(winHeapSD)))
//       return winHeapSD.copySelfRelativeSecurityDescriptor(), nil  // CRASH: nil receiver
//   }
//
//   func (sd *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() *SECURITY_DESCRIPTOR {
//       sdLen := int(selfRelativeSD.Length())  // calls getSecurityDescriptorLength(nil)
//       ...
//   }
//
// To reproduce: run this binary as SYSTEM (or any account that can see shares
// with no explicit DACL, e.g. IPC$ on some machines). On a stock Windows 10
// 32-bit VM, the IPC$ share reliably triggers the crash.
//
// Build:
//   GOOS=windows GOARCH=386   go build -o GetNamedSecurityInfo_bug_386.exe   doc/gobug/GetNamedSecurityInfo_bug_reproducer.go
//   GOOS=windows GOARCH=amd64 go build -o GetNamedSecurityInfo_bug_amd64.exe doc/gobug/GetNamedSecurityInfo_bug_reproducer.go

//go:build windows

package main

import (
	"fmt"
	"os"
	"runtime"
	"unsafe"

	"golang.org/x/sys/windows"
)

var (
	modadvapi32                = windows.NewLazySystemDLL("advapi32.dll")
	procGetNamedSecurityInfoW  = modadvapi32.NewProc("GetNamedSecurityInfoW")
	modnetapi32                = windows.NewLazySystemDLL("netapi32.dll")
	procNetShareEnum           = modnetapi32.NewProc("NetShareEnum")
	procNetApiBufferFree       = modnetapi32.NewProc("NetApiBufferFree")
)

type shareInfo0 struct {
	Netname *uint16
}

func listShares() []string {
	var buf *byte
	var entriesRead, totalEntries, resumeHandle uint32
	ret, _, _ := procNetShareEnum.Call(
		0, 0,
		uintptr(unsafe.Pointer(&buf)),
		0xFFFFFFFF,
		uintptr(unsafe.Pointer(&entriesRead)),
		uintptr(unsafe.Pointer(&totalEntries)),
		uintptr(unsafe.Pointer(&resumeHandle)),
	)
	if ret != 0 || buf == nil {
		return nil
	}
	defer procNetApiBufferFree.Call(uintptr(unsafe.Pointer(buf)))

	infos := unsafe.Slice((*shareInfo0)(unsafe.Pointer(buf)), entriesRead)
	names := make([]string, 0, entriesRead)
	for _, info := range infos {
		names = append(names, windows.UTF16PtrToString(info.Netname))
	}
	return names
}

func rawGetNamedSecurityInfo(name string) (*windows.SECURITY_DESCRIPTOR, uintptr, error) {
	namePtr, err := windows.UTF16PtrFromString(name)
	if err != nil {
		return nil, 0, err
	}
	var sd *windows.SECURITY_DESCRIPTOR
	ret, _, _ := procGetNamedSecurityInfoW.Call(
		uintptr(unsafe.Pointer(namePtr)),
		uintptr(windows.SE_LMSHARE),
		uintptr(windows.DACL_SECURITY_INFORMATION),
		0, 0, 0, 0,
		uintptr(unsafe.Pointer(&sd)),
	)
	return sd, ret, nil
}

func main() {
	fmt.Printf("GOARCH=%s  PID=%d\n\n", runtime.GOARCH, os.Getpid())

	shares := listShares()
	if len(shares) == 0 {
		fmt.Println("no shares found (try running as SYSTEM or Administrator)")
		return
	}

	fmt.Printf("found %d shares: %v\n\n", len(shares), shares)

	for _, name := range shares {
		sd, ret, err := rawGetNamedSecurityInfo(name)
		if err != nil {
			fmt.Printf("  %-20s  UTF16 error: %v\n", name, err)
			continue
		}
		if ret != 0 {
			fmt.Printf("  %-20s  Win32 error: %d\n", name, ret)
			continue
		}
		if sd == nil {
			fmt.Printf("  %-20s  SD=nil (SUCCESS) *** x/sys would crash here ***\n", name)
			continue
		}
		fmt.Printf("  %-20s  SD=%p  len=%d\n", name, sd, sd.Length())
		windows.LocalFree(windows.Handle(unsafe.Pointer(sd)))
	}

	fmt.Println("\n--- now calling x/sys wrapper (will crash if any share has nil SD) ---")
	for _, name := range shares {
		fmt.Printf("  calling GetNamedSecurityInfo(%q)...", name)
		sd, err := windows.GetNamedSecurityInfo(name, windows.SE_LMSHARE, windows.DACL_SECURITY_INFORMATION)
		if err != nil {
			fmt.Printf(" err=%v\n", err)
			continue
		}
		if sd == nil {
			fmt.Printf(" sd=nil (no crash, wrapper returned nil)\n")
			continue
		}
		fmt.Printf(" sd=%p len=%d OK\n", sd, sd.Length())
	}

	fmt.Println("\ndone — no crash")
}

What did you expect to see?

"done - no crash"

What did you see instead?

D:\xm\gobug>gobug.exe
GOARCH=amd64  PID=41656

found 10 shares: [ADMIN$ ariel C C$ D$ E E$ IPC$ office xm]

  ADMIN$                SD=nil (SUCCESS) *** x/sys would crash here ***
  ariel                 SD=0x22e00308c70  len=48
  C                     SD=0x22e00308730  len=48
  C$                    SD=nil (SUCCESS) *** x/sys would crash here ***
  D$                    SD=nil (SUCCESS) *** x/sys would crash here ***
  E                     SD=0x22e003105c0  len=120
  E$                    SD=nil (SUCCESS) *** x/sys would crash here ***
  IPC$                  SD=nil (SUCCESS) *** x/sys would crash here ***
  office                SD=0x22e00329b00  len=64
  xm                    SD=0x22e00308730  len=48

--- now calling x/sys wrapper (will crash if any share has nil SD) ---
  calling GetNamedSecurityInfo("ADMIN$")...Exception 0xc0000005 0x0 0x2 0x7ffd96e4b3d0
PC=0x7ffd96e4b3d0

runtime.cgocall(0x7ff6f92aeba0, 0x7ff6f9460cb0)
        C:/Program Files/Go/src/runtime/cgocall.go:167 +0x3e fp=0xdb7d7cbbf0 sp=0xdb7d7cbb88 pc=0x7ff6f93151de
syscall.syscalln(0x0?, 0x0?, {0xdb7d7cbc68?, 0x0?, 0x0?})
        C:/Program Files/Go/src/runtime/syscall_windows.go:431 +0x4e fp=0xdb7d7cbc10 sp=0xdb7d7cbbf0 pc=0x7ff6f931990e
syscall.SyscallN(0x0?, {0xdb7d7cbc68?, 0x0?, 0x0?})
        C:/Program Files/Go/src/syscall/dll_windows.go:99 +0x1e fp=0xdb7d7cbc48 sp=0xdb7d7cbc10 pc=0x7ff6f933551e
golang.org/x/sys/windows.getSecurityDescriptorLength(0x7ff6f9324b85?)
        C:/Users/ariel/go/pkg/mod/golang.org/x/sys@v0.42.0/windows/zsyscall_windows.go:886 +0x5c fp=0xdb7d7cbc90 sp=0xdb7d7cbc48 pc=0x7ff6f935289c
golang.org/x/sys/windows.(*SECURITY_DESCRIPTOR).Length(...)
        C:/Users/ariel/go/pkg/mod/golang.org/x/sys@v0.42.0/windows/security_windows.go:1264
golang.org/x/sys/windows.(*SECURITY_DESCRIPTOR).copySelfRelativeSecurityDescriptor(0x0)
        C:/Users/ariel/go/pkg/mod/golang.org/x/sys@v0.42.0/windows/security_windows.go:1398 +0x1c fp=0xdb7d7cbcc8 sp=0xdb7d7cbc90 pc=0x7ff6f9351dfc
golang.org/x/sys/windows.GetNamedSecurityInfo({0xdb7d6f41f8?, 0xdb7d6ee050?}, 0xf938af0f?, 0x7ff6?)
        C:/Users/ariel/go/pkg/mod/golang.org/x/sys@v0.42.0/windows/security_windows.go:1449 +0xaa fp=0xdb7d7cbd58 sp=0xdb7d7cbcc8 pc=0x7ff6f9351f4a
main.main()
        D:/xm/gobug/GetNamedSecurityInfo_bug_reproducer.go:139 +0x588 fp=0xdb7d7cbf48 sp=0xdb7d7cbd58 pc=0x7ff6f9353b68
runtime.main()
        C:/Program Files/Go/src/runtime/proc.go:290 +0x2c7 fp=0xdb7d7cbfe0 sp=0xdb7d7cbf48 pc=0x7ff6f92e8487
runtime.goexit({})
        C:/Program Files/Go/src/runtime/asm_amd64.s:1771 +0x1 fp=0xdb7d7cbfe8 sp=0xdb7d7cbfe0 pc=0x7ff6f931d0a1

goroutine 2 gp=0xdb7d79c780 m=nil [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        C:/Program Files/Go/src/runtime/proc.go:462 +0xce fp=0xdb7d79ffa8 sp=0xdb7d79ff88 pc=0x7ff6f931708e
runtime.goparkunlock(...)
        C:/Program Files/Go/src/runtime/proc.go:468
runtime.forcegchelper()
        C:/Program Files/Go/src/runtime/proc.go:375 +0xb8 fp=0xdb7d79ffe0 sp=0xdb7d79ffa8 pc=0x7ff6f92e8778
runtime.goexit({})
        C:/Program Files/Go/src/runtime/asm_amd64.s:1771 +0x1 fp=0xdb7d79ffe8 sp=0xdb7d79ffe0 pc=0x7ff6f931d0a1
created by runtime.init.7 in goroutine 1
        C:/Program Files/Go/src/runtime/proc.go:363 +0x1a

goroutine 3 gp=0xdb7d79cb40 m=nil [GC sweep wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        C:/Program Files/Go/src/runtime/proc.go:462 +0xce fp=0xdb7d7a1f88 sp=0xdb7d7a1f68 pc=0x7ff6f931708e
runtime.goparkunlock(...)
        C:/Program Files/Go/src/runtime/proc.go:468
runtime.bgsweep(0xdb7d7ac000)
        C:/Program Files/Go/src/runtime/mgcsweep.go:279 +0x94 fp=0xdb7d7a1fc8 sp=0xdb7d7a1f88 pc=0x7ff6f92d19f4
runtime.gcenable.gowrap1()
        C:/Program Files/Go/src/runtime/mgc.go:214 +0x17 fp=0xdb7d7a1fe0 sp=0xdb7d7a1fc8 pc=0x7ff6f92c2ff7
runtime.goexit({})
        C:/Program Files/Go/src/runtime/asm_amd64.s:1771 +0x1 fp=0xdb7d7a1fe8 sp=0xdb7d7a1fe0 pc=0x7ff6f931d0a1
created by runtime.gcenable in goroutine 1
        C:/Program Files/Go/src/runtime/mgc.go:214 +0x66

goroutine 4 gp=0xdb7d79cd20 m=nil [GC scavenge wait]:
runtime.gopark(0xdb7d7ac000?, 0x7ff6f938ee30?, 0x1?, 0x0?, 0xdb7d79cd20?)
        C:/Program Files/Go/src/runtime/proc.go:462 +0xce fp=0xdb7d7b3f78 sp=0xdb7d7b3f58 pc=0x7ff6f931708e
runtime.goparkunlock(...)
        C:/Program Files/Go/src/runtime/proc.go:468
runtime.(*scavengerState).park(0x7ff6f945f960)
        C:/Program Files/Go/src/runtime/mgcscavenge.go:425 +0x49 fp=0xdb7d7b3fa8 sp=0xdb7d7b3f78 pc=0x7ff6f92cf529
runtime.bgscavenge(0xdb7d7ac000)
        C:/Program Files/Go/src/runtime/mgcscavenge.go:653 +0x3c fp=0xdb7d7b3fc8 sp=0xdb7d7b3fa8 pc=0x7ff6f92cfa9c
runtime.gcenable.gowrap2()
        C:/Program Files/Go/src/runtime/mgc.go:215 +0x17 fp=0xdb7d7b3fe0 sp=0xdb7d7b3fc8 pc=0x7ff6f92c2fb7
runtime.goexit({})
        C:/Program Files/Go/src/runtime/asm_amd64.s:1771 +0x1 fp=0xdb7d7b3fe8 sp=0xdb7d7b3fe0 pc=0x7ff6f931d0a1
created by runtime.gcenable in goroutine 1
        C:/Program Files/Go/src/runtime/mgc.go:215 +0xa5

goroutine 5 gp=0xdb7d79d0e0 m=nil [GOMAXPROCS updater (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        C:/Program Files/Go/src/runtime/proc.go:462 +0xce fp=0xdb7d7b5f88 sp=0xdb7d7b5f68 pc=0x7ff6f931708e
runtime.goparkunlock(...)
        C:/Program Files/Go/src/runtime/proc.go:468
runtime.updateMaxProcsGoroutine()
        C:/Program Files/Go/src/runtime/proc.go:7095 +0xe7 fp=0xdb7d7b5fe0 sp=0xdb7d7b5f88 pc=0x7ff6f92f6607
runtime.goexit({})
        C:/Program Files/Go/src/runtime/asm_amd64.s:1771 +0x1 fp=0xdb7d7b5fe8 sp=0xdb7d7b5fe0 pc=0x7ff6f931d0a1
created by runtime.defaultGOMAXPROCSUpdateEnable in goroutine 1
        C:/Program Files/Go/src/runtime/proc.go:7083 +0x37

goroutine 6 gp=0xdb7d79d2c0 m=nil [finalizer wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        C:/Program Files/Go/src/runtime/proc.go:462 +0xce fp=0xdb7d7afe20 sp=0xdb7d7afe00 pc=0x7ff6f931708e
runtime.runFinalizers()
        C:/Program Files/Go/src/runtime/mfinal.go:210 +0x107 fp=0xdb7d7affe0 sp=0xdb7d7afe20 pc=0x7ff6f92c1f87
runtime.goexit({})
        C:/Program Files/Go/src/runtime/asm_amd64.s:1771 +0x1 fp=0xdb7d7affe8 sp=0xdb7d7affe0 pc=0x7ff6f931d0a1
created by runtime.createfing in goroutine 1
        C:/Program Files/Go/src/runtime/mfinal.go:172 +0x3d

goroutine 7 gp=0xdb7d79d4a0 m=nil [cleanup wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
        C:/Program Files/Go/src/runtime/proc.go:462 +0xce fp=0xdb7d7b1f68 sp=0xdb7d7b1f48 pc=0x7ff6f931708e
runtime.goparkunlock(...)
        C:/Program Files/Go/src/runtime/proc.go:468
runtime.(*cleanupQueue).dequeue(0x7ff6f945faa0)
        C:/Program Files/Go/src/runtime/mcleanup.go:522 +0xd4 fp=0xdb7d7b1fa0 sp=0xdb7d7b1f68 pc=0x7ff6f92bef74
runtime.runCleanups()
        C:/Program Files/Go/src/runtime/mcleanup.go:718 +0x45 fp=0xdb7d7b1fe0 sp=0xdb7d7b1fa0 pc=0x7ff6f92bf5e5
runtime.goexit({})
        C:/Program Files/Go/src/runtime/asm_amd64.s:1771 +0x1 fp=0xdb7d7b1fe8 sp=0xdb7d7b1fe0 pc=0x7ff6f931d0a1
created by runtime.(*cleanupQueue).createGs in goroutine 1
        C:/Program Files/Go/src/runtime/mcleanup.go:672 +0xa5
rax     0x7ffd95d624e0
rbx     0x7ff6f9460cb0
rcx     0x0
rdx     0xdb7d7cbb78
rdi     0x380da93000
rsi     0xdb7d7cbc68
rbp     0x380ddffd00
rsp     0x380ddffb98
r8      0x7ff6f9460960
r9      0x0
r10     0x22e001c0001
r11     0x380ddff7b0
r12     0x2d
r13     0xdb7d7a8580
r14     0xdb7d79c000
r15     0xffffffffffffffff
rip     0x7ffd96e4b3d0
rflags  0x10246
cs      0x33
fs      0x53
gs      0x2b

Reproduces with x/sys/windows v0.38, v0.40, v0.42

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.FixPendingIssues that have a fix which has not yet been reviewed or submitted.compiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Done

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions