add some int overflow check macros to rangecheck.h

Felix von Leitner 16 years ago
parent 8f51b244c1
commit b5c41baade
  1. 1
  2. 37
  3. 116

@ -3,6 +3,7 @@
start over from the beginning because always new accept()ed
connections came in and got newer, higher descriptors since the last
io_timeouted loop. (Dirk Engling)
add some int overflow check macros to rangecheck.h
array_allocate no longer truncates the array

@ -54,6 +54,43 @@ int range_str2inbuf(const void* buf,size_t len,const void* stringstart);
/* does an UTF-32 string starting at "ptr" lie in buf[0..len-1]? */
int range_str4inbuf(const void* buf,size_t len,const void* stringstart);
/* I originally omitted addition and substraction because it appeared
* trivial. You could just add the two numbers and see if it was
* smaller than either of them. This always worked for me because I
* only cared about unsigned arithmetic, but for signed arithmetic,
* adding two numbers is undefined if the result does not fit in the
* int. gcc has started to actually use this undefinedness to screw
* you. The following code illustrates this:
* int a=INT_MAX,b=a+5;
* if (b<a) abort(); // whole statement optimized away by gcc 4.1
* // at this point, b<a
* So I decided to add some integer overflow protection functionality
* here for addition and subtraction, too. */
/* first, we need a type independent way to find the min and max values
* for each type, so the macros also work for integer types you defined
* yourself */
#define __MIN_UNSIGNED(type) ((type)0)
#define __MIN_SIGNED(type) (((type)-1)<<(sizeof(type)*8-1))
#define __MAX_UNSIGNED(type) (((type)-1))
#define __MAX_SIGNED_TEMP(type) (((type)1)<<(sizeof(type)*8-2))
#define __MAX_SIGNED(type) (__MAX_SIGNED_TEMP(type)+(__MAX_SIGNED_TEMP(type)-1))
/* we use <1 and not <0 to avoid a gcc warning */
#define __MAX(type) ((type)-1 < 1?__MAX_SIGNED(type):__MAX_UNSIGNED(type))
#define __MIN(type) ((type)-1 < 1?__MIN_SIGNED(type):__MIN_UNSIGNED(type))
#define assign(dest,src) ({ typeof(src) __x=src; typeof(dest) __y=__x; (__x==__y && ((__x<1) == (__y<1))?(int)(dest=__y),0:1); })
/* if a+b is defined and does not have an integer overflow, do c=a+b and
* return 0. Otherwise, return 1. */
#define add_of(c,a,b) ({ typeof(a) __a=a; typeof(b) __b=b; (__b)<1?((__MIN(typeof(c))-(__b)<=(__a))?assign(c,__a+__b):1) : ((__MAX(typeof(c))-(__b)>=(__a))?assign(c,__a+__b):1); })
#define sub_of(c,a,b) ({ typeof(a) __a=a; typeof(b) __b=b; (__b)<1?((__MAX(typeof(c))-(__b)>=(__a))?assign(c,__a-__b):1) : ((__MIN(typeof(c))+(__b)<=(__a))?assign(c,__a-__b):1); })
#undef __static

@ -1,8 +1,54 @@
#include "rangecheck.h"
#include <assert.h>
#include <stdio.h>
#include <inttypes.h>
#include <limits.h>
int main() {
void check_minmax() {
/* make sure the __MIN and __MAX macros work */
assert(__MIN(signed char)==-128);
assert(__MAX(signed char)==127);
assert(__MIN(unsigned char)==0);
assert(__MAX(unsigned char)==255);
assert(__MIN(unsigned short)==0);
assert(__MAX(unsigned short)==USHRT_MAX);
assert(__MIN(unsigned int)==0);
assert(__MAX(unsigned int)==UINT_MAX);
assert(__MIN(unsigned long)==0);
assert(__MAX(unsigned long)==ULONG_MAX);
#ifdef LLONG_MIN
assert(__MIN(long long)==LLONG_MIN);
assert(__MAX(long long)==LLONG_MAX);
assert(__MIN(unsigned long long)==0);
assert(__MAX(unsigned long long)==ULLONG_MAX);
void check_assign() {
int a;
assert(assign(a,5)==0 && a==5);
a=23; assert(assign(a,(unsigned int)-23)==1);
#ifdef LLONG_MIN
a=23; assert(assign(a,LLONG_MAX)==1);
void check_rangeptrbuf() {
char buf[1000];
/* does range_ptrinbuf check all the incoming pointer cases right? */
@ -83,7 +129,75 @@ int main() {
void check_intof() {
/* since the macros are type independent, we only check one signed and
* one unsigned type */
/* some checks are redundant, but we want to cover off-by-one cases */
int a;
a=0; assert(add_of(a,INT_MAX-3,1)==0 && a==INT_MAX-2);
a=0; assert(add_of(a,INT_MAX-3,5)==1);
a=0; assert(add_of(a,INT_MAX-3,3)==0 && a==INT_MAX);
a=0; assert(add_of(a,INT_MAX-3,4)==1);
a=0; assert(add_of(a,0,INT_MIN)==0 && a==INT_MIN);
a=0; assert(add_of(a,-1,INT_MIN)==1);
a=0; assert(add_of(a,1,INT_MAX)==1);
/* now let's see what happens if the addition has a wider data
* type than the destionation */
a=0; assert(add_of(a,0x100000000ll,-0x80000000ll)==1);
a=0; assert(add_of(a,0x100000000ll,-0x80000001ll)==0 && a==0x7fffffff);
a=0; assert(add_of(a,0x100000000ll,-10)==1);
a=0; assert(add_of(a,-0x90000000ll,0x10000000)==0 && a==0x80000000);
/* what if we add two ints, the result does not overflow but is
* still negative, but we assign it to an unsigned int? */
a=0; assert(add_of(a,0x7ffffff0,0x10)==1);
unsigned int a;
a=0; assert(add_of(a,UINT_MAX-3,1)==0 && a==UINT_MAX-2);
a=0; assert(add_of(a,UINT_MAX-3,5)==1);
a=0; assert(add_of(a,UINT_MAX-3,4)==1);
a=0; assert(add_of(a,2,-3)==1);
a=23; assert(add_of(a,2,-2)==0 && a==0);
a=0; assert(add_of(a,(int)0x80000000,(int)-2147483648)==0 && a==0);
a=0; assert(add_of(a,(int)0x7fffffff,(int)-2147483648)==1);
a=0; assert(add_of(a,1,UINT_MAX)==1);
/* now let's see what happens if the addition has a wider data
* type than the destionation */
a=0; assert(add_of(a,0x100000000ll,-5)==0 && a==4294967291);
a=0; assert(add_of(a,-0x100000010ll,5)==1);
a=0; assert(add_of(a,0x100000010ll,-5)==1);
/* what if we add two ints, the result does not overflow but is
* still negative, but we assign it to an unsigned int? */
a=0; assert(add_of(a,-10,-5)==1);
int a;
a=0; assert(sub_of(a,10,5)==0 && a==5);
a=0; assert(sub_of(a,5,10)==0 && a==-5);
a=0; assert(sub_of(a,INT_MIN,10)==1);
a=0; assert(sub_of(a,INT_MIN,-10)==0 && a==INT_MIN+10);
a=0; assert(sub_of(a,INT_MAX,-10)==1);
a=0; assert(sub_of(a,INT_MAX,10)==0 && a==INT_MAX-10);
int main() {
puts("all tests ok");
return 0;