[cfe-dev] Ownership attribute for malloc etc. checking

Jordy Rose jediknil at belkadan.com
Thu Jul 1 19:41:06 PDT 2010


On Fri, 2 Jul 2010 12:56:30 +1200, Andrew McGregor <andrewmcgr at gmail.com>
wrote:
> On Tue, Jun 29, 2010 at 1:03 PM, Jordy Rose <jediknil at belkadan.com>
wrote:
> 
>>
>> >> Why does that matter? Because an ownership_takes() or
>> >> ownership_holds()
>> >> function might have a return value! If it's also
ownership_returns(),
>> >> then
>> >> you're fine, but otherwise you need to set a symbolic return value.
>> (You
>> >> can see how this is done in MallocMemAux(), or in StreamChecker.cpp
>> with
>> >> fopen.) There should probably be a test for this as well.
>> >
>> >
>> > It isn't necessarily the case that an ownership_takes() function that
>> > returns a pointer generated that pointer from an ownership_takes
>> argument,
>> > or allocated anything, so what would you set the region to, in the
>> absence
>> > of other information?  They default to Unknown, yes?
>>
>> I believe so, but for return values from function calls we prefer to
use
>> a
>> "conjured symbol value". This is a kind of value that can be reasoned
>> about
>> symbolically in later statements -- for example, we can bound its value
>> if
>> it's an integer, or mark that it's null or non-null if it's a pointer.
> 
> 
> So, I'm still not sure I understand what you mean me to do here... could
> you
> elaborate?
> 
> Since you seem to be the symbolic value expert, I'll ask about the case
I'm
> trying to solve now.  I added some symbolic checks to the free handling,
> similar to realloc, to prevent warnings in the common case of error
> handling
> for allocation handlers.

Sorry, I should have been clearer! If you take a look at MallocMemAux,
you'll see these lines:

  unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
  SVal RetVal = ValMgr.getConjuredSymbolVal(NULL, CE, CE->getType(),
Count);
  state = state->BindExpr(CE, RetVal);

This sort of code is used for any function call that returns a value. It's
better than just leaving the expression as UnknownVal because it lets us
track assumptions about the value from then on.

Consider this code:

int freeNode (Node *n) __attribute__(ownership_takes(1)) {
  int id = n->id;
  free(n);
  return id;
}

// in some other function
int x = freeNode(last);
if (x < 0) {
  log("invalid node: %d", x);
  return;
}
// from here on x is known to be positive

So all you would need to add is those three lines, for the case in which
you /are/ handling the function (returning true from EvalCallExpr), /not/
calling MallocMemAux (which is already handling the return value), and the
function's return type isn't void.

Alternately, go ahead and make the ownership_takes/ownership_holds cases
part of a new PreVisitCallExpr method. PreVisitCallExpr doesn't require you
to handle the return value, and allows you to stack multiple checks. This
is probably more correct anyway -- it would let users annotate methods that
may eventually need another checker to actually do the evaluation.

As for the example you showed, it should work, except:

> struct it *  __attribute((ownership_returns(malloc))) foo(void) {
>   struct it *rv = malloc(sizeof(struct it));
>   if (!rv)
>     return NULL; // Does not warn here.
>   char *textString = malloc(128*sizeof(char));
>   if(textString == NULL)
>     free(rv);
>     return NULL; // Warns about a memory leak here
>   rv->s = textString;
>   return rv; // Does NOT warn here
> }

...the code is just missing braces around the second if -- the second
"return NULL" is unconditional!

Clang should catch this. Filing a bug. *grin*



More information about the cfe-dev mailing list