Add -fsanitize=address for memory leak and out-of-bounds detection.

This commit is contained in:
mutouyun 2023-10-22 17:39:43 +08:00
parent ec602f50af
commit 872b97f1d3
8 changed files with 79 additions and 49 deletions

View File

@ -12,7 +12,7 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
if(NOT MSVC)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -g -rdynamic")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -rdynamic -fsanitize=address")
endif()
if (MSVC)
@ -22,8 +22,7 @@ if (MSVC)
CMAKE_CXX_FLAGS_RELEASE
CMAKE_C_FLAGS
CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_RELEASE
)
CMAKE_C_FLAGS_RELEASE)
if (LIBIPC_USE_STATIC_CRT)
foreach(CompilerFlag ${CompilerFlags})
string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")

View File

@ -204,6 +204,8 @@ public:
namespace detail {
/// \struct holder_type_base
/// \brief Defines the holder type operation interface.
struct holder_type_base {
virtual ~holder_type_base() noexcept = default;
virtual std::size_t size() const noexcept = 0;
@ -213,6 +215,8 @@ struct holder_type_base {
virtual void dest(void *p, std::size_t n) const noexcept = 0;
};
/// \struct template <typename Value> holder_type
/// \brief Defines generalization operations for different data types.
template <typename Value>
struct holder_type : holder_type_base {
std::size_t size() const noexcept override { return sizeof(Value); }
@ -240,6 +244,10 @@ struct holder_info {
std::size_t count;
};
struct holder_data : holder_info {
alignas(std::max_align_t) char ___data;
};
template <typename Value>
std::size_t full_sizeof(std::size_t count) noexcept {
return ::LIBIMP::round_up(sizeof(detail::holder_info), alignof(std::max_align_t))
@ -252,29 +260,31 @@ std::size_t full_sizeof(holder_info const *info) noexcept {
}
void *value_ptr(holder_info *info) noexcept {
return reinterpret_cast<void *>(
::LIBIMP::round_up(reinterpret_cast<std::size_t>(info + 1), alignof(std::max_align_t)));
if (info == nullptr) return nullptr;
return &static_cast<holder_data *>(info)->___data;
}
void const *value_ptr(holder_info const *info) noexcept {
return reinterpret_cast<void const *>(
::LIBIMP::round_up(reinterpret_cast<std::size_t>(info + 1), alignof(std::max_align_t)));
if (info == nullptr) return nullptr;
return &static_cast<holder_data const *>(info)->___data;
}
class holder_info_ptr {
/// \class holder_data_builder
/// \brief The data pointer builder.
class holder_data_builder {
allocator const &alloc_;
holder_info **pptr_;
holder_data **pptr_;
std::size_t size_;
public:
holder_info_ptr(allocator const &alloc, holder_info *(&ptr), std::size_t sz) noexcept
holder_data_builder(allocator const &alloc, holder_data *(&ptr), std::size_t sz) noexcept
: alloc_(alloc)
, pptr_ (&ptr)
, size_ (::LIBIMP::round_up(sizeof(holder_info), alignof(std::max_align_t)) + sz) {
*pptr_ = static_cast<holder_info *>(alloc_.allocate(size_));
, size_ (detail::full_sizeof<::LIBIMP::byte>(sz)) {
*pptr_ = static_cast<holder_data *>(alloc_.allocate(size_));
}
~holder_info_ptr() noexcept {
~holder_data_builder() noexcept {
if (pptr_ == nullptr) return;
alloc_.deallocate(*pptr_, size_);
*pptr_ = nullptr;
@ -284,11 +294,11 @@ public:
return (pptr_ != nullptr) && (*pptr_ != nullptr);
}
holder_info *operator->() const noexcept {
holder_data *operator->() const noexcept {
return *pptr_;
}
holder_info_ptr &operator=(holder_info const &rhs) noexcept {
holder_data_builder &operator=(holder_data const &rhs) noexcept {
if (!*this) return *this;
**pptr_ = rhs;
return *this;
@ -308,7 +318,7 @@ public:
template <>
class holder<void, true> : public holder_base {
detail::holder_info info_;
detail::holder_data info_;
public:
template <typename Value>
@ -385,7 +395,7 @@ public:
template <>
class holder<void, false> : public holder_base {
detail::holder_info *info_ptr_;
detail::holder_data *info_ptr_;
public:
holder() noexcept
@ -397,7 +407,7 @@ public:
std::enable_if_t<alignof(Value) <= alignof(std::max_align_t), bool> = true>
holder(allocator const &alloc, ::LIBIMP::types<Value>, std::size_t n) : holder() {
LIBIMP_LOG_("holder<void, false>");
detail::holder_info_ptr info_p{alloc, info_ptr_, sizeof(Value) * n};
detail::holder_data_builder info_p{alloc, info_ptr_, sizeof(Value) * n};
if (!info_p) {
log.error("The destination information-pointer failed to be constructed."
" type size = ", sizeof(Value),
@ -452,7 +462,7 @@ public:
LIBIMP_LOG_();
auto *des = ::LIBIMP::construct<holder>(p);
if (!valid()) return;
detail::holder_info_ptr info_p{alloc, des->info_ptr_, this->sizeof_type() * this->count()};
detail::holder_data_builder info_p{alloc, des->info_ptr_, this->sizeof_type() * this->count()};
if (!info_p) {
log.error("The destination information-pointer failed to be constructed."
" type size = ", this->sizeof_type(),

View File

@ -53,8 +53,10 @@ result<int> shm_open_fd(std::string const &name, mode::type type) noexcept {
log.error("name is empty.");
return std::make_error_code(std::errc::invalid_argument);
}
/// \brief Open the object for read-write access.
// For portable use, a shared memory object should be identified by name of the form /somename.
// see: https://man7.org/linux/man-pages/man3/shm_open.3.html
std::string op_name = '/' + name;
// Open the object for read-write access.
int flag = O_RDWR;
switch (type) {
case mode::open:
@ -73,13 +75,13 @@ result<int> shm_open_fd(std::string const &name, mode::type type) noexcept {
return std::make_error_code(std::errc::invalid_argument);
}
/// \brief Create/Open POSIX shared memory bject
int fd = ::shm_open(name.c_str(), flag, S_IRUSR | S_IWUSR |
// Create/Open POSIX shared memory bject.
int fd = ::shm_open(op_name.c_str(), flag, S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH);
if (fd == posix::failed) {
auto err = sys::error();
log.error("failed: shm_open(name = ", name,
log.error("failed: shm_open(name = ", op_name,
", type = ", type,
"). error = ", err);
return err;

View File

@ -72,7 +72,7 @@ result<HANDLE> mmap_open(std::string const &file, std::size_t size, mode::type t
return std::make_error_code(std::errc::invalid_argument);
}
/// \brief Opens a named file mapping object.
// Opens a named file mapping object.
auto try_open = [&]() -> result<HANDLE> {
HANDLE h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, t_name.c_str());
if (h == NULL) {
@ -83,7 +83,7 @@ result<HANDLE> mmap_open(std::string const &file, std::size_t size, mode::type t
return h;
};
/// \brief Creates or opens a named or unnamed file mapping object for a specified file.
// Creates or opens a named or unnamed file mapping object for a specified file.
auto try_create = [&]() -> result<HANDLE> {
HANDLE h = ::CreateFileMapping(INVALID_HANDLE_VALUE, detail::get_sa(), PAGE_READWRITE | SEC_COMMIT,
/// \remark dwMaximumSizeHigh always 0 here.

View File

@ -57,7 +57,7 @@ shared_memory::shared_memory(shared_memory &&other) noexcept
: shm_(std::exchange(other.shm_, nullptr)) {}
shared_memory &shared_memory::operator=(shared_memory &&rhs) & noexcept {
this->shm_ = std::exchange(rhs.shm_, nullptr);
shared_memory(std::move(rhs)).swap(*this);
return *this;
}

View File

@ -4,6 +4,7 @@
#include "libimp/detect_plat.h"
#include "libimp/system.h"
#include "libimp/log.h"
#include "libimp/aligned.h"
#include "libpmr/memory_resource.h"
@ -52,7 +53,8 @@ void *new_delete_resource::allocate(std::size_t bytes, std::size_t alignment) no
}
#if defined(LIBIMP_CPP_17)
/// \see https://en.cppreference.com/w/cpp/memory/c/aligned_alloc
return std::aligned_alloc(alignment, bytes);
/// \remark The size parameter must be an integral multiple of alignment.
return std::aligned_alloc(alignment, ::LIBIMP::round_up(bytes, alignment));
#else
if (alignment <= alignof(std::max_align_t)) {
/// \see https://en.cppreference.com/w/cpp/memory/c/malloc

View File

@ -8,19 +8,23 @@
TEST(byte, construct) {
{
imp::byte b;
SUCCEED();
}
{
imp::byte b{};
EXPECT_EQ(int(b), 0);
}
{
imp::byte b {123};
imp::byte b{123};
EXPECT_EQ(int(b), 123);
}
{
imp::byte b {65535};
imp::byte b{65535};
EXPECT_EQ(int(b), 255);
EXPECT_EQ(std::int8_t(b), -1);
}
{
imp::byte b {65536};
imp::byte b{65536};
EXPECT_EQ(int(b), 0);
}
}

View File

@ -41,14 +41,18 @@ TEST(small_storage, holder_copy_move_construct) {
EXPECT_FALSE((std::is_move_assignable<pmr::holder<void, false>>::value));
}
TEST(small_storage, holder_copy_move) {
struct foo {
namespace {
struct foo {
int i;
foo(int i) : i(i) {}
foo(foo const &rhs) : i(rhs.i) {}
foo(foo &&rhs) : i(std::exchange(rhs.i, 0)) {}
};
};
} // namespace
TEST(small_storage, holder_copy_move_object_stack) {
pmr::allocator alc;
pmr::holder<foo, true> h1(alc, 1);
pmr::holder<foo, true> h2, h3;
@ -61,7 +65,10 @@ TEST(small_storage, holder_copy_move) {
h1.destroy(alc);
h2.destroy(alc);
h3.destroy(alc);
}
TEST(small_storage, holder_copy_move_object_heap) {
pmr::allocator alc;
pmr::holder<foo, false> h4(alc, 1);
pmr::holder<foo, false> h5, h6;
h4.copy_to(alc, &h5);
@ -73,17 +80,20 @@ TEST(small_storage, holder_copy_move) {
h4.destroy(alc);
h5.destroy(alc);
h6.destroy(alc);
}
TEST(small_storage, holder_copy_move_array_stack) {
pmr::allocator alc;
void *ph1 = std::malloc(pmr::holder<void, true>::full_sizeof<int>(10));
void *ph2 = std::malloc(pmr::holder<void, true>::full_sizeof<int>(10));
void *ph3 = std::malloc(pmr::holder<void, true>::full_sizeof<int>(10));
auto *h7 = ::new (ph1) pmr::holder<void, true>(alc, ::LIBIMP::types<int>{}, 10);
auto *h8 = ::new (ph2) pmr::holder<void, true>;
auto *h9 = ::new (ph2) pmr::holder<void, true>;
h7->copy_to(alc, h8);
auto *h8 = static_cast<pmr::holder<void, true> *>(ph2);
auto *h9 = static_cast<pmr::holder<void, true> *>(ph3);
h7->copy_to(alc, ph2);
EXPECT_EQ(h7->count(), 10);
EXPECT_EQ(h8->count(), 10);
h7->move_to(alc, h9);
h7->move_to(alc, ph3);
EXPECT_EQ(h7->count(), 0);
EXPECT_EQ(h9->count(), 10);
h7->destroy(alc);
@ -92,7 +102,10 @@ TEST(small_storage, holder_copy_move) {
std::free(ph1);
std::free(ph2);
std::free(ph3);
}
TEST(small_storage, holder_copy_move_array_heap) {
pmr::allocator alc;
pmr::holder<void, false> h10(alc, ::LIBIMP::types<int>{}, 10);
pmr::holder<void, false> h11, h12;
h10.copy_to(alc, &h11);
@ -110,14 +123,14 @@ TEST(small_storage, sizeof) {
EXPECT_EQ(sizeof(pmr::holder_null), sizeof(void *));
EXPECT_EQ(sizeof(pmr::holder<int, true>), sizeof(void *) + imp::round_up(sizeof(int), alignof(void *)));
EXPECT_EQ(sizeof(pmr::holder<int, false>), sizeof(void *) + sizeof(void *));
EXPECT_EQ(sizeof(pmr::holder<void, true>), sizeof(void *) + sizeof(pmr::detail::holder_info));
EXPECT_EQ(sizeof(pmr::holder<void, true>), imp::round_up(sizeof(void *) + sizeof(pmr::detail::holder_data), alignof(std::max_align_t)));
EXPECT_EQ(sizeof(pmr::holder<void, false>), sizeof(void *) + sizeof(void *));
// pmr::small_storage<4> s1;
EXPECT_TRUE(sizeof(pmr::small_storage<16>) > 16);
EXPECT_TRUE(sizeof(pmr::small_storage<64>) > 64);
EXPECT_TRUE(sizeof(pmr::small_storage<512>) > 512);
EXPECT_TRUE(sizeof(pmr::small_storage<4096>) > 4096);
EXPECT_GT(sizeof(pmr::small_storage<16>) , 16);
EXPECT_GT(sizeof(pmr::small_storage<64>) , 64);
EXPECT_GT(sizeof(pmr::small_storage<512>) , 512);
EXPECT_GT(sizeof(pmr::small_storage<4096>), 4096);
}
TEST(small_storage, construct) {