[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH MM-PART3 v2 06/12] xen/arm: mm: Sanity check any update of Xen page tables



On Wed, 12 Jun 2019, Julien Grall wrote:
> Hi Stefano,
> 
> On 12/06/2019 01:10, Stefano Stabellini wrote:
> > On Tue, 14 May 2019, Julien Grall wrote:
> > > The code handling Xen PT update has quite a few restrictions on what it
> > > can do. This is not a bad thing as it keeps the code simple.
> > > 
> > > There are already a few checks scattered in current page table handling.
> > > However they are not sufficient as they could still allow to
> > > modify/remove entry with contiguous bit set.
> > > 
> > > The checks are divided in two sets:
> > >      - per entry check: They are gathered in a new function that will
> > >      check whether an update is valid based on the flags passed and the
> > >      current value of an entry.
> > >      - global check: They are sanity check on xen_pt_update() parameters.
> > > 
> > > Additionally to contiguous check, we also now check that the caller is
> > > not trying to modify the memory attributes of an entry.
> > > 
> > > Lastly, it was probably a bit over the top to forbid removing an
> > > invalid mapping. This could just be ignored. The new behavior will be
> > > helpful in future changes.
> > > 
> > > Signed-off-by: Julien Grall <julien.grall@xxxxxxx>
> > > Reviewed-by: Andrii Anisov <andrii_anisov@xxxxxxxx>
> > > 
> > > ---
> > >      Changes in v2:
> > >          - Correctly detect the removal of a page
> > >          - Fix ASSERT on flags in the else case
> > >          - Add Andrii's reviewed-by
> > > ---
> > >   xen/arch/arm/mm.c | 115
> > > +++++++++++++++++++++++++++++++++++++++++++++---------
> > >   1 file changed, 97 insertions(+), 18 deletions(-)
> > > 
> > > diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c
> > > index 2192dede55..45a6f9287f 100644
> > > --- a/xen/arch/arm/mm.c
> > > +++ b/xen/arch/arm/mm.c
> > > @@ -50,6 +50,19 @@ struct domain *dom_xen, *dom_io, *dom_cow;
> > >   #undef mfn_to_virt
> > >   #define mfn_to_virt(mfn) __mfn_to_virt(mfn_x(mfn))
> > >   +#ifdef NDEBUG
> > > +static inline void
> > > +__attribute__ ((__format__ (__printf__, 1, 2)))
> > > +mm_printk(const char *fmt, ...) {}
> > > +#else
> > > +#define mm_printk(fmt, args...)             \
> > > +    do                                      \
> > > +    {                                       \
> > > +        dprintk(XENLOG_ERR, fmt, ## args);  \
> > > +        WARN();                             \
> > > +    } while (0);
> > > +#endif
> > > +
> > >   #define DEFINE_PAGE_TABLES(name, nr)                    \
> > >   lpae_t __aligned(PAGE_SIZE) name[LPAE_ENTRIES * (nr)]
> > >   @@ -968,12 +981,74 @@ enum xenmap_operation {
> > >       RESERVE
> > >   };
> > >   +/* Sanity check of the entry */
> > > +static bool xen_pt_check_entry(lpae_t entry, mfn_t mfn, unsigned int
> > > flags)
> > > +{
> > > +    /* Sanity check when modifying a page. */
> > > +    if ( (flags & _PAGE_PRESENT) && mfn_eq(mfn, INVALID_MFN) )
> > > +    {
> > 
> > I understand we could skip the valid check on REMOVE, but should we skip
> > it on MODIFY too? Is that also going to be helpful in future changes?
> 
> Hmmm, I can't exactly remember why I didn't check the valid bit here.
> 
> I did it for REMOVE as for the early FDT mapping it is more convenient to
> remove the full possible range over keeping track of the exact start/size.
> 
> I would assume the same would hold for MODIFY, but I don't have a concrete
> example here. I am happy to add the valid check and defer the decision to
> remove it if it is deem to be useful in the future.

Yes, it would be better


> > 
> > > +        /* We don't allow changing memory attributes. */
> > > +        if ( entry.pt.ai != PAGE_AI_MASK(flags) )
> > > +        {
> > > +            mm_printk("Modifying memory attributes is not allowed (0x%x
> > > -> 0x%x).\n",
> > > +                      entry.pt.ai, PAGE_AI_MASK(flags));
> > > +            return false;
> > > +        }
> > > +
> > > +        /* We don't allow modifying entry with contiguous bit set. */
> > > +        if ( entry.pt.contig )
> > > +        {
> > > +            mm_printk("Modifying entry with contiguous bit set is not
> > > allowed.\n");
> > > +            return false;
> > > +        }
> > > +    }
> > > +    /* Sanity check when inserting a page */
> > > +    else if ( flags & _PAGE_PRESENT )
> > > +    {
> > > +        /* We should be here with a valid MFN. */
> > > +        ASSERT(!mfn_eq(mfn, INVALID_MFN));
> > > +
> > > +        /* We don't allow replacing any valid entry. */
> > > +        if ( lpae_is_valid(entry) )
> > > +        {
> > > +            mm_printk("Changing MFN for a valid entry is not allowed
> > > (%#"PRI_mfn" -> %#"PRI_mfn").\n",
> > > +                      mfn_x(lpae_get_mfn(entry)), mfn_x(mfn));
> > > +            return false;
> > > +        }
> > > +    }
> > > +    /* Sanity check when removing a page. */
> > > +    else if ( (flags & (_PAGE_PRESENT|_PAGE_POPULATE)) == 0 )
> > > +    {
> > > +        /* We should be here with an invalid MFN. */
> > > +        ASSERT(mfn_eq(mfn, INVALID_MFN));
> > > +
> > > +        /* We don't allow removing page with contiguous bit set. */
> > > +        if ( entry.pt.contig )
> > > +        {
> > > +            mm_printk("Removing entry with contiguous bit set is not
> > > allowed.\n");
> > > +            return false;
> > > +        }
> > > +    }
> > > +    /* Sanity check when populating the page-table. No check so far. */
> > > +    else
> > > +    {
> > > +        ASSERT(flags & _PAGE_POPULATE);
> > > +        /* We should be here with an invalid MFN */
> > > +        ASSERT(mfn_eq(mfn, INVALID_MFN));
> > > +    }
> > > +
> > > +    return true;
> > > +}
> > > +
> > >   static int xen_pt_update_entry(enum xenmap_operation op, unsigned long
> > > addr,
> > >                                  mfn_t mfn, unsigned int flags)
> > >   {
> > >       lpae_t pte, *entry;
> > >       lpae_t *third = NULL;
> > >   +    /* _PAGE_POPULATE and _PAGE_PRESENT should never be set together.
> > > */
> > > +    ASSERT((flags & (_PAGE_POPULATE|_PAGE_PRESENT)) !=
> > > (_PAGE_POPULATE|_PAGE_PRESENT));
> > 
> > over 80 chars?
> 
> It is 87 chars, I was hoping you didn't notice it :). The line splitting
> result to nasty code. Alternatively, I could introduce a define for
> _PAGE_POPULATE|_PAGE_PRESENT, maybe EXCLUSIVE_FLAGS?
> 
> Any preference?

I don't care so much about 80 chars limit.
Anything but another macro :-)

 
> > >       entry = &xen_second[second_linear_offset(addr)];
> > >       if ( !lpae_is_valid(*entry) || !lpae_is_table(*entry, 2) )
> > >       {
> > > @@ -989,15 +1064,12 @@ static int xen_pt_update_entry(enum
> > > xenmap_operation op, unsigned long addr,
> > >       third = mfn_to_virt(lpae_get_mfn(*entry));
> > >       entry = &third[third_table_offset(addr)];
> > >   +    if ( !xen_pt_check_entry(*entry, mfn, flags) )
> > > +        return -EINVAL;
> > > +
> > >       switch ( op ) {
> > >           case INSERT:
> > >           case RESERVE:
> > > -            if ( lpae_is_valid(*entry) )
> > > -            {
> > > -                printk("%s: trying to replace an existing mapping
> > > addr=%lx mfn=%"PRI_mfn"\n",
> > > -                       __func__, addr, mfn_x(mfn));
> > > -                return -EINVAL;
> > > -            }
> > >               if ( op == RESERVE )
> > >                   break;
> > >               pte = mfn_to_xen_entry(mfn, PAGE_AI_MASK(flags));
> > > @@ -1009,12 +1081,6 @@ static int xen_pt_update_entry(enum
> > > xenmap_operation op, unsigned long addr,
> > >               break;
> > >           case MODIFY:
> > >           case REMOVE:
> > > -            if ( !lpae_is_valid(*entry) )
> > > -            {
> > > -                printk("%s: trying to %s a non-existing mapping
> > > addr=%lx\n",
> > > -                       __func__, op == REMOVE ? "remove" : "modify",
> > > addr);
> > > -                return -EINVAL;
> > > -            }
> > >               if ( op == REMOVE )
> > >                   pte.bits = 0;
> > >               else
> > > @@ -1022,12 +1088,6 @@ static int xen_pt_update_entry(enum
> > > xenmap_operation op, unsigned long addr,
> > >                   pte = *entry;
> > >                   pte.pt.ro = PAGE_RO_MASK(flags);
> > >                   pte.pt.xn = PAGE_XN_MASK(flags);
> > > -                if ( !pte.pt.ro && !pte.pt.xn )
> > > -                {
> > > -                    printk("%s: Incorrect combination for addr=%lx\n",
> > > -                           __func__, addr);
> > > -                    return -EINVAL;
> > > -                }
> > >               }
> > >               write_pte(entry, pte);
> > >               break;
> > > @@ -1049,6 +1109,25 @@ static int xen_pt_update(enum xenmap_operation op,
> > >       int rc = 0;
> > >       unsigned long addr = virt, addr_end = addr + nr_mfns * PAGE_SIZE;
> > >   +    /*
> > > +     * The hardware was configured to forbid mapping both writeable and
> > > +     * executable.
> > > +     * When modifying/creating mapping (i.e _PAGE_PRESENT is set),
> > > +     * prevent any update if this happen.
> > > +     */
> > > +    if ( (flags & _PAGE_PRESENT) && !PAGE_RO_MASK(flags) &&
> > > +         !PAGE_XN_MASK(flags) )
> > > +    {
> > > +        mm_printk("Mappings should not be both Writeable and
> > > Executable.\n");
> > > +        return -EINVAL;
> > > +    }
> > 
> > I am thinking this is serious enough that we might want to always print
> > this warning when this error happens. At the same time it is awkward to
> > have all the other messages using mm_printk and only this one being
> > different. So I'll live it to you, it is also OK at this.
> 
> Any error here means the caller didn't do sanity check (if input is external)
> or pass the wrong parameters. I purposefully chose mm_printk over a normal
> printk because this could potentially lead to a DoS if accessible from outside
> of Xen.
> 
> If the error happen, then there are an high chance with DEBUG (or hacking
> mm_printk to be used in non-debug build) will make it appear as well.
 
OK

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.