aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@suse.com>2013-05-02 16:35:50 +0200
committerJan Beulich <jbeulich@suse.com>2013-05-02 16:35:50 +0200
commite2e6b7b627fec0d7a769ab46441f2985ebccbf04 (patch)
tree6997d096f5b9dfa4a91c474d8801fc1eb76a44cd
parent6cdc9be2a5f2a87b4504404fbf648d16d9503c19 (diff)
downloadxen-e2e6b7b627fec0d7a769ab46441f2985ebccbf04.tar.gz
xen-e2e6b7b627fec0d7a769ab46441f2985ebccbf04.tar.bz2
xen-e2e6b7b627fec0d7a769ab46441f2985ebccbf04.zip
x86: make new_guest_cr3() preemptible
... as it may take significant amounts of time. This is part of CVE-2013-1918 / XSA-45. Signed-off-by: Jan Beulich <jbeulich@suse.com> Acked-by: Tim Deegan <tim@xen.org>
-rw-r--r--xen/arch/x86/mm.c82
-rw-r--r--xen/arch/x86/traps.c15
2 files changed, 75 insertions, 22 deletions
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index 9a076e9d36..e43a698791 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -2656,7 +2656,7 @@ int new_guest_cr3(unsigned long mfn)
{
struct vcpu *curr = current;
struct domain *d = curr->domain;
- int okay;
+ int rc;
unsigned long old_base_mfn;
if ( is_pv_32on64_domain(d) )
@@ -2664,39 +2664,64 @@ int new_guest_cr3(unsigned long mfn)
unsigned long gt_mfn = pagetable_get_pfn(curr->arch.guest_table);
l4_pgentry_t *pl4e = map_domain_page(gt_mfn);
- okay = paging_mode_refcounts(d)
- ? 0 /* Old code was broken, but what should it be? */
- : mod_l4_entry(
+ rc = paging_mode_refcounts(d)
+ ? -EINVAL /* Old code was broken, but what should it be? */
+ : mod_l4_entry(
pl4e,
l4e_from_pfn(
mfn,
(_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED)),
- gt_mfn, 0, 0, curr) == 0;
+ gt_mfn, 0, 1, curr);
unmap_domain_page(pl4e);
- if ( unlikely(!okay) )
+ switch ( rc )
{
+ case 0:
+ break;
+ case -EINTR:
+ case -EAGAIN:
+ return -EAGAIN;
+ default:
MEM_LOG("Error while installing new compat baseptr %lx", mfn);
- return 0;
+ return rc;
}
invalidate_shadow_ldt(curr, 0);
write_ptbase(curr);
- return 1;
+ return 0;
}
- okay = paging_mode_refcounts(d)
- ? get_page_from_pagenr(mfn, d)
- : !get_page_and_type_from_pagenr(mfn, PGT_root_page_table, d, 0, 0);
- if ( unlikely(!okay) )
+ rc = put_old_guest_table(curr);
+ if ( unlikely(rc) )
+ return rc;
+
+ old_base_mfn = pagetable_get_pfn(curr->arch.guest_table);
+ /*
+ * This is particularly important when getting restarted after the
+ * previous attempt got preempted in the put-old-MFN phase.
+ */
+ if ( old_base_mfn == mfn )
{
- MEM_LOG("Error while installing new baseptr %lx", mfn);
+ write_ptbase(curr);
return 0;
}
- invalidate_shadow_ldt(curr, 0);
+ rc = paging_mode_refcounts(d)
+ ? (get_page_from_pagenr(mfn, d) ? 0 : -EINVAL)
+ : get_page_and_type_from_pagenr(mfn, PGT_root_page_table, d, 0, 1);
+ switch ( rc )
+ {
+ case 0:
+ break;
+ case -EINTR:
+ case -EAGAIN:
+ return -EAGAIN;
+ default:
+ MEM_LOG("Error while installing new baseptr %lx", mfn);
+ return rc;
+ }
- old_base_mfn = pagetable_get_pfn(curr->arch.guest_table);
+ invalidate_shadow_ldt(curr, 0);
curr->arch.guest_table = pagetable_from_pfn(mfn);
update_cr3(curr);
@@ -2705,13 +2730,25 @@ int new_guest_cr3(unsigned long mfn)
if ( likely(old_base_mfn != 0) )
{
+ struct page_info *page = mfn_to_page(old_base_mfn);
+
if ( paging_mode_refcounts(d) )
- put_page(mfn_to_page(old_base_mfn));
+ put_page(page);
else
- put_page_and_type(mfn_to_page(old_base_mfn));
+ switch ( rc = put_page_and_type_preemptible(page, 1) )
+ {
+ case -EINTR:
+ rc = -EAGAIN;
+ case -EAGAIN:
+ curr->arch.old_guest_table = page;
+ break;
+ default:
+ BUG_ON(rc);
+ break;
+ }
}
- return 1;
+ return rc;
}
static struct domain *get_pg_owner(domid_t domid)
@@ -2982,8 +3019,13 @@ long do_mmuext_op(
}
case MMUEXT_NEW_BASEPTR:
- okay = (!paging_mode_translate(d)
- && new_guest_cr3(op.arg1.mfn));
+ if ( paging_mode_translate(d) )
+ okay = 0;
+ else
+ {
+ rc = new_guest_cr3(op.arg1.mfn);
+ okay = !rc;
+ }
break;
case MMUEXT_NEW_USER_BASEPTR: {
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
index d36eddd229..4de9313c74 100644
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -2322,12 +2322,23 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
gfn = !is_pv_32on64_vcpu(v)
? xen_cr3_to_pfn(*reg) : compat_cr3_to_pfn(*reg);
page = get_page_from_gfn(v->domain, gfn, NULL, P2M_ALLOC);
- rc = page ? new_guest_cr3(page_to_mfn(page)) : 0;
if ( page )
+ {
+ rc = new_guest_cr3(page_to_mfn(page));
put_page(page);
+ }
+ else
+ rc = -EINVAL;
domain_unlock(v->domain);
- if ( rc == 0 ) /* not okay */
+ switch ( rc )
+ {
+ case 0:
+ break;
+ case -EAGAIN: /* retry after preemption */
+ goto skip;
+ default: /* not okay */
goto fail;
+ }
break;
}