aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc
diff options
context:
space:
mode:
authorDjalal Harouni <tixxdz@opendz.org>2012-07-30 17:42:26 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-07-30 20:25:20 -0400
commite8905ec27e2f4ea1b9f7e03df68a060b3ae6fca8 (patch)
tree9c9cdb2e443338be6e46c61469bd12833bce7853 /fs/proc
parent108ceeb020bb3558fe175a3fc8b60fd6c1a2a279 (diff)
proc: environ_read() make sure offset points to environment address range
Currently the following offset and environment address range check in environ_read() of /proc/<pid>/environ is buggy: int this_len = mm->env_end - (mm->env_start + src); if (this_len <= 0) break; Large or negative offsets on /proc/<pid>/environ converted to 'unsigned long' may pass this check since '(mm->env_start + src)' can overflow and 'this_len' will be positive. This can turn /proc/<pid>/environ to act like /proc/<pid>/mem since (mm->env_start + src) will point and read from another VMA. There are two fixes here plus some code cleaning: 1) Fix the overflow by checking if the offset that was converted to unsigned long will always point to the [mm->env_start, mm->env_end] address range. 2) Remove the truncation that was made to the result of the check, storing the result in 'int this_len' will alter its value and we can not depend on it. For kernels that have commit b409e578d ("proc: clean up /proc/<pid>/environ handling") which adds the appropriate ptrace check and saves the 'mm' at ->open() time, this is not a security issue. This patch is taken from the grsecurity patch since it was just made available. Signed-off-by: Djalal Harouni <tixxdz@opendz.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Brad Spengler <spender@grsecurity.net> Acked-by: Kees Cook <keescook@chromium.org> Cc: David Rientjes <rientjes@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc')
-rw-r--r--fs/proc/base.c13
1 files changed, 7 insertions, 6 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 2772208338f8..39ee093b5e96 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -827,15 +827,16 @@ static ssize_t environ_read(struct file *file, char __user *buf,
827 if (!atomic_inc_not_zero(&mm->mm_users)) 827 if (!atomic_inc_not_zero(&mm->mm_users))
828 goto free; 828 goto free;
829 while (count > 0) { 829 while (count > 0) {
830 int this_len, retval, max_len; 830 size_t this_len, max_len;
831 int retval;
831 832
832 this_len = mm->env_end - (mm->env_start + src); 833 if (src >= (mm->env_end - mm->env_start))
833
834 if (this_len <= 0)
835 break; 834 break;
836 835
837 max_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; 836 this_len = mm->env_end - (mm->env_start + src);
838 this_len = (this_len > max_len) ? max_len : this_len; 837
838 max_len = min_t(size_t, PAGE_SIZE, count);
839 this_len = min(max_len, this_len);
839 840
840 retval = access_remote_vm(mm, (mm->env_start + src), 841 retval = access_remote_vm(mm, (mm->env_start + src),
841 page, this_len, 0); 842 page, this_len, 0);