From c58e6add2b90e36bd71f7abe2ea8c780ebbb0775 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 11 Aug 2026 09:42:30 +0200 Subject: [PATCH 1/2] - Fix #1492 from zacek: Data race in log_init() on key_created/log_lock when calling ub_ctx_create() concurrently from multiple threads. --- doc/Changelog | 5 +++++ util/log.c | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 25a43cfd6..84f7aaea7 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,8 @@ +11 August 2026: Wouter + - Fix #1492 from zacek: Data race in log_init() on + key_created/log_lock when calling ub_ctx_create() + concurrently from multiple threads. + 7 August 2026: Wouter - Fix #1489 from jplesnik: Replace removed Python 2 C API macros for SWIG 4.5.0 compatibility. diff --git a/util/log.c b/util/log.c index f2beafc87..e618ee051 100644 --- a/util/log.c +++ b/util/log.c @@ -72,6 +72,14 @@ static ub_thread_key_type logkey; #ifndef THREADS_DISABLED /** pthread mutex to protect FILE* */ static lock_basic_type log_lock; +#ifdef HAVE_PTHREAD +/* Guards the one-time initialization below. Without this, two threads + * calling log_init() for the first time concurrently (e.g. via + * ub_ctx_create() from a multi-threaded application) can both observe + * key_created==0 and both call lock_basic_init(&log_lock), a data race + * that reinitializes/corrupts an in-use mutex. */ +static pthread_once_t log_lock_once = PTHREAD_ONCE_INIT; +#endif #endif /** the identity of this executable/process */ static const char* ident="unbound"; @@ -85,15 +93,28 @@ static int log_time_asc = 0; /** print time in iso format */ static int log_time_iso = 0; +#if !defined(THREADS_DISABLED) && defined(HAVE_PTHREAD) +static void log_lock_init_once(void) +{ + ub_thread_key_create(&logkey, NULL); + lock_basic_init(&log_lock); + key_created = 1; +} +#endif + void log_init(const char* filename, int use_syslog, const char* chrootdir) { FILE *f; +#if !defined(THREADS_DISABLED) && defined(HAVE_PTHREAD) + (void)pthread_once(&log_lock_once, log_lock_init_once); +#else if(!key_created) { key_created = 1; ub_thread_key_create(&logkey, NULL); lock_basic_init(&log_lock); } +#endif lock_basic_lock(&log_lock); if(logfile #if defined(HAVE_SYSLOG_H) || defined(UB_ON_WINDOWS) From 8ee0bca83351b93a21f1d622aa1290e803a7b1b1 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 11 Aug 2026 10:07:38 +0200 Subject: [PATCH 2/2] - Fix stat_values.tdir test to have less test failures. --- doc/Changelog | 1 + testdata/stat_values.tdir/stat_values.test | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/Changelog b/doc/Changelog index 84f7aaea7..39936295d 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,7 @@ - Fix #1492 from zacek: Data race in log_init() on key_created/log_lock when calling ub_ctx_create() concurrently from multiple threads. + - Fix stat_values.tdir test to have less test failures. 7 August 2026: Wouter - Fix #1489 from jplesnik: Replace removed Python 2 C API diff --git a/testdata/stat_values.tdir/stat_values.test b/testdata/stat_values.tdir/stat_values.test index c02bfcf19..79d272ee5 100644 --- a/testdata/stat_values.tdir/stat_values.test +++ b/testdata/stat_values.tdir/stat_values.test @@ -107,7 +107,12 @@ check_expected_stats () { # Check that the rest (unspecified) stats are all 0 (no surprises). check_rest_stats () { echo "> Checking rest stats" - grep -F -x -v -f $EXPECTED_STATS_FILE $FILTERED_STATS_FILE > $REST_STATS_FILE + grep -F -x -v -f $EXPECTED_STATS_FILE $FILTERED_STATS_FILE | \ + grep -F -v -e "msg.cache.max_collisions" \ + -e "rrset.cache.max_collisions" | \ + > $REST_STATS_FILE + # It filtered out the 'max_collisions', since at random, a msg + # or rrset cache entry could have a cache hash collision. */ if test $DEBUG -ne 0; then echo "Rest:" cat $REST_STATS_FILE