Commit 3b125388 authored by Al Viro's avatar Al Viro

[PATCH] sanitize unshare_files/reset_files_struct

* let unshare_files() give caller the displaced files_struct
* don't bother with grabbing reference only to drop it in the
  caller if it hadn't been shared in the first place
* in that form unshare_files() is trivially implemented via
  unshare_fd(), so we eliminate the duplicate logics in fork.c
* reset_files_struct() is not just only called for current;
  it will break the system if somebody ever calls it for anything
  else (we can't modify ->files of somebody else).  Lose the
  task_struct * argument.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent fd8328be
...@@ -1269,19 +1269,13 @@ int do_execve(char * filename, ...@@ -1269,19 +1269,13 @@ 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; struct files_struct *displaced;
int retval; int retval;
files = current->files; retval = unshare_files(&displaced);
retval = unshare_files();
if (retval) if (retval)
goto out_ret; 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)
...@@ -1340,8 +1334,8 @@ int do_execve(char * filename, ...@@ -1340,8 +1334,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) if (displaced)
put_files_struct(files); put_files_struct(displaced);
return retval; return retval;
} }
...@@ -1363,8 +1357,8 @@ out_kfree: ...@@ -1363,8 +1357,8 @@ out_kfree:
kfree(bprm); kfree(bprm);
out_files: out_files:
if (files) if (displaced)
reset_files_struct(current, files); reset_files_struct(displaced);
out_ret: out_ret:
return retval; return retval;
} }
......
...@@ -117,7 +117,8 @@ struct task_struct; ...@@ -117,7 +117,8 @@ struct task_struct;
struct files_struct *get_files_struct(struct task_struct *); struct files_struct *get_files_struct(struct task_struct *);
void put_files_struct(struct files_struct *fs); void put_files_struct(struct files_struct *fs);
void reset_files_struct(struct task_struct *, struct files_struct *); void reset_files_struct(struct files_struct *);
int unshare_files(struct files_struct **);
extern struct kmem_cache *files_cachep; extern struct kmem_cache *files_cachep;
......
...@@ -2033,9 +2033,6 @@ static inline ino_t parent_ino(struct dentry *dentry) ...@@ -2033,9 +2033,6 @@ static inline ino_t parent_ino(struct dentry *dentry)
return res; return res;
} }
/* kernel/fork.c */
extern int unshare_files(void);
/* Transaction based IO helpers */ /* Transaction based IO helpers */
/* /*
......
...@@ -507,8 +507,9 @@ void put_files_struct(struct files_struct *files) ...@@ -507,8 +507,9 @@ void put_files_struct(struct files_struct *files)
} }
} }
void reset_files_struct(struct task_struct *tsk, struct files_struct *files) void reset_files_struct(struct files_struct *files)
{ {
struct task_struct *tsk = current;
struct files_struct *old; struct files_struct *old;
old = tsk->files; old = tsk->files;
......
...@@ -840,36 +840,6 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk) ...@@ -840,36 +840,6 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk)
return 0; return 0;
} }
/*
* Helper to unshare the files of the current task.
* We don't want to expose copy_files internals to
* the exec layer of the kernel.
*/
int unshare_files(void)
{
struct files_struct *files = current->files;
struct files_struct *newf;
int error = 0;
BUG_ON(!files);
/* This can race but the race causes us to copy when we don't
need to and drop the copy */
if(atomic_read(&files->count) == 1)
{
atomic_inc(&files->count);
return 0;
}
newf = dup_fd(files, &error);
if (newf) {
task_lock(current);
current->files = newf;
task_unlock(current);
}
return error;
}
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;
...@@ -1807,3 +1777,27 @@ bad_unshare_cleanup_thread: ...@@ -1807,3 +1777,27 @@ bad_unshare_cleanup_thread:
bad_unshare_out: bad_unshare_out:
return err; return err;
} }
/*
* Helper to unshare the files of the current task.
* We don't want to expose copy_files internals to
* the exec layer of the kernel.
*/
int unshare_files(struct files_struct **displaced)
{
struct task_struct *task = current;
struct files_struct *copy;
int error;
error = unshare_fd(CLONE_FILES, &copy);
if (error || !copy) {
*displaced = NULL;
return error;
}
*displaced = task->files;
task_lock(task);
task->files = copy;
task_unlock(task);
return 0;
}
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