diff options
| author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-02-03 18:43:05 -0500 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-02-05 11:41:18 -0500 |
| commit | 78ea4639a7647f2fcc957c3a532bde49df9895c7 (patch) | |
| tree | 77a5f9ccf15bbba2c82a1613458fd68553c29888 | |
| parent | 7c2e17714e190b2ef857e7e842464fb47ceca146 (diff) | |
ACPI / hotplug: Fix potential race in acpi_bus_notify()
There is a slight possibility for the ACPI device object pointed to
by adev in acpi_hotplug_notify_cb() to become invalid between the
acpi_bus_get_device() that it comes from and the subsequent dereference
of that pointer under get_device(). Namely, if acpi_scan_drop_device()
runs in parallel with acpi_hotplug_notify_cb(), acpi_device_del_work_fn()
queued up by it may delete the device object in question right after
a successful execution of acpi_bus_get_device() in acpi_bus_notify().
An analogous problem is present in acpi_bus_notify() where the device
pointer coming from acpi_bus_get_device() may become invalid before
it subsequent dereference in the "if" block.
To prevent that from happening, introduce a new function,
acpi_bus_get_acpi_device(), working analogously to acpi_bus_get_device()
except that it will grab a reference to the ACPI device object returned
by it and it will do that under the ACPICA's namespace mutex. Then,
make both acpi_hotplug_notify_cb() and acpi_bus_notify() use
acpi_bus_get_acpi_device() instead of acpi_bus_get_device() so as to
ensure that the pointers used by them will not become stale at one
point.
In addition to that, introduce acpi_bus_put_acpi_device() as a wrapper
around put_device() to be used along with acpi_bus_get_acpi_device()
and make the (new) users of the latter use acpi_bus_put_acpi_device()
too.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
| -rw-r--r-- | drivers/acpi/bus.c | 6 | ||||
| -rw-r--r-- | drivers/acpi/scan.c | 42 | ||||
| -rw-r--r-- | include/acpi/acpi_bus.h | 2 |
3 files changed, 40 insertions, 10 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index fcb59c21c68d..c3237774632f 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c | |||
| @@ -340,7 +340,7 @@ static void acpi_bus_osc_support(void) | |||
| 340 | */ | 340 | */ |
| 341 | static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) | 341 | static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) |
| 342 | { | 342 | { |
| 343 | struct acpi_device *device = NULL; | 343 | struct acpi_device *device; |
| 344 | struct acpi_driver *driver; | 344 | struct acpi_driver *driver; |
| 345 | 345 | ||
| 346 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n", | 346 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n", |
| @@ -387,12 +387,14 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) | |||
| 387 | break; | 387 | break; |
| 388 | } | 388 | } |
| 389 | 389 | ||
| 390 | acpi_bus_get_device(handle, &device); | 390 | device = acpi_bus_get_acpi_device(handle); |
| 391 | if (device) { | 391 | if (device) { |
| 392 | driver = device->driver; | 392 | driver = device->driver; |
| 393 | if (driver && driver->ops.notify && | 393 | if (driver && driver->ops.notify && |
| 394 | (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS)) | 394 | (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS)) |
| 395 | driver->ops.notify(device, type); | 395 | driver->ops.notify(device, type); |
| 396 | |||
| 397 | acpi_bus_put_acpi_device(device); | ||
| 396 | } | 398 | } |
| 397 | } | 399 | } |
| 398 | 400 | ||
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7384158c7f87..59eba29a6066 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
| @@ -476,7 +476,7 @@ static void acpi_device_hotplug(void *data, u32 src) | |||
| 476 | 476 | ||
| 477 | out: | 477 | out: |
| 478 | acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL); | 478 | acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL); |
| 479 | put_device(&adev->dev); | 479 | acpi_bus_put_acpi_device(adev); |
| 480 | mutex_unlock(&acpi_scan_lock); | 480 | mutex_unlock(&acpi_scan_lock); |
| 481 | unlock_device_hotplug(); | 481 | unlock_device_hotplug(); |
| 482 | } | 482 | } |
| @@ -488,9 +488,6 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) | |||
| 488 | struct acpi_device *adev; | 488 | struct acpi_device *adev; |
| 489 | acpi_status status; | 489 | acpi_status status; |
| 490 | 490 | ||
| 491 | if (acpi_bus_get_device(handle, &adev)) | ||
| 492 | goto err_out; | ||
| 493 | |||
| 494 | switch (type) { | 491 | switch (type) { |
| 495 | case ACPI_NOTIFY_BUS_CHECK: | 492 | case ACPI_NOTIFY_BUS_CHECK: |
| 496 | acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); | 493 | acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); |
| @@ -512,12 +509,15 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) | |||
| 512 | /* non-hotplug event; possibly handled by other handler */ | 509 | /* non-hotplug event; possibly handled by other handler */ |
| 513 | return; | 510 | return; |
| 514 | } | 511 | } |
| 515 | get_device(&adev->dev); | 512 | adev = acpi_bus_get_acpi_device(handle); |
| 513 | if (!adev) | ||
| 514 | goto err_out; | ||
| 515 | |||
| 516 | status = acpi_hotplug_execute(acpi_device_hotplug, adev, type); | 516 | status = acpi_hotplug_execute(acpi_device_hotplug, adev, type); |
| 517 | if (ACPI_SUCCESS(status)) | 517 | if (ACPI_SUCCESS(status)) |
| 518 | return; | 518 | return; |
| 519 | 519 | ||
| 520 | put_device(&adev->dev); | 520 | acpi_bus_put_acpi_device(adev); |
| 521 | 521 | ||
| 522 | err_out: | 522 | err_out: |
| 523 | acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); | 523 | acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); |
| @@ -1112,14 +1112,16 @@ static void acpi_scan_drop_device(acpi_handle handle, void *context) | |||
| 1112 | mutex_unlock(&acpi_device_del_lock); | 1112 | mutex_unlock(&acpi_device_del_lock); |
| 1113 | } | 1113 | } |
| 1114 | 1114 | ||
| 1115 | int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) | 1115 | static int acpi_get_device_data(acpi_handle handle, struct acpi_device **device, |
| 1116 | void (*callback)(void *)) | ||
| 1116 | { | 1117 | { |
| 1117 | acpi_status status; | 1118 | acpi_status status; |
| 1118 | 1119 | ||
| 1119 | if (!device) | 1120 | if (!device) |
| 1120 | return -EINVAL; | 1121 | return -EINVAL; |
| 1121 | 1122 | ||
| 1122 | status = acpi_get_data(handle, acpi_scan_drop_device, (void **)device); | 1123 | status = acpi_get_data_full(handle, acpi_scan_drop_device, |
| 1124 | (void **)device, callback); | ||
| 1123 | if (ACPI_FAILURE(status) || !*device) { | 1125 | if (ACPI_FAILURE(status) || !*device) { |
| 1124 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", | 1126 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", |
| 1125 | handle)); | 1127 | handle)); |
| @@ -1127,8 +1129,32 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) | |||
| 1127 | } | 1129 | } |
| 1128 | return 0; | 1130 | return 0; |
| 1129 | } | 1131 | } |
| 1132 | |||
| 1133 | int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) | ||
| 1134 | { | ||
| 1135 | return acpi_get_device_data(handle, device, NULL); | ||
| 1136 | } | ||
| 1130 | EXPORT_SYMBOL(acpi_bus_get_device); | 1137 | EXPORT_SYMBOL(acpi_bus_get_device); |
| 1131 | 1138 | ||
| 1139 | static void get_acpi_device(void *dev) | ||
| 1140 | { | ||
| 1141 | if (dev) | ||
| 1142 | get_device(&((struct acpi_device *)dev)->dev); | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle) | ||
| 1146 | { | ||
| 1147 | struct acpi_device *adev = NULL; | ||
| 1148 | |||
| 1149 | acpi_get_device_data(handle, &adev, get_acpi_device); | ||
| 1150 | return adev; | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | void acpi_bus_put_acpi_device(struct acpi_device *adev) | ||
| 1154 | { | ||
| 1155 | put_device(&adev->dev); | ||
| 1156 | } | ||
| 1157 | |||
| 1132 | int acpi_device_add(struct acpi_device *device, | 1158 | int acpi_device_add(struct acpi_device *device, |
| 1133 | void (*release)(struct device *)) | 1159 | void (*release)(struct device *)) |
| 1134 | { | 1160 | { |
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 8256eb4ad057..94fd61a0456d 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
| @@ -381,6 +381,8 @@ extern int unregister_acpi_notifier(struct notifier_block *); | |||
| 381 | */ | 381 | */ |
| 382 | 382 | ||
| 383 | int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device); | 383 | int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device); |
| 384 | struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle); | ||
| 385 | void acpi_bus_put_acpi_device(struct acpi_device *adev); | ||
| 384 | acpi_status acpi_bus_get_status_handle(acpi_handle handle, | 386 | acpi_status acpi_bus_get_status_handle(acpi_handle handle, |
| 385 | unsigned long long *sta); | 387 | unsigned long long *sta); |
| 386 | int acpi_bus_get_status(struct acpi_device *device); | 388 | int acpi_bus_get_status(struct acpi_device *device); |
