Integer overflow in operator new

2007-04-06 Thread Karl Chen

Hi all, apologies if this has been discussed before, but I
couldn't find anything about this issue in gcc mailing list
archives.

Use of operator new (et al) appears to have an integer overflow;
this function:
 
int * allocate_int(size_t n)
{
return new int[n];
}

with gcc-4.1 on IA-32 compiles to:

_Z12allocate_intj:
pushl   %ebp
movl%esp, %ebp
subl$8, %esp
movl8(%ebp), %eax
sall$2, %eax   
movl%eax, (%esp)
call_Znaj
leave
ret

which is equivalent to the compilation of:

int * allocate_int(size_t n)
{
return (int*) operator new[](4 * n);
}

"4 * n", unchecked, is vulnerable to integer overflow.  On IA-32,
"new int[0x4001]" becomes equivalent to "new int[1]".  I've
verified this on gcc-2.95 through 4.1.  For larger objects the
effects are exaggerated; smaller counts are needed to overflow.

This is similar to the calloc integer overflow vulnerability in
glibc, which was fixed back in 2002.  Interestingly, RUS-CERT
2002-08:02 did mention 'operator new', and so did Bugtraq 5398.
http://cert.uni-stuttgart.de/advisories/calloc.php
http://www.securityfocus.com/bid/5398/discuss

See also this 2004 article by Raymond Chen:
http://blogs.msdn.com/oldnewthing/archive/2004/01/29/64389.aspx

Possible fixes might be to abort or to pass ULONG_MAX (0x)
to 'operator new' and let it return NULL or throw bad_alloc.

At least one other compiler already specifically guards against
integer overflow in 'operator new'.

-- 
Karl 2007-04-06 07:30



Re: Integer overflow in operator new

2007-04-06 Thread Karl Chen
> On 2007-04-06 15:35 PDT, J C Pizarro writes:

J> A possible workaround could be it but it's vulnerable if
J> it's defined with -DNDEBUG :

J> int * allocate_int(size_t n) {
J> // it's another integer overflow, a positive can
J> // become to a negative.
J> //n=1073741823 (0x3FFF) => n*4=-4
J> //(0xFFFC) return (int*) operator
J> //new[](-4); !!! it's easy for
J> buffer overflow.
J> assert(0 <= (4 * n));
J> // it's an assert against your integer overflow.
J> assert((4ULL * n) <= ULONG_MAX); return (int*)
J> operator new[](4 * n);
J> }

Good points.  

Regarding negatives, I believe 'operator new' takes a size_t,
which is unsigned, but if it were signed it, the multiplication
would indeed be in danger of creating a negative.

If possible, I would prefer a solution that's built-in to operator
new.  I was thinking it should be implemented when code is
generated, for example using jc/jo/seto on i386.

-- 
Karl 2007-04-06 15:41


Re: Integer overflow in operator new

2007-04-06 Thread Karl Chen
> On 2007-04-06 16:12 PDT, Lawrence Crowl writes:

Lawrence> Asking programmers to write extra code for rare
Lawrence> events, has not been very successful.

Well put Lawrence, I agree; I didn't expect strong opposition.

I doubt we'd find much code in the wild that checks for integer
overflow before every 'new'.  I'd like to reiterate that the
equivalent calloc issue was treated as a serious vulnerability and
fixed right away.

>From the programmer's point of view, the multiplication isn't
obvious.  For all they know, it's a 'for' loop that iterates per
element, allocating (sizeof object) bytes in each iteration.  So
it's not obvious why a check would even be necessary to a typical
user.

I would also note that all non-char uses of operator new that I
looked at in gcc's C++ headers are lacking integer overflow
checks.

Lawrence> It would be better if the compiler incorporated this
Lawrence> check into operator new, though throwing an
Lawrence> exception rather than asserting.  The compiler
Lawrence> should be able to eliminate many of the
Lawrence> conditionals.

Indeed, recent research has found that even checking for overflow
on *every arithmetic operation* is only a 5% run-time overhead.  A
default check on only 'operator new' invocations is quite cheap -
a single jump-if-overflow instruction that fares well with branch
prediction.  Handling the overflow case can be moved faraway so it
doesn't affect instruction cache performance.

Fixing it in user code at call sites is cumbersome - there's no
way to wrap a macro around 'operator new', no way to do it in
custom 'operator new' since the implicit multiplication has
already occurred by then.  It would have to be an explicitly typed
check at every use of 'operator new'.

-- 
Karl 2007-04-06 18:15