]> Cypherpunks repositories - gostls13.git/commitdiff
syscall: treat proc thread attribute lists as unsafe.Pointers
authorJason A. Donenfeld <Jason@zx2c4.com>
Sun, 28 Feb 2021 11:18:18 +0000 (12:18 +0100)
committerJason A. Donenfeld <Jason@zx2c4.com>
Thu, 4 Mar 2021 19:59:23 +0000 (19:59 +0000)
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>
src/syscall/exec_windows.go
src/syscall/export_windows_test.go [new file with mode: 0644]
src/syscall/syscall_windows.go
src/syscall/syscall_windows_test.go
src/syscall/types_windows.go
src/syscall/zsyscall_windows.go

index 7b73cf1f6f1ebdc7d44821937e610a0a9e87b959..b20a27d28b8fa590fe29f6ca658672da98e0f779 100644 (file)
@@ -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
        }
diff --git a/src/syscall/export_windows_test.go b/src/syscall/export_windows_test.go
new file mode 100644 (file)
index 0000000..a72a1ee
--- /dev/null
@@ -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
index cc8dc487d354b74c99d97e1c912cfbe025669772..05a7d3027d3904ab3ad6daa53324c0b284f74cd9 100644 (file)
@@ -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
index a9ae54752b30a5a10516c0dd8b5c991f3b61004a..d5e8d58b5a2893785d9eccf48bf4b02368bdb164 100644 (file)
@@ -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")
+       }
+}
index 384b5b4f2c1f6ce12361d0c9aa5fb7bf685222cb..31fe7664c93bb19964cf1234a74798d6dbd7a393 100644 (file)
@@ -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 (
index b08e6ac5c2a8b99be30f4fdfcefd2f13f28d12e2..10d0f54e8c71c0e59b1a1c9d2cff5bab0339f536 100644 (file)
@@ -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)