[cfe-dev] Static Analyzer Rocks Hard

Ted Kremenek kremenek at apple.com
Thu Jun 19 14:54:51 PDT 2008


On Jun 19, 2008, at 1:03 PM, Matthew Jimenez wrote:

>>
>> In reality, this leak probably wouldn't happen.  If "sql" was  
>> assigned a NULL value because "alloc" failed, chances are that the  
>> allocation to "args" also failed because memory allocation failures  
>> are correlated.  Thus at the end of the basic block for the true  
>> branch of "if (!sql)" we really should have the following  
>> possibilities:
>>
>> (a) both sql and args are null  (allocation failed first for sql,  
>> then args)
>> (b) sql is not null, but args is null (allocation succeeded for  
>> sql, but failed for args)
>> (c) neither sql or args are null (allocation succeeded in both cases)
>>
>> If we assume that these are the only possibilities (and not a  
>> fourth possibility, where allocation failed for sql but succeeded  
>> for args), then there is no leak.
>>
>> Is this the false positive that you were thinking about?  If so,  
>> this extra reasoning about correlated allocation failures is  
>> something that could be implemented fairly easily in the static  
>> analyzer (and I would be happy to do it).
>>
>
> Yes, this was what I was thinking, however I am unsure how I feel  
> about it.
> Obviously any case where the allocation or initialization fail here  
> would cause the code to misbehave, and this is something I had not  
> taken into full consideration.
> Asserting the values after allocating them causes the analyzer to  
> not report any problems, but that might be overkill.

Assertions are funny things.  If you know that the allocation could  
never fail then the assertion properly silences the warning.  If the  
allocation *could* fail, it merely turns an error that could be caught  
with static analysis into a run-time error (i.e., the assertion fires,  
causing the program to halt).

> Perhaps what I'm more interested in are flags for the analyzer for  
> assumptions it may be allowed to make.
> Normal behavior assuming allocation is always working and a flag to  
> assume allocation could fail (or perhaps the reverse).
> Although, too many such flags might also be quite bad.

Most code is not engineered to handle the case when memory allocation  
fails.  When memory allocation starts to fail, programs tend to do  
weird things since they don't know how to recover.  For such programs,  
flagging leaks (or any other kind of bug) that occur when memory  
allocation fails are likely going to be deemed as false positives.

That said, there is code out there that does care when allocations  
fail.  Such code should be designed to handle and recover from  
failure.  For such programs, the most robust test of the code is to  
allow any arbitrary allocation to fail.  For such code, users can  
silence particular warnings that they are 100% certain wouldn't fail  
using assertions (which would result in run-time errors if they  
actually did occur).  Writing code, however, that assumes that any  
arbitrary allocation can fail is difficult at best.

In order to be pragmatic, the analyzer assumes that an allocation has  
succeeded *unless* you check for failure (i.e., a pointer is tested  
for null).  This is exactly what happens in the test case; since "sql"  
is tested against null on every iteration of the loop, the analyzer  
assumes that the allocation could fail.  I believe that your test  
case, however, intended the "if (!sql)" to really test to see if "sql"  
had not been assigned a new value after its initial declaration.  We  
could potentially engineer the analyzer to handle such cases.  For  
example, sql is initialized with the value 0, so we can prove that the  
branch would unconditionally be taken on the first branch of the loop,  
and thus we shouldn't treat that particular branch like it was testing  
for the success of the allocation.  Such heuristics go into the realm  
of black art, which is fine if they are the right thing to do in 99%  
of the cases, but in this case it may silence more real bugs than  
intended.

So, I really see two options.  We can add a flag indicating that  
allocations are assumed not to fail, or we just keep the current  
behavior.  Assuming the checker is run using the current behavior, it  
looks like it is doing exactly the right thing.  From my perspective  
it probably makes the most sense to add an assertion after the  
allocation to "sql" since that really is the assumption you are  
making.  In this way the code itself documents your assumptions.   
Alternatively, the test case could be restructured, etc., but an  
assertion seems like an easy answer and would make your code more  
documented.

What do you think?  We're trying to make the tool as useful as  
possible.  Different people will want different operating models for  
the tool, but another benefit of the tool is that it makes one think  
about their assumptions and how their code could be improved,  
especially since some assumptions are not always correct.

Ted



More information about the cfe-dev mailing list