gocgolmdbunsafe-pointers

Mysterious error: "cgo argument has Go pointer to unpinned Go pointer"


I'm getting this error and I don't understand what it means even after reading the documentation. Actually I do thought I understood it, but then it doesn't fit the way it is happening for me. There is also no other information about this specific issue anywhere else on the internet it seems.

For me it happens in this function (not my library, and it seems to be working fine in many places, so that's even weirder).

In an effort to understand the bug better, I've modified it locally to make each argument to the C function there be constructed separately and see which part triggered the error:

But turns out the error happens at the line under the cursor, so I think it's just related to the C.lmdbgo_mdb_cursor_get1() call entirely.

I thought the error meant that I was passing a Go pointer to a C function and that it could be solved by wrapping the Go pointer with an unsafe.Pointer() call, but that can't be as it's already being done and all the arguments to the C function in question are already C types, not Go types.


UPDATE: With a little more digging I've discovered that the actual panic() is being issued at line 689 of /src/runtime/cgocall.go:

So cgoIsGoPointer() returns true and isPinned() returns false.

What do I have to do to "pin" a Go pointer, after all? Or is this question the wrong question?

UPDATE: With a little more digging I've discovered that the parameter causing the trouble is c.txn.key, which is outside of my control and seems to be set to new(C.MDB_val).


Solution

  • According to https://github.com/PowerDNS/lmdb-go/issues/28 this issue was really being caused by the usage of the Go new() constructor to create C pointers that are then passed to C functions. These should have been created with C.malloc().

    Apparently this issue had never showed up for anyone else because no one was doing the same mistake I did in my usage of the library: using the same transaction from multiple goroutines, which is explicitly forbidden by LMDB (well, LMDB says threads, but maybe my goroutines were spawning new threads). According to wojas:

    This spawning of goroutines may have prevented certain Go compiler optimizations that have previously hidden this allocation bug.