diff options
author | Oleg Nesterov <oleg@redhat.com> | 2012-01-31 11:15:11 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-02-01 17:39:01 -0500 |
commit | 6d08f2c7139790c268820a2e590795cb8333181a (patch) | |
tree | 78b2773b8ca6949f9561af641908e4086ba193cb /fs/proc/base.c | |
parent | 572d34b946bae070debd42db1143034d9687e13f (diff) |
proc: make sure mem_open() doesn't pin the target's memory
Once /proc/pid/mem is opened, the memory can't be released until
mem_release() even if its owner exits.
Change mem_open() to do atomic_inc(mm_count) + mmput(), this only
pins mm_struct. Change mem_rw() to do atomic_inc_not_zero(mm_count)
before access_remote_vm(), this verifies that this mm is still alive.
I am not sure what should mem_rw() return if atomic_inc_not_zero()
fails. With this patch it returns zero to match the "mm == NULL" case,
may be it should return -EINVAL like it did before e268337d.
Perhaps it makes sense to add the additional fatal_signal_pending()
check into the main loop, to ensure we do not hold this memory if
the target task was oom-killed.
Cc: stable@kernel.org
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r-- | fs/proc/base.c | 14 |
1 files changed, 13 insertions, 1 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index be1909041685..d9512bd03e6c 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -711,6 +711,13 @@ static int mem_open(struct inode* inode, struct file* file) | |||
711 | if (IS_ERR(mm)) | 711 | if (IS_ERR(mm)) |
712 | return PTR_ERR(mm); | 712 | return PTR_ERR(mm); |
713 | 713 | ||
714 | if (mm) { | ||
715 | /* ensure this mm_struct can't be freed */ | ||
716 | atomic_inc(&mm->mm_count); | ||
717 | /* but do not pin its memory */ | ||
718 | mmput(mm); | ||
719 | } | ||
720 | |||
714 | /* OK to pass negative loff_t, we can catch out-of-range */ | 721 | /* OK to pass negative loff_t, we can catch out-of-range */ |
715 | file->f_mode |= FMODE_UNSIGNED_OFFSET; | 722 | file->f_mode |= FMODE_UNSIGNED_OFFSET; |
716 | file->private_data = mm; | 723 | file->private_data = mm; |
@@ -734,6 +741,9 @@ static ssize_t mem_rw(struct file *file, char __user *buf, | |||
734 | return -ENOMEM; | 741 | return -ENOMEM; |
735 | 742 | ||
736 | copied = 0; | 743 | copied = 0; |
744 | if (!atomic_inc_not_zero(&mm->mm_users)) | ||
745 | goto free; | ||
746 | |||
737 | while (count > 0) { | 747 | while (count > 0) { |
738 | int this_len = min_t(int, count, PAGE_SIZE); | 748 | int this_len = min_t(int, count, PAGE_SIZE); |
739 | 749 | ||
@@ -761,6 +771,8 @@ static ssize_t mem_rw(struct file *file, char __user *buf, | |||
761 | } | 771 | } |
762 | *ppos = addr; | 772 | *ppos = addr; |
763 | 773 | ||
774 | mmput(mm); | ||
775 | free: | ||
764 | free_page((unsigned long) page); | 776 | free_page((unsigned long) page); |
765 | return copied; | 777 | return copied; |
766 | } | 778 | } |
@@ -797,7 +809,7 @@ static int mem_release(struct inode *inode, struct file *file) | |||
797 | { | 809 | { |
798 | struct mm_struct *mm = file->private_data; | 810 | struct mm_struct *mm = file->private_data; |
799 | if (mm) | 811 | if (mm) |
800 | mmput(mm); | 812 | mmdrop(mm); |
801 | return 0; | 813 | return 0; |
802 | } | 814 | } |
803 | 815 | ||