From: Chris Wright Use unshare_files during binary loading to eliminate potential leak of the binary's fd installed during execve(). As is, this breaks binfmt_som.c 25-akpm/fs/binfmt_elf.c | 37 +++++++++++++++++++++++++++++-------- 25-akpm/fs/binfmt_som.c | 2 +- 25-akpm/fs/exec.c | 16 +++++++++++++++- 3 files changed, 45 insertions(+), 10 deletions(-) diff -puN fs/binfmt_elf.c~use-unshare_files fs/binfmt_elf.c --- 25/fs/binfmt_elf.c~use-unshare_files Thu Dec 18 13:57:07 2003 +++ 25-akpm/fs/binfmt_elf.c Thu Dec 18 13:57:07 2003 @@ -466,6 +466,7 @@ static int load_elf_binary(struct linux_ struct elfhdr interp_elf_ex; struct exec interp_ex; char passed_fileno[6]; + struct files_struct *files; /* Get the exec-header */ elf_ex = *((struct elfhdr *) bprm->buf); @@ -498,9 +499,20 @@ static int load_elf_binary(struct linux_ if (retval < 0) goto out_free_ph; + files = current->files; /* Refcounted so ok */ + if(unshare_files() < 0) + goto out_free_ph; + if (files == current->files) { + put_files_struct(files); + files = NULL; + } + + /* exec will make our files private anyway, but for the a.out + loader stuff we need to do it earlier */ + retval = get_unused_fd(); if (retval < 0) - goto out_free_ph; + goto out_free_fh; get_file(bprm->file); fd_install(elf_exec_fileno = retval, bprm->file); @@ -631,6 +643,12 @@ static int load_elf_binary(struct linux_ if (retval) goto out_free_dentry; + /* Discard our unneeded old files struct */ + if (files) { + put_files_struct(files); + files = NULL; + } + /* OK, This is the point of no return */ current->mm->start_data = 0; current->mm->end_data = 0; @@ -745,19 +763,17 @@ static int load_elf_binary(struct linux_ elf_entry = load_elf_interp(&interp_elf_ex, interpreter, &interp_load_addr); - - allow_write_access(interpreter); - fput(interpreter); - kfree(elf_interpreter); - if (BAD_ADDR(elf_entry)) { printk(KERN_ERR "Unable to load interpreter\n"); - kfree(elf_phdata); send_sig(SIGSEGV, current, 0); retval = -ENOEXEC; /* Nobody gets to see this, but.. */ - goto out; + goto out_free_dentry; } reloc_func_desc = interp_load_addr; + + allow_write_access(interpreter); + fput(interpreter); + kfree(elf_interpreter); } else { elf_entry = elf_ex.e_entry; } @@ -835,6 +851,11 @@ out_free_interp: kfree(elf_interpreter); out_free_file: sys_close(elf_exec_fileno); +out_free_fh: + if (files) { + put_files_struct(current->files); + current->files = files; + } out_free_ph: kfree(elf_phdata); goto out; diff -puN fs/binfmt_som.c~use-unshare_files fs/binfmt_som.c --- 25/fs/binfmt_som.c~use-unshare_files Thu Dec 18 13:57:07 2003 +++ 25-akpm/fs/binfmt_som.c Thu Dec 18 13:57:07 2003 @@ -217,7 +217,7 @@ load_som_binary(struct linux_binprm * bp (char *) hpuxhdr, size); if (retval < 0) goto out_free; - +#error "Fix security hole before enabling me" retval = get_unused_fd(); if (retval < 0) goto out_free; diff -puN fs/exec.c~use-unshare_files fs/exec.c --- 25/fs/exec.c~use-unshare_files Thu Dec 18 13:57:07 2003 +++ 25-akpm/fs/exec.c Thu Dec 18 13:57:07 2003 @@ -779,6 +779,7 @@ int flush_old_exec(struct linux_binprm * { char * name; int i, ch, retval; + struct files_struct *files; /* * Make sure we have a private signal table and that @@ -789,15 +790,25 @@ int flush_old_exec(struct linux_binprm * goto out; /* + * Make sure we have private file handles. Ask the + * fork helper to do the work for us and the exit + * helper to do the cleanup of the old one. + */ + files = current->files; /* refcounted so safe to hold */ + retval = unshare_files(); + if (retval) + goto out; + /* * Release all of the old mmap stuff */ retval = exec_mmap(bprm->mm); if (retval) - goto out; + goto mmap_failed; bprm->mm = NULL; /* We're using it now */ /* This is the point of no return */ + put_files_struct(files); current->sas_ss_sp = current->sas_ss_size = 0; @@ -830,6 +841,9 @@ int flush_old_exec(struct linux_binprm * return 0; +mmap_failed: + put_files_struct(current->files); + current->files = files; out: return retval; } _