I am working on a small library where I don't want to deal with allocation errors everywhere they might pop up, so I've written a wrapper around malloc() that terminates the program (for now) if an error occurs.
void *cstr_malloc(size_t size)
{
void *buf = malloc(size);
if (!buf)
{
fprintf(stderr, "Allocation error, terminating\n");
exit(2);
}
return buf;
}
Nothing fancy there. Now, however, I noticed that the clang static analyzer, in Xcode at least, doesn't catch obvious leaks that it would have done before.
If I do something like this:
void foo(void)
{
void *p = malloc(100);
}
it would naturally inform me that p would likely leak memory.
However, with
void foo(void)
{
void *p = malloc(100);
void *q = cstr_malloc(100);
}
it only reports that p, but not q, is leaking.
That makes sense; it can't know everywhere the program might allocate memory and still analyse it in a reasonable time. It handles my allocator fine if I inline it, so there it can see it, but otherwise, it doesn't.
Is there any way to tell the analyser that I have a function that returns freshly allocated memory? Some attribute, like __attribute__((malloc)) or similar?
I can, of course, just inline the function, but I have a bunch of other functions that similarly allocate memory, and it would be problematic to inline all of them.
After some hours of googling, I found what at least looks like a solution. The trick is indeed to use an attribute, but not __attribute__((malloc)).
The attribute I needed was ownership_returns(malloc). Using that, I get warnings, at least for this function.
__attribute__((ownership_returns(malloc)))
void *cstr_malloc(size_t size);
See https://godbolt.org/z/MEfvThKnW
Unfortunately, my joy was short-lived. If I use ownership_returns(malloc), then the analyser will assume that the memory is also uninitialised, which it isn't always. I have no idea how to hint that I return memory that must be freed, but not uninitialised memory.
Since clang can figure out that this is the case for calloc(), there might be a way to do it for my functions as well, but I haven't found it.
Here is an example of how clang deals with memory returned from calloc() and what it thinks of what comes from my own function: https://godbolt.org/z/P45o691Tx