Integer Overflows on 64bit Platforms

From the Linux Kernel:

static ssize_t xxx_read( struct file *file, char *buf, size_t count, loff_t *ppos )

if (length < 0) {
    count = length;
    goto out;
}
if (file->f_pos >= length) {
    count = 0;
    goto out;
}
if (count + file->f_pos > length)
    count = length – file->f_pos;
end = count + file->f_pos;
if (copy_to_user(buf, (char *)page + file->f_pos, count)) {

length is a variable describing the length of an internal buffer. It is not under our control.  count is under our control, along with file->f_pos (by seeking in the file).

The vulnerability here is that count isn’t validated on its own merit, and that no check for an integer overflow occurs in the addition between count and file->f_pos.

if (count + file->f_pos > length)

The only validation is that count+file->f_pos has to be less than length. If it’s not less than length, count will be truncated. The goal is to make cause an integer overflow in the validation. If we passed a large value in count, near the limit of INT_MAX perhaps.

 Modern kernels in Linux have an explicit check that reading and writing to files is less than INT_MAX.  The validation of this is done in fs/read_write.c.

The addition of count and file->f_pos could possibly overflow under the right conditions.  The result is undefined according to the C specifications, but in many architectures the result is a well defined.  The result we want is a negative number.  This results in a value less than length.   A memory disclosure bug would occur later in the copy_to_user.

The catch is that count is of type size_t.  And size_t is actually an unsigned int.  The file pointer is of type loff_t.  And loff_t is a long long.  That is an unsigned int and a long long addition.  How can that overflow?  long long is bigger than unsigned int is’nt it?

Due to promotion, the result of the addition will be a long long. That is, when a long long number is added to an unsigned integer number, the result is stored as a long long number. It seems then that in the code above, we have a 32bit number (the unsigned int) under our control being added to a 64bit number (a long long), which will not cause an integer overflow

However, on some platforms, noteably 64bit platforms, size_t is actually 64bits. This means we have two 64bit numbers being added together, which definately allows an integer overflow to occur.

This was an interesting bug that I have come across in the past. It is interesting because it is only exploitable on particular architectures.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s