jeudi 8 novembre 2012

When compiler act as Static Analyzer

Today i goes through a strange compiler behavior while compiling a source code with GCC/G++. You may or not be aware of the importance of Security and Sanity checking on the code you write, but it's a fact that in the last decade the "Static Code Analysis" come from space, fly, nuclear domain and is now use in game and all professional development.

For a good introduction to "Static Analysis" take a look at http://www.altdevblogaday.com/2011/12/24/static-code-analysis/ where John Carmack present a review of several existing tools.

But here I try to explain why sometimes a good compiler like GCC "can" act as a static code checker. To demonstrate and explain it, lets talk about the code below.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>

//inline int func(int tab[], bool toSwitch) //force inline.
//__attribute__ ((noinline)) int func(int tab[], bool toSwitch)  //prevent inline.
int func(int tab[], int idx) //let compiler inline based on its own logic and optimization parameters.
{
  return tab[idx];
}

int main(int argc, char* argv[])
{
  std::cout << argc << " " << std::string(argv[0]) << std::endl;
  int myTab[4] = {1,2,3,4};
  //int myTab[10] = {1,2,3,4,5,6,7,8,9,10};
  std::cout << func(myTab, 0) << std::endl;
  std::cout << func(myTab, 8) << std::endl;

  //At least the line is detected by VS'12 Static Analyzer....
  std::cout << myTab[8] << std::endl;
  return 0;
}

I mainly code using Visual Studio IDE (08,10 at work and 12 at home for now...) but hopefully i also use GCC and we will see why just below. As you can see, i coded 2 buffer overrun by calling "func(myTab, 8)" and "myTab[8]".
  • Visual Studio 2012 
From VS compiler point of view that code is perfect, no error, no warning. But at least the line 20 is detected by VS'12 Static Analyzer


//At least the line is detected by VS'12 Static Analyzer....
std::cout << myTab[8] << std::endl;

stl_usage.cpp(22): warning : C6201: Index '8' is out of valid index range '0' to '3' for possibly stack allocated buffer 'myTab'.
stl_usage.cpp(22): warning : C6385: Reading invalid data from 'myTab': the readable size is '16' bytes, but '36' bytes may be read.

  • GCC
when i compile that source code with the following line:
g++ -Wall -Wextra -Werror -O3 ./gcc-static-analys.cpp
I got the following error:
./gcc-static-analys.cpp: In function âint main(int, char**)â:
./gcc-static-analys.cpp:17: error: âmyTab[8]â is used uninitialized in this function [-Werror=uninitialized]
./gcc-static-analys.cpp:20: error: âmyTab[8]â is used uninitialized in this function [-Werror=uninitialized]
cc1plus: all warnings being treated as errors

But why GCC can detect these 2 buffer overrun at compile-time. For the direct and invalid access to myTab[8] it should be easy but for "func(8)", it's a bit tricky. In fact if you had a look at the 2 comment line 4 and 5, you may have guess that it's mainly come from the "inlining" of "func".

Depending of the optimization level you used GCC can decide to inline some function by itself and in fact case he can detect some error and act as a Stactic Analyzer  preventing use to let something wrong (a security issue) in our code.