Sloppy coding can be done in any language, but C and C++ have 3 features that
aggravate the problem:

1. The "array=pointer" idiom. Given a parameter which is an array, you can't ask
at run-time how big the array is - you have to do extra work and pass the size
in an additional parameter (whereas most languages pass the size of an array
automatically as part of the array). So doing buffer overflow checks requires
more than just inserting the check - you have to make sure that an extra "array
size" parameter is passed all the way down the call stack. [C and C++ cannot be
considered strongly typed, in part because of the lack of distinction between
pointers and arrays].

2. By permitting pointer arithmetic, C and C++ encourage you to pass a pointer
into the middle of a buffer, rather than passing the [start of the] buffer and
an index into it, which makes bounds checking even more tedious to do (you have
to pass a pointer to one-past-the-end-of-the-array as well, and even then this
is less useful than having an index and a limit).

3. The lack of automatic bounds checking. Of course, run-time bounds checking is
by no means a complete solution, but it does at least prevent a buffer overflow
being used as a security attack, turning it into a denial-of-service attack
instead.

Apart from the obvious solution of choosing another language, there are at least
two ways to avoid these problems in C++:

1. Ban arrays (to quote Marshall Cline's "C++ FAQ Lite", arrays are evil!). Use
classes from the STL, or another template library instead. Arrays should be used
only in the template library, where their use can be controlled.

2. If you really must have naked arrays, ban the use of indexing and arithmetic
on naked pointers to arrays (i.e. if p is a pointer, then p[x], p+x, p-x, ++p
and --p are all banned). Instead, refer to arrays using instances of a template
class "Array<X>" that encapsulates both the pointer (an X*) and the limit (an
unsigned int). Such an object needs only 2 words of storage (compared to 1 word
for a naked pointer), so it can assigned and passed by value. You can provide an
operator to return the value of the limit, and an indexing operator (with
optional bounds checking). If you really must, you can even implement "pointer
arithmetic" operators for the class which update the limit at the same time as
updating the pointer.

David Crocker
Consultancy & contracting for dependable software development
www.eschertech.com






Reply via email to