I would do everything possible to avoid having a heap. I copy my string and conversion functions from "The C Programming Language" book (Kernighan and Ritchie) rather than use those from the C library. I don't use printf() for the same reason. I can usually get by with some form of puts() call.
I always look at the linker output and hope to not find _sbrk(). I won't provide one and I hope the environment doesn't provide one for me by default. If the library doesn't include _sbrk() then it may be in syscalls.c provided by the IDE (eg STM32Cube)
Unless I missed your point, looks like you are directly associating dynamic memory allocation with the standard "heap" and the standard allocator.
I think I was clear in that I wouldn't consider using that. So, it would be a fully custom allocator (or set of allocators) having nothing to do with anything standard.
(That said, you're right saying that some C std functions do themselves call malloc() and thus should be avoided in that case.)
I have no idea how you prove correctness when using dynamic allocation and an increasing call stack.
You may want to elaborate on this a bit. Otherwise, as it is, all I understand is that you're concerned with how the stack and heap could grow into each other, something I think we already discussed when talking about stacks.
Avoiding the "heap" to grow into the stack, even using the std malloc(), is easy. You just need to write _sbrk() appropriately so allocation can't go further than a predefined end of the heap. That means you need to "reserve" space for the stack, even if you don't use it all. That's the only way of making things safe IMHO.
Avoiding the stack to overflow into the heap, OTOH, is a more severe problem, which we also talked about in threads about stacks. Various ways to ensure that, some requiring a MMU if you have one, some requiring more "manual" checking inside your code, which always has some added cost.
But even so, that's still thinking about it in very very standard terms, which I was not here. In the very basic standard memory layout, especially on small targets, there's only one "heap" - all memory between the end of the statically allocated memory and the end of the stack, and then the stack, growing downwards.
Of course, a completely different memory layout could be used here.