windows: use Go-managed pointer list for ProcThreadAttributeList

It turns out that if you write Go pointers to Go memory, the Go compiler
must be involved so that it generates various calls to the GC in the
process. Letting Windows write Go pointers to Go memory violated this.

We fix this by having all the Windows-managed memory be just a boring
[]byte blob. Then, in order to prevent the GC from prematurely cleaning
up the pointers referenced by that []byte blob, or in the future moving
memory and attempting to fix up pointers, we copy the data to the
Windows heap and then maintain a little array of pointers that have been
used. Every time the Update function is called with a new pointer, we
make a copy and append it to the list.  Then, on Delete, we free the
pointers from the Windows heap.

Updates golang/go#44900.

Change-Id: I42340a93fd9f6b8d10340634cf833fd4559a5f4f
Reviewed-on: https://go-review.googlesource.com/c/sys/+/300369
Trust: Jason A. Donenfeld <Jason@zx2c4.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:
Jason A. Donenfeld
2021-05-21 09:01:06 +00:00
parent e8d321eab0
commit 6ca3eb03df
3 changed files with 47 additions and 51 deletions
-34
View File
@@ -18,7 +18,6 @@ import (
"strings"
"syscall"
"testing"
"time"
"unsafe"
"golang.org/x/sys/windows"
@@ -506,39 +505,6 @@ func TestNTStatusConversion(t *testing.T) {
}
}
func TestProcThreadAttributeListPointers(t *testing.T) {
list, err := windows.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 = list.Update(windows.PROC_THREAD_ATTRIBUTE_HANDLE_LIST, 0, unsafe.Pointer(&fds[0]), uintptr(len(fds))*unsafe.Sizeof(fds[0]), nil, nil)
if err != nil {
list.Delete()
t.Errorf("unable to update ProcThreadAttributeList: %v", err)
return
}
runtime.GC()
runtime.GC()
select {
case <-done:
t.Error("ProcThreadAttributeList was garbage collected unexpectedly")
default:
}
list.Delete()
runtime.GC()
runtime.GC()
select {
case <-done:
case <-time.After(time.Second):
t.Error("ProcThreadAttributeList was not garbage collected after a second")
}
}
func TestPEBFilePath(t *testing.T) {
peb := windows.RtlGetCurrentPeb()
if peb == nil || peb.Ldr == nil {