Skip to content

Commit 3646343

Browse files
committed
Bug#36081753 use-after-free in TcpPortPool
When built with ASAN, a use-after-free is reported for the TcpPortPool. AddressSanitizer: heap-use-after-free on address 0x60200019f190 at pc 0x00000076a18d bp 0x7fff51e7d1d0 sp 0x7fff51e7d1c0 #4 0x770b73 in UniqueId::ProcessUniqueIds::erase(unsigned int) ../router/tests/helpers/tcp_port_pool.h:112 #5 0x770c48 in UniqueId::~UniqueId() ../router/tests/helpers/tcp_port_pool.cc:234 ... #12 0x82faa3 in testing::UnitTest::~UnitTest() ../extra/googletest/googletest-release-1.12.0/googletest/src/gtest.cc:5496 #13 0x7f5fe085ace8 in __run_exit_handlers (/lib64/libc.so.6+0x39ce8) 0x60200019f190 is located 0 bytes inside of 16-byte region [0x60200019f190,0x60200019f1a0) freed by thread T0 here: #0 0x7f5fe3cbd10f in operator delete(void*, unsigned long) (/lib64/libasan.so.6+0xb710f) #1 0x7f5fe085ace8 in __run_exit_handlers (/lib64/libc.so.6+0x39ce8) Background ========== __run_exit_handlers destroys "static" and "global" variables in reverse order of their creation. googletest's unit-tests are a static, and the TcpPortPool also has ProcessUniqueId's which contains the process-wide unique-ids. At construct: unittest -> tcp-port-pool -> proces-unique-ids At destruct : process-unique-ids -> tcp-port-pool -> 💥 The use-after-free happens as the process-unique-ids static is destructed before the tcp-port-pool which tries to its Ids from the process-unique-ids. Change ====== - extend the lifetime of the process-unique-ids to after the last use of the tcp-port-pool via a std::shared_ptr<> Change-Id: I75b8b781e1d240f18ca72f2c86182639a7699f06
1 parent 3a492fe commit 3646343

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

router/tests/helpers/tcp_port_pool.cc

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@
3434
#endif
3535

3636
#include <cstring>
37+
#include <memory> // shared_ptr
3738
#include <system_error>
38-
#include <utility>
39+
#include <utility> // move
3940

4041
#include "mysql/harness/filesystem.h" // Path
4142
#include "mysql/harness/stdx/expected.h"
@@ -199,7 +200,8 @@ std::string UniqueId::get_lock_file_dir() {
199200
#endif
200201
}
201202

202-
UniqueId::UniqueId(value_type start_from, value_type range) {
203+
UniqueId::UniqueId(value_type start_from, value_type range)
204+
: proc_ids_(process_unique_ids()) {
203205
const std::string lock_file_dir = get_lock_file_dir();
204206
mysql_harness::mkdir(lock_file_dir, 0777);
205207
#ifndef _WIN32
@@ -209,7 +211,7 @@ UniqueId::UniqueId(value_type start_from, value_type range) {
209211
#endif
210212

211213
for (auto i = start_from; i < start_from + range; i++) {
212-
if (process_unique_ids().contains(i)) continue;
214+
if (proc_ids_->contains(i)) continue;
213215

214216
Path lock_file_path(lock_file_dir);
215217
lock_file_path.append(std::to_string(i));
@@ -218,7 +220,7 @@ UniqueId::UniqueId(value_type start_from, value_type range) {
218220
if (lock_res) {
219221
id_ = i;
220222

221-
process_unique_ids().insert(i);
223+
proc_ids_->insert(i);
222224

223225
// obtained the lock, we are good to go
224226
return;
@@ -231,12 +233,29 @@ UniqueId::UniqueId(value_type start_from, value_type range) {
231233
UniqueId::~UniqueId() {
232234
// release the process unique-id if we own one.
233235
if (id_) {
234-
process_unique_ids().erase(*id_);
236+
proc_ids_->erase(*id_);
235237
}
236238
}
237239

238-
UniqueId::ProcessUniqueIds &UniqueId::process_unique_ids() {
239-
static ProcessUniqueIds ids;
240+
// process-wide unique-ids
241+
//
242+
// is a "static shared_ptr<>" instead of a "static" as the TcpPort may be part
243+
// of a "static" too.
244+
//
245+
// It would create:
246+
// 1. (static) TcpPortPool
247+
// 2. static ProcessUniqueIds
248+
//
249+
// ... and then at destruct in reverse order:
250+
// * ProcessUniqueIds
251+
// * TcpPortPool ... but the TcpPortPool would try to remove itself from the
252+
// ProcessUniqueIds
253+
//
254+
//
255+
//
256+
std::shared_ptr<UniqueId::ProcessUniqueIds> UniqueId::process_unique_ids() {
257+
static std::shared_ptr<UniqueId::ProcessUniqueIds> ids =
258+
std::make_shared<UniqueId::ProcessUniqueIds>();
240259

241260
return ids;
242261
}

router/tests/helpers/tcp_port_pool.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#define _TCP_PORT_POOL_H_
2727

2828
#include <cstdint>
29+
#include <memory>
2930
#include <optional>
3031
#include <string>
3132
#include <system_error>
@@ -120,10 +121,12 @@ class UniqueId {
120121
UniqueId(const UniqueId &) = delete;
121122
UniqueId &operator=(const UniqueId &) = delete;
122123
UniqueId(UniqueId &&other) noexcept
123-
: id_(std::exchange(other.id_, {})),
124+
: proc_ids_(std::move(other.proc_ids_)),
125+
id_(std::exchange(other.id_, {})),
124126
lock_file_fd_(std::exchange(other.lock_file_fd_, {})) {}
125127

126128
UniqueId &operator=(UniqueId &&other) noexcept {
129+
proc_ids_ = std::move(other.proc_ids_);
127130
id_ = std::exchange(other.id_, {});
128131
lock_file_fd_ = std::exchange(other.lock_file_fd_, {});
129132

@@ -137,7 +140,9 @@ class UniqueId {
137140
private:
138141
stdx::expected<void, std::error_code> lock_file(const std::string &file_name);
139142

140-
static ProcessUniqueIds &process_unique_ids();
143+
static std::shared_ptr<UniqueId::ProcessUniqueIds> process_unique_ids();
144+
145+
std::shared_ptr<UniqueId::ProcessUniqueIds> proc_ids_;
141146

142147
static std::string get_lock_file_dir();
143148

0 commit comments

Comments
 (0)