Comment by jeroenhd
If you're writing any other language to comply with old C libraries, calling malloc()/free() is usually fine. Most C software uses malloc() on Linux, at least for the external API surface.
That only works when calling into C code, and even then it assumes the C code implements allocation using malloc()/free(), with proxies if alternative memory allocators are used.
This behaviour can't be relied upon, but for many programs glueing themselves together with C libraries that's not stopping anyone. This is why I'm not a big fan of sharing pointers between languages (or even libraries, to be honest); I'd much prefer intermediate identifiers (file descriptors, Windows HANDLEs) to track instances that pass in and out of a library, although sometimes those cause too much overhead.
I've long had the strong belief that allocation and deallocation should be done by the same party.
Either the caller is responsible and the function just uses the provided buffers/structures, or there is a clear convention that the caller has to call a cleanup function on the returned buffers/structures.
Mixing allocation and deallocation responsibilities between caller and callee almost always leads to pain and sorrow, in my experience.
Of course, if you're using a shared library or similar which can be written in something else entirely, mixing is a complete no-go as GP mentions.