Skip to content

Commit 6ed922d

Browse files
committed
Fix call home client thread cleanup
This commit by @thelsper fixes a race condition in call home client creation and deletion. If a call home client is deleted and then immediately re-created with the same client name, the old client thread will not notice that the old client was destroyed, and will attempt to service the new client, while the newly created client thread for the new client also attempts to service the new client! This results in a proliferation of threads and can also result in deadlock. To fix this, we added a unique client ID to the client information. When a client thread retrieves its client by name, it will now also check the client ID against the cached value of the client ID for the client it was previously servicing. If the client ID changes, this means that the client was destroyed, and the thread will clean itself up. While we were here making these changes we also changed the session ID to be a true atomic, and removed the spin lock that was protecting it. (The atomic operations library stdatomic.h was added in C11, which was why we also appended `-std=gnu11` to the `CFLAGS`. Both sysrepo and netopeer2 are already using C11.)
1 parent 821f1e0 commit 6ed922d

4 files changed

Lines changed: 27 additions & 26 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ if(NOT CMAKE_BUILD_TYPE)
2424
set(CMAKE_BUILD_TYPE debug)
2525
endif()
2626

27-
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fvisibility=hidden")
27+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fvisibility=hidden -std=gnu11")
2828
set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG")
2929
set(CMAKE_C_FLAGS_PACKAGE "-g -O2 -DNDEBUG")
3030
set(CMAKE_C_FLAGS_DEBUG "-g -O0")

src/session_p.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <stdint.h>
2020
#include <pthread.h>
21+
#include <stdatomic.h>
2122

2223
#include <libyang/libyang.h>
2324

@@ -268,14 +269,15 @@ struct nc_server_opts {
268269
} conn;
269270
NC_CH_START_WITH start_with;
270271
uint8_t max_attempts;
272+
uint32_t id;
271273
pthread_mutex_t lock;
272274
} *ch_clients;
273275
uint16_t ch_client_count;
274276
pthread_rwlock_t ch_client_lock;
275277

276-
/* ACCESS locked with sid_lock */
277-
uint32_t new_session_id;
278-
pthread_spinlock_t sid_lock;
278+
/* Atomic IDs */
279+
atomic_uint_fast32_t new_session_id;
280+
atomic_uint_fast32_t new_client_id;
279281
};
280282

281283
/**

src/session_server.c

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ nc_server_init(struct ly_ctx *ctx)
515515
server_opts.ctx = ctx;
516516

517517
server_opts.new_session_id = 1;
518-
pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
518+
server_opts.new_client_id = 1;
519519

520520
errno=0;
521521

@@ -549,8 +549,6 @@ nc_server_destroy(void)
549549
server_opts.capabilities = NULL;
550550
server_opts.capabilities_count = 0;
551551

552-
pthread_spin_destroy(&server_opts.sid_lock);
553-
554552
#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
555553
nc_server_del_endpt(NULL, 0);
556554
#endif
@@ -703,9 +701,7 @@ nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **s
703701
(*session)->ctx = server_opts.ctx;
704702

705703
/* assign new SID atomically */
706-
pthread_spin_lock(&server_opts.sid_lock);
707-
(*session)->id = server_opts.new_session_id++;
708-
pthread_spin_unlock(&server_opts.sid_lock);
704+
(*session)->id = atomic_fetch_add(&server_opts.new_session_id, 1);
709705

710706
/* NETCONF handshake */
711707
msgtype = nc_handshake_io(*session);
@@ -2015,11 +2011,7 @@ nc_accept(int timeout, struct nc_session **session)
20152011
pthread_rwlock_unlock(&server_opts.endpt_lock);
20162012

20172013
/* assign new SID atomically */
2018-
/* LOCK */
2019-
pthread_spin_lock(&server_opts.sid_lock);
2020-
(*session)->id = server_opts.new_session_id++;
2021-
/* UNLOCK */
2022-
pthread_spin_unlock(&server_opts.sid_lock);
2014+
(*session)->id = atomic_fetch_add(&server_opts.new_session_id, 1);
20232015

