How to determine if returned pointer is on the stack or heap

Distinguishing between malloc/free and new/delete is generally not possible, at least not in a reliable and/or portable way. Even more so as new simply wrapps malloc anyway in many implementations.

None of the following alternatives to distinguish heap/stack have been tested, but they should all work.

Linux:

  1. Solution proposed by Luca Tettananti, parse /proc/self/maps to get the address range of the stack.
  2. As the first thing at startup, clone your process, this implies supplying a stack. Since you supply it, you automatically know where it is.
  3. Call GCC’s __builtin_frame_address function with increasing level parameter until it returns 0. You then know the depth. Now call __builtin_frame_address again with the maximum level, and once with a level of 0. Anything that lives on the stack must necessarily be between these two addresses.
  4. sbrk(0) as the first thing at startup, and remember the value. Whenever you want to know if something is on the heap, sbrk(0) again — something that’s on the heap must be between the two values. Note that this will not work reliably with allocators that use memory mapping for large allocations.

Knowing the location and size of the stack (alternatives 1 and 2), it’s trivial to find out if an address is within that range. If it’s not, is necessarily “heap” (unless someone tries to be super smart-ass and gives you a pointer to a static global, or a function pointer, or such…).

Windows:

  1. Use CaptureStackBackTrace, anything living on the stack must be between the returned pointer array’s first and last element.
  2. Use GCC-MinGW (and __builtin_frame_address, which should just work) as above.
  3. Use GetProcessHeaps and HeapWalk to check every allocated block for a match. If none match for none of the heaps, it’s consequently allocated on the stack (… or a memory mapping, if someone tries to be super-smart with you).
  4. Use HeapReAlloc with HEAP_REALLOC_IN_PLACE_ONLY and with exactly the same size. If this fails, the memory block starting at the given address is not allocated on the heap. If it “succeeds”, it is a no-op.
  5. Use GetCurrentThreadStackLimits (Windows 8 / 2012 only)
  6. Call NtCurrentTeb() (or read fs:[18h]) and use the fields StackBase and StackLimit of the returned TEB.

Leave a Comment