aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2013-11-23 15:48:17 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2013-11-23 15:48:17 +0000
commit0a9655a082b25b1eaeb36ad7832f4c91d3e7f45d (patch)
treefb93e62e7349c113134117155612ff51dc329b6c /lib
parent8a8d9d147508fa13ec70e0adf21ee9f103a1cc82 (diff)
downloadsrc-0a9655a082b25b1eaeb36ad7832f4c91d3e7f45d.tar.gz
src-0a9655a082b25b1eaeb36ad7832f4c91d3e7f45d.zip
If check_deferred_signal() execution needs binding of PLT symbol,
unlocking the rtld bind lock results in the processing of ast and recursing into the check_deferred_signal(). Nested execution of check_deferred_signal() delivers the signal to user code and clears si_signo. On return, top-level check_deferred_signal() frame continues delivering the same signal one more time, but now with zero si_signo. Fix this by adding a flag to indicate that deferred delivery is running, so check_deferred_signal() should avoid doing anything. Since user signal handler is allowed to modify the passed machine context to make return from the signal handler to cause arbitrary jump, or do longjmp(). For this case, also clear the flag in thr_sighandler(), since kernel signal delivery means that nested delivery code should not run right now. Reported by: Vitaly Magerya <vmagerya@gmail.com> Reviewed by: davidxu, jilles Tested by: pho Sponsored by: The FreeBSD Foundation MFC after: 1 week
Notes
Notes: svn path=/head/; revision=258499
Diffstat (limited to 'lib')
-rw-r--r--lib/libthr/thread/thr_private.h3
-rw-r--r--lib/libthr/thread/thr_sig.c9
2 files changed, 10 insertions, 2 deletions
diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h
index 83a02b554f12..c6651cd2dc7e 100644
--- a/lib/libthr/thread/thr_private.h
+++ b/lib/libthr/thread/thr_private.h
@@ -433,6 +433,9 @@ struct pthread {
/* the sigaction should be used for deferred signal. */
struct sigaction deferred_sigact;
+ /* deferred signal delivery is performed, do not reenter. */
+ int deferred_run;
+
/* Force new thread to exit. */
int force_exit;
diff --git a/lib/libthr/thread/thr_sig.c b/lib/libthr/thread/thr_sig.c
index 415ddb004dac..57c94064a728 100644
--- a/lib/libthr/thread/thr_sig.c
+++ b/lib/libthr/thread/thr_sig.c
@@ -162,6 +162,7 @@ thr_sighandler(int sig, siginfo_t *info, void *_ucp)
act = _thr_sigact[sig-1].sigact;
_thr_rwl_unlock(&_thr_sigact[sig-1].lock);
errno = err;
+ curthread->deferred_run = 0;
/*
* if a thread is in critical region, for example it holds low level locks,
@@ -320,14 +321,18 @@ check_deferred_signal(struct pthread *curthread)
siginfo_t info;
int uc_len;
- if (__predict_true(curthread->deferred_siginfo.si_signo == 0))
+ if (__predict_true(curthread->deferred_siginfo.si_signo == 0 ||
+ curthread->deferred_run))
return;
+ curthread->deferred_run = 1;
uc_len = __getcontextx_size();
uc = alloca(uc_len);
getcontext(uc);
- if (curthread->deferred_siginfo.si_signo == 0)
+ if (curthread->deferred_siginfo.si_signo == 0) {
+ curthread->deferred_run = 0;
return;
+ }
__fillcontextx2((char *)uc);
act = curthread->deferred_sigact;
uc->uc_sigmask = curthread->deferred_sigmask;