Need help getting started with Interprocedural PathSensitive Analysis

Hi:

I’m getting started with clang static analyzer.
The problem I’m trying to solve is basically, for the example below:

void Test32(uint32_t a, uint32_t b) {
    uint32_t res = a << b;
    log32(res);
    res = a >> b;
    log32(res);
}
void Test64(uint64_t a, uint64_t b) {
    uint64_t res = a << b;
    log64(res);
    res = a >> b;
    log64(res);
}
int main(int argc, char**argv){
    int8_t int8_a = 13,int8_b = 2;
    Test64(int8_a,int8_b);
    Test32(int8_a,int8_b);
    log64(0x1111);
}

Which log64() and log32() is my sort of “Intrinsic”-like functions.

What I’m trying to do currently is to list my intrinsic functions in the order they are called, in this example the expected output would be:

    log64(res);
    log64(res);
    log32(res);
    log32(res);
   log64(0x1111);

First step is hopefully to use checkPreCall(const CallEvent &msg, CheckerContext &C) callback and maintain a list of states for each node.

Now the question is: How do I retreive the states?

My gut instinct would be checkEndAnalysis. however it’s fired multiple times and I’m not sure how filter out the path and starts and ends in main().

I’ve tried iterating eops of the ExploredGraph and walk up the call stack to see if the top of the frame is coming from main, however for some my test cases, eop is empty

Well, CSA creates an exploded graph.
Imagine an unbounded loop:

while (coin()) {
  res = log32(res);
}

What would you expect for this input program?
There are infinite different outputs since we cannot know how many times the loop will iterate. (In practice we will give up at some point to spend the analysis time on some other part of your code, but you get the point.

Hi:
Thanks for the info.
I’m aware of this, and is more looking for something like:

  • if log32() is called in a path that can’t be reduced to static-known loop execute times, as shown in your example, report as an error

I’m not exactly sure how is it useful for you but here is how you can maintain a list and dump it at the end of each top-level function analysis.

Note that you should create your own checker for this purpose, but I was lazy and used the CStringChecker for this prototype.
Here is the git diff you can apply on top of 6bf8133f9f34a17d4e7378af05e7eb71ea2c21a6.

prototype.patch (2.8 KB)

After compiling clang, you can execute this and have the expected result:

$ ./build/release/bin/clang --analyze -Xanalyzer -analyzer-checker=alpha.unix.cstring test.c
Call chain dump:
  log64(res)
  log64(res)
  log32(res)
  log32(res)
  log64(4369)
$ cat test.c
typedef signed char int8_t;
typedef unsigned uint32_t;
typedef unsigned long long uint64_t;

void log32(uint32_t);
void log64(uint64_t);

void Test32(uint32_t a, uint32_t b) {
  uint32_t res = a << b;
  log32(res);
  res = a >> b;
  log32(res);
}
void Test64(uint64_t a, uint64_t b) {
  uint64_t res = a << b;
  log64(res);
  res = a >> b;
  log64(res);
}
int main(int argc, char **argv) {
  int8_t int8_a = 13, int8_b = 2;
  Test64(int8_a, int8_b);
  Test32(int8_a, int8_b);
  log64(0x1111);
}