mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-16 21:35:07 +00:00
[PATCH] sanitize handling of shared descriptor tables in failing execve()
* unshare_files() can fail; doing it after irreversible actions is wrong and de_thread() is certainly irreversible. * since we do it unconditionally anyway, we might as well do it in do_execve() and save ourselves the PITA in binfmt handlers, etc. * while we are at it, binfmt_som actually leaked files_struct on failure. As a side benefit, unshare_files(), put_files_struct() and reset_files_struct() become unexported. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
6b335d9c80
commit
fd8328be87
@ -543,7 +543,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
|
|||||||
unsigned long interp_load_addr = 0;
|
unsigned long interp_load_addr = 0;
|
||||||
unsigned long start_code, end_code, start_data, end_data;
|
unsigned long start_code, end_code, start_data, end_data;
|
||||||
unsigned long reloc_func_desc = 0;
|
unsigned long reloc_func_desc = 0;
|
||||||
struct files_struct *files;
|
|
||||||
int executable_stack = EXSTACK_DEFAULT;
|
int executable_stack = EXSTACK_DEFAULT;
|
||||||
unsigned long def_flags = 0;
|
unsigned long def_flags = 0;
|
||||||
struct {
|
struct {
|
||||||
@ -593,20 +592,9 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
|
|||||||
goto out_free_ph;
|
goto out_free_ph;
|
||||||
}
|
}
|
||||||
|
|
||||||
files = current->files; /* Refcounted so ok */
|
|
||||||
retval = unshare_files();
|
|
||||||
if (retval < 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();
|
retval = get_unused_fd();
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
goto out_free_fh;
|
goto out_free_ph;
|
||||||
get_file(bprm->file);
|
get_file(bprm->file);
|
||||||
fd_install(elf_exec_fileno = retval, bprm->file);
|
fd_install(elf_exec_fileno = retval, bprm->file);
|
||||||
|
|
||||||
@ -728,12 +716,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
|
|||||||
if (retval)
|
if (retval)
|
||||||
goto out_free_dentry;
|
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 */
|
/* OK, This is the point of no return */
|
||||||
current->flags &= ~PF_FORKNOEXEC;
|
current->flags &= ~PF_FORKNOEXEC;
|
||||||
current->mm->def_flags = def_flags;
|
current->mm->def_flags = def_flags;
|
||||||
@ -1016,9 +998,6 @@ out_free_interp:
|
|||||||
kfree(elf_interpreter);
|
kfree(elf_interpreter);
|
||||||
out_free_file:
|
out_free_file:
|
||||||
sys_close(elf_exec_fileno);
|
sys_close(elf_exec_fileno);
|
||||||
out_free_fh:
|
|
||||||
if (files)
|
|
||||||
reset_files_struct(current, files);
|
|
||||||
out_free_ph:
|
out_free_ph:
|
||||||
kfree(elf_phdata);
|
kfree(elf_phdata);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -110,7 +110,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
|
|||||||
char *iname_addr = iname;
|
char *iname_addr = iname;
|
||||||
int retval;
|
int retval;
|
||||||
int fd_binary = -1;
|
int fd_binary = -1;
|
||||||
struct files_struct *files = NULL;
|
|
||||||
|
|
||||||
retval = -ENOEXEC;
|
retval = -ENOEXEC;
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
@ -133,21 +132,13 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
|
|||||||
|
|
||||||
if (fmt->flags & MISC_FMT_OPEN_BINARY) {
|
if (fmt->flags & MISC_FMT_OPEN_BINARY) {
|
||||||
|
|
||||||
files = current->files;
|
|
||||||
retval = unshare_files();
|
|
||||||
if (retval < 0)
|
|
||||||
goto _ret;
|
|
||||||
if (files == current->files) {
|
|
||||||
put_files_struct(files);
|
|
||||||
files = NULL;
|
|
||||||
}
|
|
||||||
/* if the binary should be opened on behalf of the
|
/* if the binary should be opened on behalf of the
|
||||||
* interpreter than keep it open and assign descriptor
|
* interpreter than keep it open and assign descriptor
|
||||||
* to it */
|
* to it */
|
||||||
fd_binary = get_unused_fd();
|
fd_binary = get_unused_fd();
|
||||||
if (fd_binary < 0) {
|
if (fd_binary < 0) {
|
||||||
retval = fd_binary;
|
retval = fd_binary;
|
||||||
goto _unshare;
|
goto _ret;
|
||||||
}
|
}
|
||||||
fd_install(fd_binary, bprm->file);
|
fd_install(fd_binary, bprm->file);
|
||||||
|
|
||||||
@ -205,10 +196,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
|
|||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
goto _error;
|
goto _error;
|
||||||
|
|
||||||
if (files) {
|
|
||||||
put_files_struct(files);
|
|
||||||
files = NULL;
|
|
||||||
}
|
|
||||||
_ret:
|
_ret:
|
||||||
return retval;
|
return retval;
|
||||||
_error:
|
_error:
|
||||||
@ -216,9 +203,6 @@ _error:
|
|||||||
sys_close(fd_binary);
|
sys_close(fd_binary);
|
||||||
bprm->interp_flags = 0;
|
bprm->interp_flags = 0;
|
||||||
bprm->interp_data = 0;
|
bprm->interp_data = 0;
|
||||||
_unshare:
|
|
||||||
if (files)
|
|
||||||
reset_files_struct(current, files);
|
|
||||||
goto _ret;
|
goto _ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +194,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
|
|||||||
unsigned long som_entry;
|
unsigned long som_entry;
|
||||||
struct som_hdr *som_ex;
|
struct som_hdr *som_ex;
|
||||||
struct som_exec_auxhdr *hpuxhdr;
|
struct som_exec_auxhdr *hpuxhdr;
|
||||||
struct files_struct *files;
|
|
||||||
|
|
||||||
/* Get the exec-header */
|
/* Get the exec-header */
|
||||||
som_ex = (struct som_hdr *) bprm->buf;
|
som_ex = (struct som_hdr *) bprm->buf;
|
||||||
@ -221,15 +220,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
|
|||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
files = current->files; /* Refcounted so ok */
|
|
||||||
retval = unshare_files();
|
|
||||||
if (retval < 0)
|
|
||||||
goto out_free;
|
|
||||||
if (files == current->files) {
|
|
||||||
put_files_struct(files);
|
|
||||||
files = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
retval = get_unused_fd();
|
retval = get_unused_fd();
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
34
fs/exec.c
34
fs/exec.c
@ -953,7 +953,6 @@ int flush_old_exec(struct linux_binprm * bprm)
|
|||||||
{
|
{
|
||||||
char * name;
|
char * name;
|
||||||
int i, ch, retval;
|
int i, ch, retval;
|
||||||
struct files_struct *files;
|
|
||||||
char tcomm[sizeof(current->comm)];
|
char tcomm[sizeof(current->comm)];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -964,27 +963,16 @@ int flush_old_exec(struct linux_binprm * bprm)
|
|||||||
if (retval)
|
if (retval)
|
||||||
goto out;
|
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
|
* Release all of the old mmap stuff
|
||||||
*/
|
*/
|
||||||
retval = exec_mmap(bprm->mm);
|
retval = exec_mmap(bprm->mm);
|
||||||
if (retval)
|
if (retval)
|
||||||
goto mmap_failed;
|
goto out;
|
||||||
|
|
||||||
bprm->mm = NULL; /* We're using it now */
|
bprm->mm = NULL; /* We're using it now */
|
||||||
|
|
||||||
/* This is the point of no return */
|
/* This is the point of no return */
|
||||||
put_files_struct(files);
|
|
||||||
|
|
||||||
current->sas_ss_sp = current->sas_ss_size = 0;
|
current->sas_ss_sp = current->sas_ss_size = 0;
|
||||||
|
|
||||||
if (current->euid == current->uid && current->egid == current->gid)
|
if (current->euid == current->uid && current->egid == current->gid)
|
||||||
@ -1034,8 +1022,6 @@ int flush_old_exec(struct linux_binprm * bprm)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
mmap_failed:
|
|
||||||
reset_files_struct(current, files);
|
|
||||||
out:
|
out:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@ -1283,12 +1269,23 @@ int do_execve(char * filename,
|
|||||||
struct linux_binprm *bprm;
|
struct linux_binprm *bprm;
|
||||||
struct file *file;
|
struct file *file;
|
||||||
unsigned long env_p;
|
unsigned long env_p;
|
||||||
|
struct files_struct *files;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
files = current->files;
|
||||||
|
retval = unshare_files();
|
||||||
|
if (retval)
|
||||||
|
goto out_ret;
|
||||||
|
|
||||||
|
if (files == current->files) {
|
||||||
|
put_files_struct(files);
|
||||||
|
files = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
retval = -ENOMEM;
|
retval = -ENOMEM;
|
||||||
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
|
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
|
||||||
if (!bprm)
|
if (!bprm)
|
||||||
goto out_ret;
|
goto out_files;
|
||||||
|
|
||||||
file = open_exec(filename);
|
file = open_exec(filename);
|
||||||
retval = PTR_ERR(file);
|
retval = PTR_ERR(file);
|
||||||
@ -1343,6 +1340,8 @@ int do_execve(char * filename,
|
|||||||
security_bprm_free(bprm);
|
security_bprm_free(bprm);
|
||||||
acct_update_integrals(current);
|
acct_update_integrals(current);
|
||||||
kfree(bprm);
|
kfree(bprm);
|
||||||
|
if (files)
|
||||||
|
put_files_struct(files);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1363,6 +1362,9 @@ out_file:
|
|||||||
out_kfree:
|
out_kfree:
|
||||||
kfree(bprm);
|
kfree(bprm);
|
||||||
|
|
||||||
|
out_files:
|
||||||
|
if (files)
|
||||||
|
reset_files_struct(current, files);
|
||||||
out_ret:
|
out_ret:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -507,8 +507,6 @@ void put_files_struct(struct files_struct *files)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(put_files_struct);
|
|
||||||
|
|
||||||
void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
|
void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
|
||||||
{
|
{
|
||||||
struct files_struct *old;
|
struct files_struct *old;
|
||||||
@ -519,7 +517,6 @@ void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
|
|||||||
task_unlock(tsk);
|
task_unlock(tsk);
|
||||||
put_files_struct(old);
|
put_files_struct(old);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(reset_files_struct);
|
|
||||||
|
|
||||||
void exit_files(struct task_struct *tsk)
|
void exit_files(struct task_struct *tsk)
|
||||||
{
|
{
|
||||||
|
@ -870,8 +870,6 @@ int unshare_files(void)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(unshare_files);
|
|
||||||
|
|
||||||
static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk)
|
static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk)
|
||||||
{
|
{
|
||||||
struct sighand_struct *sig;
|
struct sighand_struct *sig;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user