From 93a0155385b0c8f1657c3a706e1f325aa8b9b5cf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 18 Jan 2013 14:06:47 -0800 Subject: [PATCH] 3.4-stable patches added patches: xen-fix-stack-corruption-in-xen_failsafe_callback-for-32bit-pvops-guests.patch xen-grant-table-correctly-initialize-grant-table-version-1.patch --- queue-3.4/series | 2 + ...safe_callback-for-32bit-pvops-guests.patch | 66 ++++++ ...tly-initialize-grant-table-version-1.patch | 214 ++++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 queue-3.4/xen-fix-stack-corruption-in-xen_failsafe_callback-for-32bit-pvops-guests.patch create mode 100644 queue-3.4/xen-grant-table-correctly-initialize-grant-table-version-1.patch diff --git a/queue-3.4/series b/queue-3.4/series index 7f334ab407d..dae5256ece7 100644 --- a/queue-3.4/series +++ b/queue-3.4/series @@ -11,3 +11,5 @@ target-add-link_magic-for-fabric-allow_link-destination.patch intel-iommu-prevent-devices-with-rmrrs-from-being-placed.patch igb-release-already-assigned-msi-x-interrupts-if-setup-fails.patch drbd-add-missing-part_round_stats-to-_drbd_start_io_acct.patch +xen-grant-table-correctly-initialize-grant-table-version-1.patch +xen-fix-stack-corruption-in-xen_failsafe_callback-for-32bit-pvops-guests.patch diff --git a/queue-3.4/xen-fix-stack-corruption-in-xen_failsafe_callback-for-32bit-pvops-guests.patch b/queue-3.4/xen-fix-stack-corruption-in-xen_failsafe_callback-for-32bit-pvops-guests.patch new file mode 100644 index 00000000000..fe3c98995bf --- /dev/null +++ b/queue-3.4/xen-fix-stack-corruption-in-xen_failsafe_callback-for-32bit-pvops-guests.patch @@ -0,0 +1,66 @@ +From 9174adbee4a9a49d0139f5d71969852b36720809 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Wed, 16 Jan 2013 12:00:55 +0000 +Subject: xen: Fix stack corruption in xen_failsafe_callback for 32bit PVOPS guests. + +From: Andrew Cooper + +commit 9174adbee4a9a49d0139f5d71969852b36720809 upstream. + +This fixes CVE-2013-0190 / XSA-40 + +There has been an error on the xen_failsafe_callback path for failed +iret, which causes the stack pointer to be wrong when entering the +iret_exc error path. This can result in the kernel crashing. + +In the classic kernel case, the relevant code looked a little like: + + popl %eax # Error code from hypervisor + jz 5f + addl $16,%esp + jmp iret_exc # Hypervisor said iret fault +5: addl $16,%esp + # Hypervisor said segment selector fault + +Here, there are two identical addls on either option of a branch which +appears to have been optimised by hoisting it above the jz, and +converting it to an lea, which leaves the flags register unaffected. + +In the PVOPS case, the code looks like: + + popl_cfi %eax # Error from the hypervisor + lea 16(%esp),%esp # Add $16 before choosing fault path + CFI_ADJUST_CFA_OFFSET -16 + jz 5f + addl $16,%esp # Incorrectly adjust %esp again + jmp iret_exc + +It is possible unprivileged userspace applications to cause this +behaviour, for example by loading an LDT code selector, then changing +the code selector to be not-present. At this point, there is a race +condition where it is possible for the hypervisor to return back to +userspace from an interrupt, fault on its own iret, and inject a +failsafe_callback into the kernel. + +This bug has been present since the introduction of Xen PVOPS support +in commit 5ead97c84 (xen: Core Xen implementation), in 2.6.23. + +Signed-off-by: Frediano Ziglio +Signed-off-by: Andrew Cooper +Signed-off-by: Konrad Rzeszutek Wilk +Signed-off-by: Greg Kroah-Hartman + +--- + arch/x86/kernel/entry_32.S | 1 - + 1 file changed, 1 deletion(-) + +--- a/arch/x86/kernel/entry_32.S ++++ b/arch/x86/kernel/entry_32.S +@@ -1074,7 +1074,6 @@ ENTRY(xen_failsafe_callback) + lea 16(%esp),%esp + CFI_ADJUST_CFA_OFFSET -16 + jz 5f +- addl $16,%esp + jmp iret_exc + 5: pushl_cfi $-1 /* orig_ax = -1 => not a system call */ + SAVE_ALL diff --git a/queue-3.4/xen-grant-table-correctly-initialize-grant-table-version-1.patch b/queue-3.4/xen-grant-table-correctly-initialize-grant-table-version-1.patch new file mode 100644 index 00000000000..ba91b424fc1 --- /dev/null +++ b/queue-3.4/xen-grant-table-correctly-initialize-grant-table-version-1.patch @@ -0,0 +1,214 @@ +From d0b4d64aadb9f4a90669848de9ef3819050a98cd Mon Sep 17 00:00:00 2001 +From: Matt Wilson +Date: Tue, 15 Jan 2013 13:21:27 +0000 +Subject: xen/grant-table: correctly initialize grant table version 1 + +From: Matt Wilson + +commit d0b4d64aadb9f4a90669848de9ef3819050a98cd upstream. + +Commit 85ff6acb075a484780b3d763fdf41596d8fc0970 (xen/granttable: Grant +tables V2 implementation) changed the GREFS_PER_GRANT_FRAME macro from +a constant to a conditional expression. The expression depends on +grant_table_version being appropriately set. Unfortunately, at init +time grant_table_version will be 0. The GREFS_PER_GRANT_FRAME +conditional expression checks for "grant_table_version == 1", and +therefore returns the number of grant references per frame for v2. + +This causes gnttab_init() to allocate fewer pages for gnttab_list, as +a frame can old half the number of v2 entries than v1 entries. After +gnttab_resume() is called, grant_table_version is appropriately +set. nr_init_grefs will then be miscalculated and gnttab_free_count +will hold a value larger than the actual number of free gref entries. + +If a guest is heavily utilizing improperly initialized v1 grant +tables, memory corruption can occur. One common manifestation is +corruption of the vmalloc list, resulting in a poisoned pointer +derefrence when accessing /proc/meminfo or /proc/vmallocinfo: + +[ 40.770064] BUG: unable to handle kernel paging request at 0000200200001407 +[ 40.770083] IP: [] get_vmalloc_info+0x70/0x110 +[ 40.770102] PGD 0 +[ 40.770107] Oops: 0000 [#1] SMP +[ 40.770114] CPU 10 + +This patch introduces a static variable, grefs_per_grant_frame, to +cache the calculated value. gnttab_init() now calls +gnttab_request_version() early so that grant_table_version and +grefs_per_grant_frame can be appropriately set. A few BUG_ON()s have +been added to prevent this type of bug from reoccurring in the future. + +Signed-off-by: Matt Wilson +Reviewed-and-Tested-by: Steven Noonan +Acked-by: Ian Campbell +Cc: Konrad Rzeszutek Wilk +Cc: Annie Li +Cc: xen-devel@lists.xen.org +Cc: linux-kernel@vger.kernel.org +Signed-off-by: Konrad Rzeszutek Wilk +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/xen/grant-table.c | 48 +++++++++++++++++++++++++++------------------- + 1 file changed, 29 insertions(+), 19 deletions(-) + +--- a/drivers/xen/grant-table.c ++++ b/drivers/xen/grant-table.c +@@ -53,10 +53,6 @@ + /* External tools reserve first few grant table entries. */ + #define NR_RESERVED_ENTRIES 8 + #define GNTTAB_LIST_END 0xffffffff +-#define GREFS_PER_GRANT_FRAME \ +-(grant_table_version == 1 ? \ +-(PAGE_SIZE / sizeof(struct grant_entry_v1)) : \ +-(PAGE_SIZE / sizeof(union grant_entry_v2))) + + static grant_ref_t **gnttab_list; + static unsigned int nr_grant_frames; +@@ -151,6 +147,7 @@ static struct gnttab_ops *gnttab_interfa + static grant_status_t *grstatus; + + static int grant_table_version; ++static int grefs_per_grant_frame; + + static struct gnttab_free_callback *gnttab_free_callback_list; + +@@ -679,12 +676,14 @@ static int grow_gnttab_list(unsigned int + unsigned int new_nr_grant_frames, extra_entries, i; + unsigned int nr_glist_frames, new_nr_glist_frames; + ++ BUG_ON(grefs_per_grant_frame == 0); ++ + new_nr_grant_frames = nr_grant_frames + more_frames; +- extra_entries = more_frames * GREFS_PER_GRANT_FRAME; ++ extra_entries = more_frames * grefs_per_grant_frame; + +- nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; ++ nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP; + new_nr_glist_frames = +- (new_nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; ++ (new_nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP; + for (i = nr_glist_frames; i < new_nr_glist_frames; i++) { + gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC); + if (!gnttab_list[i]) +@@ -692,12 +691,12 @@ static int grow_gnttab_list(unsigned int + } + + +- for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames; +- i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++) ++ for (i = grefs_per_grant_frame * nr_grant_frames; ++ i < grefs_per_grant_frame * new_nr_grant_frames - 1; i++) + gnttab_entry(i) = i + 1; + + gnttab_entry(i) = gnttab_free_head; +- gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames; ++ gnttab_free_head = grefs_per_grant_frame * nr_grant_frames; + gnttab_free_count += extra_entries; + + nr_grant_frames = new_nr_grant_frames; +@@ -799,7 +798,8 @@ EXPORT_SYMBOL_GPL(gnttab_unmap_refs); + + static unsigned nr_status_frames(unsigned nr_grant_frames) + { +- return (nr_grant_frames * GREFS_PER_GRANT_FRAME + SPP - 1) / SPP; ++ BUG_ON(grefs_per_grant_frame == 0); ++ return (nr_grant_frames * grefs_per_grant_frame + SPP - 1) / SPP; + } + + static int gnttab_map_frames_v1(unsigned long *frames, unsigned int nr_gframes) +@@ -957,6 +957,7 @@ static void gnttab_request_version(void) + rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1); + if (rc == 0 && gsv.version == 2) { + grant_table_version = 2; ++ grefs_per_grant_frame = PAGE_SIZE / sizeof(union grant_entry_v2); + gnttab_interface = &gnttab_v2_ops; + } else if (grant_table_version == 2) { + /* +@@ -969,17 +970,17 @@ static void gnttab_request_version(void) + panic("we need grant tables version 2, but only version 1 is available"); + } else { + grant_table_version = 1; ++ grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1); + gnttab_interface = &gnttab_v1_ops; + } + printk(KERN_INFO "Grant tables using version %d layout.\n", + grant_table_version); + } + +-int gnttab_resume(void) ++static int gnttab_setup(void) + { + unsigned int max_nr_gframes; + +- gnttab_request_version(); + max_nr_gframes = gnttab_max_grant_frames(); + if (max_nr_gframes < nr_grant_frames) + return -ENOSYS; +@@ -1002,6 +1003,12 @@ int gnttab_resume(void) + return 0; + } + ++int gnttab_resume(void) ++{ ++ gnttab_request_version(); ++ return gnttab_setup(); ++} ++ + int gnttab_suspend(void) + { + gnttab_interface->unmap_frames(); +@@ -1013,9 +1020,10 @@ static int gnttab_expand(unsigned int re + int rc; + unsigned int cur, extra; + ++ BUG_ON(grefs_per_grant_frame == 0); + cur = nr_grant_frames; +- extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) / +- GREFS_PER_GRANT_FRAME); ++ extra = ((req_entries + (grefs_per_grant_frame-1)) / ++ grefs_per_grant_frame); + if (cur + extra > gnttab_max_grant_frames()) + return -ENOSPC; + +@@ -1033,21 +1041,23 @@ int gnttab_init(void) + unsigned int nr_init_grefs; + int ret; + ++ gnttab_request_version(); + nr_grant_frames = 1; + boot_max_nr_grant_frames = __max_nr_grant_frames(); + + /* Determine the maximum number of frames required for the + * grant reference free list on the current hypervisor. + */ ++ BUG_ON(grefs_per_grant_frame == 0); + max_nr_glist_frames = (boot_max_nr_grant_frames * +- GREFS_PER_GRANT_FRAME / RPP); ++ grefs_per_grant_frame / RPP); + + gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *), + GFP_KERNEL); + if (gnttab_list == NULL) + return -ENOMEM; + +- nr_glist_frames = (nr_grant_frames * GREFS_PER_GRANT_FRAME + RPP - 1) / RPP; ++ nr_glist_frames = (nr_grant_frames * grefs_per_grant_frame + RPP - 1) / RPP; + for (i = 0; i < nr_glist_frames; i++) { + gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL); + if (gnttab_list[i] == NULL) { +@@ -1056,12 +1066,12 @@ int gnttab_init(void) + } + } + +- if (gnttab_resume() < 0) { ++ if (gnttab_setup() < 0) { + ret = -ENODEV; + goto ini_nomem; + } + +- nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME; ++ nr_init_grefs = nr_grant_frames * grefs_per_grant_frame; + + for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++) + gnttab_entry(i) = i + 1; -- 2.47.3