diff --git a/0001-Handle-allocations-of-size-zero-in-libautohbw.patch b/0001-Handle-allocations-of-size-zero-in-libautohbw.patch new file mode 100644 index 0000000..49e234f --- /dev/null +++ b/0001-Handle-allocations-of-size-zero-in-libautohbw.patch @@ -0,0 +1,395 @@ +From 7106a2fc216a45a553ebfd2dbc6e93c34dc2e59c Mon Sep 17 00:00:00 2001 +From: Rafael Aquini +Date: Fri, 27 Oct 2023 09:50:44 -0400 +Subject: [PATCH] Handle allocations of size zero in libautohbw + +JIRA: https://issues.redhat.com/browse/RHEL-14497 + +This patch is a folded backport of the following 3 upstream commits: + +commit 0306a02e972ab148b9f1ffb297f776147c6aee48 +Author: michalbiesek +Date: Wed Jun 2 16:08:13 2021 +0200 + + Remove mallocx/rallocx check + + - allocx() size class overflow is behavior defined since jemalloc 4.1.0 + +commit 1354df2e8b443d390b51c789a062bf4e54a3f6bd +Author: Patryk Kaminski +Date: Thu Oct 6 16:11:07 2022 +0200 + + Allocation of size zero based on standard library + + When loading memtier library, return ptr is checked for malloc(0). + Libmemtier's allocations have the same result for size zero as system's + standard library. + +commit 9197066cf086eb2d14eb17fc552e66c72b438661 +Author: Patryk Kaminski +Date: Thu Nov 10 11:33:48 2022 +0100 + + Handle allocations of size zero in libautohbw + + When loading autohbw library, return ptr is checked for malloc(0). + Libautohbw's allocations have the same result for size zero as system's + standard library. + +Signed-off-by: Rafael Aquini +--- + autohbw/autohbw.c | 17 ++++++ + include/memkind.h | 8 +++ + include/memkind/internal/memkind_private.h | 1 + + man/autohbw.7 | 7 +++ + man/memkind.3 | 9 ++++ + src/memkind.c | 6 +++ + src/memkind_arena.c | 61 +++++++++------------- + src/memkind_default.c | 21 +++++--- + test/bat_tests.cpp | 2 +- + 9 files changed, 89 insertions(+), 43 deletions(-) + +diff --git a/autohbw/autohbw.c b/autohbw/autohbw.c +index 85960991..1c6670fb 100644 +--- a/autohbw/autohbw.c ++++ b/autohbw/autohbw.c +@@ -255,6 +255,19 @@ static void setEnvValues() + printLimits(); + } + ++extern void *__libc_malloc(size_t); ++extern void *__libc_free(void *); ++ ++static bool check_allow_zero_allocs() ++{ ++ void *ptr = __libc_malloc(0); ++ if (ptr) { ++ __libc_free(ptr); ++ return true; ++ } ++ return false; ++} ++ + // This function is executed at library load time. + // Initialize HBW arena by making a dummy allocation/free at library load + // time. Until HBW initialization is complete, we must not call any +@@ -292,6 +305,10 @@ static void AUTOHBW_INIT autohbw_load(void) + LOG(ALWAYS, "\t-HBW int call succeeded\n"); + memkind_free(hbw_kind, pp); + ++ bool allow_zero_allocs = check_allow_zero_allocs(); ++ memkind_set_allow_zero_allocs(hbw_kind, allow_zero_allocs); ++ memkind_set_allow_zero_allocs(MEMKIND_DEFAULT, allow_zero_allocs); ++ + MemkindInitDone = true; // enable HBW allocation + } + +diff --git a/include/memkind.h b/include/memkind.h +index 6e95dfdd..00e66e8f 100644 +--- a/include/memkind.h ++++ b/include/memkind.h +@@ -6,6 +6,7 @@ + extern "C" { + #endif + ++#include + #include + + /** +@@ -407,6 +408,13 @@ void memkind_free(memkind_t kind, void *ptr); + /// + void *memkind_defrag_reallocate(memkind_t kind, void *ptr); + ++/// @brief Sets the behavior for allocations with size zero ++/// @param kind specified memory kind ++/// @param allow_zero_allocs determines returned ptr for malloc-like functions ++/// for allocations with size zero, return a valid ptr when set to true, NULL ++/// when set to false ++void memkind_set_allow_zero_allocs(memkind_t kind, bool allow_zero_allocs); ++ + #ifdef __cplusplus + } + #endif +diff --git a/include/memkind/internal/memkind_private.h b/include/memkind/internal/memkind_private.h +index 5b83b7a5..ed34108a 100644 +--- a/include/memkind/internal/memkind_private.h ++++ b/include/memkind/internal/memkind_private.h +@@ -88,6 +88,7 @@ struct memkind { + unsigned int + arena_map_mask; // arena_map_len - 1 to optimize modulo operation on arena_map_len + unsigned int arena_zero; // index first jemalloc arena of this kind ++ bool allow_zero_allocs; // Return valid ptr for malloc(0) calls + }; + + struct memkind_config { +diff --git a/man/autohbw.7 b/man/autohbw.7 +index 6cd0b89a..2e39195a 100644 +--- a/man/autohbw.7 ++++ b/man/autohbw.7 +@@ -141,6 +141,13 @@ the environment variable + must be set to indicate the high-bandwidth node as indicated in + .B memkind(3). + ++.PP ++When \f[B]libautohbw\f[R] is loaded with \f[B]LD_PRELOAD\f[R], ++allocations with size zero, like \f[B]malloc\f[R](0), have the same ++result as the system\[cq]s standard library call. ++Most notably, a valid pointer may be returned in such calls, contrary to ++the default memkind behavior of returning NULL when size zero is passed ++to malloc-like functions. + + .SH "EXAMPLES" + .br +diff --git a/man/memkind.3 b/man/memkind.3 +index 00a8f850..b94ffcdd 100644 +--- a/man/memkind.3 ++++ b/man/memkind.3 +@@ -69,6 +69,8 @@ This header expose STANDARD and EXPERIMENTAL API. API Standards are described be + .BI "int memkind_destroy_kind(memkind_t " "kind" ); + .br + .BI "int memkind_check_available(memkind_t " "kind" ); ++.br ++.BI "void memkind_set_allow_zero_allocs(memkind_t" "kind" ", bool " "allow_zero_allocs" ); + .sp + .B "STATISTICS:" + .br +@@ -544,6 +546,13 @@ returns zero if the specified + is available or an error code from the + .B ERRORS + section if it is not. ++.BR memkind_set_allow_zero_allocs () ++for a given ++.I kind ++, determines the behavior of malloc-like functions when size passed to them is equal to zero. ++These functions return a valid pointer when ++.I allow_zero_allocs ++is set to true, return NULL when set to false (default memkind behavior). + .PP + .BR MEMKIND_PMEM_MIN_SIZE + The minimum size which allows to limit the file-backed memory partition. +diff --git a/src/memkind.c b/src/memkind.c +index 42686646..6a38dc4b 100644 +--- a/src/memkind.c ++++ b/src/memkind.c +@@ -894,3 +894,9 @@ MEMKIND_EXPORT int memkind_get_stat(memkind_t kind, memkind_stat_type stat, + return kind->ops->get_stat(kind, stat, value); + } + } ++ ++MEMKIND_EXPORT void memkind_set_allow_zero_allocs(memkind_t kind, ++ bool allow_zero_allocs) ++{ ++ kind->allow_zero_allocs = allow_zero_allocs; ++} +diff --git a/src/memkind_arena.c b/src/memkind_arena.c +index b1fd0eeb..1b29c6da 100644 +--- a/src/memkind_arena.c ++++ b/src/memkind_arena.c +@@ -48,8 +48,7 @@ static const struct stats_arena arena_stats [MEMKIND_STAT_TYPE_MAX_VALUE] = { + { .stats = {"stats.arenas.%u.small.allocated", "stats.arenas.%u.large.allocated"}, .stats_no = 2}, + }; + +-static void *jemk_mallocx_check(size_t size, int flags); +-static void *jemk_rallocx_check(void *ptr, size_t size, int flags); ++static void *jemk_mallocx_check(size_t size, int flags, bool allow_zero_allocs); + static void tcache_finalize(void *args); + + static unsigned integer_log2(unsigned v) +@@ -487,7 +486,8 @@ MEMKIND_EXPORT void *memkind_arena_malloc(struct memkind *kind, size_t size) + int err = kind->ops->get_arena(kind, &arena, size); + if (MEMKIND_LIKELY(!err)) { + result = jemk_mallocx_check(size, +- MALLOCX_ARENA(arena) | get_tcache_flag(kind->partition, size)); ++ MALLOCX_ARENA(arena) | get_tcache_flag(kind->partition, size), ++ kind->allow_zero_allocs); + } + return result; + } +@@ -496,12 +496,14 @@ static void *memkind_arena_malloc_no_tcache(struct memkind *kind, size_t size) + { + void *result = NULL; + if (kind == MEMKIND_DEFAULT) { +- result = jemk_mallocx_check(size, MALLOCX_TCACHE_NONE); ++ result = jemk_mallocx_check(size, MALLOCX_TCACHE_NONE, kind->allow_zero_allocs); + } else { + unsigned arena; + int err = kind->ops->get_arena(kind, &arena, size); + if (MEMKIND_LIKELY(!err)) { +- result = jemk_mallocx_check(size, MALLOCX_ARENA(arena) | MALLOCX_TCACHE_NONE); ++ result = jemk_mallocx_check(size, ++ MALLOCX_ARENA(arena) | MALLOCX_TCACHE_NONE, ++ kind->allow_zero_allocs); + } + } + return result; +@@ -541,10 +543,13 @@ MEMKIND_EXPORT void *memkind_arena_realloc(struct memkind *kind, void *ptr, + if (MEMKIND_LIKELY(!err)) { + if (ptr == NULL) { + ptr = jemk_mallocx_check(size, +- MALLOCX_ARENA(arena) | get_tcache_flag(kind->partition, size)); ++ MALLOCX_ARENA(arena) | get_tcache_flag(kind->partition, size), ++ kind->allow_zero_allocs); + } else { +- ptr = jemk_rallocx_check(ptr, size, +- MALLOCX_ARENA(arena) | get_tcache_flag(kind->partition, size)); ++ ptr = jemk_rallocx(ptr, size, ++ MALLOCX_ARENA(arena) | get_tcache_flag(kind->partition, size)); ++ if (MEMKIND_UNLIKELY(!ptr)) ++ errno = ENOMEM; + } + } + } +@@ -613,7 +618,8 @@ MEMKIND_EXPORT void *memkind_arena_calloc(struct memkind *kind, size_t num, + int err = kind->ops->get_arena(kind, &arena, size); + if (MEMKIND_LIKELY(!err)) { + result = jemk_mallocx_check(num * size, +- MALLOCX_ARENA(arena) | MALLOCX_ZERO | get_tcache_flag(kind->partition, size)); ++ MALLOCX_ARENA(arena) | MALLOCX_ZERO | get_tcache_flag(kind->partition, size), ++ kind->allow_zero_allocs); + } + return result; + } +@@ -631,7 +637,8 @@ MEMKIND_EXPORT int memkind_arena_posix_memalign(struct memkind *kind, + err = memkind_posix_check_alignment(kind, alignment); + } + if (MEMKIND_LIKELY(!err)) { +- if (MEMKIND_UNLIKELY(size_out_of_bounds(size))) { ++ if (!kind->allow_zero_allocs && ++ MEMKIND_UNLIKELY(size_out_of_bounds(size))) { + return 0; + } + /* posix_memalign should not change errno. +@@ -639,7 +646,8 @@ MEMKIND_EXPORT int memkind_arena_posix_memalign(struct memkind *kind, + int errno_before = errno; + *memptr = jemk_mallocx_check(size, + MALLOCX_ALIGN(alignment) | MALLOCX_ARENA(arena) | get_tcache_flag( +- kind->partition, size)); ++ kind->partition, size), ++ kind->allow_zero_allocs); + errno = errno_before; + err = *memptr ? 0 : ENOMEM; + } +@@ -711,7 +719,7 @@ MEMKIND_EXPORT int memkind_thread_get_arena(struct memkind *kind, + } + #endif //MEMKIND_TLS + +-static void *jemk_mallocx_check(size_t size, int flags) ++static void *jemk_mallocx_check(size_t size, int flags, bool allow_zero_allocs) + { + /* + * Checking for out of range size due to unhandled error in +@@ -719,33 +727,16 @@ static void *jemk_mallocx_check(size_t size, int flags) + * LLONG_MAX <= size <= ULLONG_MAX + * which is the result of passing a negative signed number as size + */ +- void *result = NULL; ++ void *ptr; + +- if (MEMKIND_UNLIKELY(size >= LLONG_MAX)) { +- errno = ENOMEM; +- } else if (size != 0) { +- result = jemk_mallocx(size, flags); +- } +- return result; +-} ++ if (!allow_zero_allocs && MEMKIND_UNLIKELY(!size)) ++ return NULL; + +-static void *jemk_rallocx_check(void *ptr, size_t size, int flags) +-{ +- /* +- * Checking for out of range size due to unhandled error in +- * jemk_mallocx(). Size invalid for the range +- * LLONG_MAX <= size <= ULLONG_MAX +- * which is the result of passing a negative signed number as size +- */ +- void *result = NULL; +- +- if (MEMKIND_UNLIKELY(size >= LLONG_MAX)) { ++ ptr = jemk_mallocx(size, flags); ++ if (MEMKIND_UNLIKELY(!ptr)) + errno = ENOMEM; +- } else { +- result = jemk_rallocx(ptr, size, flags); +- } +- return result; + ++ return ptr; + } + + void memkind_arena_init(struct memkind *kind) +diff --git a/src/memkind_default.c b/src/memkind_default.c +index b062dd2a..324a5d94 100644 +--- a/src/memkind_default.c ++++ b/src/memkind_default.c +@@ -64,25 +64,25 @@ MEMKIND_EXPORT int memkind_default_destroy(struct memkind *kind) + + MEMKIND_EXPORT void *memkind_default_malloc(struct memkind *kind, size_t size) + { +- if(MEMKIND_UNLIKELY(size_out_of_bounds(size))) { ++ if (!kind->allow_zero_allocs && MEMKIND_UNLIKELY(size_out_of_bounds(size))) + return NULL; +- } ++ + return jemk_malloc(size); + } + + MEMKIND_EXPORT void *memkind_default_calloc(struct memkind *kind, size_t num, + size_t size) + { +- if(MEMKIND_UNLIKELY(size_out_of_bounds(num) || size_out_of_bounds(size))) { ++ if (!kind->allow_zero_allocs && MEMKIND_UNLIKELY(size_out_of_bounds(num) || size_out_of_bounds(size))) + return NULL; +- } ++ + return jemk_calloc(num, size); + } + + MEMKIND_EXPORT int memkind_default_posix_memalign(struct memkind *kind, + void **memptr, size_t alignment, size_t size) + { +- if(MEMKIND_UNLIKELY(size_out_of_bounds(size))) { ++ if (!kind->allow_zero_allocs && MEMKIND_UNLIKELY(size_out_of_bounds(size))) { + *memptr = NULL; + return 0; + } +@@ -92,11 +92,18 @@ MEMKIND_EXPORT int memkind_default_posix_memalign(struct memkind *kind, + MEMKIND_EXPORT void *memkind_default_realloc(struct memkind *kind, void *ptr, + size_t size) + { +- if(MEMKIND_UNLIKELY(size_out_of_bounds(size))) { ++ void *ret_ptr; ++ ++ if (!kind->allow_zero_allocs && MEMKIND_UNLIKELY(size_out_of_bounds(size))) { + jemk_free(ptr); + return NULL; + } +- return jemk_realloc(ptr, size); ++ ++ ret_ptr = jemk_realloc(ptr, size); ++ if (MEMKIND_UNLIKELY(!ret_ptr && ptr && size != 0)) ++ errno = ENOMEM; ++ ++ return ret_ptr; + } + + MEMKIND_EXPORT void memkind_default_free(struct memkind *kind, void *ptr) +diff --git a/test/bat_tests.cpp b/test/bat_tests.cpp +index 5b5a1f57..190f319f 100644 +--- a/test/bat_tests.cpp ++++ b/test/bat_tests.cpp +@@ -1,6 +1,7 @@ + // SPDX-License-Identifier: BSD-2-Clause + /* Copyright (C) 2014 - 2020 Intel Corporation. */ + ++#include "include/memkind/internal/memkind_private.h" + #include "memkind.h" + + #include +@@ -510,4 +511,3 @@ TEST_F(BATest, test_TC_MEMKIND_REGULAR_nodemask) + check_numa_nodes(kind_nodemask, MPOL_BIND, mem, 1234567); + memkind_free(MEMKIND_REGULAR, mem); + } +- +-- +2.41.0 + diff --git a/memkind.spec b/memkind.spec index f91706d..2c4dacd 100644 --- a/memkind.spec +++ b/memkind.spec @@ -3,7 +3,7 @@ Name: memkind Summary: User Extensible Heap Manager Version: 1.10.1 -Release: 1%{?checkout}%{?dist} +Release: 2%{?checkout}%{?dist} License: BSD Group: System Environment/Libraries URL: http://memkind.github.io/memkind @@ -29,6 +29,9 @@ Source0: %{name}-%{version}.tar.gz # the strong stack protection setup Patch0: configure.ac.patch +# Fix https://issues.redhat.com/browse/RHEL-14497 +Patch1: 0001-Handle-allocations-of-size-zero-in-libautohbw.patch + %description The memkind library is an user extensible heap manager built on top of jemalloc which enables control of memory characteristics and a @@ -61,6 +64,7 @@ alpha release. Feedback on design or implementation is greatly appreciated. %prep %setup -q -a 0 -n %{name}-%{version} %patch0 -p1 +%patch1 -p1 %build %set_build_flags @@ -112,6 +116,9 @@ rm -f %{buildroot}/%{_docdir}/%{name}/VERSION %{_mandir}/man3/pmemallocator.3.* %changelog +* Mon Nov 13 2023 Rafael Aquini - 1.10.1-3 +- Allow libautohbw allocations of size zero (RHEL-14497) + * Mon Dec 14 2020 Rafael Aquini - 1.10.1-1 - Update memkind source file to 1.10.1 upstream (1841966)