diff options
author | Mark Johnston <markj@FreeBSD.org> | 2019-02-21 22:54:17 +0000 |
---|---|---|
committer | Mark Johnston <markj@FreeBSD.org> | 2019-02-21 22:54:17 +0000 |
commit | 4f1b715c8487f7a6d45083dbe998e82a971f48b4 (patch) | |
tree | f9920c102daf3505c78a07f8a93f201192ecf4bb /sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c | |
parent | c0ca37d17b451f3eedbb95041475280b721451a7 (diff) | |
download | src-4f1b715c8487f7a6d45083dbe998e82a971f48b4.tar.gz src-4f1b715c8487f7a6d45083dbe998e82a971f48b4.zip |
Fix a tracepoint lookup race in fasttrap_pid_probe().
fasttrap hooks the userspace breakpoint handler; the hook looks up the
breakpoint address in a hash table of tracepoints. It is possible for
the tracepoint to be removed by a different thread in between the
breakpoint trap and the hash table lookup, in which case SIGTRAP gets
delivered to the target process. Fix the problem by adding a
per-process generation counter that gets incremented when a tracepoint
belonging to that process is removed. Then, when a lookup fails, the
trapping instruction is restarted if the thread's counter doesn't match
that of the process.
Reviewed by: cem
MFC after: 2 weeks
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D19273
Notes
Notes:
svn path=/head/; revision=344452
Diffstat (limited to 'sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c')
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c | 36 |
1 files changed, 34 insertions, 2 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c b/sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c index f1fb8906b870..8e6840f27866 100644 --- a/sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c +++ b/sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c @@ -967,6 +967,7 @@ fasttrap_pid_probe(struct trapframe *tf) struct reg reg, *rp; proc_t *p = curproc, *pp; struct rm_priotracker tracker; + uint64_t gen; uintptr_t pc; uintptr_t new_pc = 0; fasttrap_bucket_t *bucket; @@ -1026,8 +1027,22 @@ fasttrap_pid_probe(struct trapframe *tf) while (pp->p_vmspace == pp->p_pptr->p_vmspace) pp = pp->p_pptr; pid = pp->p_pid; + if (pp != p) { + PROC_LOCK(pp); + if ((pp->p_flag & P_WEXIT) != 0) { + /* + * This can happen if the child was created with + * rfork(2). Userspace tracing cannot work reliably in + * such a scenario, but we can at least try. + */ + PROC_UNLOCK(pp); + sx_sunlock(&proctree_lock); + return (-1); + } + _PHOLD_LITE(pp); + PROC_UNLOCK(pp); + } sx_sunlock(&proctree_lock); - pp = NULL; rm_rlock(&fasttrap_tp_lock, &tracker); #endif @@ -1051,11 +1066,28 @@ fasttrap_pid_probe(struct trapframe *tf) if (tp == NULL) { #ifdef illumos mutex_exit(pid_mtx); + return (-1); #else rm_runlock(&fasttrap_tp_lock, &tracker); -#endif + gen = atomic_load_acq_64(&pp->p_fasttrap_tp_gen); + if (pp != p) + PRELE(pp); + if (curthread->t_fasttrap_tp_gen != gen) { + /* + * At least one tracepoint associated with this PID has + * been removed from the table since #BP was raised. + * Speculate that we hit a tracepoint that has since + * been removed, and retry the instruction. + */ + curthread->t_fasttrap_tp_gen = gen; + tf->tf_rip = pc; + return (0); + } return (-1); +#endif } + if (pp != p) + PRELE(pp); /* * Set the program counter to the address of the traced instruction |