realloc(NULL, 0) returned NULL without allocating anything, while malloc(0) returns a unique pointer. C11 7.22.3.5p3 says realloc with a null pointer behaves like malloc for the specified size, so code that grows an empty buffer from zero elements (p = realloc(p, n * size) with n == 0) and treats NULL as out of memory failed on its first call.
FreeBSD 11.4 never returns NULL from realloc for a zero size; je_realloc rounds the size up before choosing between resizing and allocating (contrib/jemalloc/src/jemalloc.c, je_realloc).
A null pointer with size 0 now reaches the allocator's realloc, which allocates as malloc(0) does. A non-null pointer with size 0 is still freed and NULL returned, as before.
Co-authored-by: milescoviello <kitslayer29@gmail.com>
A title that replaces the allocator through its libc parameters also
supplies malloc_stats_fast in that table, and PPSA28997 calls it. It is
now forwarded to the title's function. Without a replacement allocator
that provides it, the call throws.
The finalizer was registered with std::atexit from the heap handshake, which
runs after libc's own __cxa_atexit runner and the title's exit callbacks
register theirs. std::atexit runs handlers in reverse registration order, so
the heap was torn down first: an exit user that allocated then threw
"application heap: allocator has been finalized" out of a destructor path,
which terminates the process.
Register the finalizer from a namespace-scope object instead, so it is
registered when libc is loaded and therefore runs last, and make finalize()
idempotent rather than throwing on a second pass.
application_heap_exit_order registers a guest exit callback that allocates
through the heap and aborts with "application heap: allocator has been
finalized" on the previous code.
SceLibcMallocReplace lists calloc before realloc (malloc, free, calloc,
realloc, memalign, reallocalign, posix_memalign, ...), as the table in
retail titles shows. realloc therefore called the application's calloc
with the old pointer as the element count, and calloc called realloc.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>