Skip to content

Commit e34d26d

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 daa2a8e commit e34d26d

File tree

4 files changed

+42
-36
lines changed

4 files changed

+42
-36
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 *>(&t_lock_dummy_ring));
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 *>(&t_lock_dummy_socket));
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: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,7 @@
11
/*
22
* SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES
3-
* Copyright (c) 2001-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4-
* SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause
5-
*
6-
* This software is available to you under a choice of one of two
7-
* licenses. You may choose to be licensed under the terms of the GNU
8-
* General Public License (GPL) Version 2, available from the file
9-
* COPYING in the main directory of this source tree, or the
10-
* BSD license below:
11-
*
12-
* Redistribution and use in source and binary forms, with or
13-
* without modification, are permitted provided that the following
14-
* conditions are met:
15-
*
16-
* - Redistributions of source code must retain the above
17-
* copyright notice, this list of conditions and the following
18-
* disclaimer.
19-
*
20-
* - Redistributions in binary form must reproduce the above
21-
* copyright notice, this list of conditions and the following
22-
* disclaimer in the documentation and/or other materials
23-
* provided with the distribution.
24-
*
25-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26-
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27-
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28-
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29-
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30-
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31-
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32-
* SOFTWARE.
3+
* Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4+
* SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
335
*/
346

357
#ifndef LOCK_WRAPPER_H
@@ -501,6 +473,14 @@ class lock_dummy : public lock_base {
501473
int is_locked_by_me() override { return 1; }
502474
};
503475

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+
504484
static inline void lock_deleter_func(lock_base *lock)
505485
{
506486
lock->delete_obj();
@@ -550,4 +530,4 @@ class multilock {
550530
std::unique_ptr<lock_base, lock_deleter> m_lock;
551531
};
552532

553-
#endif // LOCK_WRAPPER_H
533+
#endif // LOCK_WRAPPER_H

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)