英文:
CGO known implementation bug in pointer checks
问题
根据CGO的文档(https://pkg.go.dev/cmd/cgo),实现中存在一个已知的bug:
注意:当前的实现存在一个bug。虽然Go代码可以将nil或C指针(但不是Go指针)写入C内存,但如果C内存的内容看起来像是一个Go指针,当前的实现有时可能会导致运行时错误。因此,如果Go代码将在其中存储指针值,请避免将未初始化的C内存传递给Go代码。在将其传递给Go之前,在C中将内存清零。
我在GitHub的问题跟踪器中搜索了一下,但没有找到相关问题。有人可以详细说明一下为什么会发生这种情况吗?运行时如何在未初始化的C内存中找到Go指针?
例如,假设我从C向Go函数传递一个未初始化的char数组,运行时如何解释这段内存中的Go指针?
此外,“如果Go代码将在其中存储指针值”这部分让我感到困惑。为什么后续对这段内存的使用会有影响?
英文:
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
得分: 3
我在GitHub的问题跟踪器中搜索了一下,但没有找到这个问题。
这个评论所指的错误是https://golang.org/issue/19928,确实不容易找到。😅
有人可以详细解释一下为什么会发生这种情况吗?运行时如何在未初始化的C内存中找到Go指针?
在垃圾回收周期的某些阶段,收集器为Go堆中指针的写入打开了一个“写屏障”,记录先前存储的指针值,以确保在GC扫描期间不会错过它。
这里的bug是写屏障有时也会记录Go堆之外指针的先前存储值。如果该值看起来像是一个Go指针,垃圾收集器可能会尝试递归扫描它,并且如果它实际上不是一个有效的指针,可能会导致崩溃。
例如,假设我将一个未初始化的char数组从C传递给一个Go函数,运行时如何解释这段内存中的Go指针?
如果传递给Go的未初始化数据不包含任何指针类型,那么这个bug不应该发生。所以对于特定的char数组,无论如何都应该没问题。
此外,“如果Go代码将在其中存储指针值”这部分让我感到困惑。为什么后续对这段内存的使用会有影响?
编译器会在指针类型的存储指令中插入写屏障。如果Go程序不存储指针,那么编译器将不会生成任何写屏障,因此写屏障中的bug不会被触发。
英文:
> 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论