What are the rules for valid unsafe.Pointer usage?
Question 351HardGo 1.22 to 1.25
The unsafe package documents six valid patterns. Everything else is invalid, even if it happens to work today:
- Convert
*T1tounsafe.Pointerto*T2, when T2 is no larger than T1 and the memory layouts are compatible.math.Float64bitsis the canonical example. - Convert
unsafe.Pointertouintptronly to print or compare it. The integer is not a reference, so it does not keep the object alive. - Pointer arithmetic, converting to
uintptr, adding an offset and converting back within one expression. Preferunsafe.Add. uintptrarguments tosyscall.Syscall, where the conversion appears in the call's argument list itself.reflect.Value.Pointer/UnsafeAddrresults, converted back tounsafe.Pointerimmediately.reflect.SliceHeader/StringHeaderdata fields. These are deprecated: useunsafe.Slice,unsafe.String,unsafe.SliceDataandunsafe.StringData.
arr := [4]int64{10, 20, 30, 40}
p := unsafe.Pointer(&arr[0])
// Valid: arithmetic via unsafe.Add (Go 1.17+)
third := *(*int64)(unsafe.Add(p, 2*unsafe.Sizeof(arr[0])))
fmt.Println(third) // 30
// INVALID: pointer stored as an integer across statements
u := uintptr(p)
// ... GC could run here; if arr lived on a stack that moved, u is stale
bad := (*int64)(unsafe.Pointer(u + 8)) // go vet: possible misuse of unsafe.Pointer
_ = bad
Tools: go vet, which includes the unsafeptr check; -gcflags=all=-d=checkptr, which is enabled automatically by -race and -msan; and -asan. Arithmetic must never produce a pointer past the end of the allocation.
More on Memory, GC & Runtime Internals
- Q349Explain 64-bit atomic alignment and false sharing. How do you lay out hot concurrent counters?
- Q350What are zero-sized types' memory semantics? What do these print?
- Q352Why is keeping a pointer as
uintptrdangerous even if the object is still referenced elsewhere? - Q353How do you do zero-copy
[]byte↔stringconversion correctly, and what can go wrong? - Q354What does this program print? Explain how finalizers behave.
- Q355What is
runtime.AddCleanupand why is it preferred overSetFinalizer?