From cc0827dec7d7f51ff7c39ec61e4127d841278e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Fri, 16 Oct 2026 17:51:06 +0200 Subject: [PATCH 1/2] test_ch BUGFIX thread-safe log msg reset The client thread logs while setting up its call-home bind, so the log callback can read the expected message while the server thread is still writing it: WARNING: ThreadSanitizer: data race (pid=3869790) Read of size 8 at 0x555556aa75b0 by thread T7 (mutexes: write M0): #0 strstr #1 test_msg_callback tests/test_ch.c:55:9 #2 nc_log_vprintf src/log.c:134:9 #3 nc_log_printf src/log.c:152:5 #4 nc_sock_bind_inet src/session_server.c #5 nc_sock_listen_inet src/session_server.c:563:9 #6 nc_client_ch_add_bind_listen src/session_client.c:1765:12 #7 nc_client_ssh_ch_add_bind_listen src/session_client_ssh.c:1268:12 #8 client_thread_delete_while_session tests/test_ch.c:504:11 Previous write of size 8 at 0x555556aa75b0 by thread T8: #0 __tsan_memcpy #1 server_thread_delete_while_session tests/test_ch.c:447:5 Location is global 'expected' of size 512 at 0x555556aa75b0 Reset both the buffer and the expected message under the lock. Co-Authored-By: Claude Opus 5.5 --- tests/test_ch.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_ch.c b/tests/test_ch.c index f0df4a49..704c4304 100644 --- a/tests/test_ch.c +++ b/tests/test_ch.c @@ -100,8 +100,10 @@ server_thread_ssh(void *arg) /* set print clb so we get access to messages */ nc_set_print_clb_session(test_msg_callback); + pthread_mutex_lock(&msg_lock); buffer[0] = '\0'; strcpy(expected, "reconnecting in"); + pthread_mutex_unlock(&msg_lock); /* prepare data for deleting the call-home client */ ret = nc_server_config_del_ch_client("ch_ssh", &test_data->tree); @@ -443,8 +445,10 @@ 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); + pthread_mutex_lock(&msg_lock); buffer[0] = '\0'; strcpy(expected, "thread exit"); + pthread_mutex_unlock(&msg_lock); ps = nc_ps_new(); assert_non_null(ps); From 0b21342bd3a70afc3dfcfec988ee74156667cd2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= Date: Fri, 16 Oct 2026 17:51:20 +0200 Subject: [PATCH 2/2] log BUGFIX atomic print callback The print callback may be changed while other threads are logging, as test_ch does when the server thread installs its callback while the client thread is already setting up its call-home bind: WARNING: ThreadSanitizer: data race (pid=3869790) Read of size 8 at 0x555556aa6f78 by thread T7: #0 nc_log_vprintf src/log.c:133:9 #1 nc_log_printf src/log.c:152:5 #2 nc_sock_bind_inet src/session_server.c #3 nc_sock_listen_inet src/session_server.c:563:9 #4 nc_client_ch_add_bind_listen src/session_client.c:1765:12 #5 nc_client_ssh_ch_add_bind_listen src/session_client_ssh.c:1268:12 #6 client_thread_delete_while_session tests/test_ch.c:504:11 Previous write of size 8 at 0x555556aa6f78 by thread T8: #0 nc_set_print_clb_session src/log.c:159:15 #1 server_thread_delete_while_session tests/test_ch.c:445:5 Location is global 'print_clb' of size 8 at 0x555556aa6f78 Store it as an atomic pointer, just like the verbosity level. Co-Authored-By: Claude Opus 5.5 --- src/log.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/log.c b/src/log.c index 52dc58f4..7b2c5a01 100644 --- a/src/log.c +++ b/src/log.c @@ -40,7 +40,12 @@ */ ATOMIC_T verbose_level = 0; -void (*print_clb)(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg); +typedef void (*nc_print_clb)(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg); + +/** + * @brief libnetconf print callback, can be changed while other threads are logging + */ +ATOMIC_PTR_T print_clb; API void nc_verbosity(NC_VERB_LEVEL level) @@ -103,6 +108,7 @@ nc_log_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char char *msg; void *mem; int req_len; + nc_print_clb clb; msg = malloc(NC_MSG_SIZE); if (!msg) { @@ -130,8 +136,9 @@ nc_log_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char } } - if (print_clb) { - print_clb(session, level, msg); + clb = (nc_print_clb)ATOMIC_PTR_LOAD_RELAXED(print_clb); + if (clb) { + clb(session, level, msg); } else if (session && session->id) { fprintf(stderr, "Session %" PRIu32 " %s: %s\n", session->id, verb[level].label, msg); } else { @@ -156,5 +163,5 @@ nc_log_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char API void nc_set_print_clb_session(void (*clb)(const struct nc_session *, NC_VERB_LEVEL, const char *)) { - print_clb = clb; + ATOMIC_PTR_STORE_RELAXED(print_clb, clb); }