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
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu