RUS-CERT Advisory 2002-08:01: Incorrect integer overflow detection in C code

Type securityvulns
Reporter Securityvulns
Modified 2002-08-08T00:00:00


Incorrect integer overflow detection in C code

A widely used method of detecting integer overflows results in undefined behavior according to the C standard.

Who Should Read This Document

This advisory deals with with details of the C programming language. It is targeted at C programmers.

Systems Affected

 * Systems which run binaries compiled by C compilers which
   perform particular optimizations.

Currently, no such systems are known to RUS-CERT.

Attack Requirements And Impact

Attack requirements and impact depend on the application.


According to the C standard (ISO/IEC 9899:1999, section 6.5, paragraph 5), overflow in integer expressions results in undefined behavior:

 If an exceptional condition occurs during the evaluation of
 an expression (that is, if the result is not mathematically
 defined or not in the range of representable values for its
 type), the behavior is undefined.

For unsigned integer types, the C standard later on defines that arithmetic can never overflow (ibid, parapgraph 9):

 [...] A computation involving unsigned operands can never
 overflow, because a result that cannot be represented by
 the resulting unsigned integer type is reduced modulo the
 number that is one greater than the largest value that can
 be represented by the resulting type.

However, for signed integer types, there is no such guarantee. As a result, it is not possible to check for signed integer arithmetic overflow after the overflow occured. This is problematic especially if such checks are introduced in order to prevent buffer overflows (see the example below).

RUS-CERT is not aware of a concrete example of a code/compiler pair which shows the problem. However, compilers will perform more and more optimizations (such as range tracking for variables), and this problem might become practical in the future.


The following source code excerpt comes from the OpenSSL library, after the fix for CAN-2002-0659 has been applied (comments in italics have been added):

static int asn1_get_length(unsigned char pp, int inf, long rl, int max) { unsigned char p= pp; long ret=0; int i;

       if (max-- < 1) return(0);
       if (*p == 0x80)
               i= *p&0x7f;
               if (*(p++) & 0x80)
                       if (i > sizeof(long))
                               return 0;
                       if (max-- == 0) return(0);

/ (1) / while (i-- > 0) { / (2) / ret<<=8L; ret|= (p++); if (max-- == 0) return(0); } } else ret=i; } / (3) / if (ret < 0) return 0; pp=p; *rl=ret; return(1); }

This routine is used to extract an encoded long value of varying length, stored with a length prefix and the actual data in big endian format. Suppose that the encoded integer is stored using the maximum number of bytes. During the last iteration of the while loop (1), assignment expression (2) copies the first byte to the most significant byte in ret. If the value of this byte exceeds 127, the range of values representable in type long is exceeded, at least on typical C implementations, leading to integer overflow and undefined behavior.

If such an overflow does not occur, the check (3) does not succeed, and the return statement ist not executed. However, after undefined behavior has occured, all behavior is allowed by the C standard, so a conforming implementation may choose not to execute the return in this case, either. As a result, the check (3) can be ommitted by a conforming C implementation. However, this check has been added to eliminate a security risk -- therefore this is obviously dangerous.

How likely is it that actual compilers will perform such an optimization in the future (or already do)? Range tracking on the ret variable could inform compilers that only positive values are assigned to it. A range tracking feature does not seem too esoteric, so compiler vendors will probably implement it one day.

As suggested below, the correct approach uses an unsigned long (unsigned arithmetic can never overflow, but in this case, the wraparound is avoided, at least if objects of type unsigned long contain no padding bits). In addition, the check (3) is replaced with one against LONG_MAX. The assignment (4') does not trigger undefined behavior because the check (3') ensures that the value is representable in type long.

include <limits.h>

static int asn1_get_length(unsigned char pp, int inf, long rl, int max) { unsigned char p= pp; unsigned long ret=0; int i;

       if &#40;max-- &lt; 1&#41; return&#40;0&#41;;
       if &#40;*p == 0x80&#41;
               i= *p&amp;0x7f;
               if &#40;*&#40;p++&#41; &amp; 0x80&#41;
                       if &#40;i &gt; sizeof&#40;long&#41;&#41;
                               return 0;
                       if &#40;max-- == 0&#41; return&#40;0&#41;;

/ (1') / while (i-- > 0) { / (2') / ret<<=8L; ret|= (p++); if (max-- == 0) return(0); } } else ret=i; } / (3') / if (ret > LONG_MAX) return 0; pp=p; / (4') / *rl=(long)ret; return(1); }

The ap_get_chunk_size routine featured a similar overflow bug (CAN-2002-0392), and again, the developers tried to check for the overflow after it had occured. However, in this case, it seems to be less likely that future compiler will optimize away the crucial check.


RUS-CERT (http://CERT.Uni-Stuttgart.DE/) is the Computer Emergency Response Team located at the Computing Center (RUS) of the University of Stuttgart, Germany.

Revision History

This document was published on 2002-08-05.

URL For This Document


-- Florian Weimer Weimer@CERT.Uni-Stuttgart.DE University of Stuttgart http://CERT.Uni-Stuttgart.DE/people/fw/ RUS-CERT fax +49-711-685-5898