From 3f41ad529ae50bc2cc68d18fb7661f2eed21bbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Fri, 16 Oct 2026 13:23:27 +0200 Subject: [PATCH 1/2] test_ch BUGFIX data race on received message buffer The log callback runs in the call-home client thread and copied the matching message into a global buffer, while the test thread polled that buffer with strlen(), which TSan reported as a data race: WARNING: ThreadSanitizer: data race (pid=7016) Read of size 1 at 0x0000019048f0 by thread T2: #0 server_thread_ssh libnetconf2/tests/test_ch.c:126:15 Previous write of size 8 at 0x0000019048f0 by thread T3: #0 strncpy #1 test_msg_callback libnetconf2/tests/test_ch.c:54:9 #2 nc_log_vprintf libnetconf2/src/log.c:134:9 #3 nc_log_printf libnetconf2/src/log.c:152:5 #4 nc_ch_client_thread libnetconf2/src/session_server.c:4478:17 As if synchronized via sleep: #0 usleep #1 nc_session_free libnetconf2/src/session.c:1206:13 #2 server_thread_ssh libnetconf2/tests/test_ch.c:122:13 Location is global 'buffer' of size 512 at 0x0000019048f0 (test_ch+0x19048f0) Thread T2 (tid=7126, running) created by main thread at: #0 pthread_create #1 test_nc_ch_ssh libnetconf2/tests/test_ch.c:249:11 #2 (libcmocka.so.0+0x618f) #3 __libc_start_call_main /usr/src/debug/glibc-2.37-18.fc38.x86_64/csu/../sysdeps/nptl/libc_start_call_main.h:58:16 Thread T3 (tid=7127, running) created by thread T2 at: #0 pthread_create #1 _nc_connect_ch_client_dispatch libnetconf2/src/session_server.c:4770:14 #2 nc_connect_ch_client_dispatch libnetconf2/src/session_server.c:4813:10 #3 server_thread_ssh libnetconf2/tests/test_ch.c:113:11 SUMMARY: ThreadSanitizer: data race libnetconf2/tests/test_ch.c:126:15 in server_thread_ssh The message text itself was never used, so replace the buffer with an atomic flag. Co-Authored-By: Claude Opus 5.5 --- tests/test_ch.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/test_ch.c b/tests/test_ch.c index 3822d395..959603f4 100644 --- a/tests/test_ch.c +++ b/tests/test_ch.c @@ -38,7 +38,8 @@ struct test_ch_data { pthread_barrier_t *barrier2; }; -char buffer[512]; +/* set by the log callback in the call-home client thread, polled by the test thread */ +static ATOMIC_T expected_msg_received; char expected[512]; int TEST_PORT = 10050, TEST_PORT_2 = 10051, TEST_PORT_3 = 10052, TEST_PORT_4 = 10053; @@ -51,7 +52,7 @@ test_msg_callback(const struct nc_session *session, NC_VERB_LEVEL level, const c (void) session; if (strstr(msg, expected)) { - strncpy(buffer, msg, 511); + ATOMIC_STORE_RELAXED(expected_msg_received, 1); } printf("%s\n", msg); @@ -97,7 +98,7 @@ server_thread_ssh(void *arg) /* set print clb so we get access to messages */ nc_set_print_clb_session(test_msg_callback); - buffer[0] = '\0'; + ATOMIC_STORE_RELAXED(expected_msg_received, 0); strcpy(expected, "reconnecting in"); /* prepare data for deleting the call-home client */ @@ -123,7 +124,7 @@ server_thread_ssh(void *arg) } else if (ret & (NC_PSPOLL_TIMEOUT | NC_PSPOLL_NOSESSIONS)) { usleep(500); } - } while (!strlen(buffer)); + } while (!ATOMIC_LOAD_RELAXED(expected_msg_received)); /* delete the call-home client, the thread should end */ ret = nc_server_config_setup_data(test_data->tree); @@ -433,7 +434,7 @@ server_thread_delete_while_session(void *arg) struct test_ch_data *test_data = test_ctx->test_data; nc_set_print_clb_session(test_msg_callback); - buffer[0] = '\0'; + ATOMIC_STORE_RELAXED(expected_msg_received, 0); strcpy(expected, "thread exit"); ps = nc_ps_new(); @@ -464,9 +465,9 @@ server_thread_delete_while_session(void *arg) do { usleep(5000); - } while (!strlen(buffer)); + } while (!ATOMIC_LOAD_RELAXED(expected_msg_received)); - assert_true(strlen(buffer) > 0); + assert_true(ATOMIC_LOAD_RELAXED(expected_msg_received)); pthread_barrier_wait(test_data->barrier2); From 1706f31deeaeda516bbf44929713274f39efd44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Fri, 16 Oct 2026 13:23:27 +0200 Subject: [PATCH 2/2] test_ch BUGFIX data race on expected message The log callback reads the expected message from whichever thread is logging, while the test thread overwrote it with strcpy(). Point to a string literal through an atomic pointer instead. Co-Authored-By: Claude Opus 5.5 --- tests/test_ch.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_ch.c b/tests/test_ch.c index 959603f4..8e22946d 100644 --- a/tests/test_ch.c +++ b/tests/test_ch.c @@ -40,7 +40,7 @@ struct test_ch_data { /* set by the log callback in the call-home client thread, polled by the test thread */ static ATOMIC_T expected_msg_received; -char expected[512]; +static ATOMIC_PTR_T expected; int TEST_PORT = 10050, TEST_PORT_2 = 10051, TEST_PORT_3 = 10052, TEST_PORT_4 = 10053; const char *TEST_PORT_STR = "10050", *TEST_PORT_2_STR = "10051", *TEST_PORT_3_STR = "10052", *TEST_PORT_4_STR = "10053"; @@ -48,10 +48,12 @@ const char *TEST_PORT_STR = "10050", *TEST_PORT_2_STR = "10051", *TEST_PORT_3_ST static void test_msg_callback(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg) { + const char *exp = ATOMIC_PTR_LOAD_RELAXED(expected); + (void) level; (void) session; - if (strstr(msg, expected)) { + if (exp && strstr(msg, exp)) { ATOMIC_STORE_RELAXED(expected_msg_received, 1); } @@ -99,7 +101,7 @@ server_thread_ssh(void *arg) /* set print clb so we get access to messages */ nc_set_print_clb_session(test_msg_callback); ATOMIC_STORE_RELAXED(expected_msg_received, 0); - strcpy(expected, "reconnecting in"); + ATOMIC_PTR_STORE_RELAXED(expected, "reconnecting in"); /* prepare data for deleting the call-home client */ ret = nc_server_config_del_ch_client("ch_ssh", &test_data->tree); @@ -435,7 +437,7 @@ server_thread_delete_while_session(void *arg) nc_set_print_clb_session(test_msg_callback); ATOMIC_STORE_RELAXED(expected_msg_received, 0); - strcpy(expected, "thread exit"); + ATOMIC_PTR_STORE_RELAXED(expected, "thread exit"); ps = nc_ps_new(); assert_non_null(ps);