[cfe-dev] More static analysis...

Ted Kremenek kremenek at apple.com
Thu Feb 26 22:02:13 PST 2009


On Feb 26, 2009, at 2:18 AM, Ben Laurie wrote:

> On Wed, Feb 25, 2009 at 5:27 PM, Ted Kremenek <kremenek at apple.com>  
> wrote:
>>
>> On Feb 25, 2009, at 8:56 AM, Ben Laurie wrote:
>>
>>> I'm interested in looking at detecting "known bad" patterns, for  
>>> example:
>>>
>>> (<expr> & 0) == 0 (this example is stolen from FindBugs)

Quick question.  Did you literally want to look for this syntax in the  
AST (i.e., the integer literal '0') or did you want to use the  
dataflow analysis to see cases when an expression evaluates to '0' and  
then is bitwise-ANDed with <expr>?  Is the motivation of this check is  
that it is trivially true?

>>> if(<non-boolean value>) (cause of recent OpenSSL vuln)

This seems a little too vague to me.  People check non-boolean values  
all the time in 'if' conditions.  Were you thinking about cases when  
<non boolean value> was negative?  Positive numbers seem completely  
legit to me (e.g., some flag was set in a bitmask, etc.).

In either case, both of these seem like "auditor" checks.  For the  
first check if all you wanted to do is grep the syntax, we can easily  
add another check that gets called by AnalysisConsumer.  Check out the  
functions "ActionXXX" in AnalysisConsumer.cpp to see all the places  
where the various checks are called.  An ActionXXX can get called for  
each function, an entire Objective-C class, or the entire translation  
unit.  For example:

static void ActionWarnDeadStores(AnalysisManager& mgr) {
   if (LiveVariables* L = mgr.getLiveVariables()) {
     BugReporter BR(mgr);
     CheckDeadStores(*L, BR);
   }
}

This action gets called for each function/method declaration.  The  
action queries the AnalysisManager (the thing that runs the checks)  
for the live variables analysis result on the current function (which  
gets computed on-the-fly).  The function 'CheckDeadStores' is then  
called with (a) the results of the live variables analysis and (b) and  
BugReporter object which is used to issue bug diagnostics to the  
user.  Analyses get registered in the Analyses.def file, where the  
"scope" of the analysis is declaratively specified.  For example:

ANALYSIS(WarnDeadStores, "warn-dead-stores",
          "Warn about stores to dead variables", Code)

Here "Code" means "code declaration"; that is a FunctionDecl or an  
ObjCMethodDecl.

A syntactic check can just walk the AST.  A good example of a  
syntactic check is CheckObjCDealloc.cpp.  Although this check will one  
day be revamped to do more real analysis, you can see how it walks the  
AST using iterators and does various pattern matching.  Syntactic  
checks are fairly easy to implement in this way and hook up to  
AnalysisConsumer.  The down-side is that they can be fairly dumb if  
the property you are checking requires any real smarts.

Another possible way to implement an "auditor" is to use the GRAuditor  
interface defined in GRAuditor.h:

template <typename STATE>
class GRAuditor {
public:
   typedef ExplodedNode<STATE>       NodeTy;
   typedef typename STATE::ManagerTy ManagerTy;

   virtual ~GRAuditor() {}
   virtual bool Audit(NodeTy* N, ManagerTy& M) = 0;
};

Checks that subclass GRAuditor<const GRState*> can be registered with  
a GRExprEngine and have their "Audit" method called after the path  
engine evaluates all expressions of a given type.  The Auditor is then  
free to inspect the analysis state at that point and flag errors.  A  
good example of this kind of check is in  
BasicObjCFoundationChecks.cpp.  The class in question is  
AuditCFNumberCreate, which flags misuses of the function  
CFNumberCreate (http://developer.apple.com/documentation/CoreFOundation/Reference/CFNumberRef/Reference/reference.html 
).  That function just interposes on the path engine at every CallExpr  
and checks the arguments in the function call for illegal values.

This is probably enough for one email.  I'm more than happy to dig  
into more details depending on what direction you decide to go in.   
Also, these interfaces are obviously evolving.  We haven't written  
many checks, so any idea of how to refactor the interface, make it  
better, etc., are welcome.




More information about the cfe-dev mailing list