Bluetooth: Fix issue with sysfs handling for connections

Due to a semantic changes in flush_workqueue() the current approach of
synchronizing the sysfs handling for connections doesn't work anymore. The
whole approach is actually fully broken and based on assumptions that are
no longer valid.

With the introduction of Simple Pairing support, the creation of low-level
ACL links got changed. This change invalidates the reason why in the past
two independent work queues have been used for adding/removing sysfs
devices. The adding of the actual sysfs device is now postponed until the
host controller successfully assigns an unique handle to that link. So
the real synchronization happens inside the controller and not the host.

The only left-over problem is that some internals of the sysfs device
handling are not initialized ahead of time. This leaves potential access
to invalid data and can cause various NULL pointer dereferences. To fix
this a new function makes sure that all sysfs details are initialized
when an connection attempt is made. The actual sysfs device is only
registered when the connection has been successfully established. To
avoid a race condition with the registration, the check if a device is
registered has been moved into the removal work.

As an extra protection two flush_work() calls are left in place to
make sure a previous add/del work has been completed first.

Based on a report by Marc Pignat <marc.pignat@hevs.ch>

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Tested-by: Justin P. Mattock <justinmattock@gmail.com>
Tested-by: Roger Quadros <ext-roger.quadros@nokia.com>
Tested-by: Marc Pignat <marc.pignat@hevs.ch>

