One big source of bugs in C is out-of-bounds accesses to arrays. We can add bounds checking using the following macro:
/* usually put in some project wide common header file */
#define bounds(_i, _n) (assert((unsigned)(_i) < (_n)), (_i))
To use this, index arrays using the following idiom:
array[bounds(i, ARRAY_SIZE)]
First, the index (i
) is compared against the array bound (ARRAY_SIZE
); then the value of the index is returned. If the index is negative, then the use of the unsigned
causes it to be a very large number, and will also cause the bounds check to fail. If assertions are turned off (i.e. NDEBUG
is defined), then bounds()
just returns the index.
We incorporate the bounds()
macro into accessor macros. For instance:
#define x_val_int_stack(_s,_i) \
x_vals_int_stack(_s)[bounds(_i,x_len_int_stack(_s)-1)]
#define val_int_stack(_s, _i) ((void)0,x_val_int_stack(_s,_i))
These indexing-accessor macros should be used in place of indexing an accessor macro. Thus:
void
pop_int_stack(
int_stack stack
)
{
VERIFY_INT_STACK(stack);
#if( CHECKSUM_INT_STACK )
{
int len = len_int_stack(stack);
x_chksum_int_stack(stack) ^= (unsigned)len;
len--;
x_chksum_int_stack(stack) ^=
(unsigned)val_int_stack(stack, len);
x_chksum_int_stack(stack) ^= (unsigned)len;
}
#endif
x_len_int_stack(stack)--;
VERIFY_INT_STACK(stack);
}
Of course, this is only used as an example. It doesn't do any additional error checking, since VERIFY_INT_STACK(stack)
would already have checked for len
being within bounds.