xorl %eax, %eax

CVE-2008-1517: Apple Mac OS X (XNU) Missing Array Index Validation

leave a comment »

This bug was discovered and reported by mu-b and it affects Mac OS X 10.5 before 10.5.7 release. The vulnerability is located at bsd/kern/pthread_synch.c. Here is the first buggy routine:

static int
workqueue_additem(struct workqueue *wq, int prio, user_addr_t item)
{
    struct workitem    *witem;
    struct workitemlist *wl;

    wl = (struct workitemlist *)&wq->wq_list[prio];

    if (TAILQ_EMPTY(&wl->wl_freelist))
        return (ENOMEM);

    witem = (struct workitem *)TAILQ_FIRST(&wl->wl_freelist);
    TAILQ_REMOVE(&wl->wl_freelist, witem, wi_entry);

    witem->wi_item = item;
    TAILQ_INSERT_TAIL(&wl->wl_itemlist, witem, wi_entry);

    if (wq->wq_itemcount == 0) {
            microuptime(&wq->wq_lastran_ts);
        wq->wq_stalled_count = 0;
    }
    wq->wq_itemcount++;

    return (0);
}

This function is used by workq_ops() when option WQOPS_QUEUE_ADD is set to add a new item to the workqueue list. Integer prio is used to find the right workqueue and it is derived directly from user controlled argument as you can see in the following snippet:

int
workq_ops(struct proc *p, struct workq_ops_args  *uap, __unused register_t *retval)
{
    int options     = uap->options;
    int prio     = uap->prio;    /* should  be used to find the right workqueue */
       ...
    switch (options) {
        case WQOPS_QUEUE_ADD: {
       ...
            error = workqueue_additem(wq, prio, item);

The bug is of course:

wl = (struct workitemlist *)&wq->wq_list[prio];

In the workqueue_additem() since the user has complete control over the index value prio. The same vulnerability was also present at the equivalent remove operation which could be reached by the WQOPS_QUEUE_REMOVE option of workq_ops() function. Here is this function:

static int
workqueue_removeitem(struct workqueue *wq, int prio, user_addr_t item)
{
    struct workitem *witem;
    struct workitemlist *wl;
    int error = ESRCH;

    wl = (struct workitemlist *)&wq->wq_list[prio];

    TAILQ_FOREACH(witem, &wl->wl_itemlist, wi_entry) {
        if (witem->wi_item == item) {
            TAILQ_REMOVE(&wl->wl_itemlist, witem, wi_entry);
            wq->wq_itemcount--;

            witem->wi_item = (user_addr_t)0;
            TAILQ_INSERT_HEAD(&wl->wl_freelist, witem, wi_entry);

            error = 0;
            break;
        }
    }
    if (wq->wq_itemcount == 0)
            wq->wq_flags &= ~(WQ_ADD_TO_POOL | WQ_TIMER_WATCH);

    return (error);
}

Once again, integer prio is used directly from the user’s parameter. The above code was taken from xnu-1228.7.58 which is part of Apple Mac OS X 10.5.5. Those issues were fixed in xnu-1228.12.14 which is used in Apple Mac OS X 10.5.7 by performing a range check workq_ops() for the two options that require prio, the first patch was:


+                       if ((prio < 0) || (prio >= 5))
+                               return (EINVAL);
+
                        workqueue_lock_spin(p);

And consequently, the remove option now includes this check:

                case WQOPS_QUEUE_REMOVE: {

+                       if ((prio < 0) || (prio >= 5))
+                               return (EINVAL);
+
                        workqueue_lock_spin(p);
                   workqueue_lock_spin(p);

In addition to this, mu-b recently released an exploit code for that vulnerability which you can find here.

Written by xorl

June 9, 2009 at 11:41

Posted in bugs, osx

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