aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2011-07-09 10:43:22 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2011-07-16 01:24:32 -0400
commit93b37905f70083d6143f5f4dba0a45cc64379a62 (patch)
treeb6917581cd390ed9967e1df9922940362ad4309e /drivers/firewire
parentd873d794235efa590ab3c94d5ee22bb1fab19ac4 (diff)
firewire: cdev: prevent race between first get_info ioctl and bus reset event queuing
Between open(2) of a /dev/fw* and the first FW_CDEV_IOC_GET_INFO ioctl(2) on it, the kernel already queues FW_CDEV_EVENT_BUS_RESET events to be read(2) by the client. The get_info ioctl is practically always issued right away after open, hence this condition only occurs if the client opens during a bus reset, especially during a rapid series of bus resets. The problem with this condition is twofold: - These bus reset events carry the (as yet undocumented) @closure value of 0. But it is not the kernel's place to choose closures; they are privat to the client. E.g., this 0 value forced from the kernel makes it unsafe for clients to dereference it as a pointer to a closure object without NULL pointer check. - It is impossible for clients to determine the relative order of bus reset events from get_info ioctl(2) versus those from read(2), except in one way: By comparison of closure values. Again, such a procedure imposes complexity on clients and reduces freedom in use of the bus reset closure. So, change the ABI to suppress queuing of bus reset events before the first FW_CDEV_IOC_GET_INFO ioctl was issued by the client. Note, this ABI change cannot be version-controlled. The kernel cannot distinguish old from new clients before the first FW_CDEV_IOC_GET_INFO ioctl. We will try to back-merge this change into currently maintained stable/ longterm series, and we only document the new behaviour. The old behavior is now considered a kernel bug, which it basically is. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> Cc: <stable@kernel.org>
Diffstat (limited to 'drivers/firewire')
-rw-r--r--drivers/firewire/core-cdev.c18
1 files changed, 10 insertions, 8 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 9b5915ebeb35..e6ad3bb6c1a6 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -253,14 +253,11 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
253 init_waitqueue_head(&client->wait); 253 init_waitqueue_head(&client->wait);
254 init_waitqueue_head(&client->tx_flush_wait); 254 init_waitqueue_head(&client->tx_flush_wait);
255 INIT_LIST_HEAD(&client->phy_receiver_link); 255 INIT_LIST_HEAD(&client->phy_receiver_link);
256 INIT_LIST_HEAD(&client->link);
256 kref_init(&client->kref); 257 kref_init(&client->kref);
257 258
258 file->private_data = client; 259 file->private_data = client;
259 260
260 mutex_lock(&device->client_list_mutex);
261 list_add_tail(&client->link, &device->client_list);
262 mutex_unlock(&device->client_list_mutex);
263
264 return nonseekable_open(inode, file); 261 return nonseekable_open(inode, file);
265} 262}
266 263
@@ -451,15 +448,20 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
451 if (ret != 0) 448 if (ret != 0)
452 return -EFAULT; 449 return -EFAULT;
453 450
451 mutex_lock(&client->device->client_list_mutex);
452
454 client->bus_reset_closure = a->bus_reset_closure; 453 client->bus_reset_closure = a->bus_reset_closure;
455 if (a->bus_reset != 0) { 454 if (a->bus_reset != 0) {
456 fill_bus_reset_event(&bus_reset, client); 455 fill_bus_reset_event(&bus_reset, client);
457 if (copy_to_user(u64_to_uptr(a->bus_reset), 456 ret = copy_to_user(u64_to_uptr(a->bus_reset),
458 &bus_reset, sizeof(bus_reset))) 457 &bus_reset, sizeof(bus_reset));
459 return -EFAULT;
460 } 458 }
459 if (ret == 0 && list_empty(&client->link))
460 list_add_tail(&client->link, &client->device->client_list);
461 461
462 return 0; 462 mutex_unlock(&client->device->client_list_mutex);
463
464 return ret ? -EFAULT : 0;
463} 465}
464 466
465static int add_client_resource(struct client *client, 467static int add_client_resource(struct client *client,