f65898f1be
Resolves: RHEL-14497 Signed-off-by: Rafael Aquini <aquini@redhat.com>
396 lines
14 KiB
Diff
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
|
|
|