+45 -36
+1
include/net/bluetooth/hci_core.h
··· 457 458 int hci_register_sysfs(struct hci_dev *hdev); 459 void hci_unregister_sysfs(struct hci_dev *hdev); 460 void hci_conn_add_sysfs(struct hci_conn *conn); 461 void hci_conn_del_sysfs(struct hci_conn *conn); 462
··· 457 458 int hci_register_sysfs(struct hci_dev *hdev); 459 void hci_unregister_sysfs(struct hci_dev *hdev); 460 + void hci_conn_init_sysfs(struct hci_conn *conn); 461 void hci_conn_add_sysfs(struct hci_conn *conn); 462 void hci_conn_del_sysfs(struct hci_conn *conn); 463
+2
net/bluetooth/hci_conn.c
··· 248 if (hdev->notify) 249 hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); 250 251 tasklet_enable(&hdev->tx_task); 252 253 return conn;
··· 248 if (hdev->notify) 249 hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); 250 251 + hci_conn_init_sysfs(conn); 252 + 253 tasklet_enable(&hdev->tx_task); 254 255 return conn;
+42 -36
net/bluetooth/hci_sysfs.c
··· 9 struct class *bt_class = NULL; 10 EXPORT_SYMBOL_GPL(bt_class); 11 12 - static struct workqueue_struct *bluetooth; 13 14 static inline char *link_typetostr(int type) 15 { ··· 89 { 90 struct hci_conn *conn = container_of(work, struct hci_conn, work_add); 91 92 - /* ensure previous add/del is complete */ 93 - flush_workqueue(bluetooth); 94 95 if (device_add(&conn->dev) < 0) { 96 BT_ERR("Failed to register connection device"); 97 return; 98 } 99 - } 100 - 101 - void hci_conn_add_sysfs(struct hci_conn *conn) 102 - { 103 - struct hci_dev *hdev = conn->hdev; 104 - 105 - BT_DBG("conn %p", conn); 106 - 107 - conn->dev.type = &bt_link; 108 - conn->dev.class = bt_class; 109 - conn->dev.parent = &hdev->dev; 110 - 111 - dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); 112 - 113 - dev_set_drvdata(&conn->dev, conn); 114 - 115 - device_initialize(&conn->dev); 116 - 117 - INIT_WORK(&conn->work_add, add_conn); 118 - 119 - queue_work(bluetooth, &conn->work_add); 120 } 121 122 /* ··· 113 struct hci_conn *conn = container_of(work, struct hci_conn, work_del); 114 struct hci_dev *hdev = conn->hdev; 115 116 - /* ensure previous add/del is complete */ 117 - flush_workqueue(bluetooth); 118 119 while (1) { 120 struct device *dev; ··· 134 hci_dev_put(hdev); 135 } 136 137 void hci_conn_del_sysfs(struct hci_conn *conn) 138 { 139 BT_DBG("conn %p", conn); 140 141 - if (!device_is_registered(&conn->dev)) 142 - return; 143 - 144 - INIT_WORK(&conn->work_del, del_conn); 145 - 146 - queue_work(bluetooth, &conn->work_del); 147 } 148 149 static inline char *host_typetostr(int type) ··· 444 445 int __init bt_sysfs_init(void) 446 { 447 - bluetooth = create_singlethread_workqueue("bluetooth"); 448 - if (!bluetooth) 449 return -ENOMEM; 450 451 bt_class = class_create(THIS_MODULE, "bluetooth"); 452 if (IS_ERR(bt_class)) { 453 - destroy_workqueue(bluetooth); 454 return PTR_ERR(bt_class); 455 } 456 ··· 459 460 void bt_sysfs_cleanup(void) 461 { 462 - destroy_workqueue(bluetooth); 463 464 class_destroy(bt_class); 465 }
··· 9 struct class *bt_class = NULL; 10 EXPORT_SYMBOL_GPL(bt_class); 11 12 + static struct workqueue_struct *bt_workq; 13 14 static inline char *link_typetostr(int type) 15 { ··· 89 { 90 struct hci_conn *conn = container_of(work, struct hci_conn, work_add); 91 92 + /* ensure previous del is complete */ 93 + flush_work(&conn->work_del); 94 95 if (device_add(&conn->dev) < 0) { 96 BT_ERR("Failed to register connection device"); 97 return; 98 } 99 } 100 101 /* ··· 134 struct hci_conn *conn = container_of(work, struct hci_conn, work_del); 135 struct hci_dev *hdev = conn->hdev; 136 137 + /* ensure previous add is complete */ 138 + flush_work(&conn->work_add); 139 + 140 + if (!device_is_registered(&conn->dev)) 141 + return; 142 143 while (1) { 144 struct device *dev; ··· 152 hci_dev_put(hdev); 153 } 154 155 + void hci_conn_init_sysfs(struct hci_conn *conn) 156 + { 157 + struct hci_dev *hdev = conn->hdev; 158 + 159 + BT_DBG("conn %p", conn); 160 + 161 + conn->dev.type = &bt_link; 162 + conn->dev.class = bt_class; 163 + conn->dev.parent = &hdev->dev; 164 + 165 + dev_set_drvdata(&conn->dev, conn); 166 + 167 + device_initialize(&conn->dev); 168 + 169 + INIT_WORK(&conn->work_add, add_conn); 170 + INIT_WORK(&conn->work_del, del_conn); 171 + } 172 + 173 + void hci_conn_add_sysfs(struct hci_conn *conn) 174 + { 175 + struct hci_dev *hdev = conn->hdev; 176 + 177 + BT_DBG("conn %p", conn); 178 + 179 + dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); 180 + 181 + queue_work(bt_workq, &conn->work_add); 182 + } 183 + 184 void hci_conn_del_sysfs(struct hci_conn *conn) 185 { 186 BT_DBG("conn %p", conn); 187 188 + queue_work(bt_workq, &conn->work_del); 189 } 190 191 static inline char *host_typetostr(int type) ··· 438 439 int __init bt_sysfs_init(void) 440 { 441 + bt_workq = create_singlethread_workqueue("bluetooth"); 442 + if (!bt_workq) 443 return -ENOMEM; 444 445 bt_class = class_create(THIS_MODULE, "bluetooth"); 446 if (IS_ERR(bt_class)) { 447 + destroy_workqueue(bt_workq); 448 return PTR_ERR(bt_class); 449 } 450 ··· 453 454 void bt_sysfs_cleanup(void) 455 { 456 + destroy_workqueue(bt_workq); 457 458 class_destroy(bt_class); 459 }