diff options
author | H. Peter Anvin <hpa@zytor.com> | 2010-02-18 19:13:40 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-02-21 11:57:08 -0500 |
commit | 87b8d1adefa1548b591cbf0d63965987e2cf893d (patch) | |
tree | 9bb71e42dfed2ac212a9dc55dd5e2ba1f8d042ac | |
parent | aea187c46f7d03ce985e55eb1398d0776a15b928 (diff) |
mm: Make copy_from_user() in migrate.c statically predictable
x86-32 has had a static test for copy_on_user() overflow for a while.
This test currently fails in mm/migrate.c resulting in an
allyesconfig/allmodconfig build failure on x86-32:
In function ‘copy_from_user’,
inlined from ‘do_pages_stat’ at
/home/hpa/kernel/git/mm/migrate.c:1012:
/home/hpa/kernel/git/arch/x86/include/asm/uaccess_32.h:212: error:
call to ‘copy_from_user_overflow’ declared
Make the logic more explicit and therefore easier for gcc to
understand.
v2: rewrite the loop entirely using a more normal structure for a
chunked-data loop (Linus Torvalds)
Reported-by: Len Brown <lenb@kernel.org>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Reviewed-and-Tested-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Arjan van de Ven <arjan@linux.kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: Hugh Dickins <hugh.dickins@tiscali.co.uk>
Cc: Rik van Riel <riel@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | mm/migrate.c | 36 |
1 files changed, 15 insertions, 21 deletions
diff --git a/mm/migrate.c b/mm/migrate.c index 9a0db5bbabe4..880bd592d38e 100644 --- a/mm/migrate.c +++ b/mm/migrate.c | |||
@@ -1002,33 +1002,27 @@ static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages, | |||
1002 | #define DO_PAGES_STAT_CHUNK_NR 16 | 1002 | #define DO_PAGES_STAT_CHUNK_NR 16 |
1003 | const void __user *chunk_pages[DO_PAGES_STAT_CHUNK_NR]; | 1003 | const void __user *chunk_pages[DO_PAGES_STAT_CHUNK_NR]; |
1004 | int chunk_status[DO_PAGES_STAT_CHUNK_NR]; | 1004 | int chunk_status[DO_PAGES_STAT_CHUNK_NR]; |
1005 | unsigned long i, chunk_nr = DO_PAGES_STAT_CHUNK_NR; | ||
1006 | int err; | ||
1007 | 1005 | ||
1008 | for (i = 0; i < nr_pages; i += chunk_nr) { | 1006 | while (nr_pages) { |
1009 | if (chunk_nr > nr_pages - i) | 1007 | unsigned long chunk_nr; |
1010 | chunk_nr = nr_pages - i; | ||
1011 | 1008 | ||
1012 | err = copy_from_user(chunk_pages, &pages[i], | 1009 | chunk_nr = nr_pages; |
1013 | chunk_nr * sizeof(*chunk_pages)); | 1010 | if (chunk_nr > DO_PAGES_STAT_CHUNK_NR) |
1014 | if (err) { | 1011 | chunk_nr = DO_PAGES_STAT_CHUNK_NR; |
1015 | err = -EFAULT; | 1012 | |
1016 | goto out; | 1013 | if (copy_from_user(chunk_pages, pages, chunk_nr * sizeof(*chunk_pages))) |
1017 | } | 1014 | break; |
1018 | 1015 | ||
1019 | do_pages_stat_array(mm, chunk_nr, chunk_pages, chunk_status); | 1016 | do_pages_stat_array(mm, chunk_nr, chunk_pages, chunk_status); |
1020 | 1017 | ||
1021 | err = copy_to_user(&status[i], chunk_status, | 1018 | if (copy_to_user(status, chunk_status, chunk_nr * sizeof(*status))) |
1022 | chunk_nr * sizeof(*chunk_status)); | 1019 | break; |
1023 | if (err) { | ||
1024 | err = -EFAULT; | ||
1025 | goto out; | ||
1026 | } | ||
1027 | } | ||
1028 | err = 0; | ||
1029 | 1020 | ||
1030 | out: | 1021 | pages += chunk_nr; |
1031 | return err; | 1022 | status += chunk_nr; |
1023 | nr_pages -= chunk_nr; | ||
1024 | } | ||
1025 | return nr_pages ? -EFAULT : 0; | ||
1032 | } | 1026 | } |
1033 | 1027 | ||
1034 | /* | 1028 | /* |