xorl %eax, %eax

CVE-2010-3067: Linux kernel io_submit() Integer Overflow

leave a comment »

This bug was discovered and disclosed by Tavis Ormandy of Google, Inc. The vulnerable code can be found at fs/aio.c and it affects Linux kernel prior to 2.6.36-rc4-next-20100915 release. Here is the buggy code.

long do_io_submit(aio_context_t ctx_id, long nr,
                  struct iocb __user *__user *iocbpp, bool compat)
{
        struct kioctx *ctx;
        long ret = 0;
        int i;
        struct hlist_head batch_hash[AIO_BATCH_HASH_SIZE] = { { 0, }, };

        if (unlikely(nr < 0))
                return -EINVAL;

        if (unlikely(!access_ok(VERIFY_READ, iocbpp, (nr*sizeof(*iocbpp)))))
                return -EFAULT;
    ...
        /*
         * AKPM: should this return a partial result if some of the IOs were
         * successfully submitted?
         */
        for (i=0; i<nr; i++) {
    ...
                if (unlikely(__get_user(user_iocb, iocbpp + i))) {
    ...
        put_ioctx(ctx);
        return i ? i : ret;
}

The problem with this system call’s handling code is that the access_ok() C macro might result in incorrectly checking an invalid address space since the multiplication of ‘nr’ (which is the number of submitted IOCBs) might result in an integer overflow. The subsequent call to __get_user() could lead in information leak of kernel memory since __get_user() doesn’t perform any range checks such as get_user(). The above code is reachable through io_submit() system call which is defined in the same source code file like this:

/* sys_io_submit:
 *      Queue the nr iocbs pointed to by iocbpp for processing.  Returns
 *      the number of iocbs queued.  May return -EINVAL if the aio_context
 *      specified by ctx_id is invalid, if nr is < 0, if the iocb at
 *      *iocbpp[0] is not properly initialized, if the operation specified
 *      is invalid for the file descriptor in the iocb.  May fail with
 *      -EFAULT if any of the data structures point to invalid data.  May
 *      fail with -EBADF if the file descriptor specified in the first
 *      iocb is invalid.  May fail with -EAGAIN if insufficient resources
 *      are available to queue any iocbs.  Will return 0 if nr is 0.  Will
 *      fail with -ENOSYS if not implemented.
 */
SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr,
                struct iocb __user * __user *, iocbpp)
{
        return do_io_submit(ctx_id, nr, iocbpp, 0);
}

Now, to fix this bug the following patch was applied to the above code.

                return -EINVAL;
 
+       if (unlikely(nr > LONG_MAX/sizeof(*iocbpp)))
+               nr = LONG_MAX/sizeof(*iocbpp);
+
        if (unlikely(!access_ok(VERIFY_READ, iocbpp, (nr*sizeof(*iocbpp)))))

As you can read, it checks for possible integer overflows before reaching the access_ok() range check.

Written by xorl

December 17, 2010 at 21:42

Posted in bugs, linux

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