From 7783c177ad16621efc52594a14b74800d958d3fe Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Mon, 10 Jun 2024 13:15:18 +0100 Subject: [PATCH 01/26] Added a template to the metadata This commit adds the storage of client meta to the configuration. This is not exposed in this commit. --- src/snmalloc/backend/backend.h | 21 +++++---- src/snmalloc/backend/fixedglobalconfig.h | 12 ++++-- src/snmalloc/backend/globalconfig.h | 9 ++-- src/snmalloc/backend_helpers/commonconfig.h | 27 ++++++++++++ .../backend_helpers/defaultpagemapentry.h | 6 ++- src/snmalloc/mem/corealloc.h | 15 +++++-- src/snmalloc/mem/metadata.h | 43 ++++++++++++++++++- src/test/func/domestication/domestication.cc | 2 +- 8 files changed, 112 insertions(+), 23 deletions(-) diff --git a/src/snmalloc/backend/backend.h b/src/snmalloc/backend/backend.h index ce5e757ed..ac55d62a8 100644 --- a/src/snmalloc/backend/backend.h +++ b/src/snmalloc/backend/backend.h @@ -23,9 +23,6 @@ namespace snmalloc using Pal = PAL; using SlabMetadata = typename PagemapEntry::SlabMetadata; - static constexpr size_t SizeofMetadata = - bits::next_pow2_const(sizeof(SlabMetadata)); - public: /** * Provide a block of meta-data with size and align. @@ -91,12 +88,18 @@ namespace snmalloc * where slab_metadata, is the second element of the pair return. */ static std::pair, SlabMetadata*> - alloc_chunk(LocalState& local_state, size_t size, uintptr_t ras) + alloc_chunk(LocalState& local_state, size_t size, uintptr_t ras, size_t extra_bytes = 0) { SNMALLOC_ASSERT(bits::is_pow2(size)); SNMALLOC_ASSERT(size >= MIN_CHUNK_SIZE); - auto meta_cap = local_state.get_meta_range().alloc_range(SizeofMetadata); + auto meta_size = bits::next_pow2(sizeof(SlabMetadata) + extra_bytes); + +#ifdef SNMALLOC_TRACING + message<1024>("Allocating metadata of size: {} ({})", meta_size, extra_bytes); +#endif + + auto meta_cap = local_state.get_meta_range().alloc_range(meta_size); auto meta = meta_cap.template as_reinterpret().unsafe_ptr(); @@ -113,7 +116,7 @@ namespace snmalloc #endif if (p == nullptr) { - local_state.get_meta_range().dealloc_range(meta_cap, SizeofMetadata); + local_state.get_meta_range().dealloc_range(meta_cap, meta_size); errno = ENOMEM; #ifdef SNMALLOC_TRACING message<1024>("Out of memory"); @@ -140,7 +143,8 @@ namespace snmalloc LocalState& local_state, SlabMetadata& slab_metadata, capptr::Alloc alloc, - size_t size) + size_t size, + size_t extra_bytes = 0) { /* * The backend takes possession of these chunks now, by disassociating @@ -167,8 +171,9 @@ namespace snmalloc */ capptr::Arena arena = Authmap::amplify(alloc); + auto meta_size = bits::next_pow2(sizeof(SlabMetadata) + extra_bytes); local_state.get_meta_range().dealloc_range( - capptr::Arena::unsafe_from(&slab_metadata), SizeofMetadata); + capptr::Arena::unsafe_from(&slab_metadata), meta_size); local_state.get_object_range()->dealloc_range(arena, size); } diff --git a/src/snmalloc/backend/fixedglobalconfig.h b/src/snmalloc/backend/fixedglobalconfig.h index 37a78c287..99f175f1d 100644 --- a/src/snmalloc/backend/fixedglobalconfig.h +++ b/src/snmalloc/backend/fixedglobalconfig.h @@ -8,11 +8,13 @@ namespace snmalloc /** * A single fixed address range allocator configuration */ - template + template< + SNMALLOC_CONCEPT(IsPAL) PAL, + typename ClientMetaDataProvider = NoClientMetaDataProvider> class FixedRangeConfig final : public CommonConfig { public: - using PagemapEntry = DefaultPagemapEntry; + using PagemapEntry = DefaultPagemapEntry; private: using ConcretePagemap = @@ -63,11 +65,13 @@ namespace snmalloc * C++, and not just its initializer fragment, to initialize a non-prefix * subset of the flags (in any order, at that). */ - static constexpr Flags Options = []() constexpr { + static constexpr Flags Options = []() constexpr + { Flags opts = {}; opts.HasDomesticate = true; return opts; - }(); + } + (); // This needs to be a forward reference as the // thread local state will need to know about this. diff --git a/src/snmalloc/backend/globalconfig.h b/src/snmalloc/backend/globalconfig.h index 525c77275..2cbda567d 100644 --- a/src/snmalloc/backend/globalconfig.h +++ b/src/snmalloc/backend/globalconfig.h @@ -28,13 +28,14 @@ namespace snmalloc * The Configuration sets up a Pagemap for the backend to use, and the state * required to build new allocators (GlobalPoolState). */ - class StandardConfig final : public CommonConfig + template + class StandardConfigClientMeta final : public CommonConfig { - using GlobalPoolState = PoolState>; + using GlobalPoolState = PoolState>>; public: using Pal = DefaultPal; - using PagemapEntry = DefaultPagemapEntry; + using PagemapEntry = DefaultPagemapEntry; private: using ConcretePagemap = @@ -163,6 +164,8 @@ namespace snmalloc } }; + using StandardConfig = StandardConfigClientMeta; + /** * Create allocator type for this configuration. */ diff --git a/src/snmalloc/backend_helpers/commonconfig.h b/src/snmalloc/backend_helpers/commonconfig.h index a69b6a389..28eeff9ee 100644 --- a/src/snmalloc/backend_helpers/commonconfig.h +++ b/src/snmalloc/backend_helpers/commonconfig.h @@ -95,6 +95,33 @@ namespace snmalloc bool HasDomesticate = false; }; + struct NoClientMetaDataProvider + { + using StorageType = Empty; + + static size_t required_count(size_t) { + return 1; + } + + static Empty& get(Empty* base, size_t) { + return *base; + } + }; + + template + struct ArrayClientMetaDataProvider + { + using StorageType = T; + + static size_t required_count(size_t max_count) { + return max_count; + } + + static T& get(StorageType* base, size_t index) { + return base[index]; + } + }; + /** * Class containing definitions that are likely to be used by all except for * the most unusual back-end implementations. This can be subclassed as a diff --git a/src/snmalloc/backend_helpers/defaultpagemapentry.h b/src/snmalloc/backend_helpers/defaultpagemapentry.h index 2083db30e..50b85eb11 100644 --- a/src/snmalloc/backend_helpers/defaultpagemapentry.h +++ b/src/snmalloc/backend_helpers/defaultpagemapentry.h @@ -64,9 +64,11 @@ namespace snmalloc SNMALLOC_FAST_PATH DefaultPagemapEntryT() = default; }; - class DefaultSlabMetadata : public FrontendSlabMetadata + template + class DefaultSlabMetadata : public FrontendSlabMetadata, ClientMetaDataProvider> {}; - using DefaultPagemapEntry = DefaultPagemapEntryT; + template + using DefaultPagemapEntry = DefaultPagemapEntryT>; } // namespace snmalloc diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index 84b34ab97..7cbcd2b1e 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -364,11 +364,15 @@ namespace snmalloc // don't touch the cache lines at this point in snmalloc_check_client. auto start = clear_slab(meta, sizeclass); + // Calculate the extra bytes required to store the client meta-data. + size_t extra_bytes = BackendSlabMetadata::get_extra_bytes(sizeclass); + Config::Backend::dealloc_chunk( get_backend_local_state(), *meta, start, - sizeclass_to_slab_size(sizeclass)); + sizeclass_to_slab_size(sizeclass), + extra_bytes); }); } @@ -401,7 +405,7 @@ namespace snmalloc meta->node.remove(); Config::Backend::dealloc_chunk( - get_backend_local_state(), *meta, p, size); + get_backend_local_state(), *meta, p, size, 0); return; } @@ -792,11 +796,16 @@ namespace snmalloc message<1024>("small_alloc_slow rsize={} slab size={}", rsize, slab_size); #endif + // Calculate the extra bytes required to store the client meta-data. + size_t extra_bytes = BackendSlabMetadata::get_extra_bytes(sizeclass); + message<1024>("extra_bytes={}, sizeclass={}", extra_bytes, sizeclass); + auto [slab, meta] = Config::Backend::alloc_chunk( get_backend_local_state(), slab_size, PagemapEntry::encode( - public_state(), sizeclass_t::from_small_class(sizeclass))); + public_state(), sizeclass_t::from_small_class(sizeclass)), + extra_bytes); if (slab == nullptr) { diff --git a/src/snmalloc/mem/metadata.h b/src/snmalloc/mem/metadata.h index e7937e9d8..fb2b921ed 100644 --- a/src/snmalloc/mem/metadata.h +++ b/src/snmalloc/mem/metadata.h @@ -368,7 +368,7 @@ namespace snmalloc class FrontendSlabMetadata_Trait { private: - template + template friend class FrontendSlabMetadata; // Can only be constructed by FrontendSlabMetadata @@ -379,10 +379,15 @@ namespace snmalloc * The FrontendSlabMetadata represent the metadata associated with a single * slab. */ - template + template class FrontendSlabMetadata : public FrontendSlabMetadata_Trait { public: + /** + * Type that encapsulates logic for accessing client meta-data. + */ + using ClientMeta = ClientMeta_; + /** * Used to link slab metadata together in various other data-structures. * This is used with `SeqSet` and so may actually hold a subclass of this @@ -424,6 +429,13 @@ namespace snmalloc */ bool large_ = false; + /** + * Stores client meta-data for this slab. This must be last element in the + * slab. The meta data will actually allocate multiple elements after this + * type, so that client_meta_[1] will work for the required meta-data size. + */ + SNMALLOC_NO_UNIQUE_ADDRESS typename ClientMeta::StorageType client_meta_; + uint16_t& needed() { return needed_; @@ -452,6 +464,9 @@ namespace snmalloc set_sleeping(sizeclass, 0); large_ = false; + + new (&client_meta_) + typename ClientMeta::StorageType[get_client_storage_count(sizeclass)]; } /** @@ -469,6 +484,8 @@ namespace snmalloc // Jump to slow path on first deallocation. needed() = 1; + + new (&client_meta_) typename ClientMeta::StorageType(); } /** @@ -583,6 +600,28 @@ namespace snmalloc { return address_cast(free_queue.read_head(0, key)); } + + typename ClientMeta::StorageType* get_client_meta_base() + { + return &client_meta_; + } + + static size_t get_client_storage_count(smallsizeclass_t sizeclass) + { + auto count = sizeclass_to_slab_object_count(sizeclass); + auto result = ClientMeta::required_count(count); + if (result == 0) + return 1; + return result; + } + + static size_t get_extra_bytes(smallsizeclass_t sizeclass) + { + // We remove one from the extra-bytes as there is one in the metadata to + // start with. + return (get_client_storage_count(sizeclass) - 1) * + sizeof(typename ClientMeta::StorageType); + } }; /** diff --git a/src/test/func/domestication/domestication.cc b/src/test/func/domestication/domestication.cc index 8ff4fa977..478099616 100644 --- a/src/test/func/domestication/domestication.cc +++ b/src/test/func/domestication/domestication.cc @@ -23,7 +23,7 @@ namespace snmalloc { public: using Pal = DefaultPal; - using PagemapEntry = DefaultPagemapEntry; + using PagemapEntry = DefaultPagemapEntry; private: using ConcretePagemap = From 82d4bec0b1100b6531a4d02955f759c445c2ed5c Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Mon, 10 Jun 2024 14:13:36 +0100 Subject: [PATCH 02/26] Small rearrangement of header includes. --- src/snmalloc/global/global.h | 1 + src/snmalloc/{override => global}/libc.h | 2 +- src/snmalloc/global/memcpy.h | 1 - src/snmalloc/global/scopedalloc.h | 1 - src/snmalloc/global/threadalloc.h | 2 -- src/snmalloc/override/malloc.cc | 1 - src/snmalloc/override/new.cc | 2 +- src/snmalloc/override/override.h | 2 +- src/snmalloc/override/rust.cc | 2 +- src/snmalloc/snmalloc_front.h | 1 - src/test/perf/memcpy/memcpy.cc | 2 +- 11 files changed, 6 insertions(+), 11 deletions(-) rename src/snmalloc/{override => global}/libc.h (99%) diff --git a/src/snmalloc/global/global.h b/src/snmalloc/global/global.h index a2f1159a1..514d69b7c 100644 --- a/src/snmalloc/global/global.h +++ b/src/snmalloc/global/global.h @@ -1,4 +1,5 @@ #include "bounds_checks.h" +#include "libc.h" #include "memcpy.h" #include "scopedalloc.h" #include "threadalloc.h" diff --git a/src/snmalloc/override/libc.h b/src/snmalloc/global/libc.h similarity index 99% rename from src/snmalloc/override/libc.h rename to src/snmalloc/global/libc.h index 587d94ac2..48157e215 100644 --- a/src/snmalloc/override/libc.h +++ b/src/snmalloc/global/libc.h @@ -1,6 +1,6 @@ #pragma once -#include "../global/global.h" +#include "threadalloc.h" #include #include diff --git a/src/snmalloc/global/memcpy.h b/src/snmalloc/global/memcpy.h index 565719a90..3054484d9 100644 --- a/src/snmalloc/global/memcpy.h +++ b/src/snmalloc/global/memcpy.h @@ -1,5 +1,4 @@ #pragma once -#include "../backend/globalconfig.h" #include "bounds_checks.h" namespace snmalloc diff --git a/src/snmalloc/global/scopedalloc.h b/src/snmalloc/global/scopedalloc.h index cb9f0fc8b..345635a70 100644 --- a/src/snmalloc/global/scopedalloc.h +++ b/src/snmalloc/global/scopedalloc.h @@ -1,5 +1,4 @@ #pragma once -#include "../backend/globalconfig.h" /** * This header requires that Alloc has been defined. diff --git a/src/snmalloc/global/threadalloc.h b/src/snmalloc/global/threadalloc.h index d900fb272..7ba8ddd79 100644 --- a/src/snmalloc/global/threadalloc.h +++ b/src/snmalloc/global/threadalloc.h @@ -1,7 +1,5 @@ #pragma once -#include "../backend/globalconfig.h" - #if defined(SNMALLOC_EXTERNAL_THREAD_ALLOC) # define SNMALLOC_THREAD_TEARDOWN_DEFINED #endif diff --git a/src/snmalloc/override/malloc.cc b/src/snmalloc/override/malloc.cc index 22d78244b..9c0571f74 100644 --- a/src/snmalloc/override/malloc.cc +++ b/src/snmalloc/override/malloc.cc @@ -1,4 +1,3 @@ -#include "libc.h" #include "override.h" using namespace snmalloc; diff --git a/src/snmalloc/override/new.cc b/src/snmalloc/override/new.cc index ff83b281e..19aa9f58c 100644 --- a/src/snmalloc/override/new.cc +++ b/src/snmalloc/override/new.cc @@ -1,4 +1,4 @@ -#include "libc.h" +#include "snmalloc/snmalloc.h" #ifdef _WIN32 # ifdef __clang__ diff --git a/src/snmalloc/override/override.h b/src/snmalloc/override/override.h index 0ca70bc11..5dda309c0 100644 --- a/src/snmalloc/override/override.h +++ b/src/snmalloc/override/override.h @@ -1,6 +1,6 @@ #pragma once -#include "../global/global.h" +#include "snmalloc/snmalloc.h" #ifndef SNMALLOC_EXPORT # define SNMALLOC_EXPORT diff --git a/src/snmalloc/override/rust.cc b/src/snmalloc/override/rust.cc index f7825cda1..ec25c5f22 100644 --- a/src/snmalloc/override/rust.cc +++ b/src/snmalloc/override/rust.cc @@ -1,5 +1,5 @@ #define SNMALLOC_NAME_MANGLE(a) sn_##a -#include "malloc.cc" +#include "snmalloc/snmalloc.h" #include diff --git a/src/snmalloc/snmalloc_front.h b/src/snmalloc/snmalloc_front.h index 7a16b6c75..4c5aa60ca 100644 --- a/src/snmalloc/snmalloc_front.h +++ b/src/snmalloc/snmalloc_front.h @@ -1,2 +1 @@ #include "global/global.h" -#include "override/libc.h" \ No newline at end of file diff --git a/src/test/perf/memcpy/memcpy.cc b/src/test/perf/memcpy/memcpy.cc index e3bee7d2c..fa344b9ab 100644 --- a/src/test/perf/memcpy/memcpy.cc +++ b/src/test/perf/memcpy/memcpy.cc @@ -1,4 +1,4 @@ -#include "snmalloc/global/memcpy.h" +#include #include #include From 5a41897456614c1b0199f59a99fa28131872ac79 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 15:28:25 +0100 Subject: [PATCH 03/26] Change template params slightly --- src/snmalloc/backend/globalconfig.h | 12 ------------ src/snmalloc/snmalloc.h | 16 ++++++++++++++-- src/test/func/malloc/malloc.cc | 2 +- src/test/func/memory/memory.cc | 6 +++--- src/test/func/statistics/stats.cc | 14 +++++++------- .../thread_alloc_external.cc | 3 ++- src/test/perf/contention/contention.cc | 2 +- .../perf/external_pointer/externalpointer.cc | 2 +- src/test/perf/singlethread/singlethread.cc | 2 +- 9 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/snmalloc/backend/globalconfig.h b/src/snmalloc/backend/globalconfig.h index 2cbda567d..80ba68bc4 100644 --- a/src/snmalloc/backend/globalconfig.h +++ b/src/snmalloc/backend/globalconfig.h @@ -1,8 +1,4 @@ #pragma once -// If you define SNMALLOC_PROVIDE_OWN_CONFIG then you must provide your own -// definition of `snmalloc::Alloc` before including any files that include -// `snmalloc.h` or consume the global allocation APIs. -#ifndef SNMALLOC_PROVIDE_OWN_CONFIG # include "../backend_helpers/backend_helpers.h" # include "backend.h" @@ -163,12 +159,4 @@ namespace snmalloc snmalloc::register_clean_up(); } }; - - using StandardConfig = StandardConfigClientMeta; - - /** - * Create allocator type for this configuration. - */ - using Alloc = snmalloc::LocalAllocator; } // namespace snmalloc -#endif diff --git a/src/snmalloc/snmalloc.h b/src/snmalloc/snmalloc.h index 47bd6e78a..2c3d414cc 100644 --- a/src/snmalloc/snmalloc.h +++ b/src/snmalloc/snmalloc.h @@ -3,8 +3,20 @@ // Core implementation of snmalloc independent of the configuration mode #include "snmalloc_core.h" -// If the user has defined SNMALLOC_PROVIDE_OWN_CONFIG, this include does -// nothing. Otherwise, it provide a default configuration of snmalloc::Alloc. +// Provides the global configuration for the snmalloc implementation. #include "backend/globalconfig.h" + +// If you define SNMALLOC_PROVIDE_OWN_CONFIG then you must provide your own +// definition of `snmalloc::Alloc` before including any files that include +// `snmalloc.h` or consume the global allocation APIs. +#ifndef SNMALLOC_PROVIDE_OWN_CONFIG +namespace snmalloc { + /** + * Create allocator type for this configuration. + */ + using Alloc = snmalloc::LocalAllocator>; +} // namespace snmalloc +#endif + // User facing API surface, needs to know what `Alloc` is. #include "snmalloc_front.h" diff --git a/src/test/func/malloc/malloc.cc b/src/test/func/malloc/malloc.cc index 1d4c31da9..6549e5834 100644 --- a/src/test/func/malloc/malloc.cc +++ b/src/test/func/malloc/malloc.cc @@ -375,6 +375,6 @@ int main(int argc, char** argv) our_malloc_usable_size(nullptr) == 0, "malloc_usable_size(nullptr) should be zero"); - snmalloc::debug_check_empty(); + snmalloc::debug_check_empty(); return 0; } diff --git a/src/test/func/memory/memory.cc b/src/test/func/memory/memory.cc index 8b3606012..7d176f43d 100644 --- a/src/test/func/memory/memory.cc +++ b/src/test/func/memory/memory.cc @@ -184,7 +184,7 @@ void test_calloc() alloc.dealloc(p, size); } - snmalloc::debug_check_empty(); + snmalloc::debug_check_empty(); } void test_double_alloc() @@ -229,7 +229,7 @@ void test_double_alloc() } } } - snmalloc::debug_check_empty(); + snmalloc::debug_check_empty(); } void test_external_pointer() @@ -275,7 +275,7 @@ void test_external_pointer() alloc.dealloc(p1, size); } - snmalloc::debug_check_empty(); + snmalloc::debug_check_empty(); }; void check_offset(void* base, void* interior) diff --git a/src/test/func/statistics/stats.cc b/src/test/func/statistics/stats.cc index c8db1cad7..214a0bcf3 100644 --- a/src/test/func/statistics/stats.cc +++ b/src/test/func/statistics/stats.cc @@ -17,7 +17,7 @@ void debug_check_empty_1() auto r = a.alloc(size); - snmalloc::debug_check_empty(&result); + snmalloc::debug_check_empty(&result); if (result != false) { std::cout << "debug_check_empty failed to detect leaked memory:" << size @@ -27,7 +27,7 @@ void debug_check_empty_1() a.dealloc(r); - snmalloc::debug_check_empty(&result); + snmalloc::debug_check_empty(&result); if (result != true) { std::cout << "debug_check_empty failed to say empty:" << size << std::endl; @@ -36,7 +36,7 @@ void debug_check_empty_1() r = a.alloc(size); - snmalloc::debug_check_empty(&result); + snmalloc::debug_check_empty(&result); if (result != false) { std::cout << "debug_check_empty failed to detect leaked memory:" << size @@ -46,7 +46,7 @@ void debug_check_empty_1() a.dealloc(r); - snmalloc::debug_check_empty(&result); + snmalloc::debug_check_empty(&result); if (result != true) { std::cout << "debug_check_empty failed to say empty:" << size << std::endl; @@ -72,7 +72,7 @@ void debug_check_empty_2() } auto r = a.alloc(size); allocs.push_back(r); - snmalloc::debug_check_empty(&result); + snmalloc::debug_check_empty(&result); if (result != false) { std::cout << "False empty after " << i << " allocations of " << size @@ -88,7 +88,7 @@ void debug_check_empty_2() { std::cout << "." << std::flush; } - snmalloc::debug_check_empty(&result); + snmalloc::debug_check_empty(&result); if (result != false) { std::cout << "False empty after " << i << " deallocations of " << size @@ -98,7 +98,7 @@ void debug_check_empty_2() a.dealloc(allocs[i]); } std::cout << std::endl; - snmalloc::debug_check_empty(); + snmalloc::debug_check_empty(); } int main() diff --git a/src/test/func/thread_alloc_external/thread_alloc_external.cc b/src/test/func/thread_alloc_external/thread_alloc_external.cc index 2b10ed8cb..686c08dc4 100644 --- a/src/test/func/thread_alloc_external/thread_alloc_external.cc +++ b/src/test/func/thread_alloc_external/thread_alloc_external.cc @@ -12,7 +12,8 @@ namespace snmalloc { - using Alloc = snmalloc::LocalAllocator; + using Alloc = snmalloc::LocalAllocator< + snmalloc::StandardConfigClientMeta>; } using namespace snmalloc; diff --git a/src/test/perf/contention/contention.cc b/src/test/perf/contention/contention.cc index e266f0491..9e12b660a 100644 --- a/src/test/perf/contention/contention.cc +++ b/src/test/perf/contention/contention.cc @@ -154,7 +154,7 @@ void test_tasks(size_t num_tasks, size_t count, size_t size) } #ifndef NDEBUG - snmalloc::debug_check_empty(); + snmalloc::debug_check_empty(); #endif }; diff --git a/src/test/perf/external_pointer/externalpointer.cc b/src/test/perf/external_pointer/externalpointer.cc index be3306cba..96d465820 100644 --- a/src/test/perf/external_pointer/externalpointer.cc +++ b/src/test/perf/external_pointer/externalpointer.cc @@ -47,7 +47,7 @@ namespace test alloc.dealloc(objects[i]); } - snmalloc::debug_check_empty(); + snmalloc::debug_check_empty(); } void test_external_pointer(xoroshiro::p128r64& r) diff --git a/src/test/perf/singlethread/singlethread.cc b/src/test/perf/singlethread/singlethread.cc index b93dcd428..b8a995fb8 100644 --- a/src/test/perf/singlethread/singlethread.cc +++ b/src/test/perf/singlethread/singlethread.cc @@ -60,7 +60,7 @@ void test_alloc_dealloc(size_t count, size_t size, bool write) } } - snmalloc::debug_check_empty(); + snmalloc::debug_check_empty(); } int main(int, char**) From 6b5e5bb55b1c6b2a62843eeda7a4e665817e3990 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 14:40:55 +0100 Subject: [PATCH 04/26] Expose client meta data This provides a way to access the client meta data for an object. --- src/snmalloc/backend/fixedglobalconfig.h | 1 + src/snmalloc/backend/globalconfig.h | 1 + src/snmalloc/backend_helpers/commonconfig.h | 8 +++- src/snmalloc/global/libc.h | 13 ++++++ src/snmalloc/mem/corealloc.h | 1 - src/snmalloc/mem/localalloc.h | 43 ++++++++++++++++++++ src/snmalloc/mem/metadata.h | 8 ++-- src/snmalloc/mem/sizeclasstable.h | 22 ++++++---- src/snmalloc/pal/pal_posix.h | 1 - src/test/func/domestication/domestication.cc | 1 + 10 files changed, 83 insertions(+), 16 deletions(-) diff --git a/src/snmalloc/backend/fixedglobalconfig.h b/src/snmalloc/backend/fixedglobalconfig.h index 99f175f1d..582a60192 100644 --- a/src/snmalloc/backend/fixedglobalconfig.h +++ b/src/snmalloc/backend/fixedglobalconfig.h @@ -15,6 +15,7 @@ namespace snmalloc { public: using PagemapEntry = DefaultPagemapEntry; + using ClientMeta = ClientMetaDataProvider; private: using ConcretePagemap = diff --git a/src/snmalloc/backend/globalconfig.h b/src/snmalloc/backend/globalconfig.h index 80ba68bc4..e7c543459 100644 --- a/src/snmalloc/backend/globalconfig.h +++ b/src/snmalloc/backend/globalconfig.h @@ -32,6 +32,7 @@ namespace snmalloc public: using Pal = DefaultPal; using PagemapEntry = DefaultPagemapEntry; + using ClientMeta = ClientMetaDataProvider; private: using ConcretePagemap = diff --git a/src/snmalloc/backend_helpers/commonconfig.h b/src/snmalloc/backend_helpers/commonconfig.h index 28eeff9ee..d538a0953 100644 --- a/src/snmalloc/backend_helpers/commonconfig.h +++ b/src/snmalloc/backend_helpers/commonconfig.h @@ -98,12 +98,14 @@ namespace snmalloc struct NoClientMetaDataProvider { using StorageType = Empty; + using DataRef = Empty&; + using ConstDataRef = const Empty&; static size_t required_count(size_t) { return 1; } - static Empty& get(Empty* base, size_t) { + static DataRef get(StorageType* base, size_t) { return *base; } }; @@ -112,12 +114,14 @@ namespace snmalloc struct ArrayClientMetaDataProvider { using StorageType = T; + using DataRef = T&; + using ConstDataRef = const T&; static size_t required_count(size_t max_count) { return max_count; } - static T& get(StorageType* base, size_t index) { + static DataRef get(StorageType* base, size_t index) { return base[index]; } }; diff --git a/src/snmalloc/global/libc.h b/src/snmalloc/global/libc.h index 48157e215..603d81fed 100644 --- a/src/snmalloc/global/libc.h +++ b/src/snmalloc/global/libc.h @@ -176,4 +176,17 @@ namespace snmalloc::libc *memptr = p; return 0; } + + inline typename snmalloc::Alloc::Config::ClientMeta::DataRef + get_client_meta_data(void* p) + { + return ThreadAlloc::get().get_client_meta_data(p); + } + + inline typename snmalloc::Alloc::Config::ClientMeta::ConstDataRef + get_client_meta_data_const(void* p) + { + return ThreadAlloc::get().get_client_meta_data_const(p); + } + } // namespace snmalloc::libc diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index 7cbcd2b1e..a07834d12 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -798,7 +798,6 @@ namespace snmalloc // Calculate the extra bytes required to store the client meta-data. size_t extra_bytes = BackendSlabMetadata::get_extra_bytes(sizeclass); - message<1024>("extra_bytes={}, sizeclass={}", extra_bytes, sizeclass); auto [slab, meta] = Config::Backend::alloc_chunk( get_backend_local_state(), diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index e26243a9b..1a467af0f 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -816,6 +816,49 @@ namespace snmalloc } } + /** + * @brief Get the client meta data for the snmalloc allocation covering this pointer. + * + */ + typename Config::ClientMeta::DataRef get_client_meta_data(void* p) + { + const PagemapEntry& entry = Config::Backend::template get_metaentry(address_cast(p)); + + size_t index = slab_index(entry.get_sizeclass(), address_cast(p)); + + auto* meta_slab = entry.get_slab_metadata(); + + if (SNMALLOC_UNLIKELY(meta_slab == nullptr)) + { + // TODO handle const case, where we read fake meta-data. + abort(); + } + + return meta_slab->get_meta_for_object(index); + } + + /** + * @brief Get the client meta data for the snmalloc allocation covering this pointer. + * + */ + typename Config::ClientMeta::ConstDataRef get_client_meta_data_const(void* p) + { + const PagemapEntry& entry = Config::Backend::template get_metaentry(address_cast(p)); + + size_t index = slab_index(entry.get_sizeclass(), address_cast(p)); + + auto* meta_slab = entry.get_slab_metadata(); + + if (SNMALLOC_UNLIKELY(meta_slab == nullptr)) + { + static typename Config::ClientMeta::StorageType null_meta_store{}; + return Config::ClientMeta::get(&null_meta_store, 0); + } + + return meta_slab->get_meta_for_object(index); + } + + /** * Returns the number of remaining bytes in an object. * diff --git a/src/snmalloc/mem/metadata.h b/src/snmalloc/mem/metadata.h index fb2b921ed..ea3e679c5 100644 --- a/src/snmalloc/mem/metadata.h +++ b/src/snmalloc/mem/metadata.h @@ -372,7 +372,7 @@ namespace snmalloc friend class FrontendSlabMetadata; // Can only be constructed by FrontendSlabMetadata - FrontendSlabMetadata_Trait() = default; + constexpr FrontendSlabMetadata_Trait() = default; }; /** @@ -434,7 +434,7 @@ namespace snmalloc * slab. The meta data will actually allocate multiple elements after this * type, so that client_meta_[1] will work for the required meta-data size. */ - SNMALLOC_NO_UNIQUE_ADDRESS typename ClientMeta::StorageType client_meta_; + SNMALLOC_NO_UNIQUE_ADDRESS typename ClientMeta::StorageType client_meta_{}; uint16_t& needed() { @@ -601,9 +601,9 @@ namespace snmalloc return address_cast(free_queue.read_head(0, key)); } - typename ClientMeta::StorageType* get_client_meta_base() + typename ClientMeta::DataRef get_meta_for_object(size_t index) { - return &client_meta_; + return ClientMeta::get(&client_meta_, index); } static size_t get_client_storage_count(smallsizeclass_t sizeclass) diff --git a/src/snmalloc/mem/sizeclasstable.h b/src/snmalloc/mem/sizeclasstable.h index 900e8332b..1a255e588 100644 --- a/src/snmalloc/mem/sizeclasstable.h +++ b/src/snmalloc/mem/sizeclasstable.h @@ -333,14 +333,11 @@ namespace snmalloc .capacity; } - constexpr address_t start_of_object(sizeclass_t sc, address_t addr) + SNMALLOC_FAST_PATH constexpr size_t slab_index(sizeclass_t sc, address_t addr) { auto meta = sizeclass_metadata.fast(sc); - address_t slab_start = addr & ~meta.slab_mask; size_t offset = addr & meta.slab_mask; - size_t size = meta.size; - - if constexpr (sizeof(addr) >= 8) + if constexpr (sizeof(offset) >= 8) { // Only works for 64 bit multiplication, as the following will overflow in // 32bit. @@ -351,17 +348,26 @@ namespace snmalloc // the slab_mask by making the `div_mult` zero. The link uses 128 bit // multiplication, we have shrunk the range of the calculation to remove // this dependency. - size_t offset_start = ((offset * meta.div_mult) >> DIV_MULT_SHIFT) * size; - return slab_start + offset_start; + size_t index = ((offset * meta.div_mult) >> DIV_MULT_SHIFT); + return index; } else { + size_t size = meta.size; if (size == 0) return 0; - return slab_start + (offset / size) * size; + return offset / size; } } + SNMALLOC_FAST_PATH constexpr address_t start_of_object(sizeclass_t sc, address_t addr) + { + auto meta = sizeclass_metadata.fast(sc); + address_t slab_start = addr & ~meta.slab_mask; + size_t index = slab_index(sc, addr); + return slab_start + (index * meta.size); + } + constexpr size_t index_in_object(sizeclass_t sc, address_t addr) { return addr - start_of_object(sc, addr); diff --git a/src/snmalloc/pal/pal_posix.h b/src/snmalloc/pal/pal_posix.h index 6c9ae05e8..097d18f61 100644 --- a/src/snmalloc/pal/pal_posix.h +++ b/src/snmalloc/pal/pal_posix.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/src/test/func/domestication/domestication.cc b/src/test/func/domestication/domestication.cc index 478099616..aa84ecdf2 100644 --- a/src/test/func/domestication/domestication.cc +++ b/src/test/func/domestication/domestication.cc @@ -24,6 +24,7 @@ namespace snmalloc public: using Pal = DefaultPal; using PagemapEntry = DefaultPagemapEntry; + using ClientMeta = NoClientMetaDataProvider; private: using ConcretePagemap = From 8aa6ac757911b171abd730da8db7486c8ef2573a Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 14:45:40 +0100 Subject: [PATCH 05/26] Add two example using the meta-data. --- src/test/func/client_meta/client_meta.cc | 58 +++++++ src/test/func/miracle_ptr/miracle_ptr.cc | 188 +++++++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 src/test/func/client_meta/client_meta.cc create mode 100644 src/test/func/miracle_ptr/miracle_ptr.cc diff --git a/src/test/func/client_meta/client_meta.cc b/src/test/func/client_meta/client_meta.cc new file mode 100644 index 000000000..7f4e28586 --- /dev/null +++ b/src/test/func/client_meta/client_meta.cc @@ -0,0 +1,58 @@ +#include "test/setup.h" + +#include + +#include +#include + +namespace snmalloc +{ + using Alloc = snmalloc::LocalAllocator< + snmalloc::StandardConfigClientMeta>>>; +} +#define SNMALLOC_PROVIDE_OWN_CONFIG +#include + + + +int main() +{ +#ifdef SNMALLOC_PASS_THROUGH + // This test does not make sense in pass-through + return 0; +#else + std::vector ptrs; + for (size_t i = 0; i < 10000; i++) + { + auto p = snmalloc::libc::malloc(1024); + auto& meta = snmalloc::libc::get_client_meta_data(p); + meta = i; + ptrs.push_back(p); + memset(p, (uint8_t)i, 1024); + } + + for (size_t i = 0; i < 10000; i++) + { + auto p = ptrs[i]; + auto& meta = snmalloc::libc::get_client_meta_data(p); + if (meta != i) + { + std::cout << "Failed at index " << i << std::endl; + abort(); + } + for (size_t j = 0; j < 1024; j++) + { + if (reinterpret_cast(p)[j] != (uint8_t)i) + { + std::cout << "Failed at index " << i << " byte " << j << std::endl; + abort(); + } + } + snmalloc::libc::free(p); + } + + auto& meta = snmalloc::libc::get_client_meta_data_const(&ptrs); + std::cout << "meta for stack" << meta << std::endl; + return 0; +#endif +} diff --git a/src/test/func/miracle_ptr/miracle_ptr.cc b/src/test/func/miracle_ptr/miracle_ptr.cc new file mode 100644 index 000000000..3924d0fb4 --- /dev/null +++ b/src/test/func/miracle_ptr/miracle_ptr.cc @@ -0,0 +1,188 @@ +/** + * This file demonstrates how the snmalloc library could be implemented to + * provide a miracle pointer like feature. This is not a hardened + * implementation and is purely for illustrative purposes. + * + * Do not use as is. + */ + +#include "test/setup.h" + +#include +#include +#include +#include + +namespace snmalloc +{ + // Instantiate the allocator with a client meta data provider that uses an + // atomic size_t to store the reference count. + using Alloc = snmalloc::LocalAllocator>>>; +} +#define SNMALLOC_PROVIDE_OWN_CONFIG +#include + +SNMALLOC_SLOW_PATH void error(std::string msg) +{ + std::cout << msg << std::endl; + abort(); +} + +SNMALLOC_FAST_PATH void check(bool b, std::string msg) +{ + if (SNMALLOC_UNLIKELY(!b)) + error(msg); +} + +namespace snmalloc::miracle +{ + // snmalloc meta-data representation + // * 2n + 1: Represents an object that has not been deallocated with n + // additional references to it + // * 2n : Represents a deallocated object that + // has n additional references to it + + inline void* malloc(size_t size) + { + auto p = snmalloc::libc::malloc(size); + if (SNMALLOC_UNLIKELY(p == nullptr)) + return nullptr; + + snmalloc::libc::get_client_meta_data(p) = 1; + return p; + } + + inline void free(void* ptr) + { + if (ptr == nullptr) + return; + + // TODO could build a check into this that it is the start of the object? + auto previous = + snmalloc::libc::get_client_meta_data(ptr).fetch_add((size_t)-1); + + if (SNMALLOC_LIKELY(previous == 1)) + { + std::cout << "Freeing " << ptr << std::endl; + snmalloc::libc::free(ptr); + return; + } + + check((previous & 1) == 1, "Double free detected"); + + // We have additional references to this object. + // We should not free it. + // TOOD this assumes this is not an internal pointer. + memset(ptr, 0, snmalloc::libc::malloc_usable_size(ptr)); + } + + inline void acquire(void* p) + { + auto previous = + snmalloc::libc::get_client_meta_data(p).fetch_add((size_t)2); + + // Can we take new pointers to a deallocated object? + check((previous & 1) == 1, "Acquiring a deallocated object"); + } + + inline void release(void* p) + { + auto previous = + snmalloc::libc::get_client_meta_data(p).fetch_add((size_t)-2); + + if (previous > 2) + return; + + check(previous == 2, "Releasing an object with insufficient references"); + + std::cout << "Freeing from release " << p << std::endl; + snmalloc::libc::free(p); + } +}; + +/** + * Overload new and delete to use the "miracle pointer" implementation. + */ +void* operator new(size_t size) +{ + return snmalloc::miracle::malloc(size); +} + +void operator delete(void* p) +{ + snmalloc::miracle::free(p); +} + +/** + * This class can be used to replace a raw pointer. It will automatically use + * the underlying backup reference counting design from the miracle pointer + * docs. + */ +template +class raw_ptr +{ + T* p; + +public: + raw_ptr() : p(nullptr) {} + + raw_ptr(T* p) : p(p) + { + snmalloc::miracle::acquire(p); + } + + T& operator*() + { + return *p; + } + + ~raw_ptr() + { + if (p == nullptr) + return; + snmalloc::miracle::release(p); + } + + raw_ptr(const raw_ptr& rp) : p(rp.p) + { + snmalloc::miracle::acquire(p); + } + + raw_ptr& operator=(const raw_ptr& other) + { + p = other.p; + snmalloc::miracle::acquire(other.p); + return *this; + } + + raw_ptr(raw_ptr&& other) : p(other.p) + { + other.p = nullptr; + } + + raw_ptr& operator=(raw_ptr&& other) + { + p = other.p; + other.p = nullptr; + return *this; + } +}; + +int main() +{ +#ifndef SNMALLOC_PASS_THROUGH + raw_ptr p; + { + auto up = std::make_unique(42); + auto up2 = std::make_unique(41); + p = up.get(); + check(*p == 42, "Failed to set p"); + } + // Still safe to access here. The unique_ptr has been destroyed, but the + // raw_ptr has kept the memory live. + // Current implementation zeros the memory when the unique_ptr is destroyed. + check(*p == 0, "Failed to keep memory live"); +#endif + return 0; +} \ No newline at end of file From b94e4991334d2b615648218989ba5702640951e0 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 15:33:26 +0100 Subject: [PATCH 06/26] Minor fix --- src/snmalloc/mem/localalloc.h | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index 1a467af0f..e5650bc19 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -817,38 +817,41 @@ namespace snmalloc } /** - * @brief Get the client meta data for the snmalloc allocation covering this pointer. - * + * @brief Get the client meta data for the snmalloc allocation covering this + * pointer. */ typename Config::ClientMeta::DataRef get_client_meta_data(void* p) { - const PagemapEntry& entry = Config::Backend::template get_metaentry(address_cast(p)); + const PagemapEntry& entry = + Config::Backend::template get_metaentry(address_cast(p)); size_t index = slab_index(entry.get_sizeclass(), address_cast(p)); auto* meta_slab = entry.get_slab_metadata(); - + if (SNMALLOC_UNLIKELY(meta_slab == nullptr)) { - // TODO handle const case, where we read fake meta-data. - abort(); + error( + "Cannot access meta-data for non-snmalloc object in writable form!") } return meta_slab->get_meta_for_object(index); } /** - * @brief Get the client meta data for the snmalloc allocation covering this pointer. - * + * @brief Get the client meta data for the snmalloc allocation covering this + * pointer. */ - typename Config::ClientMeta::ConstDataRef get_client_meta_data_const(void* p) + typename Config::ClientMeta::ConstDataRef + get_client_meta_data_const(void* p) { - const PagemapEntry& entry = Config::Backend::template get_metaentry(address_cast(p)); + const PagemapEntry& entry = + Config::Backend::template get_metaentry(address_cast(p)); size_t index = slab_index(entry.get_sizeclass(), address_cast(p)); auto* meta_slab = entry.get_slab_metadata(); - + if (SNMALLOC_UNLIKELY(meta_slab == nullptr)) { static typename Config::ClientMeta::StorageType null_meta_store{}; @@ -858,7 +861,6 @@ namespace snmalloc return meta_slab->get_meta_for_object(index); } - /** * Returns the number of remaining bytes in an object. * From 05cc7e105bc0534518b19164fd0d73cf3c22c86d Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 15:34:46 +0100 Subject: [PATCH 07/26] Clangformat --- src/snmalloc/backend/backend.h | 10 +++++++--- src/snmalloc/backend/fixedglobalconfig.h | 6 ++---- src/snmalloc/backend/globalconfig.h | 17 +++++++++-------- src/snmalloc/backend_helpers/commonconfig.h | 14 +++++++++----- .../backend_helpers/defaultpagemapentry.h | 11 +++++++---- src/snmalloc/mem/sizeclasstable.h | 3 ++- src/snmalloc/snmalloc.h | 12 +++++++----- src/test/func/client_meta/client_meta.cc | 17 +++++++---------- src/test/perf/memcpy/memcpy.cc | 1 - 9 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/snmalloc/backend/backend.h b/src/snmalloc/backend/backend.h index ac55d62a8..600777883 100644 --- a/src/snmalloc/backend/backend.h +++ b/src/snmalloc/backend/backend.h @@ -87,8 +87,11 @@ namespace snmalloc * (remote, sizeclass, slab_metadata) * where slab_metadata, is the second element of the pair return. */ - static std::pair, SlabMetadata*> - alloc_chunk(LocalState& local_state, size_t size, uintptr_t ras, size_t extra_bytes = 0) + static std::pair, SlabMetadata*> alloc_chunk( + LocalState& local_state, + size_t size, + uintptr_t ras, + size_t extra_bytes = 0) { SNMALLOC_ASSERT(bits::is_pow2(size)); SNMALLOC_ASSERT(size >= MIN_CHUNK_SIZE); @@ -96,7 +99,8 @@ namespace snmalloc auto meta_size = bits::next_pow2(sizeof(SlabMetadata) + extra_bytes); #ifdef SNMALLOC_TRACING - message<1024>("Allocating metadata of size: {} ({})", meta_size, extra_bytes); + message<1024>( + "Allocating metadata of size: {} ({})", meta_size, extra_bytes); #endif auto meta_cap = local_state.get_meta_range().alloc_range(meta_size); diff --git a/src/snmalloc/backend/fixedglobalconfig.h b/src/snmalloc/backend/fixedglobalconfig.h index 582a60192..83e111747 100644 --- a/src/snmalloc/backend/fixedglobalconfig.h +++ b/src/snmalloc/backend/fixedglobalconfig.h @@ -66,13 +66,11 @@ namespace snmalloc * C++, and not just its initializer fragment, to initialize a non-prefix * subset of the flags (in any order, at that). */ - static constexpr Flags Options = []() constexpr - { + static constexpr Flags Options = []() constexpr { Flags opts = {}; opts.HasDomesticate = true; return opts; - } - (); + }(); // This needs to be a forward reference as the // thread local state will need to know about this. diff --git a/src/snmalloc/backend/globalconfig.h b/src/snmalloc/backend/globalconfig.h index e7c543459..72a6c3d42 100644 --- a/src/snmalloc/backend/globalconfig.h +++ b/src/snmalloc/backend/globalconfig.h @@ -1,9 +1,9 @@ #pragma once -# include "../backend_helpers/backend_helpers.h" -# include "backend.h" -# include "meta_protected_range.h" -# include "standard_range.h" +#include "../backend_helpers/backend_helpers.h" +#include "backend.h" +#include "meta_protected_range.h" +#include "standard_range.h" namespace snmalloc { @@ -24,10 +24,11 @@ namespace snmalloc * The Configuration sets up a Pagemap for the backend to use, and the state * required to build new allocators (GlobalPoolState). */ - template + template class StandardConfigClientMeta final : public CommonConfig { - using GlobalPoolState = PoolState>>; + using GlobalPoolState = PoolState< + CoreAllocator>>; public: using Pal = DefaultPal; @@ -96,9 +97,9 @@ namespace snmalloc SNMALLOC_SLOW_PATH static void ensure_init_slow() { FlagLock lock{initialisation_lock}; -# ifdef SNMALLOC_TRACING +#ifdef SNMALLOC_TRACING message<1024>("Run init_impl"); -# endif +#endif if (initialised) return; diff --git a/src/snmalloc/backend_helpers/commonconfig.h b/src/snmalloc/backend_helpers/commonconfig.h index d538a0953..818e95134 100644 --- a/src/snmalloc/backend_helpers/commonconfig.h +++ b/src/snmalloc/backend_helpers/commonconfig.h @@ -101,27 +101,31 @@ namespace snmalloc using DataRef = Empty&; using ConstDataRef = const Empty&; - static size_t required_count(size_t) { + static size_t required_count(size_t) + { return 1; } - static DataRef get(StorageType* base, size_t) { + static DataRef get(StorageType* base, size_t) + { return *base; } }; - template + template struct ArrayClientMetaDataProvider { using StorageType = T; using DataRef = T&; using ConstDataRef = const T&; - static size_t required_count(size_t max_count) { + static size_t required_count(size_t max_count) + { return max_count; } - static DataRef get(StorageType* base, size_t index) { + static DataRef get(StorageType* base, size_t index) + { return base[index]; } }; diff --git a/src/snmalloc/backend_helpers/defaultpagemapentry.h b/src/snmalloc/backend_helpers/defaultpagemapentry.h index 50b85eb11..5e1f703d2 100644 --- a/src/snmalloc/backend_helpers/defaultpagemapentry.h +++ b/src/snmalloc/backend_helpers/defaultpagemapentry.h @@ -64,11 +64,14 @@ namespace snmalloc SNMALLOC_FAST_PATH DefaultPagemapEntryT() = default; }; - template - class DefaultSlabMetadata : public FrontendSlabMetadata, ClientMetaDataProvider> + template + class DefaultSlabMetadata : public FrontendSlabMetadata< + DefaultSlabMetadata, + ClientMetaDataProvider> {}; - template - using DefaultPagemapEntry = DefaultPagemapEntryT>; + template + using DefaultPagemapEntry = + DefaultPagemapEntryT>; } // namespace snmalloc diff --git a/src/snmalloc/mem/sizeclasstable.h b/src/snmalloc/mem/sizeclasstable.h index 1a255e588..0a033319d 100644 --- a/src/snmalloc/mem/sizeclasstable.h +++ b/src/snmalloc/mem/sizeclasstable.h @@ -360,7 +360,8 @@ namespace snmalloc } } - SNMALLOC_FAST_PATH constexpr address_t start_of_object(sizeclass_t sc, address_t addr) + SNMALLOC_FAST_PATH constexpr address_t + start_of_object(sizeclass_t sc, address_t addr) { auto meta = sizeclass_metadata.fast(sc); address_t slab_start = addr & ~meta.slab_mask; diff --git a/src/snmalloc/snmalloc.h b/src/snmalloc/snmalloc.h index 2c3d414cc..b05b1a330 100644 --- a/src/snmalloc/snmalloc.h +++ b/src/snmalloc/snmalloc.h @@ -10,11 +10,13 @@ // definition of `snmalloc::Alloc` before including any files that include // `snmalloc.h` or consume the global allocation APIs. #ifndef SNMALLOC_PROVIDE_OWN_CONFIG -namespace snmalloc { - /** - * Create allocator type for this configuration. - */ - using Alloc = snmalloc::LocalAllocator>; +namespace snmalloc +{ + /** + * Create allocator type for this configuration. + */ + using Alloc = snmalloc::LocalAllocator< + snmalloc::StandardConfigClientMeta>; } // namespace snmalloc #endif diff --git a/src/test/func/client_meta/client_meta.cc b/src/test/func/client_meta/client_meta.cc index 7f4e28586..de518bb3c 100644 --- a/src/test/func/client_meta/client_meta.cc +++ b/src/test/func/client_meta/client_meta.cc @@ -1,25 +1,22 @@ #include "test/setup.h" #include - -#include #include +#include namespace snmalloc { - using Alloc = snmalloc::LocalAllocator< - snmalloc::StandardConfigClientMeta>>>; + using Alloc = snmalloc::LocalAllocator>>>; } #define SNMALLOC_PROVIDE_OWN_CONFIG #include - - int main() { #ifdef SNMALLOC_PASS_THROUGH - // This test does not make sense in pass-through - return 0; + // This test does not make sense in pass-through + return 0; #else std::vector ptrs; for (size_t i = 0; i < 10000; i++) @@ -37,8 +34,8 @@ int main() auto& meta = snmalloc::libc::get_client_meta_data(p); if (meta != i) { - std::cout << "Failed at index " << i << std::endl; - abort(); + std::cout << "Failed at index " << i << std::endl; + abort(); } for (size_t j = 0; j < 1024; j++) { diff --git a/src/test/perf/memcpy/memcpy.cc b/src/test/perf/memcpy/memcpy.cc index fa344b9ab..763dcd72e 100644 --- a/src/test/perf/memcpy/memcpy.cc +++ b/src/test/perf/memcpy/memcpy.cc @@ -1,5 +1,4 @@ #include - #include #include #include From 4a45ca6f4454c9b0109db3516c5cb46501f3bfa8 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 15:39:09 +0100 Subject: [PATCH 08/26] Fix typo in fix --- src/snmalloc/mem/localalloc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index e5650bc19..e79b4f8b4 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -832,7 +832,7 @@ namespace snmalloc if (SNMALLOC_UNLIKELY(meta_slab == nullptr)) { error( - "Cannot access meta-data for non-snmalloc object in writable form!") + "Cannot access meta-data for non-snmalloc object in writable form!"); } return meta_slab->get_meta_for_object(index); From 0f36585cb062b535a18eb17ec38ed362486c6345 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 15:46:25 +0100 Subject: [PATCH 09/26] g++ build fixes --- src/test/func/miracle_ptr/miracle_ptr.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/test/func/miracle_ptr/miracle_ptr.cc b/src/test/func/miracle_ptr/miracle_ptr.cc index 3924d0fb4..d656902d2 100644 --- a/src/test/func/miracle_ptr/miracle_ptr.cc +++ b/src/test/func/miracle_ptr/miracle_ptr.cc @@ -29,7 +29,7 @@ SNMALLOC_SLOW_PATH void error(std::string msg) abort(); } -SNMALLOC_FAST_PATH void check(bool b, std::string msg) +SNMALLOC_FAST_PATH_INLINE void check(bool b, std::string msg) { if (SNMALLOC_UNLIKELY(!b)) error(msg); @@ -114,12 +114,18 @@ void operator delete(void* p) snmalloc::miracle::free(p); } +void operator delete(void* p, size_t) +{ + snmalloc::miracle::free(p); +} + + /** * This class can be used to replace a raw pointer. It will automatically use * the underlying backup reference counting design from the miracle pointer * docs. */ -template +template class raw_ptr { T* p; From 735adf3d88a2875805051017dc734682cc9fd522 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 15:47:55 +0100 Subject: [PATCH 10/26] fix Rust overrides. --- src/snmalloc/override/rust.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/snmalloc/override/rust.cc b/src/snmalloc/override/rust.cc index ec25c5f22..4d7e79a7c 100644 --- a/src/snmalloc/override/rust.cc +++ b/src/snmalloc/override/rust.cc @@ -48,6 +48,6 @@ extern "C" SNMALLOC_EXPORT void* SNMALLOC_NAME_MANGLE(rust_realloc)( extern "C" SNMALLOC_EXPORT void SNMALLOC_NAME_MANGLE(rust_statistics)( size_t* current_memory_usage, size_t* peak_memory_usage) { - *current_memory_usage = StandardConfig::Backend::get_current_usage(); - *peak_memory_usage = StandardConfig::Backend::get_peak_usage(); + *current_memory_usage = Alloc::Config::Backend::get_current_usage(); + *peak_memory_usage = Alloc::Config::Backend::get_peak_usage(); } \ No newline at end of file From 45a376578826191906d25f73f6bb1a5aa8ba4af9 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 15:50:29 +0100 Subject: [PATCH 11/26] fix mac --- src/test/func/client_meta/client_meta.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/func/client_meta/client_meta.cc b/src/test/func/client_meta/client_meta.cc index de518bb3c..657de3e38 100644 --- a/src/test/func/client_meta/client_meta.cc +++ b/src/test/func/client_meta/client_meta.cc @@ -1,6 +1,7 @@ #include "test/setup.h" #include +#include #include #include From dd3010663cb05797a02f592b2569164cbd8ee4f5 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 15:51:13 +0100 Subject: [PATCH 12/26] clangformat --- src/test/func/client_meta/client_meta.cc | 2 +- src/test/func/miracle_ptr/miracle_ptr.cc | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/func/client_meta/client_meta.cc b/src/test/func/client_meta/client_meta.cc index 657de3e38..2c055ed34 100644 --- a/src/test/func/client_meta/client_meta.cc +++ b/src/test/func/client_meta/client_meta.cc @@ -1,9 +1,9 @@ #include "test/setup.h" #include -#include #include #include +#include namespace snmalloc { diff --git a/src/test/func/miracle_ptr/miracle_ptr.cc b/src/test/func/miracle_ptr/miracle_ptr.cc index d656902d2..3b8d9f590 100644 --- a/src/test/func/miracle_ptr/miracle_ptr.cc +++ b/src/test/func/miracle_ptr/miracle_ptr.cc @@ -119,13 +119,12 @@ void operator delete(void* p, size_t) snmalloc::miracle::free(p); } - /** * This class can be used to replace a raw pointer. It will automatically use * the underlying backup reference counting design from the miracle pointer * docs. */ -template +template class raw_ptr { T* p; From aa263d9dfce18d0aa3af8a4dc8855770ea41bf6e Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 20:41:39 +0100 Subject: [PATCH 13/26] Fixes for CI --- src/snmalloc/mem/localalloc.h | 2 +- src/snmalloc/mem/metadata.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index e79b4f8b4..7689c5563 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -846,7 +846,7 @@ namespace snmalloc get_client_meta_data_const(void* p) { const PagemapEntry& entry = - Config::Backend::template get_metaentry(address_cast(p)); + Config::Backend::template get_metaentry(address_cast(p)); size_t index = slab_index(entry.get_sizeclass(), address_cast(p)); diff --git a/src/snmalloc/mem/metadata.h b/src/snmalloc/mem/metadata.h index ea3e679c5..fc7b436f5 100644 --- a/src/snmalloc/mem/metadata.h +++ b/src/snmalloc/mem/metadata.h @@ -685,7 +685,7 @@ namespace snmalloc */ [[nodiscard]] SNMALLOC_FAST_PATH SlabMetadata* get_slab_metadata() const { - SNMALLOC_ASSERT(get_remote() != nullptr); + //SNMALLOC_ASSERT(get_remote() != nullptr); return unsafe_from_uintptr(meta & ~META_BOUNDARY_BIT); } }; From fa160add34b842b2b9e14ec84861e1820e60b67c Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 20:56:53 +0100 Subject: [PATCH 14/26] CI fight --- src/snmalloc/mem/metadata.h | 4 +++- src/test/func/miracle_ptr/miracle_ptr.cc | 27 +++++++++++++++--------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/snmalloc/mem/metadata.h b/src/snmalloc/mem/metadata.h index fc7b436f5..0b45b5e49 100644 --- a/src/snmalloc/mem/metadata.h +++ b/src/snmalloc/mem/metadata.h @@ -685,7 +685,9 @@ namespace snmalloc */ [[nodiscard]] SNMALLOC_FAST_PATH SlabMetadata* get_slab_metadata() const { - //SNMALLOC_ASSERT(get_remote() != nullptr); + // TODO Following assertion removed for client meta-data use case. + // Think about possible UAF scenarios. + // SNMALLOC_ASSERT(get_remote() != nullptr); return unsafe_from_uintptr(meta & ~META_BOUNDARY_BIT); } }; diff --git a/src/test/func/miracle_ptr/miracle_ptr.cc b/src/test/func/miracle_ptr/miracle_ptr.cc index 3b8d9f590..4e3375e6d 100644 --- a/src/test/func/miracle_ptr/miracle_ptr.cc +++ b/src/test/func/miracle_ptr/miracle_ptr.cc @@ -5,13 +5,19 @@ * * Do not use as is. */ +#ifdef SNMALLOC_THREAD_SANITIZER_ENABLED +int main() +{ + return 0; +} +#else -#include "test/setup.h" +# include "test/setup.h" -#include -#include -#include -#include +# include +# include +# include +# include namespace snmalloc { @@ -20,8 +26,8 @@ namespace snmalloc using Alloc = snmalloc::LocalAllocator>>>; } -#define SNMALLOC_PROVIDE_OWN_CONFIG -#include +# define SNMALLOC_PROVIDE_OWN_CONFIG +# include SNMALLOC_SLOW_PATH void error(std::string msg) { @@ -176,7 +182,7 @@ class raw_ptr int main() { -#ifndef SNMALLOC_PASS_THROUGH +# ifndef SNMALLOC_PASS_THROUGH raw_ptr p; { auto up = std::make_unique(42); @@ -188,6 +194,7 @@ int main() // raw_ptr has kept the memory live. // Current implementation zeros the memory when the unique_ptr is destroyed. check(*p == 0, "Failed to keep memory live"); -#endif +# endif return 0; -} \ No newline at end of file +} +#endif \ No newline at end of file From 678acd9d7e8c322d133a473facbf60b93b954f42 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 22:13:54 +0100 Subject: [PATCH 15/26] CI fixes --- .github/workflows/main.yml | 3 ++- src/test/func/miracle_ptr/miracle_ptr.cc | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bcb6b974f..b12713436 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -224,6 +224,7 @@ jobs: matrix: # Build just release variant as Debug is too slow. build-type: [ Release ] + extra-cmake-flags: [ "" ] include: - os: "ubuntu-latest" continue-on-error: # Don't class as an error if this fails, until we have a more reliablity. @@ -452,7 +453,7 @@ jobs: git diff --exit-code - name: Run clang-tidy run: | - clang-tidy-15 src/snmalloc/override/malloc.cc -header-filter="`pwd`/*" -warnings-as-errors='*' -export-fixes=tidy.fail -- -std=c++17 -mcx16 -DSNMALLOC_PLATFORM_HAS_GETENTROPY=0 + clang-tidy-15 src/snmalloc/override/malloc.cc -header-filter="`pwd`/*" -warnings-as-errors='*' -export-fixes=tidy.fail -- -std=c++17 -mcx16 -DSNMALLOC_PLATFORM_HAS_GETENTROPY=0 -Isrc if [ -f tidy.fail ] ; then cat tidy.fail exit 1 diff --git a/src/test/func/miracle_ptr/miracle_ptr.cc b/src/test/func/miracle_ptr/miracle_ptr.cc index 4e3375e6d..8ed56fb2b 100644 --- a/src/test/func/miracle_ptr/miracle_ptr.cc +++ b/src/test/func/miracle_ptr/miracle_ptr.cc @@ -5,6 +5,12 @@ * * Do not use as is. */ +#if defined(__has_feature) +# if __has_feature(thread_sanitizer) +# define SNMALLOC_THREAD_SANITIZER_ENABLED +# endif +#endif + #ifdef SNMALLOC_THREAD_SANITIZER_ENABLED int main() { From 1ceb14614bbd65e11aac6747965c14d80eb70221 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 22:19:35 +0100 Subject: [PATCH 16/26] CI changes --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b12713436..ddee31194 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -224,16 +224,15 @@ jobs: matrix: # Build just release variant as Debug is too slow. build-type: [ Release ] + os: ["ubuntu-latest", "ubuntu-20.04"] extra-cmake-flags: [ "" ] include: - os: "ubuntu-latest" - continue-on-error: # Don't class as an error if this fails, until we have a more reliablity. variant: "libc++ (TSan + UBSan)" dependencies: "sudo apt install ninja-build" extra-cmake-flags: "-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_FLAGS=-stdlib=\"libc++ -g\" -DSNMALLOC_SANITIZER=undefined,thread" # Also test specifically with clang-10 (on ubuntu-20.04) - os: "ubuntu-20.04" - continue-on-error: # Don't class as an error if this fails, until we have a more reliablity. variant: "clang-10 libc++ (TSan + UBSan)" dependencies: "sudo apt install ninja-build" extra-cmake-flags: "-DCMAKE_CXX_COMPILER=clang++-10 -DCMAKE_CXX_FLAGS=-stdlib=\"libc++ -g\" -DSNMALLOC_SANITIZER=undefined,thread" From bdf4c25cfe8e799089c5551ca0023d19b5758e0a Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Tue, 11 Jun 2024 22:23:44 +0100 Subject: [PATCH 17/26] I hate CI --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ddee31194..a2a8f918d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -225,7 +225,6 @@ jobs: # Build just release variant as Debug is too slow. build-type: [ Release ] os: ["ubuntu-latest", "ubuntu-20.04"] - extra-cmake-flags: [ "" ] include: - os: "ubuntu-latest" variant: "libc++ (TSan + UBSan)" From d3b284cfcacb03a83dd9c53ce90c6871dd1eb758 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 12 Jun 2024 10:32:21 +0100 Subject: [PATCH 18/26] Fix UBSan complaint. --- src/snmalloc/mem/freelist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snmalloc/mem/freelist.h b/src/snmalloc/mem/freelist.h index 49348d1d8..fb401d4b2 100644 --- a/src/snmalloc/mem/freelist.h +++ b/src/snmalloc/mem/freelist.h @@ -187,7 +187,7 @@ namespace snmalloc signed_prev(address_cast(this), address_cast(n_tame), key)); } } - Aal::prefetch(&(n_tame->next_object)); + Aal::prefetch(n_tame.unsafe_ptr()); return n_tame; } From 3ce3a23d41fa8522e8c436ef85f7d4d66079c910 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 12 Jun 2024 10:32:45 +0100 Subject: [PATCH 19/26] More miracle_ptr into a namespace --- src/test/func/miracle_ptr/miracle_ptr.cc | 114 ++++++++++++----------- 1 file changed, 58 insertions(+), 56 deletions(-) diff --git a/src/test/func/miracle_ptr/miracle_ptr.cc b/src/test/func/miracle_ptr/miracle_ptr.cc index 8ed56fb2b..a55835122 100644 --- a/src/test/func/miracle_ptr/miracle_ptr.cc +++ b/src/test/func/miracle_ptr/miracle_ptr.cc @@ -111,7 +111,63 @@ namespace snmalloc::miracle std::cout << "Freeing from release " << p << std::endl; snmalloc::libc::free(p); } -}; + + /** + * This class can be used to replace a raw pointer. It will automatically use + * the underlying backup reference counting design from the miracle pointer + * docs. + */ + template + class raw_ptr + { + T* p; + + public: + raw_ptr() : p(nullptr) {} + + raw_ptr(T* p) : p(p) + { + snmalloc::miracle::acquire(p); + } + + T& operator*() + { + return *p; + } + + ~raw_ptr() + { + if (p == nullptr) + return; + snmalloc::miracle::release(p); + } + + raw_ptr(const raw_ptr& rp) : p(rp.p) + { + snmalloc::miracle::acquire(p); + } + + raw_ptr& operator=(const raw_ptr& other) + { + p = other.p; + snmalloc::miracle::acquire(other.p); + return *this; + } + + raw_ptr(raw_ptr&& other) : p(other.p) + { + other.p = nullptr; + } + + raw_ptr& operator=(raw_ptr&& other) + { + p = other.p; + other.p = nullptr; + return *this; + } + }; +} // namespace snmalloc::miracle + /** * Overload new and delete to use the "miracle pointer" implementation. @@ -131,65 +187,11 @@ void operator delete(void* p, size_t) snmalloc::miracle::free(p); } -/** - * This class can be used to replace a raw pointer. It will automatically use - * the underlying backup reference counting design from the miracle pointer - * docs. - */ -template -class raw_ptr -{ - T* p; - -public: - raw_ptr() : p(nullptr) {} - - raw_ptr(T* p) : p(p) - { - snmalloc::miracle::acquire(p); - } - - T& operator*() - { - return *p; - } - - ~raw_ptr() - { - if (p == nullptr) - return; - snmalloc::miracle::release(p); - } - - raw_ptr(const raw_ptr& rp) : p(rp.p) - { - snmalloc::miracle::acquire(p); - } - - raw_ptr& operator=(const raw_ptr& other) - { - p = other.p; - snmalloc::miracle::acquire(other.p); - return *this; - } - - raw_ptr(raw_ptr&& other) : p(other.p) - { - other.p = nullptr; - } - - raw_ptr& operator=(raw_ptr&& other) - { - p = other.p; - other.p = nullptr; - return *this; - } -}; int main() { # ifndef SNMALLOC_PASS_THROUGH - raw_ptr p; + snmalloc::miracle::raw_ptr p; { auto up = std::make_unique(42); auto up2 = std::make_unique(41); From 8f33229e44b9d202e5261ecb073d9b295358e5d0 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 12 Jun 2024 11:12:57 +0100 Subject: [PATCH 20/26] Change backend to calculate extra bytes required for the metaslab. --- src/snmalloc/backend/backend.h | 10 ++++++++-- src/snmalloc/mem/backend_concept.h | 10 ++++++---- src/snmalloc/mem/corealloc.h | 12 +++--------- src/snmalloc/mem/localalloc.h | 3 ++- src/snmalloc/mem/metadata.h | 17 +++++++++++------ 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/snmalloc/backend/backend.h b/src/snmalloc/backend/backend.h index 600777883..08b80ee99 100644 --- a/src/snmalloc/backend/backend.h +++ b/src/snmalloc/backend/backend.h @@ -91,11 +91,14 @@ namespace snmalloc LocalState& local_state, size_t size, uintptr_t ras, - size_t extra_bytes = 0) + sizeclass_t sizeclass) { SNMALLOC_ASSERT(bits::is_pow2(size)); SNMALLOC_ASSERT(size >= MIN_CHUNK_SIZE); + // Calculate the extra bytes required to store the client meta-data. + size_t extra_bytes = SlabMetadata::get_extra_bytes(sizeclass); + auto meta_size = bits::next_pow2(sizeof(SlabMetadata) + extra_bytes); #ifdef SNMALLOC_TRACING @@ -148,7 +151,7 @@ namespace snmalloc SlabMetadata& slab_metadata, capptr::Alloc alloc, size_t size, - size_t extra_bytes = 0) + sizeclass_t sizeclass) { /* * The backend takes possession of these chunks now, by disassociating @@ -175,6 +178,9 @@ namespace snmalloc */ capptr::Arena arena = Authmap::amplify(alloc); + // Calculate the extra bytes required to store the client meta-data. + size_t extra_bytes = SlabMetadata::get_extra_bytes(sizeclass); + auto meta_size = bits::next_pow2(sizeof(SlabMetadata) + extra_bytes); local_state.get_meta_range().dealloc_range( capptr::Arena::unsafe_from(&slab_metadata), meta_size); diff --git a/src/snmalloc/mem/backend_concept.h b/src/snmalloc/mem/backend_concept.h index 1680391f7..32699f6d0 100644 --- a/src/snmalloc/mem/backend_concept.h +++ b/src/snmalloc/mem/backend_concept.h @@ -2,6 +2,7 @@ #ifdef __cpp_concepts # include "../ds/ds.h" +# include "sizeclasstable.h" # include namespace snmalloc @@ -97,9 +98,9 @@ namespace snmalloc template concept IsBackend = - requires(LocalState& local_state, size_t size, uintptr_t ras) { + requires(LocalState& local_state, size_t size, uintptr_t ras, sizeclass_t sizeclass) { { - Backend::alloc_chunk(local_state, size, ras) + Backend::alloc_chunk(local_state, size, ras, sizeclass) } -> ConceptSame< std::pair, typename Backend::SlabMetadata*>>; } && @@ -112,9 +113,10 @@ namespace snmalloc LocalState& local_state, typename Backend::SlabMetadata& slab_metadata, capptr::Alloc alloc, - size_t size) { + size_t size, + sizeclass_t sizeclass) { { - Backend::dealloc_chunk(local_state, slab_metadata, alloc, size) + Backend::dealloc_chunk(local_state, slab_metadata, alloc, size, sizeclass) } -> ConceptSame; } && requires(address_t p) { diff --git a/src/snmalloc/mem/corealloc.h b/src/snmalloc/mem/corealloc.h index a07834d12..2577eab3b 100644 --- a/src/snmalloc/mem/corealloc.h +++ b/src/snmalloc/mem/corealloc.h @@ -364,15 +364,12 @@ namespace snmalloc // don't touch the cache lines at this point in snmalloc_check_client. auto start = clear_slab(meta, sizeclass); - // Calculate the extra bytes required to store the client meta-data. - size_t extra_bytes = BackendSlabMetadata::get_extra_bytes(sizeclass); - Config::Backend::dealloc_chunk( get_backend_local_state(), *meta, start, sizeclass_to_slab_size(sizeclass), - extra_bytes); + sizeclass_t::from_small_class(sizeclass)); }); } @@ -405,7 +402,7 @@ namespace snmalloc meta->node.remove(); Config::Backend::dealloc_chunk( - get_backend_local_state(), *meta, p, size, 0); + get_backend_local_state(), *meta, p, size, entry.get_sizeclass()); return; } @@ -796,15 +793,12 @@ namespace snmalloc message<1024>("small_alloc_slow rsize={} slab size={}", rsize, slab_size); #endif - // Calculate the extra bytes required to store the client meta-data. - size_t extra_bytes = BackendSlabMetadata::get_extra_bytes(sizeclass); - auto [slab, meta] = Config::Backend::alloc_chunk( get_backend_local_state(), slab_size, PagemapEntry::encode( public_state(), sizeclass_t::from_small_class(sizeclass)), - extra_bytes); + sizeclass_t::from_small_class(sizeclass)); if (slab == nullptr) { diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index 7689c5563..768d8731a 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -197,7 +197,8 @@ namespace snmalloc core_alloc->get_backend_local_state(), large_size_to_chunk_size(size), PagemapEntry::encode( - core_alloc->public_state(), size_to_sizeclass_full(size))); + core_alloc->public_state(), size_to_sizeclass_full(size)), + size_to_sizeclass_full(size)); // set up meta data so sizeclass is correct, and hence alloc size, and // external pointer. #ifdef SNMALLOC_TRACING diff --git a/src/snmalloc/mem/metadata.h b/src/snmalloc/mem/metadata.h index 0b45b5e49..57c2fd09c 100644 --- a/src/snmalloc/mem/metadata.h +++ b/src/snmalloc/mem/metadata.h @@ -615,12 +615,17 @@ namespace snmalloc return result; } - static size_t get_extra_bytes(smallsizeclass_t sizeclass) - { - // We remove one from the extra-bytes as there is one in the metadata to - // start with. - return (get_client_storage_count(sizeclass) - 1) * - sizeof(typename ClientMeta::StorageType); + static size_t get_extra_bytes(sizeclass_t sizeclass) + { + if (sizeclass.is_small()) + // We remove one from the extra-bytes as there is one in the metadata to + // start with. + return (get_client_storage_count(sizeclass.as_small()) - 1) * + sizeof(typename ClientMeta::StorageType); + + // For large classes there is only a single entry, so this is covered by the + // existing entry in the metaslab, and further bytes are not required. + return 0; } }; From d90c19b543b1dc4d9b05bcf4df08aee7bfc3a3c8 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 12 Jun 2024 11:29:13 +0100 Subject: [PATCH 21/26] Improved test comments --- src/test/func/client_meta/client_meta.cc | 11 +++++++++++ src/test/func/miracle_ptr/miracle_ptr.cc | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/test/func/client_meta/client_meta.cc b/src/test/func/client_meta/client_meta.cc index 2c055ed34..aeaed9761 100644 --- a/src/test/func/client_meta/client_meta.cc +++ b/src/test/func/client_meta/client_meta.cc @@ -1,3 +1,7 @@ +/** + * This test performs a very simple use of the client_meta data feature in snmalloc. + */ + #include "test/setup.h" #include @@ -7,6 +11,7 @@ namespace snmalloc { + // Create an allocator that stores an std::atomic> per allocation. using Alloc = snmalloc::LocalAllocator>>>; } @@ -19,6 +24,7 @@ int main() // This test does not make sense in pass-through return 0; #else + // Allocate a bunch of objects, and store the index into the meta-data. std::vector ptrs; for (size_t i = 0; i < 10000; i++) { @@ -29,6 +35,8 @@ int main() memset(p, (uint8_t)i, 1024); } + // Check meta-data contains expected value, and that the memory contains + // the expected pattern. for (size_t i = 0; i < 10000; i++) { auto p = ptrs[i]; @@ -49,8 +57,11 @@ int main() snmalloc::libc::free(p); } + // Access in a read-only way meta-data associated with the stack. + // This would fail if it was accessed for write. auto& meta = snmalloc::libc::get_client_meta_data_const(&ptrs); std::cout << "meta for stack" << meta << std::endl; + return 0; #endif } diff --git a/src/test/func/miracle_ptr/miracle_ptr.cc b/src/test/func/miracle_ptr/miracle_ptr.cc index a55835122..8111b1b2d 100644 --- a/src/test/func/miracle_ptr/miracle_ptr.cc +++ b/src/test/func/miracle_ptr/miracle_ptr.cc @@ -193,8 +193,10 @@ int main() # ifndef SNMALLOC_PASS_THROUGH snmalloc::miracle::raw_ptr p; { + auto up1 = std::make_unique(41); auto up = std::make_unique(42); - auto up2 = std::make_unique(41); + auto up2 = std::make_unique(40); + auto up3 = std::make_unique(39); p = up.get(); check(*p == 42, "Failed to set p"); } From 739fae25954b0ee9975dc7afed9d925744502510 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 12 Jun 2024 11:40:24 +0100 Subject: [PATCH 22/26] Clang format --- src/snmalloc/backend/backend.h | 2 +- src/snmalloc/mem/backend_concept.h | 9 +++++++-- src/snmalloc/mem/metadata.h | 6 +++--- src/test/func/client_meta/client_meta.cc | 3 ++- src/test/func/miracle_ptr/miracle_ptr.cc | 2 -- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/snmalloc/backend/backend.h b/src/snmalloc/backend/backend.h index 08b80ee99..883c471c6 100644 --- a/src/snmalloc/backend/backend.h +++ b/src/snmalloc/backend/backend.h @@ -98,7 +98,7 @@ namespace snmalloc // Calculate the extra bytes required to store the client meta-data. size_t extra_bytes = SlabMetadata::get_extra_bytes(sizeclass); - + auto meta_size = bits::next_pow2(sizeof(SlabMetadata) + extra_bytes); #ifdef SNMALLOC_TRACING diff --git a/src/snmalloc/mem/backend_concept.h b/src/snmalloc/mem/backend_concept.h index 32699f6d0..f1b428607 100644 --- a/src/snmalloc/mem/backend_concept.h +++ b/src/snmalloc/mem/backend_concept.h @@ -98,7 +98,11 @@ namespace snmalloc template concept IsBackend = - requires(LocalState& local_state, size_t size, uintptr_t ras, sizeclass_t sizeclass) { + requires( + LocalState& local_state, + size_t size, + uintptr_t ras, + sizeclass_t sizeclass) { { Backend::alloc_chunk(local_state, size, ras, sizeclass) } -> ConceptSame< @@ -116,7 +120,8 @@ namespace snmalloc size_t size, sizeclass_t sizeclass) { { - Backend::dealloc_chunk(local_state, slab_metadata, alloc, size, sizeclass) + Backend::dealloc_chunk( + local_state, slab_metadata, alloc, size, sizeclass) } -> ConceptSame; } && requires(address_t p) { diff --git a/src/snmalloc/mem/metadata.h b/src/snmalloc/mem/metadata.h index 57c2fd09c..d9e2e39ab 100644 --- a/src/snmalloc/mem/metadata.h +++ b/src/snmalloc/mem/metadata.h @@ -622,9 +622,9 @@ namespace snmalloc // start with. return (get_client_storage_count(sizeclass.as_small()) - 1) * sizeof(typename ClientMeta::StorageType); - - // For large classes there is only a single entry, so this is covered by the - // existing entry in the metaslab, and further bytes are not required. + + // For large classes there is only a single entry, so this is covered by + // the existing entry in the metaslab, and further bytes are not required. return 0; } }; diff --git a/src/test/func/client_meta/client_meta.cc b/src/test/func/client_meta/client_meta.cc index aeaed9761..88f3cc319 100644 --- a/src/test/func/client_meta/client_meta.cc +++ b/src/test/func/client_meta/client_meta.cc @@ -1,5 +1,6 @@ /** - * This test performs a very simple use of the client_meta data feature in snmalloc. + * This test performs a very simple use of the client_meta data feature in + * snmalloc. */ #include "test/setup.h" diff --git a/src/test/func/miracle_ptr/miracle_ptr.cc b/src/test/func/miracle_ptr/miracle_ptr.cc index 8111b1b2d..45e383828 100644 --- a/src/test/func/miracle_ptr/miracle_ptr.cc +++ b/src/test/func/miracle_ptr/miracle_ptr.cc @@ -168,7 +168,6 @@ namespace snmalloc::miracle }; } // namespace snmalloc::miracle - /** * Overload new and delete to use the "miracle pointer" implementation. */ @@ -187,7 +186,6 @@ void operator delete(void* p, size_t) snmalloc::miracle::free(p); } - int main() { # ifndef SNMALLOC_PASS_THROUGH From 4da5b35c691e5a5d8dbba1b8fa4b545e993ea9b7 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 12 Jun 2024 13:40:22 +0100 Subject: [PATCH 23/26] Fix TSAN for CI --- CMakeLists.txt | 3 +++ src/snmalloc/ds/mpmcstack.h | 6 ------ src/snmalloc/ds/pagemap.h | 8 ++++++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5697790cc..745ae198f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -331,6 +331,9 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY) if(SNMALLOC_SANITIZER) target_compile_options(${TESTNAME} PRIVATE -g -fsanitize=${SNMALLOC_SANITIZER} -fno-omit-frame-pointer) target_link_libraries(${TESTNAME} -fsanitize=${SNMALLOC_SANITIZER}) + if (${SNMALLOC_SANITIZER} MATCHES "thread") + target_compile_definitions(${TESTNAME} PRIVATE SNMALLOC_THREAD_SANITIZER_ENABLED) + endif() endif() add_warning_flags(${TESTNAME}) diff --git a/src/snmalloc/ds/mpmcstack.h b/src/snmalloc/ds/mpmcstack.h index cd005e9bf..e6a3b1d9f 100644 --- a/src/snmalloc/ds/mpmcstack.h +++ b/src/snmalloc/ds/mpmcstack.h @@ -4,12 +4,6 @@ #include "aba.h" #include "allocconfig.h" -#if defined(__has_feature) -# if __has_feature(thread_sanitizer) -# define SNMALLOC_THREAD_SANITIZER_ENABLED -# endif -#endif - namespace snmalloc { template diff --git a/src/snmalloc/ds/pagemap.h b/src/snmalloc/ds/pagemap.h index 267fe9a0b..abfc539d0 100644 --- a/src/snmalloc/ds/pagemap.h +++ b/src/snmalloc/ds/pagemap.h @@ -1,5 +1,7 @@ #pragma once +#include "../ds_core/ds_core.h" + namespace snmalloc { /** @@ -179,7 +181,13 @@ namespace snmalloc // Allocate a power of two extra to allow the placement of the // pagemap be difficult to guess if randomize_position set. size_t additional_size = +#ifdef SNMALLOC_THREAD_SANITIZER_ENABLED + // When running with TSAN we failed to allocate the very large range + // randomly + randomize_position ? bits::next_pow2(REQUIRED_SIZE) : 0; +#else randomize_position ? bits::next_pow2(REQUIRED_SIZE) * 4 : 0; +#endif size_t request_size = REQUIRED_SIZE + additional_size; auto new_body_untyped = PAL::reserve(request_size); From 6c90e21fa6a952eb83514e68e5a3b4a9ccb7ba0e Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 12 Jun 2024 13:49:18 +0100 Subject: [PATCH 24/26] Use const adding template --- src/snmalloc/backend_helpers/commonconfig.h | 2 -- src/snmalloc/global/libc.h | 2 +- src/snmalloc/mem/localalloc.h | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/snmalloc/backend_helpers/commonconfig.h b/src/snmalloc/backend_helpers/commonconfig.h index 818e95134..5ef023ce0 100644 --- a/src/snmalloc/backend_helpers/commonconfig.h +++ b/src/snmalloc/backend_helpers/commonconfig.h @@ -99,7 +99,6 @@ namespace snmalloc { using StorageType = Empty; using DataRef = Empty&; - using ConstDataRef = const Empty&; static size_t required_count(size_t) { @@ -117,7 +116,6 @@ namespace snmalloc { using StorageType = T; using DataRef = T&; - using ConstDataRef = const T&; static size_t required_count(size_t max_count) { diff --git a/src/snmalloc/global/libc.h b/src/snmalloc/global/libc.h index 603d81fed..700fd3901 100644 --- a/src/snmalloc/global/libc.h +++ b/src/snmalloc/global/libc.h @@ -183,7 +183,7 @@ namespace snmalloc::libc return ThreadAlloc::get().get_client_meta_data(p); } - inline typename snmalloc::Alloc::Config::ClientMeta::ConstDataRef + inline std::add_const_t get_client_meta_data_const(void* p) { return ThreadAlloc::get().get_client_meta_data_const(p); diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index 768d8731a..6f99c7bc9 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -843,7 +843,7 @@ namespace snmalloc * @brief Get the client meta data for the snmalloc allocation covering this * pointer. */ - typename Config::ClientMeta::ConstDataRef + std::add_const_t get_client_meta_data_const(void* p) { const PagemapEntry& entry = From 318c9dda00534576140b1066926d17fdd6a4e8d8 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 12 Jun 2024 14:30:05 +0100 Subject: [PATCH 25/26] Missing TSAN define refactor. --- src/test/func/miracle_ptr/miracle_ptr.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/func/miracle_ptr/miracle_ptr.cc b/src/test/func/miracle_ptr/miracle_ptr.cc index 45e383828..6d4ea26af 100644 --- a/src/test/func/miracle_ptr/miracle_ptr.cc +++ b/src/test/func/miracle_ptr/miracle_ptr.cc @@ -5,11 +5,6 @@ * * Do not use as is. */ -#if defined(__has_feature) -# if __has_feature(thread_sanitizer) -# define SNMALLOC_THREAD_SANITIZER_ENABLED -# endif -#endif #ifdef SNMALLOC_THREAD_SANITIZER_ENABLED int main() From a93cd15d740f450b48fc770929242a332bf82a47 Mon Sep 17 00:00:00 2001 From: Matthew Parkinson Date: Wed, 12 Jun 2024 15:17:43 +0100 Subject: [PATCH 26/26] Protect against use-after-free access to meta data. --- src/snmalloc/mem/localalloc.h | 8 +++++++- src/snmalloc/mem/metadata.h | 4 +--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/snmalloc/mem/localalloc.h b/src/snmalloc/mem/localalloc.h index 6f99c7bc9..e0096d80d 100644 --- a/src/snmalloc/mem/localalloc.h +++ b/src/snmalloc/mem/localalloc.h @@ -830,6 +830,11 @@ namespace snmalloc auto* meta_slab = entry.get_slab_metadata(); + if (SNMALLOC_UNLIKELY(entry.is_backend_owned())) + { + error("Cannot access meta-data for write for freed memory!"); + } + if (SNMALLOC_UNLIKELY(meta_slab == nullptr)) { error( @@ -853,7 +858,8 @@ namespace snmalloc auto* meta_slab = entry.get_slab_metadata(); - if (SNMALLOC_UNLIKELY(meta_slab == nullptr)) + if (SNMALLOC_UNLIKELY( + (meta_slab == nullptr) || (entry.is_backend_owned()))) { static typename Config::ClientMeta::StorageType null_meta_store{}; return Config::ClientMeta::get(&null_meta_store, 0); diff --git a/src/snmalloc/mem/metadata.h b/src/snmalloc/mem/metadata.h index d9e2e39ab..10fa93e37 100644 --- a/src/snmalloc/mem/metadata.h +++ b/src/snmalloc/mem/metadata.h @@ -690,9 +690,7 @@ namespace snmalloc */ [[nodiscard]] SNMALLOC_FAST_PATH SlabMetadata* get_slab_metadata() const { - // TODO Following assertion removed for client meta-data use case. - // Think about possible UAF scenarios. - // SNMALLOC_ASSERT(get_remote() != nullptr); + SNMALLOC_ASSERT(!is_backend_owned()); return unsafe_from_uintptr(meta & ~META_BOUNDARY_BIT); } };