telnetgmike / google-security-research

Automatically exported from code.google.com/p/google-security-research
0 stars 0 forks source link

Magic Mouse HID device driver overflow #100

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
Stack overflow in Magic Mouse HID driver

The following structure from the hid-magicmouse.c file defines the hid_driver 
structure used to register the device driver:

The bug presented here is in the magicmouse_raw_event function. Specifically 
when more than 64 bytes of input are passed to this function. This *should* be 
possible with an xhci or ehci USB interface. Either should allow for 512 bytes 
of input instead of the expected 64 byte limit assumed by the driver. 

Here is the function in it’s entirety (comments in red) as it appears in the 
linux 3.16 kernel:

The structure defined on the stack where the overflow occurs is:

struct magicmouse_sc {
    struct input_dev *input;
    unsigned long quirks;

    int ntouches;
    int scroll_accel;
    unsigned long scroll_jiffies;

    struct {
        short x;
        short y;
        short scroll_x;
        short scroll_y;
        u8 size;
    } touches[16];
    int tracking_ids[16];
};

// contents of data are attacker controlled up to a size limit of 4K (citation 
needed)
static int magicmouse_raw_event(struct hid_device *hdev,
        struct hid_report *report, u8 *data, int size)
{
    struct magicmouse_sc *msc = hid_get_drvdata(hdev);
    struct input_dev *input = msc->input;
    int x = 0, y = 0, ii, clicks = 0, npoints;

    switch (data[0]) {
    // Three supported types for first byte of data
    case TRACKPAD_REPORT_ID:
        /* Expect four bytes of prefix, and N*9 bytes of touch data. */
        // No upper bound on size
        if (size < 4 || ((size - 4) % 9) != 0)
            return 0;
        // 4086 is the maximum valid value for size, 512 - 4 / 9
        // leading to possible npoints = 56
        npoints = (size - 4) / 9;
        msc->ntouches = 0;
        for (ii = 0; ii < npoints; ii++)
            // 2nd argument loops from 0 - 56
magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);

        clicks = data[1];

        /* The following bits provide a device specific timestamp. They
         * are unused here.
         *
         * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10;
         */
        break;
    case MOUSE_REPORT_ID:
        /* Expect six bytes of prefix, and N*8 bytes of touch data. */
        if (size < 6 || ((size - 6) % 8) != 0)
            return 0;
        npoints = (size - 6) / 8;
        msc->ntouches = 0;

        //possible npoints = 63

        for (ii = 0; ii < npoints; ii++)
            magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);

        /* When emulating three-button mode, it is important
         * to have the current touch information before
         * generating a click event.
         */
        x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22;
        y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;
        clicks = data[3];

        /* The following bits provide a device specific timestamp. They
         * are unused here.
         *
         * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
         */
        break;
    case DOUBLE_REPORT_ID:
        /* Sometimes the trackpad sends two touch reports in one
         * packet.
         */
        magicmouse_raw_event(hdev, report, data + 2, data[1]);
        magicmouse_raw_event(hdev, report, data + 2 + data[1],
            size - 2 - data[1]);
        break;
    default:
        return 0;
    }

    if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
        magicmouse_emit_buttons(msc, clicks & 3);
        input_report_rel(input, REL_X, x);
        input_report_rel(input, REL_Y, y);
    } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
        input_report_key(input, BTN_MOUSE, clicks & 1);
        input_mt_report_pointer_emulation(input, true);
    }

    input_sync(input);
    return 1;
}

The function that does the writing beyond the buffer is magicmouse_emit_touch:

static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 
*tdata)
{
    struct input_dev *input = msc->input;
    int id, x, y, size, orientation, touch_major, touch_minor, state, down;

    if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
        id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
        x = (tdata[1] << 28 | tdata[0] << 20) >> 20;
        y = -((tdata[2] << 24 | tdata[1] << 16) >> 20);
        size = tdata[5] & 0x3f;
        orientation = (tdata[6] >> 2) - 32;
        touch_major = tdata[3];
        touch_minor = tdata[4];
        state = tdata[7] & TOUCH_STATE_MASK;
        down = state != TOUCH_STATE_NONE;
    } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
        id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf;
        x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
        y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
        size = tdata[6] & 0x3f;
        orientation = (tdata[7] >> 2) - 32;
        touch_major = tdata[4];
        touch_minor = tdata[5];
        state = tdata[8] & TOUCH_STATE_MASK;
        down = state != TOUCH_STATE_NONE;
    }

    /* Store tracking ID and other fields. */
    // structure defines int tracking_ids[16]; raw_id can exceed these bounds
    msc->tracking_ids[raw_id] = id;
    msc->touches[id].x = x;
    msc->touches[id].y = y;
    msc->touches[id].size = size;

...
}

Original issue reported on code.google.com by scvi...@google.com on 25 Aug 2014 at 5:41

GoogleCodeExporter commented 9 years ago

Original comment by scvi...@google.com on 25 Aug 2014 at 5:49

GoogleCodeExporter commented 9 years ago

Original comment by scvi...@google.com on 5 Sep 2014 at 8:26

GoogleCodeExporter commented 9 years ago

Original comment by haw...@google.com on 11 Sep 2014 at 8:30

GoogleCodeExporter commented 9 years ago

Original comment by scvi...@google.com on 13 Jan 2015 at 12:19