summaryrefslogtreecommitdiff
path: root/lib/tevent/tevent_signal.c
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2009-08-21 15:07:25 -0700
committerJeremy Allison <jra@samba.org>2009-08-21 15:07:25 -0700
commitba52f18bfecfd7b0ba22c4ad9e9b5bfd18f34c93 (patch)
treeb94fb6caa43ad2ff5255ee3b87041e0eb1313585 /lib/tevent/tevent_signal.c
parent6afb02cb53f47e0fd7e7df3935b067e7e1f8a9de (diff)
downloadsamba-ba52f18bfecfd7b0ba22c4ad9e9b5bfd18f34c93.tar.gz
samba-ba52f18bfecfd7b0ba22c4ad9e9b5bfd18f34c93.tar.bz2
samba-ba52f18bfecfd7b0ba22c4ad9e9b5bfd18f34c93.zip
Fix for bug 6651 - smbd SIGSEGV when breaking oplocks.
Based on a patch submitted by Petr Vandrovec <petr@vandrovec.name>. Multiple pending signals with siginfo_t's weren't being handled correctly leading to smbd abort with kernel oplock signals. Jeremy
Diffstat (limited to 'lib/tevent/tevent_signal.c')
-rw-r--r--lib/tevent/tevent_signal.c71
1 files changed, 54 insertions, 17 deletions
diff --git a/lib/tevent/tevent_signal.c b/lib/tevent/tevent_signal.c
index 4a58a8bb85..27e862453a 100644
--- a/lib/tevent/tevent_signal.c
+++ b/lib/tevent/tevent_signal.c
@@ -97,7 +97,11 @@ static void tevent_common_signal_handler_info(int signum, siginfo_t *info,
void *uctx)
{
uint32_t count = sig_count(sig_state->signal_count[signum]);
- sig_state->sig_info[signum][count] = *info;
+ /* sig_state->signal_count[signum].seen % SA_INFO_QUEUE_COUNT
+ * is the base of the unprocessed signals in the ringbuffer. */
+ uint32_t ofs = (sig_state->signal_count[signum].seen + count) %
+ SA_INFO_QUEUE_COUNT;
+ sig_state->sig_info[signum][ofs] = *info;
tevent_common_signal_handler(signum);
@@ -229,7 +233,7 @@ struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev,
act.sa_handler = NULL;
act.sa_sigaction = tevent_common_signal_handler_info;
if (sig_state->sig_info[signum] == NULL) {
- sig_state->sig_info[signum] = talloc_array(sig_state, siginfo_t, SA_INFO_QUEUE_COUNT);
+ sig_state->sig_info[signum] = talloc_zero_array(sig_state, siginfo_t, SA_INFO_QUEUE_COUNT);
if (sig_state->sig_info[signum] == NULL) {
talloc_free(se);
return NULL;
@@ -294,6 +298,11 @@ int tevent_common_check_signal(struct tevent_context *ev)
struct tevent_common_signal_list *sl, *next;
struct sigcounter counter = sig_state->signal_count[i];
uint32_t count = sig_count(counter);
+#ifdef SA_SIGINFO
+ /* Ensure we null out any stored siginfo_t entries
+ * after processing for debugging purposes. */
+ bool clear_processed_siginfo = false;
+#endif
if (count == 0) {
continue;
@@ -303,25 +312,21 @@ int tevent_common_check_signal(struct tevent_context *ev)
next = sl->next;
#ifdef SA_SIGINFO
if (se->sa_flags & SA_SIGINFO) {
- int j;
+ uint32_t j;
+
+ clear_processed_siginfo = true;
+
for (j=0;j<count;j++) {
- /* note the use of the sig_info array as a
- ring buffer */
- int ofs = ((count-1) + j) % SA_INFO_QUEUE_COUNT;
- se->handler(ev, se, i, 1,
+ /* sig_state->signal_count[i].seen
+ * % SA_INFO_QUEUE_COUNT is
+ * the base position of the unprocessed
+ * signals in the ringbuffer. */
+ uint32_t ofs = (counter.seen + j)
+ % SA_INFO_QUEUE_COUNT;
+ se->handler(ev, se, i, 1,
(void*)&sig_state->sig_info[i][ofs],
se->private_data);
}
- if (SIG_PENDING(sig_state->sig_blocked[i])) {
- /* we'd filled the queue, unblock the
- signal now */
- sigset_t set;
- sigemptyset(&set);
- sigaddset(&set, i);
- SIG_SEEN(sig_state->sig_blocked[i],
- sig_count(sig_state->sig_blocked[i]));
- sigprocmask(SIG_UNBLOCK, &set, NULL);
- }
if (se->sa_flags & SA_RESETHAND) {
talloc_free(se);
}
@@ -333,8 +338,40 @@ int tevent_common_check_signal(struct tevent_context *ev)
talloc_free(se);
}
}
+
+#ifdef SA_SIGINFO
+ if (clear_processed_siginfo) {
+ uint32_t j;
+ for (j=0;j<count;j++) {
+ uint32_t ofs = (counter.seen + j)
+ % SA_INFO_QUEUE_COUNT;
+ memset((void*)&sig_state->sig_info[i][ofs],
+ '\0',
+ sizeof(siginfo_t));
+ }
+ }
+#endif
+
SIG_SEEN(sig_state->signal_count[i], count);
SIG_SEEN(sig_state->got_signal, count);
+
+#ifdef SA_SIGINFO
+ if (SIG_PENDING(sig_state->sig_blocked[i])) {
+ /* We'd filled the queue, unblock the
+ signal now the queue is empty again.
+ Note we MUST do this after the
+ SIG_SEEN(sig_state->signal_count[i], count)
+ call to prevent a new signal running
+ out of room in the sig_state->sig_info[i][]
+ ring buffer. */
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, i);
+ SIG_SEEN(sig_state->sig_blocked[i],
+ sig_count(sig_state->sig_blocked[i]));
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+ }
+#endif
}
return 1;