cmd/vet: "possible misuse of unsafe.Pointer" on C pointer -> uintptr -> different package -> unsafe.Pointer -> C pointer

766 views
Skip to first unread message

Alex

unread,
Feb 13, 2020, 11:42:30 AM2/13/20
to golang-nuts
I have to pass C pointers between packages so I used uintptr like how syscall does things.
However go vet gives the message "possible misuse of unsafe.Pointer". 

Is there something I could do to avoid vet complaining?

Package A:
type Foo struct {
procAddr uintptr
}

func (f Foo) ProcAddr() uintptr {
return f.procAddr
}

func Bar() Foo {
        return Foo{C.SomeFunc()}
}

Package B:
p := C.ASDF(unsafe.Pointer(A.Bar().ProcAddr())) // possible misuse of unsafe.Pointer

Jake Montgomery

unread,
Feb 13, 2020, 12:02:24 PM2/13/20
to golang-nuts
You need to read https://golang.org/pkg/unsafe/#Pointer very, very, very carefully before using unsafe.Pointer in any way. It spells out 6 conversions that are considered "valid". It says: "Code not using these patterns is likely to be invalid today or to become invalid in the future." AFAICT, your code does not fit any of those 6 allowable patterns.

Go is simple and easy, CGO is tricky, difficult and full of dragons.

Alex

unread,
Feb 13, 2020, 12:37:47 PM2/13/20
to golang-nuts
I have to use a massive third party API so it's not really a choice to use cgo.

Do you have any safer options to pass C pointers between packages?
The C pointers are all C allocated and package B needs to call C functions directly and pass those pointers.

Keith Randall

unread,
Feb 13, 2020, 6:40:46 PM2/13/20
to golang-nuts
Why all the conversions? Why not just pass the C pointer from one package to another? Or an unsafe.Pointer? You can use a placeholder type, even. Make procAddr a *byte, for example.

The pointer conversion rules are intended to ensure safety when playing with Go pointers (by that, I mean pointers to objects in the Go heap or stack). They're definitely overkill for pointers that you know point into the C heap. Unfortunately, the vet rules don't know where the pointers came from.

Alex

unread,
Feb 13, 2020, 7:37:04 PM2/13/20
to golang-nuts
Why not just pass the C pointer from one package to another?
 
From docs:
Cgo translates C types into equivalent unexported Go types. Because the translations are unexported, a Go package should not expose C types in its exported API: a C type used in one Go package is different from the same C type used in another.

There is a issue to allow that tho https://github.com/golang/go/issues/13467

Or an unsafe.Pointer?
 
Same reason syscall doesn't use unsafe.Pointer, so you have to import unsafe to convert.

Make procAddr a *byte
 
I don't want to risk the GC (or even the user) thinking it's a Go pointer and doing stuff it shouldn't. 
CGO uses uintptr for certain pointer types, so thought it's better to be safe, the docs says:

These types are uintptr on the Go side because they would otherwise confuse the Go garbage collector; they are sometimes not really pointers but data structures encoded in a pointer type. All operations on these types must happen in C. The proper constant to initialize an empty such reference is 0, not nil.

Ian Lance Taylor

unread,
Feb 13, 2020, 11:41:48 PM2/13/20
to Alex, golang-nuts
If the pointers involved are all C pointers--if they all point to
memory allocated in C--then use little wrapper functions in your cgo
comment to do the conversion to uintptr_t on the C side rather than on
the Go side.

/*
static uintptr_t SomeFuncUintptr() { return (uintptr_t)(SomeFunc()); }
*/
import "C"

Then the Go code will only ever see uintptr values, which is what you want.

To reiterate, this is only safe if the C code is only ever using
pointers to memory allocated in C.

Ian

Alex

unread,
Feb 14, 2020, 3:45:08 AM2/14/20
to golang-nuts
wow ... that just seems so obvious now. Thanks!

On Friday, 14 February 2020 12:41:48 UTC+8, Ian Lance Taylor wrote:
Reply all
Reply to author
Forward
0 new messages