CGO known implementation bug in pointer checks

Viewed 155

According to the documentation of CGO (https://pkg.go.dev/cmd/cgo), there is a known bug in the implementation:

Note: the current implementation has a bug. While Go code is permitted to write nil or a C pointer (but not a Go pointer) to C memory, the current implementation may sometimes cause a runtime error if the contents of the C memory appear to be a Go pointer. Therefore, avoid passing uninitialized C memory to Go code if the Go code is going to store pointer values in it. Zero out the memory in C before passing it to Go.

I looked for this in the issue tracker at GitHub but can't find it there. Could someone please elaborate on why this might happen? How does the runtime find Go pointers in uninitialized C memory?

Eg. let's say I am passing an uninitialized char array to a Go function from C, how can the runtime interpret a Go pointer in this memory?

Also, the "if the Go code is going to store pointer values in it" part confuses me. Why does later use of this memory matter?

1 Answers

I looked for this in the issue tracker at GitHub but can't find it there.

The bug to which this comment refers is https://golang.org/issue/19928, which is admittedly not easy to find.

Could someone please elaborate on why this might happen? How does the runtime find Go pointers in uninitialized C memory?

During certain parts of a garbage collection cycle, the collector turns on a ā€œwrite barrierā€ for writes to pointers in the Go heap, recording the previously-stored pointer value to ensure that it is not missed during the GC scan.

The bug here is that the write barrier sometimes also records the previously-stored pointer value for pointers outside the Go heap. If that value looks like a Go pointer, the garbage collector may try to scan it recursively, and could crash if it isn't actually a valid pointer.

Eg. let's say I am passing an uninitialized char array to a Go function from C, how can the runtime interpret a Go pointer in this memory?

This bug should not occur if the uninitialized data passed to Go is of a type that does not contain any pointers. So for a char array in particular you should be fine either way.

Also, the "if the Go code is going to store pointer values in it" part confuses me. Why does later use of this memory matter?

The compiler inserts write barriers at store instructions for pointer types. If the Go program does not store pointers, then the compiler will not emit any write barriers, and the bug in the write barrier will not be triggered.

Related