summaryrefslogtreecommitdiffstats
path: root/system/xen/xsa/xsa298.patch
diff options
context:
space:
mode:
Diffstat (limited to 'system/xen/xsa/xsa298.patch')
-rw-r--r--system/xen/xsa/xsa298.patch89
1 files changed, 89 insertions, 0 deletions
diff --git a/system/xen/xsa/xsa298.patch b/system/xen/xsa/xsa298.patch
new file mode 100644
index 0000000000..aa39042be5
--- /dev/null
+++ b/system/xen/xsa/xsa298.patch
@@ -0,0 +1,89 @@
+From: Jan Beulich <jbeulich@suse.com>
+Subject: x86/PV: check GDT/LDT limits during emulation
+
+Accesses beyond the LDT limit originating from emulation would trigger
+the ASSERT() in pv_map_ldt_shadow_page(). On production builds such
+accesses would cause an attempt to promote the touched page (offset from
+the present LDT base address) to a segment descriptor one. If this
+happens to succeed, guest user mode would be able to elevate its
+privileges to that of the guest kernel. This is particularly easy when
+there's no LDT at all, in which case the LDT base stored internally to
+Xen is simply zero.
+
+Also adjust the ASSERT() that was triggering: It was off by one to
+begin with, and for production builds we also better use
+ASSERT_UNREACHABLE() instead with suitable recovery code afterwards.
+
+This is XSA-298.
+
+Reported-by: Andrew Cooper <andrew.cooper3@citrix.com>
+Signed-off-by: Jan Beulich <jbeulich@suse.com>
+Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
+---
+v2: Correct 64-bit-only limit check (by folding into the common one).
+
+--- a/xen/arch/x86/pv/emul-gate-op.c
++++ b/xen/arch/x86/pv/emul-gate-op.c
+@@ -51,7 +51,13 @@ static int read_gate_descriptor(unsigned
+ const seg_desc_t *pdesc = gdt_ldt_desc_ptr(gate_sel);
+
+ if ( (gate_sel < 4) ||
+- ((gate_sel >= FIRST_RESERVED_GDT_BYTE) && !(gate_sel & 4)) ||
++ /*
++ * We're interested in call gates only, which occupy a single
++ * seg_desc_t for 32-bit and a consecutive pair of them for 64-bit.
++ */
++ ((gate_sel >> 3) + !is_pv_32bit_vcpu(v) >=
++ (gate_sel & 4 ? v->arch.pv.ldt_ents
++ : v->arch.pv.gdt_ents)) ||
+ __get_user(desc, pdesc) )
+ return 0;
+
+@@ -70,7 +76,7 @@ static int read_gate_descriptor(unsigned
+ if ( !is_pv_32bit_vcpu(v) )
+ {
+ if ( (*ar & 0x1f00) != 0x0c00 ||
+- (gate_sel >= FIRST_RESERVED_GDT_BYTE - 8 && !(gate_sel & 4)) ||
++ /* Limit check done above already. */
+ __get_user(desc, pdesc + 1) ||
+ (desc.b & 0x1f00) )
+ return 0;
+--- a/xen/arch/x86/pv/emulate.c
++++ b/xen/arch/x86/pv/emulate.c
+@@ -31,7 +31,14 @@ int pv_emul_read_descriptor(unsigned int
+ {
+ seg_desc_t desc;
+
+- if ( sel < 4)
++ if ( sel < 4 ||
++ /*
++ * Don't apply the GDT limit here, as the selector may be a Xen
++ * provided one. __get_user() will fail (without taking further
++ * action) for ones falling in the gap between guest populated
++ * and Xen ones.
++ */
++ ((sel & 4) && (sel >> 3) >= v->arch.pv.ldt_ents) )
+ desc.b = desc.a = 0;
+ else if ( __get_user(desc, gdt_ldt_desc_ptr(sel)) )
+ return 0;
+--- a/xen/arch/x86/pv/mm.c
++++ b/xen/arch/x86/pv/mm.c
+@@ -92,12 +92,16 @@ bool pv_map_ldt_shadow_page(unsigned int
+ BUG_ON(unlikely(in_irq()));
+
+ /*
+- * Hardware limit checking should guarantee this property. NB. This is
++ * Prior limit checking should guarantee this property. NB. This is
+ * safe as updates to the LDT can only be made by MMUEXT_SET_LDT to the
+ * current vcpu, and vcpu_reset() will block until this vcpu has been
+ * descheduled before continuing.
+ */
+- ASSERT((offset >> 3) <= curr->arch.pv.ldt_ents);
++ if ( unlikely((offset >> 3) >= curr->arch.pv.ldt_ents) )
++ {
++ ASSERT_UNREACHABLE();
++ return false;
++ }
+
+ if ( is_pv_32bit_domain(currd) )
+ linear = (uint32_t)linear;