xorl %eax, %eax

Linux kernel MD Driver NULL Pointer Dereference

leave a comment »

I was having a look at 2.6.30.2’s ChangeLog and noticed this one. The bug is easy to understand, here is one of the buggy functions from 2.6.30’s drivers/md/md.c:

static ssize_t
suspend_lo_store(mddev_t *mddev, const char *buf, size_t len)
{
        char *e;
        unsigned long long new = simple_strtoull(buf, &e, 10);

        if (mddev->pers->quiesce == NULL)
                return -EINVAL;
        if (buf == e || (*e && *e != '\n'))
                return -EINVAL;
        if (new >= mddev->suspend_hi ||
            (new > mddev->suspend_lo && new < mddev->suspend_hi)) {
                mddev->suspend_lo = new;
                mddev->pers->quiesce(mddev, 2);
                return len;
        } else
                return -EINVAL;
}
static struct md_sysfs_entry md_suspend_lo =
__ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store);

As you can easily deduce from the two last lines, this can be reached through /sys/block/mdX/md/suspend_lo sysfs file. Of course as you can see that its default mode is 0644 and that might make this non-exploitable unless some distribution has different mode. Anyhow, the first if condition checks that mddev->pers->quiesce is not NULL. However, if the array is not active, then mddev->pers which is an mdk_personality structure describing the device and it includes numerous function pointers, could be NULL. In this case, the first if will be bypassed since mddev->pers->quiesce will definitely not going to be NULL, it will be NULL + the offset until quiesce which is just a void * if we have a look at the equivalent structure in drivers/md/md.h. This can result in a NULL pointer dereference or if it isn’t triggered by that time, then it will be triggered when mddev function pointer of mddev->pers->quiesce is called.
This was patched by adding the missing check.

        unsigned long long new = simple_strtoull(buf, &e, 10);

-      if (mddev->pers->quiesce == NULL)
+      if (mddev->pers == NULL ||
+          mddev->pers->quiesce == NULL)
               return -EINVAL;

And the exact same vulnerability was also present in suspend_hi_store() of the same file.

Written by xorl

July 21, 2009 at 21:48

Posted in linux, vulnerabilities

Leave a comment