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 Jason