On 4/4/19 1:43 AM, Peter Zijlstra wrote:
> 
> You must be so glad I no longer use kmap_atomic from NMI context :-)
> 
> On Wed, Apr 03, 2019 at 11:34:04AM -0600, Khalid Aziz wrote:
>> +static inline void xpfo_kmap(void *kaddr, struct page *page)
>> +{
>> +    unsigned long flags;
>> +
>> +    if (!static_branch_unlikely(&xpfo_inited))
>> +            return;
>> +
>> +    if (!PageXpfoUser(page))
>> +            return;
>> +
>> +    /*
>> +     * The page was previously allocated to user space, so
>> +     * map it back into the kernel if needed. No TLB flush required.
>> +     */
>> +    spin_lock_irqsave(&page->xpfo_lock, flags);
>> +
>> +    if ((atomic_inc_return(&page->xpfo_mapcount) == 1) &&
>> +            TestClearPageXpfoUnmapped(page))
>> +            set_kpte(kaddr, page, PAGE_KERNEL);
>> +
>> +    spin_unlock_irqrestore(&page->xpfo_lock, flags);
> 
> That's a really sad sequence, not wrong, but sad. _3_ atomic operations,
> 2 on likely the same cacheline. And mostly all pointless.
> 
> This patch makes xpfo_mapcount an atomic, but then all modifications are
> under the spinlock, what gives?
> 
> Anyway, a possibly saner sequence might be:
> 
>       if (atomic_inc_not_zero(&page->xpfo_mapcount))
>               return;
> 
>       spin_lock_irqsave(&page->xpfo_lock, flag);
>       if ((atomic_inc_return(&page->xpfo_mapcount) == 1) &&
>           TestClearPageXpfoUnmapped(page))
>               set_kpte(kaddr, page, PAGE_KERNEL);
>       spin_unlock_irqrestore(&page->xpfo_lock, flags);
> 
>> +}
>> +
>> +static inline void xpfo_kunmap(void *kaddr, struct page *page)
>> +{
>> +    unsigned long flags;
>> +
>> +    if (!static_branch_unlikely(&xpfo_inited))
>> +            return;
>> +
>> +    if (!PageXpfoUser(page))
>> +            return;
>> +
>> +    /*
>> +     * The page is to be allocated back to user space, so unmap it from
>> +     * the kernel, flush the TLB and tag it as a user page.
>> +     */
>> +    spin_lock_irqsave(&page->xpfo_lock, flags);
>> +
>> +    if (atomic_dec_return(&page->xpfo_mapcount) == 0) {
>> +#ifdef CONFIG_XPFO_DEBUG
>> +            WARN_ON(PageXpfoUnmapped(page));
>> +#endif
>> +            SetPageXpfoUnmapped(page);
>> +            set_kpte(kaddr, page, __pgprot(0));
>> +            xpfo_flush_kernel_tlb(page, 0);
> 
> You didn't speak about the TLB invalidation anywhere. But basically this
> is one that x86 cannot do.
> 
>> +    }
>> +
>> +    spin_unlock_irqrestore(&page->xpfo_lock, flags);
> 
> Idem:
> 
>       if (atomic_add_unless(&page->xpfo_mapcount, -1, 1))
>               return;
> 
>       ....
> 
> 
>> +}
> 
> Also I'm failing to see the point of PG_xpfo_unmapped, afaict it
> is identical to !atomic_read(&page->xpfo_mapcount).
> 

Thanks Peter. I really appreciate your review. Your feedback helps make
this code better and closer to where I can feel comfortable not calling
it RFC any more.

The more I look at xpfo_kmap()/xpfo_kunmap() code, the more I get
uncomfortable with it. As you pointed out about calling kmap_atomic from
NMI context, that just makes the kmap_atomic code look even worse. I
pointed out one problem with this code in cover letter and suggested a
rewrite. I see these problems with this code:

1. When xpfo_kmap maps a page back in physmap, it opens up the ret2dir
attack security hole again even if just for the duration of kmap. A kmap
can stay around for some time if the page is being used for I/O.

2. This code uses spinlock which leads to problems. If it does not
disable IRQ, it is exposed to deadlock around xpfo_lock. If it disables
IRQ, I think it can still deadlock around pgd_lock.

I think a better implementation of xpfo_kmap()/xpfo_kunmap() would map
the page at a new virtual address similar to what kmap_high for i386
does. This avoids re-opening the ret2dir security hole. We can also
possibly do away with xpfo_lock saving bytes in page-frame and the not
so sane code sequence can go away.

Good point about PG_xpfo_unmapped flag. You are right that it can be
replaced with !atomic_read(&page->xpfo_mapcount).

Thanks,
Khalid

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to