Debunking the Unsafe C myths part 2
Introduction
On end of March'25 I wrote the part 1 of this series here and although at the end of that article I mentioned we are going to finish the rest of the video, I felt the test was boring since I knew the results would be: all these "unsafe" C practices would be caught by either static analyzer or dynamic sanitizer.
Hence it would not bring any real challenge ~ boring! But fear not! I found a better, much nastier challenge...it is in the form of X/twitter conversation I had with Dmitry, who writes Undefined Behaviour guide book.
Also please visit Dmitry's blog. You can't imagine how excited I am when I found his blog and UB-book, it is like a heaven-on-earth to me since he outlined lots and lots of the most difficult undefined behaviours in C/C++ --> prime candidates for me to test with my modern C safety tools!
The screenshot below is taken from part-1 of Dmitry's blog.
Focus point: a collection of the most disgusting examples.
This is the real deal, unlike the video I debunked in the 1st part...that was puny, kindergarten-level toy stuff in comparison with Dmitry's UB-book and I'm super ready to get absolutely trashed by the UB-book LOL !!
In this article I will walk you through my conversation with Dmitry.
The unsafe C++ code
Comment from Dmitry:
The fun part — it becomes significantly harder to catch all these
issues in more complicated cases, when the bug can be triggered by
interactions between functions
E.g. stack use after free is not detected by asan (C++ stuff can be
replaced with equal C)
Rewrite the C++ code to C
I rewrote Dmitry's C++ code to C:
Note: the output are the same on the left-split. I compiled using -fanalyzer on the right-split and you can see the error pointed out to the use after free / stale stack frame:
logger->dest memory ref is no longer valid.
Below is the lower part of the gcc static analyzer error trace:
Minor code change.
Logger** passed to the callback directly instead of the global variable, this is to make the C code as close as possible to the C++ code (reference every time captured by the lambda closure)
Note: Logger is now local in main instead of global var. I still get the same error with gcc static analyzer. I then tried using sanitizer and it also caught the UAF, as shown in the below image:
Conclusion
At the end of the conversation Dmitry mentioned that he specifically crafted this example to trick dynamic analysis for C++. I'm a big fan of Dmitry's work and I will dig deeper into his blog and UB-book for the next part of this series. I still fully expect I would get absolutely trashed by one of the UBs he crafted in his UB-book and that makes me excited!
What's the message we can get from this exercise? Use the tools: static analyzer (make sure use GCC, it's much superior compared to Clang's as shown in my other post), dynamic sanitizer, and Valgrind if you want to be thorough. The C ecosystem is vast, far and wide...you'd be crazy not leveraging the use of these powerful tools available to you.
There's one particular tool I might cover in the future: Fil-C. In my opinion it got the potential to have widespread use because all you need is to recompile your C program using Fil-C (and maybe some minor changes to your codebase) and get a safe C program. This sounds crazy, but watch this space! If Fil-C works well, then there's no need to rewrite C programs in any other language! The only major hurdle is performance, which recompiling using Fil-C would introduce around 2x overhead. The author is targeting for a 1.2x overhead across most use cases.
Imagine how easy and most importantly how scalable it will be to make billions upon billions of lines of C codebase safer by recompiling them using Fil-C..
Here are some references about it:
- Hacker News: focus point on the arguments made by the creator (Filip Pizlo).
- Article #1
- Fil-C Github Manifesto