Message ID | 20201120231441.29911-1-ebiederm@xmission.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v2,01/24] exec: Move unshare_files to fix posix file locking during exec | expand |
On Fri, Nov 20, 2020 at 3:16 PM Eric W. Biederman <ebiederm@xmission.com> wrote: > > @@ -1257,6 +1258,13 @@ int begin_new_exec(struct linux_binprm * bprm) > if (retval) > goto out; > > + /* Ensure the files table is not shared. */ > + retval = unshare_files(&displaced); > + if (retval) > + goto out; > + if (displaced) > + put_files_struct(displaced); It's not obvious from the patch (not enough context), but the new placement seems to make much more sense - and it's where we do the de-thread and switch the vm and signals too. So this does seem to be the much more logical place. Ack. Linus
On Fri, Nov 20, 2020 at 05:14:18PM -0600, Eric W. Biederman wrote: > @@ -1805,8 +1808,12 @@ static int bprm_execve(struct linux_binprm *bprm, > bprm->file = file; > /* > * Record that a name derived from an O_CLOEXEC fd will be > - * inaccessible after exec. Relies on having exclusive access to > - * current->files (due to unshare_files above). > + * inaccessible after exec. This allows the code in exec to > + * choose to fail when the executable is not mmaped into the > + * interpreter and an open file descriptor is not passed to > + * the interpreter. This makes for a better user experience > + * than having the interpreter start and then immediately fail > + * when it finds the executable is inaccessible. > */ > if (bprm->fdpath && > close_on_exec(fd, rcu_dereference_raw(current->files->fdt))) We do not have rcu_read_lock() here. What would happen if * we get fdt * another thread does e.g. dup2() with target descriptor greater than the current size * old fdt gets copied and (RCU-delayed) freed * nobody is holding rcu_read_lock(), so it gets really freed * we read a bitmap from the already freed sucker It's a narrow window, but on SMP KVM it's not impossible to hit; if you have preemption enabled, the race window is not so narrow even when running on bare metal. In the mainline it is safe, but only because the damn thing is guaranteed to be _not_ shared with any other thread (which is what the comment had been about). Why not simply say if (bprm->fdpath && get_close_on_exec(fd)) anyway?
On Mon, Dec 07, 2020 at 05:07:55PM -0600, Eric W. Biederman wrote: > My mistake. I missed that the actual code was highly optimized and only > safe in the presence of an unshared files struct. That's a polite way to spell "overoptimized for no good reason" ;-) > What I saw and what I thought the comment was talking about was that the > result of the test isn't guaranteed to be stable without having an > exclusive access to the files. I figured and if something changes later > and it becomes safe to pass the file name to a script later we don't > really care. > > In any event thank you for the review. I will queue up a follow on > patch that makes this use get_close_on_exec. I would rather put that switch to get_close_on_exec() first in the queue (or fold it into this patch)...
diff --git a/fs/exec.c b/fs/exec.c index 547a2390baf5..fa788988efe9 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1238,6 +1238,7 @@ void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec) int begin_new_exec(struct linux_binprm * bprm) { struct task_struct *me = current; + struct files_struct *displaced; int retval; /* Once we are committed compute the creds */ @@ -1257,6 +1258,13 @@ int begin_new_exec(struct linux_binprm * bprm) if (retval) goto out; + /* Ensure the files table is not shared. */ + retval = unshare_files(&displaced); + if (retval) + goto out; + if (displaced) + put_files_struct(displaced); + /* * Must be called _before_ exec_mmap() as bprm->mm is * not visibile until then. This also enables the update @@ -1776,7 +1784,6 @@ static int bprm_execve(struct linux_binprm *bprm, int fd, struct filename *filename, int flags) { struct file *file; - struct files_struct *displaced; int retval; /* @@ -1784,13 +1791,9 @@ static int bprm_execve(struct linux_binprm *bprm, */ io_uring_task_cancel(); - retval = unshare_files(&displaced); - if (retval) - return retval; - retval = prepare_bprm_creds(bprm); if (retval) - goto out_files; + return retval; check_unsafe_exec(bprm); current->in_execve = 1; @@ -1805,8 +1808,12 @@ static int bprm_execve(struct linux_binprm *bprm, bprm->file = file; /* * Record that a name derived from an O_CLOEXEC fd will be - * inaccessible after exec. Relies on having exclusive access to - * current->files (due to unshare_files above). + * inaccessible after exec. This allows the code in exec to + * choose to fail when the executable is not mmaped into the + * interpreter and an open file descriptor is not passed to + * the interpreter. This makes for a better user experience + * than having the interpreter start and then immediately fail + * when it finds the executable is inaccessible. */ if (bprm->fdpath && close_on_exec(fd, rcu_dereference_raw(current->files->fdt))) @@ -1827,8 +1834,6 @@ static int bprm_execve(struct linux_binprm *bprm, rseq_execve(current); acct_update_integrals(current); task_numa_free(current, false); - if (displaced) - put_files_struct(displaced); return retval; out: @@ -1845,10 +1850,6 @@ static int bprm_execve(struct linux_binprm *bprm, current->fs->in_exec = 0; current->in_execve = 0; -out_files: - if (displaced) - reset_files_struct(displaced); - return retval; }