add some int overflow check macros to rangecheck.h

master
leitner 18 years ago
parent e76d0e4500
commit d22de55cee

@ -3,6 +3,7 @@
start over from the beginning because always new accept()ed start over from the beginning because always new accept()ed
connections came in and got newer, higher descriptors since the last connections came in and got newer, higher descriptors since the last
io_timeouted loop. (Dirk Engling) io_timeouted loop. (Dirk Engling)
add some int overflow check macros to rangecheck.h
0.25: 0.25:
array_allocate no longer truncates the array 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]? */ /* 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); 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 #undef __static
#endif #endif

@ -1,8 +1,54 @@
#include "rangecheck.h" #include "rangecheck.h"
#include <assert.h> #include <assert.h>
#include <stdio.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(short)==SHRT_MIN);
assert(__MAX(short)==SHRT_MAX);
assert(__MIN(unsigned short)==0);
assert(__MAX(unsigned short)==USHRT_MAX);
assert(__MIN(int)==INT_MIN);
assert(__MAX(int)==INT_MAX);
assert(__MIN(unsigned int)==0);
assert(__MAX(unsigned int)==UINT_MAX);
assert(__MIN(long)==LONG_MIN);
assert(__MAX(long)==LONG_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);
#endif
assert(__MIN(int32_t)==(int32_t)0x80000000);
assert(__MAX(int32_t)==0x7fffffff);
assert(__MIN(uint32_t)==0);
assert(__MAX(uint32_t)==0xffffffff);
}
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);
#endif
}
void check_rangeptrbuf() {
char buf[1000]; char buf[1000];
/* does range_ptrinbuf check all the incoming pointer cases right? */ /* does range_ptrinbuf check all the incoming pointer cases right? */
@ -83,7 +129,75 @@ int main() {
assert(range_str4inbuf(y,sizeof(y),y+5)==1); assert(range_str4inbuf(y,sizeof(y),y+5)==1);
assert(range_str4inbuf(y,sizeof(y),y+6)==0); assert(range_str4inbuf(y,sizeof(y),y+6)==0);
} }
}
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() {
check_minmax();
check_assign();
check_intof();
check_rangeptrbuf();
puts("all tests ok"); puts("all tests ok");
return 0; return 0;

Loading…
Cancel
Save