memkind/0001-Handle-allocations-of-size-zero-in-libautohbw.patch
Rafael Aquini f65898f1be Allow libautohbw allocations of size zero
Resolves: RHEL-14497

Signed-off-by: Rafael Aquini <aquini@redhat.com>
2023-11-13 16:04:39 -05:00

396 lines
14 KiB
Diff

From 7106a2fc216a45a553ebfd2dbc6e93c34dc2e59c Mon Sep 17 00:00:00 2001
From: Rafael Aquini <aquini@redhat.com>
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 <michal.biesek@intel.com>
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 <patryk.kaminski@intel.com>
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 <patryk.kaminski@intel.com>
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 <aquini@redhat.com>
---
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 <stdbool.h>
#include <sys/types.h>
/**
@@ -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 <fstream>
@@ -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