Commit 7d33101c authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] fix /proc mm_struct refcounting bug

From: Suparna Bhattacharya <suparna@in.ibm.com>

The /proc code's bare atomic_inc(&mm->count) is racy against __exit_mm()'s
mmput() on another CPU: it calls mmput() outside task_lock(tsk), and
task_lock() isn't appropriate locking anyway.

So what happens is:

	CPU0			          CPU1

      mmput()
      ->atomic_dec_and_lock(mm->mm_users)
                                          atomic_inc(mm->mm_users)
      ->list_del(mm->mmlist)
                                          mmput()
                                          ->atomic_dec_and_lock(mm->mm_users)
                                          ->list_del(mm->mmlist)

And the double list_del() of course goes splat.

So we use mmlist_lock to synchronise these steps.

The patch implements a new mmgrab() routine which increments mm_users only if
the mm isn't already going away.  Changes get_task_mm() and proc_pid_stat()
to call mmgrab() instead of a direct atomic_inc(&mm->mm_users).

Hugh, there's some cruft in swapoff which looks like it should be using
mmgrab()...
parent e3e0c299
......@@ -300,7 +300,7 @@ int proc_pid_stat(struct task_struct *task, char * buffer)
task_lock(task);
mm = task->mm;
if(mm)
atomic_inc(&mm->mm_users);
mm = mmgrab(mm);
if (task->tty) {
tty_pgrp = task->tty->pgrp;
tty_nr = task->tty->device;
......
......@@ -638,6 +638,8 @@ static inline void mmdrop(struct mm_struct * mm)
/* mmput gets rid of the mappings and all user-space */
extern void mmput(struct mm_struct *);
/* Grab a reference to the mm if its not already going away */
extern struct mm_struct *mmgrab(struct mm_struct *);
/* Remove the current tasks stale references to the old mm_struct */
extern void mm_release(struct task_struct *, struct mm_struct *);
......@@ -745,7 +747,7 @@ static inline struct mm_struct * get_task_mm(struct task_struct * task)
task_lock(task);
mm = task->mm;
if (mm)
atomic_inc(&mm->mm_users);
mm = mmgrab(mm);
task_unlock(task);
return mm;
......
......@@ -398,6 +398,23 @@ void mmput(struct mm_struct *mm)
}
}
/*
* Checks if the use count of an mm is non-zero and if so
* returns a reference to it after bumping up the use count.
* If the use count is zero, it means this mm is going away,
* so return NULL.
*/
struct mm_struct *mmgrab(struct mm_struct *mm)
{
spin_lock(&mmlist_lock);
if (!atomic_read(&mm->mm_users))
mm = NULL;
else
atomic_inc(&mm->mm_users);
spin_unlock(&mmlist_lock);
return mm;
}
/* Please note the differences between mmput and mm_release.
* mmput is called whenever we stop holding onto a mm_struct,
* error success whatever.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment