mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
vNUMA: close race window in domain cleanup
Calling vnuma_destroy() without any locking can race the handling of both XENMEM_get_vnumainfo and XEN_DOMCTL_setvnumainfo. While the latter is, without Flask, strictly only exposed to the control domain, the former can also be invoked by a stubdom DM or a de-privileged DM running in the control domain. Isolate the logic used by XEN_DOMCTL_setvnumainfo into a new helper function, which then is also used from domain_kill(). While doing so also move the vnuma_destroy() invocation out of the locked region. With d->vnuma properly cleared by domain_kill(), XENMEM_get_vnumainfo now really only needs to check for the field being NULL. That check needs repeating, though, after re-acquiring the lock. This is CVE-2026-62429 / XSA-502. Fixes:9695014966("xen: vnuma topology and subop hypercalls") Reported-by: Teddy Astie <teddy.astie@vates.tech> Signed-off-by: Jan Beulich <jbeulich@suse.com> Reviewed-by: Juergen Gross <jgross@suse.com> (cherry picked from commit2afa6fea9d)
This commit is contained in:
1 parent
060c68ab1d
commit
bed5d12bd4
4 files changed
+33
-12
No files matched your search
+1
-1
@@ -1255,7 +1255,7 @@ int domain_kill(struct domain *d)
|
||||
d->is_dying = DOMDYING_dying;
|
||||
rspin_barrier(&d->domain_lock);
|
||||
argo_destroy(d);
|
||||
vnuma_destroy(d->vnuma);
|
||||
vnuma_replace(d, NULL);
|
||||
domain_set_outstanding_pages(d, 0);
|
||||
/* fallthrough */
|
||||
case DOMDYING_dying:
|
||||
|
||||
+15
-7
@@ -142,7 +142,7 @@ void domctl_lock_release(void)
|
||||
spin_unlock(¤t->domain->hypercall_deadlock_mutex);
|
||||
}
|
||||
|
||||
void vnuma_destroy(struct vnuma_info *vnuma)
|
||||
static void vnuma_destroy(struct vnuma_info *vnuma)
|
||||
{
|
||||
if ( vnuma )
|
||||
{
|
||||
@@ -154,6 +154,19 @@ void vnuma_destroy(struct vnuma_info *vnuma)
|
||||
}
|
||||
}
|
||||
|
||||
/* Overwrite (replace) vnuma topology for a domain. */
|
||||
void vnuma_replace(struct domain *d, struct vnuma_info *vnuma)
|
||||
{
|
||||
struct vnuma_info *old;
|
||||
|
||||
write_lock(&d->vnuma_rwlock);
|
||||
old = d->vnuma;
|
||||
d->vnuma = vnuma;
|
||||
write_unlock(&d->vnuma_rwlock);
|
||||
|
||||
vnuma_destroy(old);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates memory for vNUMA, **vnuma should be NULL.
|
||||
* Caller has to make sure that domain has max_pages
|
||||
@@ -907,12 +920,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl)
|
||||
break;
|
||||
}
|
||||
|
||||
/* overwrite vnuma topology for domain. */
|
||||
write_lock(&d->vnuma_rwlock);
|
||||
vnuma_destroy(d->vnuma);
|
||||
d->vnuma = vnuma;
|
||||
write_unlock(&d->vnuma_rwlock);
|
||||
|
||||
vnuma_replace(d, vnuma);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+14
-2
@@ -1804,12 +1804,24 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
|
||||
goto vnumainfo_out;
|
||||
}
|
||||
|
||||
read_lock(&d->vnuma_rwlock);
|
||||
|
||||
/*
|
||||
* Check d->vnuma again after re-acquiring the lock as we can race
|
||||
* with domain destruction.
|
||||
*/
|
||||
if ( !d->vnuma )
|
||||
{
|
||||
ASSERT(d->is_dying);
|
||||
read_unlock(&d->vnuma_rwlock);
|
||||
rc = -ESRCH;
|
||||
goto vnumainfo_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if vnuma info has changed and if the allocated arrays
|
||||
* are not big enough.
|
||||
*/
|
||||
read_lock(&d->vnuma_rwlock);
|
||||
|
||||
if ( dom_vnodes < d->vnuma->nr_vnodes ||
|
||||
dom_vranges < d->vnuma->nr_vmemranges ||
|
||||
dom_vcpus < d->max_vcpus )
|
||||
|
||||
@@ -188,9 +188,10 @@ struct vnuma_info {
|
||||
};
|
||||
|
||||
#ifndef CONFIG_PV_SHIM_EXCLUSIVE
|
||||
void vnuma_destroy(struct vnuma_info *vnuma);
|
||||
void vnuma_replace(struct domain *d, struct vnuma_info *vnuma);
|
||||
#else
|
||||
static inline void vnuma_destroy(struct vnuma_info *vnuma) { ASSERT(!vnuma); }
|
||||
static inline void vnuma_replace(struct domain *d, struct vnuma_info *vnuma)
|
||||
{ ASSERT(!vnuma); }
|
||||
#endif
|
||||
|
||||
extern bool vmtrace_available;
|
||||
|
||||