diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2010-05-19 02:28:32 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2010-06-09 13:42:18 -0400 |
commit | a10c0ce76098857b899505d05de9f2e13ddf7a7a (patch) | |
tree | 130592c6baaff2e38dd813448337dded1ee1645b /drivers | |
parent | 262444eecce40950af19ea4d75a3dc03b3c07283 (diff) |
firewire: check cdev response length
Add a check that the data length in the SEND_RESPONSE ioctl is correct.
Incidentally, this also fixes the previously wrong response length of
software-handled lock requests.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/firewire/core-cdev.c | 9 | ||||
-rw-r--r-- | drivers/firewire/core-transaction.c | 38 | ||||
-rw-r--r-- | drivers/firewire/core.h | 1 |
3 files changed, 44 insertions, 4 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 9d1a1a1a83c9..50332b84f49a 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c | |||
@@ -756,9 +756,12 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg) | |||
756 | if (is_fcp_request(r->request)) | 756 | if (is_fcp_request(r->request)) |
757 | goto out; | 757 | goto out; |
758 | 758 | ||
759 | if (a->length < r->length) | 759 | if (a->length != fw_get_response_length(r->request)) { |
760 | r->length = a->length; | 760 | ret = -EINVAL; |
761 | if (copy_from_user(r->data, u64_to_uptr(a->data), r->length)) { | 761 | kfree(r->request); |
762 | goto out; | ||
763 | } | ||
764 | if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) { | ||
762 | ret = -EFAULT; | 765 | ret = -EFAULT; |
763 | kfree(r->request); | 766 | kfree(r->request); |
764 | goto out; | 767 | goto out; |
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index fdc33ff06dc1..4fd5c3b2128e 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c | |||
@@ -580,6 +580,41 @@ static void free_response_callback(struct fw_packet *packet, | |||
580 | kfree(request); | 580 | kfree(request); |
581 | } | 581 | } |
582 | 582 | ||
583 | int fw_get_response_length(struct fw_request *r) | ||
584 | { | ||
585 | int tcode, ext_tcode, data_length; | ||
586 | |||
587 | tcode = HEADER_GET_TCODE(r->request_header[0]); | ||
588 | |||
589 | switch (tcode) { | ||
590 | case TCODE_WRITE_QUADLET_REQUEST: | ||
591 | case TCODE_WRITE_BLOCK_REQUEST: | ||
592 | return 0; | ||
593 | |||
594 | case TCODE_READ_QUADLET_REQUEST: | ||
595 | return 4; | ||
596 | |||
597 | case TCODE_READ_BLOCK_REQUEST: | ||
598 | data_length = HEADER_GET_DATA_LENGTH(r->request_header[3]); | ||
599 | return data_length; | ||
600 | |||
601 | case TCODE_LOCK_REQUEST: | ||
602 | ext_tcode = HEADER_GET_EXTENDED_TCODE(r->request_header[3]); | ||
603 | data_length = HEADER_GET_DATA_LENGTH(r->request_header[3]); | ||
604 | switch (ext_tcode) { | ||
605 | case EXTCODE_FETCH_ADD: | ||
606 | case EXTCODE_LITTLE_ADD: | ||
607 | return data_length; | ||
608 | default: | ||
609 | return data_length / 2; | ||
610 | } | ||
611 | |||
612 | default: | ||
613 | WARN(1, KERN_ERR "wrong tcode %d", tcode); | ||
614 | return 0; | ||
615 | } | ||
616 | } | ||
617 | |||
583 | void fw_fill_response(struct fw_packet *response, u32 *request_header, | 618 | void fw_fill_response(struct fw_packet *response, u32 *request_header, |
584 | int rcode, void *payload, size_t length) | 619 | int rcode, void *payload, size_t length) |
585 | { | 620 | { |
@@ -713,7 +748,8 @@ void fw_send_response(struct fw_card *card, | |||
713 | 748 | ||
714 | if (rcode == RCODE_COMPLETE) | 749 | if (rcode == RCODE_COMPLETE) |
715 | fw_fill_response(&request->response, request->request_header, | 750 | fw_fill_response(&request->response, request->request_header, |
716 | rcode, request->data, request->length); | 751 | rcode, request->data, |
752 | fw_get_response_length(request)); | ||
717 | else | 753 | else |
718 | fw_fill_response(&request->response, request->request_header, | 754 | fw_fill_response(&request->response, request->request_header, |
719 | rcode, NULL, 0); | 755 | rcode, NULL, 0); |
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 0ecfcd95f4c5..25a72e57a0cd 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h | |||
@@ -218,6 +218,7 @@ static inline bool is_next_generation(int new_generation, int old_generation) | |||
218 | 218 | ||
219 | void fw_core_handle_request(struct fw_card *card, struct fw_packet *request); | 219 | void fw_core_handle_request(struct fw_card *card, struct fw_packet *request); |
220 | void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet); | 220 | void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet); |
221 | int fw_get_response_length(struct fw_request *request); | ||
221 | void fw_fill_response(struct fw_packet *response, u32 *request_header, | 222 | void fw_fill_response(struct fw_packet *response, u32 *request_header, |
222 | int rcode, void *payload, size_t length); | 223 | int rcode, void *payload, size_t length); |
223 | void fw_send_phy_config(struct fw_card *card, | 224 | void fw_send_phy_config(struct fw_card *card, |