diff options
author | Jiri Kosina <jkosina@suse.cz> | 2012-04-26 18:56:08 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2012-04-27 14:10:09 -0400 |
commit | b6787242f32700377d3da3b8d788ab3928bab849 (patch) | |
tree | 54428bcff5865df64980d0af4423d52b6c1dffa5 | |
parent | d4f0e4daf0d867f80c78ca4f9ac03a562e229e72 (diff) |
HID: hidraw: add proper error handling to raw event reporting
If kmemdup() in hidraw_report_event() fails, we are not propagating
this fact properly.
Let hidraw_report_event() and hid_report_raw_event() return an error
value to the caller.
Reported-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/hid-core.c | 16 | ||||
-rw-r--r-- | drivers/hid/hidraw.c | 19 | ||||
-rw-r--r-- | include/linux/hid.h | 2 | ||||
-rw-r--r-- | include/linux/hidraw.h | 4 |
4 files changed, 27 insertions, 14 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8be458b79b2c..0cddcaa3fe7f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -1032,7 +1032,7 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, | |||
1032 | return report; | 1032 | return report; |
1033 | } | 1033 | } |
1034 | 1034 | ||
1035 | void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, | 1035 | int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, |
1036 | int interrupt) | 1036 | int interrupt) |
1037 | { | 1037 | { |
1038 | struct hid_report_enum *report_enum = hid->report_enum + type; | 1038 | struct hid_report_enum *report_enum = hid->report_enum + type; |
@@ -1040,10 +1040,11 @@ void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, | |||
1040 | unsigned int a; | 1040 | unsigned int a; |
1041 | int rsize, csize = size; | 1041 | int rsize, csize = size; |
1042 | u8 *cdata = data; | 1042 | u8 *cdata = data; |
1043 | int ret = 0; | ||
1043 | 1044 | ||
1044 | report = hid_get_report(report_enum, data); | 1045 | report = hid_get_report(report_enum, data); |
1045 | if (!report) | 1046 | if (!report) |
1046 | return; | 1047 | goto out; |
1047 | 1048 | ||
1048 | if (report_enum->numbered) { | 1049 | if (report_enum->numbered) { |
1049 | cdata++; | 1050 | cdata++; |
@@ -1063,14 +1064,19 @@ void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, | |||
1063 | 1064 | ||
1064 | if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) | 1065 | if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) |
1065 | hid->hiddev_report_event(hid, report); | 1066 | hid->hiddev_report_event(hid, report); |
1066 | if (hid->claimed & HID_CLAIMED_HIDRAW) | 1067 | if (hid->claimed & HID_CLAIMED_HIDRAW) { |
1067 | hidraw_report_event(hid, data, size); | 1068 | ret = hidraw_report_event(hid, data, size); |
1069 | if (ret) | ||
1070 | goto out; | ||
1071 | } | ||
1068 | 1072 | ||
1069 | for (a = 0; a < report->maxfield; a++) | 1073 | for (a = 0; a < report->maxfield; a++) |
1070 | hid_input_field(hid, report->field[a], cdata, interrupt); | 1074 | hid_input_field(hid, report->field[a], cdata, interrupt); |
1071 | 1075 | ||
1072 | if (hid->claimed & HID_CLAIMED_INPUT) | 1076 | if (hid->claimed & HID_CLAIMED_INPUT) |
1073 | hidinput_report_event(hid, report); | 1077 | hidinput_report_event(hid, report); |
1078 | out: | ||
1079 | return ret; | ||
1074 | } | 1080 | } |
1075 | EXPORT_SYMBOL_GPL(hid_report_raw_event); | 1081 | EXPORT_SYMBOL_GPL(hid_report_raw_event); |
1076 | 1082 | ||
@@ -1147,7 +1153,7 @@ nomem: | |||
1147 | } | 1153 | } |
1148 | } | 1154 | } |
1149 | 1155 | ||
1150 | hid_report_raw_event(hid, type, data, size, interrupt); | 1156 | ret = hid_report_raw_event(hid, type, data, size, interrupt); |
1151 | 1157 | ||
1152 | unlock: | 1158 | unlock: |
1153 | up(&hid->driver_lock); | 1159 | up(&hid->driver_lock); |
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index cf7d6d58e79f..36fa77b40ffb 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c | |||
@@ -87,11 +87,13 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, | |||
87 | len = list->buffer[list->tail].len > count ? | 87 | len = list->buffer[list->tail].len > count ? |
88 | count : list->buffer[list->tail].len; | 88 | count : list->buffer[list->tail].len; |
89 | 89 | ||
90 | if (copy_to_user(buffer, list->buffer[list->tail].value, len)) { | 90 | if (list->buffer[list->tail].value) { |
91 | ret = -EFAULT; | 91 | if (copy_to_user(buffer, list->buffer[list->tail].value, len)) { |
92 | goto out; | 92 | ret = -EFAULT; |
93 | goto out; | ||
94 | } | ||
95 | ret = len; | ||
93 | } | 96 | } |
94 | ret = len; | ||
95 | 97 | ||
96 | kfree(list->buffer[list->tail].value); | 98 | kfree(list->buffer[list->tail].value); |
97 | list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); | 99 | list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); |
@@ -437,19 +439,24 @@ static const struct file_operations hidraw_ops = { | |||
437 | .llseek = noop_llseek, | 439 | .llseek = noop_llseek, |
438 | }; | 440 | }; |
439 | 441 | ||
440 | void hidraw_report_event(struct hid_device *hid, u8 *data, int len) | 442 | int hidraw_report_event(struct hid_device *hid, u8 *data, int len) |
441 | { | 443 | { |
442 | struct hidraw *dev = hid->hidraw; | 444 | struct hidraw *dev = hid->hidraw; |
443 | struct hidraw_list *list; | 445 | struct hidraw_list *list; |
446 | int ret = 0; | ||
444 | 447 | ||
445 | list_for_each_entry(list, &dev->list, node) { | 448 | list_for_each_entry(list, &dev->list, node) { |
446 | list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC); | 449 | if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) { |
450 | ret = -ENOMEM; | ||
451 | break; | ||
452 | } | ||
447 | list->buffer[list->head].len = len; | 453 | list->buffer[list->head].len = len; |
448 | list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); | 454 | list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); |
449 | kill_fasync(&list->fasync, SIGIO, POLL_IN); | 455 | kill_fasync(&list->fasync, SIGIO, POLL_IN); |
450 | } | 456 | } |
451 | 457 | ||
452 | wake_up_interruptible(&dev->wait); | 458 | wake_up_interruptible(&dev->wait); |
459 | return ret; | ||
453 | } | 460 | } |
454 | EXPORT_SYMBOL_GPL(hidraw_report_event); | 461 | EXPORT_SYMBOL_GPL(hidraw_report_event); |
455 | 462 | ||
diff --git a/include/linux/hid.h b/include/linux/hid.h index 3a95da60fd3e..58b3857dc51c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h | |||
@@ -896,7 +896,7 @@ static inline int hid_hw_power(struct hid_device *hdev, int level) | |||
896 | return hdev->ll_driver->power ? hdev->ll_driver->power(hdev, level) : 0; | 896 | return hdev->ll_driver->power ? hdev->ll_driver->power(hdev, level) : 0; |
897 | } | 897 | } |
898 | 898 | ||
899 | void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, | 899 | int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, |
900 | int interrupt); | 900 | int interrupt); |
901 | 901 | ||
902 | extern int hid_generic_init(void); | 902 | extern int hid_generic_init(void); |
diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h index 4b88e697c4e9..9cdc9b67ff49 100644 --- a/include/linux/hidraw.h +++ b/include/linux/hidraw.h | |||
@@ -76,13 +76,13 @@ struct hidraw_list { | |||
76 | #ifdef CONFIG_HIDRAW | 76 | #ifdef CONFIG_HIDRAW |
77 | int hidraw_init(void); | 77 | int hidraw_init(void); |
78 | void hidraw_exit(void); | 78 | void hidraw_exit(void); |
79 | void hidraw_report_event(struct hid_device *, u8 *, int); | 79 | int hidraw_report_event(struct hid_device *, u8 *, int); |
80 | int hidraw_connect(struct hid_device *); | 80 | int hidraw_connect(struct hid_device *); |
81 | void hidraw_disconnect(struct hid_device *); | 81 | void hidraw_disconnect(struct hid_device *); |
82 | #else | 82 | #else |
83 | static inline int hidraw_init(void) { return 0; } | 83 | static inline int hidraw_init(void) { return 0; } |
84 | static inline void hidraw_exit(void) { } | 84 | static inline void hidraw_exit(void) { } |
85 | static inline void hidraw_report_event(struct hid_device *hid, u8 *data, int len) { } | 85 | static inline int hidraw_report_event(struct hid_device *hid, u8 *data, int len) { } |
86 | static inline int hidraw_connect(struct hid_device *hid) { return -1; } | 86 | static inline int hidraw_connect(struct hid_device *hid) { return -1; } |
87 | static inline void hidraw_disconnect(struct hid_device *hid) { } | 87 | static inline void hidraw_disconnect(struct hid_device *hid) { } |
88 | #endif | 88 | #endif |