mirror of
https://github.com/golang/go
synced 2024-11-11 19:41:36 -07:00
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 <Jason@zx2c4.com> Trust: Alex Brainman <alex.brainman@gmail.com> Trust: Bryan C. Mills <bcmills@google.com> Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
9d88a9e2bf
commit
77505c25d8
@ -340,7 +340,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
|
||||
si.ShowWindow = SW_HIDE
|
||||
}
|
||||
if sys.ParentProcess != 0 {
|
||||
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, uintptr(unsafe.Pointer(&sys.ParentProcess)), unsafe.Sizeof(sys.ParentProcess), 0, nil)
|
||||
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, unsafe.Pointer(&sys.ParentProcess), unsafe.Sizeof(sys.ParentProcess), nil, nil)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
@ -351,7 +351,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle
|
||||
|
||||
fd = append(fd, sys.AdditionalInheritedHandles...)
|
||||
// Do not accidentally inherit more than these handles.
|
||||
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, uintptr(unsafe.Pointer(&fd[0])), uintptr(len(fd))*unsafe.Sizeof(fd[0]), 0, nil)
|
||||
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
11
src/syscall/export_windows_test.go
Normal file
11
src/syscall/export_windows_test.go
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package syscall
|
||||
|
||||
var NewProcThreadAttributeList = newProcThreadAttributeList
|
||||
var UpdateProcThreadAttribute = updateProcThreadAttribute
|
||||
var DeleteProcThreadAttributeList = deleteProcThreadAttributeList
|
||||
|
||||
const PROC_THREAD_ATTRIBUTE_HANDLE_LIST = _PROC_THREAD_ATTRIBUTE_HANDLE_LIST
|
@ -286,7 +286,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
|
||||
//sys CreateHardLink(filename *uint16, existingfilename *uint16, reserved uintptr) (err error) [failretval&0xff==0] = CreateHardLinkW
|
||||
//sys initializeProcThreadAttributeList(attrlist *_PROC_THREAD_ATTRIBUTE_LIST, attrcount uint32, flags uint32, size *uintptr) (err error) = InitializeProcThreadAttributeList
|
||||
//sys deleteProcThreadAttributeList(attrlist *_PROC_THREAD_ATTRIBUTE_LIST) = DeleteProcThreadAttributeList
|
||||
//sys updateProcThreadAttribute(attrlist *_PROC_THREAD_ATTRIBUTE_LIST, flags uint32, attr uintptr, value uintptr, size uintptr, prevvalue uintptr, returnedsize *uintptr) (err error) = UpdateProcThreadAttribute
|
||||
//sys updateProcThreadAttribute(attrlist *_PROC_THREAD_ATTRIBUTE_LIST, flags uint32, attr uintptr, value unsafe.Pointer, size uintptr, prevvalue unsafe.Pointer, returnedsize *uintptr) (err error) = UpdateProcThreadAttribute
|
||||
|
||||
// syscall interface implementation for other packages
|
||||
|
||||
@ -1256,7 +1256,8 @@ func newProcThreadAttributeList(maxAttrCount uint32) (*_PROC_THREAD_ATTRIBUTE_LI
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
al := (*_PROC_THREAD_ATTRIBUTE_LIST)(unsafe.Pointer(&make([]byte, size)[0]))
|
||||
// size is guaranteed to be ≥1 by initializeProcThreadAttributeList.
|
||||
al := (*_PROC_THREAD_ATTRIBUTE_LIST)(unsafe.Pointer(&make([]unsafe.Pointer, (size+ptrSize-1)/ptrSize)[0]))
|
||||
err = initializeProcThreadAttributeList(al, maxAttrCount, 0, &size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -7,8 +7,11 @@ package syscall_test
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestWin32finddata(t *testing.T) {
|
||||
@ -75,3 +78,36 @@ func TestTOKEN_ALL_ACCESS(t *testing.T) {
|
||||
t.Errorf("TOKEN_ALL_ACCESS = %x, want 0xF01FF", syscall.TOKEN_ALL_ACCESS)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcThreadAttributeListPointers(t *testing.T) {
|
||||
list, err := syscall.NewProcThreadAttributeList(1)
|
||||
if err != nil {
|
||||
t.Errorf("unable to create ProcThreadAttributeList: %v", err)
|
||||
}
|
||||
done := make(chan struct{})
|
||||
fds := make([]syscall.Handle, 20)
|
||||
runtime.SetFinalizer(&fds[0], func(*syscall.Handle) {
|
||||
close(done)
|
||||
})
|
||||
err = syscall.UpdateProcThreadAttribute(list, 0, syscall.PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fds[0]), uintptr(len(fds))*unsafe.Sizeof(fds[0]), nil, nil)
|
||||
if err != nil {
|
||||
syscall.DeleteProcThreadAttributeList(list)
|
||||
t.Errorf("unable to update ProcThreadAttributeList: %v", err)
|
||||
return
|
||||
}
|
||||
runtime.GC()
|
||||
runtime.GC()
|
||||
select {
|
||||
case <-done:
|
||||
t.Error("ProcThreadAttributeList was garbage collected unexpectedly")
|
||||
default:
|
||||
}
|
||||
syscall.DeleteProcThreadAttributeList(list)
|
||||
runtime.GC()
|
||||
runtime.GC()
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(time.Second):
|
||||
t.Error("ProcThreadAttributeList was not garbage collected after a second")
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package syscall
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const (
|
||||
// Windows errors.
|
||||
ERROR_FILE_NOT_FOUND Errno = 2
|
||||
@ -491,7 +493,11 @@ type StartupInfo struct {
|
||||
}
|
||||
|
||||
type _PROC_THREAD_ATTRIBUTE_LIST struct {
|
||||
_ [1]byte
|
||||
// This is of type unsafe.Pointer, not of type byte or uintptr, because
|
||||
// the contents of it is mostly a list of pointers, and in most cases,
|
||||
// that's a list of pointers to Go-allocated objects. In order to keep
|
||||
// the GC from collecting these objects, we declare this as unsafe.Pointer.
|
||||
_ [1]unsafe.Pointer
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -1115,7 +1115,7 @@ func UnmapViewOfFile(addr uintptr) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func updateProcThreadAttribute(attrlist *_PROC_THREAD_ATTRIBUTE_LIST, flags uint32, attr uintptr, value uintptr, size uintptr, prevvalue uintptr, returnedsize *uintptr) (err error) {
|
||||
func updateProcThreadAttribute(attrlist *_PROC_THREAD_ATTRIBUTE_LIST, flags uint32, attr uintptr, value unsafe.Pointer, size uintptr, prevvalue unsafe.Pointer, returnedsize *uintptr) (err error) {
|
||||
r1, _, e1 := Syscall9(procUpdateProcThreadAttribute.Addr(), 7, uintptr(unsafe.Pointer(attrlist)), uintptr(flags), uintptr(attr), uintptr(value), uintptr(size), uintptr(prevvalue), uintptr(unsafe.Pointer(returnedsize)), 0, 0)
|
||||
if r1 == 0 {
|
||||
err = errnoErr(e1)
|
||||
|
Loading…
Reference in New Issue
Block a user