diff options
author | Philip Guenther <guenther@cvs.openbsd.org> | 2014-06-17 03:49:04 +0000 |
---|---|---|
committer | Philip Guenther <guenther@cvs.openbsd.org> | 2014-06-17 03:49:04 +0000 |
commit | 1939433f4a45f8852b2ed3e9694479f3627eca17 (patch) | |
tree | 883d539bffbd12ea096e3fcc92f8ca356097c6aa | |
parent | 6a6a5d174372ea769ca2688be5ce9fb41970ecd6 (diff) |
Revise previous: if a user calls STRIOCGETCWD two or more times in a row
without an intervening STRIOCRESCWD, release the current values instead
of the saved values, so as to effectively emulate a STRIOCRESCWD.
While here, make sure the fdc->fd_[cr]dir members are updated before
vrele()ing their old values, so that they can't be caught as dangling refs.
ok matthew@
-rw-r--r-- | sys/dev/systrace.c | 80 |
1 files changed, 51 insertions, 29 deletions
diff --git a/sys/dev/systrace.c b/sys/dev/systrace.c index a61caabe9b1..ab9c0e6e0ae 100644 --- a/sys/dev/systrace.c +++ b/sys/dev/systrace.c @@ -1,4 +1,4 @@ -/* $OpenBSD: systrace.c,v 1.68 2014/06/15 20:22:12 matthew Exp $ */ +/* $OpenBSD: systrace.c,v 1.69 2014/06/17 03:49:03 guenther Exp $ */ /* * Copyright 2002 Niels Provos <provos@citi.umich.edu> * All rights reserved. @@ -155,6 +155,7 @@ int systrace_policy(struct fsystrace *, struct systrace_policy *); int systrace_preprepl(struct str_process *, struct systrace_replace *); int systrace_replace(struct str_process *, size_t, register_t []); int systrace_getcwd(struct fsystrace *, struct str_process *, int); +int systrace_restorecwd(struct fsystrace *, struct proc *); int systrace_fname(struct str_process *, caddr_t, size_t); void systrace_replacefree(struct str_process *); @@ -260,7 +261,6 @@ systracef_ioctl(struct file *fp, u_long cmd, caddr_t data, struct proc *p) { int ret = 0; struct fsystrace *fst = (struct fsystrace *)fp->f_data; - struct filedesc *fdp; struct str_process *strp; pid_t pid = 0; int atfd = -1; @@ -367,23 +367,7 @@ systracef_ioctl(struct file *fp, u_long cmd, caddr_t data, struct proc *p) ret = systrace_preprepl(strp, (struct systrace_replace *)data); break; case STRIOCRESCWD: - if (!fst->fd_pid) { - ret = EINVAL; - break; - } - fdp = p->p_fd; - - /* Release cwd from other process */ - if (fdp->fd_cdir) - vrele(fdp->fd_cdir); - if (fdp->fd_rdir) - vrele(fdp->fd_rdir); - /* This restores the cwd we had before */ - fdp->fd_cdir = fst->fd_cdir; - fdp->fd_rdir = fst->fd_rdir; - /* Note that we are normal again */ - fst->fd_pid = 0; - fst->fd_cdir = fst->fd_rdir = NULL; + ret = systrace_restorecwd(fst, p); break; case STRIOCGETCWD: ret = systrace_getcwd(fst, strp, atfd); @@ -1076,7 +1060,7 @@ int systrace_getcwd(struct fsystrace *fst, struct str_process *strp, int atfd) { struct filedesc *myfdp, *fdp; - struct vnode *dvp; + struct vnode *dvp, *odvp; int error; DPRINTF(("%s: %d\n", __func__, strp->pid)); @@ -1101,16 +1085,23 @@ systrace_getcwd(struct fsystrace *fst, struct str_process *strp, int atfd) return (EINVAL); } - /* Release any saved vnodes. */ - if (fst->fd_cdir != NULL) - vrele(fst->fd_cdir); - if (fst->fd_rdir != NULL) - vrele(fst->fd_rdir); - - /* Store our current values */ + /* Is there a STRIOCGETCWD currently in effect? */ + if (fst->fd_pid == 0) { + /* nope: just save the current values */ + fst->fd_cdir = myfdp->fd_cdir; + fst->fd_rdir = myfdp->fd_rdir; + } else { + /* yep: carefully release the current values */ + odvp = myfdp->fd_rdir; + myfdp->fd_rdir = fst->fd_rdir; + if (odvp != NULL) + vrele(odvp); + odvp = myfdp->fd_cdir; + myfdp->fd_cdir = fst->fd_cdir; + if (odvp != NULL) + vrele(odvp); + } fst->fd_pid = strp->pid; - fst->fd_cdir = myfdp->fd_cdir; - fst->fd_rdir = myfdp->fd_rdir; if ((myfdp->fd_cdir = dvp) != NULL) vref(myfdp->fd_cdir); @@ -1121,6 +1112,37 @@ systrace_getcwd(struct fsystrace *fst, struct str_process *strp, int atfd) } int +systrace_restorecwd(struct fsystrace *fst, struct proc *p) +{ + struct filedesc *fdp; + struct vnode *rvp, *cvp; + + if (!fst->fd_pid) + return (EINVAL); + + fdp = p->p_fd; + + /* + * Restore original root and current directories and release the + * ones from the other process. + */ + rvp = fdp->fd_rdir; + cvp = fdp->fd_cdir; + fdp->fd_rdir = fst->fd_rdir; + fdp->fd_cdir = fst->fd_cdir; + fst->fd_cdir = fst->fd_rdir = NULL; + if (rvp != NULL) + vrele(rvp); + if (cvp != NULL) + vrele(cvp); + + /* Note that we are normal again */ + fst->fd_pid = 0; + + return (0); +} + +int systrace_io(struct str_process *strp, struct systrace_io *io) { struct proc *p = curproc, *t = strp->proc; |