Responsible C++
Using Static and Dynamic Analysis for Responsible C++.
C based languages are incredible unsafe.
Microsoft found that 70 percent of all of their security bugs are memory safety issues.
This brings up the question of whether the use of binary/static analysis alongside fuzzing is a moral imperative if nothing else.
I have the opinion that all production code in these languages should be regularly fuzzed and put through binary/static analysis.
My reasoning lies in the fact that many bugs are not obvious especially in a language as complex as something like C++.
Code Correctness VS Compiler optimization
This code example is trivial but just meant to represent some form of memory leak that could be elided.
In this case a function allocates an int on the heap that is never freed therefore causing a memory leak.
You can see both trunk clang and gcc make a call to operator new in the assembly output without optimisations enabled.
After compiling with debug symbols enabled and running the outputted binary through valgrind we get the following output.
root@bd38132127e8:/my_code$ valgrind --leak-check=full ./a.out
==22== Memcheck, a memory error detector
==22== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==22== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==22== Command: ./a.out
==22==
==22==
==22== HEAP SUMMARY:
==22== in use at exit: 4 bytes in 1 blocks
==22== total heap usage: 2 allocs, 1 frees, 72,708 bytes allocated
==22==
==22== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==22== at 0x4835DEF: operator new(unsigned long) (vg_replace_malloc.c:334)
==22== by 0x401133: foo() (main.cpp:2)
==22== by 0x401143: main (main.cpp:6)
==22==
==22== LEAK SUMMARY:
==22== definitely lost: 4 bytes in 1 blocks
==22== indirectly lost: 0 bytes in 0 blocks
==22== possibly lost: 0 bytes in 0 blocks
==22== still reachable: 0 bytes in 0 blocks
==22== suppressed: 0 bytes in 0 blocks
==22==
==22== For counts of detected and suppressed errors, rerun with: -v
==22== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
The binary analysis showing we definitely leaked the int on the heap.
When Optimizations Hide Bugs
Now, enable compiler optimizations. The compiler performs heap elision removing the allocation entirely and the leak disappears at runtime.
root@bd38132127e8:/my_code$ valgrind ./a.out
==47== Memcheck, a memory error detector
==47== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==47== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==47== Command: ./a.out
==47==
==47==
==47== HEAP SUMMARY:
==47== in use at exit: 0 bytes in 0 blocks
==47== total heap usage: 1 allocs, 1 frees, 72,704 bytes allocated
==47==
==47== All heap blocks were freed -- no leaks are possible
==47==
==47== For counts of detected and suppressed errors, rerun with: -v
==47== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
This is worrying your code is still flawed, but the binary appears correct.
Relying on runtime analysis alone would cause you to miss this bug entirely.
Summary
This shows that there is no overarching solution.
A combination of dynamic and static analysis along with fuzzing is the best way to hedge your bets in creating and maintaining code correctness.
For example in this case had we also been using a static analysis tool like clang-tidy this bug would have been easily caught as can be seen below.
Further Information
If you're not already using binary/static analysis in your workflow, now is the time to start.
There are countless great tools and resources out there to get started.
The people behind PVS Studio a commercial option provide great insightful blog posts.
Ericsson's codechecker is built upon Clang Static Analyzer and Clang Tidy and provides a great GUI based solution.

If you want to try out valgrind I have a simple docker image to get you started.
