summaryrefslogtreecommitdiff
path: root/rangecheck.h
blob: f15ed148e5eb1f1ca624ce541af77bbb0e450910 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/* this header file comes from libowfat, http://www.fefe.de/libowfat/ */
#ifndef RANGECHECK_H
#define RANGECHECK_H

#include <inttypes.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

/* We are trying to achieve that gcc has to inline the function and we
 * don't want it to emit a copy of the function.  This can be done with
 * static inline or with extern inline.  static inline tells gcc to not
 * emit a copy unless someone is using & to take a pointer, which nobody
 * is ever supposed to do.  extern inline tells gcc to not ever emit a
 * copy.
 *
 * Unfortunately, the C99 standard defines extern inline to mean "always
 * emit a copy for external reference", so this causes duplicate symbol
 * linking errors.  gcc signals C99 inline expansion mode by defining
 * __GNUC_STDC_INLINE__ and it then has an attribute gnu_inline to
 * switch back to GNU behavior.  So that's what we are doing.  Taking
 * the address of one of these functions is considered a user error.
 *
 * We are so anal about inlining here because these checks can in most
 * cases be optimized away.  In particular, if you call this function
 * often, gcc can see that some of the basic checks are done repeatedly
 * and not do them again.  But this only works if the function is
 * inlined. */

#if defined(__GNUC_STDC_INLINE__)
#define __gnuinline __attribute__((gnu_inline))
#else
#define __gnuinline
#endif

#if defined(__GNUC__) && !defined(__NO_INLINE__) && !defined(__clang__)
#define __static extern
#else
#define __static static
#endif

#if !defined(__GNUC__) || (__GNUC__ < 3)
#define __builtin_expect(foo,bar) (foo)
#define __expect(foo,bar) (foo)
#else
#define __expect(foo,bar) __builtin_expect((long)(foo),bar)
#endif

#if defined(__GNUC__) && !defined(__likely)
#define __likely(foo) __expect((foo),1)
#define __unlikely(foo) __expect((foo),0)
#endif

#if !defined(__likely)
#define __likely(foo) (foo)
#define __unlikely(foo) (foo)
#endif

/* return 0 for range error / overflow, 1 for ok */

/* we assume the normal case is that the checked value is in range */

/* does ptr point to one of buf[0], buf[1], ... buf[len-1]? */
__static inline __gnuinline int range_ptrinbuf(const void* buf,size_t len,const void* ptr) {
  register const char* c=(const char*)buf;	/* no pointer arithmetic on void* */
  return __likely(c &&		/* is buf non-NULL? */
	  ((uintptr_t)c)+len>(uintptr_t)c &&	/* gcc 4.1 miscompiles without (uintptr_t) */
			/* catch integer overflows and fail if buffer is 0 bytes long */
			/* because then ptr can't point _in_ the buffer */
	  (uintptr_t)((const char*)ptr-c)<len);	/* this one is a little tricky.
     "ptr-c" checks the offset of ptr in the buffer is inside the buffer size.
     Now, ptr-c can underflow; say it is -1.  When we cast it to uintptr_t, it becomes
     a very large number. */
}

/* same thing, but the buffer is specified by a pointer to the first
 * byte (Min) and a pointer after the last byte (Max). */
__static inline __gnuinline int range_ptrinbuf2(const void* Min,const void* Max,const void* ptr) {
  return __likely(Min && ptr>=Min && ptr<Max);
  /* Min <= Max is implicitly checked here */
}

/* Is this a plausible buffer?
 * Check whether buf is NULL, and whether buf+len overflows.
 * Does NOT check whether buf has a non-zero length! */
__static inline __gnuinline int range_validbuf(const void* buf,size_t len) {
  return __likely(buf && (uintptr_t)buf+len>=(uintptr_t)buf);
}

/* same thing but buffer is given as pointer to first byte (Min) and
 * pointer beyond last byte (Max).  Again, an 0-size buffer is valid. */
__static inline __gnuinline int range_validbuf2(const void* Min,const void* Max) {
  return __likely(Min && Max>=Min);
}

/* is buf2[0..len2-1] inside buf1[0..len-1]? */
__static inline __gnuinline int range_bufinbuf(const void* buf1,size_t len1,const void* buf2,size_t len2) {
  return range_validbuf(buf1,len1) &&
         range_validbuf(buf2,len2) &&
	 __likely(buf1<=buf2 &&
	 (ptrdiff_t)buf1+len1>=(ptrdiff_t)buf2+len2);
}

/* does an array of "elements" members of size "membersize" starting at
 * "arraystart" lie inside buf1[0..len-1]? */
int range_arrayinbuf(const void* buf,size_t len,
		     const void* arraystart,size_t elements,size_t membersize);

/* does an ASCIIZ string starting at "ptr" lie in buf[0..len-1]? */
int range_strinbuf(const void* buf,size_t len,const void* stringstart);

/* does an UTF-16 string starting at "ptr" lie in buf[0..len-1]? */
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. */

/* usage:
 * if (add_of(dest,a,b)) return EINVAL;		// dest=a+b;
 * if (sub_of(dest,a,b)) return EINVAL;		// dest=a-b;
 * if (assign(dest,some_int)) return EINVAL;	// dest=some_int;
 */

/* two important assumptions:
 *   1. the platform is using two's complement
 *   2. there are 8 bits in a byte
 */

#define __HALF_MAX_SIGNED(type) ((type)1 << (sizeof(type)*8-2))
#define __MAX_SIGNED(type) (__HALF_MAX_SIGNED(type) - 1 + __HALF_MAX_SIGNED(type))
#define __MIN_SIGNED(type) (-1 - __MAX_SIGNED(type))

/* we use <1 and not <0 to avoid a gcc warning */
#define __MIN(type) ((type)-1 < 1?__MIN_SIGNED(type):(type)0)
#define __MAX(type) ((type)~__MIN(type))

#define assign(dest,src) ({ typeof(src) __x=(src); typeof(dest) __y=__x; (__x==__y && ((__x<1) == (__y<1))?(void)((dest)=__y),0:1); })

/* gcc 5 now has nice builtins we can use instead */
#if defined(__GNUC__) && (__GNUC__ >= 5)

#define add_of(c,a,b) __builtin_add_overflow(a,b,&c)
#define sub_of(c,a,b) __builtin_sub_overflow(a,b,&c)

#else

/* 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(a+b))-(__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(a+b))+__b>=__a)?assign(c,__a-__b):1) : ((__MIN(typeof(c))+__b<=__a)?assign(c,__a-__b):1); })

#endif

#undef __static

#ifdef __cplusplus
}
#endif

#endif