]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.7-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 18 Jan 2013 22:06:55 +0000 (14:06 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 18 Jan 2013 22:06:55 +0000 (14:06 -0800)
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.7/series
queue-3.7/xen-fix-stack-corruption-in-xen_failsafe_callback-for-32bit-pvops-guests.patch [new file with mode: 0644]
queue-3.7/xen-grant-table-correctly-initialize-grant-table-version-1.patch [new file with mode: 0644]

index 96847b3998484f0b7b298a31ecea78872d3e02b5..8b9c80f32219de725f186e28d59782e1cbc0cf3e 100644 (file)
@@ -18,3 +18,5 @@ drm-radeon-fix-a-bogus-kfree.patch
 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
+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.7/xen-fix-stack-corruption-in-xen_failsafe_callback-for-32bit-pvops-guests.patch b/queue-3.7/xen-fix-stack-corruption-in-xen_failsafe_callback-for-32bit-pvops-guests.patch
new file mode 100644 (file)
index 0000000..1ae39c8
--- /dev/null
@@ -0,0 +1,66 @@
+From 9174adbee4a9a49d0139f5d71969852b36720809 Mon Sep 17 00:00:00 2001
+From: Andrew Cooper <andrew.cooper3@citrix.com>
+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 <andrew.cooper3@citrix.com>
+
+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 <frediano.ziglio@citrix.com>
+Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
+Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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
+@@ -1084,7 +1084,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.7/xen-grant-table-correctly-initialize-grant-table-version-1.patch b/queue-3.7/xen-grant-table-correctly-initialize-grant-table-version-1.patch
new file mode 100644 (file)
index 0000000..a055a42
--- /dev/null
@@ -0,0 +1,214 @@
+From d0b4d64aadb9f4a90669848de9ef3819050a98cd Mon Sep 17 00:00:00 2001
+From: Matt Wilson <msw@amazon.com>
+Date: Tue, 15 Jan 2013 13:21:27 +0000
+Subject: xen/grant-table: correctly initialize grant table version 1
+
+From: Matt Wilson <msw@amazon.com>
+
+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: [<ffffffff811a6fb0>] 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 <msw@amazon.com>
+Reviewed-and-Tested-by: Steven Noonan <snoonan@amazon.com>
+Acked-by: Ian Campbell <Ian.Campbell@citrix.com>
+Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+Cc: Annie Li <annie.li@oracle.com>
+Cc: xen-devel@lists.xen.org
+Cc: linux-kernel@vger.kernel.org
+Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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
+@@ -56,10 +56,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;
+@@ -154,6 +150,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;
+@@ -767,12 +764,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])
+@@ -780,12 +779,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;
+@@ -957,7 +956,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(xen_pfn_t *frames, unsigned int nr_gframes)
+@@ -1115,6 +1115,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) {
+               /*
+@@ -1127,17 +1128,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;
+@@ -1160,6 +1161,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();
+@@ -1171,9 +1178,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;
+@@ -1191,21 +1199,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) {
+@@ -1214,12 +1224,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;