Skip to content

Commit 55659e4

Browse files
tomerdbzNirWolfer
authored andcommitted
issue: 4347777 Replace thread-local dummy lock
The thread-local dummy locker in ring_slave could cause use-after-free issues during XLIO shutdown when one thread attempts to access a socket's locker that was created by a terminated thread. This occurs because the thread-local object is freed when its creator thread terminates. Replace the thread-local dummy locker with a global one to prevent this issue. To maintain data path performance, optimize the dummy lock for a different cache-line to prevent false sharing by aligning the lock on a 64-byte boundary. Signed-off-by: Tomer Cabouly <[email protected]>
1 parent f660eef commit 55659e4

File tree

4 files changed

+39
-5
lines changed

4 files changed

+39
-5
lines changed

src/core/dev/ring_slave.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,13 @@
4949
// AF_INET address 0.0.0.0:0, used for 3T flow spec keys.
5050
static const sock_addr s_sock_addrany;
5151

52-
// static thread_local lock_dummy t_lock_dummy_ring;
52+
static padded_lock_dummy g_lock_dummy_ring;
5353

5454
static lock_base *get_new_lock(const char *name, bool real_lock)
5555
{
5656
return (real_lock
5757
? static_cast<lock_base *>(multilock::create_new_lock(MULTILOCK_RECURSIVE, name))
58-
: static_cast<lock_base *>(new lock_dummy()));
58+
: static_cast<lock_base *>(&g_lock_dummy_ring.lock));
5959
}
6060

6161
ring_slave::ring_slave(int if_index, ring *parent, ring_type_t type, bool use_locks)

src/core/sock/sockinfo_tcp.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ extern global_stats_t g_global_stat_static;
8585
tcp_timers_collection *g_tcp_timers_collection = nullptr;
8686
thread_local thread_local_tcp_timers g_thread_local_tcp_timers;
8787
bind_no_port *g_bind_no_port = nullptr;
88-
// static thread_local lock_dummy t_lock_dummy_socket;
88+
89+
static padded_lock_dummy g_lock_dummy_socket;
8990

9091
/*
9192
* The following socket options are inherited by a connected TCP socket from the listening socket:
@@ -165,7 +166,7 @@ static lock_base *get_new_tcp_lock()
165166
return (
166167
safe_mce_sys().tcp_ctl_thread != option_tcp_ctl_thread::CTL_THREAD_DELEGATE_TCP_TIMERS
167168
? static_cast<lock_base *>(multilock::create_new_lock(MULTILOCK_RECURSIVE, "tcp_con"))
168-
: static_cast<lock_base *>(new lock_dummy()));
169+
: static_cast<lock_base *>(&g_lock_dummy_socket.lock));
169170
}
170171

171172
inline void sockinfo_tcp::lwip_pbuf_init_custom(mem_buf_desc_t *p_desc)

src/utils/lock_wrapper.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,14 @@ class lock_dummy : public lock_base {
501501
int is_locked_by_me() override { return 1; }
502502
};
503503

504+
// Users of lock_dummy may wish to alignas(64) to place this lock in in a different cache-line and
505+
// prevent false sharing
506+
struct alignas(64) padded_lock_dummy {
507+
lock_dummy lock;
508+
// Padding to fill a full cache line
509+
char padding[64 - sizeof(lock_dummy)];
510+
};
511+
504512
static inline void lock_deleter_func(lock_base *lock)
505513
{
506514
lock->delete_obj();

tests/gtest/tcp/tcp_socket.cc

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
#include "common/sys.h"
3838
#include "common/base.h"
3939
#include "tcp_base.h"
40-
40+
#include <thread>
4141
class tcp_socket : public tcp_base {};
4242

4343
/**
@@ -153,3 +153,28 @@ TEST_F(tcp_socket, ti_2_ipv6only_listen_all)
153153
test_lambda(true);
154154
test_lambda(false);
155155
}
156+
157+
/**
158+
* @test tcp_socket.ti_3_socket_closed_different_thread_works
159+
* @brief
160+
* Test that a socket can be closed after its creator thread terminates
161+
* @details
162+
* Creates a socket in a separate thread, then closes it from the main thread
163+
* after the creator thread has terminated. This verifies that socket cleanup
164+
* works correctly across thread boundaries.
165+
*/
166+
TEST_F(tcp_socket, ti_3_socket_closed_different_thread_works)
167+
{
168+
int fd = -1;
169+
170+
std::thread t([&fd]() {
171+
fd = socket(m_family, SOCK_STREAM, IPPROTO_IP);
172+
EXPECT_LE(0, fd);
173+
EXPECT_EQ(errno, EOK);
174+
});
175+
176+
t.join();
177+
178+
EXPECT_LE(0, fd);
179+
close(fd);
180+
}

0 commit comments

Comments
 (0)