If load_elf_binary() (and the other binary handlers) fail after flush_old_exec() (for example, in setup_arg_pages()) then do_execve() will go through and do mmdrop(bprm.mm). But bprm.mm is now current->mm. We've just freed the current process's mm. The kernel dies in a most ghastly manner. Fix that up by nulling out bprm.mm in flush_old_exec(), at the point where we consumed the mm. Handle the null pointer in the do_execve() error path. Also: don't open-code free_arg_pages() in do_execve(): call it instead. fs/exec.c | 20 +++++++++----------- 1 files changed, 9 insertions(+), 11 deletions(-) diff -puN fs/exec.c~double-mmdrop-fix fs/exec.c --- 25/fs/exec.c~double-mmdrop-fix 2003-07-01 01:45:51.000000000 -0700 +++ 25-akpm/fs/exec.c 2003-07-01 01:45:51.000000000 -0700 @@ -441,9 +441,9 @@ static inline void free_arg_pages(struct { int i; - for (i = 0 ; i < MAX_ARG_PAGES ; i++) { + for (i = 0; i < MAX_ARG_PAGES; i++) { if (bprm->page[i]) - __free_page(bprm->page[i]); + __free_page(bprm->page[i]); bprm->page[i] = NULL; } } @@ -773,6 +773,8 @@ int flush_old_exec(struct linux_binprm * if (retval) goto out; + bprm->mm = NULL; /* We're using it now */ + /* This is the point of no return */ current->sas_ss_sp = current->sas_ss_size = 0; @@ -1000,7 +1002,7 @@ int search_binary_handler(struct linux_b } read_lock(&binfmt_lock); put_binfmt(fmt); - if (retval != -ENOEXEC) + if (retval != -ENOEXEC || bprm->mm == NULL) break; if (!bprm->file) { read_unlock(&binfmt_lock); @@ -1008,7 +1010,7 @@ int search_binary_handler(struct linux_b } } read_unlock(&binfmt_lock); - if (retval != -ENOEXEC) { + if (retval != -ENOEXEC || bprm->mm == NULL) { break; #ifdef CONFIG_KMOD }else{ @@ -1036,7 +1038,6 @@ int do_execve(char * filename, struct linux_binprm bprm; struct file *file; int retval; - int i; sched_balance_exec(); @@ -1104,17 +1105,14 @@ int do_execve(char * filename, out: /* Something went wrong, return the inode and free the argument pages*/ - for (i = 0 ; i < MAX_ARG_PAGES ; i++) { - struct page * page = bprm.page[i]; - if (page) - __free_page(page); - } + free_arg_pages(&bprm); if (bprm.security) security_bprm_free(&bprm); out_mm: - mmdrop(bprm.mm); + if (bprm.mm) + mmdrop(bprm.mm); out_file: if (bprm.file) { _