firewire: check cdev response length
authorClemens Ladisch <clemens@ladisch.de>
Wed, 19 May 2010 06:28:32 +0000 (08:28 +0200)
committerStefan Richter <stefanr@s5r6.in-berlin.de>
Wed, 9 Jun 2010 17:42:18 +0000 (19:42 +0200)
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>
drivers/firewire/core-cdev.c
drivers/firewire/core-transaction.c
drivers/firewire/core.h

index 9d1a1a1a83c953ce75cfe955efc747ef67558be2..50332b84f49af354d90cf42c66f6e3cf4050bc56 100644 (file)
@@ -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;
index fdc33ff06dc11c536b080797d1ba4a81482711b0..4fd5c3b2128e07b99ac001e4d7508e13f2214eb5 100644 (file)
@@ -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);
index 0ecfcd95f4c500f62389aa4085c68281b648cc7c..25a72e57a0cdb85e6a39bc336b3b38e62a4e39fa 100644 (file)
@@ -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,