Writing simple checkers for the static analyzer

Hello,

I am trying to write a very simple checker for the clang static analyzer for the sake of writing a first exercise on this topic. Its goal is to simply alert whether a specific function has been called twice in a given path. Let’s assume the name of this specific function that I am tracking is “doNotCallTwice()”.

In order to record state information, I use the REGISTER_TRAIT_WITH_PROGRAMSTATE macro to register an unsigned together with the program state. This integer indicates whether the function “doNotCallTwice()” has been called in a path and, if it is equal to 1 in a node where I detect yet another call, I prepare to report a “double call” bug. I use “checkPostCall” for changing the state.

However, something strange happens. My extra integer registered in the program state is not sufficient to differentiate two ProgramStates with the same ProgramPoint: the engine fold the two nodes anyway, ignoring my new state information. On the other hand, the information is propagated. If I use other ways to avoid the nodes being folded, the checker works fine.

An example where it does not work:

void myfunc (int x, int y) {
if (x)
doNotCallTwice();
if (y)
doNotCallTwice();
doNotCallTwice();
}

Since programstates get folded in the ExplodedGraph, I never detect any path where two calls to doNotCallTwice() happen. However, change the code in the following way avoids the folding and make my checker work:

void myfunc (int x, int y) {
if (x)
doNotCallTwice();
if (y)
doNotCallTwice();
y = x; // Now x and y are not dead anymore and this won’t be folded
doNotCallTwice();
}

I based my checker on SimpleStreamChecker.cpp. Am I doing something conceptually wrong?

Best regards,
Rafael

Hi, Rafael. From your description, this sounds like a bug in the analyzer—two program states with differing user data should not be folded. Can you attach your checker so I can take a look and see if there are any obvious mistakes? (on your part or ours).

Thanks,
Jordan

Hi Jordan,

Sure, it is attached. Thanks for taking a look at this.

Cheers,
Rafael

MyChecker.cpp (3.76 KB)

mytest.c (159 Bytes)

Hm, if I drop this into my clang sources (and update it to match changes in trunk), I don’t see any issues with what you’ve written—building and running it on your sample input works fine. Are you sure you have it enabled? (I forgot to pass -analyzer-checker on my first test, so I have to ask.)

Jordan

Hi Jordan,

I’m using the tagged version 3.4, do you think it is an issue that got fixed in trunk? I am quite sure that I have it enabled, since I put some debugging printfs and saw the results:

-1 = doNotCallTwice() has not been called before

0 = doNotCallTwice() has been called before

rafael$ clang -cc1 -analyze -analyzer-checker=alpha.mychecker.MyChecker mytest.c
doNotCallTwice! -1
doNotCallTwice! -1
doNotCallTwice! -1
rafael$ vim mytest.c # change to avoid folding
rafael$ clang -cc1 -analyze -analyzer-checker=alpha.mychecker.MyChecker mytest.c
doNotCallTwice! -1
doNotCallTwice! -1
doNotCallTwice! -1
doNotCallTwice! 0
mytest.c:8:5: warning: Called twice
doNotCallTwice();
^~~~~~~~~~~~~~~~
1 warning generated.

In the first case, I use only function calls in the test case. In the second, I put the extra statement that forces the engine to avoid folding, and the detection finally works.

Thanks,
Rafael

I checked out the trunk version and updated my source code in two places:

  • include the OwningPtr header, which was previously unnecessary
  • changed to instantiate the BugType using the new constructor interface, passing a pointer to the checker instance in the first argument

Afterwards I tested again and got the same output. I will continue testing here to figure out why this is happening only to me.

Thanks for helping!

Rafael

It’s worth noting that running without the core checkers enabled isn’t really a sane state for the analyzer. You should either pass “core,alpha.mychecker.MyChecker” to -analyzer-checker, or use “clang --analyze” instead of “clang -cc1 -analyze” and use “-Xanalyzer” to pass down the -analyzer-checker option.

Jordan

Hi Jordan,

Thanks! Running it with the clang driver made it work. Regarding the clang frontend, I couldn’t make it work by using “-analyzer-checker=core,alpha.mychecker”, but “-analyzer-checker=core,osx,alpha.mychecker” did work!

Best regards,
Rafael

So, several weeks later…I poked further into this and found that it was actually a bug in the analyzer, fixed in r211209. So, thanks for letting us know!

Jordan