aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Cooper <andrew.cooper3@citrix.com>2013-10-22 12:03:03 +0200
committerJan Beulich <jbeulich@suse.com>2013-10-22 12:03:03 +0200
commitbb3e0cc28ba3d519ca78a4ce19ff6493b496aeee (patch)
treed2771d07ccecc39d2c0d148d46c71aac704efffa
parenteba971d94289d91e4a3959d2c083a59deb100568 (diff)
downloadxen-bb3e0cc28ba3d519ca78a4ce19ff6493b496aeee.tar.gz
xen-bb3e0cc28ba3d519ca78a4ce19ff6493b496aeee.tar.bz2
xen-bb3e0cc28ba3d519ca78a4ce19ff6493b496aeee.zip
x86/idle: Fix get_cpu_idle_time()'s interaction with offline pcpus
Checking for "idle_vcpu[cpu] != NULL" is insufficient protection against offline pcpus. From a hypercall, vcpu_runstate_get() will determine "v != current", and try to take the vcpu_schedule_lock(). This will try to look up per_cpu(schedule_data, v->processor) and promptly suffer a NULL structure deference as v->processors' __per_cpu_offset is INVALID_PERCPU_AREA. One example might look like this: ... Xen call trace: [<ffff82c4c0126ddb>] vcpu_runstate_get+0x50/0x113 [<ffff82c4c0126ec6>] get_cpu_idle_time+0x28/0x2e [<ffff82c4c012b5cb>] do_sysctl+0x3db/0xeb8 [<ffff82c4c023280d>] compat_hypercall+0xbd/0x116 Pagetable walk from 0000000000000040: L4[0x000] = 0000000186df8027 0000000000028207 L3[0x000] = 0000000188e36027 00000000000261c9 L2[0x000] = 0000000000000000 ffffffffffffffff **************************************** Panic on CPU 11: ... get_cpu_idle_time() has been updated to correctly deal with offline pcpus itself by returning 0, in the same way as it would if it was missing the idle_vcpu[] pointer. In doing so, XENPF_getidletime needed updating to correctly retain its described behaviour of clearing bits in the cpumap for offline pcpus. As this crash can only be triggered with toolstack hypercalls, it is not a security issue and just a simple bug. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Acked-by: Keir Fraser <keir@xen.org> master commit: 0aa27ce3351f7eb09d13e863a1d5f303086aa32a master date: 2013-10-04 12:23:23 +0200
-rw-r--r--xen/arch/x86/platform_hypercall.c8
-rw-r--r--xen/common/schedule.c9
2 files changed, 10 insertions, 7 deletions
diff --git a/xen/arch/x86/platform_hypercall.c b/xen/arch/x86/platform_hypercall.c
index 88880b0d63..96895a3d4b 100644
--- a/xen/arch/x86/platform_hypercall.c
+++ b/xen/arch/x86/platform_hypercall.c
@@ -388,10 +388,14 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xen_platform_op_t) u_xenpf_op)
for_each_cpu ( cpu, cpumap )
{
- if ( idle_vcpu[cpu] == NULL )
- cpumask_clear_cpu(cpu, cpumap);
idletime = get_cpu_idle_time(cpu);
+ if ( !idletime )
+ {
+ cpumask_clear_cpu(cpu, cpumap);
+ continue;
+ }
+
if ( copy_to_guest_offset(idletimes, cpu, &idletime, 1) )
{
ret = -EFAULT;
diff --git a/xen/common/schedule.c b/xen/common/schedule.c
index 7f298d827f..3f7d463185 100644
--- a/xen/common/schedule.c
+++ b/xen/common/schedule.c
@@ -175,13 +175,12 @@ void vcpu_runstate_get(struct vcpu *v, struct vcpu_runstate_info *runstate)
uint64_t get_cpu_idle_time(unsigned int cpu)
{
- struct vcpu_runstate_info state;
- struct vcpu *v;
+ struct vcpu_runstate_info state = { 0 };
+ struct vcpu *v = idle_vcpu[cpu];
- if ( (v = idle_vcpu[cpu]) == NULL )
- return 0;
+ if ( cpu_online(cpu) && v )
+ vcpu_runstate_get(v, &state);
- vcpu_runstate_get(v, &state);
return state.time[RUNSTATE_running];
}