// 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")
}
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
What version of Go are you using (
go version)?Does this issue reproduce with the latest release?
Yes!
What operating system and processor architecture are you using (
go env)?go envOutputWhat did you do?
What did you expect to see?
"done - no crash"
What did you see instead?
Reproduces with x/sys/windows v0.38, v0.40, v0.42