Commit 281e2032 authored by Stefan Richter's avatar Stefan Richter

firewire: core: fix use-after-free regression in FCP handler

Commit db5d247a "firewire: fix use of multiple AV/C devices, allow
multiple FCP listeners" introduced a regression into 2.6.33-rc3:
The core freed payloads of incoming requests to FCP_Request or
FCP_Response before a userspace driver accessed them.

We need to copy such payloads for each registered userspace client
and free the copies according to the lifetime rules of non-FCP client
request resources.

(This could possibly be optimized by reference counts instead of
copies.)

The presently only kernelspace driver which listens for FCP requests,
firedtv, was not affected because it already copies FCP frames into an
own buffer before returning to firewire-core's FCP handler dispatcher.
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 6d3faf6f
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/preempt.h> #include <linux/preempt.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
...@@ -595,13 +596,20 @@ static int ioctl_send_request(struct client *client, void *buffer) ...@@ -595,13 +596,20 @@ static int ioctl_send_request(struct client *client, void *buffer)
client->device->max_speed); client->device->max_speed);
} }
static inline bool is_fcp_request(struct fw_request *request)
{
return request == NULL;
}
static void release_request(struct client *client, static void release_request(struct client *client,
struct client_resource *resource) struct client_resource *resource)
{ {
struct inbound_transaction_resource *r = container_of(resource, struct inbound_transaction_resource *r = container_of(resource,
struct inbound_transaction_resource, resource); struct inbound_transaction_resource, resource);
if (r->request) if (is_fcp_request(r->request))
kfree(r->data);
else
fw_send_response(client->device->card, r->request, fw_send_response(client->device->card, r->request,
RCODE_CONFLICT_ERROR); RCODE_CONFLICT_ERROR);
kfree(r); kfree(r);
...@@ -616,6 +624,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request, ...@@ -616,6 +624,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
struct address_handler_resource *handler = callback_data; struct address_handler_resource *handler = callback_data;
struct inbound_transaction_resource *r; struct inbound_transaction_resource *r;
struct inbound_transaction_event *e; struct inbound_transaction_event *e;
void *fcp_frame = NULL;
int ret; int ret;
r = kmalloc(sizeof(*r), GFP_ATOMIC); r = kmalloc(sizeof(*r), GFP_ATOMIC);
...@@ -627,6 +636,18 @@ static void handle_request(struct fw_card *card, struct fw_request *request, ...@@ -627,6 +636,18 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
r->data = payload; r->data = payload;
r->length = length; r->length = length;
if (is_fcp_request(request)) {
/*
* FIXME: Let core-transaction.c manage a
* single reference-counted copy?
*/
fcp_frame = kmemdup(payload, length, GFP_ATOMIC);
if (fcp_frame == NULL)
goto failed;
r->data = fcp_frame;
}
r->resource.release = release_request; r->resource.release = release_request;
ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC);
if (ret < 0) if (ret < 0)
...@@ -640,13 +661,15 @@ static void handle_request(struct fw_card *card, struct fw_request *request, ...@@ -640,13 +661,15 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
e->request.closure = handler->closure; e->request.closure = handler->closure;
queue_event(handler->client, &e->event, queue_event(handler->client, &e->event,
&e->request, sizeof(e->request), payload, length); &e->request, sizeof(e->request), r->data, length);
return; return;
failed: failed:
kfree(r); kfree(r);
kfree(e); kfree(e);
if (request) kfree(fcp_frame);
if (!is_fcp_request(request))
fw_send_response(card, request, RCODE_CONFLICT_ERROR); fw_send_response(card, request, RCODE_CONFLICT_ERROR);
} }
...@@ -717,18 +740,17 @@ static int ioctl_send_response(struct client *client, void *buffer) ...@@ -717,18 +740,17 @@ static int ioctl_send_response(struct client *client, void *buffer)
r = container_of(resource, struct inbound_transaction_resource, r = container_of(resource, struct inbound_transaction_resource,
resource); resource);
if (r->request) { if (is_fcp_request(r->request))
goto out;
if (request->length < r->length) if (request->length < r->length)
r->length = request->length; r->length = request->length;
if (copy_from_user(r->data, u64_to_uptr(request->data), if (copy_from_user(r->data, u64_to_uptr(request->data), r->length)) {
r->length)) {
ret = -EFAULT; ret = -EFAULT;
kfree(r->request); kfree(r->request);
goto out; goto out;
} }
fw_send_response(client->device->card, r->request, fw_send_response(client->device->card, r->request, request->rcode);
request->rcode);
}
out: out:
kfree(r); kfree(r);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment