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)))
