In some projects, I defined free()/malloc() directly.
There is another way, using __wrap_malloc and __wrap_free, see this post.
I don’t think that [vsn]printf functions use the heap, but I have noticed that some implementations are very stack-hungry. I would never use snprintf() in an ISR context.
I personally like to use printf-stdarg.c
That implementation is ISR-safe, and it has a small use of the stack. However, it does not implement floating point notations.