[patch v1] malloc: add malloc_zero_aborts tunable

DJ Delorie dj@redhat.com
Mon Feb 16 16:10:45 GMT 2026


Florian Weimer <fweimer@redhat.com> writes:
> The AI is wrong, it would work as usual.  The troublesome part is that
> we need to build certain parts of malloc multiple times, which won't
> improve the current source file situation.

The AI wasn't writing or testing the code, just helping me understand
how glibc implemented ifuncs and pointing out gotchas.  I actually found
it very useful for learning and guiding, but not very accurate when it
started to run out of suggestions.  It really didn't want to admit defeat.

As for the problems I was seeing, that was based on a modified libc.so
and using gdb to see what was happening.  I know ld.so has its own
allocator and I know static libs are different; the problem was when
ld.so was linking the test case to libc.so - it calls the resolvers at
link time, and for malloc() that's before a lot of other stuff you'd
expect.  I assume there's a way to defer that resolution until the first
call but I couldn't figure that out.

Patch so far...

commit 8615a8e0bd38ac6a336bf4edf7a1004d5675076d
Author: DJ Delorie <dj@redhat.com>
Date:   Mon Feb 16 11:04:24 2026 -0500

    malloc ifuncs so far

diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
index 040a544c0e..55c6a619e5 100644
--- a/elf/dl-tunables.list
+++ b/elf/dl-tunables.list
@@ -79,6 +79,8 @@ glibc {
       type: SIZE_T
       minval: 0
     }
