aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@suse.com>2013-06-26 15:35:56 +0200
committerJan Beulich <jbeulich@suse.com>2013-06-26 15:35:56 +0200
commit63a1ea8506d520881e4f7426b39774dce4be1ff9 (patch)
tree069793aaaf730c9772cb6e6480f2ee11e886680a
parentd21d36e84354c04638b60a739a5f7c3d9f8adaf8 (diff)
downloadxen-63a1ea8506d520881e4f7426b39774dce4be1ff9.tar.gz
xen-63a1ea8506d520881e4f7426b39774dce4be1ff9.tar.bz2
xen-63a1ea8506d520881e4f7426b39774dce4be1ff9.zip
x86: fix page refcount handling in page table pin error path
In the original patch 7 of the series addressing XSA-45 I mistakenly took the addition of the call to get_page_light() in alloc_page_type() to cover two decrements that would happen: One for the PGT_partial bit that is getting set along with the call, and the other for the page reference the caller hold (and would be dropping on its error path). But of course the additional page reference is tied to the PGT_partial bit, and hence any caller of a function that may leave ->arch.old_guest_table non-NULL for error cleanup purposes has to make sure a respective page reference gets retained. Similar issues were then also spotted elsewhere: In effect all callers of get_page_type_preemptible() need to deal with errors in similar ways. To make sure error handling can work this way without leaking page references, a respective assertion gets added to that function. This is CVE-2013-1432 / XSA-58. Reported-by: Andrew Cooper <andrew.cooper3@citrix.com> Signed-off-by: Jan Beulich <jbeulich@suse.com> Tested-by: Andrew Cooper <andrew.cooper3@citrix.com> Reviewed-by: Tim Deegan <tim@xen.org> master commit: 9b167bd2f394f821ae3252d74a15704a4bf91f6d master date: 2013-06-26 15:32:58 +0200
-rw-r--r--xen/arch/x86/domain.c27
-rw-r--r--xen/arch/x86/mm.c9
-rw-r--r--xen/include/asm-x86/mm.h1
3 files changed, 28 insertions, 9 deletions
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 830c79df37..58f3039e39 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -941,6 +941,10 @@ int arch_set_info_guest(
if ( v->vcpu_id == 0 )
d->vm_assist = c(vm_assist);
+ rc = put_old_guest_table(current);
+ if ( rc )
+ return rc;
+
if ( !compat )
rc = (int)set_gdt(v, c.nat->gdt_frames, c.nat->gdt_ents);
#ifdef CONFIG_COMPAT
@@ -980,18 +984,24 @@ int arch_set_info_guest(
}
else
{
- /*
- * Since v->arch.guest_table{,_user} are both NULL, this effectively
- * is just a call to put_old_guest_table().
- */
if ( !compat )
- rc = vcpu_destroy_pagetables(v);
+ rc = put_old_guest_table(v);
if ( !rc )
rc = get_page_type_preemptible(cr3_page,
!compat ? PGT_root_page_table
: PGT_l3_page_table);
- if ( rc == -EINTR )
+ switch ( rc )
+ {
+ case -EINTR:
rc = -EAGAIN;
+ case -EAGAIN:
+ case 0:
+ break;
+ default:
+ if ( cr3_page == current->arch.old_guest_table )
+ cr3_page = NULL;
+ break;
+ }
}
if ( rc )
/* handled below */;
@@ -1018,6 +1028,11 @@ int arch_set_info_guest(
pagetable_get_page(v->arch.guest_table);
v->arch.guest_table = pagetable_null();
break;
+ default:
+ if ( cr3_page == current->arch.old_guest_table )
+ cr3_page = NULL;
+ case 0:
+ break;
}
}
if ( !rc )
diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c
index a9e361839c..5b0b953d61 100644
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -718,7 +718,8 @@ static int get_page_and_type_from_pagenr(unsigned long page_nr,
get_page_type_preemptible(page, type) :
(get_page_type(page, type) ? 0 : -EINVAL));
- if ( unlikely(rc) && partial >= 0 )
+ if ( unlikely(rc) && partial >= 0 &&
+ (!preemptible || page != current->arch.old_guest_table) )
put_page(page);
return rc;
@@ -2638,6 +2639,7 @@ int put_page_type_preemptible(struct page_info *page)
int get_page_type_preemptible(struct page_info *page, unsigned long type)
{
+ ASSERT(!current->arch.old_guest_table);
return __get_page_type(page, type, 1);
}
@@ -2848,7 +2850,7 @@ static void put_superpage(unsigned long mfn)
#endif
-static int put_old_guest_table(struct vcpu *v)
+int put_old_guest_table(struct vcpu *v)
{
int rc;
@@ -3253,7 +3255,8 @@ long do_mmuext_op(
rc = -EAGAIN;
else if ( rc != -EAGAIN )
MEM_LOG("Error while pinning mfn %lx", page_to_mfn(page));
- put_page(page);
+ if ( page != curr->arch.old_guest_table )
+ put_page(page);
break;
}
diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h
index 372e3ac429..e783d8a9ab 100644
--- a/xen/include/asm-x86/mm.h
+++ b/xen/include/asm-x86/mm.h
@@ -374,6 +374,7 @@ void put_page_type(struct page_info *page);
int get_page_type(struct page_info *page, unsigned long type);
int put_page_type_preemptible(struct page_info *page);
int get_page_type_preemptible(struct page_info *page, unsigned long type);
+int put_old_guest_table(struct vcpu *);
int get_page_from_l1e(
l1_pgentry_t l1e, struct domain *l1e_owner, struct domain *pg_owner);
void put_page_from_l1e(l1_pgentry_t l1e, struct domain *l1e_owner);