xen-devel.lists.xenproject.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/3] x86: XSA-298 follow-up
@ 2020-05-20  7:50 Jan Beulich
  2020-05-20  7:53 ` [PATCH v3 1/3] x86: relax GDT check in arch_set_info_guest() Jan Beulich
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Jan Beulich @ 2020-05-20  7:50 UTC (permalink / raw)
  To: xen-devel; +Cc: Andrew Cooper, Wei Liu, Roger Pau Monné

A few things stumbled across while doing the investigations.

1: relax GDT check in arch_set_info_guest()
2: relax LDT check in arch_set_info_guest()
3: PV: polish pv_set_gdt()

Jan


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 1/3] x86: relax GDT check in arch_set_info_guest()
  2020-05-20  7:50 [PATCH v3 0/3] x86: XSA-298 follow-up Jan Beulich
@ 2020-05-20  7:53 ` Jan Beulich
  2020-05-20 10:20   ` Roger Pau Monné
  2020-05-22 13:27   ` Andrew Cooper
  2020-05-20  7:54 ` [PATCH v3 2/3] x86: relax LDT " Jan Beulich
  2020-05-20  7:54 ` [PATCH v3 3/3] x86/PV: polish pv_set_gdt() Jan Beulich
  2 siblings, 2 replies; 7+ messages in thread
From: Jan Beulich @ 2020-05-20  7:53 UTC (permalink / raw)
  To: xen-devel; +Cc: Andrew Cooper, Wei Liu, Roger Pau Monné

It is wrong for us to check frames beyond the guest specified limit
(in the compat case another loop bound is already correct).

Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
v3: Move nr_gdt_frames range check earlier. Avoid |= where not really
    needed.

