summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2010-05-19 10:28:32 +0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2010-06-09 21:42:18 +0400
commita10c0ce76098857b899505d05de9f2e13ddf7a7a (patch)
tree130592c6baaff2e38dd813448337dded1ee1645b
parent262444eecce40950af19ea4d75a3dc03b3c07283 (diff)
downloadlinux-a10c0ce76098857b899505d05de9f2e13ddf7a7a.tar.xz
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>
-rw-r--r--drivers/firewire/core-cdev.c9
-rw-r--r--drivers/firewire/core-transaction.c38
-rw-r--r--drivers/firewire/core.h1
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)
if (is_fcp_request(r->request))
goto out;
- if (a->length < r->length)
- r->length = a->length;
- if (copy_from_user(r->data, u64_to_uptr(a->data), r->length)) {
+ if (a->length != fw_get_response_length(r->request)) {
+ ret = -EINVAL;
+ kfree(r->request);
+ goto out;
+ }
+ if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) {
ret = -EFAULT;
kfree(r->request);
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,
kfree(request);
}
+int fw_get_response_length(struct fw_request *r)
+{
+ int tcode, ext_tcode, data_length;
+
+ tcode = HEADER_GET_TCODE(r->request_header[0]);
+
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ case TCODE_WRITE_BLOCK_REQUEST:
+ return 0;
+
+ case TCODE_READ_QUADLET_REQUEST:
+ return 4;
+
+ case TCODE_READ_BLOCK_REQUEST:
+ data_length = HEADER_GET_DATA_LENGTH(r->request_header[3]);
+ return data_length;
+
+ case TCODE_LOCK_REQUEST:
+ ext_tcode = HEADER_GET_EXTENDED_TCODE(r->request_header[3]);
+ data_length = HEADER_GET_DATA_LENGTH(r->request_header[3]);
+ switch (ext_tcode) {
+ case EXTCODE_FETCH_ADD:
+ case EXTCODE_LITTLE_ADD:
+ return data_length;
+ default:
+ return data_length / 2;
+ }
+
+ default:
+ WARN(1, KERN_ERR "wrong tcode %d", tcode);
+ return 0;
+ }
+}
+
void fw_fill_response(struct fw_packet *response, u32 *request_header,
int rcode, void *payload, size_t length)
{
@@ -713,7 +748,8 @@ void fw_send_response(struct fw_card *card,
if (rcode == RCODE_COMPLETE)
fw_fill_response(&request->response, request->request_header,
- rcode, request->data, request->length);
+ rcode, request->data,
+ fw_get_response_length(request));
else
fw_fill_response(&request->response, request->request_header,
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)
void fw_core_handle_request(struct fw_card *card, struct fw_packet *request);
void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet);
+int fw_get_response_length(struct fw_request *request);
void fw_fill_response(struct fw_packet *response, u32 *request_header,
int rcode, void *payload, size_t length);
void fw_send_phy_config(struct fw_card *card,