I think the only way this design could deadlock is if a signal handler calls malloc() -- specifically, if the interrupted code on that CPU thread was partway through a malloc() call of its own, having locked the mutex but not yet unlocked it.
In any other scenario, if some execution thread reaches the point where it returns from calling lock() on the mutex guarding malloc(), it must eventually reach the call to unlock().
In any other scenario, if some execution thread reaches the point where it returns from calling lock() on the mutex guarding malloc(), it must eventually reach the call to unlock().