Re: mm: use-after-free in collapse_huge_page

From: Kirill A. Shutemov
Date: Wed Sep 07 2016 - 08:26:46 EST


On Mon, Aug 29, 2016 at 05:35:48PM +0200, Andrea Arcangeli wrote:
> Hello Kirill,
>
> On Mon, Aug 29, 2016 at 03:42:33PM +0300, Kirill A. Shutemov wrote:
> > @@ -898,13 +899,13 @@ static bool __collapse_huge_page_swapin(struct mm_struct *mm,
> > /* do_swap_page returns VM_FAULT_RETRY with released mmap_sem */
> > if (ret & VM_FAULT_RETRY) {
> > down_read(&mm->mmap_sem);
> > - if (hugepage_vma_revalidate(mm, address)) {
> > + if (hugepage_vma_revalidate(mm, address, &vma)) {
> > /* vma is no longer available, don't continue to swapin */
> > trace_mm_collapse_huge_page_swapin(mm, swapped_in, referenced, 0);
> > return false;
> > }
> > /* check if the pmd is still valid */
> > - if (mm_find_pmd(mm, address) != pmd)
> > + if (mm_find_pmd(mm, address) != pmd || vma != fe.vma)
> > return false;
> > }
> > if (ret & VM_FAULT_ERROR) {
>
> You check if the vma changed if the mmap_sem was released by the
> VM_FAULT_RETRY case but not below:
>
> /*
> * Prevent all access to pagetables with the exception of
> * gup_fast later handled by the ptep_clear_flush and the VM
> > @@ -994,7 +995,7 @@ static void collapse_huge_page(struct mm_struct *mm,
> > * handled by the anon_vma lock + PG_lock.
> > */
> > down_write(&mm->mmap_sem);
> > - result = hugepage_vma_revalidate(mm, address);
> > + result = hugepage_vma_revalidate(mm, address, &vma);
> > if (result)
> > goto out;
> > /* check if the pmd is still valid */
> if (mm_find_pmd(mm, address) != pmd)
> goto out;
>
> Here you go ahead without care if the vma has changed as long as the
> "vma" pointer was updated to the new one, and the pmd is still present
> and stable (present and not huge) and all vma details matched as
> before.
>
> Either we care that the vma changed in both places or we don't in
> either of the two places.
>
> The idea was that even if the vma changed it doesn't matter because
> it's still good to proceed for a collapse if all revalidation check
> pass.
>
> What we failed at, was in refreshing the pointer of the vma to the new
> one after the vma revalidation passed, so that the code that goes
> ahead uses the right vma pointer and not the stale one we got
> initially.
>
> Now it may give a perception that it is safer to check fa.vma != vma
> but in reality it is not, because the vma may be freed and reallocated
> in exactly the same address...
>
> So I think the vma != fe.vma check shall be removed because no matter
> what the safety of the vma revalidate cannot come from checking if the
> pointer has not changed and it must come from something else.

[ Finally back to this. ]

Here's updated version.