aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorNick Piggin <npiggin@suse.de>2007-05-08 03:25:16 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-08 14:15:00 -0400
commit4fc75ff4816c3483b4b772b2f6cb3d8fd88ca547 (patch)
treef2169f152113af815e69d74ca40c282439e7c3f6 /fs
parentaf7c693f146069a1f44739acef9abf1bc27f7247 (diff)
exec: fix remove_arg_zero
Petr Tesarik discovered a problem in remove_arg_zero(). He writes: When a script is loaded, load_script() replaces argv[0] with the name of the interpreter and the filename passed to the exec syscall. However, there is no guarantee that the length of the interpreter name plus the length of the filename is greater than the length of the original argv[0]. If the difference happens to cross a page boundary, setup_arg_pages() will call put_dirty_page() [aka install_arg_page()] with an address outside the VMA. Therefore, remove_arg_zero() must free all pages which would be unused after the argument is removed. So, rewrite the remove_arg_zero function without gotos, with a few comments, and with the commonly used explicit index/offset. This fixes the problem and makes it easier to understand as well. [a.p.zijlstra@chello.nl: add comment] Signed-off-by: Nick Piggin <npiggin@suse.de> Cc: Petr Tesarik <ptesarik@suse.cz> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/exec.c50
1 files changed, 34 insertions, 16 deletions
diff --git a/fs/exec.c b/fs/exec.c
index 3155e915307a..f1691cd0c9d2 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -982,33 +982,51 @@ void compute_creds(struct linux_binprm *bprm)
982 task_unlock(current); 982 task_unlock(current);
983 security_bprm_post_apply_creds(bprm); 983 security_bprm_post_apply_creds(bprm);
984} 984}
985
986EXPORT_SYMBOL(compute_creds); 985EXPORT_SYMBOL(compute_creds);
987 986
987/*
988 * Arguments are '\0' separated strings found at the location bprm->p
989 * points to; chop off the first by relocating brpm->p to right after
990 * the first '\0' encountered.
991 */
988void remove_arg_zero(struct linux_binprm *bprm) 992void remove_arg_zero(struct linux_binprm *bprm)
989{ 993{
990 if (bprm->argc) { 994 if (bprm->argc) {
991 unsigned long offset; 995 char ch;
992 char * kaddr;
993 struct page *page;
994 996
995 offset = bprm->p % PAGE_SIZE; 997 do {
996 goto inside; 998 unsigned long offset;
999 unsigned long index;
1000 char *kaddr;
1001 struct page *page;
997 1002
998 while (bprm->p++, *(kaddr+offset++)) { 1003 offset = bprm->p & ~PAGE_MASK;
999 if (offset != PAGE_SIZE) 1004 index = bprm->p >> PAGE_SHIFT;
1000 continue; 1005
1001 offset = 0; 1006 page = bprm->page[index];
1002 kunmap_atomic(kaddr, KM_USER0);
1003inside:
1004 page = bprm->page[bprm->p/PAGE_SIZE];
1005 kaddr = kmap_atomic(page, KM_USER0); 1007 kaddr = kmap_atomic(page, KM_USER0);
1006 } 1008
1007 kunmap_atomic(kaddr, KM_USER0); 1009 /* run through page until we reach end or find NUL */
1010 do {
1011 ch = *(kaddr + offset);
1012
1013 /* discard that character... */
1014 bprm->p++;
1015 offset++;
1016 } while (offset < PAGE_SIZE && ch != '\0');
1017
1018 kunmap_atomic(kaddr, KM_USER0);
1019
1020 /* free the old page */
1021 if (offset == PAGE_SIZE) {
1022 __free_page(page);
1023 bprm->page[index] = NULL;
1024 }
1025 } while (ch != '\0');
1026
1008 bprm->argc--; 1027 bprm->argc--;
1009 } 1028 }
1010} 1029}
1011
1012EXPORT_SYMBOL(remove_arg_zero); 1030EXPORT_SYMBOL(remove_arg_zero);
1013 1031
1014/* 1032/*