Commit a10c0ce7 authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Stefan Richter
Browse files

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: default avatarClemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 262444ee
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -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;
+37 −1
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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,