From dfd80c3e40b1fde37c25508a9d3fa3a0bdb3e7f6 Mon Sep 17 00:00:00 2001 From: Brian Neradt Date: Wed, 10 Dec 2025 02:30:06 +0000 Subject: [PATCH] Fix memory leak in Stripe destructor Add a destructor to the Stripe class to properly free the directory memory allocated in _init_directory(). The memory was allocated via ats_memalign or ats_alloc_hugepage but never freed when Stripe objects were destroyed. A new private member tracks whether hugepages were used so the destructor can call the appropriate free function. This was discovered via an infrequent ASAN failure in the cache_disk_replacement_stability regression test, which reported a ~27GB leak from 24 StripeSM objects that went out of scope without releasing their directory buffers. ``` ================================================================= ==5207==ERROR: LeakSanitizer: detected memory leaks Direct leak of 29272965120 byte(s) in 24 object(s) allocated from: #0 0x7f8b9cd1669c in __interceptor_posix_memalign (/lib64/libasan.so.6+0xb569c) #1 0xa69d99 in ats_memalign(unsigned long, unsigned long) ../src/tscore/ink_memory.cc:108 #2 0xfd96e2 in Stripe::_init_directory(unsigned long, int, int) ../src/iocore/cache/Stripe.cc:159 #3 0xfd905f in Stripe::Stripe(CacheDisk*, long, long, int, int) ../src/iocore/cache/Stripe.cc:97 #4 0xfdc7ef in StripeSM::StripeSM(CacheDisk*, long, long, int, int) ../src/iocore/cache/StripeSM.cc:120 #5 0xff8c81 in std::_MakeUniq::__single_object std::make_unique(CacheDisk*&&, long&&, int&&) (/tmp/ats-quiche/bin/traffic_server+0xff8c81) #6 0xff4797 in RegressionTest_cache_disk_replacement_stability(RegressionTest*, int, int*) ../src/iocore/cache/CacheTest.cc:458 #7 0xa4c994 in start_test ../src/tscore/Regression.cc:83 #8 0xa4cd6c in RegressionTest::run(char const*, int) ../src/tscore/Regression.cc:106 #9 0xa1a2a8 in mainEvent ../src/traffic_server/traffic_server.cc:1570 #10 0x9fd61a in Continuation::handleEvent(int, void*) ../include/iocore/eventsystem/Continuation.h:228 #11 0x13373e7 in EThread::process_event(Event*, int, long) ../src/iocore/eventsystem/UnixEThread.cc:171 #12 0x133809c in EThread::execute_regular() ../src/iocore/eventsystem/UnixEThread.cc:288 #13 0x1338a7b in EThread::execute() ../src/iocore/eventsystem/UnixEThread.cc:383 #14 0x13356e8 in spawn_thread_internal ../src/iocore/eventsystem/Thread.cc:75 #15 0x7f8b9abeb1c9 in start_thread (/lib64/libpthread.so.0+0x81c9) ``` --- src/iocore/cache/Stripe.cc | 16 ++++++++++++++++ src/iocore/cache/Stripe.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/src/iocore/cache/Stripe.cc b/src/iocore/cache/Stripe.cc index 825efe4c531..18518571fed 100644 --- a/src/iocore/cache/Stripe.cc +++ b/src/iocore/cache/Stripe.cc @@ -32,6 +32,7 @@ #include "tscore/ink_memory.h" #include +#include #include using CacheHTTPInfo = HTTPInfo; @@ -97,6 +98,18 @@ Stripe::Stripe(CacheDisk *disk, off_t blocks, off_t dir_skip, int avg_obj_size, this->_init_directory(this->dirlen(), this->headerlen(), DIRECTORY_FOOTER_SIZE); } +Stripe::~Stripe() +{ + if (this->directory.raw_dir != nullptr) { + if (this->_dir_uses_hugepages) { + ats_free_hugepage(this->directory.raw_dir, this->dirlen()); + } else { + ats_free(this->directory.raw_dir); + } + this->directory.raw_dir = nullptr; + } +} + void Stripe::_init_hash_text(CacheDisk const *disk, off_t blocks, off_t dir_skip) { @@ -154,6 +167,9 @@ Stripe::_init_directory(std::size_t directory_size, int header_size, int footer_ directory_size, (long long)this->len, percent(directory_size, this->len)); if (ats_hugepage_enabled()) { this->directory.raw_dir = static_cast(ats_alloc_hugepage(directory_size)); + if (this->directory.raw_dir != nullptr) { + this->_dir_uses_hugepages = true; + } } if (nullptr == this->directory.raw_dir) { this->directory.raw_dir = static_cast(ats_memalign(ats_pagesize(), directory_size)); diff --git a/src/iocore/cache/Stripe.h b/src/iocore/cache/Stripe.h index 7cf6f02482b..2a1465964a9 100644 --- a/src/iocore/cache/Stripe.h +++ b/src/iocore/cache/Stripe.h @@ -96,6 +96,8 @@ class Stripe */ Stripe(CacheDisk *disk, off_t blocks, off_t dir_skip, int avg_obj_size = -1, int fragment_size = -1); + ~Stripe(); + int dir_check(); uint32_t round_to_approx_size(uint32_t l) const; @@ -150,6 +152,8 @@ class Stripe bool flush_aggregate_write_buffer(int fd); private: + bool _dir_uses_hugepages{false}; + void _init_hash_text(CacheDisk const *disk, off_t blocks, off_t dir_skip); void _init_data(off_t store_block_size, int avg_obj_size = -1); void _init_data_internal(int avg_obj_size = -1); // Defaults to cache_config_min_average_object_size;