Skip to content

Commit bb86a6f

Browse files
committed
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 81a1553 commit bb86a6f

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
@@ -21,13 +21,13 @@
2121
// AF_INET address 0.0.0.0:0, used for 3T flow spec keys.
2222
static const sock_addr s_sock_addrany;
2323

24-
static thread_local lock_dummy t_lock_dummy_ring;
24+
static padded_lock_dummy g_lock_dummy_ring;
2525

2626
static lock_base *get_new_lock(const char *name, bool real_lock)
2727
{
2828
return (real_lock
2929
? static_cast<lock_base *>(multilock::create_new_lock(MULTILOCK_RECURSIVE, name))
30-
: static_cast<lock_base *>(&t_lock_dummy_ring));
30+
: static_cast<lock_base *>(&g_lock_dummy_ring.lock));
3131
}
3232

3333
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
@@ -57,7 +57,8 @@ extern global_stats_t g_global_stat_static;
5757
tcp_timers_collection *g_tcp_timers_collection = nullptr;
5858
thread_local thread_local_tcp_timers g_thread_local_tcp_timers;
5959
bind_no_port *g_bind_no_port = nullptr;
60-
static thread_local lock_dummy t_lock_dummy_socket;
60+
61+
static padded_lock_dummy g_lock_dummy_socket;
6162

6263
/*
6364
* The following socket options are inherited by a connected TCP socket from the listening socket:
@@ -137,7 +138,7 @@ static lock_base *get_new_tcp_lock()
137138
return (
138139
safe_mce_sys().tcp_ctl_thread != option_tcp_ctl_thread::CTL_THREAD_DELEGATE_TCP_TIMERS
139140
? static_cast<lock_base *>(multilock::create_new_lock(MULTILOCK_RECURSIVE, "tcp_con"))
140-
: static_cast<lock_base *>(&t_lock_dummy_socket));
141+
: static_cast<lock_base *>(&g_lock_dummy_socket.lock));
141142
}
142143

143144
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
@@ -473,6 +473,14 @@ class lock_dummy : public lock_base {
473473
int is_locked_by_me() override { return 1; }
474474
};
475475

476+
// Users of lock_dummy may wish to alignas(64) to place this lock in in a different cache-line and
477+
// prevent false sharing
478+
struct alignas(64) padded_lock_dummy {
479+
lock_dummy lock;
480+
// Padding to fill a full cache line
481+
char padding[64 - sizeof(lock_dummy)];
482+
};
483+
476484
static inline void lock_deleter_func(lock_base *lock)
477485
{
478486
lock->delete_obj();

tests/gtest/tcp/tcp_socket.cc

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include "common/sys.h"
1010
#include "common/base.h"
1111
#include "tcp_base.h"
12-
12+
#include <thread>
1313
class tcp_socket : public tcp_base {};
1414

1515
/**
@@ -125,3 +125,28 @@ TEST_F(tcp_socket, ti_2_ipv6only_listen_all)
125125
test_lambda(true);
126126
test_lambda(false);
127127
}
128+
129+
/**
130+
* @test tcp_socket.ti_3_socket_closed_different_thread_works
131+
* @brief
132+
* Test that a socket can be closed after its creator thread terminates
133+
* @details
134+
* Creates a socket in a separate thread, then closes it from the main thread
135+
* after the creator thread has terminated. This verifies that socket cleanup
136+
* works correctly across thread boundaries.
137+
*/
138+
TEST_F(tcp_socket, ti_3_socket_closed_different_thread_works)
139+
{
140+
int fd = -1;
141+
142+
std::thread t([&fd]() {
143+
fd = socket(m_family, SOCK_STREAM, IPPROTO_IP);
144+
EXPECT_LE(0, fd);
145+
EXPECT_EQ(errno, EOK);
146+
});
147+
148+
t.join();
149+
150+
EXPECT_LE(0, fd);
151+
close(fd);
152+
}

0 commit comments

Comments
 (0)