Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/cgo: cannot convert from type *_Ctype_char to type _Ctype_HANDLE (1.18 regression) #51726

Closed
josharian opened this issue Mar 16, 2022 · 9 comments
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. release-blocker Soon This needs to be done soon. (regressions, serious bugs, outages)
Milestone

Comments

@josharian
Copy link
Contributor

This code compiled on Windows with Go 1.17, but not with Go 1.18:

package p

/*
#include <windows.h>
*/
import "C"

func Error() {
	var x *C.char
	_ = C.HLOCAL(x)
}

The error message is:

p.go:10:15: cannot convert x (variable of type *_Ctype_char) to type _Ctype_HANDLE

Reverting 635e493 allows the code to compile again.

cc @eliasnaur @randall77 @dsnet @bradfitz

@josharian josharian added the Soon This needs to be done soon. (regressions, serious bugs, outages) label Mar 16, 2022
@josharian josharian added this to the Go1.18.1 milestone Mar 16, 2022
@ALTree
Copy link
Member

ALTree commented Mar 16, 2022

@josharian is this a problem in tip too? (I assume it is). If that's the case, this issue should be put in the 1.19 milestone (to track fixing on tip), and the backport issue that you can ask gopherbot to open will be the one in the 1.18.1 milestone (to track the backporting).

@josharian josharian removed this from the Go1.18.1 milestone Mar 16, 2022
@josharian
Copy link
Contributor Author

@ALTree oh right. thanks.

@gopherbot please open a backport issue for Go 1.18. This is a compilation regression.

@gopherbot
Copy link

Backport issue(s) opened: #51728 (for 1.18).

Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://go.dev/wiki/MinorReleases.

@bradfitz
Copy link
Contributor

josharian added a commit to tailscale/go that referenced this issue Mar 16, 2022
…to Windows handle types"

This reverts commit 635e493.

This is a temporary workaround for golang#51726.
It isn't a good one, as that commit was fixing an important bug,
but it is at least no worse than Go 1.17.

Once that issue has been fixed properly upstream, we can revert
this revert and pull in the proper fix.

Change-Id: I8b3fbad2392d12a84f977761a57e545a4678cca9
josharian added a commit to tailscale/go that referenced this issue Mar 16, 2022
…ws handle types"

This reverts commit 635e493.

This is a temporary workaround for golang#51726.
It isn't a good one, as that commit was fixing an important bug,
but it is at least no worse than Go 1.17.

Once that issue has been fixed properly upstream, we can revert
this revert and pull in the proper fix.

Change-Id: I8b3fbad2392d12a84f977761a57e545a4678cca9
josharian added a commit to tailscale/tailscale that referenced this issue Mar 16, 2022
…lscale Go toolchain

The certstore code is impacted by golang/go#51726.
The Tailscale Go toolchain fork contains a temporary workaround,
so it can compile it. Once the upstream toolchain can compile certstore,
presumably in Go 1.18.1, we can revert this change.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
josharian added a commit to tailscale/tailscale that referenced this issue Mar 16, 2022
…lscale Go toolchain

The certstore code is impacted by golang/go#51726.
The Tailscale Go toolchain fork contains a temporary workaround,
so it can compile it. Once the upstream toolchain can compile certstore,
presumably in Go 1.18.1, we can revert this change.

Note that depaware runs with the upstream toolchain.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
@heschi heschi added the NeedsFix The path to resolution is known, but the work has not been done. label Mar 16, 2022
josharian added a commit to tailscale/tailscale that referenced this issue Mar 16, 2022
…lscale Go toolchain

The certstore code is impacted by golang/go#51726.
The Tailscale Go toolchain fork contains a temporary workaround,
so it can compile it. Once the upstream toolchain can compile certstore,
presumably in Go 1.18.1, we can revert this change.

Note that depaware runs with the upstream toolchain.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
josharian added a commit to tailscale/tailscale that referenced this issue Mar 16, 2022
…lscale Go toolchain

