Now that you’ve chosen a software process standard appropriate for your project, next comes the job of actually writing the software. The objective of mature software development teams is to reduce defects by building quality into the software as it’s being written.
However, quality means different things in different applications. A usability defect in a desktop application, such as the infamous Excel 2007 defect that presented any numeric calculation amounting to 65,535 as 100,000, can be considered to be purely an annoyance. Usability issues in safety-critical systems can take on a much more severe slant. Between 1985 and 1986, for example, a race condition in the Therac-25 radiotherapy machine caused an incorrect mode of operation to be applied, exposing six patients to massive overdoses of radiation that claimed several lives.
This file type includes high resolution graphics and schematics when applicable.
These examples may seem dated, but are by no means isolated. The news is littered with stories documenting the impact of software failures. One example from the safety-critical world is the huge recent settlement by Toyota in response to the “unintended acceleration” lawsuit decided against them. On the software security front, the remediation costs associated with the recently announced OpenSSL “Heartbleed” vulnerability has yet to be calculated, but is believed to run into many millions of dollars for Cloudfare.
So how are these types of software defects mitigated as the software is being written? The answer lies in programming standards. Programming standards represent an invaluable approach for building quality into software. They encapsulate the knowledge and experience of dozens of programmers on best practices for writing software in a given language for a given domain, as well as providing guidance for creating good code.
The Benefits of Standards
At their most basic, programming standards help ensure that the code produced by a given organization is consistent, ensuring that the code output from any developer can be read by any other developer in the organization, facilitating code reviews and downstream maintenance. This helps during the defect identification and isolation process, helping diminish latent defects, which lowers overall software costs. But programming standards can also help organizations avoid areas of significant programming risk, leading to enhanced quality of code. An example is the best way to shed light on this scenario.
One of the most revered and widely used programming languages in use today is the C programming language. Originally designed to be a lightweight language with a small footprint, C does carry some inherent risks. There have been multiple versions of an international C language “standard,” with the latest, C11, being ratified in 2011. While a few compilers comply with C11, many popular compilers only comply with the international C language standard that was ratified in 1989.
All versions of the language identify specific language semantics that are referred to as “undefined behavior,” which C11 rather unnervingly defines as:
Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance or a diagnostic message).
The problem is, not only does the list of undefined behavior semantics change from C language version to C language version, but the implementation changes from compiler to compiler. Many programmers take advantage of these undefined behaviors to optimize their application for a specific platform when the code is compiled with a specific version of the compiler. However, these optimizations can result in defects when used with a different compiler, or when the code is compiled for a different platform.
Now add C++, Ada, Java, and the multitude of other languages being used on modern projects, and the areas of risk increase exponentially. Not all of these languages display the same type of undefined behaviors like those of C, but they all have risks associated with their usage.
How do programmers become aware of all these risks? They must refer to a programming standard that codifies the risk areas to be avoided.
Beyond helping make code easier to read and reducing areas of risk, programming standards also encapsulate the risks inherent to domain-specific applications. For example, most safety-critical devices aren’t designed to be failure-free. Rather, they’re implemented so that when failures do occur, they’re detected and the system response to them ensures user safety.
However, in the case of a security-critical application, software must be written with failures in mind to ensure against the creation of vulnerabilities, since security failures open the door to outsiders to exploit a system. This means that risk areas associated with creating a safety-critical device aren’t the same as risk areas associated with creating a security-focused device. Interestingly, with today’s “network-enabled” trend in medical devices, these lines are blurring somewhat.
Programming Standards
What programming standards are available? Greater interest in the topic has led to a surge in the publication of such standards over the past 10 years (see the table).
So, after choosing your process standard, programming language, and programming standard, you’re good to go, right? Not so fast. The implementer of a coding standard has to balance the strictness of the guidelines against the effort required to follow them.
Coding standards used within a formal-methods environment often contain “absolute” guidelines that may never be violated. On the other hand, while it may be desirable to have strict enforcement of a coding standard, it’s not always practical—some products can effectively use a subset to better meet project cost, build-in maintainability, and comply with safety requirements.
A programming standard needs to allow some flexibility so that guidelines can be violated in a controlled way. Decisions around flexibility and tradeoffs should be made early enough to able to analyze and determine deviations, and subsequently establish rules that define the appropriate use of those deviations throughout the project team.
Is a Subset Acceptable?
Early in the project planning, it’s important to determine whether a subset is permissible and to identify the guidelines for exclusion from the full standard. The first step is to understand certification requirements.
Many projects are expected to adhere to an international industry-specific standard, such as ISO 26262 for automotive or IEC 62304 for medical systems; especially if those projects have high-integrity requirements. Certification of compliance will either come via a certification authority or self-certification. These international standards generally require a language subset that’s defined by a programming standard.
If a project needs to be certified before it’s put into service, then the requirements laid down by the certification body will be a primary driver. A specific programming standard may be mandated, chosen from a set of approved standards, or left to contractual arrangements. The body may further restrict what subsetting is permitted, or prohibit it entirely, particularly if compliance with an approved standard is formally verifiable.
The process differs slightly when there’s no requirement for certification. In this case, the project may have more flexibility to decide if it’s appropriate to use a subset of a coding standard, especially when the safety requirements are less stringent. In addition, determinations need to be made regarding whether the chosen coding standard permits a subset to be used and if the supply contract allows the use of a subset. As with all decisions related to the standard, these should be addressed as early as possible in the design cycle to avoid code rework or the need for excessive documentation to back up later decisions.
In many cases, such as the JPL standard described in the table, organizations have developed their own internal coding standards and guidelines to support programming efficiencies, establish consistency, and improve code readability and maintenance within development teams and over the life of the product. These internal standards might also include a combination of other standards or subsets.
Choosing the Guidelines
Once a coding standard subset is determined as acceptable for the project, then you can proceed to select candidate guidelines. Choosing the subset depends on the type of issue covered by the standard. Guidelines offered by a standard generally fall into one or more of the following areas, with a particular standard providing guidance in one or more of them:
• Language issues (e.g., prevention of undefined behavior)
• Security issues (e.g., buffer overrun errors that enable the execution of malicious code)
• Stylistic matters (e.g., use of tabs versus spaces)
Four main tasks must be carried out when producing a subset. First, identify which guidelines should be left out. For each instance, produce a reasoned justification explaining why the guideline isn’t required, including evidence to show that excluding the guideline will not compromise the system, and (important!) obtain official approval for the deviation. Next, produce a subset document to show the incorporated collection of guidelines. Finally, produce configuration file(s) for the tool(s) used within the project to enforce the coding standard subset.
Beyond the important standard and subset decisions, development tools must be evaluated and chosen early in the process. Ideally, the same tools will be used by the entire development team and could include standalone rule-checking tools that can incorporate into larger tool suites. Therefore, standards checking can be integrated into all aspects of the development lifecycle.
To ensure that the team will be at its most effective, check whether the test tools make it possible to easily choose between versions of the standard and appropriate subsets for both legacy and new projects (Fig. 1). Do they let you choose full compliance, or can you incorporate a user-defined subset of rules to ensure that in-house templates and requirements can also be automated.
If they present a superset of all available guidelines for a given programming language, then individual elements can be selected either as a subset that represents a specific programming standard, such as MISRA-C, or as a custom subset for a specific project.
One of the most important decisions during development-tool selection is to find tools that can enforce the chosen programming standard or subset. Static-code analysis tools parse through the code under development and help identify sections of code that don’t comply with the programming standard. More advanced static analysis tools go beyond simple semantic parsing of the code and consider the code in context, helping to introduce new programming standard concepts.
Advanced static analysis tools, for example, can help identify the following issues that can compromise code quality, and should be considered for inclusion by any organization seeking to adopt a programming standard (Fig. 2):
• Areas of excess code complexity
• Unreachable code
• Unintentional data coupling
The overall objective is to improve overall code quality. It’s important to note that code testing is still required, because defects can stem from higher-level process issues (e.g., requirements misinterpretation) just as much as from coding errors.
Smart Standards Application
Building quality into software requires knowledge of the project domain and the chosen programming language to identify areas of risk. Programming standards encapsulate this knowledge to help mitigate the risks of implementing a software system in a specific programming language and ensure performance and quality. Although the guidelines are designed to benefit all projects, some of the more onerous ones can be overkill for projects with lower integrity requirements.
A coding standard subset can reduce the severity of the standard in a reasonable and controlled way, allowing it to be applied more cost-effectively to a wider range of projects. In either case, the decision must be made early in the development process to save time and costs involved in changing direction after coding has begun.
This file type includes high resolution graphics and schematics when applicable.
Documenting compliance to a chosen programming standard, especially for projects that must conform to an industry process standard like ISO 26262, is best achieved via automation tools. When used during the development phase, static analysis tools in particular can help identify areas of code that don’t comply with the chosen programming standard, ensuring that they can be corrected before releasing the project. Selecting the right combination of programming standard and automation tools for a software project is essential to building quality into the software from the start.
Jay Thomas, technical development manager for LDRA Technology, has worked on embedded controls simulation, processor simulation, mission- and safety-critical flight software, and communications applications in the aerospace industry. His focus is on embedded verification implementation.
References
- “The Power of 10: Rules for Developing Safety-Critical Code,” IEEE Computer, June 2006, pp. 93-95.