summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2010-10-25 11:43:05 +0200
committerStefan Richter <stefanr@s5r6.in-berlin.de>2010-10-30 23:37:20 +0200
commit693fa7792e9db9f32da9436e633976fbacd04b55 (patch)
tree227078047db9c8f3497133769b1b9b1f7b681fa1
parentfirewire: ohci: avoid reallocation of AR buffers (diff)
downloadlinux-693fa7792e9db9f32da9436e633976fbacd04b55.tar.xz
linux-693fa7792e9db9f32da9436e633976fbacd04b55.zip
firewire: ohci: fix race when reading count in AR descriptor
If the controller is storing a split packet and therefore changing d->res_count to zero between the two reads by the driver, we end up with an end pointer that is not at a packet boundary, and therefore overflow the buffer when handling the split packet. To fix this, read the field once, atomically. The compiler usually merges the two reads anyway, but for correctness, we have to enforce it. Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Tested-by: Maxim Levitsky <maximlevitsky@gmail.com> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
-rw-r--r--drivers/firewire/ohci.c6
1 files changed, 4 insertions, 2 deletions
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index b5ba66656c6c..84eb607d6c03 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -740,11 +740,13 @@ static void ar_context_tasklet(unsigned long data)
struct ar_buffer *ab;
struct descriptor *d;
void *buffer, *end;
+ __le16 res_count;
ab = ctx->current_buffer;
d = &ab->descriptor;
- if (d->res_count == 0) {
+ res_count = ACCESS_ONCE(d->res_count);
+ if (res_count == 0) {
size_t size, size2, rest, pktsize, size3, offset;
dma_addr_t start_bus;
void *start;
@@ -812,7 +814,7 @@ static void ar_context_tasklet(unsigned long data)
} else {
buffer = ctx->pointer;
ctx->pointer = end =
- (void *) ab + PAGE_SIZE - le16_to_cpu(d->res_count);
+ (void *) ab + PAGE_SIZE - le16_to_cpu(res_count);
while (buffer < end)
buffer = handle_ar_packet(ctx, buffer);