diff --git a/intern/guardedalloc/CMakeLists.txt b/intern/guardedalloc/CMakeLists.txt index d622adab486..d9ba4acf414 100644 --- a/intern/guardedalloc/CMakeLists.txt +++ b/intern/guardedalloc/CMakeLists.txt @@ -28,6 +28,7 @@ set(SRC MEM_guardedalloc.h ./intern/mallocn_inline.hh ./intern/mallocn_intern.hh + ./intern/mallocn_intern_function_pointers.hh # only so the header is known by cmake ../atomic/atomic_ops.h diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index c02ff5371bb..55f8ab926f0 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -54,7 +54,7 @@ extern size_t (*MEM_allocN_len)(const void *vmemh) ATTR_WARN_UNUSED_RESULT; /** * Release memory previously allocated by this module. */ -extern void (*MEM_freeN)(void *vmemh); +void MEM_freeN(void *vmemh); #if 0 /* UNUSED */ /** @@ -129,9 +129,9 @@ extern void *(*MEM_malloc_arrayN)(size_t len, * Allocate an aligned block of memory of size len, with tag name str. The * name must be a static, because only a pointer to it is stored! */ -extern void *(*MEM_mallocN_aligned)(size_t len, - size_t alignment, - const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT +void *MEM_mallocN_aligned(size_t len, + size_t alignment, + const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3); /** @@ -274,6 +274,8 @@ void MEM_use_guarded_allocator(void); # include # include +# include "intern/mallocn_intern_function_pointers.hh" + /** * Conservative value of memory alignment returned by non-aligned OS-level memory allocation * functions. For alignments smaller than this value, using non-aligned versions of allocator API @@ -297,7 +299,8 @@ void MEM_use_guarded_allocator(void); template inline T *MEM_new(const char *allocation_name, Args &&...args) { - void *buffer = MEM_mallocN_aligned(sizeof(T), alignof(T), allocation_name); + void *buffer = mem_guarded::internal::mem_mallocN_aligned_ex( + sizeof(T), alignof(T), allocation_name, mem_guarded::internal::AllocationType::NEW_DELETE); return new (buffer) T(std::forward(args)...); } @@ -315,7 +318,8 @@ template inline void MEM_delete(const T *ptr) } /* C++ allows destruction of `const` objects, so the pointer is allowed to be `const`. */ ptr->~T(); - MEM_freeN(const_cast(ptr)); + mem_guarded::internal::mem_freeN_ex(const_cast(ptr), + mem_guarded::internal::AllocationType::NEW_DELETE); } /** @@ -365,30 +369,45 @@ template inline T *MEM_cnew(const char *allocation_name, const T &ot public: \ void *operator new(size_t num_bytes) \ { \ - return MEM_mallocN_aligned(num_bytes, __STDCPP_DEFAULT_NEW_ALIGNMENT__, _id); \ + return mem_guarded::internal::mem_mallocN_aligned_ex( \ + num_bytes, \ + __STDCPP_DEFAULT_NEW_ALIGNMENT__, \ + _id, \ + mem_guarded::internal::AllocationType::NEW_DELETE); \ } \ void *operator new(size_t num_bytes, std::align_val_t alignment) \ { \ - return MEM_mallocN_aligned(num_bytes, size_t(alignment), _id); \ + return mem_guarded::internal::mem_mallocN_aligned_ex( \ + num_bytes, size_t(alignment), _id, mem_guarded::internal::AllocationType::NEW_DELETE); \ } \ void operator delete(void *mem) \ { \ if (mem) { \ - MEM_freeN(mem); \ + mem_guarded::internal::mem_freeN_ex(mem, \ + mem_guarded::internal::AllocationType::NEW_DELETE); \ } \ } \ void *operator new[](size_t num_bytes) \ { \ - return MEM_mallocN_aligned(num_bytes, __STDCPP_DEFAULT_NEW_ALIGNMENT__, _id "[]"); \ + return mem_guarded::internal::mem_mallocN_aligned_ex( \ + num_bytes, \ + __STDCPP_DEFAULT_NEW_ALIGNMENT__, \ + _id "[]", \ + mem_guarded::internal::AllocationType::NEW_DELETE); \ } \ void *operator new[](size_t num_bytes, std::align_val_t alignment) \ { \ - return MEM_mallocN_aligned(num_bytes, size_t(alignment), _id "[]"); \ + return mem_guarded::internal::mem_mallocN_aligned_ex( \ + num_bytes, \ + size_t(alignment), \ + _id "[]", \ + mem_guarded::internal::AllocationType::NEW_DELETE); \ } \ void operator delete[](void *mem) \ { \ if (mem) { \ - MEM_freeN(mem); \ + mem_guarded::internal::mem_freeN_ex(mem, \ + mem_guarded::internal::AllocationType::NEW_DELETE); \ } \ } \ void *operator new(size_t /*count*/, void *ptr) \ diff --git a/intern/guardedalloc/cpp/mallocn.cpp b/intern/guardedalloc/cpp/mallocn.cpp index a86704adf91..71d1ed9f9f6 100644 --- a/intern/guardedalloc/cpp/mallocn.cpp +++ b/intern/guardedalloc/cpp/mallocn.cpp @@ -6,42 +6,46 @@ * \ingroup intern_mem */ -#include "../MEM_guardedalloc.h" +#include #include +#include "../intern/mallocn_intern_function_pointers.hh" + +using namespace mem_guarded::internal; + void *operator new(size_t size, const char *str); void *operator new[](size_t size, const char *str); /* not default but can be used when needing to set a string */ void *operator new(size_t size, const char *str) { - return MEM_mallocN(size, str); + return mem_mallocN_aligned_ex(size, 1, str, AllocationType::NEW_DELETE); } void *operator new[](size_t size, const char *str) { - return MEM_mallocN(size, str); + return mem_mallocN_aligned_ex(size, 1, str, AllocationType::NEW_DELETE); } void *operator new(size_t size) { - return MEM_mallocN(size, "C++/anonymous"); + return mem_mallocN_aligned_ex(size, 1, "C++/anonymous", AllocationType::NEW_DELETE); } void *operator new[](size_t size) { - return MEM_mallocN(size, "C++/anonymous[]"); + return mem_mallocN_aligned_ex(size, 1, "C++/anonymous[]", AllocationType::NEW_DELETE); } void operator delete(void *p) throw() { - /* delete NULL is valid in c++ */ + /* `delete nullptr` is valid in c++. */ if (p) { - MEM_freeN(p); + mem_freeN_ex(p, AllocationType::NEW_DELETE); } } void operator delete[](void *p) throw() { - /* delete NULL is valid in c++ */ + /* `delete nullptr` is valid in c++. */ if (p) { - MEM_freeN(p); + mem_freeN_ex(p, AllocationType::NEW_DELETE); } } diff --git a/intern/guardedalloc/intern/mallocn.cc b/intern/guardedalloc/intern/mallocn.cc index a331d39b658..193f531e683 100644 --- a/intern/guardedalloc/intern/mallocn.cc +++ b/intern/guardedalloc/intern/mallocn.cc @@ -17,6 +17,10 @@ #include "mallocn_intern.hh" +#include "mallocn_intern_function_pointers.hh" + +using namespace mem_guarded::internal; + #ifdef WITH_JEMALLOC_CONF /** * If JEMALLOC is used, it reads this global variable and enables background @@ -30,7 +34,8 @@ const char *malloc_conf = /* NOTE: Keep in sync with MEM_use_lockfree_allocator(). */ size_t (*MEM_allocN_len)(const void *vmemh) = MEM_lockfree_allocN_len; -void (*MEM_freeN)(void *vmemh) = MEM_lockfree_freeN; +void (*mem_guarded::internal::mem_freeN_ex)(void *vmemh, + AllocationType allocation_type) = MEM_lockfree_freeN; void *(*MEM_dupallocN)(const void *vmemh) = MEM_lockfree_dupallocN; void *(*MEM_reallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_reallocN_id; void *(*MEM_recallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_recallocN_id; @@ -38,9 +43,11 @@ void *(*MEM_callocN)(size_t len, const char *str) = MEM_lockfree_callocN; void *(*MEM_calloc_arrayN)(size_t len, size_t size, const char *str) = MEM_lockfree_calloc_arrayN; void *(*MEM_mallocN)(size_t len, const char *str) = MEM_lockfree_mallocN; void *(*MEM_malloc_arrayN)(size_t len, size_t size, const char *str) = MEM_lockfree_malloc_arrayN; -void *(*MEM_mallocN_aligned)(size_t len, - size_t alignment, - const char *str) = MEM_lockfree_mallocN_aligned; +void *(*mem_guarded::internal::mem_mallocN_aligned_ex)(size_t len, + size_t alignment, + const char *str, + AllocationType allocation_type) = + MEM_lockfree_mallocN_aligned; void *(*MEM_calloc_arrayN_aligned)(size_t len, size_t size, size_t alignment, @@ -95,6 +102,16 @@ void aligned_free(void *ptr) #endif } +void MEM_freeN(void *vmemh) +{ + mem_freeN_ex(vmemh, AllocationType::ALLOC_FREE); +} + +void *MEM_mallocN_aligned(size_t len, size_t alignment, const char *str) +{ + return mem_mallocN_aligned_ex(len, alignment, str, AllocationType::ALLOC_FREE); +} + /** * Perform assert checks on allocator type change. * @@ -120,7 +137,7 @@ void MEM_use_lockfree_allocator() assert_for_allocator_change(); MEM_allocN_len = MEM_lockfree_allocN_len; - MEM_freeN = MEM_lockfree_freeN; + mem_freeN_ex = MEM_lockfree_freeN; MEM_dupallocN = MEM_lockfree_dupallocN; MEM_reallocN_id = MEM_lockfree_reallocN_id; MEM_recallocN_id = MEM_lockfree_recallocN_id; @@ -128,7 +145,7 @@ void MEM_use_lockfree_allocator() MEM_calloc_arrayN = MEM_lockfree_calloc_arrayN; MEM_mallocN = MEM_lockfree_mallocN; MEM_malloc_arrayN = MEM_lockfree_malloc_arrayN; - MEM_mallocN_aligned = MEM_lockfree_mallocN_aligned; + mem_mallocN_aligned_ex = MEM_lockfree_mallocN_aligned; MEM_calloc_arrayN_aligned = MEM_lockfree_calloc_arrayN_aligned; MEM_printmemlist_pydict = MEM_lockfree_printmemlist_pydict; MEM_printmemlist = MEM_lockfree_printmemlist; @@ -155,7 +172,7 @@ void MEM_use_guarded_allocator() assert_for_allocator_change(); MEM_allocN_len = MEM_guarded_allocN_len; - MEM_freeN = MEM_guarded_freeN; + mem_freeN_ex = MEM_guarded_freeN; MEM_dupallocN = MEM_guarded_dupallocN; MEM_reallocN_id = MEM_guarded_reallocN_id; MEM_recallocN_id = MEM_guarded_recallocN_id; @@ -163,7 +180,7 @@ void MEM_use_guarded_allocator() MEM_calloc_arrayN = MEM_guarded_calloc_arrayN; MEM_mallocN = MEM_guarded_mallocN; MEM_malloc_arrayN = MEM_guarded_malloc_arrayN; - MEM_mallocN_aligned = MEM_guarded_mallocN_aligned; + mem_mallocN_aligned_ex = MEM_guarded_mallocN_aligned; MEM_calloc_arrayN_aligned = MEM_guarded_calloc_arrayN_aligned; MEM_printmemlist_pydict = MEM_guarded_printmemlist_pydict; MEM_printmemlist = MEM_guarded_printmemlist; diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.cc b/intern/guardedalloc/intern/mallocn_guarded_impl.cc index 74ab255adef..8f8ca9fe7e9 100644 --- a/intern/guardedalloc/intern/mallocn_guarded_impl.cc +++ b/intern/guardedalloc/intern/mallocn_guarded_impl.cc @@ -30,6 +30,9 @@ #include "atomic_ops.h" #include "mallocn_intern.hh" +#include "mallocn_intern_function_pointers.hh" + +using namespace mem_guarded::internal; /* Only for debugging: * store original buffer's name when doing MEM_dupallocN @@ -112,7 +115,7 @@ typedef struct MemHead { const char *name; const char *nextname; int tag2; - short pad1; + uint16_t flag; /* if non-zero aligned allocation was used and alignment is stored here. */ short alignment; #ifdef DEBUG_MEMCOUNTER @@ -134,6 +137,17 @@ static_assert(MEM_MIN_CPP_ALIGNMENT <= sizeof(MemHead), "Bad size of MemHead"); typedef MemHead MemHeadAligned; +/* #MemHead::flag. */ +enum MemHeadFlag { + /** + * This block of memory has been allocated from CPP `new` (e.g. #MEM_new, or some + * guardedalloc-overloaded `new` operator). It mainly checks that #MEM_freeN is not directly + * called on it (#MEM_delete or some guardedalloc-overloaded `delete` operator should always be + * used instead). + */ + MEMHEAD_FLAG_FROM_CPP_NEW = 1 << 1, +}; + typedef struct MemTail { int tag3, pad; } MemTail; @@ -275,12 +289,22 @@ void *MEM_guarded_dupallocN(const void *vmemh) const MemHead *memh = static_cast(vmemh); memh--; + if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) { + print_error( + "Attempt to use C-style MEM_dupallocN on a pointer created with CPP-style MEM_new or " + "new\n"); +#ifdef WITH_ASSERT_ABORT + abort(); +#endif + } + #ifndef DEBUG_MEMDUPLINAME if (LIKELY(memh->alignment == 0)) { newp = MEM_guarded_mallocN(memh->len, "dupli_alloc"); } else { - newp = MEM_guarded_mallocN_aligned(memh->len, size_t(memh->alignment), "dupli_alloc"); + newp = MEM_guarded_mallocN_aligned( + memh->len, size_t(memh->alignment), "dupli_alloc", AllocationType::ALLOC_FREE); } if (newp == nullptr) { @@ -300,7 +324,8 @@ void *MEM_guarded_dupallocN(const void *vmemh) newp = MEM_guarded_mallocN(memh->len, name); } else { - newp = MEM_guarded_mallocN_aligned(memh->len, (size_t)memh->alignment, name); + newp = MEM_guarded_mallocN_aligned( + memh->len, (size_t)memh->alignment, name, AllocationType::ALLOC_FREE); } if (newp == nullptr) @@ -327,11 +352,21 @@ void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str) MemHead *memh = static_cast(vmemh); memh--; + if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) { + print_error( + "Attempt to use C-style MEM_reallocN on a pointer created with CPP-style MEM_new or " + "new\n"); +#ifdef WITH_ASSERT_ABORT + abort(); +#endif + } + if (LIKELY(memh->alignment == 0)) { newp = MEM_guarded_mallocN(len, memh->name); } else { - newp = MEM_guarded_mallocN_aligned(len, size_t(memh->alignment), memh->name); + newp = MEM_guarded_mallocN_aligned( + len, size_t(memh->alignment), memh->name, AllocationType::ALLOC_FREE); } if (newp) { @@ -345,7 +380,7 @@ void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str) } } - MEM_guarded_freeN(vmemh); + MEM_guarded_freeN(vmemh, AllocationType::ALLOC_FREE); } else { newp = MEM_guarded_mallocN(len, str); @@ -362,11 +397,21 @@ void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str) MemHead *memh = static_cast(vmemh); memh--; + if ((memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) { + print_error( + "Attempt to use C-style MEM_recallocN on a pointer created with CPP-style MEM_new or " + "new\n"); +#ifdef WITH_ASSERT_ABORT + abort(); +#endif + } + if (LIKELY(memh->alignment == 0)) { newp = MEM_guarded_mallocN(len, memh->name); } else { - newp = MEM_guarded_mallocN_aligned(len, size_t(memh->alignment), memh->name); + newp = MEM_guarded_mallocN_aligned( + len, size_t(memh->alignment), memh->name, AllocationType::ALLOC_FREE); } if (newp) { @@ -385,7 +430,7 @@ void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str) } } - MEM_guarded_freeN(vmemh); + MEM_guarded_freeN(vmemh, AllocationType::ALLOC_FREE); } else { newp = MEM_guarded_callocN(len, str); @@ -414,7 +459,10 @@ static void print_memhead_backtrace(MemHead *memh) } #endif /* DEBUG_BACKTRACE_EXECINFO */ -static void make_memhead_header(MemHead *memh, size_t len, const char *str) +static void make_memhead_header(MemHead *memh, + size_t len, + const char *str, + const AllocationType allocation_type) { MemTail *memt; @@ -422,7 +470,7 @@ static void make_memhead_header(MemHead *memh, size_t len, const char *str) memh->name = str; memh->nextname = nullptr; memh->len = len; - memh->pad1 = 0; + memh->flag = (allocation_type == AllocationType::NEW_DELETE ? MEMHEAD_FLAG_FROM_CPP_NEW : 0); memh->alignment = 0; memh->tag2 = MEMTAG2; @@ -461,7 +509,7 @@ void *MEM_guarded_mallocN(size_t len, const char *str) memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail)); if (LIKELY(memh)) { - make_memhead_header(memh, len, str); + make_memhead_header(memh, len, str, AllocationType::ALLOC_FREE); if (LIKELY(len)) { if (UNLIKELY(malloc_debug_memset)) { @@ -509,7 +557,10 @@ void *MEM_guarded_malloc_arrayN(size_t len, size_t size, const char *str) return MEM_guarded_mallocN(total_size, str); } -void *MEM_guarded_mallocN_aligned(size_t len, size_t alignment, const char *str) +void *MEM_guarded_mallocN_aligned(size_t len, + size_t alignment, + const char *str, + const AllocationType allocation_type) { /* Huge alignment values doesn't make sense and they wouldn't fit into 'short' used in the * MemHead. */ @@ -548,7 +599,7 @@ void *MEM_guarded_mallocN_aligned(size_t len, size_t alignment, const char *str) */ memh = (MemHead *)((char *)memh + extra_padding); - make_memhead_header(memh, len, str); + make_memhead_header(memh, len, str, allocation_type); memh->alignment = short(alignment); if (LIKELY(len)) { if (UNLIKELY(malloc_debug_memset)) { @@ -587,7 +638,7 @@ void *MEM_guarded_callocN(size_t len, const char *str) memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1); if (memh) { - make_memhead_header(memh, len, str); + make_memhead_header(memh, len, str, AllocationType::ALLOC_FREE); #ifdef DEBUG_MEMCOUNTER if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL) memcount_raise(__func__); @@ -931,7 +982,7 @@ void mem_guarded_clearmemlist() membase->first = membase->last = nullptr; } -void MEM_guarded_freeN(void *vmemh) +void MEM_guarded_freeN(void *vmemh, const AllocationType allocation_type) { MemTail *memt; MemHead *memh = static_cast(vmemh); @@ -957,6 +1008,17 @@ void MEM_guarded_freeN(void *vmemh) } memh--; + + if (allocation_type != AllocationType::NEW_DELETE && + (memh->flag & MEMHEAD_FLAG_FROM_CPP_NEW) != 0) + { + print_error( + "Attempt to use C-style MEM_freeN on a pointer created with CPP-style MEM_new or new\n"); +#ifdef WITH_ASSERT_ABORT + abort(); +#endif + } + if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) { MemorY_ErroR(memh->name, "double free"); return; diff --git a/intern/guardedalloc/intern/mallocn_intern.hh b/intern/guardedalloc/intern/mallocn_intern.hh index 92ff8d0f8dc..b6e17005bc0 100644 --- a/intern/guardedalloc/intern/mallocn_intern.hh +++ b/intern/guardedalloc/intern/mallocn_intern.hh @@ -123,7 +123,7 @@ extern void (*mem_clearmemlist)(void); /* Prototypes for counted allocator functions */ size_t MEM_lockfree_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT; -void MEM_lockfree_freeN(void *vmemh); +void MEM_lockfree_freeN(void *vmemh, mem_guarded::internal::AllocationType allocation_type); void *MEM_lockfree_dupallocN(const void *vmemh) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, @@ -147,8 +147,9 @@ void *MEM_lockfree_malloc_arrayN(size_t len, ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3); void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, - const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT - ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3); + const char *str, + mem_guarded::internal::AllocationType allocation_type) + ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3); void *MEM_lockfree_calloc_arrayN_aligned(size_t len, size_t size, size_t alignment, @@ -175,7 +176,7 @@ void MEM_lockfree_name_ptr_set(void *vmemh, const char *str); /* Prototypes for fully guarded allocator functions */ size_t MEM_guarded_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT; -void MEM_guarded_freeN(void *vmemh); +void MEM_guarded_freeN(void *vmemh, mem_guarded::internal::AllocationType allocation_type); void *MEM_guarded_dupallocN(const void *vmemh) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; void *MEM_guarded_reallocN_id(void *vmemh, size_t len, @@ -199,8 +200,9 @@ void *MEM_guarded_malloc_arrayN(size_t len, ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(3); void *MEM_guarded_mallocN_aligned(size_t len, size_t alignment, - const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT - ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3); + const char *str, + mem_guarded::internal::AllocationType allocation_type) + ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(3); void *MEM_guarded_calloc_arrayN_aligned(size_t len, size_t size, size_t alignment, const char *str) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1, 2) ATTR_NONNULL(4); void MEM_guarded_printmemlist_pydict(void); diff --git a/intern/guardedalloc/intern/mallocn_intern_function_pointers.hh b/intern/guardedalloc/intern/mallocn_intern_function_pointers.hh new file mode 100644 index 00000000000..7c19e43d195 --- /dev/null +++ b/intern/guardedalloc/intern/mallocn_intern_function_pointers.hh @@ -0,0 +1,30 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup intern_mem + */ + +#pragma once + +namespace mem_guarded::internal { + +enum class AllocationType { + /** Allocation is handled through 'C type' alloc/free calls. */ + ALLOC_FREE, + /** Allocation is handled through 'C++ type' new/delete calls. */ + NEW_DELETE, +}; + +/** Internal implementation of #MEM_freeN, exposed because #MEM_delete needs access to it. */ +extern void (*mem_freeN_ex)(void *vmemh, AllocationType allocation_type); + +/** Internal implementation of #MEM_mallocN_aligned, exposed because #MEM_new needs access to it. + */ +extern void *(*mem_mallocN_aligned_ex)(size_t len, + size_t alignment, + const char *str, + AllocationType allocation_type); + +} // namespace mem_guarded::internal diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.cc b/intern/guardedalloc/intern/mallocn_lockfree_impl.cc index e2197ab1d83..a6cc4b5bfba 100644 --- a/intern/guardedalloc/intern/mallocn_lockfree_impl.cc +++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.cc @@ -27,6 +27,9 @@ #include "atomic_ops.h" #include "mallocn_intern.hh" +#include "mallocn_intern_function_pointers.hh" + +using namespace mem_guarded::internal; typedef struct MemHead { /* Length of allocated memory block. */ @@ -46,15 +49,30 @@ static bool malloc_debug_memset = false; static void (*error_callback)(const char *) = nullptr; +/** + * Guardedalloc always allocate multiple of 4 bytes. That means that the lower 2 bits of the + * `len` member of #MemHead/#MemHeadAligned data can be used for the bitflags below. + */ enum { - MEMHEAD_ALIGN_FLAG = 1, + /** This block used aligned allocation, and its 'head' is of #MemHeadAligned type. */ + MEMHEAD_FLAG_ALIGN = 1 << 0, + /** + * This block of memory has been allocated from CPP `new` (e.g. #MEM_new, or some + * guardedalloc-overloaded `new` operator). It mainly checks that #MEM_freeN is not directly + * called on it (#MEM_delete or some guardedalloc-overloaded `delete` operator should always be + * used instead). + */ + MEMHEAD_FLAG_FROM_CPP_NEW = 1 << 1, + + MEMHEAD_FLAG_MASK = (1 << 2) - 1 }; #define MEMHEAD_FROM_PTR(ptr) (((MemHead *)ptr) - 1) #define PTR_FROM_MEMHEAD(memhead) (memhead + 1) #define MEMHEAD_ALIGNED_FROM_PTR(ptr) (((MemHeadAligned *)ptr) - 1) -#define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & size_t(MEMHEAD_ALIGN_FLAG)) -#define MEMHEAD_LEN(memhead) ((memhead)->len & ~size_t(MEMHEAD_ALIGN_FLAG)) +#define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & size_t(MEMHEAD_FLAG_ALIGN)) +#define MEMHEAD_IS_FROM_CPP_NEW(memhead) ((memhead)->len & size_t(MEMHEAD_FLAG_FROM_CPP_NEW)) +#define MEMHEAD_LEN(memhead) ((memhead)->len & ~size_t(MEMHEAD_FLAG_MASK)) #ifdef __GNUC__ __attribute__((format(printf, 1, 2))) @@ -84,7 +102,7 @@ size_t MEM_lockfree_allocN_len(const void *vmemh) return 0; } -void MEM_lockfree_freeN(void *vmemh) +void MEM_lockfree_freeN(void *vmemh, AllocationType allocation_type) { if (UNLIKELY(leak_detector_has_run)) { print_error("%s\n", free_after_leak_detection_message); @@ -101,6 +119,14 @@ void MEM_lockfree_freeN(void *vmemh) MemHead *memh = MEMHEAD_FROM_PTR(vmemh); size_t len = MEMHEAD_LEN(memh); + if (allocation_type != AllocationType::NEW_DELETE && MEMHEAD_IS_FROM_CPP_NEW(memh)) { + print_error( + "Attempt to use C-style MEM_freeN on a pointer created with CPP-style MEM_new or new\n"); +#ifdef WITH_ASSERT_ABORT + abort(); +#endif + } + memory_usage_block_free(len); if (UNLIKELY(malloc_debug_memset && len)) { @@ -121,10 +147,20 @@ void *MEM_lockfree_dupallocN(const void *vmemh) if (vmemh) { const MemHead *memh = MEMHEAD_FROM_PTR(vmemh); const size_t prev_size = MEM_lockfree_allocN_len(vmemh); + + if (MEMHEAD_IS_FROM_CPP_NEW(memh)) { + print_error( + "Attempt to use C-style MEM_dupallocN on a pointer created with CPP-style MEM_new or " + "new\n"); +#ifdef WITH_ASSERT_ABORT + abort(); +#endif + } + if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) { const MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh); newp = MEM_lockfree_mallocN_aligned( - prev_size, size_t(memh_aligned->alignment), "dupli_malloc"); + prev_size, size_t(memh_aligned->alignment), "dupli_malloc", AllocationType::ALLOC_FREE); } else { newp = MEM_lockfree_mallocN(prev_size, "dupli_malloc"); @@ -142,12 +178,22 @@ void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str) const MemHead *memh = MEMHEAD_FROM_PTR(vmemh); const size_t old_len = MEM_lockfree_allocN_len(vmemh); + if (MEMHEAD_IS_FROM_CPP_NEW(memh)) { + print_error( + "Attempt to use C-style MEM_reallocN on a pointer created with CPP-style MEM_new or " + "new\n"); +#ifdef WITH_ASSERT_ABORT + abort(); +#endif + } + if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) { newp = MEM_lockfree_mallocN(len, "realloc"); } else { const MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh); - newp = MEM_lockfree_mallocN_aligned(len, size_t(memh_aligned->alignment), "realloc"); + newp = MEM_lockfree_mallocN_aligned( + len, size_t(memh_aligned->alignment), "realloc", AllocationType::ALLOC_FREE); } if (newp) { @@ -161,7 +207,7 @@ void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str) } } - MEM_lockfree_freeN(vmemh); + MEM_lockfree_freeN(vmemh, AllocationType::ALLOC_FREE); } else { newp = MEM_lockfree_mallocN(len, str); @@ -178,12 +224,22 @@ void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str) const MemHead *memh = MEMHEAD_FROM_PTR(vmemh); const size_t old_len = MEM_lockfree_allocN_len(vmemh); + if (MEMHEAD_IS_FROM_CPP_NEW(memh)) { + print_error( + "Attempt to use C-style MEM_recallocN on a pointer created with CPP-style MEM_new or " + "new\n"); +#ifdef WITH_ASSERT_ABORT + abort(); +#endif + } + if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) { newp = MEM_lockfree_mallocN(len, "recalloc"); } else { const MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh); - newp = MEM_lockfree_mallocN_aligned(len, size_t(memh_aligned->alignment), "recalloc"); + newp = MEM_lockfree_mallocN_aligned( + len, size_t(memh_aligned->alignment), "recalloc", AllocationType::ALLOC_FREE); } if (newp) { @@ -202,7 +258,7 @@ void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str) } } - MEM_lockfree_freeN(vmemh); + MEM_lockfree_freeN(vmemh, AllocationType::ALLOC_FREE); } else { newp = MEM_lockfree_callocN(len, str); @@ -307,7 +363,10 @@ void *MEM_lockfree_malloc_arrayN(size_t len, size_t size, const char *str) return MEM_lockfree_mallocN(total_size, str); } -void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str) +void *MEM_lockfree_mallocN_aligned(size_t len, + size_t alignment, + const char *str, + const AllocationType allocation_type) { /* Huge alignment values doesn't make sense and they wouldn't fit into 'short' used in the * MemHead. */ @@ -358,7 +417,9 @@ void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str #endif /* WITH_MEM_VALGRIND */ } - memh->len = len | size_t(MEMHEAD_ALIGN_FLAG); + memh->len = len | size_t(MEMHEAD_FLAG_ALIGN) | + size_t(allocation_type == AllocationType::NEW_DELETE ? MEMHEAD_FLAG_FROM_CPP_NEW : + 0); memh->alignment = short(alignment); memory_usage_block_alloc(len);