Improve Microsoft Visual C++ Application Security and Robustness with SafeInt

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

At a cost of $370 million dollars, the Ariane 5 Flight 501 rocket explosion in 1996 highlighted the potential catastrophic effects of software defects. The explosion was caused by an integer overflow in the software controlling the inertial guidance systems of the rocket, with the flow-on effects of wild path alterations causing disintegration followed by self-destruction. While the fact that a simple integer overflow could ultimately cause such a serious flow on effect highlighted significant lapses in overall system redundancy and error-handling, the fact remains that without the integer overflow, the flight would likely be a success. At the end of the day, it is often easier to lay the blame at a very tangible issue like a software bug rather than vaguer but equally relevant issues like missing design in fault-tolerance and insufficient testing. Flight 501 provides a very real reminder that seemingly mundane issues like integer overflow still need attention.

There are certain mathematical assertions that hold true for integer operations, such as the addition of two unsigned integers must produce a result that is larger than either number. These assertions can be used to check for integer overflow in mathematical operations, but outside of projects with very formal control and quality procedures, it is rare for a code base to be comprehensive in its use of checking of integer mathematical operations. To address this problem, the SafeInt library was developed for C/C++ programming as part of the Microsoft security initiative that has been ongoing since 2003. David LeBlanc of ‘Writing Secure Code’ fame has been working on and blogging about SafeInt for quite a few years. The SafeInt library has been released on CodePlex, and even ported to GCC. With Visual C++ 2010, SafeInt ships out-of-the-box.

SafeInt is implemented as a template type that takes template parameters of the underlying integer type and the exception policy that should be applied in the case of a detected overflow or divide-by-zero. The exception policy parameter has a default value of the hash-defined _SAFEINT_DEFAULT_ERROR_POLICY variable and hence does not need to be specified each time a SafeInt is declared, with the ability to define the policy at the project level by defining _SAFEINT_DEFAULT_ERROR_POLICY as the appropriate policy type. The SafeInt template is defined in the safeint.h header file and contained in the msl::utilities namespace. Using SafeInt is very easy–once the template-based integer has been declared, the SafeInt variable will behave in very much the same way as the underlying integer type, as shown in the code below which declares some SafeInt variables and exercises them with a number of operations.

  int _tmain(int argc, _TCHAR* argv[])
  {
   SafeInt<short unsigned int> number1 = 100;
   SafeInt<short unsigned int> number2 = 150;
  //operator overload to support standard mathematical operations
   SafeInt<short unsigned int> result = number1 + number2;
   ++result;
   result += 2;

  // operator overload to support comparison
   if (number1 < number2){
    result = number2 - number1;
   }

  // operator overload to support bitwise operations
  result = number1 ^ number2

  // operator overload to support casting to an integer type
   return (int)result;
  }

As would be expected, SafeInt provides a wide variety of assignment, casting, comparison, arithmetic and logical operators. With the wide variety of operators available, using SafeInt should feel very much like using the underlying integer type in all cases except when an overflow is detected.

Two exception policies ship with SafeInt – SafeIntErrorPolicy_SafeIntException, which will throw a C++ SafeIntException object when an overflow or divide-by-zero error is detected by the SafeInt template, and SafeIntErrorPolicy_InvalidParameter, which will call the C Runtime invalid parameter function, which has a default behaviour of invoking Watson crash reporting. Implementing a custom SafeInt exception policy is very simple–a struct with two static functions needs to be declared as below.

  struct CustomSafeIntErrorPolicy
  {
      static __declspec(noreturn) void __stdcall SafeIntOnOverflow()
      {/*deal with error*/ }

      static __declspec(noreturn) void __stdcall SafeIntOnDivZero()
      {/*deal with error*/ }
  };

With this policy declared, any SafeInt can take advantage of it by specifying the policy as the second template parameter:

  SafeInt<short int, CustomSafeIntErrorPolicy> mySafeIntwithCustomPolicy;

The default policy of an application can be set be defining _SAFEINT_DEFAULT_ERROR_POLICY before including the safeint.h header file in a source file. There is code in the header file to check whether _SAFEINT_DEFAULT_ERROR_POLICY has already been defined, and if it hasn’t, SafeIntErrorPolicy_SafeIntException will be set as the default. This struct will throw a C++ exception of type SafeIntException (which is also defined in safeint.h). The value of SafeIntException::SafeIntError is used to record whether an overflow or divide by zero error has occurred.

The performance implications of SafeInt may raise some concerns. The safeint.h header contains a brief discussion of the performance side of SafeInt, with the following observations:

  • Design has explicitly included performance vs safety trade-offs, with the maximum performance that guarantees correctness a design goal.
  • Signed integers will have a higher overhead than the unsigned equivalent.
  • Significant performance improvements will occur in release builds.
  • SafeInt is not appropriate for tight computational loops, particularly where overflow can be guarded against outside the loop conditions.
  • A performance difference of about 8% has been observed in release builds using SafeInt compared with the direct use of the underlying integer type.

SafeInt is a great library that can be used to implement a simple, consistent and robust approach to dealing with the serious issue of integer overflows. By containing all the error-checking code outside the main flow of application code through the use of operator overloading provided by SafeInt, application code can be converted to an overflow-checked equivalent with minimal rework and overhead of effort. The error checking by SafeInt does not come for free, and code in highly performance sensitive scenarios should undergo appropriate performance testing if SafeInt is used. The performance overhead can generally be significantly mitigated by isolating it to scenarios where integer operations are being conducted on values supplied by external routines and whose range can span any value from the underlying integer type.

Related Articles

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read