Is there a way to tell the clang analyser that a function returns malloc()'ed memory?

Viewed 97

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.

1 Answers

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

Related