Writing Secure Automotive C++ Software Using MISRA Guidelines
<Writing Secure Automotive C++ Software Using MISRA Guidelines>
With the rapid increase in software components and lines of code that go into modern automotive systems, there is a corresponding rise in the risk of introducing critical bugs. Competitive pressure and the drive to get new features to market faster also leave less time to test and improve software. As vehicles become more and more connected, these circumstances combine to leave automotive systems more vulnerable to hacking than ever before.
The cheapest way to create secure software is to identify defects during the first stage of the development cycle, (i.e. when the software is written) where the cost and effort necessary to resolve them is minimal. The Motor Industry Software Reliability Association (MISRA), a collaboration between major automotive manufacturers and suppliers, has developed standards to assist developers in writing secure software from the beginning. Utilizing automated tools that enforce these standards, many bugs and vulnerabilities can be caught and fixed early on.
The standards are written as set of rules that (for the most part) are widely accepted good programming practices, and this multi-part blog will explore how MISRA and other standards affect software development, focusing on the standards for the C++ language. Although “C” is the predominant programming language used in the automotive industry, C++ is gaining traction, especially on the infotainment side. The following explores the MISRA C++ guidelines, their implications for development, and compliance requirements.
C++ is a powerful language that offers a lot of flexibility. However, C++ does have certain drawbacks, as some areas of the language are too complicated and can be easily misunderstood.
The language assumes that the developers know what they are doing, but even highly experienced C++ developers can make mistakes. C++ programs can be written in a way that results in unspecified behavior, and a code written for one platform (and compiler) might behave differently when compiled for another platform. The language also lacks runtime checking, and even the minimum runtime infrastructure (RTTI - Runtime Type Information) is often disabled for performance reasons.
One weak area of C++ is type-checking and automatic type casting.
For example, it is a common misconception among developers that the type in which a calculation is conducted is influenced in some way by the type to which the result is assigned or converted. Consider the simple addition of two unsigned integers being assigned to 32-bit unsigned integer:
u32x = u16a + u16b
On a 16-bit platform, the addition will be performed as 16-bit with the potential side effects of integer overflow. In the above example, it is not unusual for the developers to be deceived into thinking that the addition is performed in 32-bit arithmetic - because of the type of u32x. The following are a few more examples where an expected evaluation can be performed:
u32a = static_cast<uint32_t>(u16a * u16b); // Evaluated in u16
f64a = u16a / u16b; //Evaluated in u16
f32a = static_cast<float32_t>(u16a / ua6b); // Evaluated in u16
And this is just one area of C++ that can lead to bugs, unexpected behavior and potential security vulnerabilities.
The coding guidelines for safety-critical systems describe best practices to avoid the pitfalls of the language. Some compilers and many static analysis tools look for these kinds of problems and give warnings to the developers.
<MISRA C++ Guidelines>
MISRA provides coding guidelines for C and C++ languages. Their goal is to enable developers to create safe, secure and portable code in the context of embedded systems. MISRA initially started with C guidelines, which are widely accepted as a standard in the automotive industry. In 2008 MISRA created guidelines for the C++ language as defined by ISO/IEC 14882:2003 (C++ 03). There are currently no guidelines covering C++ 11 or C++ 14 features.
The MISRA C++ guidelines can be ordered here: https://www.misra.org.uk/MISRACHome/tabid/128/Default.aspx
The MISRA guidelines are useful for establishing processes in accordance with ISO 26262.
The MISRA C++ guidelines consist of 228 rules that are divided into “Required”, “Advisory” or “Document” categories. Required rules are mandatory and C++ code that claims to conform to MISRA C++ needs to comply with every required rule. In cases where this cannot be achieved, formal deviations need to be documented. Advisory rules should be followed where reasonably practical. Document rules are also mandatory requirements of the comments in the code, and these rules cannot have deviations.
<Dynamic Memory Allocation Rules>
Like nearly every embedded software safety standard, MISRA C++ forbids dynamic memory allocation, via Rule 18-4-1. This is a reasonable requirement for embedded safety critical systems, as the dynamic heap allocation may lead to memory leaks, data inconsistency, memory exhaustion, memory fragmentation, non-deterministic behavior and other problems.
Memory allocation is often used for infotainment components and applications running on higher-end operating systems such as Linux or QNX. In these instances, there is often a dependency on external libraries such as STL that use dynamic memory allocation. This limitation can be overcome by using a custom STL implementation that uses static memory allocators or another strategy that avoids dynamic memory allocation. Extra care needs to be taken when using third-party libraries to ensure that there is no internal dynamic memory allocation. An alternate option is to describe a deviation as shown in the procedure below.
Unlike most modern embedded application software, it is worth noting that some of the newer standards such as AUTOSAR are allowing dynamic memory allocation.
According to the guidelines, MISRA C++ compliance can be claimed for a given product by stating that evidence exists to show:
A compliance matrix showing how compliance has been enforced;
All the C++ code in the product is compliant with the rules from the MISRA C++ guidelines or is subject to documented deviations;
A list of instances where the rules have not been followed with each deviation appropriately signed-off;
Well documented software development practices that include training, style guide, compiler selection and validation, checking tool validation, metrics and test coverage.
The MISRA guidelines recognize that instances can arise where it is necessary to deviate from the rules in the document. A formal procedure must therefore be created rather than allowing an individual programmer to deviate at will. The guidelines prescribe a sign-off procedure for each deviation, or class of deviations, each of which must be justified and well documented. A good practice is to structure the code in such way that that modules containing deviations are separate from the rest of the code and are clearly documented.
Static analysis tools are the most effective means of checking conformance with the guidelines. As MISRA C++ guidelines have been released for a decade now, many commercial static analysis tools support checking for MISRA C++ rules. No tools support 100% of the MISRA rules, and those that cannot be checked by a tool will require manual review.
As the de facto coding standard for automotive systems, MISRA C/C++ has strong support from various commercial tool vendors and follows good programming practices applicable beyond safety-critical systems alone. As the C++ guidelines were finalized in 2008, they do not reflect the more recent developments in the C++ language (C++11 and C++14) and compilers, nor some of the more recent security breaches and vulnerabilities. The next blog in this series will describe the AUTOSAR C++ guidelines that cover the new features of the language and combines knowledge from multiple standards and recent discoveries around software security.