diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 96bc585..afb72bc 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -3004,7 +3004,7 @@ sysctl_kern_proc_ofiledesc(SYSCTL_HANDLER_ARGS) struct tty *tp; name = (int *)arg1; - error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); + error = pget((pid_t)name[0], PGET_WANTREAD | PGET_LOCK, &p); if (error != 0) return (error); fdp = fdhold(p); @@ -3189,6 +3189,7 @@ sysctl_kern_proc_ofiledesc(SYSCTL_HANDLER_ARGS) } FILEDESC_SUNLOCK(fdp); fddrop(fdp); + sx_sunlock(&p->p_keeplock); free(kif, M_TEMP); return (0); } @@ -3477,6 +3478,7 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen) FILEDESC_SUNLOCK(fdp); fddrop(fdp); fail: + sx_sunlock(&p->p_keeplock); free(efbuf, M_TEMP); return (error); } @@ -3497,7 +3499,7 @@ sysctl_kern_proc_filedesc(SYSCTL_HANDLER_ARGS) name = (int *)arg1; sbuf_new_for_sysctl(&sb, NULL, FILEDESC_SBUF_SIZE, req); - error = pget((pid_t)name[0], PGET_CANDEBUG | PGET_NOTWEXIT, &p); + error = pget((pid_t)name[0], PGET_WANTREAD | PGET_LOCK, &p); if (error != 0) { sbuf_delete(&sb); return (error); diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 489096b..1b7e183 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -370,6 +370,7 @@ do_execve(td, args, mac_p) * that might allow a local user to illicitly obtain elevated * privileges. */ + sx_xlock(&p->p_keeplock); PROC_LOCK(p); KASSERT((p->p_flag & P_INEXEC) == 0, ("%s(): process already has P_INEXEC flag", __func__)); @@ -917,6 +918,8 @@ exec_fail: SDT_PROBE(proc, kernel, , exec__failure, error, 0, 0, 0, 0); done2: + sx_xunlock(&p->p_keeplock); + #ifdef MAC mac_execve_exit(imgp); mac_execve_interpreter_exit(interpvplabel); diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 9efaac2..a5e9448 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -148,6 +148,7 @@ exit1(struct thread *td, int rv) /* * MUST abort all other threads before proceeding past here. */ + sx_xlock(&p->p_keeplock); PROC_LOCK(p); while (p->p_flag & P_HADTHREADS) { /* @@ -206,7 +207,9 @@ exit1(struct thread *td, int rv) * PIOCWAIT in case they aren't listening for S_EXIT stops or * decided to wait again after we told them we are exiting. */ + p->p_flag |= P_WEXIT; + sx_xunlock(&p->p_keeplock); wakeup(&p->p_stype); /* diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 3163622..0391c2b 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -228,6 +228,7 @@ proc_init(void *mem, int size, int flags) bzero(&p->p_mtx, sizeof(struct mtx)); mtx_init(&p->p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK); mtx_init(&p->p_slock, "process slock", NULL, MTX_SPIN | MTX_RECURSE); + sx_init(&p->p_keeplock, "process keeplock"); cv_init(&p->p_pwait, "ppwait"); cv_init(&p->p_dbgwait, "dbgwait"); TAILQ_INIT(&p->p_threads); /* all threads in proc */ @@ -361,7 +362,9 @@ int pget(pid_t pid, int flags, struct proc **pp) { struct proc *p; - int error; + int error, keeplocked; + + keeplocked = 0; sx_slock(&allproc_lock); if (pid <= PID_MAX) { @@ -376,6 +379,7 @@ pget(pid_t pid, int flags, struct proc **pp) sx_sunlock(&allproc_lock); if (p == NULL) return (ESRCH); +checks: if ((flags & PGET_CANSEE) != 0) { error = p_cansee(curthread, p); if (error != 0) @@ -402,14 +406,54 @@ pget(pid_t pid, int flags, struct proc **pp) error = ESRCH; goto errout; } + if ((flags & PGET_KEEP) != 0) { + MPASS((flags & PGET_NOTWEXIT)); + if (keeplocked) { + /* + * We get here after succesfull second run of all + * checks. + */ + if ((flags & PGET_LOCK) == 0) + PROC_UNLOCK(p); + goto out; + } + + if (sx_try_slock(&p->p_keeplock)) { + if ((flags & PGET_LOCK) == 0) + PROC_UNLOCK(p); + goto out; + } else { + /* + * Process was found but, but it is execing. + * We have to perform all checks once more since it + * can change credentials with suid binaries. + * + * We already checked it is not exiting with + * PGET_NOTWEXIT. + * + * Now we have to drop locks before we sx_slock, so + * make sure it wont exit in the meantime. + */ + _PHOLD(p); + PROC_UNLOCK(p); + sx_slock(&p->p_keeplock); + keeplocked = 1; + PROC_LOCK(p); + _PRELE(p); + goto checks; + } + } if ((flags & PGET_HOLD) != 0) { _PHOLD(p); PROC_UNLOCK(p); } +out: *pp = p; return (0); errout: PROC_UNLOCK(p); + if (keeplocked) + sx_sunlock(&p->p_keeplock); return (error); } diff --git a/sys/sys/proc.h b/sys/sys/proc.h index fbd064c..51abe5c 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -511,6 +511,7 @@ struct proc { struct proc *p_pptr; /* (c + e) Pointer to parent process. */ LIST_ENTRY(proc) p_sibling; /* (e) List of sibling processes. */ LIST_HEAD(, proc) p_children; /* (e) Pointer to list of children. */ + struct sx p_keeplock; struct mtx p_mtx; /* (n) Lock for this struct. */ struct ksiginfo *p_ksi; /* Locked by parent proc lock */ sigqueue_t p_sigqueue; /* (c) Sigs not delivered to a td. */ @@ -849,8 +850,10 @@ struct proc *zpfind(pid_t); /* Find zombie process by id. */ #define PGET_NOTWEXIT 0x00010 /* Check that the process is not in P_WEXIT. */ #define PGET_NOTINEXEC 0x00020 /* Check that the process is not in P_INEXEC. */ #define PGET_NOTID 0x00040 /* Do not assume tid if pid > PID_MAX. */ +#define PGET_KEEP 0x00080 +#define PGET_LOCK 0x00100 /* force taking a lock even with PGET_KEEP */ -#define PGET_WANTREAD (PGET_HOLD | PGET_CANDEBUG | PGET_NOTWEXIT) +#define PGET_WANTREAD (PGET_CANDEBUG | PGET_NOTWEXIT | PGET_KEEP) int pget(pid_t pid, int flags, struct proc **pp);