Until recently, developers of high integrity systems – i.e., where the highest levels of safety and/or security are required – have largely avoided Object-Oriented Programming (OOP). The costs and risks in moving to new technology, and uncertainty on how to comply with safety or security certification have all been deterrents.
However, this situation is changing. With high-integrity systems growing in size and scope, OOP’s advantages in designing extensible software architectures are looking increasingly attractive. The question is: how can OOP be used safely and securely?
Some of the issues with OOP
OOP provides dynamic flexibility, but it also introduces potential vulnerabilities that can lead to bugs. It can also add semantic complexity making verification and certification more difficult. In most OO languages, object creation entails dynamic allocation.
Storage leakage, heap fragmentation, and dangling references must be prevented. Furthermore, allocation and de-allocation must have predictable time bounds if real-time deadlines are to be met. Inheritance represents a specialization relationship: if class C2 inherits from C1, then any C2 instance 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 known as the Liskov Substitution Principle (“LSP”). However, inheritance as a programming feature can be misused to violate LSP. A call that works correctly when applied to a polymorphic variable denoting an instance of a superclass can fail, notably when the variable denotes an instance of a subclass if the subclass does not conform to LSP.
If the name of an operation intended as overriding is misspelled, the superclass’ operation that is supposed to be overridden is inherited instead. In the other direction, adding an operation to a superclass may incorrectly make a subclass’s operation (with the same signature) overriding.
Multiple implementation inheritance introduces semantic complexity – for example inheriting different implementations of the same operation through different paths – that interferes with traceability and other verification goals.
The Ada approach
Even when OOP is not used, many Ada features support safe and secure programming. These include strongly typed numeric data, scalar range constraints, array index checking, integer overflow checking, safe pointers to declared objects, and alternatives to dynamic allocation for flexible-length data structures. Ada also supplies a compiler directive, pragma Restrictions, that specifies features that are not being used. The resulting features-used subset may have simpler support libraries, reducing the certification effort.
When OOP is used, Ada helps avoid the vulnerabilities and address the certification challenges noted above.
Ada’s type system and containers library avoid the need for dynamic allocation in many circumstances. When dynamic allocation is needed, the programmer can use Ada’s storage pool facility to define desired behavior for the allocation and de-allocation operations. A practical example is a pool of fixed-size blocks with constant-time allocation and de-allocation. Garbage collection, which introduces complex run-time support, is not required by Ada, so the application code needs to reclaim inaccessible objects. Analysis has to ensure the absence of dangling references, but Ada’s strong typing simplifies this analysis compared with other languages.
About the author:
Ph.D Benjamin M. Brosgol is Senior Software Engineer at AdaCore - www.adacore.com
Part 1 of this three-part article reviews the basics of object-oriented programming and summarizes the challenges it presents for high-integrity programming. Part 2 will provide a primer on the Ada programming language, and Part 3 will detail the tools Ada offers to help developers meet the OOP challenges.
This article originated from the March edition of the EETimes Europe magazine.