• Linus Torvalds's avatar
    Merge branch 'expand-stack' · 9471f1f2
    Linus Torvalds authored
    This modifies our user mode stack expansion code to always take the
    mmap_lock for writing before modifying the VM layout.
    
    It's actually something we always technically should have done, but
    because we didn't strictly need it, we were being lazy ("opportunistic"
    sounds so much better, doesn't it?) about things, and had this hack in
    place where we would extend the stack vma in-place without doing the
    proper locking.
    
    And it worked fine.  We just needed to change vm_start (or, in the case
    of grow-up stacks, vm_end) and together with some special ad-hoc locking
    using the anon_vma lock and the mm->page_table_lock, it all was fairly
    straightforward.
    
    That is, it was all fine until Ruihan Li pointed out that now that the
    vma layout uses the maple tree code, we *really* don't just change
    vm_start and vm_end any more, and the locking really is broken.  Oops.
    
    It's not actually all _that_ horrible to fix this once and for all, and
    do proper locking, but it's a bit painful.  We have basically three
    different cases of stack expansion, and they all work just a bit
    differently:
    
     - the common and obvious case is the page fault handling. It's actually
       fairly simple and straightforward, except for the fact that we have
       something like 24 different versions of it, and you end up in a maze
       of twisty little passages, all alike.
    
     - the simplest case is the execve() code that creates a new stack.
       There are no real locking concerns because it's all in a private new
       VM that hasn't been exposed to anybody, but lockdep still can end up
       unhappy if you get it wrong.
    
     - and finally, we have GUP and page pinning, which shouldn't really be
       expanding the stack in the first place, but in addition to execve()
       we also use it for ptrace(). And debuggers do want to possibly access
       memory under the stack pointer and thus need to be able to expand the
       stack as a special case.
    
    None of these cases are exactly complicated, but the page fault case in
    particular is just repeated slightly differently many many times.  And
    ia64 in particular has a fairly complicated situation where you can have
    both a regular grow-down stack _and_ a special grow-up stack for the
    register backing store.
    
    So to make this slightly more manageable, the bulk of this series is to
    first create a helper function for the most common page fault case, and
    convert all the straightforward architectures to it.
    
    Thus the new 'lock_mm_and_find_vma()' helper function, which ends up
    being used by x86, arm, powerpc, mips, riscv, alpha, arc, csky, hexagon,
    loongarch, nios2, sh, sparc32, and xtensa.  So we not only convert more
    than half the architectures, we now have more shared code and avoid some
    of those twisty little passages.
    
    And largely due to this common helper function, the full diffstat of
    this series ends up deleting more lines than it adds.
    
    That still leaves eight architectures (ia64, m68k, microblaze, openrisc,
    parisc, s390, sparc64 and um) that end up doing 'expand_stack()'
    manually because they are doing something slightly different from the
    normal pattern.  Along with the couple of special cases in execve() and
    GUP.
    
    So there's a couple of patches that first create 'locked' helper
    versions of the stack expansion functions, so that there's a obvious
    path forward in the conversion.  The execve() case is then actually
    pretty simple, and is a nice cleanup from our old "grow-up stackls are
    special, because at execve time even they grow down".
    
    The #ifdef CONFIG_STACK_GROWSUP in that code just goes away, because
    it's just more straightforward to write out the stack expansion there
    manually, instead od having get_user_pages_remote() do it for us in some
    situations but not others and have to worry about locking rules for GUP.
    
    And the final step is then to just convert the remaining odd cases to a
    new world order where 'expand_stack()' is called with the mmap_lock held
    for reading, but where it might drop it and upgrade it to a write, only
    to return with it held for reading (in the success case) or with it
    completely dropped (in the failure case).
    
    In the process, we remove all the stack expansion from GUP (where
    dropping the lock wouldn't be ok without special rules anyway), and add
    it in manually to __access_remote_vm() for ptrace().
    
    Thanks to Adrian Glaubitz and Frank Scheiner who tested the ia64 cases.
    Everything else here felt pretty straightforward, but the ia64 rules for
    stack expansion are really quite odd and very different from everything
    else.  Also thanks to Vegard Nossum who caught me getting one of those
    odd conditions entirely the wrong way around.
    
    Anyway, I think I want to actually move all the stack expansion code to
    a whole new file of its own, rather than have it split up between
    mm/mmap.c and mm/memory.c, but since this will have to be backported to
    the initial maple tree vma introduction anyway, I tried to keep the
    patches _fairly_ minimal.
    
    Also, while I don't think it's valid to expand the stack from GUP, the
    final patch in here is a "warn if some crazy GUP user wants to try to
    expand the stack" patch.  That one will be reverted before the final
    release, but it's left to catch any odd cases during the merge window
    and release candidates.
    Reported-by: default avatarRuihan Li <lrh2000@pku.edu.cn>
    
    * branch 'expand-stack':
      gup: add warning if some caller would seem to want stack expansion
      mm: always expand the stack with the mmap write lock held
      execve: expand new process stack manually ahead of time
      mm: make find_extend_vma() fail if write lock not held
      powerpc/mm: convert coprocessor fault to lock_mm_and_find_vma()
      mm/fault: convert remaining simple cases to lock_mm_and_find_vma()
      arm/mm: Convert to using lock_mm_and_find_vma()
      riscv/mm: Convert to using lock_mm_and_find_vma()
      mips/mm: Convert to using lock_mm_and_find_vma()
      powerpc/mm: Convert to using lock_mm_and_find_vma()
      arm64/mm: Convert to using lock_mm_and_find_vma()
      mm: make the page fault mmap locking killable
      mm: introduce new 'lock_mm_and_find_vma()' page fault helper
    9471f1f2
Kconfig 12.7 KB