go: syscall: (*Proc).Call does not keep arguments live

The following test program should give the output of the ImageState value for Windows, which for most systems is IMAGE_STATE_COMPLETE. However, when trying to get the value into a slice it is possible for GC to mess up the slice and not get the updated value from the syscall.

Test program:

package main

import (
    "fmt"
    "runtime"
    "syscall"
    "unsafe"
)

var (
    advapi32 = syscall.NewLazyDLL("advapi32.dll")

    regGetValue = advapi32.NewProc("RegGetValueW")
)

const (
    HKEY_LOCAL_MACHINE = 0x80000002

    RRF_RT_REG_SZ = 0x00000002
)

func RegGetValue(hKey uintptr, lpSubKey *uint16, lpValue *uint16, dwFlags uint32, pdwType *uint32) (string, uint32, error) {
    var buf [1024]uint16
    bufSizeBytes := uint32(1024 * 2)
    runtime.GC() // Everything works correctly if this line is removed
    ret, _, callErr := regGetValue.Call(uintptr(hKey), uintptr(unsafe.Pointer(lpSubKey)), uintptr(unsafe.Pointer(lpValue)), uintptr(dwFlags), uintptr(unsafe.Pointer(pdwType)), uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&bufSizeBytes)))
    if ret != 0 {
        return "", bufSizeBytes, callErr
    }
    return syscall.UTF16ToString(buf[:]), bufSizeBytes, nil
}

func main() {
    key, _ := syscall.UTF16PtrFromString("SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Setup\\\\State")
    value, _ := syscall.UTF16PtrFromString("ImageState")
    str, _, _ := RegGetValue(
        HKEY_LOCAL_MACHINE,
        key,
        value,
        RRF_RT_REG_SZ,
        nil,
    )
    fmt.Printf("str: %s\n", str)
}

Expected response: str: IMAGE_STATE_COMPLETE

Actual response: str:

If the regGetValue.Call(...) line is replace with syscall.Syscall9(regGetValue.Addr(), ...) then the code works as intended.

Environment:

go version go1.6.2 windows/amd64

set GOARCH=amd64
set GOBIN=
set GOEXE=.exe
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=
set GORACE=
set GOROOT=C:\go162
set GOTOOLDIR=C:\go162\pkg\tool\windows_amd64
set GO15VENDOREXPERIMENT=1
set CC=gcc
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0
set CXX=g++
set CGO_ENABLED=1

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 33 (26 by maintainers)

Commits related to this issue

Most upvoted comments

OS version: Windows 10 [10.0.10586]

The bug also happens in go1.7beta1