No, the reason is simply that by statically allocating all memory you can avoid entire classes of program faults and bugs. There are no memory leaks and you don't need to solve the NP-complete problem of "is there an execution path where a dynamic memory allocation will fail". Keep in mind that it is not just about the total amount of dynamically allocated memory, but also the order of allocations (and frees).
If you still need dynamic allocation, you might choose to have a custom allocator working from a fixed-size pool or arena created in code (possibly itself carved out of RAM with malloc(), but importantly only once at first setup, where you have a better guarantee that the allocation will succeed).
> working from a fixed-size pool or arena created in code (possibly itself carved out of RAM with malloc(), but importantly only once at first setup, where you have a better guarantee that the allocation will succeed)
And I should add to this that you probably want to access all of the pool/arena to do setup, or just ensure it's physically allocated if you are running in a virtual memory space. This is something that is reasonable at setup time, though.
I’m working on a project now and trying my best to make it MISRA compatible because FreeRTOS did the same. You don’t use malloc() but rather pvPortMalloc() which pulls from an already allocated pool. Their HEAP4 system tries to keep the chunks organized. So far I’ve been very happy with it.
I would expect (but verify) a malloc implementation on embedded to return null if it can't satisfy the allocation.
But even with that assumption malloc in embedded is often a bad idea. You need to plan for worst case anyway and you can not afford memory fragmentation.
This video springs to mind as one example: https://www.youtube.com/watch?v=tfAnxaWiSeE