From 6e629e1deb5309cefc2328300f9b53ccefbad2a0 Mon Sep 17 00:00:00 2001 From: Chairn Date: Tue, 3 Jan 2023 00:35:21 +0100 Subject: [PATCH] Type safe mem_zero function (fixes #5228) Partially replaces #5690. POD types are just memset. Other types are either destructed if not trivial and/or constructed if not trivial. Types need to have a default constructor. Virtual classes can be mem_zeroed only if they already have been constructed, otherwise it is UB. --- CMakeLists.txt | 3 + src/base/system.cpp | 5 - src/base/system.h | 52 ++++- src/engine/client/client.cpp | 7 +- src/test/memory.cpp | 389 +++++++++++++++++++++++++++++++++++ 5 files changed, 444 insertions(+), 12 deletions(-) create mode 100644 src/test/memory.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bfabfd29..a6906d957 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -325,6 +325,8 @@ if(NOT MSVC AND NOT HAIKU) add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wthread-safety) add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wthread-safety-negative) add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wsuggest-override) + add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wdynamic-class-memaccess) # clang + add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wclass-memaccess) # gcc add_linker_flag_if_supported(OUR_FLAGS_LINK -Wno-alloc-size-larger-than) # save.cpp with LTO # add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wdouble-promotion) # Many occurrences # add_cxx_compiler_flag_if_supported(OUR_FLAGS_OWN -Wnull-dereference) # Many occurrences @@ -2750,6 +2752,7 @@ if(GTEST_FOUND OR DOWNLOAD_GTEST) linereader.cpp mapbugs.cpp math.cpp + memory.cpp name_ban.cpp net.cpp netaddr.cpp diff --git a/src/base/system.cpp b/src/base/system.cpp index 84e59fb6d..877daebc0 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -236,11 +236,6 @@ void mem_move(void *dest, const void *source, size_t size) memmove(dest, source, size); } -void mem_zero(void *block, size_t size) -{ - memset(block, 0, size); -} - int mem_comp(const void *a, const void *b, size_t size) { return memcmp(a, b, size); diff --git a/src/base/system.h b/src/base/system.h index bb98d36e0..bfc1c0fe8 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -171,7 +171,55 @@ void mem_move(void *dest, const void *source, size_t size); * @param block Pointer to the block to zero out. * @param size Size of the block. */ -void mem_zero(void *block, size_t size); +template +inline void mem_zero(T *block, size_t size) +{ + typedef typename std::remove_all_extents::type BaseT; + if constexpr(std::is_pointer::value || std::is_pointer::value) + { + // pointer of pointer, just memset it + memset(block, 0, size); + } + else if constexpr(std::is_array::value) + { // pointer to array type + if constexpr(std::is_fundamental::value) + { // array of fundamental type, just memset it + memset(block, 0, size); + } + else + { // array of user defined type, destroying all objects and recreating new ones + const size_t N = size / sizeof(BaseT); + if constexpr(!std::is_trivially_destructible::value) + { // non trivial destructor means user provided or virtual destructor, see https://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor + // so we gotta call it manually + for(size_t i(0); i < N; ++i) + ((BaseT *)block)[i].~BaseT(); + } + if constexpr(std::is_trivially_constructible::value) + memset(block, 0, size); // trivial constructor implies POD + else + new(block) BaseT[N]{}; + } + } + else if constexpr(std::is_fundamental::type>::value || std::is_same::value) + { // pointer to fundamental type, just memset it + memset(block, 0, size); + } + else + { // pointer to type T, BUT CAN BE AN ARRAY... + const size_t N = size / sizeof(T); + if constexpr(!std::is_trivially_destructible::value) + { // non trivial destructor means user provided or virtual destructor, see https://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor + // so we gotta call it manually + for(size_t i(0); i < N; ++i) + block[i].~T(); + } + if constexpr(std::is_trivially_constructible::value) + memset(block, 0, size); // trivial constructor implies POD + else + new(block) T[N]{}; + } +} /** * Compares two blocks of memory @@ -2830,7 +2878,7 @@ bool shell_unregister_application(const char *executable, bool *updated); * Notifies the system that a protocol or file extension has been changed and the shell needs to be updated. * * @ingroup Shell - * + * * @remark This is a potentially expensive operation, so it should only be called when necessary. */ void shell_update(); diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 708b39e3d..2e313fabc 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -4195,9 +4195,7 @@ void CClient::RegisterCommands() static CClient *CreateClient() { - CClient *pClient = static_cast(malloc(sizeof(*pClient))); - mem_zero(pClient, sizeof(CClient)); - return new(pClient) CClient; + return new CClient; } void CClient::HandleConnectAddress(const NETADDR *pAddr) @@ -4369,8 +4367,7 @@ int main(int argc, const char **argv) CleanerFunctions.emplace([pKernel, pClient]() { pKernel->Shutdown(); delete pKernel; - pClient->~CClient(); - free(pClient); + delete pClient; }); const std::thread::id MainThreadId = std::this_thread::get_id(); diff --git a/src/test/memory.cpp b/src/test/memory.cpp new file mode 100644 index 000000000..4ccb7d58c --- /dev/null +++ b/src/test/memory.cpp @@ -0,0 +1,389 @@ +#include + +#include + +#include +#include + +static std::mt19937 gs_randomizer; + +void mem_fill(void *block, size_t size) +{ + unsigned char *bytes = (unsigned char *)block; + size_t i; + for(i = 0; i < size; i++) + { + bytes[i] = gs_randomizer(); + } +} + +bool mem_is_null(const void *block, size_t size) +{ + const unsigned char *bytes = (const unsigned char *)block; + size_t i; + for(i = 0; i < size; i++) + { + if(bytes[i] != 0) + { + return false; + } + } + return true; +} + +TEST(Memory, BaseTypes) +{ + void *aVoid[123]; + mem_fill(aVoid, sizeof(aVoid)); + EXPECT_FALSE(mem_is_null(aVoid, sizeof(aVoid))); + mem_zero(&aVoid, sizeof(aVoid)); + EXPECT_TRUE(mem_is_null(aVoid, sizeof(aVoid))); + mem_fill(aVoid, sizeof(aVoid)); + EXPECT_FALSE(mem_is_null(aVoid, sizeof(aVoid))); + mem_zero(&aVoid[0], 123 * sizeof(void *)); + EXPECT_TRUE(mem_is_null(aVoid, sizeof(aVoid))); + + mem_fill(aVoid, sizeof(aVoid)); + EXPECT_FALSE(mem_is_null(aVoid, sizeof(aVoid))); + mem_zero(aVoid, sizeof(aVoid)); + EXPECT_TRUE(mem_is_null(aVoid, sizeof(aVoid))); + + int aInt[512]; + mem_fill(aInt, sizeof(aInt)); + EXPECT_FALSE(mem_is_null(aInt, sizeof(aInt))); + mem_zero(&aInt, sizeof(aInt)); + EXPECT_TRUE(mem_is_null(aInt, sizeof(aInt))); + mem_fill(aInt, sizeof(aInt)); + EXPECT_FALSE(mem_is_null(aInt, sizeof(aInt))); + mem_zero(&aInt[0], sizeof(aInt)); + EXPECT_TRUE(mem_is_null(aInt, sizeof(aInt))); + + mem_fill(aInt, sizeof(aInt)); + EXPECT_FALSE(mem_is_null(aInt, sizeof(aInt))); + mem_zero(aInt, sizeof(aInt)); + EXPECT_TRUE(mem_is_null(aInt, sizeof(aInt))); + + int *apInt[512]; + mem_fill(apInt, sizeof(apInt)); + EXPECT_FALSE(mem_is_null(apInt, sizeof(apInt))); + mem_zero(&apInt, sizeof(apInt)); + EXPECT_TRUE(mem_is_null(apInt, sizeof(apInt))); + + mem_fill(apInt, sizeof(apInt)); + EXPECT_FALSE(mem_is_null(apInt, sizeof(apInt))); + mem_zero(apInt, sizeof(apInt)); + EXPECT_TRUE(mem_is_null(apInt, sizeof(apInt))); + + int aaInt[10][20]; + mem_fill(aaInt, sizeof(aaInt)); + EXPECT_FALSE(mem_is_null(aaInt, sizeof(aaInt))); + mem_zero(&aaInt, sizeof(aaInt)); + EXPECT_TRUE(mem_is_null(aaInt, sizeof(aaInt))); + mem_fill(aaInt, sizeof(aaInt)); + EXPECT_FALSE(mem_is_null(aaInt, sizeof(aaInt))); + mem_zero(&aaInt[0], sizeof(aaInt)); + EXPECT_TRUE(mem_is_null(aaInt, sizeof(aaInt))); + mem_fill(aaInt, sizeof(aaInt)); + EXPECT_FALSE(mem_is_null(aaInt, sizeof(aaInt))); + mem_zero(&aaInt[0][0], sizeof(aaInt)); + EXPECT_TRUE(mem_is_null(aaInt, sizeof(aaInt))); + + mem_fill(aaInt, sizeof(aaInt)); + EXPECT_FALSE(mem_is_null(aaInt, sizeof(aaInt))); + mem_zero(aaInt, sizeof(aaInt)); + EXPECT_TRUE(mem_is_null(aaInt, sizeof(aaInt))); + mem_fill(aaInt, sizeof(aaInt)); + EXPECT_FALSE(mem_is_null(aaInt, sizeof(aaInt))); + mem_zero(aaInt[0], sizeof(aaInt)); + EXPECT_TRUE(mem_is_null(aaInt, sizeof(aaInt))); + + int *aapInt[10][20]; + mem_fill(aapInt, sizeof(aapInt)); + EXPECT_FALSE(mem_is_null(aapInt, sizeof(aapInt))); + mem_zero(&aapInt, sizeof(aapInt)); + EXPECT_TRUE(mem_is_null(aapInt, sizeof(aapInt))); + mem_fill(aapInt, sizeof(aapInt)); + EXPECT_FALSE(mem_is_null(aapInt, sizeof(aapInt))); + mem_zero(&aapInt[0], sizeof(aapInt)); + EXPECT_TRUE(mem_is_null(aapInt, sizeof(aapInt))); + + mem_fill(aapInt, sizeof(aapInt)); + EXPECT_FALSE(mem_is_null(aapInt, sizeof(aapInt))); + mem_zero(aapInt, sizeof(aapInt)); + EXPECT_TRUE(mem_is_null(aapInt, sizeof(aapInt))); + mem_fill(aapInt, sizeof(aapInt)); + EXPECT_FALSE(mem_is_null(aapInt, sizeof(aapInt))); + mem_zero(aapInt[0], sizeof(aapInt)); + EXPECT_TRUE(mem_is_null(aapInt, sizeof(aapInt))); +} + +TEST(Memory, PODTypes) +{ + NETADDR aAddr[123]; + mem_fill(aAddr, sizeof(aAddr)); + EXPECT_FALSE(mem_is_null(aAddr, sizeof(aAddr))); + mem_zero(&aAddr, sizeof(aAddr)); + EXPECT_TRUE(mem_is_null(aAddr, sizeof(aAddr))); + mem_fill(aAddr, sizeof(aAddr)); + EXPECT_FALSE(mem_is_null(aAddr, sizeof(aAddr))); + mem_zero(&aAddr[0], sizeof(aAddr)); + EXPECT_TRUE(mem_is_null(aAddr, sizeof(aAddr))); + + mem_fill(aAddr, sizeof(aAddr)); + EXPECT_FALSE(mem_is_null(aAddr, sizeof(aAddr))); + mem_zero(aAddr, sizeof(aAddr)); + EXPECT_TRUE(mem_is_null(aAddr, sizeof(aAddr))); + + NETADDR *apAddr[123]; + mem_fill(apAddr, sizeof(apAddr)); + EXPECT_FALSE(mem_is_null(apAddr, sizeof(apAddr))); + mem_zero((NETADDR **)apAddr, sizeof(apAddr)); + EXPECT_TRUE(mem_is_null(apAddr, sizeof(apAddr))); + mem_fill(apAddr, sizeof(apAddr)); + EXPECT_FALSE(mem_is_null(apAddr, sizeof(apAddr))); + mem_zero(&apAddr, sizeof(apAddr)); + EXPECT_TRUE(mem_is_null(apAddr, sizeof(apAddr))); + mem_fill(apAddr, sizeof(apAddr)); + EXPECT_FALSE(mem_is_null(apAddr, sizeof(apAddr))); + mem_zero(&apAddr[0], sizeof(apAddr)); + EXPECT_TRUE(mem_is_null(apAddr, sizeof(apAddr))); + mem_fill(apAddr, sizeof(apAddr)); + EXPECT_FALSE(mem_is_null(apAddr, sizeof(apAddr))); + mem_zero(apAddr, sizeof(apAddr)); + EXPECT_TRUE(mem_is_null(apAddr, sizeof(apAddr))); + + // 2D arrays + NETADDR aaAddr[10][20]; + mem_fill(aaAddr, sizeof(aaAddr)); + EXPECT_FALSE(mem_is_null((NETADDR *)aaAddr, sizeof(aaAddr))); + mem_zero(&aaAddr, sizeof(aaAddr)); + EXPECT_TRUE(mem_is_null((NETADDR *)aaAddr, sizeof(aaAddr))); + mem_fill(aaAddr, sizeof(aaAddr)); + EXPECT_FALSE(mem_is_null((NETADDR *)aaAddr, sizeof(aaAddr))); + mem_zero(&aaAddr[0], sizeof(aaAddr)); + EXPECT_TRUE(mem_is_null((NETADDR *)aaAddr, sizeof(aaAddr))); + mem_fill(aaAddr, sizeof(aaAddr)); + EXPECT_FALSE(mem_is_null((NETADDR *)aaAddr, sizeof(aaAddr))); + mem_zero(&aaAddr[0][0], sizeof(aaAddr)); + EXPECT_TRUE(mem_is_null((NETADDR *)aaAddr, sizeof(aaAddr))); + + mem_fill(aaAddr, sizeof(aaAddr)); + EXPECT_FALSE(mem_is_null((NETADDR *)aaAddr, sizeof(aaAddr))); + mem_zero(aaAddr, sizeof(aaAddr)); + EXPECT_TRUE(mem_is_null((NETADDR *)aaAddr, sizeof(aaAddr))); + mem_fill(aaAddr, sizeof(aaAddr)); + EXPECT_FALSE(mem_is_null((NETADDR *)aaAddr, sizeof(aaAddr))); + mem_zero(aaAddr[0], sizeof(aaAddr)); + EXPECT_TRUE(mem_is_null((NETADDR *)aaAddr, sizeof(aaAddr))); + + // 2D pointer arrays + NETADDR *aapAddr[10][20]; + mem_fill(aapAddr, sizeof(aapAddr)); + EXPECT_FALSE(mem_is_null(aapAddr, sizeof(aapAddr))); + mem_zero(&aapAddr, sizeof(aapAddr)); + EXPECT_TRUE(mem_is_null(aapAddr, sizeof(aapAddr))); + mem_fill(aapAddr, sizeof(aapAddr)); + EXPECT_FALSE(mem_is_null(aapAddr, sizeof(aapAddr))); + mem_zero(&aapAddr[0], sizeof(aapAddr)); + EXPECT_TRUE(mem_is_null(aapAddr, sizeof(aapAddr))); + mem_fill(aapAddr, sizeof(aapAddr)); + EXPECT_FALSE(mem_is_null(aapAddr, sizeof(aapAddr))); + mem_zero(&aapAddr[0][0], sizeof(aapAddr)); + EXPECT_TRUE(mem_is_null(aapAddr, sizeof(aapAddr))); + + mem_fill(aapAddr, sizeof(aapAddr)); + EXPECT_FALSE(mem_is_null(aapAddr, sizeof(aapAddr))); + mem_zero(aapAddr, sizeof(aapAddr)); + EXPECT_TRUE(mem_is_null(aapAddr, sizeof(aapAddr))); + mem_fill(aapAddr, sizeof(aapAddr)); + EXPECT_FALSE(mem_is_null(aapAddr, sizeof(aapAddr))); + mem_zero(aapAddr[0], sizeof(aapAddr)); + EXPECT_TRUE(mem_is_null(aapAddr, sizeof(aapAddr))); +} + +struct CDummyClass +{ + int x = 1234; + int y; +}; + +bool mem_is_null(CDummyClass *block, size_t size) +{ + const CDummyClass *data = block; + size_t i; + for(i = 0; i < size / sizeof(CDummyClass); i++) + { + if(data[i].x != 1234 || data[i].y != 0) + { + return false; + } + } + return true; +} + +TEST(Memory, ConstructibleTypes) +{ + CDummyClass aTest[123]; + mem_fill(aTest, sizeof(aTest)); + EXPECT_FALSE(mem_is_null(aTest, sizeof(aTest))); + mem_zero(&aTest, sizeof(aTest)); + EXPECT_TRUE(mem_is_null(aTest, sizeof(aTest))); + mem_fill(aTest, sizeof(aTest)); + EXPECT_FALSE(mem_is_null(aTest, sizeof(aTest))); + mem_zero(&aTest[0], sizeof(aTest)); + EXPECT_TRUE(mem_is_null(aTest, sizeof(aTest))); + + mem_fill(aTest, sizeof(aTest)); + EXPECT_FALSE(mem_is_null(aTest, sizeof(aTest))); + mem_zero(aTest, sizeof(aTest)); + EXPECT_TRUE(mem_is_null(aTest, sizeof(aTest))); + + CDummyClass aaTest[2][2]; + mem_fill(aaTest, sizeof(aaTest)); + EXPECT_FALSE(mem_is_null((CDummyClass *)aaTest, sizeof(aaTest))); + mem_zero(&aaTest, sizeof(aaTest)); + EXPECT_TRUE(mem_is_null((CDummyClass *)aaTest, sizeof(aaTest))); + mem_fill(aaTest, sizeof(aaTest)); + EXPECT_FALSE(mem_is_null((CDummyClass *)aaTest, sizeof(aaTest))); + mem_zero(&aaTest[0], sizeof(aaTest)); + EXPECT_TRUE(mem_is_null((CDummyClass *)aaTest, sizeof(aaTest))); + mem_fill(aaTest, sizeof(aaTest)); + EXPECT_FALSE(mem_is_null((CDummyClass *)aaTest, sizeof(aaTest))); + mem_zero(&aaTest[0][0], sizeof(aaTest)); + EXPECT_TRUE(mem_is_null((CDummyClass *)aaTest, sizeof(aaTest))); + + mem_fill(aaTest, sizeof(aaTest)); + EXPECT_FALSE(mem_is_null((CDummyClass *)aaTest, sizeof(aaTest))); + mem_zero(aaTest, sizeof(aaTest)); + EXPECT_TRUE(mem_is_null((CDummyClass *)aaTest, sizeof(aaTest))); + mem_fill(aaTest, sizeof(aaTest)); + EXPECT_FALSE(mem_is_null((CDummyClass *)aaTest, sizeof(aaTest))); + mem_zero(aaTest[0], sizeof(aaTest)); + EXPECT_TRUE(mem_is_null((CDummyClass *)aaTest, sizeof(aaTest))); +} + +struct CDummyClass2 +{ + int x; + int y; + ~CDummyClass2() + { + if(x == 684684 && y == -12345678) + dbg_break(); + } +}; + +TEST(Memory, DestructibleTypes) +{ + CDummyClass2 aTest2[123]; + mem_fill(aTest2, sizeof(aTest2)); + EXPECT_FALSE(mem_is_null(aTest2, sizeof(aTest2))); + mem_zero(&aTest2, sizeof(aTest2)); + EXPECT_TRUE(mem_is_null(aTest2, sizeof(aTest2))); + mem_fill(aTest2, sizeof(aTest2)); + EXPECT_FALSE(mem_is_null(aTest2, sizeof(aTest2))); + mem_zero(&aTest2[0], sizeof(aTest2)); + EXPECT_TRUE(mem_is_null(aTest2, sizeof(aTest2))); + + mem_fill(aTest2, sizeof(aTest2)); + EXPECT_FALSE(mem_is_null(aTest2, sizeof(aTest2))); + mem_zero(aTest2, sizeof(aTest2)); + EXPECT_TRUE(mem_is_null(aTest2, sizeof(aTest2))); + + CDummyClass2 aaTest2[10][20]; + mem_fill(aaTest2, sizeof(aaTest2)); + EXPECT_FALSE(mem_is_null((CDummyClass2 *)aaTest2, sizeof(aaTest2))); + mem_zero(&aaTest2, sizeof(aaTest2)); + EXPECT_TRUE(mem_is_null((CDummyClass2 *)aaTest2, sizeof(aaTest2))); + mem_fill(aaTest2, sizeof(aaTest2)); + EXPECT_FALSE(mem_is_null((CDummyClass2 *)aaTest2, sizeof(aaTest2))); + mem_zero(&aaTest2[0], sizeof(aaTest2)); + EXPECT_TRUE(mem_is_null((CDummyClass2 *)aaTest2, sizeof(aaTest2))); + mem_fill(aaTest2, sizeof(aaTest2)); + EXPECT_FALSE(mem_is_null((CDummyClass2 *)aaTest2, sizeof(aaTest2))); + mem_zero(&aaTest2[0][0], sizeof(aaTest2)); + EXPECT_TRUE(mem_is_null((CDummyClass2 *)aaTest2, sizeof(aaTest2))); + + mem_fill(aaTest2, sizeof(aaTest2)); + EXPECT_FALSE(mem_is_null((CDummyClass2 *)aaTest2, sizeof(aaTest2))); + mem_zero(aaTest2, sizeof(aaTest2)); + EXPECT_TRUE(mem_is_null((CDummyClass2 *)aaTest2, sizeof(aaTest2))); + + mem_fill(aaTest2, sizeof(aaTest2)); + EXPECT_FALSE(mem_is_null((CDummyClass2 *)aaTest2, sizeof(aaTest2))); + mem_zero(aaTest2[0], sizeof(aaTest2)); + EXPECT_TRUE(mem_is_null((CDummyClass2 *)aaTest2, sizeof(aaTest2))); +} + +struct CDummyClass3 +{ + int x; + int y; + std::vector v; +}; + +void mem_fill(CDummyClass3 *block, size_t size) +{ + size_t i; + for(i = 0; i < size / sizeof(CDummyClass3); i++) + { + block[i].x = gs_randomizer(); + block[i].y = gs_randomizer(); + block[i].v.resize(gs_randomizer() & 127, gs_randomizer()); + } +} + +bool mem_is_null(CDummyClass3 *block, size_t size) +{ + const CDummyClass3 *data = block; + size_t i; + for(i = 0; i < size / sizeof(CDummyClass3); i++) + { + if(data[i].x != 0 || data[i].y != 0 || !data[i].v.empty()) + { + return false; + } + } + return true; +} + +TEST(Memory, ComplexTypes) +{ + CDummyClass3 aTest3[123]; + mem_fill(aTest3, sizeof(aTest3)); + EXPECT_FALSE(mem_is_null(aTest3, sizeof(aTest3))); + mem_zero(&aTest3, sizeof(aTest3)); + EXPECT_TRUE(mem_is_null(aTest3, sizeof(aTest3))); + mem_fill(aTest3, sizeof(aTest3)); + EXPECT_FALSE(mem_is_null(aTest3, sizeof(aTest3))); + mem_zero(&aTest3[0], sizeof(aTest3)); + EXPECT_TRUE(mem_is_null(aTest3, sizeof(aTest3))); + + mem_fill(aTest3, sizeof(aTest3)); + EXPECT_FALSE(mem_is_null(aTest3, sizeof(aTest3))); + mem_zero(aTest3, sizeof(aTest3)); + EXPECT_TRUE(mem_is_null(aTest3, sizeof(aTest3))); + + CDummyClass3 aaTest3[10][20]; + mem_fill((CDummyClass3 *)aaTest3, sizeof(aaTest3)); + EXPECT_FALSE(mem_is_null((CDummyClass3 *)aaTest3, sizeof(aaTest3))); + mem_zero(&aaTest3, sizeof(aaTest3)); + EXPECT_TRUE(mem_is_null((CDummyClass3 *)aaTest3, sizeof(aaTest3))); + mem_fill((CDummyClass3 *)aaTest3, sizeof(aaTest3)); + EXPECT_FALSE(mem_is_null((CDummyClass3 *)aaTest3, sizeof(aaTest3))); + mem_zero(&aaTest3[0], sizeof(aaTest3)); + EXPECT_TRUE(mem_is_null((CDummyClass3 *)aaTest3, sizeof(aaTest3))); + mem_fill((CDummyClass3 *)aaTest3, sizeof(aaTest3)); + EXPECT_FALSE(mem_is_null((CDummyClass3 *)aaTest3, sizeof(aaTest3))); + mem_zero(&aaTest3[0][0], sizeof(aaTest3)); + EXPECT_TRUE(mem_is_null((CDummyClass3 *)aaTest3, sizeof(aaTest3))); + + mem_fill((CDummyClass3 *)aaTest3, sizeof(aaTest3)); + EXPECT_FALSE(mem_is_null((CDummyClass3 *)aaTest3, sizeof(aaTest3))); + mem_zero(aaTest3, sizeof(aaTest3)); + EXPECT_TRUE(mem_is_null((CDummyClass3 *)aaTest3, sizeof(aaTest3))); + mem_fill((CDummyClass3 *)aaTest3, sizeof(aaTest3)); + EXPECT_FALSE(mem_is_null((CDummyClass3 *)aaTest3, sizeof(aaTest3))); + mem_zero(aaTest3[0], sizeof(aaTest3)); + EXPECT_TRUE(mem_is_null((CDummyClass3 *)aaTest3, sizeof(aaTest3))); +}