Skip to content

Commit 77505c2

Browse files
committed
syscall: 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. Fixes #44662. Change-Id: Ib392ba8ceacacec94b11379919c8179841cba29f Reviewed-on: https://go-review.googlesource.com/c/go/+/297389 Trust: Jason A. Donenfeld <[email protected]> Trust: Alex Brainman <[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 9d88a9e commit 77505c2

6 files changed

+60
-6
lines changed

src/syscall/exec_windows.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
340340
si.ShowWindow = SW_HIDE
341341
}
342342
if sys.ParentProcess != 0 {
343-
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, uintptr(unsafe.Pointer(&sys.ParentProcess)), unsafe.Sizeof(sys.ParentProcess), 0, nil)
343+
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, unsafe.Pointer(&sys.ParentProcess), unsafe.Sizeof(sys.ParentProcess), nil, nil)
344344
if err != nil {
345345
return 0, 0, err
346346
}
@@ -351,7 +351,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
351351

352352
fd = append(fd, sys.AdditionalInheritedHandles...)
353353
// Do not accidentally inherit more than these handles.
354-
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, uintptr(unsafe.Pointer(&fd[0])), uintptr(len(fd))*unsafe.Sizeof(fd[0]), 0, nil)
354+
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil)
355355
if err != nil {
356356
return 0, 0, err
357357
}

src/syscall/export_windows_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package syscall
6+
7+
var NewProcThreadAttributeList = newProcThreadAttributeList
8+
var UpdateProcThreadAttribute = updateProcThreadAttribute
9+
var DeleteProcThreadAttributeList = deleteProcThreadAttributeList
10+
11+
const PROC_THREAD_ATTRIBUTE_HANDLE_LIST = _PROC_THREAD_ATTRIBUTE_HANDLE_LIST

src/syscall/syscall_windows.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
286286
//sys CreateHardLink(filename *uint16, existingfilename *uint16, reserved uintptr) (err error) [failretval&0xff==0] = CreateHardLinkW
287287
//sys initializeProcThreadAttributeList(attrlist *_PROC_THREAD_ATTRIBUTE_LIST, attrcount uint32, flags uint32, size *uintptr) (err error) = InitializeProcThreadAttributeList
288288
//sys deleteProcThreadAttributeList(attrlist *_PROC_THREAD_ATTRIBUTE_LIST) = DeleteProcThreadAttributeList
289-
//sys updateProcThreadAttribute(attrlist *_PROC_THREAD_ATTRIBUTE_LIST, flags uint32, attr uintptr, value uintptr, size uintptr, prevvalue uintptr, returnedsize *uintptr) (err error) = UpdateProcThreadAttribute
289+
//sys updateProcThreadAttribute(attrlist *_PROC_THREAD_ATTRIBUTE_LIST, flags uint32, attr uintptr, value unsafe.Pointer, size uintptr, prevvalue unsafe.Pointer, returnedsize *uintptr) (err error) = UpdateProcThreadAttribute
290290

291291
// syscall interface implementation for other packages
292292

@@ -1256,7 +1256,8 @@ func newProcThreadAttributeList(maxAttrCount uint32) (*_PROC_THREAD_ATTRIBUTE_LI
12561256
}
12571257
return nil, err
12581258
}
1259-
al := (*_PROC_THREAD_ATTRIBUTE_LIST)(unsafe.Pointer(&make([]byte, size)[0]))
1259+
// size is guaranteed to be ≥1 by initializeProcThreadAttributeList.
1260+
al := (*_PROC_THREAD_ATTRIBUTE_LIST)(unsafe.Pointer(&make([]unsafe.Pointer, (size+ptrSize-1)/ptrSize)[0]))
12601261
err = initializeProcThreadAttributeList(al, maxAttrCount, 0, &size)
12611262
if err != nil {
12621263
return nil, err

src/syscall/syscall_windows_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ package syscall_test
77
import (
88
"os"
99
"path/filepath"
10+
"runtime"
1011
"syscall"
1112
"testing"
13+
"time"
14+
"unsafe"
1215
)
1316

1417
func TestWin32finddata(t *testing.T) {
@@ -75,3 +78,36 @@ func TestTOKEN_ALL_ACCESS(t *testing.T) {
7578
t.Errorf("TOKEN_ALL_ACCESS = %x, want 0xF01FF", syscall.TOKEN_ALL_ACCESS)
7679
}
7780
}
81+
82+
func TestProcThreadAttributeListPointers(t *testing.T) {
83+
list, err := syscall.NewProcThreadAttributeList(1)
84+
if err != nil {
85+
t.Errorf("unable to create ProcThreadAttributeList: %v", err)
86+
}
87+
done := make(chan struct{})
88+
fds := make([]syscall.Handle, 20)
89+
runtime.SetFinalizer(&fds[0], func(*syscall.Handle) {
90+
close(done)
91+
})
92+
err = syscall.UpdateProcThreadAttribute(list, 0, syscall.PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fds[0]), uintptr(len(fds))*unsafe.Sizeof(fds[0]), nil, nil)
93+
if err != nil {
94+
syscall.DeleteProcThreadAttributeList(list)
95+
t.Errorf("unable to update ProcThreadAttributeList: %v", err)
96+
return
97+
}
98+
runtime.GC()
99+
runtime.GC()
100+
select {
101+
case <-done:
102+
t.Error("ProcThreadAttributeList was garbage collected unexpectedly")
103+
default:
104+
}
105+
syscall.DeleteProcThreadAttributeList(list)
106+
runtime.GC()
107+
runtime.GC()
108+
select {
109+
case <-done:
110+
case <-time.After(time.Second):
111+
t.Error("ProcThreadAttributeList was not garbage collected after a second")
112+
}
113+
}

src/syscall/types_windows.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
package syscall
66

7+
import "unsafe"
8+
79
const (
810
// Windows errors.
911
ERROR_FILE_NOT_FOUND Errno = 2
@@ -491,7 +493,11 @@ type StartupInfo struct {
491493
}
492494

493495
type _PROC_THREAD_ATTRIBUTE_LIST struct {
494-
_ [1]byte
496+
// This is of type unsafe.Pointer, not of type byte or uintptr, because
497+
// the contents of it is mostly a list of pointers, and in most cases,
498+
// that's a list of pointers to Go-allocated objects. In order to keep
499+
// the GC from collecting these objects, we declare this as unsafe.Pointer.
500+
_ [1]unsafe.Pointer
495501
}
496502

497503
const (

src/syscall/zsyscall_windows.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)