diff options
author | Richard Cochran <rcochran@linutronix.de> | 2016-04-06 17:00:53 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-04-07 16:10:57 -0400 |
commit | ca42489d9ee3262482717c83428e087322fdc39c (patch) | |
tree | e5a121d896f3800241610c64ab0eb9cd87732bb1 | |
parent | e9df69ccd1322e87eee10f28036fad9e6c71f8dd (diff) |
intel_idle: Avoid a double free of the per-CPU data.
The helper function, intel_idle_cpuidle_devices_uninit, frees the
globally allocated per-CPU data. However, this function is invoked
from the hot plug notifier callback at a time when freeing that data
is not safe.
If the call to cpuidle_register_driver() should fail (say, due to lack
of memory), then the driver will free its per-CPU region. On the
*next* CPU_ONLINE event, the driver will happily use the region again
and even free it again if the failure repeats.
This patch fixes the issue by moving the call to free_percpu() outside
of the helper function at the two call sites that actually need to
free the per-CPU data.
Signed-off-by: Richard Cochran <rcochran@linutronix.de>
Signed-off-by: Len Brown <len.brown@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/idle/intel_idle.c | 7 |
1 files changed, 3 insertions, 4 deletions
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 5dd741ff4de3..0b568728867e 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c | |||
@@ -1002,7 +1002,7 @@ static int __init intel_idle_probe(void) | |||
1002 | 1002 | ||
1003 | /* | 1003 | /* |
1004 | * intel_idle_cpuidle_devices_uninit() | 1004 | * intel_idle_cpuidle_devices_uninit() |
1005 | * unregister, free cpuidle_devices | 1005 | * Unregisters the cpuidle devices. |
1006 | */ | 1006 | */ |
1007 | static void intel_idle_cpuidle_devices_uninit(void) | 1007 | static void intel_idle_cpuidle_devices_uninit(void) |
1008 | { | 1008 | { |
@@ -1013,9 +1013,6 @@ static void intel_idle_cpuidle_devices_uninit(void) | |||
1013 | dev = per_cpu_ptr(intel_idle_cpuidle_devices, i); | 1013 | dev = per_cpu_ptr(intel_idle_cpuidle_devices, i); |
1014 | cpuidle_unregister_device(dev); | 1014 | cpuidle_unregister_device(dev); |
1015 | } | 1015 | } |
1016 | |||
1017 | free_percpu(intel_idle_cpuidle_devices); | ||
1018 | return; | ||
1019 | } | 1016 | } |
1020 | 1017 | ||
1021 | /* | 1018 | /* |
@@ -1231,6 +1228,7 @@ static int __init intel_idle_init(void) | |||
1231 | if (retval) { | 1228 | if (retval) { |
1232 | cpu_notifier_register_done(); | 1229 | cpu_notifier_register_done(); |
1233 | cpuidle_unregister_driver(&intel_idle_driver); | 1230 | cpuidle_unregister_driver(&intel_idle_driver); |
1231 | free_percpu(intel_idle_cpuidle_devices); | ||
1234 | return retval; | 1232 | return retval; |
1235 | } | 1233 | } |
1236 | } | 1234 | } |
@@ -1253,6 +1251,7 @@ static void __exit intel_idle_exit(void) | |||
1253 | cpu_notifier_register_done(); | 1251 | cpu_notifier_register_done(); |
1254 | 1252 | ||
1255 | cpuidle_unregister_driver(&intel_idle_driver); | 1253 | cpuidle_unregister_driver(&intel_idle_driver); |
1254 | free_percpu(intel_idle_cpuidle_devices); | ||
1256 | } | 1255 | } |
1257 | 1256 | ||
1258 | module_init(intel_idle_init); | 1257 | module_init(intel_idle_init); |