You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
“Conversion of the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr from uintptr to Pointer … immediately after making the call, in the same expression.”
“Conversion of a reflect.SliceHeader or reflect.StringHeaderData field to or from Pointer … when interpreting the content of an actual slice or string value.”
The unsafeptr check provided by cmd/vet warns about uses that do not follow the above patterns. However, it currently flags many violations in x/sys/unix (see #41205) when addresses returned by system calls such as mmap are converted to Go pointers, and in code generated by ebitengine/purego (see #56487) when hard-coded addresses provided by the operating system or linker are converted to Go pointers. In both cases, the program is attempting to create Go pointers that refer to known-valid addresses that are not managed by the Go runtime; notably, the compiler's -d=checkptr mode does not flag them as invalid at run-time.
Ideally, the unsafe.Pointer documentation, the unsafeptr check in cmd/vet, the -d=checkptr mode in cmd/compile, and the real-world usage in syscall, x/sys, and similar low-level libraries should all agree on what is valid. This proposal aims to narrow that gap.
Proposal
I propose that we add another allowed case in the unsafe.Pointer documentation:
(7) Conversion of a uintptr to Pointer when the address is allocated outside of Go.
A uintptr containing a valid memory address allocated outside of Go
(such as by a system call) may be converted to Pointer. The address must remain valid for as long as any Go pointer (of type Pointer
or any other pointer type) refers to it.
The address must remain unavailable to the Go runtime (for example, due to an
allocation using cgo or syscall.Mmap) for as long as any Go pointer
(of type Pointer or any other pointer type) refers to it.
[edited per https://github.com/golang/go/issues/58625#issuecomment-1440188404\]
The uintptr constant 0 may be converted to Pointer.
The resulting Pointer has the value nil.
(https://go.dev/play/p/Wh5f8k0_yyR), on the theory that the memory layout of a uintptr must be in some sense “equivalent” to the memory layout of unsafe.Pointer. However, I believe that such a rewrite does not capture the intent of the code as accurately, and should not be necessary.
Background
The documentation for
unsafe.Pointercurrently lists six valid conversion patterns:“Conversion of a
*T1toPointerto*T2… [p]rovided thatT2is no larger thanT1and that the two share an equivalent memory layout.”“Conversion of a
Pointerto auintptr(but not back toPointer).”“Conversion of a
Pointerto auintptrand back, with arithmetic … in the same expression, with only the intervening arithmetic between them.”“Conversion of a
Pointerto auintptrwhen callingsyscall.Syscall… [or] in the argument list of a call to a function implemented in assembly.” (Compare proposal: unsafe: clarify unsafe.Pointer rules for package syscall #34684.)“Conversion of the result of
reflect.Value.Pointerorreflect.Value.UnsafeAddrfromuintptrtoPointer… immediately after making the call, in the same expression.”“Conversion of a
reflect.SliceHeaderorreflect.StringHeaderDatafield to or fromPointer… when interpreting the content of an actual slice or string value.”The
unsafeptrcheck provided bycmd/vetwarns about uses that do not follow the above patterns. However, it currently flags many violations inx/sys/unix(see #41205) when addresses returned by system calls such asmmapare converted to Go pointers, and in code generated by ebitengine/purego (see #56487) when hard-coded addresses provided by the operating system or linker are converted to Go pointers. In both cases, the program is attempting to create Go pointers that refer to known-valid addresses that are not managed by the Go runtime; notably, the compiler's-d=checkptrmode does not flag them as invalid at run-time.Ideally, the
unsafe.Pointerdocumentation, theunsafeptrcheck incmd/vet, the-d=checkptrmode incmd/compile, and the real-world usage insyscall,x/sys, and similar low-level libraries should all agree on what is valid. This proposal aims to narrow that gap.Proposal
I propose that we add another allowed case in the
unsafe.Pointerdocumentation:The
unsafeptrcheck incmd/vetwould be changed to allow the new case, resolving the warnings inx/sysandebitengine/purego.The compiler's
-d=checkptrmode would check conversions fromuintptrtounsafe.Pointer. The conversion may throw at run-time if:uintptrrefers to an address managed by Go that is not explicitly pinned (runtime: provide Pinner API for object pinning #46787), ortheuintptrrefers to an invalid (unmapped) nonzero address.[edit: per unsafe: allow conversion of uintptr to unsafe.Pointer when it points to non-Go memory #58625 (comment), unmapped addresses should be allowed as long as they cannot become Go addresses]
Alternatives
The
vetwarning can be worked around today by relying on a liberal reading of the “equivalent memory layout” rule, rewriting(https://go.dev/play/p/ZWZxv6URqTW) as
(https://go.dev/play/p/Wh5f8k0_yyR), on the theory that the memory layout of a
uintptrmust be in some sense “equivalent” to the memory layout ofunsafe.Pointer. However, I believe that such a rewrite does not capture the intent of the code as accurately, and should not be necessary.(CC @golang/runtime)