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:
Bastien Montagne
2024-07-03 17:23:03 +02:00
committed by Bastien Montagne
parent a1e2cc2b6e
commit 06be295946
8 changed files with 256 additions and 60 deletions

View File

@@ -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

View File

@@ -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) \

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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);