--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -840,6 +840,7 @@ int arch_set_info_guest(
 #ifdef CONFIG_PV
     mfn_t cr3_mfn;
     struct page_info *cr3_page = NULL;
+    unsigned int nr_gdt_frames;
     int rc = 0;
 #endif
 
@@ -957,6 +958,10 @@ int arch_set_info_guest(
     /* Ensure real hardware interrupts are enabled. */
     v->arch.user_regs.eflags |= X86_EFLAGS_IF;
 
+    nr_gdt_frames = DIV_ROUND_UP(c(gdt_ents), 512);
+    if ( nr_gdt_frames > ARRAY_SIZE(v->arch.pv.gdt_frames) )
+        return -EINVAL;
+
     if ( !v->is_initialised )
     {
         if ( !compat && !(flags & VGCF_in_kernel) && !c.nat->ctrlreg[1] )
@@ -988,9 +993,9 @@ int arch_set_info_guest(
             fail = compat_pfn_to_cr3(pfn) != c.cmp->ctrlreg[3];
         }
 
-        for ( i = 0; i < ARRAY_SIZE(v->arch.pv.gdt_frames); ++i )
-            fail |= v->arch.pv.gdt_frames[i] != c(gdt_frames[i]);
         fail |= v->arch.pv.gdt_ents != c(gdt_ents);
+        for ( i = 0; !fail && i < nr_gdt_frames; ++i )
+            fail = v->arch.pv.gdt_frames[i] != c(gdt_frames[i]);
 
         fail |= v->arch.pv.ldt_base != c(ldt_base);
         fail |= v->arch.pv.ldt_ents != c(ldt_ents);
@@ -1095,12 +1100,8 @@ int arch_set_info_guest(
     else
     {
         unsigned long gdt_frames[ARRAY_SIZE(v->arch.pv.gdt_frames)];
-        unsigned int nr_frames = DIV_ROUND_UP(c.cmp->gdt_ents, 512);
-
-        if ( nr_frames > ARRAY_SIZE(v->arch.pv.gdt_frames) )
-            return -EINVAL;
 
-        for ( i = 0; i < nr_frames; ++i )
+        for ( i = 0; i < nr_gdt_frames; ++i )
             gdt_frames[i] = c.cmp->gdt_frames[i];
 
         rc = (int)pv_set_gdt(v, gdt_frames, c.cmp->gdt_ents);



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 2/3] x86: relax LDT check in arch_set_info_guest()
  2020-05-20  7:50 [PATCH v3 0/3] x86: XSA-298 follow-up Jan Beulich
  2020-05-20  7:53 ` [PATCH v3 1/3] x86: relax GDT check in arch_set_info_guest() Jan Beulich
@ 2020-05-20  7:54 ` Jan Beulich
  2020-05-20  7:54 ` [PATCH v3 3/3] x86/PV: polish pv_set_gdt() Jan Beulich
  2 siblings, 0 replies; 7+ messages in thread
From: Jan Beulich @ 2020-05-20  7:54 UTC (permalink / raw)
  To: xen-devel; +Cc: Andrew Cooper, Wei Liu, Roger Pau Monné

It is wrong for us to check the base address when there's no LDT in the
first place. Once we don't do this check anymore we can also set the
base address to a non-canonical value when the LDT is empty.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
---
v3: Re-base over changes to earlier patch.
v2: Set v->arch.pv.ldt_base to non-canonical for an empty LDT, plus
    related necessary adjustments.

--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -967,8 +967,10 @@ int arch_set_info_guest(
         if ( !compat && !(flags & VGCF_in_kernel) && !c.nat->ctrlreg[1] )
             return -EINVAL;
 
-        v->arch.pv.ldt_base = c(ldt_base);
         v->arch.pv.ldt_ents = c(ldt_ents);
+        v->arch.pv.ldt_base = v->arch.pv.ldt_ents
+                              ? c(ldt_base)
+                              : (unsigned long)ZERO_BLOCK_PTR;
     }
     else
     {
@@ -997,8 +999,9 @@ int arch_set_info_guest(
         for ( i = 0; !fail && i < nr_gdt_frames; ++i )
             fail = v->arch.pv.gdt_frames[i] != c(gdt_frames[i]);
 
-        fail |= v->arch.pv.ldt_base != c(ldt_base);
         fail |= v->arch.pv.ldt_ents != c(ldt_ents);
+        if ( v->arch.pv.ldt_ents )
+            fail |= v->arch.pv.ldt_base != c(ldt_base);
 
         if ( fail )
            return -EOPNOTSUPP;
--- a/xen/arch/x86/domctl.c
+++ b/xen/arch/x86/domctl.c
@@ -1583,7 +1583,7 @@ void arch_get_info_guest(struct vcpu *v,
     }
     else
     {
-        c(ldt_base = v->arch.pv.ldt_base);
+        c(ldt_base = v->arch.pv.ldt_ents ? v->arch.pv.ldt_base : 0);
         c(ldt_ents = v->arch.pv.ldt_ents);
         for ( i = 0; i < ARRAY_SIZE(v->arch.pv.gdt_frames); ++i )
             c(gdt_frames[i] = v->arch.pv.gdt_frames[i]);
--- a/xen/arch/x86/mm.c
+++ b/xen/arch/x86/mm.c
@@ -3669,14 +3669,15 @@ long do_mmuext_op(
         case MMUEXT_SET_LDT:
         {
             unsigned int ents = op.arg2.nr_ents;
-            unsigned long ptr = ents ? op.arg1.linear_addr : 0;
+            unsigned long ptr = ents ? op.arg1.linear_addr
+                                     : (unsigned long)ZERO_BLOCK_PTR;
 
             if ( unlikely(currd != pg_owner) )
                 rc = -EPERM;
             else if ( paging_mode_external(currd) )
                 rc = -EINVAL;
-            else if ( ((ptr & (PAGE_SIZE - 1)) != 0) || !__addr_ok(ptr) ||
-                      (ents > 8192) )
+            else if ( (ents > 8192) ||
+                      (ents && ((ptr & (PAGE_SIZE - 1)) || !__addr_ok(ptr))) )
             {
                 gdprintk(XENLOG_WARNING,
                          "Bad args to SET_LDT: ptr=%lx, ents=%x\n", ptr, ents);



^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v3 3/3] x86/PV: polish pv_set_gdt()
  2020-05-20  7:50 [PATCH v3 0/3] x86: XSA-298 follow-up Jan Beulich
  2020-05-20  7:53 ` [PATCH v3 1/3] x86: relax GDT check in arch_set_info_guest() Jan Beulich
  2020-05-20  7:54 ` [PATCH v3 2/3] x86: relax LDT " Jan Beulich
@ 2020-05-20  7:54 ` Jan Beulich
  2 siblings, 0 replies; 7+ messages in thread
From: Jan Beulich @ 2020-05-20  7:54 UTC (permalink / raw)
  To: xen-devel; +Cc: Andrew Cooper, Wei Liu, Roger Pau Monné

There's no need to invoke get_page_from_gfn(), and there's also no need
to update the passed in frames[]. Invoke get_page_and_type() directly.

Also make the function's frames[] parameter const, change its return
type to int, and drop the bogus casts from two of its invocations.

Finally a little bit of cosmetics.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>

--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -1099,7 +1099,7 @@ int arch_set_info_guest(
         return rc;
 
     if ( !compat )
-        rc = (int)pv_set_gdt(v, c.nat->gdt_frames, c.nat->gdt_ents);
+        rc = pv_set_gdt(v, c.nat->gdt_frames, c.nat->gdt_ents);
     else
     {
         unsigned long gdt_frames[ARRAY_SIZE(v->arch.pv.gdt_frames)];
@@ -1107,7 +1107,7 @@ int arch_set_info_guest(
         for ( i = 0; i < nr_gdt_frames; ++i )
             gdt_frames[i] = c.cmp->gdt_frames[i];
 
-        rc = (int)pv_set_gdt(v, gdt_frames, c.cmp->gdt_ents);
+        rc = pv_set_gdt(v, gdt_frames, c.cmp->gdt_ents);
     }
     if ( rc != 0 )
         return rc;
--- a/xen/arch/x86/pv/descriptor-tables.c
+++ b/xen/arch/x86/pv/descriptor-tables.c
@@ -81,7 +81,8 @@ void pv_destroy_gdt(struct vcpu *v)
     }
 }
 
-long pv_set_gdt(struct vcpu *v, unsigned long *frames, unsigned int entries)
+int pv_set_gdt(struct vcpu *v, const unsigned long frames[],
+               unsigned int entries)
 {
     struct domain *d = v->domain;
     l1_pgentry_t *pl1e;
@@ -95,17 +96,11 @@ long pv_set_gdt(struct vcpu *v, unsigned
     /* Check the pages in the new GDT. */
     for ( i = 0; i < nr_frames; i++ )
     {
-        struct page_info *page;
+        mfn_t mfn = _mfn(frames[i]);
 
-        page = get_page_from_gfn(d, frames[i], NULL, P2M_ALLOC);
-        if ( !page )
+        if ( !mfn_valid(mfn) ||
+             !get_page_and_type(mfn_to_page(mfn), d, PGT_seg_desc_page) )
             goto fail;
-        if ( !get_page_type(page, PGT_seg_desc_page) )
-        {
-            put_page(page);
-            goto fail;
-        }
-        frames[i] = mfn_x(page_to_mfn(page));
     }
 
     /* Tear down the old GDT. */
@@ -124,9 +119,8 @@ long pv_set_gdt(struct vcpu *v, unsigned
 
  fail:
     while ( i-- > 0 )
-    {
         put_page_and_type(mfn_to_page(_mfn(frames[i])));
-    }
+
     return -EINVAL;
 }
 
--- a/xen/include/asm-x86/pv/mm.h
+++ b/xen/include/asm-x86/pv/mm.h
@@ -25,7 +25,8 @@
 
 int pv_ro_page_fault(unsigned long addr, struct cpu_user_regs *regs);
 
-long pv_set_gdt(struct vcpu *v, unsigned long *frames, unsigned int entries);
+int pv_set_gdt(struct vcpu *v, const unsigned long frames[],
+               unsigned int entries);
 void pv_destroy_gdt(struct vcpu *v);
 
 bool pv_map_ldt_shadow_page(unsigned int off);
@@ -43,8 +44,8 @@ static inline int pv_ro_page_fault(unsig
     return 0;
 }
 
-static inline long pv_set_gdt(struct vcpu *v, unsigned long *frames,
-                              unsigned int entries)
+static inline int pv_set_gdt(struct vcpu *v, const unsigned long frames[],
+                             unsigned int entries)
 { ASSERT_UNREACHABLE(); return -EINVAL; }
 static inline void pv_destroy_gdt(struct vcpu *v) { ASSERT_UNREACHABLE(); }
 



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 1/3] x86: relax GDT check in arch_set_info_guest()
  2020-05-20  7:53 ` [PATCH v3 1/3] x86: relax GDT check in arch_set_info_guest() Jan Beulich
@ 2020-05-20 10:20   ` Roger Pau Monné
  2020-05-22 13:27   ` Andrew Cooper
  1 sibling, 0 replies; 7+ messages in thread
From: Roger Pau Monné @ 2020-05-20 10:20 UTC (permalink / raw)
  To: Jan Beulich; +Cc: xen-devel, Wei Liu, Andrew Cooper

On Wed, May 20, 2020 at 09:53:50AM +0200, Jan Beulich wrote:
> It is wrong for us to check frames beyond the guest specified limit
> (in the compat case another loop bound is already correct).
> 
> Signed-off-by: Jan Beulich <jbeulich@suse.com>

Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>

Thanks, Roger.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 1/3] x86: relax GDT check in arch_set_info_guest()
  2020-05-20  7:53 ` [PATCH v3 1/3] x86: relax GDT check in arch_set_info_guest() Jan Beulich
  2020-05-20 10:20   ` Roger Pau Monné
@ 2020-05-22 13:27   ` Andrew Cooper
  2020-05-22 14:14     ` Jan Beulich
  1 sibling, 1 reply; 7+ messages in thread
From: Andrew Cooper @ 2020-05-22 13:27 UTC (permalink / raw)
  To: Jan Beulich, xen-devel; +Cc: Wei Liu, Roger Pau Monné

On 20/05/2020 08:53, Jan Beulich wrote:
> It is wrong for us to check frames beyond the guest specified limit
> (in the compat case another loop bound is already correct).
>
> Signed-off-by: Jan Beulich <jbeulich@suse.com>

I'm still not overly convinced this is a good idea, because all it will
allow people to do is write lazy code which breaks on older Xen.

However, if you still insist, Acked-by: Andrew Cooper
<andrew.cooper3@citrix.com>


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v3 1/3] x86: relax GDT check in arch_set_info_guest()
  2020-05-22 13:27   ` Andrew Cooper
@ 2020-05-22 14:14     ` Jan Beulich
  0 siblings, 0 replies; 7+ messages in thread
From: Jan Beulich @ 2020-05-22 14:14 UTC (permalink / raw)
  To: Andrew Cooper; +Cc: xen-devel, Wei Liu, Roger Pau Monné

On 22.05.2020 15:27, Andrew Cooper wrote:
> On 20/05/2020 08:53, Jan Beulich wrote:
>> It is wrong for us to check frames beyond the guest specified limit
>> (in the compat case another loop bound is already correct).
>>
>> Signed-off-by: Jan Beulich <jbeulich@suse.com>
> 
> I'm still not overly convinced this is a good idea, because all it will
> allow people to do is write lazy code which breaks on older Xen.

Sounds a little like keeping bugs for the sake of keeping things
broken. The range of misbehaving versions could be shrunk by
backporting this change; I didn't intend to though, so far.

> However, if you still insist, Acked-by: Andrew Cooper
> <andrew.cooper3@citrix.com>

Thanks!

Jan


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-05-22 14:15 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-20  7:50 [PATCH v3 0/3] x86: XSA-298 follow-up Jan Beulich
2020-05-20  7:53 ` [PATCH v3 1/3] x86: relax GDT check in arch_set_info_guest() Jan Beulich
2020-05-20 10:20   ` Roger Pau Monné
2020-05-22 13:27   ` Andrew Cooper
2020-05-22 14:14     ` Jan Beulich
2020-05-20  7:54 ` [PATCH v3 2/3] x86: relax LDT " Jan Beulich
2020-05-20  7:54 ` [PATCH v3 3/3] x86/PV: polish pv_set_gdt() Jan Beulich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).