diff --git a/packages/network/samba/package.mk b/packages/network/samba/package.mk index e722c69ae5..e30ff32e11 100644 --- a/packages/network/samba/package.mk +++ b/packages/network/samba/package.mk @@ -21,7 +21,7 @@ PKG_VERSION="4.6.6" PKG_ARCH="any" PKG_LICENSE="GPLv3+" PKG_SITE="https://www.samba.org" -PKG_URL="https://samba.org/samba/ftp/stable/$PKG_NAME-$PKG_VERSION.tar.gz" +PKG_URL="https://download.samba.org/pub/samba/stable/$PKG_NAME-$PKG_VERSION.tar.gz" PKG_DEPENDS_TARGET="toolchain heimdal:host e2fsprogs Python zlib readline popt libaio connman" PKG_NEED_UNPACK="$(get_pkg_directory heimdal) $(get_pkg_directory e2fsprogs)" PKG_SECTION="network" diff --git a/packages/network/samba/patches/samba-952-backport-tbd_deadlock_fix.patch b/packages/network/samba/patches/samba-952-backport-tbd_deadlock_fix.patch new file mode 100644 index 0000000000..c270444a6b --- /dev/null +++ b/packages/network/samba/patches/samba-952-backport-tbd_deadlock_fix.patch @@ -0,0 +1,215 @@ +From 19b193ebc974efdebdf347143938b5d053e67051 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Tue, 14 Mar 2017 14:24:18 +0100 +Subject: [PATCH] tdb: runtime check for robust mutexes may hang in threaded + programs + +The current runtime check for robust mutexes in +tdb_runtime_check_for_robust_mutexes() is not thread-safe. + +When called in a multi-threaded program where any another thread doesn't +have SIGCHLD blocked, we may end up hung in sigsuspend() waiting for a +SIGCHLD of a child procecss and the signal was delivered to another +thread. + +Revert to the previous behaviour of waiting for the child instead of +waiting for the SIGCHLD signal. + +Ensure the pid we wait for is not reset to -1 in a toctou race with the +signal handler. + +Check whether waitpid() returns ECHILD which can happen if the signal +handler is run by more then one thread in parallel (yes, this can +happen) or if tdb_robust_mutex_wait_for_child() and the signal handler +are racing. + +Bug: https://bugzilla.samba.org/show_bug.cgi?id=12593 + +Pair-programmed-with: Stefan Metzmacher + +Signed-off-by: Ralph Boehme +Signed-off-by: Stefan Metzmacher +Reviewed-by: Andreas Schneider +--- + lib/tdb/common/mutex.c | 116 +++++++++++++++++++++++++++++-------------------- + 1 file changed, 70 insertions(+), 46 deletions(-) + +diff --git a/lib/tdb/common/mutex.c b/lib/tdb/common/mutex.c +index cac3916713f..8a122d5d6a2 100644 +--- a/lib/tdb/common/mutex.c ++++ b/lib/tdb/common/mutex.c +@@ -752,12 +752,23 @@ static bool tdb_robust_mutex_setup_sigchild(void (*handler)(int), + + static void tdb_robust_mutex_handler(int sig) + { +- if (tdb_robust_mutex_pid != -1) { ++ pid_t child_pid = tdb_robust_mutex_pid; ++ ++ if (child_pid != -1) { + pid_t pid; +- int status; + +- pid = waitpid(tdb_robust_mutex_pid, &status, WNOHANG); +- if (pid == tdb_robust_mutex_pid) { ++ pid = waitpid(child_pid, NULL, WNOHANG); ++ if (pid == -1) { ++ switch (errno) { ++ case ECHILD: ++ tdb_robust_mutex_pid = -1; ++ return; ++ ++ default: ++ return; ++ } ++ } ++ if (pid == child_pid) { + tdb_robust_mutex_pid = -1; + return; + } +@@ -776,6 +787,44 @@ static void tdb_robust_mutex_handler(int sig) + tdb_robust_mutext_old_handler(sig); + } + ++static void tdb_robust_mutex_wait_for_child(pid_t *child_pid) ++{ ++ int options = WNOHANG; ++ ++ if (*child_pid == -1) { ++ return; ++ } ++ ++ while (tdb_robust_mutex_pid > 0) { ++ pid_t pid; ++ ++ /* ++ * First we try with WNOHANG, as the process might not exist ++ * anymore. Once we've sent SIGKILL we block waiting for the ++ * exit. ++ */ ++ pid = waitpid(*child_pid, NULL, options); ++ if (pid == -1) { ++ if (errno == EINTR) { ++ continue; ++ } else if (errno == ECHILD) { ++ break; ++ } else { ++ abort(); ++ } ++ } ++ if (pid == *child_pid) { ++ break; ++ } ++ ++ kill(*child_pid, SIGKILL); ++ options = 0; ++ } ++ ++ tdb_robust_mutex_pid = -1; ++ *child_pid = -1; ++} ++ + _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void) + { + void *ptr = NULL; +@@ -788,9 +837,8 @@ _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void) + char c = 0; + bool ok; + static bool initialized; +- sigset_t mask, old_mask, suspend_mask; ++ pid_t saved_child_pid = -1; + bool cleanup_ma = false; +- bool cleanup_sigmask = false; + + if (initialized) { + return tdb_mutex_locking_cached; +@@ -798,8 +846,6 @@ _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void) + + initialized = true; + +- sigemptyset(&suspend_mask); +- + ok = tdb_mutex_locking_supported(); + if (!ok) { + return false; +@@ -845,26 +891,13 @@ _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void) + } + m = (pthread_mutex_t *)ptr; + +- /* +- * 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; +- } +- cleanup_sigmask = true; +- 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; + } + + tdb_robust_mutex_pid = fork(); ++ saved_child_pid = tdb_robust_mutex_pid; + if (tdb_robust_mutex_pid == 0) { + size_t nwritten; + close(pipe_down[1]); +@@ -914,14 +947,7 @@ _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void) + goto cleanup; + } + +- while (tdb_robust_mutex_pid > 0) { +- ret = sigsuspend(&suspend_mask); +- if (ret != -1 || errno != EINTR) { +- abort(); +- } +- } +- tdb_robust_mutex_setup_sigchild(tdb_robust_mutext_old_handler, NULL); +- tdb_robust_mutext_old_handler = SIG_ERR; ++ tdb_robust_mutex_wait_for_child(&saved_child_pid); + + ret = pthread_mutex_trylock(m); + if (ret != EOWNERDEAD) { +@@ -950,23 +976,21 @@ _PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void) + tdb_mutex_locking_cached = true; + + cleanup: +- while (tdb_robust_mutex_pid > 0) { +- kill(tdb_robust_mutex_pid, SIGKILL); +- ret = sigsuspend(&suspend_mask); +- if (ret != -1 || errno != EINTR) { +- abort(); +- } +- } ++ /* ++ * Note that we don't reset the signal handler we just reset ++ * tdb_robust_mutex_pid to -1. This is ok as this code path is only ++ * called once per process. ++ * ++ * Leaving our signal handler avoids races with other threads potentialy ++ * setting up their SIGCHLD handlers. ++ * ++ * The worst thing that can happen is that the other newer signal ++ * handler will get the SIGCHLD signal for our child and/or reap the ++ * child with a wait() function. tdb_robust_mutex_wait_for_child() ++ * handles the case where waitpid returns ECHILD. ++ */ ++ tdb_robust_mutex_wait_for_child(&saved_child_pid); + +- if (tdb_robust_mutext_old_handler != SIG_ERR) { +- tdb_robust_mutex_setup_sigchild(tdb_robust_mutext_old_handler, NULL); +- } +- if (cleanup_sigmask) { +- ret = pthread_sigmask(SIG_SETMASK, &old_mask, NULL); +- if (ret != 0) { +- abort(); +- } +- } + if (m != NULL) { + pthread_mutex_destroy(m); + }