Add detection of mismatches usages of MEM_new/MEM_freeN.
This commit will error (and abort if enabled) when trying to call `MEM_freeN` (and related `MEM_dupallocN`, `MEM_reallocN` and `MEM_recallocN` functions) with a pointer created the C++ way (i.e. through `MEM_new`, or the guardedalloc-overloaded `new` operator). To do so, it adds internal use only implementations for `malloc_alligned` and `free`, which take an extra parameter indicating whether they are dealing with data created/deleted the 'C++ way' (using `new`/`delete` and similar). The cpp-created data are flagged with the new `MEMHEAD_FLAG_FROM_CPP_NEW`, either in the lower two-bytes len value for lockfree allocator, or as a new flag member of the guarded allocator header data. The public `MEM_new`/`MEM_delete` template functions, and the guardedalloc-overloaded versions of `new`/`delete` operators are updated accordingly. These changes have been successfully tested both with and without `WITH_CXX_GUARDEDALLOC`. NOTE: A lot of mismatches have already been fixed in `main` before merging this change. There are likely some less easy to trigger ones still in our codebase though. Pull Request: https://projects.blender.org/blender/blender/pulls/123740
This commit is contained in:
committed by
Bastien Montagne
parent
a1e2cc2b6e
commit
06be295946
@@ -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
|
||||
|
||||
@@ -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 <type_traits>
|
||||
# include <utility>
|
||||
|
||||
# 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<typename T, typename... Args>
|
||||
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>(args)...);
|
||||
}
|
||||
|
||||
@@ -315,7 +318,8 @@ template<typename T> 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<T *>(ptr));
|
||||
mem_guarded::internal::mem_freeN_ex(const_cast<T *>(ptr),
|
||||
mem_guarded::internal::AllocationType::NEW_DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,30 +369,45 @@ template<typename T> 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) \
|
||||
|
||||
@@ -6,42 +6,46 @@
|
||||
* \ingroup intern_mem
|
||||
*/
|
||||
|
||||
#include "../MEM_guardedalloc.h"
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<const MemHead *>(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<MemHead *>(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<MemHead *>(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<MemHead *>(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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user