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;
...
}