Xen Security Advisory 388 v3 (CVE-2021-28704,CVE-2021-28707,CVE-2021-28708) - PoD operations on misaligned GFNs
Xen.org security team <[email protected]> Tue, 23 Nov 2021 12:11:13 +0000
| Newsgroups | gmane.comp.emulators.xen.announce |
|---|---|
| Message-ID | <E1mpUdl-0004Ug-Aw__349.631688889929$1637669732$gmane$org@xenbits.xenproject.org> |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Xen Security Advisory CVE-2021-28704,CVE-2021-28707,CVE-2021-28708 / XSA-388
version 3
PoD operations on misaligned GFNs
UPDATES IN VERSION 3
====================
Correct affected versions range.
Add CVE numbers to patches.
Public release.
ISSUE DESCRIPTION
=================
x86 HVM and PVH guests may be started in populate-on-demand (PoD) mode,
to provide a way for them to later easily have more memory assigned.
Guests are permitted to control certain P2M aspects of individual
pages via hypercalls. These hypercalls may act on ranges of pages
specified via page orders (resulting in a power-of-2 number of pages).
The implementation of some of these hypercalls for PoD does not
enforce the base page frame number to be suitably aligned for the
specified order, yet some code involved in PoD handling actually makes
such an assumption.
These operations are XENMEM_decrease_reservation (CVE-2021-28704) and
XENMEM_populate_physmap (CVE-2021-28707), the latter usable only by
domains controlling the guest, i.e. a de-privileged qemu or a stub
domain. (Patch 1, combining the fix to both these two issues.)
In addition handling of XENMEM_decrease_reservation can also trigger a
host crash when the specified page order is neither 4k nor 2M nor 1G
(CVE-2021-28708, patch 2).
IMPACT
======
Malicious or buggy guest kernels may be able to mount a Denial of
Service (DoS) attack affecting the entire system. Privilege escalation
and information leaks cannot be ruled out.
VULNERABLE SYSTEMS
==================
All Xen versions from 4.7 onwards are affected. Xen versions 4.6 and
older are not affected.
Only x86 HVM and PVH guests started in populate-on-demand mode can
leverage the vulnerability. Populate-on-demand mode is activated
when the guest's xl configuration file specifies a "maxmem" value which
is larger than the "memory" value.
MITIGATION
==========
Not starting x86 HVM or PVH guests in populate-on-demand mode will avoid
the vulnerability.
CREDITS
=======
This issue was discovered by Jan Beulich of SUSE.
RESOLUTION
==========
Applying the appropriate pair if attached patches resolves this issue.
Note that patches for released versions are generally prepared to
apply to the stable branches, and may not apply cleanly to the most
recent release tarball. Downstreams are encouraged to update to the
tip of the stable branch before applying these patches.
xsa388-?.patch xen-unstable
xsa388-4.15-?.patch Xen 4.15.x
xsa388-4.14-?.patch Xen 4.14.x - 4.12.x
$ sha256sum xsa388*
43f6647e9f7d28d22eeb98680e116b301b0e29ef63ea65c9839a5aaebd449bc4 xsa388-1.patch
64b27a8c7c02036528e00a3070e27e873762d68f4ea1504e906aaf2ddc1c06be xsa388-2.patch
6917267482101a3f8f1d13905e14994344a0af81370c7a2b92275fb176b321a0 xsa388-4.14-1.patch
d5886e046c69f34f98f7e1fc6ffcc36d92f8fc79242b9dc88412c39aa79b4ac3 xsa388-4.14-2.patch
fbe6af409447edc2318940d7c4bc0861a236d40db037166608fc09fa57ef54b1 xsa388-4.15-1.patch
c828d735aaa3f430ccef314bf27519cd6a5f4daaa79e1c493dc47e42ab09ec9f xsa388-4.15-2.patch
$
DEPLOYMENT DURING EMBARGO
=========================
Deployment of the patches described above (or others which are
substantially similar) is permitted during the embargo, even on public-
facing systems with untrusted guest users and administrators.
HOWEVER, deployment of the mitigation described above is NOT permitted
during the embargo on public-facing systems with untrusted guest users
and administrators. This is because such a configuration change is
recognizable by the affected guests.
AND: Distribution of updated software is prohibited (except to other
members of the predisclosure list).
Predisclosure list members who wish to deploy significantly different
patches and/or mitigations, please contact the Xen Project Security
Team.
(Note: this during-embargo deployment notice is retained in
post-embargo publicly released Xen Project advisories, even though it
is then no longer applicable. This is to enable the community to have
oversight of the Xen Project Security Team's decisionmaking.)
For more information about permissible uses of embargoed information,
consult the Xen Project community's agreed Security Policy:
http://www.xenproject.org/security-policy.html
-----BEGIN PGP SIGNATURE-----
iQFABAEBCAAqFiEEI+MiLBRfRHX6gGCng/4UyVfoK9kFAmGc2jkMHHBncEB4ZW4u
b3JnAAoJEIP+FMlX6CvZROMIALJsptV0nV8H5/nCLUWld3mKjAeb/+N20ul9NEwn
rUwIGGGzyrKZQdAljno+9y9o5pM8+BC+aTBwYhmxEWsHm1kodTD+YnJYf8uNW/CW
uhTJp/ZB5EsWhTFHF7YoKbPG0on4KIsy0TgoUug7bv+l2zEny9gfknsj8jdp3qCy
aFv1Bb2PzRh462qVHI3f27Ee8bn7GfErouuLppmDpCva19D3bhUXQ5PhxFB+oqsI
bww4VKUo0nxZftYhpbInWm34dajEIXK7jy5Z/CUPgCj2sTOHHBv7+5JJdw0umn/A
lJ2Ta1u03sdC9JWbat4qjvdVgK9L9vT+jWsfcwk02qq+XSU=
=uSRt
-----END PGP SIGNATURE-----
xsa388-1.patch
(application/octet-stream, 6 KB)
From: Jan Beulich <[email protected]> Subject: x86/PoD: deal with misaligned GFNs Users of XENMEM_decrease_reservation and XENMEM_populate_physmap aren't required to pass in order-aligned GFN values. (While I consider this bogus, I don't think we can fix this there, as that might break existing code, e.g Linux'es swiotlb, which - while affecting PV only - until recently had been enforcing only page alignment on the original allocation.) Only non-PoD code paths (guest_physmap_{add,remove}_page(), p2m_set_entry()) look to be dealing with this properly (in part by being implemented inefficiently, handling every 4k page separately). Introduce wrappers taking care of splitting the incoming request into aligned chunks, without putting much effort in trying to determine the largest possible chunk at every iteration. Also "handle" p2m_set_entry() failure for non-order-0 requests by crashing the domain in one more place. Alongside putting a log message there, also add one to the other similar path. Note regarding locking: This is left in the actual worker functions on the assumption that callers aren't guaranteed atomicity wrt acting on multiple pages at a time. For mis-aligned GFNs gfn_lock() wouldn't have locked the correct GFN range anyway, if it didn't simply resolve to p2m_lock(), and for well-behaved callers there continues to be only a single iteration, i.e. behavior is unchanged for them. (FTAOD pulling out just pod_lock() into p2m_pod_decrease_reservation() would result in a lock order violation.) This is CVE-2021-28704 and CVE-2021-28707 / part of XSA-388. Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") Signed-off-by: Jan Beulich <[email protected]> Reviewed-by: Roger Pau Monné <[email protected]> --- The security aspect here stems from the various BUG_ON()s we have checking - p2m->pod.entry_count, - the alignment of the MFN p2m_pod_cache_get() returns. --- v3: Re-do using wrappers. v2: Prefix domain_crash() by printk(XENLOG_G_ERR ...) (including the pre-existing similar one). --- a/xen/arch/x86/mm/p2m-pod.c +++ b/xen/arch/x86/mm/p2m-pod.c @@ -504,7 +504,7 @@ static void pod_unlock_and_flush(struct } /* - * This function is needed for two reasons: + * This pair of functions is needed for two reasons: * + To properly handle clearing of PoD entries * + To "steal back" memory being freed for the PoD cache, rather than * releasing it. @@ -512,8 +512,8 @@ static void pod_unlock_and_flush(struct * Once both of these functions have been completed, we can return and * allow decrease_reservation() to handle everything else. */ -unsigned long -p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) +static unsigned long +decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) { unsigned long ret = 0, i, n; struct p2m_domain *p2m = p2m_get_hostp2m(d); @@ -561,8 +561,10 @@ p2m_pod_decrease_reservation(struct doma * All PoD: Mark the whole region invalid and tell caller * we're done. */ - if ( p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, - p2m->default_access) ) + int rc = p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, + p2m->default_access); + + if ( rc ) { /* * If this fails, we can't tell how much of the range was changed. @@ -570,7 +572,12 @@ p2m_pod_decrease_reservation(struct doma * impossible. */ if ( order != 0 ) + { + printk(XENLOG_G_ERR + "%pd: marking GFN %#lx (order %u) as non-PoD failed: %d\n", + d, gfn_x(gfn), order, rc); domain_crash(d); + } goto out_unlock; } ret = 1UL << order; @@ -679,6 +686,22 @@ out_unlock: return ret; } +unsigned long +p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) +{ + unsigned long left = 1UL << order, ret = 0; + unsigned int chunk_order = find_first_set_bit(gfn_x(gfn) | left); + + do { + ret += decrease_reservation(d, gfn, chunk_order); + + left -= 1UL << chunk_order; + gfn = gfn_add(gfn, 1UL << chunk_order); + } while ( left ); + + return ret; +} + void p2m_pod_dump_data(struct domain *d) { struct p2m_domain *p2m = p2m_get_hostp2m(d); @@ -1289,19 +1312,15 @@ remap_and_retry: return true; } - -int -guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn_l, - unsigned int order) +static int +mark_populate_on_demand(struct domain *d, unsigned long gfn_l, + unsigned int order) { struct p2m_domain *p2m = p2m_get_hostp2m(d); gfn_t gfn = _gfn(gfn_l); unsigned long i, n, pod_count = 0; int rc = 0; - if ( !paging_mode_translate(d) ) - return -EINVAL; - gfn_lock(p2m, gfn, order); P2M_DEBUG("mark pod gfn=%#lx\n", gfn_l); @@ -1341,12 +1360,44 @@ guest_physmap_mark_populate_on_demand(st ioreq_request_mapcache_invalidate(d); } + else if ( order ) + { + /* + * If this failed, we can't tell how much of the range was changed. + * Best to crash the domain. + */ + printk(XENLOG_G_ERR + "%pd: marking GFN %#lx (order %u) as PoD failed: %d\n", + d, gfn_l, order, rc); + domain_crash(d); + } out: gfn_unlock(p2m, gfn, order); return rc; } + +int +guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn, + unsigned int order) +{ + unsigned long left = 1UL << order; + unsigned int chunk_order = find_first_set_bit(gfn | left); + int rc; + + if ( !paging_mode_translate(d) ) + return -EINVAL; + + do { + rc = mark_populate_on_demand(d, gfn, chunk_order); + + left -= 1UL << chunk_order; + gfn += 1UL << chunk_order; + } while ( !rc && left ); + + return rc; +} void p2m_pod_init(struct p2m_domain *p2m) {
xsa388-2.patch
(application/octet-stream, 1.4 KB)
From: Jan Beulich <[email protected]> Subject: x86/PoD: handle intermediate page orders in p2m_pod_cache_add() p2m_pod_decrease_reservation() may pass pages to the function which aren't 4k, 2M, or 1G. Handle all intermediate orders as well, to avoid hitting the BUG() at the switch() statement's "default" case. This is CVE-2021-28708 / part of XSA-388. Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") Signed-off-by: Jan Beulich <[email protected]> Reviewed-by: Roger Pau Monné <[email protected]> --- a/xen/arch/x86/mm/p2m-pod.c +++ b/xen/arch/x86/mm/p2m-pod.c @@ -114,15 +114,13 @@ p2m_pod_cache_add(struct p2m_domain *p2m /* Then add to the appropriate populate-on-demand list. */ switch ( order ) { - case PAGE_ORDER_1G: - for ( i = 0; i < (1UL << PAGE_ORDER_1G); i += 1UL << PAGE_ORDER_2M ) + case PAGE_ORDER_2M ... PAGE_ORDER_1G: + for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_2M ) page_list_add_tail(page + i, &p2m->pod.super); break; - case PAGE_ORDER_2M: - page_list_add_tail(page, &p2m->pod.super); - break; - case PAGE_ORDER_4K: - page_list_add_tail(page, &p2m->pod.single); + case PAGE_ORDER_4K ... PAGE_ORDER_2M - 1: + for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_4K ) + page_list_add_tail(page + i, &p2m->pod.single); break; default: BUG();
xsa388-4.14-1.patch
(application/octet-stream, 5.8 KB)
From: Jan Beulich <[email protected]> Subject: x86/PoD: deal with misaligned GFNs Users of XENMEM_decrease_reservation and XENMEM_populate_physmap aren't required to pass in order-aligned GFN values. (While I consider this bogus, I don't think we can fix this there, as that might break existing code, e.g Linux'es swiotlb, which - while affecting PV only - until recently had been enforcing only page alignment on the original allocation.) Only non-PoD code paths (guest_physmap_{add,remove}_page(), p2m_set_entry()) look to be dealing with this properly (in part by being implemented inefficiently, handling every 4k page separately). Introduce wrappers taking care of splitting the incoming request into aligned chunks, without putting much effort in trying to determine the largest possible chunk at every iteration. Also "handle" p2m_set_entry() failure for non-order-0 requests by crashing the domain in one more place. Alongside putting a log message there, also add one to the other similar path. Note regarding locking: This is left in the actual worker functions on the assumption that callers aren't guaranteed atomicity wrt acting on multiple pages at a time. For mis-aligned GFNs gfn_lock() wouldn't have locked the correct GFN range anyway, if it didn't simply resolve to p2m_lock(), and for well-behaved callers there continues to be only a single iteration, i.e. behavior is unchanged for them. (FTAOD pulling out just pod_lock() into p2m_pod_decrease_reservation() would result in a lock order violation.) This is CVE-2021-28704 and CVE-2021-28707 / part of XSA-388. Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") Signed-off-by: Jan Beulich <[email protected]> Reviewed-by: Roger Pau Monné <[email protected]> --- a/xen/arch/x86/mm/p2m-pod.c +++ b/xen/arch/x86/mm/p2m-pod.c @@ -495,7 +495,7 @@ p2m_pod_zero_check_superpage(struct p2m_ /* - * This function is needed for two reasons: + * This pair of functions is needed for two reasons: * + To properly handle clearing of PoD entries * + To "steal back" memory being freed for the PoD cache, rather than * releasing it. @@ -503,8 +503,8 @@ p2m_pod_zero_check_superpage(struct p2m_ * Once both of these functions have been completed, we can return and * allow decrease_reservation() to handle everything else. */ -unsigned long -p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) +static unsigned long +decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) { unsigned long ret = 0, i, n; struct p2m_domain *p2m = p2m_get_hostp2m(d); @@ -551,8 +551,10 @@ p2m_pod_decrease_reservation(struct doma * All PoD: Mark the whole region invalid and tell caller * we're done. */ - if ( p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, - p2m->default_access) ) + int rc = p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, + p2m->default_access); + + if ( rc ) { /* * If this fails, we can't tell how much of the range was changed. @@ -560,7 +562,12 @@ p2m_pod_decrease_reservation(struct doma * impossible. */ if ( order != 0 ) + { + printk(XENLOG_G_ERR + "%pd: marking GFN %#lx (order %u) as non-PoD failed: %d\n", + d, gfn_x(gfn), order, rc); domain_crash(d); + } goto out_unlock; } ret = 1UL << order; @@ -667,6 +674,22 @@ out_unlock: return ret; } +unsigned long +p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) +{ + unsigned long left = 1UL << order, ret = 0; + unsigned int chunk_order = find_first_set_bit(gfn_x(gfn) | left); + + do { + ret += decrease_reservation(d, gfn, chunk_order); + + left -= 1UL << chunk_order; + gfn = gfn_add(gfn, 1UL << chunk_order); + } while ( left ); + + return ret; +} + void p2m_pod_dump_data(struct domain *d) { struct p2m_domain *p2m = p2m_get_hostp2m(d); @@ -1266,19 +1289,15 @@ remap_and_retry: return true; } - -int -guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn_l, - unsigned int order) +static int +mark_populate_on_demand(struct domain *d, unsigned long gfn_l, + unsigned int order) { struct p2m_domain *p2m = p2m_get_hostp2m(d); gfn_t gfn = _gfn(gfn_l); unsigned long i, n, pod_count = 0; int rc = 0; - if ( !paging_mode_translate(d) ) - return -EINVAL; - gfn_lock(p2m, gfn, order); P2M_DEBUG("mark pod gfn=%#lx\n", gfn_l); @@ -1316,12 +1335,44 @@ guest_physmap_mark_populate_on_demand(st BUG_ON(p2m->pod.entry_count < 0); pod_unlock(p2m); } + else if ( order ) + { + /* + * If this failed, we can't tell how much of the range was changed. + * Best to crash the domain. + */ + printk(XENLOG_G_ERR + "%pd: marking GFN %#lx (order %u) as PoD failed: %d\n", + d, gfn_l, order, rc); + domain_crash(d); + } out: gfn_unlock(p2m, gfn, order); return rc; } + +int +guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn, + unsigned int order) +{ + unsigned long left = 1UL << order; + unsigned int chunk_order = find_first_set_bit(gfn | left); + int rc; + + if ( !paging_mode_translate(d) ) + return -EINVAL; + + do { + rc = mark_populate_on_demand(d, gfn, chunk_order); + + left -= 1UL << chunk_order; + gfn += 1UL << chunk_order; + } while ( !rc && left ); + + return rc; +} void p2m_pod_init(struct p2m_domain *p2m) {
xsa388-4.14-2.patch
(application/octet-stream, 1.4 KB)
From: Jan Beulich <[email protected]> Subject: x86/PoD: handle intermediate page orders in p2m_pod_cache_add() p2m_pod_decrease_reservation() may pass pages to the function which aren't 4k, 2M, or 1G. Handle all intermediate orders as well, to avoid hitting the BUG() at the switch() statement's "default" case. This is CVE-2021-28708 / part of XSA-388. Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") Signed-off-by: Jan Beulich <[email protected]> Reviewed-by: Roger Pau Monné <[email protected]> --- a/xen/arch/x86/mm/p2m-pod.c +++ b/xen/arch/x86/mm/p2m-pod.c @@ -111,15 +111,13 @@ p2m_pod_cache_add(struct p2m_domain *p2m /* Then add to the appropriate populate-on-demand list. */ switch ( order ) { - case PAGE_ORDER_1G: - for ( i = 0; i < (1UL << PAGE_ORDER_1G); i += 1UL << PAGE_ORDER_2M ) + case PAGE_ORDER_2M ... PAGE_ORDER_1G: + for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_2M ) page_list_add_tail(page + i, &p2m->pod.super); break; - case PAGE_ORDER_2M: - page_list_add_tail(page, &p2m->pod.super); - break; - case PAGE_ORDER_4K: - page_list_add_tail(page, &p2m->pod.single); + case PAGE_ORDER_4K ... PAGE_ORDER_2M - 1: + for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_4K ) + page_list_add_tail(page + i, &p2m->pod.single); break; default: BUG();
xsa388-4.15-1.patch
(application/octet-stream, 5.8 KB)
From: Jan Beulich <[email protected]> Subject: x86/PoD: deal with misaligned GFNs Users of XENMEM_decrease_reservation and XENMEM_populate_physmap aren't required to pass in order-aligned GFN values. (While I consider this bogus, I don't think we can fix this there, as that might break existing code, e.g Linux'es swiotlb, which - while affecting PV only - until recently had been enforcing only page alignment on the original allocation.) Only non-PoD code paths (guest_physmap_{add,remove}_page(), p2m_set_entry()) look to be dealing with this properly (in part by being implemented inefficiently, handling every 4k page separately). Introduce wrappers taking care of splitting the incoming request into aligned chunks, without putting much effort in trying to determine the largest possible chunk at every iteration. Also "handle" p2m_set_entry() failure for non-order-0 requests by crashing the domain in one more place. Alongside putting a log message there, also add one to the other similar path. Note regarding locking: This is left in the actual worker functions on the assumption that callers aren't guaranteed atomicity wrt acting on multiple pages at a time. For mis-aligned GFNs gfn_lock() wouldn't have locked the correct GFN range anyway, if it didn't simply resolve to p2m_lock(), and for well-behaved callers there continues to be only a single iteration, i.e. behavior is unchanged for them. (FTAOD pulling out just pod_lock() into p2m_pod_decrease_reservation() would result in a lock order violation.) This is CVE-2021-28704 and CVE-2021-28707 / part of XSA-388. Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") Signed-off-by: Jan Beulich <[email protected]> Reviewed-by: Roger Pau Monné <[email protected]> --- a/xen/arch/x86/mm/p2m-pod.c +++ b/xen/arch/x86/mm/p2m-pod.c @@ -496,7 +496,7 @@ p2m_pod_zero_check_superpage(struct p2m_ /* - * This function is needed for two reasons: + * This pair of functions is needed for two reasons: * + To properly handle clearing of PoD entries * + To "steal back" memory being freed for the PoD cache, rather than * releasing it. @@ -504,8 +504,8 @@ p2m_pod_zero_check_superpage(struct p2m_ * Once both of these functions have been completed, we can return and * allow decrease_reservation() to handle everything else. */ -unsigned long -p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) +static unsigned long +decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) { unsigned long ret = 0, i, n; struct p2m_domain *p2m = p2m_get_hostp2m(d); @@ -552,8 +552,10 @@ p2m_pod_decrease_reservation(struct doma * All PoD: Mark the whole region invalid and tell caller * we're done. */ - if ( p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, - p2m->default_access) ) + int rc = p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, + p2m->default_access); + + if ( rc ) { /* * If this fails, we can't tell how much of the range was changed. @@ -561,7 +563,12 @@ p2m_pod_decrease_reservation(struct doma * impossible. */ if ( order != 0 ) + { + printk(XENLOG_G_ERR + "%pd: marking GFN %#lx (order %u) as non-PoD failed: %d\n", + d, gfn_x(gfn), order, rc); domain_crash(d); + } goto out_unlock; } ret = 1UL << order; @@ -670,6 +677,22 @@ out_unlock: return ret; } +unsigned long +p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) +{ + unsigned long left = 1UL << order, ret = 0; + unsigned int chunk_order = find_first_set_bit(gfn_x(gfn) | left); + + do { + ret += decrease_reservation(d, gfn, chunk_order); + + left -= 1UL << chunk_order; + gfn = gfn_add(gfn, 1UL << chunk_order); + } while ( left ); + + return ret; +} + void p2m_pod_dump_data(struct domain *d) { struct p2m_domain *p2m = p2m_get_hostp2m(d); @@ -1273,19 +1296,15 @@ remap_and_retry: return true; } - -int -guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn_l, - unsigned int order) +static int +mark_populate_on_demand(struct domain *d, unsigned long gfn_l, + unsigned int order) { struct p2m_domain *p2m = p2m_get_hostp2m(d); gfn_t gfn = _gfn(gfn_l); unsigned long i, n, pod_count = 0; int rc = 0; - if ( !paging_mode_translate(d) ) - return -EINVAL; - gfn_lock(p2m, gfn, order); P2M_DEBUG("mark pod gfn=%#lx\n", gfn_l); @@ -1325,12 +1344,44 @@ guest_physmap_mark_populate_on_demand(st ioreq_request_mapcache_invalidate(d); } + else if ( order ) + { + /* + * If this failed, we can't tell how much of the range was changed. + * Best to crash the domain. + */ + printk(XENLOG_G_ERR + "%pd: marking GFN %#lx (order %u) as PoD failed: %d\n", + d, gfn_l, order, rc); + domain_crash(d); + } out: gfn_unlock(p2m, gfn, order); return rc; } + +int +guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn, + unsigned int order) +{ + unsigned long left = 1UL << order; + unsigned int chunk_order = find_first_set_bit(gfn | left); + int rc; + + if ( !paging_mode_translate(d) ) + return -EINVAL; + + do { + rc = mark_populate_on_demand(d, gfn, chunk_order); + + left -= 1UL << chunk_order; + gfn += 1UL << chunk_order; + } while ( !rc && left ); + + return rc; +} void p2m_pod_init(struct p2m_domain *p2m) {
xsa388-4.15-2.patch
(application/octet-stream, 1.4 KB)
From: Jan Beulich <[email protected]> Subject: x86/PoD: handle intermediate page orders in p2m_pod_cache_add() p2m_pod_decrease_reservation() may pass pages to the function which aren't 4k, 2M, or 1G. Handle all intermediate orders as well, to avoid hitting the BUG() at the switch() statement's "default" case. This is CVE-2021-28708 / part of XSA-388. Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") Signed-off-by: Jan Beulich <[email protected]> Reviewed-by: Roger Pau Monné <[email protected]> --- a/xen/arch/x86/mm/p2m-pod.c +++ b/xen/arch/x86/mm/p2m-pod.c @@ -112,15 +112,13 @@ p2m_pod_cache_add(struct p2m_domain *p2m /* Then add to the appropriate populate-on-demand list. */ switch ( order ) { - case PAGE_ORDER_1G: - for ( i = 0; i < (1UL << PAGE_ORDER_1G); i += 1UL << PAGE_ORDER_2M ) + case PAGE_ORDER_2M ... PAGE_ORDER_1G: + for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_2M ) page_list_add_tail(page + i, &p2m->pod.super); break; - case PAGE_ORDER_2M: - page_list_add_tail(page, &p2m->pod.super); - break; - case PAGE_ORDER_4K: - page_list_add_tail(page, &p2m->pod.single); + case PAGE_ORDER_4K ... PAGE_ORDER_2M - 1: + for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_4K ) + page_list_add_tail(page + i, &p2m->pod.single); break; default: BUG();