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") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
if(NOT MSVC) if(NOT MSVC)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") 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() endif()
if (MSVC) if (MSVC)
@ -22,8 +22,7 @@ if (MSVC)
CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE
CMAKE_C_FLAGS CMAKE_C_FLAGS
CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELEASE)
)
if (LIBIPC_USE_STATIC_CRT) if (LIBIPC_USE_STATIC_CRT)
foreach(CompilerFlag ${CompilerFlags}) foreach(CompilerFlag ${CompilerFlags})
string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")

View File

@ -204,6 +204,8 @@ public:
namespace detail { namespace detail {
/// \struct holder_type_base
/// \brief Defines the holder type operation interface.
struct holder_type_base { struct holder_type_base {
virtual ~holder_type_base() noexcept = default; virtual ~holder_type_base() noexcept = default;
virtual std::size_t size() const noexcept = 0; 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; 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> template <typename Value>
struct holder_type : holder_type_base { struct holder_type : holder_type_base {
std::size_t size() const noexcept override { return sizeof(Value); } std::size_t size() const noexcept override { return sizeof(Value); }
@ -240,6 +244,10 @@ struct holder_info {
std::size_t count; std::size_t count;
}; };
struct holder_data : holder_info {
alignas(std::max_align_t) char ___data;
};
template <typename Value> template <typename Value>
std::size_t full_sizeof(std::size_t count) noexcept { std::size_t full_sizeof(std::size_t count) noexcept {
return ::LIBIMP::round_up(sizeof(detail::holder_info), alignof(std::max_align_t)) 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 { void *value_ptr(holder_info *info) noexcept {
return reinterpret_cast<void *>( if (info == nullptr) return nullptr;
::LIBIMP::round_up(reinterpret_cast<std::size_t>(info + 1), alignof(std::max_align_t))); return &static_cast<holder_data *>(info)->___data;
} }
void const *value_ptr(holder_info const *info) noexcept { void const *value_ptr(holder_info const *info) noexcept {
return reinterpret_cast<void const *>( if (info == nullptr) return nullptr;
::LIBIMP::round_up(reinterpret_cast<std::size_t>(info + 1), alignof(std::max_align_t))); 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_; allocator const &alloc_;
holder_info **pptr_; holder_data **pptr_;
std::size_t size_; std::size_t size_;
public: 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) : alloc_(alloc)
, pptr_ (&ptr) , pptr_ (&ptr)
, size_ (::LIBIMP::round_up(sizeof(holder_info), alignof(std::max_align_t)) + sz) { , size_ (detail::full_sizeof<::LIBIMP::byte>(sz)) {
*pptr_ = static_cast<holder_info *>(alloc_.allocate(size_)); *pptr_ = static_cast<holder_data *>(alloc_.allocate(size_));
} }
~holder_info_ptr() noexcept { ~holder_data_builder() noexcept {
if (pptr_ == nullptr) return; if (pptr_ == nullptr) return;
alloc_.deallocate(*pptr_, size_); alloc_.deallocate(*pptr_, size_);
*pptr_ = nullptr; *pptr_ = nullptr;
@ -284,11 +294,11 @@ public:
return (pptr_ != nullptr) && (*pptr_ != nullptr); return (pptr_ != nullptr) && (*pptr_ != nullptr);
} }
holder_info *operator->() const noexcept { holder_data *operator->() const noexcept {
return *pptr_; return *pptr_;
} }
holder_info_ptr &operator=(holder_info const &rhs) noexcept { holder_data_builder &operator=(holder_data const &rhs) noexcept {
if (!*this) return *this; if (!*this) return *this;
**pptr_ = rhs; **pptr_ = rhs;
return *this; return *this;
@ -308,7 +318,7 @@ public:
template <> template <>
class holder<void, true> : public holder_base { class holder<void, true> : public holder_base {
detail::holder_info info_; detail::holder_data info_;
public: public:
template <typename Value> template <typename Value>
@ -385,7 +395,7 @@ public:
template <> template <>
class holder<void, false> : public holder_base { class holder<void, false> : public holder_base {
detail::holder_info *info_ptr_; detail::holder_data *info_ptr_;
public: public:
holder() noexcept holder() noexcept
@ -397,7 +407,7 @@ public:
std::enable_if_t<alignof(Value) <= alignof(std::max_align_t), bool> = true> 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() { holder(allocator const &alloc, ::LIBIMP::types<Value>, std::size_t n) : holder() {
LIBIMP_LOG_("holder<void, false>"); 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) { if (!info_p) {
log.error("The destination information-pointer failed to be constructed." log.error("The destination information-pointer failed to be constructed."
" type size = ", sizeof(Value), " type size = ", sizeof(Value),
@ -452,7 +462,7 @@ public:
LIBIMP_LOG_(); LIBIMP_LOG_();
auto *des = ::LIBIMP::construct<holder>(p); auto *des = ::LIBIMP::construct<holder>(p);
if (!valid()) return; 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) { if (!info_p) {
log.error("The destination information-pointer failed to be constructed." log.error("The destination information-pointer failed to be constructed."
" type size = ", this->sizeof_type(), " 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."); log.error("name is empty.");
return std::make_error_code(std::errc::invalid_argument); return std::make_error_code(std::errc::invalid_argument);
} }
// For portable use, a shared memory object should be identified by name of the form /somename.
/// \brief Open the object for read-write access. // 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; int flag = O_RDWR;
switch (type) { switch (type) {
case mode::open: 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); return std::make_error_code(std::errc::invalid_argument);
} }
/// \brief Create/Open POSIX shared memory bject // Create/Open POSIX shared memory bject.
int fd = ::shm_open(name.c_str(), flag, S_IRUSR | S_IWUSR | int fd = ::shm_open(op_name.c_str(), flag, S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP | S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH); S_IROTH | S_IWOTH);
if (fd == posix::failed) { if (fd == posix::failed) {
auto err = sys::error(); auto err = sys::error();
log.error("failed: shm_open(name = ", name, log.error("failed: shm_open(name = ", op_name,
", type = ", type, ", type = ", type,
"). error = ", err); "). error = ", err);
return 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); 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> { auto try_open = [&]() -> result<HANDLE> {
HANDLE h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, t_name.c_str()); HANDLE h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, t_name.c_str());
if (h == NULL) { if (h == NULL) {
@ -83,7 +83,7 @@ result<HANDLE> mmap_open(std::string const &file, std::size_t size, mode::type t
return h; 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> { auto try_create = [&]() -> result<HANDLE> {
HANDLE h = ::CreateFileMapping(INVALID_HANDLE_VALUE, detail::get_sa(), PAGE_READWRITE | SEC_COMMIT, HANDLE h = ::CreateFileMapping(INVALID_HANDLE_VALUE, detail::get_sa(), PAGE_READWRITE | SEC_COMMIT,
/// \remark dwMaximumSizeHigh always 0 here. /// \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)) {} : shm_(std::exchange(other.shm_, nullptr)) {}
shared_memory &shared_memory::operator=(shared_memory &&rhs) & noexcept { 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; return *this;
} }

View File

@ -4,6 +4,7 @@
#include "libimp/detect_plat.h" #include "libimp/detect_plat.h"
#include "libimp/system.h" #include "libimp/system.h"
#include "libimp/log.h" #include "libimp/log.h"
#include "libimp/aligned.h"
#include "libpmr/memory_resource.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) #if defined(LIBIMP_CPP_17)
/// \see https://en.cppreference.com/w/cpp/memory/c/aligned_alloc /// \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 #else
if (alignment <= alignof(std::max_align_t)) { if (alignment <= alignof(std::max_align_t)) {
/// \see https://en.cppreference.com/w/cpp/memory/c/malloc /// \see https://en.cppreference.com/w/cpp/memory/c/malloc

View File

@ -8,19 +8,23 @@
TEST(byte, construct) { TEST(byte, construct) {
{ {
imp::byte b; imp::byte b;
SUCCEED();
}
{
imp::byte b{};
EXPECT_EQ(int(b), 0); EXPECT_EQ(int(b), 0);
} }
{ {
imp::byte b {123}; imp::byte b{123};
EXPECT_EQ(int(b), 123); EXPECT_EQ(int(b), 123);
} }
{ {
imp::byte b {65535}; imp::byte b{65535};
EXPECT_EQ(int(b), 255); EXPECT_EQ(int(b), 255);
EXPECT_EQ(std::int8_t(b), -1); EXPECT_EQ(std::int8_t(b), -1);
} }
{ {
imp::byte b {65536}; imp::byte b{65536};
EXPECT_EQ(int(b), 0); 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)); EXPECT_FALSE((std::is_move_assignable<pmr::holder<void, false>>::value));
} }
TEST(small_storage, holder_copy_move) { namespace {
struct foo {
struct foo {
int i; int i;
foo(int i) : i(i) {} foo(int i) : i(i) {}
foo(foo const &rhs) : i(rhs.i) {} foo(foo const &rhs) : i(rhs.i) {}
foo(foo &&rhs) : i(std::exchange(rhs.i, 0)) {} foo(foo &&rhs) : i(std::exchange(rhs.i, 0)) {}
}; };
} // namespace
TEST(small_storage, holder_copy_move_object_stack) {
pmr::allocator alc; pmr::allocator alc;
pmr::holder<foo, true> h1(alc, 1); pmr::holder<foo, true> h1(alc, 1);
pmr::holder<foo, true> h2, h3; pmr::holder<foo, true> h2, h3;
@ -61,7 +65,10 @@ TEST(small_storage, holder_copy_move) {
h1.destroy(alc); h1.destroy(alc);
h2.destroy(alc); h2.destroy(alc);
h3.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> h4(alc, 1);
pmr::holder<foo, false> h5, h6; pmr::holder<foo, false> h5, h6;
h4.copy_to(alc, &h5); h4.copy_to(alc, &h5);
@ -73,17 +80,20 @@ TEST(small_storage, holder_copy_move) {
h4.destroy(alc); h4.destroy(alc);
h5.destroy(alc); h5.destroy(alc);
h6.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 *ph1 = std::malloc(pmr::holder<void, true>::full_sizeof<int>(10));
void *ph2 = 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)); 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 *h7 = ::new (ph1) pmr::holder<void, true>(alc, ::LIBIMP::types<int>{}, 10);
auto *h8 = ::new (ph2) pmr::holder<void, true>; auto *h8 = static_cast<pmr::holder<void, true> *>(ph2);
auto *h9 = ::new (ph2) pmr::holder<void, true>; auto *h9 = static_cast<pmr::holder<void, true> *>(ph3);
h7->copy_to(alc, h8); h7->copy_to(alc, ph2);
EXPECT_EQ(h7->count(), 10); EXPECT_EQ(h7->count(), 10);
EXPECT_EQ(h8->count(), 10); EXPECT_EQ(h8->count(), 10);
h7->move_to(alc, h9); h7->move_to(alc, ph3);
EXPECT_EQ(h7->count(), 0); EXPECT_EQ(h7->count(), 0);
EXPECT_EQ(h9->count(), 10); EXPECT_EQ(h9->count(), 10);
h7->destroy(alc); h7->destroy(alc);
@ -92,7 +102,10 @@ TEST(small_storage, holder_copy_move) {
std::free(ph1); std::free(ph1);
std::free(ph2); std::free(ph2);
std::free(ph3); 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> h10(alc, ::LIBIMP::types<int>{}, 10);
pmr::holder<void, false> h11, h12; pmr::holder<void, false> h11, h12;
h10.copy_to(alc, &h11); 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_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, 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<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 *)); EXPECT_EQ(sizeof(pmr::holder<void, false>), sizeof(void *) + sizeof(void *));
// pmr::small_storage<4> s1; // pmr::small_storage<4> s1;
EXPECT_TRUE(sizeof(pmr::small_storage<16>) > 16); EXPECT_GT(sizeof(pmr::small_storage<16>) , 16);
EXPECT_TRUE(sizeof(pmr::small_storage<64>) > 64); EXPECT_GT(sizeof(pmr::small_storage<64>) , 64);
EXPECT_TRUE(sizeof(pmr::small_storage<512>) > 512); EXPECT_GT(sizeof(pmr::small_storage<512>) , 512);
EXPECT_TRUE(sizeof(pmr::small_storage<4096>) > 4096); EXPECT_GT(sizeof(pmr::small_storage<4096>), 4096);
} }
TEST(small_storage, construct) { TEST(small_storage, construct) {