The certstore code is impacted by golang/go#51726.
The Tailscale Go toolchain fork contains a temporary workaround,
so it can compile it. Once the upstream toolchain can compile certstore,
presumably in Go 1.18.1, we can revert this change.

Note that depaware runs with the upstream toolchain.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
@ianlancetaylor
Copy link
Contributor

CL 350070 is based on the valid observation that although the type of Windows HANDLE is void*, the values stored in variables of type HANDLE are normally not pointers. Since Go's GC really wants to know whether something is a pointer or not, telling it that values of type HANDLE are not pointers seems like the right move in general.

Now, perhaps it's true that HLOCAL values happen to be pointers to memory. As far as I can tell the correct way to get an HLOCAL value is to call LocalHandle; I guess that's a pointer to memory? And it appears that new code is supposed to call HeapAlloc which doesn't have this problem.

Anyhow, if we are expected to convert Go pointers to HLOCAL and thus to HANDLE, we are in trouble, because now a HANDLE is sometimes a pointer to memory and sometimes not.

I don't see a problem with the code in cyolosecurity, and the code can easily be changed to compile by writing

	defer C.LocalFree(C.HLOCAL(unsafe.Pointer(cmsg)))

So it seems to me that the Go 1.18 behavior is safer and code that plays fast and loose with pointers should be adjusted. (Note that the Go 1 compatibility guarantee does not apply to cgo code.)

Of course it would be better if that code could continue to work, and I'm certainly open to suggestions.

@dblohm7
Copy link

dblohm7 commented Mar 16, 2022

I'll offer a bit more context here:

The LocalAlloc, LocalFree, GlobalAlloc, GlobalFree and their other adjacent APIs are holdovers from 16-bit Windows. In those days, when we allocated heap memory, those functions would truly return a handle. The programmer would have to then call LocalLock or GlobalLock to convert that handle into a pointer, and then call LocalFree or GlobalFree when done accessing the memory.

Nowadays, for better or worse, the HLOCAL handle is frequently treated as synonymous with the pointer. HGLOBALs often are as well (though my personal preference is not to do so).

In general, I'd suggest that the best rule of thumb is to avoid allocating memory using those functions unless other APIs involved specifically require it -- eg the Win32 clipboard APIs expect to work with HGLOBALs. If some some reason a block of memory must absolutely be allocated via the Windows heap, HeapAlloc should be the default API choice.

josharian added a commit to tailscale/certstore that referenced this issue Mar 16, 2022
josharian added a commit to tailscale/go that referenced this issue Mar 16, 2022
…to Windows handle types""

This reverts commit 860e090.

Per conversation in golang#51726,
we are going to work around the issue in github.com/tailscale/certstore
by explicitly casting to unsafe.Pointer.
josharian added a commit to tailscale/go that referenced this issue Mar 16, 2022
…to Windows handle types""

This reverts commit 860e090.

Per conversation in golang#51726,
we are going to work around the issue in github.com/tailscale/certstore
by explicitly casting to unsafe.Pointer.
@ianlancetaylor
Copy link
Contributor

@dblohm7 Thanks.

It looks like the code that led to this issue is calling FormatMessage with the FORMAT_MESSAGE_ALLOCATE_BUFFER which does indeed allocate memory using LocalAlloc. It's then casually converting that memory to char*, which I guess is fine today but it should really be calling LocalLock, and should really be passing the original value to LocalFree.

So I think there is a plausible argument that the code in question is technically incorrect and this change caught the error.

@josharian
Copy link
Contributor Author

OK, sounds like this working as intended. Thanks, @ianlancetaylor and @dblohm7.

@ianlancetaylor
Copy link
Contributor

Thanks, closing this issue as there currently doesn't seem to be anything to change in the Go tools. Please comment if you disagree.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. release-blocker Soon This needs to be done soon. (regressions, serious bugs, outages)
Projects
None yet
Development

No branches or pull requests

7 participants