From 8c5d1a41ad53d9a24885c760cca9002275807d48 Mon Sep 17 00:00:00 2001 From: Michael Anthony Knyszek Date: Wed, 24 May 2023 19:53:14 +0000 Subject: [PATCH] cmd/cgo: rewrite pointer passing rules in terms of pinning With the introduction of runtime.Pinner, we need to update the cgo pointer passing rules to accomodate the new functionality. These rule changes are easier to describe if the rest of the pointer passing rules are described in terms of pinning as well (Go memory is implicitly pinned when a pointer to it is passed to a C function, and implicitly unpinned when that function returns). For #46787. Change-Id: I263f03412bc9165f19c9ada72fb005ed0483a8ee Reviewed-on: https://go-review.googlesource.com/c/go/+/498116 Reviewed-by: Austin Clements Reviewed-by: Ian Lance Taylor Run-TryBot: Michael Knyszek TryBot-Result: Gopher Robot Auto-Submit: Michael Knyszek --- src/cmd/cgo/doc.go | 71 ++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 56f52d1ae0..b1a288f573 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -350,31 +350,52 @@ pointers, depending on the element types. All the discussion below about Go pointers applies not just to pointer types, but also to other types that include Go pointers. -Go code may pass a Go pointer to C provided the Go memory to which it -points does not contain any Go pointers. The C code must preserve -this property: it must not store any Go pointers in Go memory, even -temporarily. When passing a pointer to a field in a struct, the Go -memory in question is the memory occupied by the field, not the entire -struct. When passing a pointer to an element in an array or slice, -the Go memory in question is the entire array or the entire backing -array of the slice. - -C code may not keep a copy of a Go pointer after the call returns. -This includes the _GoString_ type, which, as noted above, includes a -Go pointer; _GoString_ values may not be retained by C code. - -A Go function called by C code may not return a Go pointer (which -implies that it may not return a string, slice, channel, and so -forth). A Go function called by C code may take C pointers as -arguments, and it may store non-pointer or C pointer data through -those pointers, but it may not store a Go pointer in memory pointed to -by a C pointer. A Go function called by C code may take a Go pointer -as an argument, but it must preserve the property that the Go memory -to which it points does not contain any Go pointers. - -Go code may not store a Go pointer in C memory. C code may store Go -pointers in C memory, subject to the rule above: it must stop storing -the Go pointer when the C function returns. +All Go pointers passed to C must point to pinned Go memory. Go pointers +passed as function arguments to C functions have the memory they point to +implicitly pinned for the duration of the call. Go memory reachable from +these function arguments must be pinned as long as the C code has access +to it. Whether Go memory is pinned is a dynamic property of that memory +region; it has nothing to do with the type of the pointer. + +Go values created by calling new, by taking the address of a composite +literal, or by taking the address of a local variable may also have their +memory pinned using [runtime.Pinner]. This type may be used to manage +the duration of the memory's pinned status, potentially beyond the +duration of a C function call. Memory may be pinned more than once and +must be unpinned exactly the same number of times it has been pinned. + +Go code may pass a Go pointer to C provided the memory to which it +points does not contain any Go pointers to memory that is unpinned. When +passing a pointer to a field in a struct, the Go memory in question is +the memory occupied by the field, not the entire struct. When passing a +pointer to an element in an array or slice, the Go memory in question is +the entire array or the entire backing array of the slice. + +C code may keep a copy of a Go pointer only as long as the memory it +points to is pinned. + +C code may not keep a copy of a Go pointer after the call returns, +unless the memory it points to is pinned with [runtime.Pinner] and the +Pinner is not unpinned while the Go pointer is stored in C memory. +This implies that C code may not keep a copy of a string, slice, +channel, and so forth, because they cannot be pinned with +[runtime.Pinner]. + +The _GoString_ type also may not be pinned with [runtime.Pinner]. +Because it includes a Go pointer, the memory it points to is only pinned +for the duration of the call; _GoString_ values may not be retained by C +code. + +A Go function called by C code may return a Go pointer to pinned memory +(which implies that it may not return a string, slice, channel, and so +forth). A Go function called by C code may take C pointers as arguments, +and it may store non-pointer data, C pointers, or Go pointers to pinned +memory through those pointers. It may not store a Go pointer to unpinned +memory in memory pointed to by a C pointer (which again, implies that it +may not store a string, slice, channel, and so forth). A Go function +called by C code may take a Go pointer but it must preserve the property +that the Go memory to which it points (and the Go memory to which that +memory points, and so on) is pinned. These rules are checked dynamically at runtime. The checking is controlled by the cgocheck setting of the GODEBUG environment -- 2.50.0