Good, clean code is the starting point for secure software, and having the right tools is essential when that’s not the case.
William G. Wong
No one wants to write bad, buggy code, but that’s often what we get from developers who don’t follow best practices. This problem is worse when one considers the requirements for safe and secure applications. These days, safe and secure code covers just about everything. If you’re sharing the code, then people depend on its quality. If you’re designing a product for self-driving cars, aircraft, or medical devices, then it’s even more important to use the right tools and procedures to deliver reliable software that’s also secure.
I do recommend Ada and SPARK for programming in general because of features like contract-based programming. However, I know C still dominates for embedded programmers. For those who haven’t moved to C++, I would push that as a better solution even if object-oriented programming (OOP) isn’t part of your repertoire. C++ is significantly better when it comes to everything from namespaces to type management to references. The new C++20 standard will incorporate new features like modules.
Many assume C++ is less efficient because C and C++ have more overhead. That’s not really the case, but if you use some features like virtual class functions, then, yes, there’s overhead. It’s on par with any overhead of providing similar functionality in C, although in a much cruder fashion. Using C++ features like non-virtual methods in a class allows for the same efficiency as C while enabling methods to be hidden as well.
So, what does code quality have to do with safety and security?
It’s difficult to have either without good code to back them up. Though coding standards can help in writing good code, it’s just a starting point. Code reviews can also help, but many developers overlook tools like static analysis that have the advantage of enforcing rules by a computer, which brings a level of consistency to the process as well as enforcement.
One well-known standard is MISRA C. It started in the automotive space where it remains important, but MISRA C and MISRA C++ are applicable to any application. The standards include a multitude of rules that limit the functionality available within the programming language, as well as how features are used. Some, like Rule 1.3, address undefined behavior, which covers a lot of ground when it comes to C. The C90/C99 standard has over 200 instances of undefined behavior.
Other MISRA C rules might appear less useful, such as preventing local variable and type names from hiding more global definitions. This can result in a debugging nightmare if one isn’t aware, because he or she may be looking at the more global definition and wondering why it’s not operating as expected.
I’ve been working with IAR Systems’ Embedded Workbench, which has MISRA C support built-in, along with GigaDevice’s GD32V RISC-V board. It was an interesting exercise getting the demo code working. Turning on MISRA C with the default settings results in hundreds of errors, but disabling or tweaking a few rules cuts this number down to a dozen, so it’s worth making the code changes.
Following the rules when writing code and having the compiler check it doesn’t eliminate all errors. However, it can force better programming practices and highlights errors that would otherwise be overlooked until the code was running in the field. Debugging costs rise exponentially as it moves farther from the original developer.
Developing safe and secure code should be part of the goals for any developer. Using the right procedures and tools can help, especially for larger projects and groups.