The main change from this commit is the usage of ASAN poisoning (if available) to trigger an ASAN report on the erroring memory block. The main benefit is the report of the allocation backtrace of that faulty memory block. Pull Request: https://projects.blender.org/blender/blender/pulls/124231
90 lines
2.7 KiB
C++
90 lines
2.7 KiB
C++
/* SPDX-FileCopyrightText: 2002-2017 `Jason Evans <jasone@canonware.com>`. All rights reserved.
|
|
* SPDX-FileCopyrightText: 2007-2012 Mozilla Foundation. All rights reserved.
|
|
* SPDX-FileCopyrightText: 2009-2017 Facebook, Inc. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause */
|
|
#pragma once
|
|
|
|
/** \file
|
|
* \ingroup intern_mem
|
|
*/
|
|
|
|
#include <cstdlib>
|
|
|
|
/* BEGIN copied from BLI_asan.h */
|
|
|
|
/* Clang defines this. */
|
|
#ifndef __has_feature
|
|
# define __has_feature(x) 0
|
|
#endif
|
|
|
|
#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) && \
|
|
(!defined(_MSC_VER) || _MSC_VER > 1929) /* MSVC 2019 and below doesn't ship ASAN headers. */
|
|
# include "sanitizer/asan_interface.h"
|
|
# define WITH_ASAN
|
|
#else
|
|
/* Ensure return value is used. Just using UNUSED_VARS results in a warning. */
|
|
# define ASAN_POISON_MEMORY_REGION(addr, size) (void)(0 && ((size) != 0 && (addr) != NULL))
|
|
# define ASAN_UNPOISON_MEMORY_REGION(addr, size) (void)(0 && ((size) != 0 && (addr) != NULL))
|
|
#endif
|
|
|
|
/* END copied from BLI_asan.h */
|
|
|
|
MEM_INLINE bool MEM_size_safe_multiply(size_t a, size_t b, size_t *result)
|
|
{
|
|
/* A size_t with its high-half bits all set to 1. */
|
|
const size_t high_bits = SIZE_MAX << (sizeof(size_t) * 8 / 2);
|
|
*result = a * b;
|
|
|
|
if (UNLIKELY(*result == 0)) {
|
|
return (a == 0 || b == 0);
|
|
}
|
|
|
|
/*
|
|
* We got a non-zero size, but we don't know if we overflowed to get
|
|
* there. To avoid having to do a divide, we'll be clever and note that
|
|
* if both A and B can be represented in N/2 bits, then their product
|
|
* can be represented in N bits (without the possibility of overflow).
|
|
*/
|
|
return ((high_bits & (a | b)) == 0 || (*result / b == a));
|
|
}
|
|
|
|
/**
|
|
* Util to trigger an error for the given memory block.
|
|
*
|
|
* When ASAN is available, it will poison the memory block before accessing it, to trigger a
|
|
* detailed ASAN report. Otherwise, it will abort if aborting on assert is set.
|
|
*/
|
|
#ifdef WITH_ASAN
|
|
MEM_INLINE void MEM_trigger_error_on_memory_block(const void *address, const size_t size)
|
|
{
|
|
if (address == nullptr) {
|
|
# ifdef WITH_ASSERT_ABORT
|
|
abort();
|
|
# endif
|
|
return;
|
|
}
|
|
|
|
/* Trigger ASAN error by poisoning the memory and accessing it. */
|
|
ASAN_POISON_MEMORY_REGION(address, size);
|
|
char *buffer = const_cast<char *>(static_cast<const char *>(address));
|
|
const char c = *buffer;
|
|
*buffer &= 255;
|
|
*buffer = c;
|
|
|
|
/* In case ASAN is set to not terminate on error, but abort on assert is requested. */
|
|
# ifdef WITH_ASSERT_ABORT
|
|
abort();
|
|
# endif
|
|
ASAN_UNPOISON_MEMORY_REGION(address, size);
|
|
}
|
|
#else
|
|
MEM_INLINE void MEM_trigger_error_on_memory_block(const void * /* address */,
|
|
const size_t /* size */)
|
|
{
|
|
# ifdef WITH_ASSERT_ABORT
|
|
abort();
|
|
# endif
|
|
}
|
|
#endif
|