-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Description
Background
The documentation for unsafe.Pointer currently 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 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
uintptrtoPointerwhen the address is allocated outside of Go.A
uintptrcontaining a valid memory address allocated outside of Go
(such as by a system call) may be converted toPointer.
The address must remain valid for as long as any Go pointer (of typePointer
or any other pointer type) refers to it.
The address must remain unavailable to the Go runtime (for example, due to an
allocation usingcgoorsyscall.Mmap) for as long as any Go pointer
(of typePointeror any other pointer type) refers to it.
[edited per https://github.com/golang/go/issues/58625#issuecomment-1440188404\]The uintptr constant
0may be converted toPointer.
The resultingPointerhas the valuenil.addr, _, err := syscall.Syscall(…) … p := unsafe.Pointer(addr)
The unsafeptr check in cmd/vet would be changed to allow the new case, resolving the warnings in x/sys and ebitengine/purego.
The compiler's -d=checkptr mode would check conversions from uintptr to unsafe.Pointer. The conversion may throw at run-time if:
- The
uintptrrefers to an address managed by Go that is not explicitly pinned (runtime: provide Pinner API for object pinning #46787), or theuintptrrefers 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 vet warning can be worked around today by relying on a liberal reading of the “equivalent memory layout” rule, rewriting
var addr uintptr = …
p := unsafe.Pointer(addr)(https://go.dev/play/p/ZWZxv6URqTW) as
var addr uintptr = …
p := *(*unsafe.Pointer)(unsafe.Pointer(&addr))(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.
(CC @golang/runtime)
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
Status