Skip to content
  • Andrea Arcangeli's avatar
    userfaultfd: use RCU to free the task struct when fork fails · 8bae4398
    Andrea Arcangeli authored
    commit c3f3ce04 upstream.
    
    The task structure is freed while get_mem_cgroup_from_mm() holds
    rcu_read_lock() and dereferences mm->owner.
    
      get_mem_cgroup_from_mm()                failing fork()
      ----                                    ---
      task = mm->owner
                                              mm->owner = NULL;
                                              free(task)
      if (task) *task; /* use after free */
    
    The fix consists in freeing the task with RCU also in the fork failure
    case, exactly like it always happens for the regular exit(2) path.  That
    is enough to make the rcu_read_lock hold in get_mem_cgroup_from_mm()
    (left side above) effective to avoid a use after free when dereferencing
    the task structure.
    
    An alternate possible fix would be to defer the delivery of the
    userfaultfd contexts to the monitor until after fork() is guaranteed to
    succeed.  Such a change would require more changes because it would
    create a strict ordering dependency where the uffd methods would need to
    be called beyond the last potentially failing branch in order to be
    safe.  This solution as opposed only adds the dependency to common code
    to set mm->owner to NULL and to free the task struct that was pointed by
    mm->owner with RCU, if fork ends up failing.  The userfaultfd methods
    can still be called anywhere during the fork runtime and the monitor
    will keep discarding orphaned "mm" coming from failed forks in userland.
    
    This race condition couldn't trigger if CONFIG_MEMCG was set =n at build
    time.
    
    [aarcange@redhat.com: improve changelog, reduce #ifdefs per Michal]
      Link: http://lkml.kernel.org/r/20190429035752.4508-1-aarcange@redhat.com
    Link: http://lkml.kernel.org/r/20190325225636.11635-2-aarcange@redhat.com
    Fixes: 893e26e6
    
     ("userfaultfd: non-cooperative: Add fork() event")
    Signed-off-by: default avatarAndrea Arcangeli <aarcange@redhat.com>
    Tested-by: default avatarzhong jiang <zhongjiang@huawei.com>
    Reported-by: default avatar <syzbot+cbb52e396df3e565ab02@syzkaller.appspotmail.com>
    Cc: Oleg Nesterov <oleg@redhat.com>
    Cc: Jann Horn <jannh@google.com>
    Cc: Hugh Dickins <hughd@google.com>
    Cc: Mike Rapoport <rppt@linux.vnet.ibm.com>
    Cc: Mike Kravetz <mike.kravetz@oracle.com>
    Cc: Peter Xu <peterx@redhat.com>
    Cc: Jason Gunthorpe <jgg@mellanox.com>
    Cc: "Kirill A . Shutemov" <kirill.shutemov@linux.intel.com>
    Cc: Michal Hocko <mhocko@suse.com>
    Cc: zhong jiang <zhongjiang@huawei.com>
    Cc: syzbot+cbb52e396df3e565ab02@syzkaller.appspotmail.com
    Cc: <stable@vger.kernel.org>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    8bae4398