Its depends on what is your sofware for and what part of your code is affected.
First to know, malloc() can fail when no pages available, if your application reached it's limit then will not work any loop, but if your system ran out of memory it worth a try, but you must avoid any infinte loops. Surprise! Perfectly normal if the operating system temporarily response that could not allocate more RAM, you can handle at your way.
This is a very good question anyway
Similar problem is not capturing SIGNALS and when a multi-threaded or async TCP server got an aborted client connection the software terminated by SIGPIPE. This is normal, but leads to end your program however its should not. To prevent this, you have to hook for signals.
In realworld examples (my own).
When malloc fails and only affect partially of my code
I used to malloc() or new[] when new connection sending data, Im storing received data into buffers, if malloc or realloc fails, the function return as false and free the buffer, the connection dropped with error (lingerie) so in this case the software continue to run but one connection dropped. I think this is a right way here.
When malloc must cause software abort
I used to malloc() to make space for critical data, like arrays, structures that defines the core, this is usually run at the begining of the software as init section, if there is malloc() fail the software must abort and exit with error code, because all operation depending on the tables that must filled with data. (Built-In-Filesystem)
When malloc able to retry
I had my datalogger software which is some industry-leading type (High-Availability), if malloc() fails I trigger a mutex_lock() which cause software freeze in backend side and trying to retry the malloc procedure for X seconds. If malloc() continues to fail the software starting to call destructors on all threads and performs a complete shutdown except that thread which is tried to malloc() unsuccessful at this point there are two option, malloc() succeed and finish the stack call and exit the last thread or doing a rollback and exit the last thread.
Whenever is happening the software does not quit as well, try to start from begining and so on..
Maybe worth to mention ..
I had exactly the same dilemma years ago, something caused a memory leak in my software and ate all of my RAM but to save the current state I had to write it out which done after many malloc(), the solution was when this happened I closed all threads and calls destructors and saved the data, however the interesting thing was when I closed all connections and free'd up socket, ssl_ctx, ... memory consumption dropped to 128KB, after days of happy debugging I figured it out that SSL_CTX has internal storage and cache. So now when no connection online I free up SSL_CTX and works like a charm.
SUMM
So as you can see this is an art, you do what you want with malloc() and anything its just up to you, there is no book and no standard what you should do if malloc() fails. If someone tells you what you should do its just his opinion nothing more.
my preferred way to avoid infinte loops
PSEUDO CODE:
var ts = TIME()
var max_seconds = 5
var success = true
WHILE (MALLOC() == FAIL) DO
IF TS + max_seconds < TIME() THEN
success = false
BREAK
END
SLEEP(100ms)
END