Skip to content

Commit 4442543

Browse files
committed
windows: treat proc thread attribute lists as unsafe.Pointers
It turns out that the proc thread update function doesn't actually allocate new memory for its arguments and instead just copies the pointer values into the preallocated memory. Since we were allocating that memory as []byte, the garbage collector didn't scan it for pointers to Go allocations and freed them. We _could_ fix this by requiring that all users of this use runtime.KeepAlive for everything they pass to the update function, but that seems harder than necessary. Instead, we can just do the allocation as []unsafe.Pointer, which means the GC can operate as intended and not free these from beneath our feet. In order to ensure this remains true, we also add a test for this. Updates golang/go#44662. Change-Id: Iaa8b694a6682cc1876879632c7ba068e47b8666d Reviewed-on: https://go-review.googlesource.com/c/sys/+/297331 Trust: Jason A. Donenfeld <[email protected]> Trust: Bryan C. Mills <[email protected]> Run-TryBot: Jason A. Donenfeld <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent afaa365 commit 4442543

File tree

5 files changed

+46
-6
lines changed

5 files changed

+46
-6
lines changed

windows/exec_windows.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeList,
111111
}
112112
return nil, err
113113
}
114-
al := (*ProcThreadAttributeList)(unsafe.Pointer(&make([]byte, size)[0]))
114+
const psize = unsafe.Sizeof(uintptr(0))
115+
// size is guaranteed to be ≥1 by InitializeProcThreadAttributeList.
116+
al := (*ProcThreadAttributeList)(unsafe.Pointer(&make([]unsafe.Pointer, (size+psize-1)/psize)[0]))
115117
err = initializeProcThreadAttributeList(al, maxAttrCount, 0, &size)
116118
if err != nil {
117119
return nil, err
@@ -120,8 +122,8 @@ func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeList,
120122
}
121123

122124
// Update modifies the ProcThreadAttributeList using UpdateProcThreadAttribute.
123-
func (al *ProcThreadAttributeList) Update(attribute uintptr, flags uint32, value unsafe.Pointer, size uintptr, prevValue uintptr, returnedSize *uintptr) error {
124-
return updateProcThreadAttribute(al, flags, attribute, uintptr(value), size, prevValue, returnedSize)
125+
func (al *ProcThreadAttributeList) Update(attribute uintptr, flags uint32, value unsafe.Pointer, size uintptr, prevValue unsafe.Pointer, returnedSize *uintptr) error {
126+
return updateProcThreadAttribute(al, flags, attribute, value, size, prevValue, returnedSize)
125127
}
126128

127129
// Delete frees ProcThreadAttributeList's resources.

windows/syscall_windows.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
216216
//sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) = CreateProcessW
217217
//sys initializeProcThreadAttributeList(attrlist *ProcThreadAttributeList, attrcount uint32, flags uint32, size *uintptr) (err error) = InitializeProcThreadAttributeList
218218
//sys deleteProcThreadAttributeList(attrlist *ProcThreadAttributeList) = DeleteProcThreadAttributeList
219-
//sys updateProcThreadAttribute(attrlist *ProcThreadAttributeList, flags uint32, attr uintptr, value uintptr, size uintptr, prevvalue uintptr, returnedsize *uintptr) (err error) = UpdateProcThreadAttribute
219+
//sys updateProcThreadAttribute(attrlist *ProcThreadAttributeList, flags uint32, attr uintptr, value unsafe.Pointer, size uintptr, prevvalue unsafe.Pointer, returnedsize *uintptr) (err error) = UpdateProcThreadAttribute
220220
//sys OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) (handle Handle, err error)
221221
//sys ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *uint16, showCmd int32) (err error) [failretval<=32] = shell32.ShellExecuteW
222222
//sys GetWindowThreadProcessId(hwnd HWND, pid *uint32) (tid uint32, err error) = user32.GetWindowThreadProcessId

windows/syscall_windows_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"strings"
1717
"syscall"
1818
"testing"
19+
"time"
1920
"unsafe"
2021

2122
"golang.org/x/sys/windows"
@@ -502,3 +503,36 @@ func TestNTStatusConversion(t *testing.T) {
502503
t.Errorf("NTStatus.Errno = %q (0x%x); want %q (0x%x)", got.Error(), got, want.Error(), want)
503504
}
504505
}
506+
507+
func TestProcThreadAttributeListPointers(t *testing.T) {
508+
list, err := windows.NewProcThreadAttributeList(1)
509+
if err != nil {
510+
t.Errorf("unable to create ProcThreadAttributeList: %v", err)
511+
}
512+
done := make(chan struct{})
513+
fds := make([]syscall.Handle, 20)
514+
runtime.SetFinalizer(&fds[0], func(*syscall.Handle) {
515+
close(done)
516+
})
517+
err = list.Update(windows.PROC_THREAD_ATTRIBUTE_HANDLE_LIST, 0, unsafe.Pointer(&fds[0]), uintptr(len(fds))*unsafe.Sizeof(fds[0]), nil, nil)
518+
if err != nil {
519+
list.Delete()
520+
t.Errorf("unable to update ProcThreadAttributeList: %v", err)
521+
return
522+
}
523+
runtime.GC()
524+
runtime.GC()
525+
select {
526+
case <-done:
527+
t.Error("ProcThreadAttributeList was garbage collected unexpectedly")
528+
default:
529+
}
530+
list.Delete()
531+
runtime.GC()
532+
runtime.GC()
533+
select {
534+
case <-done:
535+
case <-time.After(time.Second):
536+
t.Error("ProcThreadAttributeList was not garbage collected after a second")
537+
}
538+
}

windows/types_windows.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,11 @@ type StartupInfoEx struct {
912912
// To create a *ProcThreadAttributeList, use NewProcThreadAttributeList, and
913913
// free its memory using ProcThreadAttributeList.Delete.
914914
type ProcThreadAttributeList struct {
915-
_ [1]byte
915+
// This is of type unsafe.Pointer, not of type byte or uintptr, because
916+
// the contents of it is mostly a list of pointers, and in most cases,
917+
// that's a list of pointers to Go-allocated objects. In order to keep
918+
// the GC from collecting these objects, we declare this as unsafe.Pointer.
919+
_ [1]unsafe.Pointer
916920
}
917921

918922
type ProcessInformation struct {

windows/zsyscall_windows.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)