ChangeSet 1.1673.8.23, 2004/03/25 17:19:38-08:00, arjanv@redhat.com [PATCH] USB: usb hiddev stack usage patch Patch below fixes some obscenely high stack uage; struct hiddev_usage_ref_multi is well over 4Kb in size so really doesn't belong on the stack. drivers/usb/input/hiddev.c | 80 +++++++++++++++++++++++++++------------------ 1 files changed, 49 insertions(+), 31 deletions(-) diff -Nru a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c --- a/drivers/usb/input/hiddev.c Wed Apr 14 14:38:25 2004 +++ b/drivers/usb/input/hiddev.c Wed Apr 14 14:38:25 2004 @@ -403,8 +403,8 @@ struct hiddev_collection_info cinfo; struct hiddev_report_info rinfo; struct hiddev_field_info finfo; - struct hiddev_usage_ref_multi uref_multi; - struct hiddev_usage_ref *uref = &uref_multi.uref; + struct hiddev_usage_ref_multi *uref_multi=NULL; + struct hiddev_usage_ref *uref; struct hiddev_devinfo dinfo; struct hid_report *report; struct hid_field *field; @@ -576,26 +576,31 @@ return 0; case HIDIOCGUCODE: - if (copy_from_user(uref, (void *) arg, sizeof(*uref))) - return -EFAULT; + uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); + if (!uref_multi) + return -ENOMEM; + uref = &uref_multi->uref; + if (copy_from_user(uref, (void *) arg, sizeof(*uref))) + goto fault; rinfo.report_type = uref->report_type; rinfo.report_id = uref->report_id; if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; + goto inval; if (uref->field_index >= report->maxfield) - return -EINVAL; + goto inval; field = report->field[uref->field_index]; if (uref->usage_index >= field->maxusage) - return -EINVAL; + goto inval; uref->usage_code = field->usage[uref->usage_index].hid; if (copy_to_user((void *) arg, uref, sizeof(*uref))) - return -EFAULT; + goto fault; + kfree(uref_multi); return 0; case HIDIOCGUSAGE: @@ -603,42 +608,46 @@ case HIDIOCGUSAGES: case HIDIOCSUSAGES: case HIDIOCGCOLLECTIONINDEX: + uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); + if (!uref_multi) + return -ENOMEM; + uref = &uref_multi->uref; if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { - if (copy_from_user(&uref_multi, (void *) arg, + if (copy_from_user(uref_multi, (void *) arg, sizeof(uref_multi))) - return -EFAULT; + goto fault; } else { if (copy_from_user(uref, (void *) arg, sizeof(*uref))) - return -EFAULT; + goto fault; } if (cmd != HIDIOCGUSAGE && cmd != HIDIOCGUSAGES && uref->report_type == HID_REPORT_TYPE_INPUT) - return -EINVAL; + goto inval; if (uref->report_id == HID_REPORT_ID_UNKNOWN) { field = hiddev_lookup_usage(hid, uref); if (field == NULL) - return -EINVAL; + goto inval; } else { rinfo.report_type = uref->report_type; rinfo.report_id = uref->report_id; if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; + goto inval; if (uref->field_index >= report->maxfield) - return -EINVAL; + goto inval; field = report->field[uref->field_index]; if (uref->usage_index >= field->maxusage) - return -EINVAL; + goto inval; if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { - if (uref_multi.num_values >= HID_MAX_USAGES || + if (uref_multi->num_values >= HID_MAX_USAGES || uref->usage_index >= field->maxusage || - (uref->usage_index + uref_multi.num_values) >= field->maxusage) - return -EINVAL; + (uref->usage_index + uref_multi->num_values) >= field->maxusage) + goto inval; } } @@ -646,31 +655,40 @@ case HIDIOCGUSAGE: uref->value = field->value[uref->usage_index]; if (copy_to_user((void *) arg, uref, sizeof(*uref))) - return -EFAULT; - return 0; + goto fault; + goto goodreturn; case HIDIOCSUSAGE: field->value[uref->usage_index] = uref->value; - return 0; + goto goodreturn; case HIDIOCGCOLLECTIONINDEX: + kfree(uref_multi); return field->usage[uref->usage_index].collection_index; case HIDIOCGUSAGES: - for (i = 0; i < uref_multi.num_values; i++) - uref_multi.values[i] = + for (i = 0; i < uref_multi->num_values; i++) + uref_multi->values[i] = field->value[uref->usage_index + i]; - if (copy_to_user((void *) arg, &uref_multi, - sizeof(uref_multi))) - return -EFAULT; - return 0; + if (copy_to_user((void *) arg, uref_multi, + sizeof(*uref_multi))) + goto fault; + goto goodreturn; case HIDIOCSUSAGES: - for (i = 0; i < uref_multi.num_values; i++) + for (i = 0; i < uref_multi->num_values; i++) field->value[uref->usage_index + i] = - uref_multi.values[i]; - return 0; + uref_multi->values[i]; + goto goodreturn; } +goodreturn: + kfree(uref_multi); return 0; +fault: + kfree(uref_multi); + return -EFAULT; +inval: + kfree(uref_multi); + return -EINVAL; case HIDIOCGCOLLECTIONINFO: if (copy_from_user(&cinfo, (void *) arg, sizeof(cinfo)))