From: Chris Wright <chrisw@osdl.org>

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;
 }

_