The Object-Oriented Technologies supplement to DO-178C3
identifies a number of vulnerabilities and provides guidance how to deal with them. They fall into several categories.
Storage predictability is essential for high-integrity systems. A stack overflow, or the failure of a dynamic allocation because of heap exhaustion or fragmentation, is unacceptable, and the developer needs to show that such errors are prevented.
Polymorphism in OOP makes this difficult. A polymorphic variable can denote objects from different classes at different times. Since a class may be added to an inheritance hierarchy after the code containing the declaration of a polymorphic variable has been compiled, the compiler cannot predict the maximum size for the object that the variable denotes. As a result, a compiler for an object-oriented language will implement polymorphic variables as references (pointers) to their denoted objects. Application code will generally use dynamic allocation to create such objects, so the developer needs to ensure that:
- No allocation request fails (i.e., the heap is large enough, it does not become fragmented, and there are no storage leaks).
- No object is deallocated while it is still accessible to the program (i.e., dangling references are prevented), if the language provides an explicit deallocation construct.
The time required for allocation and deallocation is bounded and short enough so that all real-time deadlines are met.
Whether and how these requirements are met depends on both the techniques used in writing the application code, such as avoidance of storage leaks and dangling references, and the implementation of the run-time library for storage management.
Some object-oriented languages, such as Java, provide automatic storage reclamation, also known as a garbage collector, instead of an explicit deallocation feature. A garbage collector prevents dangling references but complicates the software verification effort by introducing an element of nondeterminism — when, for example, is an inaccessible object reclaimed? The garbage collector, similar to any other run-time support library, is also subject to the same certification requirements as the application.
Note that the presence of a garbage collector does not by itself prevent storage leaks. An errant program with an infinite loop that adds nodes to a linked list will eventually exhaust the heap.
Inheritance, Substitutability, and Dynamic Dispatch
Inheritance is both a programming technique — defining a subclass as an extension of a superclass — and a type of theoretic and data modeling formalism. From the latter perspective, inheritance corresponds to the specialization relationship: if class C2 inherits from (is a subclass of) C1, then any instance of C2 is also an instance of C1, and thus any operation applied to a C1 instance should also work correctly (with dynamic dispatch) when applied to a C2 instance. This concept is referred to as the Liskov Substitution Principle4 (LSP). The problem is that inheritance as a general programming language feature can be misused to violate LSP.
Violation of LSP can lead to vulnerabilities in dynamic dispatch. A call that works correctly when applied to a polymorphic variable denoting an instance of a superclass can fail when the variable denotes an instance of a subclass, if the subclass does not conform to LSP. Further, in the context of DO-178B, it’s unclear what kinds of requirements-based tests and structural coverage analysis are needed to detect and prevent such errors. Testing each call for every possible subclass for a denoted object can lead to a combinatorial explosion and might not be necessary.