Sanitize with gcc

January 11, 2017 · 2 minutes to read · C++

I just watched a very interesting conference on how a race detector works. I learned a lot of new things such happened-before relation and vector clock algorithm. But I am not going to talk about these concepts now. The purpose of this article is to discover how to detect a race in a C++ executable (or any other common mistakes).

The race

Let’s start with a race between two threads:

 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 <thread>

using namespace std;

int main()
{
    int inc = 0;
    thread t1([&inc](){
        ++inc;
    });
    thread t2([&inc](){
    	--inc;
    });

    t1.join();
    t2.join();

    cout << inc << endl;

    return 0;
}

I deliberately did not put a mutex and made a single increment. You will have to compile using the following arguments:

g++ -o main main.cpp -lpthread -g -O1 -fsanitize=thread -no-pie

Be careful, with last versions of gcc you must add argument -no-pie.

The out-of-bound access

Compiler can detect an array access at a wrong position, for example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <iostream>

using namespace std;

int main()
{
    int t[] = { 1, 3, 7 };
    cout << t[2] << endl;
    cout << t[4] << endl;

    return 0;
}

Compile with:

g++ -o main main.cpp -lpthread -g -O1 -fsanitize=undefined -no-pie

Lots of more options

With the fsanitize argument, you can detect other types of bugs, look at documentation of your compiler (personnaly, I recommand Clang documentation).

The next question is when do I have to enable these options? First of all: not in a production environment. But during your fonctionnal tests, is a good practice to make a global pass.