Xen Security Advisory 99 - unexpected pitfall in xenaccess API
Xen.org security team <[email protected]>
| Newsgroups | gmane.comp.emulators.xen.announce |
|---|---|
| Message-ID | <E1WwsJm-00080M-3v__33426.0673415431$1403007819$gmane$org@xenbits.xen.org> |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Xen Security Advisory XSA-99
version 2
unexpected pitfall in xenaccess API
UPDATES IN VERSION 2
====================
Public Release.
Added note regarding CVE.
ISSUE DESCRIPTION
=================
A test/example program, for exercising the Xen memaccess API, does not
take all necessary precautions against hostile guest behaviour.
As a result, software developers using it as an example or template
might have written and deployed vulnerable code.
See the patch for technical details of the problem.
IMPACT
======
Deployments of software inspired by, or derived from,
xen.git/tools/tests/xen-access/xen-access.c, may be vulnerable to
privilege escalation by a malicious guest administrator.
xen-access is a test/example program and is not, without modification,
useful in production. It is not built or installed by default.
VULNERABLE SYSTEMS
==================
Unmodified Xen installations (including installations as provided by
typical Free Software distributions) are not vulnerable.
The following toolstacks/libraries do not use memaccess, so systems
using Xen only via the following are not vulnerable:
libxl; xl; xend; xm; libvirt
In general, Xen installations which make no use of the Xen memory
access API (xc_mem_access_..., "XENMEM_access_...",
XEN_DOMCTL_MEM_EVENT_OP_ACCESS_ENABLE) are not vulnerable.
Systems using the Xen hypervisor 4.1 or earlier are not vulnerable.
ARM systems are not vulnerable. AMD systems are not vulnerable.
Intel x86 systems without EPT are not vulnerable.
Software developers who have based their efforts on xen-access.c may
have constructed vulnerable systems. Such developers should examine
their software, and communicate with their own downstreams, as
applicable.
Users of Xen-derived systems, whose vulnerability is not excluded
above, should consult their vendor for information about the
applicability of this vulnerability.
MITIGATION
==========
Disabling whatever functionality uses the memaccess API will avoid the
vulnerability.
NOTE REGARDING CVE
==================
The CVE assignment team at the MITRE CVE Numbering Authority have told
us that type of issue is typically considered site-specific and is not
eligible for a CVE ID:
The scope of CVE does not include issues where a vulnerable program
can be present after a customer modifies shipped source code or
modifies the build process. The primary purpose of this guideline is
to avoid CVE assignments where, for example, the vulnerability exists
only when a customer enables experimental code and then recompiles. A
secondary purpose of this guideline is to avoid CVE assignments for
example code that wasn't intended to be used as-is.
Software developers who have based production code on xen-access.c
should obtain their own CVE number(s).
CREDITS
=======
This vulnerability was discovered by Ian Campbell of Citrix.
RESOLUTION
==========
The attached patch repairs the test/example utility provided in the
Xen Project source tree.
To resolve the issue in production software, appropriate changes
will have to be be made by its developers.
$ sha256sum xsa99*.patch
d6496699d9952bbfe1cd86e0ba84182e455a5dc4626654d387f92390d9680cd4 xsa99.patch
$
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)
iQEcBAEBAgAGBQJToCn/AAoJEIP+FMlX6CvZBp8H/Az39oLQiAIyrZRD+IktvGuB
mCLRcoyTJxxfE+9bAFltypelGNwq5NT/JUwub82whapbPW/e/rtGbln43FkdkoLu
oFlddcteOzJMTLsLXxe50zrgb4QaUEt4lxQ2zEyFpL6PYz32pO24NLK8QzG480Ol
4u1UlBJeYM61Z4JPuCy0h5vMy0eU6G3yry6B09s4Dmdfvd6AU7BprFT4/aW+noQ0
84w11iL8Y53ddnidTgaXNkyvcq+5m57RL9uHvrRz7mViqhazkVkxGZHVKsUYuRPb
wkBpSaa+cJkeF8AnDue/QuW0pWYpfrPoniD86SwgzsYYj5bN0EnQ4CTzVIAx284=
=9myT
-----END PGP SIGNATURE-----
_______________________________________________
Xen-announce mailing list
[email protected]
http://lists.xen.org/xen-announce
xsa99.patch
(application/octet-stream, 9.7 KB)
From: Aravindh Puthiyaparambil <[email protected]> Date: Tue May 20 16:35:44 2014 -0700 mem_access: Add helper API to setup ring and enable mem_access tools/libxc: Add helper function to setup ring for mem events This patch adds a helper function that maps the ring, enables mem_event and removes the ring from the guest physmap while the domain is paused. This can be used by all mem_events but is only enabled for mem_access at the moment. tests/xen-access: Use helper API to setup ring and enable mem_access Prior to this patch, xen-access was setting up the ring page in a way that would give a malicous guest a window to write in to the shared ring page. This patch fixes this by using the helper API that does it safely on behalf of xen-access. This is XSA-99. Signed-off-by: Aravindh Puthiyaparambil <[email protected]> Reviewed-by: Jan Beulich <[email protected]> diff --git a/tools/libxc/xc_mem_access.c b/tools/libxc/xc_mem_access.c index f436e69..461f0e9 100644 --- a/tools/libxc/xc_mem_access.c +++ b/tools/libxc/xc_mem_access.c @@ -24,19 +24,9 @@ #include "xc_private.h" #include <xen/memory.h> -int xc_mem_access_enable(xc_interface *xch, domid_t domain_id, - uint32_t *port) +void *xc_mem_access_enable(xc_interface *xch, domid_t domain_id, uint32_t *port) { - if ( !port ) - { - errno = EINVAL; - return -1; - } - - return xc_mem_event_control(xch, domain_id, - XEN_DOMCTL_MEM_EVENT_OP_ACCESS_ENABLE, - XEN_DOMCTL_MEM_EVENT_OP_ACCESS, - port); + return xc_mem_event_enable(xch, domain_id, HVM_PARAM_ACCESS_RING_PFN, port); } int xc_mem_access_disable(xc_interface *xch, domid_t domain_id) diff --git a/tools/libxc/xc_mem_event.c b/tools/libxc/xc_mem_event.c index d43a0af..be7c63d 100644 --- a/tools/libxc/xc_mem_event.c +++ b/tools/libxc/xc_mem_event.c @@ -56,3 +56,118 @@ int xc_mem_event_memop(xc_interface *xch, domid_t domain_id, return do_memory_op(xch, mode, &meo, sizeof(meo)); } +void *xc_mem_event_enable(xc_interface *xch, domid_t domain_id, int param, + uint32_t *port) +{ + void *ring_page = NULL; + unsigned long ring_pfn, mmap_pfn; + unsigned int op, mode; + int rc1, rc2, saved_errno; + + if ( !port ) + { + errno = EINVAL; + return NULL; + } + + /* Pause the domain for ring page setup */ + rc1 = xc_domain_pause(xch, domain_id); + if ( rc1 != 0 ) + { + PERROR("Unable to pause domain\n"); + return NULL; + } + + /* Get the pfn of the ring page */ + rc1 = xc_get_hvm_param(xch, domain_id, param, &ring_pfn); + if ( rc1 != 0 ) + { + PERROR("Failed to get pfn of ring page\n"); + goto out; + } + + mmap_pfn = ring_pfn; + ring_page = xc_map_foreign_batch(xch, domain_id, PROT_READ | PROT_WRITE, + &mmap_pfn, 1); + if ( mmap_pfn & XEN_DOMCTL_PFINFO_XTAB ) + { + /* Map failed, populate ring page */ + rc1 = xc_domain_populate_physmap_exact(xch, domain_id, 1, 0, 0, + &ring_pfn); + if ( rc1 != 0 ) + { + PERROR("Failed to populate ring pfn\n"); + goto out; + } + + mmap_pfn = ring_pfn; + ring_page = xc_map_foreign_batch(xch, domain_id, PROT_READ | PROT_WRITE, + &mmap_pfn, 1); + if ( mmap_pfn & XEN_DOMCTL_PFINFO_XTAB ) + { + PERROR("Could not map the ring page\n"); + goto out; + } + } + + switch ( param ) + { + case HVM_PARAM_PAGING_RING_PFN: + op = XEN_DOMCTL_MEM_EVENT_OP_PAGING_ENABLE; + mode = XEN_DOMCTL_MEM_EVENT_OP_PAGING; + break; + + case HVM_PARAM_ACCESS_RING_PFN: + op = XEN_DOMCTL_MEM_EVENT_OP_ACCESS_ENABLE; + mode = XEN_DOMCTL_MEM_EVENT_OP_ACCESS; + break; + + case HVM_PARAM_SHARING_RING_PFN: + op = XEN_DOMCTL_MEM_EVENT_OP_SHARING_ENABLE; + mode = XEN_DOMCTL_MEM_EVENT_OP_SHARING; + break; + + /* + * This is for the outside chance that the HVM_PARAM is valid but is invalid + * as far as mem_event goes. + */ + default: + errno = EINVAL; + rc1 = -1; + goto out; + } + + rc1 = xc_mem_event_control(xch, domain_id, op, mode, port); + if ( rc1 != 0 ) + { + PERROR("Failed to enable mem_event\n"); + goto out; + } + + /* Remove the ring_pfn from the guest's physmap */ + rc1 = xc_domain_decrease_reservation_exact(xch, domain_id, 1, 0, &ring_pfn); + if ( rc1 != 0 ) + PERROR("Failed to remove ring page from guest physmap"); + + out: + saved_errno = errno; + + rc2 = xc_domain_unpause(xch, domain_id); + if ( rc1 != 0 || rc2 != 0 ) + { + if ( rc2 != 0 ) + { + if ( rc1 == 0 ) + saved_errno = errno; + PERROR("Unable to unpause domain"); + } + + if ( ring_page ) + munmap(ring_page, XC_PAGE_SIZE); + ring_page = NULL; + + errno = saved_errno; + } + + return ring_page; +} diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h index 02129f7..34678d8 100644 --- a/tools/libxc/xenctrl.h +++ b/tools/libxc/xenctrl.h @@ -2023,6 +2023,12 @@ int xc_mem_event_control(xc_interface *xch, domid_t domain_id, unsigned int op, int xc_mem_event_memop(xc_interface *xch, domid_t domain_id, unsigned int op, unsigned int mode, uint64_t gfn, void *buffer); +/* + * Enables mem_event and returns the mapped ring page indicated by param. + * param can be HVM_PARAM_PAGING/ACCESS/SHARING_RING_PFN + */ +void *xc_mem_event_enable(xc_interface *xch, domid_t domain_id, int param, + uint32_t *port); /** * Mem paging operations. @@ -2043,7 +2049,13 @@ int xc_mem_paging_load(xc_interface *xch, domid_t domain_id, * Access tracking operations. * Supported only on Intel EPT 64 bit processors. */ -int xc_mem_access_enable(xc_interface *xch, domid_t domain_id, uint32_t *port); + +/* + * Enables mem_access and returns the mapped ring page. + * Will return NULL on error. + * Caller has to unmap this page when done. + */ +void *xc_mem_access_enable(xc_interface *xch, domid_t domain_id, uint32_t *port); int xc_mem_access_disable(xc_interface *xch, domid_t domain_id); int xc_mem_access_resume(xc_interface *xch, domid_t domain_id); diff --git a/tools/tests/xen-access/xen-access.c b/tools/tests/xen-access/xen-access.c index 0a84bd5..572ab63 100644 --- a/tools/tests/xen-access/xen-access.c +++ b/tools/tests/xen-access/xen-access.c @@ -223,7 +223,6 @@ xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t domain_id) xenaccess_t *xenaccess = 0; xc_interface *xch; int rc; - unsigned long ring_pfn, mmap_pfn; xch = xc_interface_open(NULL, NULL, 0); if ( !xch ) @@ -245,40 +244,12 @@ xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t domain_id) /* Initialise lock */ mem_event_ring_lock_init(&xenaccess->mem_event); - /* Map the ring page */ - xc_get_hvm_param(xch, xenaccess->mem_event.domain_id, - HVM_PARAM_ACCESS_RING_PFN, &ring_pfn); - mmap_pfn = ring_pfn; - xenaccess->mem_event.ring_page = - xc_map_foreign_batch(xch, xenaccess->mem_event.domain_id, - PROT_READ | PROT_WRITE, &mmap_pfn, 1); - if ( mmap_pfn & XEN_DOMCTL_PFINFO_XTAB ) - { - /* Map failed, populate ring page */ - rc = xc_domain_populate_physmap_exact(xenaccess->xc_handle, - xenaccess->mem_event.domain_id, - 1, 0, 0, &ring_pfn); - if ( rc != 0 ) - { - PERROR("Failed to populate ring gfn\n"); - goto err; - } - - mmap_pfn = ring_pfn; - xenaccess->mem_event.ring_page = - xc_map_foreign_batch(xch, xenaccess->mem_event.domain_id, - PROT_READ | PROT_WRITE, &mmap_pfn, 1); - if ( mmap_pfn & XEN_DOMCTL_PFINFO_XTAB ) - { - PERROR("Could not map the ring page\n"); - goto err; - } - } - - /* Initialise Xen */ - rc = xc_mem_access_enable(xenaccess->xc_handle, xenaccess->mem_event.domain_id, - &xenaccess->mem_event.evtchn_port); - if ( rc != 0 ) + /* Enable mem_access */ + xenaccess->mem_event.ring_page = + xc_mem_access_enable(xenaccess->xc_handle, + xenaccess->mem_event.domain_id, + &xenaccess->mem_event.evtchn_port); + if ( xenaccess->mem_event.ring_page == NULL ) { switch ( errno ) { case EBUSY: @@ -288,7 +259,7 @@ xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t domain_id) ERROR("EPT not supported for this guest"); break; default: - perror("Error initialising shared page"); + perror("Error enabling mem_access"); break; } goto err; @@ -322,11 +293,6 @@ xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t domain_id) (mem_event_sring_t *)xenaccess->mem_event.ring_page, XC_PAGE_SIZE); - /* Now that the ring is set, remove it from the guest's physmap */ - if ( xc_domain_decrease_reservation_exact(xch, - xenaccess->mem_event.domain_id, 1, 0, &ring_pfn) ) - PERROR("Failed to remove ring from guest physmap"); - /* Get domaininfo */ xenaccess->domain_info = malloc(sizeof(xc_domaininfo_t)); if ( xenaccess->domain_info == NULL )