20242016
/* NETCONF handshake */
20252017
msgtype = nc_handshake_io(*session);
@@ -2081,6 +2073,7 @@ nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
20812073
return -1;
20822074
}
20832075
server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
2076+
server_opts.ch_clients[server_opts.ch_client_count - 1].id = atomic_fetch_add(&server_opts.new_client_id, 1);
20842077
server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
20852078
server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpts = NULL;
20862079
server_opts.ch_clients[server_opts.ch_client_count - 1].ch_endpt_count = 0;
@@ -2758,11 +2751,7 @@ nc_connect_ch_client_endpt(struct nc_ch_client *client, struct nc_ch_endpt *endp
27582751
}
27592752

27602753
/* assign new SID atomically */
2761-
/* LOCK */
2762-
pthread_spin_lock(&server_opts.sid_lock);
2763-
(*session)->id = server_opts.new_session_id++;
2764-
/* UNLOCK */
2765-
pthread_spin_unlock(&server_opts.sid_lock);
2754+
(*session)->id = atomic_fetch_add(&server_opts.new_session_id, 1);
27662755

27672756
/* NETCONF handshake */
27682757
msgtype = nc_handshake_io(*session);
@@ -2909,12 +2898,14 @@ nc_ch_client_thread(void *arg)
29092898
struct nc_ch_endpt *cur_endpt;
29102899
struct nc_session *session;
29112900
struct nc_ch_client *client;
2901+
uint32_t client_id;
29122902

29132903
/* LOCK */
29142904
client = nc_server_ch_client_with_endpt_lock(data->client_name);
29152905
if (!client) {
29162906
goto cleanup;
29172907
}
2908+
client_id = client->id;
29182909

29192910
cur_endpt = &client->ch_endpts[0];
29202911
cur_endpt_name = strdup(cur_endpt->name);
@@ -2938,6 +2929,10 @@ nc_ch_client_thread(void *arg)
29382929
if (!client) {
29392930
goto cleanup;
29402931
}
2932+
if (client->id != client_id) {
2933+
nc_server_ch_client_unlock(client);
2934+
goto cleanup;
2935+
}
29412936

29422937
/* session changed status -> it was disconnected for whatever reason,
29432938
* persistent connection immediately tries to reconnect, periodic waits some first */
@@ -2953,6 +2948,10 @@ nc_ch_client_thread(void *arg)
29532948
if (!client) {
29542949
goto cleanup;
29552950
}
2951+
if (client->id != client_id) {
2952+
nc_server_ch_client_unlock(client);
2953+
goto cleanup;
2954+
}
29562955
}
29572956

29582957
/* set next endpoint to try */
@@ -2983,6 +2982,10 @@ nc_ch_client_thread(void *arg)
29832982
if (!client) {
29842983
goto cleanup;
29852984
}
2985+
if (client->id != client_id) {
2986+
nc_server_ch_client_unlock(client);
2987+
goto cleanup;
2988+
}
29862989

29872990
++cur_attempts;
29882991

src/session_server_ssh.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,9 +1555,7 @@ nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session
15551555
}
15561556

15571557
/* assign new SID atomically */
1558-
pthread_spin_lock(&server_opts.sid_lock);
1559-
new_session->id = server_opts.new_session_id++;
1560-
pthread_spin_unlock(&server_opts.sid_lock);
1558+
new_session->id = atomic_fetch_add(&server_opts.new_session_id, 1);
15611559

15621560
/* NETCONF handshake */
15631561
msgtype = nc_handshake_io(new_session);
@@ -1628,9 +1626,7 @@ nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
16281626
}
16291627

16301628
/* assign new SID atomically */
1631-
pthread_spin_lock(&server_opts.sid_lock);
1632-
new_session->id = server_opts.new_session_id++;
1633-
pthread_spin_unlock(&server_opts.sid_lock);
1629+
new_session->id = atomic_fetch_add(&server_opts.new_session_id, 1);
16341630

16351631
/* NETCONF handshake */
16361632
msgtype = nc_handshake_io(new_session);

0 commit comments

Comments
 (0)