diff options
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r-- | fs/proc/base.c | 126 |
1 files changed, 46 insertions, 80 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 9cde9edf9c4d..d4548dd49b02 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -198,26 +198,6 @@ static int proc_root_link(struct dentry *dentry, struct path *path) | |||
198 | return result; | 198 | return result; |
199 | } | 199 | } |
200 | 200 | ||
201 | static struct mm_struct *mm_access(struct task_struct *task, unsigned int mode) | ||
202 | { | ||
203 | struct mm_struct *mm; | ||
204 | int err; | ||
205 | |||
206 | err = mutex_lock_killable(&task->signal->cred_guard_mutex); | ||
207 | if (err) | ||
208 | return ERR_PTR(err); | ||
209 | |||
210 | mm = get_task_mm(task); | ||
211 | if (mm && mm != current->mm && | ||
212 | !ptrace_may_access(task, mode)) { | ||
213 | mmput(mm); | ||
214 | mm = ERR_PTR(-EACCES); | ||
215 | } | ||
216 | mutex_unlock(&task->signal->cred_guard_mutex); | ||
217 | |||
218 | return mm; | ||
219 | } | ||
220 | |||
221 | struct mm_struct *mm_for_maps(struct task_struct *task) | 201 | struct mm_struct *mm_for_maps(struct task_struct *task) |
222 | { | 202 | { |
223 | return mm_access(task, PTRACE_MODE_READ); | 203 | return mm_access(task, PTRACE_MODE_READ); |
@@ -711,6 +691,13 @@ static int mem_open(struct inode* inode, struct file* file) | |||
711 | if (IS_ERR(mm)) | 691 | if (IS_ERR(mm)) |
712 | return PTR_ERR(mm); | 692 | return PTR_ERR(mm); |
713 | 693 | ||
694 | if (mm) { | ||
695 | /* ensure this mm_struct can't be freed */ | ||
696 | atomic_inc(&mm->mm_count); | ||
697 | /* but do not pin its memory */ | ||
698 | mmput(mm); | ||
699 | } | ||
700 | |||
714 | /* OK to pass negative loff_t, we can catch out-of-range */ | 701 | /* OK to pass negative loff_t, we can catch out-of-range */ |
715 | file->f_mode |= FMODE_UNSIGNED_OFFSET; | 702 | file->f_mode |= FMODE_UNSIGNED_OFFSET; |
716 | file->private_data = mm; | 703 | file->private_data = mm; |
@@ -718,57 +705,13 @@ static int mem_open(struct inode* inode, struct file* file) | |||
718 | return 0; | 705 | return 0; |
719 | } | 706 | } |
720 | 707 | ||
721 | static ssize_t mem_read(struct file * file, char __user * buf, | 708 | static ssize_t mem_rw(struct file *file, char __user *buf, |
722 | size_t count, loff_t *ppos) | 709 | size_t count, loff_t *ppos, int write) |
723 | { | 710 | { |
724 | int ret; | ||
725 | char *page; | ||
726 | unsigned long src = *ppos; | ||
727 | struct mm_struct *mm = file->private_data; | 711 | struct mm_struct *mm = file->private_data; |
728 | 712 | unsigned long addr = *ppos; | |
729 | if (!mm) | 713 | ssize_t copied; |
730 | return 0; | ||
731 | |||
732 | page = (char *)__get_free_page(GFP_TEMPORARY); | ||
733 | if (!page) | ||
734 | return -ENOMEM; | ||
735 | |||
736 | ret = 0; | ||
737 | |||
738 | while (count > 0) { | ||
739 | int this_len, retval; | ||
740 | |||
741 | this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; | ||
742 | retval = access_remote_vm(mm, src, page, this_len, 0); | ||
743 | if (!retval) { | ||
744 | if (!ret) | ||
745 | ret = -EIO; | ||
746 | break; | ||
747 | } | ||
748 | |||
749 | if (copy_to_user(buf, page, retval)) { | ||
750 | ret = -EFAULT; | ||
751 | break; | ||
752 | } | ||
753 | |||
754 | ret += retval; | ||
755 | src += retval; | ||
756 | buf += retval; | ||
757 | count -= retval; | ||
758 | } | ||
759 | *ppos = src; | ||
760 | |||
761 | free_page((unsigned long) page); | ||
762 | return ret; | ||
763 | } | ||
764 | |||
765 | static ssize_t mem_write(struct file * file, const char __user *buf, | ||
766 | size_t count, loff_t *ppos) | ||
767 | { | ||
768 | int copied; | ||
769 | char *page; | 714 | char *page; |
770 | unsigned long dst = *ppos; | ||
771 | struct mm_struct *mm = file->private_data; | ||
772 | 715 | ||
773 | if (!mm) | 716 | if (!mm) |
774 | return 0; | 717 | return 0; |
@@ -778,31 +721,54 @@ static ssize_t mem_write(struct file * file, const char __user *buf, | |||
778 | return -ENOMEM; | 721 | return -ENOMEM; |
779 | 722 | ||
780 | copied = 0; | 723 | copied = 0; |
724 | if (!atomic_inc_not_zero(&mm->mm_users)) | ||
725 | goto free; | ||
726 | |||
781 | while (count > 0) { | 727 | while (count > 0) { |
782 | int this_len, retval; | 728 | int this_len = min_t(int, count, PAGE_SIZE); |
783 | 729 | ||
784 | this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; | 730 | if (write && copy_from_user(page, buf, this_len)) { |
785 | if (copy_from_user(page, buf, this_len)) { | ||
786 | copied = -EFAULT; | 731 | copied = -EFAULT; |
787 | break; | 732 | break; |
788 | } | 733 | } |
789 | retval = access_remote_vm(mm, dst, page, this_len, 1); | 734 | |
790 | if (!retval) { | 735 | this_len = access_remote_vm(mm, addr, page, this_len, write); |
736 | if (!this_len) { | ||
791 | if (!copied) | 737 | if (!copied) |
792 | copied = -EIO; | 738 | copied = -EIO; |
793 | break; | 739 | break; |
794 | } | 740 | } |
795 | copied += retval; | 741 | |
796 | buf += retval; | 742 | if (!write && copy_to_user(buf, page, this_len)) { |
797 | dst += retval; | 743 | copied = -EFAULT; |
798 | count -= retval; | 744 | break; |
745 | } | ||
746 | |||
747 | buf += this_len; | ||
748 | addr += this_len; | ||
749 | copied += this_len; | ||
750 | count -= this_len; | ||
799 | } | 751 | } |
800 | *ppos = dst; | 752 | *ppos = addr; |
801 | 753 | ||
754 | mmput(mm); | ||
755 | free: | ||
802 | free_page((unsigned long) page); | 756 | free_page((unsigned long) page); |
803 | return copied; | 757 | return copied; |
804 | } | 758 | } |
805 | 759 | ||
760 | static ssize_t mem_read(struct file *file, char __user *buf, | ||
761 | size_t count, loff_t *ppos) | ||
762 | { | ||
763 | return mem_rw(file, buf, count, ppos, 0); | ||
764 | } | ||
765 | |||
766 | static ssize_t mem_write(struct file *file, const char __user *buf, | ||
767 | size_t count, loff_t *ppos) | ||
768 | { | ||
769 | return mem_rw(file, (char __user*)buf, count, ppos, 1); | ||
770 | } | ||
771 | |||
806 | loff_t mem_lseek(struct file *file, loff_t offset, int orig) | 772 | loff_t mem_lseek(struct file *file, loff_t offset, int orig) |
807 | { | 773 | { |
808 | switch (orig) { | 774 | switch (orig) { |
@@ -822,8 +788,8 @@ loff_t mem_lseek(struct file *file, loff_t offset, int orig) | |||
822 | static int mem_release(struct inode *inode, struct file *file) | 788 | static int mem_release(struct inode *inode, struct file *file) |
823 | { | 789 | { |
824 | struct mm_struct *mm = file->private_data; | 790 | struct mm_struct *mm = file->private_data; |
825 | 791 | if (mm) | |
826 | mmput(mm); | 792 | mmdrop(mm); |
827 | return 0; | 793 | return 0; |
828 | } | 794 | } |
829 | 795 | ||