diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 6342119..36240c8 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -1794,6 +1794,7 @@ note_procstat_files(void *arg, struct sbuf *sb, size_t *sizep) sbuf_set_drain(sb, sbuf_drain_count, &size); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); + _PROC_IMAGELOCK(p); kern_proc_filedesc_out(p, sb, -1); sbuf_finish(sb); sbuf_delete(sb); @@ -1802,6 +1803,7 @@ note_procstat_files(void *arg, struct sbuf *sb, size_t *sizep) structsize = sizeof(struct kinfo_file); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); + _PROC_IMAGELOCK(p); kern_proc_filedesc_out(p, sb, -1); } } diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index d860256..c9ab225 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -3023,7 +3023,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_LOCK, &p); if (error != 0) return (error); fdp = fdhold(p); @@ -3208,6 +3208,7 @@ sysctl_kern_proc_ofiledesc(SYSCTL_HANDLER_ARGS) } FILEDESC_SUNLOCK(fdp); fddrop(fdp); + PROC_IMAGEUNLOCK(p); free(kif, M_TEMP); return (0); } @@ -3496,6 +3497,7 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen) FILEDESC_SUNLOCK(fdp); fddrop(fdp); fail: + PROC_IMAGEUNLOCK(p); free(efbuf, M_TEMP); return (error); } @@ -3516,7 +3518,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_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..b699464 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -374,6 +374,7 @@ do_execve(td, args, mac_p) KASSERT((p->p_flag & P_INEXEC) == 0, ("%s(): process already has P_INEXEC flag", __func__)); p->p_flag |= P_INEXEC; + proc_wait_imagelocked(p); PROC_UNLOCK(p); /* diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 9efaac2..5b4b097 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -215,6 +215,7 @@ exit1(struct thread *td, int rv) */ while (p->p_lock > 0) msleep(&p->p_lock, &p->p_mtx, PWAIT, "exithold", 0); + proc_wait_imagelocked(p); p->p_xstat = rv; /* Let event handler change exit status */ PROC_UNLOCK(p); diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 3163622..45536c2 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -259,6 +259,14 @@ proc_fini(void *mem, int size) #endif } +void +proc_wait_imagelocked(struct proc *p) +{ + + while (p->p_imagelock > 0) + msleep(&p->p_imagelock, &p->p_mtx, PWAIT, "imagelock", 0); +} + /* * Is p an inferior of the current process? */ @@ -402,7 +410,12 @@ pget(pid_t pid, int flags, struct proc **pp) error = ESRCH; goto errout; } + if ((flags & PGET_IMAGELOCK) != 0) { + MPASS((flags & PGET_NOTWEXIT) != 0); + _PROC_IMAGELOCK(p); + } if ((flags & PGET_HOLD) != 0) { + MPASS((flags & PGET_IMAGELOCK) == 0); _PHOLD(p); PROC_UNLOCK(p); } diff --git a/sys/sys/proc.h b/sys/sys/proc.h index fbd064c..7789cf9 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -532,6 +532,7 @@ struct proc { struct ucred *p_tracecred; /* (o) Credentials to trace with. */ struct vnode *p_textvp; /* (b) Vnode of executable. */ u_int p_lock; /* (c) Proclock (prevent swap) count. */ + u_int p_imagelock; /* (c) Prevent exit and exec. */ struct sigiolst p_sigiolst; /* (c) List of sigio sources. */ int p_sigparent; /* (c) Signal to parent on exit. */ int p_sig; /* (n) For core dump/debugger XXX. */ @@ -791,6 +792,40 @@ extern pid_t pid_max; KASSERT((p)->p_lock == 0, ("process held")); \ } while (0) + +/* Prevent the process from execing or exiting */ +#define PROC_IMAGELOCK(p) do { \ + PROC_LOCK(p); \ + _PROC_IMAGELOCK(p); \ + PROC_UNLOCK(p); \ +} while (0) +#define _PROC_IMAGELOCK(p) do { \ + PROC_LOCK_ASSERT((p), MA_OWNED); \ + KASSERT(!((p)->p_flag & P_WEXIT), \ + ("PROC_IMAGELOCK of exiting process")); \ + KASSERT(!((p)->p_flag & P_INEXEC), \ + ("PROC_IMAGELOCK of execing process")); \ + (p)->p_imagelock++; \ +} while (0) +#define PROC_ASSERT_IMAGELOCKED(p) do { \ + KASSERT((p)->p_imagelock > 0, ("process image not locked")); \ +} while (0) + +#define PROC_IMAGEUNLOCK(p) do { \ + PROC_LOCK(p); \ + _PROC_IMAGEUNLOCK(p); \ + PROC_UNLOCK(p); \ +} while (0) +#define _PROC_IMAGEUNLOCK(p) do { \ + PROC_LOCK_ASSERT((p), MA_OWNED); \ + PROC_ASSERT_IMAGELOCKED(p); \ + (p)->p_imagelock--; \ + if (((p)->p_flag & (P_WEXIT|P_INEXEC)) && (p)->p_imagelock == 0)\ + wakeup(&(p)->p_imagelock); \ +} while (0) + +void proc_wait_imagelocked(struct proc *p); + /* Check whether a thread is safe to be swapped out. */ #define thread_safetoswapout(td) ((td)->td_flags & TDF_CANSWAP) @@ -849,8 +884,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_IMAGELOCK 0x00080 /* Prevent exec and exit. */ #define PGET_WANTREAD (PGET_HOLD | PGET_CANDEBUG | PGET_NOTWEXIT) +#define PGET_LOCK (PGET_CANDEBUG | PGET_NOTWEXIT | PGET_IMAGELOCK) int pget(pid_t pid, int flags, struct proc **pp);