Commit 4fc75ff4 authored by Nick Piggin's avatar Nick Piggin Committed by Linus Torvalds

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: default avatarNick Piggin <npiggin@suse.de>
Cc: Petr Tesarik <ptesarik@suse.cz>
Signed-off-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent af7c693f
...@@ -982,33 +982,51 @@ void compute_creds(struct linux_binprm *bprm) ...@@ -982,33 +982,51 @@ void compute_creds(struct linux_binprm *bprm)
task_unlock(current); task_unlock(current);
security_bprm_post_apply_creds(bprm); security_bprm_post_apply_creds(bprm);
} }
EXPORT_SYMBOL(compute_creds); EXPORT_SYMBOL(compute_creds);
/*
* Arguments are '\0' separated strings found at the location bprm->p
* points to; chop off the first by relocating brpm->p to right after
* the first '\0' encountered.
*/
void remove_arg_zero(struct linux_binprm *bprm) void remove_arg_zero(struct linux_binprm *bprm)
{ {
if (bprm->argc) { if (bprm->argc) {
char ch;
do {
unsigned long offset; unsigned long offset;
char * kaddr; unsigned long index;
char *kaddr;
struct page *page; struct page *page;
offset = bprm->p % PAGE_SIZE; offset = bprm->p & ~PAGE_MASK;
goto inside; index = bprm->p >> PAGE_SHIFT;
while (bprm->p++, *(kaddr+offset++)) { page = bprm->page[index];
if (offset != PAGE_SIZE)
continue;
offset = 0;
kunmap_atomic(kaddr, KM_USER0);
inside:
page = bprm->page[bprm->p/PAGE_SIZE];
kaddr = kmap_atomic(page, KM_USER0); kaddr = kmap_atomic(page, KM_USER0);
}
/* run through page until we reach end or find NUL */
do {
ch = *(kaddr + offset);
/* discard that character... */
bprm->p++;
offset++;
} while (offset < PAGE_SIZE && ch != '\0');
kunmap_atomic(kaddr, KM_USER0); kunmap_atomic(kaddr, KM_USER0);
/* free the old page */
if (offset == PAGE_SIZE) {
__free_page(page);
bprm->page[index] = NULL;
}
} while (ch != '\0');
bprm->argc--; bprm->argc--;
} }
} }
EXPORT_SYMBOL(remove_arg_zero); EXPORT_SYMBOL(remove_arg_zero);
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment