summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRalph Boehme <slow@samba.org>2016-03-26 12:43:55 +0100
committerRalph Boehme <slow@samba.org>2016-03-29 16:04:19 +0200
commita7a77e2f43a5baa00e104037b20487f28aabe866 (patch)
treefa920d2b342bd8c71aeef9fcf1774cf380ec7618
parentaa81199bec3cb5a3f8f7b990395108a6eb988815 (diff)
downloadsamba-a7a77e2f43a5baa00e104037b20487f28aabe866.tar.gz
tdb: avoid a race condition when checking for robust mutexes
This fixes a race between calling waitpid() in two places (SIGCHLD the signal handler and the rendezvous code when waiting for the child to terminate), by - blocking SIGCHLD before installing our signal handler - in the rendezvous code call sigssuspend() which unblocks SIGCHLD and suspends the thread and waits for signal delivery BUG: https://bugzilla.samba.org/show_bug.cgi?id=11808 Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Uri Simchoni <uri@samba.org> Autobuild-User(master): Ralph Böhme <slow@samba.org> Autobuild-Date(master): Tue Mar 29 16:04:19 CEST 2016 on sn-devel-144
-rw-r--r--lib/tdb/common/mutex.c60
1 files changed, 32 insertions, 28 deletions
diff --git a/lib/tdb/common/mutex.c b/lib/tdb/common/mutex.c
index fae43d4ff7b..e57031dc222 100644
--- a/lib/tdb/common/mutex.c
+++ b/lib/tdb/common/mutex.c
@@ -775,8 +775,8 @@ _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void)
ssize_t nread;
char c = 0;
bool ok;
- int status;
static bool initialized;
+ sigset_t mask, old_mask, suspend_mask;
if (initialized) {
return tdb_mutex_locking_cached;
@@ -828,9 +828,22 @@ _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void)
goto cleanup_ma;
}
+ /*
+ * Block SIGCHLD so we can atomically wait for it later with
+ * sigsuspend()
+ */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ ret = pthread_sigmask(SIG_BLOCK, &mask, &old_mask);
+ if (ret != 0) {
+ goto cleanup_m;
+ }
+ suspend_mask = old_mask;
+ sigdelset(&suspend_mask, SIGCHLD);
+
if (tdb_robust_mutex_setup_sigchild(tdb_robust_mutex_handler,
&tdb_robust_mutext_old_handler) == false) {
- goto cleanup_ma;
+ goto cleanup_sigmask;
}
tdb_robust_mutex_pid = fork();
@@ -884,16 +897,9 @@ _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void)
}
while (tdb_robust_mutex_pid > 0) {
- pid_t pid;
-
- errno = 0;
- pid = waitpid(tdb_robust_mutex_pid, &status, 0);
- if (pid == tdb_robust_mutex_pid) {
- tdb_robust_mutex_pid = -1;
- break;
- }
- if (pid == -1 && errno != EINTR) {
- goto cleanup_child;
+ ret = sigsuspend(&suspend_mask);
+ if (ret != -1 || errno != EINTR) {
+ abort();
}
}
tdb_robust_mutex_setup_sigchild(tdb_robust_mutext_old_handler, NULL);
@@ -903,46 +909,44 @@ _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void)
if (ret == 0) {
pthread_mutex_unlock(m);
}
- goto cleanup_m;
+ goto cleanup_sigmask;
}
ret = pthread_mutex_consistent(m);
if (ret != 0) {
- goto cleanup_m;
+ goto cleanup_sigmask;
}
ret = pthread_mutex_trylock(m);
if (ret != EDEADLK) {
pthread_mutex_unlock(m);
- goto cleanup_m;
+ goto cleanup_sigmask;
}
ret = pthread_mutex_unlock(m);
if (ret != 0) {
- goto cleanup_m;
+ goto cleanup_sigmask;
}
tdb_mutex_locking_cached = true;
- goto cleanup_m;
+ goto cleanup_sigmask;
cleanup_child:
while (tdb_robust_mutex_pid > 0) {
- pid_t pid;
-
kill(tdb_robust_mutex_pid, SIGKILL);
-
- errno = 0;
- pid = waitpid(tdb_robust_mutex_pid, &status, 0);
- if (pid == tdb_robust_mutex_pid) {
- tdb_robust_mutex_pid = -1;
- break;
- }
- if (pid == -1 && errno != EINTR) {
- break;
+ ret = sigsuspend(&suspend_mask);
+ if (ret != -1 || errno != EINTR) {
+ abort();
}
}
+
cleanup_sig_child:
tdb_robust_mutex_setup_sigchild(tdb_robust_mutext_old_handler, NULL);
+cleanup_sigmask:
+ ret = pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
+ if (ret != 0) {
+ abort();
+ }
cleanup_m:
pthread_mutex_destroy(m);
cleanup_ma: