Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0e3f2f1
fix: prevent MemVector UAF in CacheBuffer by locking m_mem access
raakella1 Sep 17, 2026
bd44021
fix: also use get_memvec_intrusive in writeBack_cache refresh_buf
raakella1 Sep 17, 2026
df918e1
Revert "fix: also use get_memvec_intrusive in writeBack_cache refresh…
raakella1 Sep 17, 2026
ee7c5e7
cache: replace std::shared_mutex with folly::SharedMutexReadPriority …
raakella1 Sep 17, 2026
fd852ba
cache: fix CacheBuffer::m_mem data race (Race A) and add regression t…
shosseinimotlagh Sep 23, 2026
fd886bd
Merge pull request #1 from shosseinimotlagh/fix/memvec-uaf-race
raakella1 Sep 23, 2026
7734123
cache: keep MemVector alive through at_offset()'s returned blob
raakella1 Sep 28, 2026
c4fb386
Merge pull request #2 from raakella1/fix/memvec-uaf-blob-view
raakella1 Sep 29, 2026
20fff05
use folly::shared_mutex which is just 4 bytes in size
raakella1 Sep 29, 2026
08984ab
disable the release_cache_after_recovery flag by default
raakella1 Sep 30, 2026
571a553
conanfile: fix nlohmann_json range and add openssl override
shosseinimotlagh Oct 1, 2026
8e431ee
conanfile: bump iomgr to 8.8.7
shosseinimotlagh Oct 1, 2026
3528e52
logstore: fix use-after-free in on_write_completion
shosseinimotlagh Oct 1, 2026
788aaf1
test_wb_cache_integration: fix crash/leak in teardown
shosseinimotlagh Oct 1, 2026
de7ef0d
update locks
raakella1 Oct 1, 2026
9e829fa
blkstore: use dynamic_pointer_cast in to_blkstore_req to fix UBSan do…
shosseinimotlagh Oct 1, 2026
30dfe61
test_wb_cache_integration: use MappingKey/MappingValue to match index…
shosseinimotlagh Oct 1, 2026
a0afb8c
test_wb_cache_integration: initialize MappingValue with valid BlkId
shosseinimotlagh Oct 1, 2026
16130e6
test_wb_cache_integration: avoid duplicate-key writes to VAR_VALUE bt…
shosseinimotlagh Oct 1, 2026
c4ffdb6
log_dev: fix heap-use-after-free in append_async on data.size post-cr…
shosseinimotlagh Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

class HomestoreConan(ConanFile):
name = "homestore"
version = "3.8.7"
version = "3.8.8"

homepage = "https://github.corp.ebay.com/SDS/homestore"
description = "HomeStore"
Expand Down Expand Up @@ -55,16 +55,17 @@ def build_requirements(self):
self.test_requires("gtest/1.15.0")

def requirements(self):
self.requires("iomgr/8.8.4")
self.requires("sisl/8.9.6")
self.requires("iomgr/8.8.7")
self.requires("sisl/8.9.8")

# FOSS, rarely updated
self.requires("boost/1.79.0")
self.requires("farmhash/cci.20190513@")
self.requires("folly/2022.01.31.00")
self.requires("isa-l/2.30.0")
self.requires("nlohmann_json/3.12.0")
self.requires("nlohmann_json/[^3.11]")
self.requires("spdk/nbi.21.07.y")
self.requires("openssl/1.1.1w", override=True)

def build(self):
cmake = CMake(self)
Expand Down
25 changes: 11 additions & 14 deletions locks/base.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"graph_lock": {
"nodes": {
"0": {
"ref": "homestore/3.8.7",
"ref": "homestore/3.8.8",
"requires": [
"1",
"2",
Expand All @@ -21,7 +21,7 @@
"context": "host"
},
"1": {
"ref": "iomgr/8.8.4#8b6efbb0f5a6dbfa21d54d333d4c85f7",
"ref": "iomgr/8.8.7#a8b556d68402d7704b82287b1a8c9b72",
"requires": [
"2",
"3",
Expand All @@ -37,7 +37,7 @@
"context": "host"
},
"2": {
"ref": "sisl/8.9.6#8ad5a667b06c1a75d1f8efab55a840ce",
"ref": "sisl/8.9.8#85065452eb527275f3a889e2918d4881",
"requires": [
"3",
"7",
Expand Down Expand Up @@ -70,11 +70,11 @@
"context": "host"
},
"5": {
"ref": "bzip2/1.0.8#411fc05e80d47a89045edc1ee6f23c1d",
"ref": "bzip2/1.0.8#8779e3ee0cf27649212cd3fc0db4438b",
"context": "host"
},
"6": {
"ref": "libbacktrace/cci.20210118#ec1aa63bbc10145c6a299e68e711670c",
"ref": "libbacktrace/cci.20210118#f5d18dee9942ec36058b965afa867e7b",
"context": "host"
},
"7": {
Expand Down Expand Up @@ -105,10 +105,7 @@
"context": "host"
},
"11": {
"ref": "openssl/3.6.0#89e8af1d4a21afcac0557079d23d8890",
"requires": [
"4"
],
"ref": "openssl/1.1.1w#c5ede42cc8ea41086b99aa237ad138d2",
"context": "host"
},
"12": {
Expand Down Expand Up @@ -143,11 +140,11 @@
"context": "host"
},
"15": {
"ref": "double-conversion/3.2.0#dba4f6ac0cfbfb3ab6f0b6bd4130af55",
"ref": "double-conversion/3.2.0#640e35791a4bac95b0545e2f54b7aceb",
"context": "host"
},
"16": {
"ref": "gflags/2.2.2#48d1262ffac8d30c3224befb8275a533",
"ref": "gflags/2.2.2#b15c28c567c7ade7449cf994168a559f",
"context": "host"
},
"17": {
Expand All @@ -167,7 +164,7 @@
"context": "host"
},
"19": {
"ref": "xz_utils/5.2.5#774a53815bc66047a56ef8470a144a91",
"ref": "xz_utils/5.2.5#a6d90890193dc851fa0d470163271c7a",
"context": "host"
},
"20": {
Expand Down Expand Up @@ -202,11 +199,11 @@
"context": "host"
},
"26": {
"ref": "libsodium/1.0.18#9e310e52ed60084484c5f640ab88883a",
"ref": "libsodium/1.0.18#7429a9e5351cc67bea3537229921714d",
"context": "host"
},
"27": {
"ref": "libiberty/9.1.0#a6a0b042484db9cefa233a8aa31a33c5",
"ref": "libiberty/9.1.0#3060045a116b0fff6d4937b0fc9cfc0e",
"context": "host"
},
"28": {
Expand Down
107 changes: 52 additions & 55 deletions locks/debug_deps.lock

Large diffs are not rendered by default.

107 changes: 52 additions & 55 deletions locks/release_deps.lock

Large diffs are not rendered by default.

103 changes: 50 additions & 53 deletions locks/sanitize_deps.lock

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions src/api/blob_view.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*********************************************************************************
* Modifications Copyright 2017-2019 eBay Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*********************************************************************************/
#pragma once

/* NOTE: This file must stay dependency-free (only <memory> and sisl::blob) so it can be
* included both from public interface headers (e.g. vol_interface.hpp, which must avoid
* including homestore-internal headers) and from internal engine headers.
*/

#include <memory>

#include <sisl/fds/buffer.hpp>

namespace homestore {

/* A sisl::blob that also carries a type-erased ownership token. As long as a blob_view
* instance is alive, m_holder keeps whatever backing memory blob.bytes points into alive too
* (whatever that memory actually is is opaque here on purpose). Callers should bind the
* result of an at_offset()-style accessor to `auto` so this token is not sliced away. */
struct blob_view : public sisl::blob {
std::shared_ptr< void > m_holder;
};

} // namespace homestore
4 changes: 3 additions & 1 deletion src/api/vol_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
#include <engine/common/homestore_header.hpp>
#include <engine/device/device.h>

#include "blob_view.hpp"

namespace homestore {
class Volume;
class Snapshot;
Expand Down Expand Up @@ -319,7 +321,7 @@ class VolInterface {
virtual std::map< boost::uuids::uuid, uint64_t > get_used_size(const VolumePtr& vol) = 0;
virtual uint64_t get_page_size(const VolumePtr& vol) = 0;
virtual boost::uuids::uuid get_uuid(std::shared_ptr< Volume > vol) = 0;
virtual sisl::blob at_offset(const boost::intrusive_ptr< BlkBuffer >& buf, uint32_t offset) = 0;
virtual blob_view at_offset(const boost::intrusive_ptr< BlkBuffer >& buf, uint32_t offset) = 0;
virtual VolumePtr create_volume(const vol_params& params) = 0;
virtual std::error_condition remove_volume(const boost::uuids::uuid& uuid,
const hs_comp_callback& shutdown_done_cb = nullptr) = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/engine/blkstore/blkstore.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ struct blkstore_req : public virtualdev_req {
}
};

static auto to_blkstore_req(auto& req) { return boost::static_pointer_cast< blkstore_req< Buffer > >(req); }
static auto to_blkstore_req(auto& req) { return boost::dynamic_pointer_cast< blkstore_req< Buffer > >(req); }

static boost::intrusive_ptr< blkstore_req< Buffer > > make_request() {
return boost::intrusive_ptr< blkstore_req< Buffer > >(
Expand Down
61 changes: 52 additions & 9 deletions src/engine/cache/cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <limits>
#include <memory>
#include <mutex>
#include <folly/SharedMutex.h>
#include <sstream>
#include <string>
#include <type_traits>
Expand All @@ -36,6 +37,7 @@
#include <sisl/utility/enum.hpp>
#include <sisl/utility/obj_life_counter.hpp>

#include "api/blob_view.hpp"
#include "engine/common/homestore_assert.hpp"
#include "engine/common/homestore_config.hpp"
#include "engine/homeds/hash/intrusive_hashset.hpp"
Expand All @@ -45,6 +47,12 @@

SISL_LOGGING_DECL(cache, cache_vmod_evict, cache_vmod_read, cache_vmod_write)

#ifdef _PRERELEASE
#include <chrono>
#include <thread>
#include "engine/common/homestore_flip.hpp"
#endif

namespace homestore {

#define CurrentEvictor LRUEvictor
Expand Down Expand Up @@ -208,6 +216,7 @@ class CacheBuffer : public CacheRecord {
bool recovered;
#endif
K m_key; // Key to access this cache
mutable folly::SharedMutexReadPriority m_mem_mtx; // protects m_mem and m_data_offset (4 bytes vs 56 for std::shared_mutex)
boost::intrusive_ptr< homeds::MemVector > m_mem; // Memory address which is what this buffer contained with
sisl::atomic_counter< uint32_t > m_refcount; // Refcount
uint32_t m_data_offset; // offset in m_mem that it points to
Expand Down Expand Up @@ -302,18 +311,41 @@ class CacheBuffer : public CacheRecord {
uint32_t get_data_offset() const { return m_data_offset; }

bool update_missing_piece(const uint32_t offset, const uint32_t size, uint8_t* const ptr) {
const bool inserted{get_memvec().update_missing_piece(m_data_offset + offset, size, ptr, [this]() { init(); })};
boost::intrusive_ptr< homeds::MemVector > mv;
uint32_t data_offset;
{
std::shared_lock< folly::SharedMutexReadPriority > lk{m_mem_mtx};
mv = m_mem;
data_offset = m_data_offset;
}
#ifdef _PRERELEASE
if (homestore_flip->test_flip("cache_buf_update_before_use")) {
std::this_thread::sleep_for(std::chrono::microseconds(1));
}
#endif
const bool inserted{mv->update_missing_piece(data_offset + offset, size, ptr, [this]() { init(); })};
return inserted;
}

uint32_t insert_missing_pieces(const uint32_t offset, const uint32_t size_to_read,
std::vector< std::pair< uint32_t, uint32_t > >& missing_mp) {
const uint32_t inserted_size{
get_memvec().insert_missing_pieces(m_data_offset + offset, size_to_read, missing_mp)};
boost::intrusive_ptr< homeds::MemVector > mv;
uint32_t data_offset;
{
std::shared_lock< folly::SharedMutexReadPriority > lk{m_mem_mtx};
mv = m_mem;
data_offset = m_data_offset;
}
#ifdef _PRERELEASE
if (homestore_flip->test_flip("wb_cache_get_memvec_before_use")) {
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
#endif
const uint32_t inserted_size{mv->insert_missing_pieces(data_offset + offset, size_to_read, missing_mp)};
/* it should return a relative offset */
for (auto& missing_mp : missing_mp) {
assert(missing_mp.first >= m_data_offset);
missing_mp.first -= m_data_offset;
for (auto& mp : missing_mp) {
assert(mp.first >= data_offset);
mp.first -= data_offset;
}

return inserted_size;
Expand All @@ -325,6 +357,7 @@ class CacheBuffer : public CacheRecord {

void set_memvec(boost::intrusive_ptr< homeds::MemVector > vec, const uint32_t offset, const uint32_t size) {
HS_DBG_ASSERT_LE(size, UINT16_MAX);
std::unique_lock< folly::SharedMutexReadPriority > lk{m_mem_mtx};
m_mem = std::move(vec);
m_data_offset = offset;
m_cache_size = size;
Expand Down Expand Up @@ -352,11 +385,21 @@ class CacheBuffer : public CacheRecord {
return m_mem;
}

sisl::blob at_offset(const uint32_t offset) const {
sisl::blob b;
blob_view at_offset(const uint32_t offset) const {
boost::intrusive_ptr< homeds::MemVector > mv;
uint32_t data_offset;
{
std::shared_lock< folly::SharedMutexReadPriority > lk{m_mem_mtx};
mv = m_mem;
data_offset = m_data_offset;
}
blob_view b;
b.bytes = nullptr;
b.size = 0;
get_memvec().get(&b, m_data_offset + offset);
mv->get(&b, data_offset + offset);
// keep MemVector alive for as long as the caller holds this blob_view, not just
// for the duration of this accessor call (see api/blob_view.hpp)
b.m_holder = std::make_shared< boost::intrusive_ptr< homeds::MemVector > >(std::move(mv));
return b;
}

Expand Down
15 changes: 15 additions & 0 deletions src/engine/homeds/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ if (${non_io_tests})
target_sources(test_btree_crud PRIVATE tests/test_btree_crud.cpp)
target_link_libraries(test_btree_crud homestore ${COMMON_TEST_DEPS})

add_executable(test_wb_cache_race)
target_sources(test_wb_cache_race PRIVATE tests/test_wb_cache_race.cpp)
target_link_libraries(test_wb_cache_race homestore ${COMMON_TEST_DEPS})
# SIMULATE_WB_MEM_RACE=ON: activates the unfixed wb_req->m_mem code path (no mutex) for Race B reproduction.
option(SIMULATE_WB_MEM_RACE "Activate unfixed wb_req->m_mem path (no mutex) for Race B reproduction" OFF)
if(SIMULATE_WB_MEM_RACE)
target_compile_definitions(test_wb_cache_race PRIVATE SIMULATE_WB_MEM_RACE)
endif()

add_executable(test_hash)
target_sources(test_hash PRIVATE tests/test_hashset.cpp)
target_link_libraries(test_hash ${COMMON_TEST_DEPS} benchmark::benchmark)
Expand All @@ -50,6 +59,10 @@ if (${non_io_tests})
target_compile_options(test_load PRIVATE -Wno-deprecated)
target_link_libraries(test_load homeblks ${COMMON_TEST_DEPS})

add_executable(test_wb_cache_integration)
target_sources(test_wb_cache_integration PRIVATE tests/test_wb_cache_integration.cpp)
target_link_libraries(test_wb_cache_integration homeblks ${COMMON_TEST_DEPS})

add_executable(test_iomgr_exec)
target_sources(test_iomgr_exec PRIVATE
tests/test_iomgr_executor.cpp
Expand All @@ -62,6 +75,8 @@ if (${non_io_tests})
target_sources(test_threadpool PRIVATE tests/test_thread_pool.cpp)
target_link_libraries(test_threadpool ${COMMON_TEST_DEPS})
endif()
add_test(NAME WbCacheRace COMMAND ${CMAKE_SOURCE_DIR}/test_wrap.sh ${CMAKE_BINARY_DIR}/bin/test_wb_cache_race)
add_test(NAME WbCacheIntegration COMMAND ${CMAKE_SOURCE_DIR}/test_wrap.sh ${CMAKE_BINARY_DIR}/bin/test_wb_cache_integration)
add_test(NAME BTreeCRUD COMMAND ${CMAKE_SOURCE_DIR}/test_wrap.sh ${CMAKE_BINARY_DIR}/bin/test_btree_crud)
add_test(NAME Hash COMMAND ${CMAKE_SOURCE_DIR}/test_wrap.sh ${CMAKE_BINARY_DIR}/bin/test_hash)
add_test(NAME Avector COMMAND ${CMAKE_SOURCE_DIR}/test_wrap.sh ${CMAKE_BINARY_DIR}/bin/test_avector)
Expand Down
4 changes: 2 additions & 2 deletions src/engine/homeds/btree/ssd_btree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ class SSDBtreeStore {

static uint8_t* get_physical(const SSDBtreeNode* const bn) {
const wb_cache_buffer_t* const bbuf{static_cast< const wb_cache_buffer_t* >(bn)};
const sisl::blob b{bbuf->at_offset(0)};
const auto b{bbuf->at_offset(0)};
return b.bytes;
}

Expand Down Expand Up @@ -290,7 +290,7 @@ class SSDBtreeStore {
_init_node(SSDBtreeStore* store, auto& safe_buf, bool is_leaf, const BlkId& blkid,
const boost::intrusive_ptr< SSDBtreeNode >& copy_from = nullptr) {
// Access the physical node buffer and initialize it
sisl::blob b = safe_buf->at_offset(0);
auto b = safe_buf->at_offset(0);
HS_DBG_ASSERT_EQ(b.size, store->get_node_size());
if (is_leaf) {
bnodeid_t bid = blkid.to_integer();
Expand Down
5 changes: 5 additions & 0 deletions src/engine/homeds/btree/writeBack_cache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,11 @@ class WriteBackCache : public std::enable_shared_from_this< WriteBackCache< K, V
auto shared_this = this->shared_from_this();
queue_flush_buffers([shared_this, cp_id, it = m_req_list[cp_id]->begin(true /* latest */),
bt_cp_id = bcp->cp_id, cbq_id = s_cbq_id]() mutable -> bool {
#ifdef _PRERELEASE
if (homestore_flip->test_flip("wb_flush_delay_before_write")) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
#endif
size_t write_count{0};
size_t dep_wait_count{0};

Expand Down
Loading
Loading