Re: [PATCH v2 15/15] mm/gup: sanity-check with CONFIG_DEBUG_VM that anonymous pages are exclusive when (un)pinning

From: David Hildenbrand
Date: Sat Mar 19 2022 - 06:23:02 EST


On 19.03.22 00:35, Jason Gunthorpe wrote:
> On Tue, Mar 15, 2022 at 11:47:41AM +0100, David Hildenbrand wrote:
>> Let's verify when (un)pinning anonymous pages that we always deal with
>> exclusive anonymous pages, which guarantees that we'll have a reliable
>> PIN, meaning that we cannot end up with the GUP pin being inconsistent
>> with he pages mapped into the page tables due to a COW triggered
>> by a write fault.
>>
>> When pinning pages, after conditionally triggering GUP unsharing of
>> possibly shared anonymous pages, we should always only see exclusive
>> anonymous pages. Note that anonymous pages that are mapped writable
>> must be marked exclusive, otherwise we'd have a BUG.
>>
>> When pinning during ordinary GUP, simply add a check after our
>> conditional GUP-triggered unsharing checks. As we know exactly how the
>> page is mapped, we know exactly in which page we have to check for
>> PageAnonExclusive().
>>
>> When pinning via GUP-fast we have to be careful, because we can race with
>> fork(): verify only after we made sure via the seqcount that we didn't
>> race with concurrent fork() that we didn't end up pinning a possibly
>> shared anonymous page.
>>
>> Similarly, when unpinning, verify that the pages are still marked as
>> exclusive: otherwise something turned the pages possibly shared, which
>> can result in random memory corruptions, which we really want to catch.
>>
>> With only the pinned pages at hand and not the actual page table entries
>> we have to be a bit careful: hugetlb pages are always mapped via a
>> single logical page table entry referencing the head page and
>> PG_anon_exclusive of the head page applies. Anon THP are a bit more
>> complicated, because we might have obtained the page reference either via
>> a PMD or a PTE -- depending on the mapping type we either have to check
>> PageAnonExclusive of the head page (PMD-mapped THP) or the tail page
>> (PTE-mapped THP) applies: as we don't know and to make our life easier,
>> check that either is set.
>>
>> Take care to not verify in case we're unpinning during GUP-fast because
>> we detected concurrent fork(): we might stumble over an anonymous page
>> that is now shared.
>>
>> Signed-off-by: David Hildenbrand <david@xxxxxxxxxx>
>> mm/gup.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++-
>> mm/huge_memory.c | 3 +++
>> mm/hugetlb.c | 3 +++
>> 3 files changed, 63 insertions(+), 1 deletion(-)
>>
>> diff --git a/mm/gup.c b/mm/gup.c
>> index 92dcd92f9d67..72e39b77da10 100644
>> +++ b/mm/gup.c
>> @@ -45,6 +45,38 @@ static void hpage_pincount_sub(struct page *page, int refs)
>> atomic_sub(refs, compound_pincount_ptr(page));
>> }
>>
>> +static inline void sanity_check_pinned_pages(struct page **pages,
>> + unsigned long npages)
>> +{
>> +#ifdef CONFIG_DEBUG_VM
>
> Perhaps:
>
> if (!IS_ENABLED(CONFIG_DEBUG_VM))
> return;
>
> So this gets compilation coverage

Makes sense, that code should compile just fine without CONFIG_DEBUG_VM.
Thanks!

--
Thanks,

David / dhildenb