Skip to content

Commit 576db0f

Browse files
committed
Merge tag 'thermal-7.0-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull thermal control fixes from Rafael Wysocki: "Address potential races between thermal zone removal and system resume that may lead to a use-after-free (in two different ways) and a potential use-after-free in the thermal zone unregistration path (Rafael Wysocki)" * tag 'thermal-7.0-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: thermal: core: Fix thermal zone device registration error path thermal: core: Address thermal zone removal races with resume
2 parents 116a330 + 9e07e3b commit 576db0f

1 file changed

Lines changed: 27 additions & 5 deletions

File tree

drivers/thermal/thermal_core.c

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ static struct thermal_governor *def_governor;
4141

4242
static bool thermal_pm_suspended;
4343

44+
static struct workqueue_struct *thermal_wq __ro_after_init;
45+
4446
/*
4547
* Governor section: set of functions to handle thermal governors
4648
*
@@ -313,7 +315,7 @@ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
313315
if (delay > HZ)
314316
delay = round_jiffies_relative(delay);
315317

316-
mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay);
318+
mod_delayed_work(thermal_wq, &tz->poll_queue, delay);
317319
}
318320

319321
static void thermal_zone_recheck(struct thermal_zone_device *tz, int error)
@@ -1640,6 +1642,7 @@ thermal_zone_device_register_with_trips(const char *type,
16401642
device_del(&tz->device);
16411643
release_device:
16421644
put_device(&tz->device);
1645+
wait_for_completion(&tz->removal);
16431646
remove_id:
16441647
ida_free(&thermal_tz_ida, id);
16451648
free_tzp:
@@ -1785,6 +1788,10 @@ static void thermal_zone_device_resume(struct work_struct *work)
17851788

17861789
guard(thermal_zone)(tz);
17871790

1791+
/* If the thermal zone is going away, there's nothing to do. */
1792+
if (tz->state & TZ_STATE_FLAG_EXIT)
1793+
return;
1794+
17881795
tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING);
17891796

17901797
thermal_debug_tz_resume(tz);
@@ -1811,6 +1818,9 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz)
18111818
}
18121819

18131820
tz->state |= TZ_STATE_FLAG_SUSPENDED;
1821+
1822+
/* Prevent new work from getting to the workqueue subsequently. */
1823+
cancel_delayed_work(&tz->poll_queue);
18141824
}
18151825

18161826
static void thermal_pm_notify_prepare(void)
@@ -1829,8 +1839,6 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz)
18291839
{
18301840
guard(thermal_zone)(tz);
18311841

1832-
cancel_delayed_work(&tz->poll_queue);
1833-
18341842
reinit_completion(&tz->resume);
18351843
tz->state |= TZ_STATE_FLAG_RESUMING;
18361844

@@ -1840,7 +1848,7 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz)
18401848
*/
18411849
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume);
18421850
/* Queue up the work without a delay. */
1843-
mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0);
1851+
mod_delayed_work(thermal_wq, &tz->poll_queue, 0);
18441852
}
18451853

18461854
static void thermal_pm_notify_complete(void)
@@ -1863,6 +1871,11 @@ static int thermal_pm_notify(struct notifier_block *nb,
18631871
case PM_RESTORE_PREPARE:
18641872
case PM_SUSPEND_PREPARE:
18651873
thermal_pm_notify_prepare();
1874+
/*
1875+
* Allow any leftover thermal work items already on the
1876+
* worqueue to complete so they don't get in the way later.
1877+
*/
1878+
flush_workqueue(thermal_wq);
18661879
break;
18671880
case PM_POST_HIBERNATION:
18681881
case PM_POST_RESTORE:
@@ -1895,9 +1908,16 @@ static int __init thermal_init(void)
18951908
if (result)
18961909
goto error;
18971910

1911+
thermal_wq = alloc_workqueue("thermal_events",
1912+
WQ_FREEZABLE | WQ_POWER_EFFICIENT | WQ_PERCPU, 0);
1913+
if (!thermal_wq) {
1914+
result = -ENOMEM;
1915+
goto unregister_netlink;
1916+
}
1917+
18981918
result = thermal_register_governors();
18991919
if (result)
1900-
goto unregister_netlink;
1920+
goto destroy_workqueue;
19011921

19021922
thermal_class = kzalloc_obj(*thermal_class);
19031923
if (!thermal_class) {
@@ -1924,6 +1944,8 @@ static int __init thermal_init(void)
19241944

19251945
unregister_governors:
19261946
thermal_unregister_governors();
1947+
destroy_workqueue:
1948+
destroy_workqueue(thermal_wq);
19271949
unregister_netlink:
19281950
thermal_netlink_exit();
19291951
error:

0 commit comments

Comments
 (0)