Date: Wed, 19 May 2010 08:28:32 +0200 From: Clemens Ladisch Subject: 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 Signed-off-by: Stefan Richter --- drivers/firewire/core-cdev.c | 9 ++++-- drivers/firewire/core-transaction.c | 38 +++++++++++++++++++++++++++- drivers/firewire/core.h | 1 drivers/firewire/core-cdev.c | 9 +++++-- drivers/firewire/core-transaction.c | 38 +++++++++++++++++++++++++++- drivers/firewire/core.h | 1 + 3 files changed, 44 insertions(+), 4 deletions(-) Index: b/drivers/firewire/core.h =================================================================== --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -218,6 +218,7 @@ static inline bool is_next_generation(in 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, Index: b/drivers/firewire/core-cdev.c =================================================================== --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -757,9 +757,12 @@ static int ioctl_send_response(struct cl 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: b/drivers/firewire/core-transaction.c =================================================================== --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -580,6 +580,41 @@ static void free_response_callback(struc 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 *ca 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);