Unless I'm reading your example incorrectly, it's pretty easy to catch those if, in foo_destroy, you ensure that you don't try to free a null pointer.
> It's just a minimal example.
I know -- but it tends to be confusing if you free() something in another scope than the one where you're malloc()-ing it. That's why your example, even if minimal, seemed confusing.
The obvious exception is if you have some reference counting mechanism/GC mechanism in place -- but then the wise thing to do is to not manually touch pointers all over the code.