Vulnerability: Array index error in the logi_dj_raw_event function in drivers/hid/hid-logitech-dj.c in the Linux kernel before 3.16.2 allows physically proximate attackers to execute arbitrary code or cause a denial of service (invalid kfree) via a crafted device that provides a malformed REPORT_TYPE_NOTIF_DEVICE_UNPAIRED value.
Vulnerability ID: CVE-2014-3182
CWE: CWE-119
Severity score: 10
Project: https://github.com/torvalds/linux
Published: 2014-09-28
File: drivers/hid/hid-logitech-dj.c
Commit message: HID: logitech: perform bounds checking on device_id early enough

device_index is a char type and the size of paired_dj_deivces is 7
elements, therefore proper bounds checking has to be applied to
device_index before it is used.

We are currently performing the bounds checking in
logi_dj_recv_add_djhid_device(), which is too late, as malicious device
could send REPORT_TYPE_NOTIF_DEVICE_UNPAIRED early enough and trigger the
problem in one of the report forwarding functions called from
logi_dj_raw_event().

Fix this by performing the check at the earliest possible ocasion in
logi_dj_raw_event().

Cc: stable@vger.kernel.org
Reported-by: Ben Hawkes <hawkes@google.com>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Commit author: Jiri Kosina

Vulnerable version (before fix):
 static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, 		return; 	}  	if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) || 	    (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) { 		dev_err(&djrcv_hdev->dev, "%s: invalid device index:%d\n", 			__func__, dj_report->device_index); 		return; 	}  	if (djrcv_dev->paired_dj_devices[dj_report->device_index]) { 		/* The device is already known. No need to reallocate it. */ 		dbg_hid("%s: device is already known\n", __func__); static int logi_dj_raw_event(struct hid_device *hdev, 	 * device (via hid_input_report() ) and return 1 so hid-core does not do 	 * anything else with it. 	 */  	spin_lock_irqsave(&djrcv_dev->lock, flags); 	if (dj_report->report_id == REPORT_ID_DJ_SHORT) {

Fixed version (after fix):
 static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, 		return; 	}  	if (djrcv_dev->paired_dj_devices[dj_report->device_index]) { 		/* The device is already known. No need to reallocate it. */ 		dbg_hid("%s: device is already known\n", __func__); static int logi_dj_raw_event(struct hid_device *hdev, 	 * device (via hid_input_report() ) and return 1 so hid-core does not do 	 * anything else with it. 	 */ 	if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) || 	    (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) { 		dev_err(&hdev->dev, "%s: invalid device index:%d\n", 				__func__, dj_report->device_index); 		return false; 	}  	spin_lock_irqsave(&djrcv_dev->lock, flags); 	if (dj_report->report_id == REPORT_ID_DJ_SHORT) {

Patch:
patch: @@ -238,13 +238,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
 		return;
 	}
 
-	if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
-	    (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
-		dev_err(&djrcv_hdev->dev, "%s: invalid device index:%d\n",
-			__func__, dj_report->device_index);
-		return;
-	}
-
 	if (djrcv_dev->paired_dj_devices[dj_report->device_index]) {
 		/* The device is already known. No need to reallocate it. */
 		dbg_hid("%s: device is already known\n", __func__);
@@ -690,6 +683,12 @@ static int logi_dj_raw_event(struct hid_device *hdev,
 	 * device (via hid_input_report() ) and return 1 so hid-core does not do
 	 * anything else with it.
 	 */
+	if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
+	    (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
+		dev_err(&hdev->dev, "%s: invalid device index:%d\n",
+				__func__, dj_report->device_index);
+		return false;
+	}
 
 	spin_lock_irqsave(&djrcv_dev->lock, flags);
 	if (dj_report->report_id == REPORT_ID_DJ_SHORT) {
path: drivers/hid/hid-logitech-dj.c