Use get_online_cpus to avoid races involving CPU hotplug
Subsystems (2)
PATCH v2 2/4 Use get_online_cpus to avoid races involving CPU hotplug
If arch/x86/kernel/cpuid.c is a module, a CPU might offline or online between the for_each_online_cpu() loop and the call to register_hotcpu_notifier in cpuid_init or the call to unregister_hotcpu_notifier in cpuid_exit. The potential races can lead to leaks/duplicates, attempts to destroy non-existant devices, or random pointer dereferences.
For example, in cpuid_exit if:
for_each_online_cpu(cpu)
cpuid_device_destroy(cpu);
class_destroy(cpuid_class);
__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
<----- CPU onlines
unregister_hotcpu_notifier(&cpuid_class_cpu_notifier);the hotcpu notifier will attempt to create a device for the cpuid_class, which the module already destroyed.
This fix surrounds for_each_online_cpu and register_hotcpu_notifier or unregister_hotcpu_notifier with get_online_cpus+put_online_cpus.
Tested on a VM.
--- arch/x86/kernel/cpuid.c | 5 +++++ 1 file changed, 5 insertions(+)
arch/x86/kernel/cpuid.c +5 -0
@@ -199,12 +199,14 @@ static int __init cpuid_init(void) goto out_chrdev; } cpuid_class->devnode = cpuid_devnode; +get_online_cpus ();for_each_online_cpu (i) { err =cpuid_device_create (i);if (err !=0 ) goto out_class; }register_hotcpu_notifier (&cpuid_class_cpu_notifier); +put_online_cpus (); err =0 ; goto out;
@@ -214,6 +216,7 @@ out_class:for_each_online_cpu (i) {cpuid_device_destroy (i); } +put_online_cpus ();class_destroy (cpuid_class); out_chrdev:__unregister_chrdev (CPUID_MAJOR ,0 ,NR_CPUS ,"cpu/cpuid" );
@@ -225,11 +228,13 @@ static void __exit cpuid_exit(void) {int cpu =0 ; +get_online_cpus ();for_each_online_cpu (cpu)cpuid_device_destroy (cpu);class_destroy (cpuid_class);__unregister_chrdev (CPUID_MAJOR ,0 ,NR_CPUS ,"cpu/cpuid" );unregister_hotcpu_notifier (&cpuid_class_cpu_notifier); +put_online_cpus (); }module_init (cpuid_init);
Signature · 1 line
1.7.10.4
Quoted 19 lines · Silas Boyd-Wickizer
If
arch/x86/kernel/cpuid.cis a module, a CPU might offline or online between thefor_each_online_cpu()loop and the call toregister_hotcpu_notifierincpuid_initor the call tounregister_hotcpu_notifierincpuid_exit. The potential races can lead to leaks/duplicates, attempts to destroy non-existant devices, or random pointer dereferences.For example, in
cpuid_exitif:for_each_online_cpu(cpu) cpuid_device_destroy(cpu); class_destroy(cpuid_class); __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid"); <----- CPU onlines unregister_hotcpu_notifier(&cpuid_class_cpu_notifier);the hotcpu notifier will attempt to create a device for the
cpuid_class, which the module already destroyed.
Quoted 6 lines
This fix surrounds
for_each_online_cpuandregister_hotcpu_notifierorunregister_hotcpu_notifierwith get_online_cpus+put_online_cpus.Tested on a VM.
Signed-off-by Silas Boyd-Wickizer <[email protected]>
Quoted 48 lines
--- arch/x86/kernel/cpuid.c | 5 +++++ 1 file changed, 5 insertions(+)arch/x86/kernel/cpuid.c +5
-0@@ -199,12 +199,14 @@ static int __init cpuid_init(void) goto out_chrdev; } cpuid_class->devnode = cpuid_devnode; +get_online_cpus ();for_each_online_cpu (i) { err =cpuid_device_create (i);if (err !=0 ) goto out_class; }register_hotcpu_notifier (&cpuid_class_cpu_notifier); +put_online_cpus (); err =0 ; goto out;@@ -214,6 +216,7 @@ out_class:for_each_online_cpu (i) {cpuid_device_destroy (i); } +put_online_cpus ();class_destroy (cpuid_class); out_chrdev:__unregister_chrdev (CPUID_MAJOR ,0 ,NR_CPUS ,"cpu/cpuid" );@@ -225,11 +228,13 @@ static void __exit cpuid_exit(void) {int cpu =0 ; +get_online_cpus ();for_each_online_cpu (cpu)cpuid_device_destroy (cpu);class_destroy (cpuid_class);__unregister_chrdev (CPUID_MAJOR ,0 ,NR_CPUS ,"cpu/cpuid" );unregister_hotcpu_notifier (&cpuid_class_cpu_notifier); +put_online_cpus (); }module_init (cpuid_init);Signature · 1 line
1.7.10.4