+    ifunc {
+    }
   }
 
   rtld {
diff --git a/malloc/Makefile b/malloc/Makefile
index fef5021298..b2074966bc 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -212,6 +212,7 @@ routines = malloc mcheck mtrace obstack reallocarray \
   alloc_buffer_copy_bytes  \
   alloc_buffer_copy_string \
   alloc_buffer_create_failure \
+  malloc_ifuncs
 
 install-lib := libmcheck.a
 non-lib.a := libmcheck.a
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 0ff016e549..8f6f050bfc 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -3525,7 +3525,7 @@ libc_hidden_def (__libc_memalign)
 /* For ISO C17.  */
 void *
 weak_function
-aligned_alloc (size_t alignment, size_t bytes)
+__libc_aligned_alloc (size_t alignment, size_t bytes)
 {
 /* Similar to memalign, but starting with ISO C17 the standard
    requires an error for alignments that are not supported by the
@@ -3543,7 +3543,7 @@ aligned_alloc (size_t alignment, size_t bytes)
 /* For ISO C23.  */
 void
 weak_function
-free_sized (void *ptr, __attribute_maybe_unused__ size_t size)
+__libc_free_sized (void *ptr, __attribute_maybe_unused__ size_t size)
 {
   /* We do not perform validation that size is the same as the original
      requested size at this time. We leave that to the sanitizers.  We
@@ -3556,8 +3556,8 @@ free_sized (void *ptr, __attribute_maybe_unused__ size_t size)
 /* For ISO C23.  */
 void
 weak_function
-free_aligned_sized (void *ptr, __attribute_maybe_unused__ size_t alignment,
-                    __attribute_maybe_unused__ size_t size)
+__libc_free_aligned_sized (void *ptr, __attribute_maybe_unused__ size_t alignment,
+			   __attribute_maybe_unused__ size_t size)
 {
   /* We do not perform validation that size and alignment is the same as
      the original requested size and alignment at this time.  We leave that
@@ -5497,22 +5497,29 @@ __malloc_info (int options, FILE *fp)
 #if IS_IN (libc)
 weak_alias (__malloc_info, malloc_info)
 
-strong_alias (__libc_calloc, __calloc) weak_alias (__libc_calloc, calloc)
-strong_alias (__libc_free, __free) strong_alias (__libc_free, free)
-strong_alias (__libc_malloc, __malloc) strong_alias (__libc_malloc, malloc)
+strong_alias (__libc_calloc, __calloc)
+//weak_alias (__libc_calloc, calloc)
+strong_alias (__libc_free, __free)
+//strong_alias (__libc_free, free)
+strong_alias (__libc_malloc, __malloc)
+//strong_alias (__libc_malloc, malloc)
 strong_alias (__libc_memalign, __memalign)
-weak_alias (__libc_memalign, memalign)
-strong_alias (__libc_realloc, __realloc) strong_alias (__libc_realloc, realloc)
-strong_alias (__libc_valloc, __valloc) weak_alias (__libc_valloc, valloc)
-strong_alias (__libc_pvalloc, __pvalloc) weak_alias (__libc_pvalloc, pvalloc)
+//weak_alias (__libc_memalign, memalign)
+strong_alias (__libc_realloc, __realloc)
+//strong_alias (__libc_realloc, realloc)
+strong_alias (__libc_valloc, __valloc)
+weak_alias (__libc_valloc, valloc)
+strong_alias (__libc_pvalloc, __pvalloc)
+weak_alias (__libc_pvalloc, pvalloc)
 strong_alias (__libc_mallinfo, __mallinfo)
 weak_alias (__libc_mallinfo, mallinfo)
 strong_alias (__libc_mallinfo2, __mallinfo2)
 weak_alias (__libc_mallinfo2, mallinfo2)
-strong_alias (__libc_mallopt, __mallopt) weak_alias (__libc_mallopt, mallopt)
+strong_alias (__libc_mallopt, __mallopt)
+weak_alias (__libc_mallopt, mallopt)
 
 weak_alias (__malloc_stats, malloc_stats)
-weak_alias (__malloc_usable_size, malloc_usable_size)
+//weak_alias (__malloc_usable_size, malloc_usable_size)
 weak_alias (__malloc_trim, malloc_trim)
 #endif
 
diff --git a/malloc/malloc_ifuncs.c b/malloc/malloc_ifuncs.c
new file mode 100644
index 0000000000..9437055a7b
--- /dev/null
+++ b/malloc/malloc_ifuncs.c
@@ -0,0 +1,147 @@
+/* Malloc IFUNC support
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   Copyright The GNU Toolchain Authors.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, see <https://www.gnu.org/licenses/>.  */
+
+/* This file does not wrap *all* of glibc's malloc API, just those
+   functions which are "wrapped" by our various extensions.  Add more
+   as needed.  */
+
+#define _GNU_SOURCE 1
+#include <stddef.h>
+#define TUNABLE_NAMESPACE malloc
+#include <elf/dl-tunables.h>
+
+#include <sysdep.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+
+extern char **_environ;
+
+static void
+djputs(const char *str)
+{
+  int i;
+  for (i=0; str[i]; i++);
+  INLINE_SYSCALL (write, 3, 1, str, i);
+}
+
+/* These are the API sets we choose between.  */
+typedef void *(malloc_ptr)(size_t);
+void *__libc_malloc (size_t);
+
+typedef void (free_ptr)(void *);
+void __libc_free (void *);
+
+typedef void *(calloc_ptr)(size_t, size_t);
+void*  __libc_calloc(size_t, size_t);
+
+typedef void *(realloc_ptr)(void*, size_t);
+void*  __libc_realloc(void*, size_t);
+
+typedef void *(aligned_alloc_ptr)(size_t, size_t);
+void *__libc_aligned_alloc (size_t, size_t);
+
+typedef void (free_aligned_sized_ptr)(void *, size_t, size_t);
+void __libc_free_aligned_sized (void *, size_t, size_t);
+
+typedef void (free_sized_ptr)(void *,size_t);
+void __libc_free_sized (void *,size_t);
+
+typedef size_t (malloc_usable_size_ptr)(void *);
+size_t __malloc_usable_size (void *);
+
+typedef void *(memalign_ptr)(size_t, size_t);
+void *__libc_memalign (size_t, size_t);
+
+typedef struct api_funcs_struct {
+  struct api_funcs_struct *parent;
+  malloc_ptr *malloc_func;
+  free_ptr *free_func;
+  calloc_ptr *calloc_func;
+  realloc_ptr *realloc_func;
+  aligned_alloc_ptr *aligned_alloc_func;
+  free_aligned_sized_ptr *free_aligned_sized_func;
+  free_sized_ptr *free_sized_func;
+  malloc_usable_size_ptr *malloc_usable_size_func;
+  memalign_ptr *memalign_func;
+} api_funcs_type;
+
+/* This one must be complete, since the others refer to it.  */
+static api_funcs_type generic_abi = {
+  .parent = NULL,
+  .malloc_func = __libc_malloc,
+  .free_func = __libc_free,
+  .calloc_func = __libc_calloc,
+  .realloc_func = __libc_realloc,
+  .aligned_alloc_func = __libc_aligned_alloc,
+  .free_aligned_sized_func = __libc_free_aligned_sized,
+  .free_sized_func = __libc_free_sized,
+  .malloc_usable_size_func = __malloc_usable_size,
+  .memalign_func = __libc_memalign,
+};
+
+/* The resolver will return one of these choices.  */
+typedef enum {
+  choose_unknown,
+  choose_generic
+} api_choice_type;
+
+static api_choice_type choice = choose_unknown;
+
+/* To ensure we resolve to a consistent API, the chooser relies on
+   this function to return the same choice every time.  */
+static api_funcs_type *
+which_api (void)
+{
+  if (choice == choose_unknown)
+    {
+      djputs("Resolver is running!\n");
+      //tun = TUNABLE_GET (ifunc, const char *, NULL);
+      djputs(_environ[0]);
+      choice = choose_generic;
+    }
+
+  switch (choice)
+    {
+    case choose_generic:
+    default:
+      return &generic_abi;
+    }
+}
+
+/* Resolvers.  */
+
+#define RESOLVE(f) \
+  static f##_ptr *resolve_##f (void) { \
+  api_funcs_type *api = which_api (); \
+  while (api->f##_func == NULL && api->parent) \
+    api = api->parent; \
+  return api->f##_func; \
+  } \
+  f##_ptr f##_ifunc __attribute((ifunc("resolve_" #f))); \
+  weak_alias (f##_ifunc, f)
+
+RESOLVE(malloc)
+RESOLVE(free)
+RESOLVE(calloc)
+RESOLVE(realloc)
+RESOLVE(aligned_alloc)
+RESOLVE(free_aligned_sized)
+RESOLVE(free_sized)
+RESOLVE(malloc_usable_size)
+RESOLVE(memalign)



More information about the Libc-alpha mailing list