Welcome to the first in a series of posts looking at examples of different types of bugs that can easily find their way into your C/C++ codebase and how Codesonar, our static analysis tool, can detect and present the issues to you for review. I’ll be picking flaws as named by the Common Weakness Enumeration Specification (CWE), a scheme created to formalise the language used in naming general categories of software security vulnerabilities, each potentially subdivided into individual unique variations.
In this post, I will pick one of the most difficult to address, Race Conditions. What makes instances of this problem so hard to deal with is the difficulty of reproducing the bug consistently and the complexity of multiple concurrent paths of code execution that need to be considered by the person tasked with fixing the issue.
Briefly, what is a Race Condition? Consider this piece of code:
void *CreditBalance(void *);
void *DebitBalance(void *);
int balance = 0;
int rc1, rc2;
pthread_t thread1, thread2;
int amount1 = 100, amount2 = 50;
/* Create independent threads each of which will execute functionC */
if( (rc1=pthread_create( &thread1, NULL, &CreditBalance, (void*)&amount1)) )
printf("Thread creation failed: %d\n", rc1);
if( (rc2=pthread_create( &thread2, NULL, &DebitBalance, (void*)&amount2)) )
printf("Thread creation failed: %d\n", rc2);
/* Wait till threads are complete before main continues. Unless we */
/* wait we run the risk of executing an exit which will terminate */
/* the process and all threads before the threads have completed. */
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
printf("Closing Balance: %d\n",balance);
void *CreditBalance(void *sum)
int *s = (int*)sum;
balance += *s;
// add bouns
balance += (balance / 100);
printf("After Credit, Balance: %d\n",balance);
void *DebitBalance(void *sum)
int *s = (int*)sum;
if (balance > *s)
balance -= *s;
printf("After Debit, Balance: %d\n",balance);
This is a contrived version of the classic credit/debit bank balance transaction code that contains a race condition. Compiling and running the above give us different outcomes depending on the order of interleaving of the statements in the CreditBalance and DebitBalance threads.
It’s possible for CreditBalance to partially complete and update balance to be £100 before the bonus is added, but the DebitBalance thread could become active and subtract £50 from the balance, upon which CreditBalance continues and now calculates the bonus on the balance as £50+ (£50 / 100) = £50.50. Other varied outcomes are possible! This happens because of thread scheduling decisions beyond our programs control. It may be noted as well that in the majority of cases where the above code is run on Linux, the behaviour is repeatable, resulting in the expected balance, highlighting one of the reasons why race conditions are so difficult to detect.
So what does Codesonar make of this code? Firstly, it is pretty straightforward to analyse. Codesonar works by observing native compilations of code, so in this rather simple case, we can look for problems with this command:
codesonar analyze RACECONDITION -preset concurrency gcc pthread.c -o pthread -lpthread
This will run Codesonar which will then run as a child process the build of the code, which could be anything, including makefiles, scripts, and so on, but in this case, is a direct call to gcc. As Codesonar detects the native build issue compilations, Codesonar reacts by invoking its own compiler to process the same source files, ready for eventual analysis. By default, Codesonar runs an out of the box set of checkers, looking for classic problems like null pointer dereferences, resource leaks, buffer overruns, uninitialised variables and many more. However, by default the analysis does not look for concurrency issues, hence the “-preset concurrency” arguments to enable the family of concurrency checkers.
Once complete, Codesonar reports several problems in this example:
Clicking on the first Data Race presents us with this view:
As it is easily appreciated from the annotations on the pair of side by side thread contexts, Codesonar has identified that the global variable balance has been accessed for reading and writing in a potentially dangerous way, as there is no synchronisation protecting access to it by either thread. So, whilst this example may appear easy, I’m sure if you’ve written threaded code before, you’ve been caught out by the ease with which its easy to overlook adding suitable synchronisation and then suffered the consequences of unpredictable program behaviour, which has needed some dedicated puzzling overtime for the penny to drop what has happened – never mind the calamity of when these things get out to the customers.
With Codesonar, it took less than 30 seconds to have the precise and clear results ready (albeit a small piece of code in this case). Of course, if we were to have added synchronisation, Codesonar would have been sensitive to that and assuming it was done correctly (i.e, no deadlocks!), the code would have had a clean bill of health.
If you would like to try Codesonar on your source code please register for a free trial and we will get in touch.