Go

Why is keeping a pointer as uintptr dangerous even if the object is still referenced elsewhere?

Question 352HardGo 1.22 to 1.25

There are two independent problems:

  1. No liveness. The GC does not treat a uintptr as a reference. If the uintptr is the last "reference", the object can be collected and its memory reused, and converting back gives you a dangling pointer. Worse, the GC may see a valid-looking unsafe.Pointer into freed memory and crash with "found bad pointer in Go heap".
  2. Objects can move. The heap does not move objects, but stacks do (see stack growth). If the variable is on the stack, a later stack copy leaves your integer pointing at stale memory, and the runtime cannot fix it up because it does not know the integer is an address. The Go team also keeps the right to add a moving collector.
// WRONG: two statements
addr := uintptr(unsafe.Pointer(&buf[0]))
syscall.Syscall(SYS_WRITE, fd, addr, n)

// RIGHT: conversion inside the call expression; the compiler
// keeps buf alive and pinned for the duration of the call
syscall.Syscall(SYS_WRITE, fd, uintptr(unsafe.Pointer(&buf[0])), n)

If you must hand Go memory to C for longer, use runtime.Pinner (Go 1.21+) to pin the object, or copy it into C-allocated memory. cgo.Handle lets C code refer to Go values by an integer handle instead of a raw pointer.

What the interviewer is looking for: you understand that a uintptr is "just a number" to the runtime.

More on Memory, GC & Runtime Internals

All 38 Memory, GC & Runtime Internals questions