The upcoming OpenBSD 5.6 release introduces a new libc function called reallocarray(3) that extends realloc(3) with built-in integer overflow detection. In this post, I’ll discuss why it’s useful and how it can be used to fix unsafe code.
If you’re not familiar with integer overflows or need a refresher, Ray Lai’s Undeadly article on integer overflows is a good place to start, especially if you would like to read about it in the context of OpenBSD.
In his article which was written in 2006 (way before reallocarray() existed), Ray wrote, “Unfortunately there is no safe replacement for realloc(3). To prevent multiplication overflow, a check must be added”, where the check is as follows:
1 2 3 4 5 6 7 8 9 10 11
Adding that check before every realloc() call is clunky, not to mention error-prone. Ray recognized this and recommended the use of a function called xrealloc() — an early effort that attempts to solve what reallocarray() now solves. xrealloc() is implemented in parts of the OpenBSD source tree but is not part of OpenBSD’s libc.
reallocarray() solves this integer overflow problem more thoroughly by wrapping around realloc() like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
As you can see, reallocarray() checks to see if the attempted multiplication will result in an overflow before calling realloc(). This means you can avoid writing checks every time you need to call realloc() – thus gaining integer overflow detection virtually for free.
Another advantage over xrealloc() is that reallocarray() is available in OpenBSD’s libc, which means all C programs in OpenBSD can easily use it.
“But wait! There’s more!”™
At this point, you may be thinking, “Okay, it’s a wrapper around realloc(). What’s the big deal?” Well, it is a wrapper around realloc() with built-in integer overflow detection, but it’s also so much more than that!
In my opinion, reallocarray() is useful in three cases:
- Replacing unsafe malloc() calls
- Avoiding unneeded calloc() calls
- Replacing unsafe realloc() calls
Let’s look at each case in turn.
1. Replacing unsafe malloc() calls
malloc() as we all know is used to allocate memory for an object with a given size.
Its prototype is simple:
When a programmer wants to allocate memory for multiple objects, the obvious but unsafe way is to call malloc() like this:
1 2 3
As noted in the comment, the multiplication of
size could result
in a potential overflow.
With reallocarray(), the above malloc(num * size) call can be replaced with:
1 2 3
Since reallocarray() does the overflow check for you, you’re done!
2. Avoiding unneeded calloc() calls
Before reallocarray() came along, the recommended way in OpenBSD to fix malloc(num * size) calls is to replace them with calloc() instead:
1 2 3
calloc() comes with built-in integer overflow detection, at least on OpenBSD. But calloc() also zeroes out the allocated memory. There are times when you don’t need or want to zero out the memory, for example if you’re going to intialize the entire allocated memory immediately after the calloc() call anyway.
This is where reallocarray() shines again. Just like the above case, the call can be replaced with:
1 2 3
This allows the memory to be allocated without the cost of zeroing the memory, while at the same time detecting integer overflows. Neat, huh?
3. Replacing unsafe realloc() calls
As noted by Ray, there is no safe replacement for realloc(). realloc() is frequently called like this:
1 2 3 4
Like the malloc(num * size) case, the multiplication occurs without a check, which could result in an integer overflow.
reallocarray() can replace the unsafe realloc() call like this:
1 2 3 4
You have to be careful here though, because if the code is part of a
function which returns on error instead of exiting completely, the
p would not be freed and you will lose it. For example:
1 2 3 4
A better way is to borrow the recommended idiom for realloc() from OpenBSD’s realloc(3) man page and apply it to reallocarray():
1 2 3 4 5 6 7
reallocarray() is a non-portable OpenBSD extension. But thanks to its liberal ISC license, you are free to use it with your own code as long as you adhere to the terms of the license. So not only do you get integer overflow for free, you get the code for free as well!
Due to its usefulness, at the time of writing, more than 130 commits have been made to the OpenBSD tree that involve reallocarray(). Theo himself has been on a merciless reallocarray() rampage this past month. :–) Some of the fixes have been easy and obvious; others have been very tricky. The audit continues…
UPDATE (2014/10/30): Revised the “Replacing unsafe realloc() calls” section to discuss potential memory leak issues. Thank you to Andrew Dalgleish for pointing out this issue in the original version of the blog post.