From 71d67bda7418603ad3a34c1c9e8e9374a7607b90 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 27 Mar 2018 12:35:27 +0100 Subject: [PATCH] aligned: aligned memory alloc/realloc/free --- src/common/CMakeLists.txt | 6 +- src/common/aligned.c | 139 ++++++++++++++++++++++++++++++++ src/common/aligned.h | 41 ++++++++++ tests/unit_tests/CMakeLists.txt | 3 +- tests/unit_tests/aligned.cpp | 86 ++++++++++++++++++++ 5 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 src/common/aligned.c create mode 100644 src/common/aligned.h create mode 100644 tests/unit_tests/aligned.cpp diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index f0df05b0d..c6bac2199 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -38,7 +38,8 @@ set(common_sources password.cpp perf_timer.cpp threadpool.cpp - updates.cpp) + updates.cpp + aligned.c) if (STACK_TRACE) list(APPEND common_sources stack_trace.cpp) @@ -67,7 +68,8 @@ set(common_private_headers perf_timer.h stack_trace.h threadpool.h - updates.h) + updates.h + aligned.h) monero_private_headers(common ${common_private_headers}) diff --git a/src/common/aligned.c b/src/common/aligned.c new file mode 100644 index 000000000..763dfd0e7 --- /dev/null +++ b/src/common/aligned.c @@ -0,0 +1,139 @@ +// Copyright (c) 2017-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include +#include +#include "aligned.h" + +static inline int is_power_of_2(size_t n) { return n && (n & (n-1)) == 0; } + +#define MAGIC 0xaa0817161500ff81 +#define MAGIC_FREED 0xaa0817161500ff82 + +static void local_abort(const char *msg) +{ + fprintf(stderr, "%s\n", msg); +#ifdef NDEBUG + _exit(1); +#else + abort(); +#endif +} + +typedef struct +{ + uint64_t magic; + void *raw; + size_t bytes; + size_t align; +} control; + +void *aligned_malloc(size_t bytes, size_t align) +{ + void *raw, *ptr; + control *ctrl; + + if (!is_power_of_2(align)) + return NULL; + if (bytes > (size_t)-1 - align) + return NULL; + if (bytes + align > (size_t)-1 - sizeof(control)) + return NULL; + + raw = malloc(bytes + sizeof(control) + align); + if (!raw) + return NULL; + ptr = (void*)(((uintptr_t)raw + align + sizeof(control) - 1) & ~(align-1)); + ctrl = ((control*)ptr) - 1; + ctrl->magic = MAGIC; + ctrl->raw = raw; + ctrl->bytes = bytes; + ctrl->align = align; + return ptr; +} + +void *aligned_realloc(void *ptr, size_t bytes, size_t align) +{ + void *raw, *ptr2; + control *ctrl, *ctrl2; + + if (!ptr) + return aligned_malloc(bytes, align); + if (!bytes) + { + aligned_free(ptr); + return NULL; + } + if (!is_power_of_2(align)) + return NULL; + + ctrl = ((control*)ptr) - 1; + if (ctrl->magic == MAGIC_FREED) + local_abort("Double free detected"); + if (ctrl->magic != MAGIC) + local_abort("Freeing unallocated memory"); + if (ctrl->align != align) + return NULL; + if (ctrl->bytes >= bytes) + return ptr; + + if (ctrl->bytes > (size_t)-1 - ctrl->align) + return NULL; + if (ctrl->bytes + ctrl->align > (size_t)-1 - sizeof(control)) + return NULL; + + raw = malloc(bytes + sizeof(control) + ctrl->align); + if (!raw) + return NULL; + ptr2 = (void*)(((uintptr_t)raw + ctrl->align + sizeof(control) - 1) & ~(ctrl->align-1)); + memcpy(ptr2, ptr, ctrl->bytes); + ctrl2 = ((control*)ptr2) - 1; + ctrl2->magic = MAGIC; + ctrl2->raw = raw; + ctrl2->bytes = bytes; + ctrl2->align = ctrl->align; + ctrl->magic = MAGIC_FREED; + free(ctrl->raw); + return ptr2; +} + +void aligned_free(void *ptr) +{ + if (!ptr) + return; + control *ctrl = ((control*)ptr) - 1; + if (ctrl->magic == MAGIC_FREED) + local_abort("Double free detected"); + if (ctrl->magic != MAGIC) + local_abort("Freeing unallocated memory"); + ctrl->magic = MAGIC_FREED; + free(ctrl->raw); +} diff --git a/src/common/aligned.h b/src/common/aligned.h new file mode 100644 index 000000000..fed3ccb36 --- /dev/null +++ b/src/common/aligned.h @@ -0,0 +1,41 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void *aligned_malloc(size_t bytes, size_t align); +void *aligned_realloc(void *ptr, size_t bytes, size_t align); +void aligned_free(void *ptr); + +#ifdef __cplusplus +} +#endif diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index cdb741699..741bb1882 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -79,7 +79,8 @@ set(unit_tests_sources vercmp.cpp ringdb.cpp wipeable_string.cpp - is_hdd.cpp) + is_hdd.cpp + aligned.cpp) set(unit_tests_headers unit_tests_utils.h) diff --git a/tests/unit_tests/aligned.cpp b/tests/unit_tests/aligned.cpp new file mode 100644 index 000000000..ad4837bec --- /dev/null +++ b/tests/unit_tests/aligned.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gtest/gtest.h" + +#include "common/aligned.h" + +TEST(aligned, large_null) { ASSERT_TRUE(aligned_malloc((size_t)-1, 1) == NULL); } +TEST(aligned, free_null) { aligned_free(NULL); } +TEST(aligned, zero) { void *ptr = aligned_malloc(0, 1); ASSERT_TRUE(ptr); aligned_free(ptr); } +TEST(aligned, aligned1) { void *ptr = aligned_malloc(1, 1); ASSERT_TRUE(ptr); aligned_free(ptr); } +TEST(aligned, aligned4096) { void *ptr = aligned_malloc(1, 4096); ASSERT_TRUE(ptr && ((uintptr_t)ptr & 4095) == 0); aligned_free(ptr); } +TEST(aligned, aligned8) { void *ptr = aligned_malloc(1, 8); ASSERT_TRUE(ptr && ((uintptr_t)ptr & 7) == 0); aligned_free(ptr); } +TEST(aligned, realloc_null) { void *ptr = aligned_realloc(NULL, 1, 4096); ASSERT_TRUE(ptr && ((uintptr_t)ptr & 4095) == 0); aligned_free(ptr); } +TEST(aligned, realloc_diff_align) { void *ptr = aligned_malloc(1, 4096); ASSERT_TRUE(!aligned_realloc(ptr, 1, 2048)); aligned_free(ptr); } +TEST(aligned, realloc_same) { void *ptr = aligned_malloc(1, 4096), *ptr2 = aligned_realloc(ptr, 1, 4096); ASSERT_TRUE(ptr == ptr2); aligned_free(ptr2); } +TEST(aligned, realloc_larger) { void *ptr = aligned_malloc(1, 4096), *ptr2 = aligned_realloc(ptr, 2, 4096); ASSERT_TRUE(ptr != ptr2); aligned_free(ptr2); } +TEST(aligned, realloc_zero) { void *ptr = aligned_malloc(1, 4096), *ptr2 = aligned_realloc(ptr, 0, 4096); ASSERT_TRUE(ptr && !ptr2); } + +TEST(aligned, contents_larger) +{ + unsigned char *ptr = (unsigned char*)aligned_malloc(50, 256); + ASSERT_TRUE(ptr); + for (int n = 0; n < 50; ++n) + ptr[n] = n; + unsigned char *ptr2 = (unsigned char*)aligned_realloc(ptr, 51, 256); + for (int n = 0; n < 50; ++n) + { + ASSERT_TRUE(ptr2[n] == n); + } + aligned_free(ptr2); +} + +TEST(aligned, contents_same) +{ + unsigned char *ptr = (unsigned char*)aligned_malloc(50, 256); + ASSERT_TRUE(ptr); + for (int n = 0; n < 50; ++n) + ptr[n] = n; + unsigned char *ptr2 = (unsigned char*)aligned_realloc(ptr, 50, 256); + for (int n = 0; n < 50; ++n) + { + ASSERT_TRUE(ptr2[n] == n); + } + aligned_free(ptr2); +} + +TEST(aligned, contents_smaller) +{ + unsigned char *ptr = (unsigned char*)aligned_malloc(50, 256); + ASSERT_TRUE(ptr); + for (int n = 0; n < 50; ++n) + ptr[n] = n; + unsigned char *ptr2 = (unsigned char*)aligned_realloc(ptr, 49, 256); + for (int n = 0; n < 49; ++n) + { + ASSERT_TRUE(ptr2[n] == n); + } + aligned_free(ptr2); +} +