Thursday, 15 November 2018

Intro to CppCheck

Cppcheck is an open-source static analysis tool for C++. In this post I'm going to describe how to install and run Cppcheck over your C++ code base.

Static Analysis

Static analysis is the analysis of code without executing it. It can be used to find common programming errors and enforce coding guidelines. Some common benefits of static analysis include:

  • Prevent unexpected / undefined behaviour.
  • Find security vulnerabilities and make code more secure.
  • Make code more maintainable by enforcing coding standards.

The very first static analysis tool that programmers use is their compiler. In my previous blog posts, I've discussed how using multiple compilers can help catch errors and prevent undefined behaviour. Using multiple compilers is a great first step in detecting issues, however, dedicated static analysis tools can help to prevent more errors as they can look at more detailed and complex code paths to analyse issues.

Cppcheck

Cppcheck is a standalone static analysis tool that can perform the following checks:

  • Undefined Behaviour
    • Dead pointers
    • Integer Overflow
    • Null pointer dereferences
    • Out of bounds checking
  • Security checking including some common CVE errors
  • Coding Standards

It is integrated into may common CI systems, IDEs and programming environments to allow it to be easily run over different code bases.

Installing

On Linux, most common package managers include a version of Cppcheck, however, this version is often older and out of date. As a result I recommended to install the latest version which can be found from the Cppcheck website.

For windows a binary installer is available.

For other platforms, it is easy to build the latest version from source. The only requirements are CMake and a C++11 compatible compiler. To build and install v1.85 (the latest at time or writing), you can run:

1
2
3
4
5
6
7
8
$ wget https://github.com/danmar/cppcheck/archive/1.85.tar.gz
$ tar xvf 1.85.tar.gz
$ cd cppcheck-1.85
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install

You should then have cppcheck available in your path:

1
2
$ cppcheck --version
Cppcheck 1.85

Basic Usage

Single File

To run Cppcheck against a single cpp file you can run cppcheck filename.cpp. For a simple example, take the following file.

1
2
3
4
5
6
7
8
#include <array>
 
int main() {
  const int ex_sz = 5;
  std::array<int, ex_sz> ex;
  ex[ex_sz] = 1;
  return 1;
}

Under GCC and Clang this compiles without any warnings using -Wall. However, with Cppcheck I see:

1
2
3
$ cppcheck test.cpp
Checking test.cpp ...
[test.cpp:6]: (error) Array 'ex[5]' accessed at index 5, which is out of bounds.

This simple tests shows us how using Cppcheck as a static analysis tool can save us from an out of bounds error.

Header Files

If you have header files, you can use the -I flag to tell cppcheck where to search for them.

1
cppcheck -I inc/ with_header.cpp

Recursive Checks

To recursively check all cpp files in a folder you can pass the folder name to Cppcheck.

1
cppcheck -I inc/ src/

Enabling Checks

As mentioned, Cppcheck has a number of different checks available. You can enable or disable checks by using the --enable flag.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
--enable=<id>        Enable additional checks. The available ids are:
                      * all
                              Enable all checks. It is recommended to only
                              use --enable=all when the whole program is
                              scanned, because this enables unusedFunction.
                      * warning
                              Enable warning messages
                      * style
                              Enable all coding style checks. All messages
                              with the severities 'style', 'performance' and
                              'portability' are enabled.
                      * performance
                              Enable performance messages
                      * portability
                              Enable portability messages
                      * information
                              Enable information messages
                      * unusedFunction
                              Check for unused functions. It is recommend
                              to only enable this when the whole program is
                              scanned.
                      * missingInclude
                              Warn if there are missing includes. For
                              detailed information, use '--check-config'.
                     Several ids can be given if you separate them with
                     commas. See also --std

Usage for an existing project

As you can see from the above you have to pass details about your source code to Cppcheck in order for it to understand what files to analyse, how to find dependencies, and what compiler flags to use. The easiest way to do this is to use a compilation database.

If you are using CMake you can create a compilation database using the -DCMAKE_EXPORT_COMPILE_COMMANDS=ON flag, or by adding the following to your root CMakeLists.txt.

1
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

After running CMake with this enabled a file compile_commands.json will be created. This will look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[
  {
    "directory": "/home/user/development/project",
    "command": "/usr/bin/c++ -Iinc -std=c+11 ... -c ../foo/foo.cc",
    "file": "../foo/foo.cc"
  },
 
  ...
 
  {
    "directory": "/home/user/development/project",
    "command": "/usr/bin/c++ -Iinc -std=c+11 ... -c ../foo/bar.cc",
    "file": "../foo/bar.cc"
  }
]

Once you have the compilation database available, you can then run Cppcheck as:

1
cppcheck --enable=all --project=compile_commands.json

Conclusions

This post showed how to install and run Cppcheck over your project. It also showed a simple example of how using static analysis can help you to detect errors. In future posts, I hope to show more advanced features of Cppcheck and also some common errors that it can help detect.

No comments:

Post a Comment