Safe Integer Arithmetic in C

There has been plenty of literature written regarding integer arithmetic issues and security bugs. If you need a good refresher, I would urge you to read one or more of the following:

 

 

Recently, the good folks in the Windows division set out to create small set of C functions (obviously, you can use them in C++ code too) to perform safe integer arithmetic. These functions are used mainly to perform safe memory allocation calculations and array offset calculations. The small library is aptly called IntSafe. Not to be confused with SafeInt from LeBlanc, IntSafe is simply a set of easy to use function calls, while SafeInt is a C++ template class.

 

IntSafe has two categories of functions, those that perform real math, and those that convert from one data type to another. The math functions are listed here, and the conversion functions are listed here.

 

Now here’s something you should know if you want to use IntSafe. All arithmetic is performed on unsigned values. If you want to use signed values, then use the appropriate conversion function first to convert the value to an unsigned value safely. These are omitted on purpose - we found that it was too easy to use the safe functions and assume nothing bad could ever happen because, well, you are using the safe functions! Also, we found that many of the serious security bugs were signed #'s were involved could be fixed by simply switching to unsigned values. Why use a signed value to index an array? Or a signed value to allocate or reallocate memory?

 

 For example, the following code converts a signed int to an unsigned long:

 

#include “intsafe.h”

...

int cElem = GetValueFromTheNetwork();

unsigned long ulcElem = 0L;

if (SUCCEEDED(IntToULong(cb,&ulcb))) {

      // success

}

 

If IntToULong is passed a negative value, the function returns INTSAFE_E_ARITHMETIC_OVERFLOW, and fails. Simply casting a negative number to an unsigned value is dangerous, so don’t take any risks.

 

There are plenty more functions like this for converting from one data type to another.

 

Next, we want to perform the actual math operation. There are three categories of math: addition, subtraction and multiplication. There are no division functions, because you can’t overflow a division! Sure you can divide by zero, but you know to check for that in your code, right? Actually, there are some corner cases when using negative numbers, but IntSafe handles only zero and positive numbers.

 

This code performs a data conversion and then multiplies the two numbers together, and then adds one to the result:

 

int cElem = GetValueFromTheNetwork();

DWORD cbElem = 0, cbBlockSize = 0x00F00000;

void *p = NULL;

// Performing: cbElem = (DWORD)cElem * cbBlockSize + 1

if (SUCCEEDED(IntToDWord(cElem,&cbElem)) &&

    SUCCEEDED(DWordMult(cbBlockSize, cbElem, &cbElem)) &&

    SUCCEEDED(DWordAdd(cbElem,1,&cbElem))) {

      // Cool! Calculation worked

      p = malloc(cbElem);

      assert(p);

}

if (p) {

      // use p

      free(p);

} else {

      // Oops! Overflow, or out of memory

}

 

So what about kernel mode code? Well, there is a kernel-mode header also, named ntintsafe.h. Rather than returning an HRESULT, these functions return an NTSTATUS, and the functions are prefixed with Rtl. Other than these little differences, the code is basically the same between the user mode and kernel mode functions.

 

I want to point out that these functions are being used a great deal in Windows Vista, primarily in code that calculates buffer sizes and array offsets. I wouldn’t use them for all arithmetic, just when you’re calculating an index, buffer size or computing some other form of bound check.

 

Finally, these functions are optimized for speed -- they are inline on purpose, and the compiler generates amazingly good code.

 

You can pick up these headers in the latest Windows Server 2003 and Windows Vista Platform SDKs, or to save you the hassle, you can get them here too :)

 

<Special thanks to Reiner Fink for creating IntSafe and for reviewing drafts of this posting and thanks to Raymond Chen, Larry Osterman and Daniel Wang for also reviewing the drafts>

intsafe.zip