*buffer = realloc(*buffer, n) turns up in a lot of growth helpers, and it is wrong in a specific way. If the allocation fails, the only owned pointer is overwritten with null. The C11 draft says the old object stays allocated and unchanged in that case. The helper has just lost the one route to storage it still has to release.
The helper I have in mind takes char **buffer and size_t *capacity from the caller. *buffer owns a valid allocation, and the requested size is strictly positive and larger than the current capacity.
Put the result in a temporary pointer first. On failure, return an allocation error and leave both output slots as they were. On success, write the returned pointer and the new capacity.
int grow(char **buffer, size_t *capacity, size_t n)
{
char *p = realloc(*buffer, n);
if (p == NULL)
return -1;
*buffer = p;
*capacity = n;
return 0;
}
Nothing here is clever, and that is the point. The order of the two writes does not matter for correctness, since neither can fail, but writing the pointer first keeps the slots consistent for anyone stepping through it.
Callers then get one simple promise. After a failed call the buffer is the same one they had, so they can report the error, free it, or retry with a more modest size, and nothing has to be repaired first.
One more rule belongs in the documentation. A successful realloc deallocates the old object, and by 6.2.4 every pointer a caller saved into it becomes indeterminate, even when the returned address happens to be the same. My draft returns plain success or failure and tells callers to rebuild their aliases after success. I think that covers it, but I am not fully sure the alias rule is clear enough as written.

