Go

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:

  1. Convert *T1 to unsafe.Pointer to *T2, when T2 is no larger than T1 and the memory layouts are compatible. math.Float64bits is the canonical example.
  2. Convert unsafe.Pointer to uintptr only to print or compare it. The integer is not a reference, so it does not keep the object alive.
  3. Pointer arithmetic, converting to uintptr, adding an offset and converting back within one expression. Prefer unsafe.Add.
  4. uintptr arguments to syscall.Syscall, where the conversion appears in the call's argument list itself.
  5. reflect.Value.Pointer / UnsafeAddr results, converted back to unsafe.Pointer immediately.
  6. reflect.SliceHeader / StringHeader data fields. These are deprecated: use unsafe.Slice, unsafe.String, unsafe.SliceData and unsafe.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

All 38 Memory, GC & Runtime Internals questions