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 |
