Compare commits

...
Author SHA1 Message Date
Yury Khrustalev 4189e8060e malloc: aarch64: Add MTE-aware implementations
Tag memory on systems that support MTE and when memory
tagging is enabled at runtime.

Also add tests that check logical and allocation tags.
2026-05-07 13:44:26 +01:00
Yury Khrustalev 43bd158d89 malloc: aarch64: Add MTE ifunc resolvers
We introduce the ARCH_INIT_MALLOC() macro that is defined in
the sysdep headers to allow doing target-specific initialisation
in __ptmalloc_init. On AArch64 we use this macro to modify
global variables in malloc.c when MTE is enabled based on the
runtime value calculated from the glibc.mem.aarch64_mte tunable.

We create AArch64-specific version of the 'malloc-size.h' header
to provide custom 'memsize()' and 'checked_request2size()'. This
is required to support MTE memory tagging without the need to
check its status every time.

We modify ifunc resolvers for AArch64 to return MTE-aware versions
if malloc functions when MTE is enabled. At this point these *_mte
functions don't actually do any memory tagging. This will be done
in a subsequent commit.

Finally, we add the glibc.mem.aarch64_mte tunable to all malloc
tests on AArch64. On systems without MTE this will have no effect
but on systems with MTE it will make sure that tests are executed
with memory tagging enabled in sync mode.
2026-05-07 13:43:10 +01:00
Yury Khrustalev fe1173fcf4 aarch64: add glibc.mem.aarch64_mte tunable
Add new tunable glibc.mem.aarch64_mte of string type with supported
values: none, sync, async, and auto. Use it to control prctl syscall
used to send the PR_SET_TAGGED_ADDR_CTRL command.

To avoid confusion, we also remove the glibc.mem.tagging tunable.

The new tunable defaults to 'none' and only works of MTE is supported.
2026-05-07 13:43:10 +01:00
Yury Khrustalev acce1ee5a0 malloc: Put malloc_chunk into separate header
Refactor malloc code to extract the definition of malloc_chunk
struct into a dedicated header  malloc-chunk.h along with basic
chunk-related macros.

This is a sysdep header.

The new header 'malloc-chunk.h' will include 'malloc-alignment.h'
that has all dependencies necessary for chunk declarations.

In turn, 'malloc-size.h' will now include 'malloc-chunk.h'. We
also move size-related definitions from malloc.c into this header.
2026-05-07 13:43:10 +01:00
Yury Khrustalev 23d806003d malloc: Put MALLOC_ALIGNMENT depends in one header
The 'malloc-alignment.h' header defines macro MALLOC_ALIGNMENT that
depends on SIZE_SZ which in turn depends on INTERNAL_SIZE_T.

These dependencies used to be defined in 'malloc-size.h'. We should
either merge these two headers or move the dependencies or change the
'malloc-alignment.h' header to make sure it is self-consistent.

This commit does the latter. In addition we fix the tst-mallocalign1.c
test that uses the MALLOC_ALIGN_MASK internal to malloc. Since it's
been moved to 'malloc-alignment.h' it makes sense to include this
header into the test source.
2026-05-07 13:43:10 +01:00
Yury Khrustalev 884f8f9325 malloc: Remove currently broken memory tagging
Remove AArch64-specific code, that is currently broken, from the
core malloc implementation.
2026-05-07 13:43:10 +01:00
Yury Khrustalev f33b8e0df5 malloc: Remove code conditional on USE_MTAG
Further malloc refactoring related to memory tagging.

Remove code that was only compiled when macro USE_MTAG was defined
except for the AArch64-specific assembly MTE code that is going to
be compiled unconditionally from now on.

As a result, we change 'mtag_mmap_flags' to 'extra_mmap_prot' that
is now always defined. Change of the name due to this being used
as part of PROT options in mmap syscalls rather than part of flags.

Remove 'mtag_enabled' that would become compile-time false. Also
remove any code that would never be compiled when 'mtag_enabled'
is false.
2026-05-07 13:43:10 +01:00
Yury Khrustalev d59e08f09b configure: remove --enable-memory-tagging option
Remove the '--enable-memory-tagging' configure option along with
all associated variables and macros.

This includes:
 - Removing the 'glibc.mem.tagging' tunable.
 - Removing parts of the configure script that processes the
   '--enable-memory-tagging' configure option.
 - Remove the 'memory-tagging' makefile variable.
 - Remove the USE_MTAG macro definition. Code that is conditionally
   compiled when this macro is defined is not removed at this stage
   and will be dealt with in the following commits.

Also, update documentation accordingly.
2026-05-07 13:43:10 +01:00
Yury Khrustalev 7ca145fabe malloc: introduce ifuncs for malloc functions
Introduce ifuncs and resolvers for functions pertinent to the
malloc interface.

In order to do this, we first rename all core implementations by
adding the '_core' suffix. These functions are supposed to be strictly
internal and must not be used beyond the malloc code. For example, the
__libc_malloc() function becomes __libc_malloc_core() and instead
__libc_malloc() becomes an ifunc with a corresponding resolver that
may return '__libc_malloc_core' as the implementation.

Each ifunc comes with a generic resolver that will be used unless a
target overrides the resolvers in its sysdeps directory. All resolvers
are supposed to be overridden together, and the logic of the generic
resolvers should be replicated by the target-specific ones.

This patch contains, as an example, aarch64-specific resolvers. At this
moment they are identical to the generic ones but in the future they can
be changed to support for features, e.g. to handle memory tagging.

Corresponding aliases are moved next to the ifuncs as well.

On targets that do not support ifuncs we alias externally visible
functions as well as the __libc_* functions to their respective
*_core symbols.
2026-05-07 13:43:10 +01:00
Yury Khrustalev 1b27e7732f support: add support_ptr_after_free
Some tests use pointers after the associated memory has been freed.
On targets that support memory tagging, using such pointers even
for test purposes might be impossible. To work around this, we add
new function that would allow to clear a pointer in a target-specific
way.

We modify 3 relevant malloc tests: tst-malloc-backtrace, tst-tcfree3,
and tst-safe-linking.
2026-05-07 13:43:07 +01:00
58 changed files with 1707 additions and 715 deletions
-14
View File
@@ -154,20 +154,6 @@ passed to 'configure'. For example:
NOTE: '--enable-cet' is only supported on x86_64 and x32.
'--enable-memory-tagging'
Enable memory tagging support if the architecture supports it.
When the GNU C Library is built with this option then the resulting
library will be able to control the use of tagged memory when
hardware support is present by use of the tunable
'glibc.mem.tagging'. This includes the generation of tagged memory
when using the 'malloc' APIs.
At present only AArch64 platforms with MTE provide this
functionality, although the library will still operate (without
memory tagging) on older versions of the architecture.
The default is to disable support for memory tagging.
'--disable-profile'
Don't build libraries with profiling information. You may want to
use this option if you don't plan to do profiling.
-3
View File
@@ -182,9 +182,6 @@
/* Define if inlined system calls are available. */
#undef HAVE_INLINED_SYSCALLS
/* Define if memory tagging support should be enabled. */
#undef USE_MTAG
/* Package description. */
#undef PKGVERSION
-2
View File
@@ -78,8 +78,6 @@ multi-arch = @multi_arch@
mach-interface-list = @mach_interface_list@
memory-tagging = @memory_tagging@
# Configuration options.
build-shared = @shared@
build-profile = @profile@
Vendored
-24
View File
@@ -703,7 +703,6 @@ INSTALL_PROGRAM
base_machine
build_pt_chown
build_nscd
memory_tagging
enable_werror
force_install
bindnow
@@ -811,7 +810,6 @@ enable_maintainer_mode
enable_kernel
enable_werror
enable_multi_arch
enable_memory_tagging
enable_systemtap
enable_build_nscd
enable_nscd
@@ -1490,8 +1488,6 @@ Optional Features:
--disable-werror do not build with -Werror
--enable-multi-arch enable single DSO with optimizations for multiple
architectures
--enable-memory-tagging enable memory tagging if supported by the
architecture [default=no]
--enable-systemtap enable systemtap static probe points [default=no]
--disable-build-nscd disable building and installing the nscd daemon
--disable-nscd library functions will not contact the nscd daemon
@@ -4690,26 +4686,6 @@ esac
fi
# Check whether --enable-memory-tagging was given.
if test ${enable_memory_tagging+y}
then :
enableval=$enable_memory_tagging; memory_tagging=$enableval
else case e in #(
e) memory_tagging=no ;;
esac
fi
if test "$memory_tagging" = yes; then
# Only enable this on architectures that support it.
case $host_cpu in
aarch64)
printf "%s\n" "#define USE_MTAG 1" >>confdefs.h
;;
esac
fi
# Check whether --enable-systemtap was given.
if test ${enable_systemtap+y}
then :
-15
View File
@@ -328,21 +328,6 @@ AC_ARG_ENABLE([multi-arch],
[multi_arch=$enableval],
[multi_arch=default])
AC_ARG_ENABLE([memory-tagging],
AS_HELP_STRING([--enable-memory-tagging],
[enable memory tagging if supported by the architecture @<:@default=no@:>@]),
[memory_tagging=$enableval],
[memory_tagging=no])
if test "$memory_tagging" = yes; then
# Only enable this on architectures that support it.
case $host_cpu in
aarch64)
AC_DEFINE(USE_MTAG)
;;
esac
fi
AC_SUBST(memory_tagging)
AC_ARG_ENABLE([systemtap],
[AS_HELP_STRING([--enable-systemtap],
[enable systemtap static probe points @<:@default=no@:>@])],
-5
View File
@@ -116,11 +116,6 @@ glibc {
}
mem {
tagging {
type: INT_32
minval: 0
maxval: 255
}
decorate_maps {
type: INT_32
minval: 0
+6
View File
@@ -411,6 +411,12 @@ endif
endif
endif
tests-special += \
$(objpfx)tst-check-internal-calls.out \
# tests-special
$(objpfx)tst-check-internal-calls.out: tst-check-internal-calls.sh malloc.c
$(SHELL) $^ > $@; $(evaluate-test)
include ../Rules
CFLAGS-mcheck-init.c += $(PIC-ccflag)
+7 -16
View File
@@ -22,6 +22,8 @@
#define TUNABLE_NAMESPACE malloc
#include <elf/dl-tunables.h>
#include <malloc-init.h>
/* Compile-time constants. */
#define HEAP_MIN_SIZE (32 * 1024)
@@ -252,20 +254,6 @@ __ptmalloc_init (void)
tcache_key_initialize ();
#endif
#ifdef USE_MTAG
if ((TUNABLE_GET_FULL (glibc, mem, tagging, int32_t, NULL) & 1) != 0)
{
/* If the tunable says that we should be using tagged memory
and that morecore does not support tagged regions, then
disable it. */
if (__MTAG_SBRK_UNTAGGED)
__always_fail_morecore = true;
mtag_enabled = true;
mtag_mmap_flags = __MTAG_MMAP_FLAGS;
}
#endif
#if defined SHARED && IS_IN (libc)
/* In case this libc copy is in a non-default namespace, never use
brk. Likewise if dlopened from statically linked program. The
@@ -302,6 +290,9 @@ __ptmalloc_init (void)
do_set_mmap_threshold (mp_.hp_pagesize);
__always_fail_morecore = true;
}
/* Perform any target-specific initialisation. */
ARCH_INIT_MALLOC ();
}
/* Managing heaps and arenas (for concurrent threads) */
@@ -417,7 +408,7 @@ alloc_new_heap (size_t size, size_t top_pad, size_t pagesize,
}
}
}
if (__mprotect (p2, size, mtag_mmap_flags | PROT_READ | PROT_WRITE) != 0)
if (__mprotect (p2, size, extra_mmap_prot | PROT_READ | PROT_WRITE) != 0)
{
__munmap (p2, max_size);
return NULL;
@@ -471,7 +462,7 @@ grow_heap (heap_info *h, long diff)
{
if (__mprotect ((char *) h + h->mprotect_size,
(unsigned long) new_size - h->mprotect_size,
mtag_mmap_flags | PROT_READ | PROT_WRITE) != 0)
extra_mmap_prot | PROT_READ | PROT_WRITE) != 0)
return -2;
h->mprotect_size = new_size;
+5 -21
View File
@@ -19,12 +19,8 @@
#define __mremap mremap
#include "malloc.c"
/* When memory is tagged, the checking data is stored in the user part
of the chunk. We can't rely on the user not having modified the
tags, so fetch the tag at each location before dereferencing
it. */
#define SAFE_CHAR_OFFSET(p,offset) \
((unsigned char *) tag_at (((unsigned char *) p) + offset))
((unsigned char *) (((unsigned char *) p) + offset))
/* A simple, standard set of debugging hooks. Overhead is `only' one
byte per chunk; still this will catch most cases of double frees or
@@ -204,7 +200,7 @@ malloc_check (size_t sz)
top_check ();
victim = _int_malloc (&main_arena, nb);
__libc_lock_unlock (main_arena.mutex);
return mem2mem_check (tag_new_usable (victim), sz);
return mem2mem_check (victim, sz);
}
static void
@@ -217,11 +213,6 @@ free_check (void *mem)
int err = errno;
/* Quickly check that the freed pointer matches the tag for the memory.
This gives a useful double-free detection. */
if (__glibc_unlikely (mtag_enabled))
*(volatile char *)mem;
__libc_lock_lock (main_arena.mutex);
p = mem2chunk_check (mem, NULL);
if (!p)
@@ -233,8 +224,6 @@ free_check (void *mem)
}
else
{
/* Mark the chunk as belonging to the library again. */
(void)tag_region (chunk2mem (p), memsize (p));
_int_free_chunk (&main_arena, p, chunksize (p), 1);
__libc_lock_unlock (main_arena.mutex);
}
@@ -263,11 +252,6 @@ realloc_check (void *oldmem, size_t bytes)
return NULL;
}
/* Quickly check that the freed pointer matches the tag for the memory.
This gives a useful double-free detection. */
if (__glibc_unlikely (mtag_enabled))
*(volatile char *)oldmem;
__libc_lock_lock (main_arena.mutex);
const mchunkptr oldp = mem2chunk_check (oldmem, &magic_p);
__libc_lock_unlock (main_arena.mutex);
@@ -288,7 +272,7 @@ realloc_check (void *oldmem, size_t bytes)
#if HAVE_MREMAP
mchunkptr newp = mremap_chunk (oldp, chnb);
if (newp)
newmem = chunk2mem_tag (newp);
newmem = chunk2mem (newp);
else
#endif
{
@@ -323,7 +307,7 @@ invert:
__libc_lock_unlock (main_arena.mutex);
return mem2mem_check (tag_new_usable (newmem), bytes);
return mem2mem_check (newmem, bytes);
}
static void *
@@ -365,7 +349,7 @@ memalign_check (size_t alignment, size_t bytes)
top_check ();
mem = _int_memalign (&main_arena, alignment, bytes + 1);
__libc_lock_unlock (main_arena.mutex);
return mem2mem_check (tag_new_usable (mem), bytes);
return mem2mem_check (mem, bytes);
}
static void
+115 -363
View File
@@ -233,9 +233,7 @@
/* For ALIGN_UP et. al. */
#include <libc-pointer-arith.h>
/* For memory tagging. */
#include <libc-mtag.h>
/* For internal malloc interfaces and declarations. */
#include <malloc/malloc-internal.h>
/* For SINGLE_THREAD_P. */
@@ -349,99 +347,7 @@ verify (PTRDIFF_MAX <= SIZE_MAX / 2);
#define MORECORE (*__glibc_morecore)
#define MORECORE_FAILURE NULL
/* Memory tagging. */
/* Some systems support the concept of tagging (sometimes known as
coloring) memory locations on a fine grained basis. Each memory
location is given a color (normally allocated randomly) and
pointers are also colored. When the pointer is dereferenced, the
pointer's color is checked against the memory's color and if they
differ the access is faulted (sometimes lazily).
We use this in glibc by maintaining a single color for the malloc
data structures that are interleaved with the user data and then
assigning separate colors for each block allocation handed out. In
this way simple buffer overruns will be rapidly detected. When
memory is freed, the memory is recolored back to the glibc default
so that simple use-after-free errors can also be detected.
If memory is reallocated the buffer is recolored even if the
address remains the same. This has a performance impact, but
guarantees that the old pointer cannot mistakenly be reused (code
that compares old against new will see a mismatch and will then
need to behave as though realloc moved the data to a new location).
Internal API for memory tagging support.
The aim is to keep the code for memory tagging support as close to
the normal APIs in glibc as possible, so that if tagging is not
enabled in the library, or is disabled at runtime then standard
operations can continue to be used. Support macros are used to do
this:
void *tag_new_zero_region (void *ptr, size_t size)
Allocates a new tag, colors the memory with that tag, zeros the
memory and returns a pointer that is correctly colored for that
location. The non-tagging version will simply call memset with 0.
void *tag_region (void *ptr, size_t size)
Color the region of memory pointed to by PTR and size SIZE with
the color of PTR. Returns the original pointer.
void *tag_new_usable (void *ptr)
Allocate a new random color and use it to color the user region of
a chunk; this may include data from the subsequent chunk's header
if tagging is sufficiently fine grained. Returns PTR suitably
recolored for accessing the memory there.
void *tag_at (void *ptr)
Read the current color of the memory at the address pointed to by
PTR (ignoring it's current color) and return PTR recolored to that
color. PTR must be valid address in all other respects. When
tagging is not enabled, it simply returns the original pointer.
*/
#ifdef USE_MTAG
static bool mtag_enabled = false;
static int mtag_mmap_flags = 0;
#else
# define mtag_enabled false
# define mtag_mmap_flags 0
#endif
static __always_inline void *
tag_region (void *ptr, size_t size)
{
if (__glibc_unlikely (mtag_enabled))
return __libc_mtag_tag_region (ptr, size);
return ptr;
}
static __always_inline void *
tag_new_zero_region (void *ptr, size_t size)
{
if (__glibc_unlikely (mtag_enabled))
return __libc_mtag_tag_zero_region (__libc_mtag_new_tag (ptr), size);
return memset (ptr, 0, size);
}
/* Defined later. */
static void *
tag_new_usable (void *ptr);
static __always_inline void *
tag_at (void *ptr)
{
if (__glibc_unlikely (mtag_enabled))
return __libc_mtag_address_get_tag (ptr);
return ptr;
}
#include <string.h>
static int extra_mmap_prot = 0;
/*
MORECORE-related declarations. By default, rely on sbrk
@@ -566,8 +472,8 @@ tag_at (void *ptr)
differs across systems, but is in all cases less than the maximum
representable value of a size_t.
*/
void *__libc_malloc (size_t);
libc_hidden_proto (__libc_malloc)
void *__libc_malloc_core (size_t);
libc_hidden_proto (__libc_malloc_core)
static void *__libc_calloc2 (size_t);
static void *__libc_malloc2 (size_t);
@@ -583,15 +489,16 @@ static void *__libc_malloc2 (size_t);
when possible, automatically trigger operations that give
back unused memory to the system, thus reducing program footprint.
*/
void __libc_free(void*);
libc_hidden_proto (__libc_free)
void __libc_free_core (void *);
libc_hidden_proto (__libc_free_core)
/*
calloc(size_t n_elements, size_t element_size);
Returns a pointer to n_elements * element_size bytes, with all locations
set to zero.
*/
void* __libc_calloc(size_t, size_t);
void * __libc_calloc_core (size_t, size_t);
libc_hidden_proto (__libc_calloc_core)
/*
realloc(void* p, size_t n)
@@ -620,8 +527,8 @@ void* __libc_calloc(size_t, size_t);
The old unix realloc convention of allowing the last-free'd chunk
to be used as an argument to realloc is not supported.
*/
void* __libc_realloc(void*, size_t);
libc_hidden_proto (__libc_realloc)
void *__libc_realloc_core (void *, size_t);
libc_hidden_proto (__libc_realloc_core)
/*
memalign(size_t alignment, size_t n);
@@ -635,16 +542,16 @@ libc_hidden_proto (__libc_realloc)
Overreliance on memalign is a sure way to fragment space.
*/
void* __libc_memalign(size_t, size_t);
libc_hidden_proto (__libc_memalign)
void *__libc_memalign_core (size_t, size_t);
libc_hidden_proto (__libc_memalign_core)
/*
valloc(size_t n);
Equivalent to memalign(pagesize, n), where pagesize is the page
size of the system. If the pagesize is unknown, 4096 is used.
*/
void* __libc_valloc(size_t);
void *__libc_valloc_core (size_t);
libc_hidden_proto (__libc_valloc_core)
/*
@@ -677,7 +584,8 @@ struct mallinfo __libc_mallinfo(void);
Equivalent to valloc(minimum-page-that-holds(n)), that is,
round up n to nearest pagesize.
*/
void* __libc_pvalloc(size_t);
void *__libc_pvalloc_core (size_t);
libc_hidden_proto (__libc_pvalloc_core)
/*
malloc_trim(size_t pad);
@@ -720,7 +628,8 @@ int __malloc_trim(size_t);
assert(malloc_usable_size(p) >= 256);
*/
size_t __malloc_usable_size(void*);
size_t __malloc_usable_size_core (void *);
libc_hidden_proto (__malloc_usable_size_core)
/*
malloc_stats();
@@ -749,7 +658,8 @@ void __malloc_stats(void);
POSIX wrapper like memalign(), checking for validity of size.
*/
int __posix_memalign(void **, size_t, size_t);
int __posix_memalign_core (void **, size_t, size_t);
libc_hidden_proto (__posix_memalign_core)
#endif /* IS_IN (libc) */
/*
@@ -1017,12 +927,10 @@ libc_hidden_proto (__libc_mallopt)
#define RETURN_ADDRESS(X_) (NULL)
#endif
/* Forward declarations. */
struct malloc_chunk;
typedef struct malloc_chunk* mchunkptr;
/* Internal routines. */
typedef struct malloc_chunk* mchunkptr;
static void* _int_malloc(mstate, size_t);
static void _int_free_chunk (mstate, mchunkptr, INTERNAL_SIZE_T, int);
static void _int_free_merge_chunk (mstate, mchunkptr, INTERNAL_SIZE_T);
@@ -1069,27 +977,7 @@ static size_t musable (void *mem);
/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
/*
malloc_chunk details:
malloc_chunk details (see malloc-chunk.h):
(The following includes lightly edited explanations by Colin Plumb.)
@@ -1195,137 +1083,35 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
---------- Size and alignment checks and conversions ----------
*/
/* Conversion from malloc headers to user pointers, and back. When
using memory tagging the user data and the malloc data structure
headers have distinct tags. Converting fully from one to the other
involves extracting the tag at the other address and creating a
suitable pointer using it. That can be quite expensive. There are
cases when the pointers are not dereferenced (for example only used
for alignment check) so the tags are not relevant, and there are
cases when user data is not tagged distinctly from malloc headers
(user data is untagged because tagging is done late in malloc and
early in free). User memory tagging across internal interfaces:
sysmalloc: Returns untagged memory.
_int_malloc: Returns untagged memory.
_int_memalign: Returns untagged memory.
_int_memalign: Returns untagged memory.
_mid_memalign: Returns tagged memory.
_int_realloc: Takes and returns tagged memory.
*/
/* The chunk header is two SIZE_SZ elements, but this is used widely, so
we define it here for clarity later. */
#define CHUNK_HDR_SZ (2 * SIZE_SZ)
/* Convert a chunk address to a user mem pointer without correcting
the tag. */
/* Convert a chunk address to a user mem pointer. */
#define chunk2mem(p) ((void*)((char*)(p) + CHUNK_HDR_SZ))
/* Convert a chunk address to a user mem pointer and extract the right tag. */
#define chunk2mem_tag(p) ((void*)tag_at ((char*)(p) + CHUNK_HDR_SZ))
/* Convert a user mem pointer to a chunk address and extract the right tag. */
#define mem2chunk(mem) ((mchunkptr)tag_at (((char*)(mem) - CHUNK_HDR_SZ)))
/* The smallest possible chunk */
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
/* The smallest size we can malloc is an aligned minimal chunk */
#define MINSIZE \
(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
/* Convert a user mem pointer to a chunk address. */
#define mem2chunk(mem) ((mchunkptr) (((char*)(mem) - CHUNK_HDR_SZ)))
/* Check if m has acceptable alignment */
#define misaligned_mem(m) ((uintptr_t)(m) & MALLOC_ALIGN_MASK)
#define misaligned_chunk(p) (misaligned_mem( chunk2mem (p)))
/* pad request bytes into a usable size -- internal version */
/* Note: This must be a macro that evaluates to a compile time constant
if passed a literal constant. */
#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
/* Check if REQ overflows when padded and aligned and if the resulting
value is less than PTRDIFF_T. Returns the requested size or
MINSIZE in case the value is less than MINSIZE, or SIZE_MAX if any
of the previous checks fail. */
static __always_inline size_t
checked_request2size (size_t req) __nonnull (1)
{
if (__glibc_unlikely (req > PTRDIFF_MAX))
return SIZE_MAX;
/* When using tagged memory, we cannot share the end of the user
block with the header for the next chunk, so ensure that we
allocate blocks that are rounded up to the granule size. Take
care not to overflow from close to MAX_SIZE_T to a small
number. Ideally, this would be part of request2size(), but that
must be a macro that produces a compile time constant if passed
a constant literal. */
if (__glibc_unlikely (mtag_enabled))
{
/* Ensure this is not evaluated if !mtag_enabled, see gcc PR 99551. */
asm ("");
req = (req + (__MTAG_GRANULE_SIZE - 1)) &
~(size_t)(__MTAG_GRANULE_SIZE - 1);
}
return request2size (req);
}
#define misaligned_chunk(p) (misaligned_mem (chunk2mem (p)))
/*
--------------- Physical chunk operations ---------------
*/
/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1
/* extract inuse bit of previous chunk */
#define prev_inuse(p) ((p)->mchunk_size & PREV_INUSE)
/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2
/* check for mmap()'ed chunk */
#define chunk_is_mmapped(p) ((p)->mchunk_size & IS_MMAPPED)
/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
from a non-main arena. This is only set immediately before handing
the chunk to the user, if necessary. */
#define NON_MAIN_ARENA 0x4
/* Check for chunk from main arena. */
#define chunk_main_arena(p) (((p)->mchunk_size & NON_MAIN_ARENA) == 0)
/* Mark a chunk as not being on the main arena. */
#define set_non_main_arena(p) ((p)->mchunk_size |= NON_MAIN_ARENA)
/*
Bits to mask off when extracting size
Note: IS_MMAPPED is intentionally not masked off from size field in
macros for which mmapped chunks should never be seen. This should
cause helpful core dumps to occur if it is tried by accident by
people extending or adapting this malloc.
*/
#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))
/* Like chunksize, but do not mask SIZE_BITS. */
#define chunksize_nomask(p) ((p)->mchunk_size)
/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))
@@ -1376,30 +1162,6 @@ checked_request2size (size_t req) __nonnull (1)
#pragma GCC poison mchunk_size
#pragma GCC poison mchunk_prev_size
/* This is the size of the real usable data in the chunk. Not valid for
dumped heap chunks. */
#define memsize(p) \
(__MTAG_GRANULE_SIZE > SIZE_SZ && __glibc_unlikely (mtag_enabled) ? \
chunksize (p) - CHUNK_HDR_SZ : \
chunksize (p) - CHUNK_HDR_SZ + SIZE_SZ)
/* If memory tagging is enabled the layout changes to accommodate the granule
size, this is wasteful for small allocations so not done by default.
Both the chunk header and user data has to be granule aligned. */
_Static_assert (__MTAG_GRANULE_SIZE <= CHUNK_HDR_SZ,
"memory tagging is not supported with large granule.");
static __always_inline void *
tag_new_usable (void *ptr)
{
if (__glibc_unlikely (mtag_enabled) && ptr)
{
mchunkptr cp = mem2chunk(ptr);
ptr = __libc_mtag_tag_region (__libc_mtag_new_tag (ptr), memsize (cp));
}
return ptr;
}
/* Huge page used for an mmap chunk. */
#define MMAP_HP 0x1
@@ -2233,7 +1995,7 @@ sysmalloc_mmap (INTERNAL_SIZE_T nb, size_t pagesize, int extra_flags)
size_t size = ALIGN_UP (nb + padding + CHUNK_HDR_SZ, pagesize);
char *mm = (char *) MMAP (NULL, size,
mtag_mmap_flags | PROT_READ | PROT_WRITE,
extra_mmap_prot | PROT_READ | PROT_WRITE,
extra_flags);
if (mm == MAP_FAILED)
return mm;
@@ -2274,7 +2036,7 @@ sysmalloc_mmap_fallback (size_t *s, size_t size, size_t minsize,
size = minsize;
char *mbrk = (char *) (MMAP (NULL, size,
mtag_mmap_flags | PROT_READ | PROT_WRITE,
extra_mmap_prot | PROT_READ | PROT_WRITE,
extra_flags));
if (mbrk == MAP_FAILED)
return MAP_FAILED;
@@ -2951,7 +2713,7 @@ tcache_key_initialize (void)
/* We need tcache_key to be non-zero (otherwise tcache_double_free_verify's
clearing of e->key would go unnoticed and it would loop getting called
through __libc_free), and we want tcache_key not to be a
through __libc_free_core), and we want tcache_key not to be a
commonly-occurring value in memory, so ensure a minimum amount of one and
zero bits. */
int minimum_bits = __WORDSIZE / 4;
@@ -2983,7 +2745,7 @@ tcache_put_n (mchunkptr chunk, size_t tc_idx, tcache_entry **ep, bool mangled)
{
tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
/* Mark this chunk as "in the tcache" so the test in __libc_free will
/* Mark this chunk as "in the tcache" so the test in __libc_free_core will
detect a double free. */
e->key = tcache_key;
@@ -3111,7 +2873,7 @@ tcache_get_align (size_t nb, size_t alignment)
if (te != NULL
&& csize == nb
&& PTR_IS_ALIGNED (te, alignment))
return tag_new_usable (tcache_get_n (tc_idx, tep, mangled));
return tcache_get_n (tc_idx, tep, mangled);
}
return NULL;
}
@@ -3142,7 +2904,7 @@ tcache_double_free_verify (tcache_entry *e)
or user data that happens to match the key. Since we are not sure,
clear the key and retry freeing it. */
e->key = 0;
__libc_free (e);
__libc_free_core (e);
}
static void
@@ -3229,7 +2991,7 @@ __libc_malloc2 (size_t bytes)
if (SINGLE_THREAD_P)
{
victim = tag_new_usable (_int_malloc (&main_arena, bytes));
victim = _int_malloc (&main_arena, bytes);
assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
&main_arena == arena_for_chunk (mem2chunk (victim)));
return victim;
@@ -3250,15 +3012,13 @@ __libc_malloc2 (size_t bytes)
if (ar_ptr != NULL)
__libc_lock_unlock (ar_ptr->mutex);
victim = tag_new_usable (victim);
assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
ar_ptr == arena_for_chunk (mem2chunk (victim)));
return victim;
}
void *
__libc_malloc (size_t bytes)
__libc_malloc_core (size_t bytes)
{
#if USE_TCACHE
size_t nb = checked_request2size (bytes);
@@ -3270,47 +3030,39 @@ __libc_malloc (size_t bytes)
if (__glibc_likely (tc_idx < TCACHE_SMALL_BINS))
{
if (tcache->entries[tc_idx] != NULL)
return tag_new_usable (tcache_get (tc_idx));
return tcache_get (tc_idx);
}
else
{
tc_idx = large_csize2tidx (nb);
void *victim = tcache_get_large (tc_idx, nb);
if (victim != NULL)
return tag_new_usable (victim);
return victim;
}
}
#endif
return __libc_malloc2 (bytes);
}
libc_hidden_def (__libc_malloc)
libc_hidden_def (__libc_malloc_core)
static void __attribute_noinline__
tcache_free_init (void *mem)
{
tcache_init (NULL);
__libc_free (mem);
__libc_free_core (mem);
}
void
__libc_free (void *mem)
__libc_free_core (void *mem)
{
mchunkptr p; /* chunk corresponding to mem */
if (mem == NULL) /* free(0) has no effect */
return;
/* Quickly check that the freed pointer matches the tag for the memory.
This gives a useful double-free detection. */
if (__glibc_unlikely (mtag_enabled))
*(volatile char *)mem;
p = mem2chunk (mem);
/* Mark the chunk as belonging to the library again. */
tag_region (chunk2mem (p), memsize (p));
INTERNAL_SIZE_T size = chunksize (p);
if (__glibc_unlikely (misaligned_chunk (p)))
@@ -3352,10 +3104,10 @@ __libc_free (void *mem)
_int_free_chunk (arena_for_chunk (p), p, size, 0);
}
libc_hidden_def (__libc_free)
libc_hidden_def (__libc_free_core)
void *
__libc_realloc (void *oldmem, size_t bytes)
__libc_realloc_core (void *oldmem, size_t bytes)
{
mstate ar_ptr;
INTERNAL_SIZE_T nb; /* padded request size */
@@ -3364,20 +3116,15 @@ __libc_realloc (void *oldmem, size_t bytes)
/* realloc of null is supposed to be same as malloc */
if (oldmem == NULL)
return __libc_malloc (bytes);
return __libc_malloc_core (bytes);
#if REALLOC_ZERO_BYTES_FREES
if (bytes == 0)
{
__libc_free (oldmem); return NULL;
__libc_free_core (oldmem); return NULL;
}
#endif
/* Perform a quick check to ensure that the pointer's tag matches the
memory's tag. */
if (__glibc_unlikely (mtag_enabled))
*(volatile char*) oldmem;
/* chunk corresponding to oldmem */
const mchunkptr oldp = mem2chunk (oldmem);
@@ -3419,22 +3166,14 @@ __libc_realloc (void *oldmem, size_t bytes)
#if HAVE_MREMAP
newp = mremap_chunk (oldp, nb);
if (newp)
{
void *newmem = chunk2mem_tag (newp);
/* Give the new block a different tag. This helps to ensure
that stale handles to the previous mapping are not
reused. There's a performance hit for both us and the
caller for doing this, so we might want to
reconsider. */
return tag_new_usable (newmem);
}
return chunk2mem (newp);
#endif
/* Return if shrinking and mremap was unsuccessful. */
if (bytes <= usable)
return oldmem;
/* Must alloc, copy, free. */
newmem = __libc_malloc (bytes);
newmem = __libc_malloc_core (bytes);
if (newmem == NULL)
return NULL; /* propagate failure */
@@ -3466,22 +3205,21 @@ __libc_realloc (void *oldmem, size_t bytes)
{
/* Try harder to allocate memory in other arenas. */
LIBC_PROBE (memory_realloc_retry, 2, bytes, oldmem);
newp = __libc_malloc (bytes);
newp = __libc_malloc_core (bytes);
if (newp != NULL)
{
size_t sz = memsize (oldp);
memcpy (newp, oldmem, sz);
(void) tag_region (chunk2mem (oldp), sz);
_int_free_chunk (ar_ptr, oldp, chunksize (oldp), 0);
_int_free_chunk (ar_ptr, oldp, chunksize (oldp), 0);
}
}
return newp;
}
libc_hidden_def (__libc_realloc)
libc_hidden_def (__libc_realloc_core)
void *
__libc_memalign (size_t alignment, size_t bytes)
__libc_memalign_core (size_t alignment, size_t bytes)
{
/* Round the alignment up to a power of 2. Reject alignments that overflow
when rounded up. Zero alignment is handled by _mid_memalign. */
@@ -3498,12 +3236,14 @@ __libc_memalign (size_t alignment, size_t bytes)
return _mid_memalign (alignment, bytes);
}
libc_hidden_def (__libc_memalign)
libc_hidden_def (__libc_memalign_core)
/* For ISO C17. */
void *__aligned_alloc_core (size_t, size_t);
libc_hidden_proto (__aligned_alloc_core)
void *
weak_function
aligned_alloc (size_t alignment, size_t bytes)
__aligned_alloc_core (size_t alignment, size_t bytes)
{
/* Starting with ISO C17 the standard requires an error for alignments
that are not supported. Only integral powers of 2 are valid. */
@@ -3515,11 +3255,14 @@ aligned_alloc (size_t alignment, size_t bytes)
return _mid_memalign (alignment, bytes);
}
libc_hidden_def (__aligned_alloc_core)
/* For ISO C23. */
void __free_sized_core (void *, size_t);
libc_hidden_proto (__free_sized_core)
void
weak_function
free_sized (void *ptr, __attribute_maybe_unused__ size_t size)
__free_sized_core (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
@@ -3528,11 +3271,14 @@ free_sized (void *ptr, __attribute_maybe_unused__ size_t size)
free (ptr);
}
libc_hidden_def (__free_sized_core)
/* For ISO C23. */
void __free_aligned_sized_core (void *, size_t, size_t);
libc_hidden_proto (__free_aligned_sized_core)
void
weak_function
free_aligned_sized (void *ptr, __attribute_maybe_unused__ size_t alignment,
__free_aligned_sized_core (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
@@ -3542,6 +3288,7 @@ free_aligned_sized (void *ptr, __attribute_maybe_unused__ size_t alignment,
free (ptr);
}
libc_hidden_def (__free_aligned_sized_core)
static void *
_mid_memalign (size_t alignment, size_t bytes)
@@ -3551,12 +3298,12 @@ _mid_memalign (size_t alignment, size_t bytes)
/* If we need less alignment than we give anyway, just relay to malloc. */
if (alignment <= MALLOC_ALIGNMENT)
return __libc_malloc (bytes);
return __libc_malloc_core (bytes);
#if USE_TCACHE
void *victim = tcache_get_align (checked_request2size (bytes), alignment);
if (victim != NULL)
return tag_new_usable (victim);
return victim;
#endif
if (SINGLE_THREAD_P)
@@ -3564,7 +3311,7 @@ _mid_memalign (size_t alignment, size_t bytes)
p = _int_memalign (&main_arena, alignment, bytes);
assert (!p || chunk_is_mmapped (mem2chunk (p)) ||
&main_arena == arena_for_chunk (mem2chunk (p)));
return tag_new_usable (p);
return p;
}
arena_get (ar_ptr, bytes + alignment + MINSIZE);
@@ -3582,17 +3329,18 @@ _mid_memalign (size_t alignment, size_t bytes)
assert (!p || chunk_is_mmapped (mem2chunk (p)) ||
ar_ptr == arena_for_chunk (mem2chunk (p)));
return tag_new_usable (p);
return p;
}
void *
__libc_valloc (size_t bytes)
__libc_valloc_core (size_t bytes)
{
return _mid_memalign (GLRO (dl_pagesize), bytes);
}
libc_hidden_def (__libc_valloc_core)
void *
__libc_pvalloc (size_t bytes)
__libc_pvalloc_core (size_t bytes)
{
size_t pagesize = GLRO (dl_pagesize);
size_t rounded_bytes;
@@ -3607,6 +3355,7 @@ __libc_pvalloc (size_t bytes)
return _mid_memalign (pagesize, rounded_bytes & -pagesize);
}
libc_hidden_def (__libc_pvalloc_core)
static void * __attribute_noinline__
__libc_calloc2 (size_t sz)
@@ -3673,12 +3422,6 @@ __libc_calloc2 (size_t sz)
p = mem2chunk (mem);
/* If we are using memory tagging, then we need to set the tags
regardless of MORECORE_CLEARS, so we zero the whole block while
doing so. */
if (__glibc_unlikely (mtag_enabled))
return tag_new_zero_region (mem, memsize (p));
csz = chunksize (p);
/* Two optional cases in which clearing not necessary */
@@ -3703,7 +3446,7 @@ __libc_calloc2 (size_t sz)
}
void *
__libc_calloc (size_t n, size_t elem_size)
__libc_calloc_core (size_t n, size_t elem_size)
{
size_t bytes;
@@ -3725,9 +3468,6 @@ __libc_calloc (size_t n, size_t elem_size)
if (tcache->entries[tc_idx] != NULL)
{
void *mem = tcache_get (tc_idx);
if (__glibc_unlikely (mtag_enabled))
return tag_new_zero_region (mem, memsize (mem2chunk (mem)));
return clear_memory ((INTERNAL_SIZE_T *) mem, tidx2usize (tc_idx));
}
}
@@ -3737,9 +3477,6 @@ __libc_calloc (size_t n, size_t elem_size)
void *mem = tcache_get_large (tc_idx, nb);
if (mem != NULL)
{
if (__glibc_unlikely (mtag_enabled))
return tag_new_zero_region (mem, memsize (mem2chunk (mem)));
return memset (mem, 0, memsize (mem2chunk (mem)));
}
}
@@ -3747,6 +3484,7 @@ __libc_calloc (size_t n, size_t elem_size)
#endif
return __libc_calloc2 (bytes);
}
libc_hidden_def (__libc_calloc_core)
#endif /* IS_IN (libc) */
/*
@@ -4511,7 +4249,7 @@ _int_realloc (mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
av->top = chunk_at_offset (oldp, nb);
set_head (av->top, (newsize - nb) | PREV_INUSE);
check_inuse_chunk (av, oldp);
return tag_new_usable (chunk2mem (oldp));
return chunk2mem (oldp);
}
/* Try to expand forward into next chunk; split off remainder below */
@@ -4545,10 +4283,7 @@ _int_realloc (mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
else
{
void *oldmem = chunk2mem (oldp);
size_t sz = memsize (oldp);
(void) tag_region (oldmem, sz);
newmem = tag_new_usable (newmem);
memcpy (newmem, oldmem, sz);
memcpy (newmem, oldmem, memsize (oldp));
_int_free_chunk (av, oldp, chunksize (oldp), 1);
check_inuse_chunk (av, newp);
return newmem;
@@ -4570,8 +4305,6 @@ _int_realloc (mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
else /* split remainder */
{
remainder = chunk_at_offset (newp, nb);
/* Clear any user-space tags before writing the header. */
remainder = tag_region (remainder, remainder_size);
set_head_size (newp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));
@@ -4581,7 +4314,7 @@ _int_realloc (mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
}
check_inuse_chunk (av, newp);
return tag_new_usable (chunk2mem (newp));
return chunk2mem (newp);
}
/*
@@ -4754,12 +4487,13 @@ musable (void *mem)
#if IS_IN (libc)
size_t
__malloc_usable_size (void *m)
__malloc_usable_size_core (void *m)
{
if (m == NULL)
return 0;
return musable (m);
}
libc_hidden_def (__malloc_usable_size_core)
#endif
/*
@@ -5280,7 +5014,7 @@ malloc_printerr_tail (const char *str)
#if IS_IN (libc)
/* We need a wrapper function for one of the additions of POSIX. */
int
__posix_memalign (void **memptr, size_t alignment, size_t size)
__posix_memalign_core (void **memptr, size_t alignment, size_t size)
{
/* Test whether the SIZE argument is valid. It must be a power of
two multiple of sizeof (void *) (which must be either 4 or 8). */
@@ -5295,7 +5029,7 @@ __posix_memalign (void **memptr, size_t alignment, size_t size)
*memptr = mem;
return 0;
}
weak_alias (__posix_memalign, posix_memalign)
libc_hidden_def (__posix_memalign_core)
#endif
@@ -5458,26 +5192,44 @@ __malloc_info (int options, FILE *fp)
}
#if IS_IN (libc)
weak_alias (__malloc_info, malloc_info)
weak_alias (__libc_calloc, calloc)
strong_alias (__libc_free, free)
strong_alias (__libc_malloc, malloc)
weak_alias (__libc_memalign, memalign)
strong_alias (__libc_realloc, realloc)
weak_alias (__libc_valloc, valloc)
weak_alias (__libc_pvalloc, pvalloc)
weak_alias (__libc_mallinfo, mallinfo)
weak_alias (__libc_mallinfo2, mallinfo2)
weak_alias (__libc_mallopt, mallopt)
weak_alias (__malloc_stats, malloc_stats)
weak_alias (__malloc_usable_size, malloc_usable_size)
weak_alias (__malloc_trim, malloc_trim)
#endif
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)
compat_symbol (libc, __libc_free, cfree, GLIBC_2_0);
#endif
/* On targets that do not support ifuncs we alias externally visible
functions as well as the __libc_* functions to their respective
*_core symbols.
For ifunc prototypes and resolvers see sysdeps/generic/malloc-ifuncs.h.
*/
# if !HAVE_IFUNC
strong_alias (__libc_malloc_core, malloc)
strong_alias (__libc_malloc_core, __libc_malloc)
weak_alias (__libc_calloc_core, calloc)
strong_alias (__libc_calloc_core, __libc_calloc)
weak_alias (__libc_memalign_core, memalign)
strong_alias (__libc_memalign_core, __libc_memalign)
weak_alias (__libc_valloc_core, valloc)
strong_alias (__libc_valloc_core, __libc_valloc)
weak_alias (__libc_pvalloc_core, pvalloc)
strong_alias (__libc_pvalloc_core, __libc_pvalloc)
strong_alias (__libc_realloc_core, realloc)
strong_alias (__libc_realloc_core, __libc_realloc)
weak_alias (__posix_memalign_core, posix_memalign)
# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)
compat_symbol (libc, __libc_free_core, cfree, GLIBC_2_0);
# endif
strong_alias (__libc_free_core, free)
strong_alias (__libc_free_core, __libc_free)
weak_alias (__malloc_usable_size_core, malloc_usable_size)
weak_alias (__aligned_alloc_core, aligned_alloc)
weak_alias (__free_sized_core, free_sized)
weak_alias (__free_aligned_sized_core, free_aligned_sized)
# endif /* !HAVE_IFUNC */
#endif /* IS_IN (libc) */
/* ------------------------------------------------------------
History:
+53
View File
@@ -0,0 +1,53 @@
#!/bin/sh
# This script checks that malloc.c does not use any external API
# functions.
# It is important that whenever an internal function needs to used e.g.
# __libc_foo() it actually uses __libc_foo_core() symbol. If it uses a
# non-_core symbol, the returned result may not be suitable for the
# subsequent use of it internally.
# The non-_core malloc functions return and accept user-pointers which
# are different from the internal-pointers that are used by the _core
# functions.
# Copyright (C) 2026 Free Software Foundation, Inc.
# 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; if not, see
# <https://www.gnu.org/licenses/>.
# This script accepts list of source files that need to be grep-ed for
# certain symbols and the result should be no matches except for the
# alias declarations
status=0
for src; do
echo "checking $src..."
for fun in \
__libc_malloc \
__libc_calloc \
__libc_memalign \
__libc_valloc \
__libc_pvalloc \
__libc_realloc \
__libc_free; do
grep -nw "$fun" $src | egrep -vw "^[0-9]+:(strong_alias|weak_alias)" && {
good=$fun"_core"
echo "error: code in $src should not use '$fun' (use '$good' instead)"
status=1
}
done
done
exit $status
+5 -1
View File
@@ -20,6 +20,7 @@
#include <stdlib.h>
#include <support/support.h>
#include <support/test-pointer.h>
#include <libc-diag.h>
#include "tst-malloc-aux.h"
@@ -37,7 +38,10 @@ call_free (void *ptr)
by a prior call to free(). */
DIAG_IGNORE_NEEDS_COMMENT (12, "-Wuse-after-free");
#endif
*(size_t *)(ptr - sizeof (size_t)) = 1;
/* We attempt to write to the chunk header thus corrupting memory.
If memory tagging is used, we need to make sure that tag in ptr
is cleared. */
*(size_t *)(support_ptr_after_free (ptr) - sizeof (size_t)) = 1;
#if __GNUC_PREREQ (12, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
+1 -1
View File
@@ -19,7 +19,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <malloc-size.h>
#include <malloc-alignment.h>
#include <support/check.h>
static void *
+1 -1
View File
@@ -23,7 +23,7 @@
#include <unistd.h>
#include <array_length.h>
#include <libc-pointer-arith.h>
#include <support/address-diff.h>
#include <support/test-pointer.h>
#include <support/check.h>
#include "tst-malloc-aux.h"
+1 -1
View File
@@ -24,7 +24,7 @@
#include <unistd.h>
#include <array_length.h>
#include <libc-pointer-arith.h>
#include <support/address-diff.h>
#include <support/test-pointer.h>
#include <support/check.h>
#include <support/xthread.h>
#include "tst-malloc-aux.h"
+1 -1
View File
@@ -22,7 +22,7 @@
#include <string.h>
#include <libc-diag.h>
#include <support/check.h>
#include <support/address-diff.h>
#include <support/test-pointer.h>
#include "tst-malloc-aux.h"
+5 -1
View File
@@ -25,6 +25,7 @@
#include <stdbool.h>
#include <support/capture_subprocess.h>
#include <support/check.h>
#include <support/test-pointer.h>
#include "tst-malloc-aux.h"
@@ -93,7 +94,10 @@ test_tcache (void *closure)
free (b);
free (c);
/* Corrupt the pointer with a random value, and avoid optimizations. */
/* Corrupt the pointer with a random value, and avoid optimizations.
If memory tagging is used, we need to make sure that tag in ptr
is cleared. */
c = support_ptr_after_free (c);
printf ("Before: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
memset (c, mask & 0xFF, size);
printf ("After: c=%p, c[0]=%p\n", c, ((void **)c)[0]);
+6
View File
@@ -19,6 +19,8 @@
#include <malloc.h>
#include <string.h>
#include <support/test-pointer.h>
static int
do_test (void)
{
@@ -36,6 +38,10 @@ do_test (void)
free (a); // puts in tcache
/* If memory tagging is used, we need to make sure that tag in ptr
is cleared. */
a = support_ptr_after_free (a);
/* A is now free and contains the key we use to detect in-tcache.
Copy the key to the other chunks. */
memcpy (b, a, 32);
-13
View File
@@ -185,19 +185,6 @@ non CET enabled shared library in CET enabled application.
NOTE: @option{--enable-cet} is only supported on x86_64 and x32.
@item --enable-memory-tagging
Enable memory tagging support if the architecture supports it. When
@theglibc{} is built with this option then the resulting library will
be able to control the use of tagged memory when hardware support is
present by use of the tunable @samp{glibc.mem.tagging}. This includes
the generation of tagged memory when using the @code{malloc} APIs.
At present only AArch64 platforms with MTE provide this functionality,
although the library will still operate (without memory tagging) on
older versions of the architecture.
The default is to disable support for memory tagging.
@item --disable-profile
Don't build libraries with profiling information. You may want to use
this option if you don't plan to do profiling.
+15 -29
View File
@@ -40,7 +40,6 @@ glibc.malloc.perturb: 0 (min: 0, max: 255)
glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
glibc.pthread.rseq: 1 (min: 0, max: 1)
glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
glibc.mem.tagging: 0 (min: 0, max: 255)
glibc.malloc.hugetlb: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.cpu.x86_rep_movsb_threshold: 0x2000 (min: 0x100, max: 0xffffffffffffffff)
glibc.malloc.mxfast: 0x0 (min: 0x0, max: 0xffffffffffffffff)
@@ -666,34 +665,6 @@ This tunable namespace supports operations that affect the way @theglibc{}
and the process manage memory.
@end deftp
@deftp Tunable glibc.mem.tagging
If the hardware supports memory tagging, this tunable can be used to
control the way @theglibc{} uses this feature. At present this is only
supported on AArch64 systems with the MTE extension; it is ignored for
all other systems.
This tunable takes a value between 0 and 255 and acts as a bitmask
that enables various capabilities.
Bit 0 (the least significant bit) causes the @code{malloc}
subsystem to allocate
tagged memory, with each allocation being assigned a random tag.
Bit 1 enables precise faulting mode for tag violations on systems that
support deferred tag violation reporting. This may cause programs
to run more slowly.
Bit 2 enables either precise or deferred faulting mode for tag violations
whichever is preferred by the system.
Other bits are currently reserved.
@Theglibc{} startup code will automatically enable memory tagging
support in the kernel if this tunable has any non-zero value.
The default value is @samp{0}, which disables all memory tagging.
@end deftp
@deftp Tunable glibc.mem.decorate_maps
If the kernel supports naming anonymous virtual memory areas (since
Linux version 5.17, although not always enabled by some kernel
@@ -711,6 +682,21 @@ This tunable takes a value of 0 and 1, where 1 enables the feature.
The default value is @samp{0}, which disables the decoration.
@end deftp
@deftp Tunable glibc.mem.aarch64_mte
On AArch64 systems that support the Memory Tagging Extension (MTE) extension
this tunable allows to select the tag check fault mode (MTE mode).
Available values are:
@itemize @bullet
@item @code{none}: (the default), memory tagging is disabled.
@item @code{auto}: enable CPU preferred tag checking mode.
@item @code{sync}: enable synchronous tag check fault mode.
@item @code{async}: enable asynchronous tag check fault mode.
@end itemize
@end deftp
@node gmon Tunables
@section gmon Tunables
@cindex gmon tunables
@@ -1,4 +1,4 @@
/* Support functions for pointer arithmetic.
/* Support functions for tests that check pointers.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
@@ -16,12 +16,16 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _POINTER_ARITH_H
#define _POINTER_ARITH_H 1
#ifndef _SUPPORT_TEST_POINTER_H
#define _SUPPORT_TEST_POINTER_H 1
#include <stddef.h>
/* Returns difference in bytes between addresses of two pointers. */
ptrdiff_t support_address_diff (const void *lhs, const void *rhs);
#endif /* _POINTER_ARITH_H */
/* Returns pointer suitable for tests that rely on use-after-free
behaviour. */
void *support_ptr_after_free (void *ptr);
#endif /* _SUPPORT_TEST_POINTER_H */
+8 -6
View File
@@ -4,6 +4,7 @@ ifeq ($(subdir),elf)
sysdep-dl-routines += \
dl-bti \
dl-gcs \
dl-mte \
# sysdep-dl-routines
tests += \
@@ -82,8 +83,8 @@ sysdep_headers += \
sysdep_routines += \
__alloc_gcs \
__arm_za_disable \
__mtag_tag_region \
__mtag_tag_zero_region \
__mte_tag_region \
__mte_tag_region_zero \
# sysdep_routines
tests += \
@@ -102,14 +103,15 @@ $(objpfx)tst-sme-clone3: $(objpfx)clone3.o $(objpfx)__arm_za_disable.o
endif
ifeq ($(subdir),malloc)
sysdep_malloc_debug_routines = \
__mtag_tag_region \
__mtag_tag_zero_region \
# sysdep_malloc_debug_routines
sysdep_routines += \
malloc-ifuncs \
malloc-mte \
# sysdep_routines
endif # malloc directory
ifeq ($(subdir),support)
libsupport-sysdep_routines += \
support-address-diff \
support-use-after-free \
# libsupport-sysdep_routines
endif
@@ -18,8 +18,6 @@
#include <sysdep.h>
#ifdef USE_MTAG
/* Assumptions:
*
* ARMv8-a, AArch64, MTE, LP64 ABI.
@@ -39,7 +37,7 @@
#define tmp x4
#define zva_val x4
ENTRY (__libc_mtag_tag_region)
ENTRY (__mte_tag_region)
add dstend, dstin, count
cmp count, 96
@@ -106,5 +104,4 @@ L(no_zva_loop):
st2g dstin, [dstend, -32]
ret
END (__libc_mtag_tag_region)
#endif /* USE_MTAG */
END (__mte_tag_region)
@@ -18,8 +18,6 @@
#include <sysdep.h>
#ifdef USE_MTAG
/* Assumptions:
*
* ARMv8-a, AArch64, MTE, LP64 ABI.
@@ -39,7 +37,7 @@
#define tmp x4
#define zva_val x4
ENTRY (__libc_mtag_tag_zero_region)
ENTRY (__mte_tag_region_zero)
add dstend, dstin, count
cmp count, 96
@@ -106,5 +104,4 @@ L(no_zva_loop):
stz2g dstin, [dstend, -32]
ret
END (__libc_mtag_tag_zero_region)
#endif /* USE_MTAG */
END (__mte_tag_region_zero)
@@ -1,4 +1,4 @@
/* libc-internal interface for tagged (colored) memory support.
/* AArch64 MTE (Memory Tagging Extension) declarations.
Copyright (C) 2020-2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
@@ -16,54 +16,49 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef _AARCH64_LIBC_MTAG_H
#define _AARCH64_LIBC_MTAG_H 1
#ifndef _AARCH64_MTE_H
#define _AARCH64_MTE_H 1
#ifndef USE_MTAG
/* Generic bindings for systems that do not support memory tagging. */
#include_next "libc-mtag.h"
#else
#include <stddef.h>
#include <stdint.h>
#include <sys/cdefs.h>
/* Used to ensure additional alignment when objects need to have distinct
tags. */
#define __MTAG_GRANULE_SIZE 16
/* Non-zero if memory obtained via morecore (sbrk) is not tagged. */
#define __MTAG_SBRK_UNTAGGED 1
/* Extra flags to pass to mmap to get tagged pages. */
#define __MTAG_MMAP_FLAGS PROT_MTE
/* Set the tags for a region of memory, which must have size and alignment
that are multiples of __MTAG_GRANULE_SIZE. Size cannot be zero. */
void *__libc_mtag_tag_region (void *, size_t);
/* Optimized equivalent to __libc_mtag_tag_region followed by memset to 0. */
void *__libc_mtag_tag_zero_region (void *, size_t);
/* Convert address P to a pointer that is tagged correctly for that
location. */
static __always_inline void *
__libc_mtag_address_get_tag (void *p)
/* Assign a new (random) tag to a pointer P (does not adjust the
allocation tag on the memory addressed). */
static __always_inline __attribute_maybe_unused__ void *
__mte_new_tag (void *p)
{
register void *x0 asm ("x0") = p;
asm (".inst 0xd9600000 /* ldg x0, [x0] */" : "+r" (x0));
return x0;
}
/* Assign a new (random) tag to a pointer P (does not adjust the tag on
the memory addressed). */
static __always_inline void *
__libc_mtag_new_tag (void *p)
{
register void *x0 asm ("x0") = p;
register unsigned long x1 asm ("x1");
register uintptr_t x1 asm ("x1");
/* Guarantee that the new tag is not the same as now. */
asm (".inst 0x9adf1401 /* gmi x1, x0, xzr */\n"
".inst 0x9ac11000 /* irg x0, x0, x1 */" : "+r" (x0), "=r" (x1));
return x0;
}
#endif /* USE_MTAG */
/* Clears logical tag in the input pointer. */
static __always_inline __attribute_maybe_unused__ void *
__mte_clear_tag (void *p)
{
return (void *)((uintptr_t)p & ~(0xfull << 56ull));
}
#endif /* _AARCH64_LIBC_MTAG_H */
/* Convert address P to a pointer that is tagged correctly for that
location (logical tag in the returned pointer will be the same
as the allocation tag in the addressed memory). */
static __always_inline __attribute_maybe_unused__ void *
__mte_get_tag (void *p)
{
register void *x0 asm ("x0") = p;
asm (".inst 0xd9600000 /* ldg x0, [x0] */" : "+r" (x0));
return x0;
}
/* Set the tags for a region of memory, which must have size and alignment
that are multiples of MTE_GRANULE_SIZE. Size cannot be zero. */
void *__mte_tag_region (void *, size_t);
/* Optimized equivalent to __mte_tag_region followed by memset to 0. */
void *__mte_tag_region_zero (void *, size_t);
#endif /* _AARCH64_MTE_H */
+8 -2
View File
@@ -59,13 +59,19 @@ enum {
BTI_CHECK_ENFORCED = 1,
};
enum {
MTE_TUNABLE_NONE = 0,
MTE_TUNABLE_AUTO = 1,
MTE_TUNABLE_SYNC = 2,
MTE_TUNABLE_ASYNC = 3,
};
struct cpu_features
{
uint64_t midr_el1;
unsigned zva_size;
bool bti;
/* Currently, the GLIBC memory tagging tunable only defines 8 bits. */
uint8_t mte_state;
uint8_t reserved;
bool sve;
bool unused;
bool mops;
-2
View File
@@ -45,8 +45,6 @@ _dl_diagnostics_cpu (void)
print_cpu_features_value ("midr_el1",
GLRO (dl_aarch64_cpu_features).midr_el1);
print_cpu_features_value ("mops", GLRO (dl_aarch64_cpu_features).mops);
print_cpu_features_value ("mte_state",
GLRO (dl_aarch64_cpu_features).mte_state);
print_cpu_features_value ("sve", GLRO (dl_aarch64_cpu_features).sve);
print_cpu_features_value ("zva_size",
GLRO (dl_aarch64_cpu_features).zva_size);
+65
View File
@@ -0,0 +1,65 @@
/* AArch64 implementation for MTE (memory tagging).
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#include <sys/cdefs.h>
#include <ldsodefs.h>
/* For the prctl syscall. */
#define PR_SET_TAGGED_ADDR_CTRL 55
#define PR_MTE_TAG_SHIFT 3
#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
#define PR_MTE_TCF_SYNC (1UL << 1)
#define PR_MTE_TCF_ASYNC (1UL << 2)
/* The maximal set of permitted tags that the MTE random tag generation
instruction may use. We exclude tag 0 because a) we want to reserve
that for the libc heap structures and b) because it makes it easier
to see when pointer have been correctly tagged. */
#define MTE_ALLOWED_TAGS (0xfffe << PR_MTE_TAG_SHIFT)
void __mte_init (void);
rtld_hidden_proto (__mte_init)
void __mte_init (void)
{
int mode = GL (dl_aarch64_mte);
if (mode == MTE_TUNABLE_NONE)
return;
uint64_t flags = PR_TAGGED_ADDR_ENABLE | MTE_ALLOWED_TAGS;
switch (mode)
{
case MTE_TUNABLE_AUTO:
flags |= PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC;
break;
case MTE_TUNABLE_SYNC:
flags |= PR_MTE_TCF_SYNC;
break;
case MTE_TUNABLE_ASYNC:
flags |= PR_MTE_TCF_ASYNC;
break;
default:
_dl_fatal_printf ("unknown MTE mode: %d\n", mode);
__builtin_unreachable ();
}
/* We use inline system call to avoid unnecessary dependency
on the sys/prctl.h header. */
int r = INLINE_SYSCALL_CALL (prctl, PR_SET_TAGGED_ADDR_CTRL, flags, 0, 0, 0);
if (r == -1)
_dl_fatal_printf ("failed to enable MTE\n");
}
rtld_hidden_def (__mte_init)
+2
View File
@@ -66,6 +66,8 @@ ENTRY (_start)
cbnz w0, L(failed_gcs_lock)
L(skip_gcs_enable):
bl HIDDEN_JUMPTARGET(__mte_init)
.globl _dl_start_user
.type _dl_start_user, %function
_dl_start_user:
+5
View File
@@ -34,4 +34,9 @@ glibc {
default: 0
}
}
mem {
aarch64_mte {
type: STRING
}
}
}
+142
View File
@@ -0,0 +1,142 @@
/* Code for ifunc resolvers for malloc: aarch64 version.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#if IS_IN (libc)
#include <malloc/malloc-internal.h>
#include <malloc-ifuncs.h>
#include <ldsodefs.h>
#include "malloc-mte.h"
#define MTE_ENABLED (GL (dl_aarch64_mte) != MTE_TUNABLE_NONE)
/* AArch64-specific resolvers for malloc ifuncs. */
IFUNC_PROTO (__libc_malloc);
IFUNC_RESOLVER (__libc_malloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __libc_malloc_mte;
return __libc_malloc_core;
}
strong_alias (__libc_malloc, malloc)
IFUNC_PROTO (__libc_calloc);
IFUNC_RESOLVER (__libc_calloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __libc_calloc_mte;
return __libc_calloc_core;
}
weak_alias (__libc_calloc, calloc)
IFUNC_PROTO (__libc_memalign);
IFUNC_RESOLVER (__libc_memalign, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __libc_memalign_mte;
return __libc_memalign_core;
}
weak_alias (__libc_memalign, memalign)
IFUNC_PROTO (__libc_valloc);
IFUNC_RESOLVER (__libc_valloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __libc_valloc_mte;
return __libc_valloc_core;
}
weak_alias (__libc_valloc, valloc)
IFUNC_PROTO (__libc_pvalloc);
IFUNC_RESOLVER (__libc_pvalloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __libc_pvalloc_mte;
return __libc_pvalloc_core;
}
weak_alias (__libc_pvalloc, pvalloc)
IFUNC_PROTO (__libc_realloc);
IFUNC_RESOLVER (__libc_realloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __libc_realloc_mte;
return __libc_realloc_core;
}
strong_alias (__libc_realloc, realloc)
IFUNC_PROTO (__libc_free);
IFUNC_RESOLVER (__libc_free, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __libc_free_mte;
return __libc_free_core;
}
# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)
compat_symbol (libc, __libc_free, cfree, GLIBC_2_0);
# endif
strong_alias (__libc_free, free)
IFUNC_PROTO (__malloc_usable_size);
IFUNC_RESOLVER (__malloc_usable_size, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __malloc_usable_size_mte;
return __malloc_usable_size_core;
}
weak_alias (__malloc_usable_size, malloc_usable_size)
IFUNC_PROTO (__posix_memalign);
IFUNC_RESOLVER (__posix_memalign, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __posix_memalign_mte;
return __posix_memalign_core;
}
weak_alias (__posix_memalign, posix_memalign)
IFUNC_PROTO (__aligned_alloc);
IFUNC_RESOLVER (__aligned_alloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __aligned_alloc_mte;
return __aligned_alloc_core;
}
weak_alias (__aligned_alloc, aligned_alloc)
IFUNC_PROTO (__free_sized);
IFUNC_RESOLVER (__free_sized, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __free_sized_mte;
return __free_sized_core;
}
weak_alias (__free_sized, free_sized)
IFUNC_PROTO (__free_aligned_sized);
IFUNC_RESOLVER (__free_aligned_sized, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ENABLED)
return __free_aligned_sized_mte;
return __free_aligned_sized_core;
}
weak_alias (__free_aligned_sized, free_aligned_sized)
#endif /* IS_IN (libc) */
+29
View File
@@ -0,0 +1,29 @@
/* Definitions for malloc init: aarch64 version.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _AARCH64_MALLOC_INIT_H
#define _AARCH64_MALLOC_INIT_H
#define ARCH_INIT_MALLOC() \
if (GL (dl_aarch64_mte) != MTE_TUNABLE_NONE) \
{ \
extra_mmap_prot |= PROT_MTE; \
__always_fail_morecore = true; \
}
#endif /* _AARCH64_MALLOC_INIT_H */
+180
View File
@@ -0,0 +1,180 @@
/* Implementation for MTE (memory tagging) in malloc.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<http://www.gnu.org/licenses/>. */
#include "malloc-mte.h"
#include "aarch64-mte.h"
#include <malloc-ifuncs.h>
#include <errno.h>
#define TAG_MEM(ptr, tagfun) __glibc_unlikely (ptr == NULL) ? NULL : ({ \
size_t size = __malloc_usable_size_core (ptr); \
tagfun (__mte_new_tag (ptr), size); \
})
void *__libc_malloc_mte (size_t bytes)
{
void *untagged = __libc_malloc_core (bytes);
return TAG_MEM (untagged, __mte_tag_region);
}
libc_hidden_def (__libc_malloc_mte)
void *__libc_calloc_mte (size_t n, size_t elem_size)
{
/* We use core malloc instead of calloc because we can
take advantage of MTE to zero memory region. */
void *untagged = __libc_malloc_core (n * elem_size);
return TAG_MEM (untagged, __mte_tag_region_zero);
}
libc_hidden_def (__libc_calloc_mte)
void *__libc_memalign_mte (size_t alignment, size_t bytes)
{
void *untagged = __libc_memalign_core (alignment, bytes);
return TAG_MEM (untagged, __mte_tag_region);
}
libc_hidden_def (__libc_memalign_mte)
void *__libc_valloc_mte (size_t bytes)
{
void *untagged = __libc_valloc_core (bytes);
return TAG_MEM (untagged, __mte_tag_region);
}
libc_hidden_def (__libc_valloc_mte)
void *__libc_pvalloc_mte (size_t bytes)
{
void *untagged = __libc_pvalloc_core (bytes);
return TAG_MEM (untagged, __mte_tag_region);
}
libc_hidden_def (__libc_pvalloc_mte)
/* See malloc.c for details. */
#ifndef REALLOC_ZERO_BYTES_FREES
#define REALLOC_ZERO_BYTES_FREES 1
#endif
void *__libc_realloc_mte (void *tagged_oldmem, size_t bytes)
{
/* Quick check: realloc of null is supposed to be same as malloc. */
if (tagged_oldmem == NULL)
return __libc_malloc_mte (bytes);
#if REALLOC_ZERO_BYTES_FREES
/* Quick check: realloc with 0 size is supposed to be same as free. */
if (bytes == 0)
{
__libc_free_mte (tagged_oldmem);
return NULL;
}
#endif
/* Bad size, old memory remains unchanged. */
if (bytes > PTRDIFF_MAX)
{
__set_errno (ENOMEM);
return NULL;
}
/* At this point we untag oldmem allocation. */
void *untagged_oldmem = __mte_clear_tag (tagged_oldmem);
/* Mark the chunk as belonging to the library again. */
size_t size_old = __malloc_usable_size_core (untagged_oldmem);
untagged_oldmem = __mte_tag_region (untagged_oldmem, size_old);
/* Call realloc core. */
void *untagged_newmem = __libc_realloc_core (untagged_oldmem, bytes);
if (untagged_newmem == NULL)
return NULL;
size_t size_new = __malloc_usable_size_core (untagged_newmem);
/* If realloc core returns old pointer, we need re-tag it. */
if (size_new == size_old && untagged_newmem == untagged_oldmem)
return __mte_tag_region (tagged_oldmem, size_new);
/* Otherwise, assign new tag. */
void *tagged_newmem = __mte_new_tag (untagged_newmem);
return __mte_tag_region (tagged_newmem, size_new);
}
libc_hidden_def (__libc_realloc_mte)
void __libc_free_mte (void *tagged)
{
if (__glibc_unlikely (tagged == NULL))
return;
/* Mark the chunk as belonging to the library again. */
void *untagged = __mte_clear_tag (tagged);
size_t size = __malloc_usable_size_core (untagged);
untagged = __mte_tag_region (untagged, size);
/* Call free core. */
__libc_free_core (untagged);
}
libc_hidden_def (__libc_free_mte)
size_t __malloc_usable_size_mte (void *tagged)
{
/* Clear logical tag only to allow accessing internal malloc
structures via offset from this pointer. */
void *untagged = __mte_clear_tag (tagged);
return __malloc_usable_size_core (untagged);
}
libc_hidden_def (__malloc_usable_size_mte)
int __posix_memalign_mte (void **memptr, size_t alignment, size_t size)
{
int err = __posix_memalign_core (memptr, alignment, size);
if (err != 0)
return err;
*memptr = TAG_MEM (*memptr, __mte_tag_region);
return err;
}
libc_hidden_def (__posix_memalign_mte)
void *__aligned_alloc_mte (size_t alignment, size_t bytes)
{
void *untagged = __aligned_alloc_core (alignment, bytes);
return TAG_MEM (untagged, __mte_tag_region);
}
libc_hidden_def (__aligned_alloc_mte)
void __free_sized_mte (void *tagged, size_t size)
{
if (__glibc_unlikely (tagged == NULL))
return;
/* Mark the chunk as belonging to the library again. */
void *untagged = __mte_clear_tag (tagged);
size_t int_size = __malloc_usable_size_core (untagged);
untagged = __mte_tag_region (untagged, int_size);
/* Call core function. */
__free_sized_core (untagged, size);
}
libc_hidden_def (__free_sized_mte)
void __free_aligned_sized_mte (void *tagged, __attribute_maybe_unused__ size_t alignment, __attribute_maybe_unused__ size_t size)
{
if (__glibc_unlikely (tagged == NULL))
return;
/* Mark the chunk as belonging to the library again. */
void *untagged = __mte_clear_tag (tagged);
size_t int_size = __malloc_usable_size_core (untagged);
untagged = __mte_tag_region (untagged, int_size);
/* Call core function. */
__free_aligned_sized_core (untagged, alignment, size);
}
libc_hidden_def (__free_aligned_sized_mte)
+52
View File
@@ -0,0 +1,52 @@
/* Definitions and macros for MTE (memory tagging) in malloc.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef _AARCH64_MALLOC_MTE_H
#define _AARCH64_MALLOC_MTE_H
#include <stddef.h>
#include <sys/cdefs.h>
void *__libc_malloc_mte (size_t);
libc_hidden_proto (__libc_malloc_mte)
void *__libc_calloc_mte (size_t, size_t);
libc_hidden_proto (__libc_calloc_mte)
void *__libc_memalign_mte (size_t, size_t);
libc_hidden_proto (__libc_memalign_mte)
void *__libc_valloc_mte (size_t);
libc_hidden_proto (__libc_valloc_mte)
void *__libc_pvalloc_mte (size_t);
libc_hidden_proto (__libc_pvalloc_mte)
void *__libc_realloc_mte (void *, size_t);
libc_hidden_proto (__libc_realloc_mte)
void __libc_free_mte (void *);
libc_hidden_proto (__libc_free_mte)
size_t __malloc_usable_size_mte (void *);
libc_hidden_proto (__malloc_usable_size_mte)
int __posix_memalign_mte (void **, size_t, size_t);
libc_hidden_proto (__posix_memalign_mte)
void *__aligned_alloc_mte (size_t, size_t);
libc_hidden_proto (__aligned_alloc_mte)
void __free_sized_mte (void *, size_t);
libc_hidden_proto (__free_sized_mte)
void __free_aligned_sized_mte (void *, size_t, size_t);
libc_hidden_proto (__free_aligned_sized_mte)
#endif /* _AARCH64_MALLOC_MTE_H */
+71
View File
@@ -0,0 +1,71 @@
/* Size-related definitions for malloc: aarch64 version.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _AARCH64_MALLOC_SIZE_H
#define _AARCH64_MALLOC_SIZE_H
#include <malloc-chunk.h>
#include <stdint.h>
#include <sys/cdefs.h>
/* The smallest size we can malloc is an aligned minimal chunk. */
#define MINSIZE \
(unsigned long)(((MIN_CHUNK_SIZE + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
/* Pad request bytes into a usable size -- internal version. Note: This must
be a macro that evaluates to a compile time constant if passed a literal
constant. */
#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
/* MTE uses 16-byte granules. */
#define MTE_GRANULE_SIZE 16
/* Memory tagging is not supported with small granule. */
verify (MTE_GRANULE_SIZE > SIZE_SZ);
/* Memory tagging is not supported with large granule. */
verify (MTE_GRANULE_SIZE <= CHUNK_HDR_SZ);
/* Check if REQ overflows when padded and aligned and if the resulting
value is less than PTRDIFF_T. Returns the requested size or
MINSIZE in case the value is less than MINSIZE, or SIZE_MAX if any
of the previous checks fail. */
static __always_inline __attribute_maybe_unused__ size_t
checked_request2size (size_t req) __nonnull (1)
{
if (__glibc_unlikely (req > PTRDIFF_MAX))
return SIZE_MAX;
req = (req + (MTE_GRANULE_SIZE - 1))
& ~(size_t) (MTE_GRANULE_SIZE - 1);
return request2size (req);
}
/* Like chunksize, but do not mask SIZE_BITS. */
#define chunksize_nomask(p) ((p)->mchunk_size)
/* Get size, ignoring use bits. */
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))
/* This is the size of the real usable data in the chunk. Not valid for
dumped heap chunks. */
#define memsize(p) (chunksize (p) - CHUNK_HDR_SZ)
#endif /* _AARCH64_MALLOC_SIZE_H */
+1 -1
View File
@@ -16,7 +16,7 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include "address-diff.h"
#include "test-pointer.h"
#include <sys/ifunc.h>
#include <sys/auxv.h>
+47
View File
@@ -0,0 +1,47 @@
/* Support functions testing malloc: aarch64 version.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#include "test-pointer.h"
#include <sys/ifunc.h>
#include <sys/auxv.h>
/* This version clears bits 59:56 (4 bits) to remove possible
MTE tag from the pointer without trying to access memory
that this pointer points to. */
static void *ptr_after_free_mte (void *ptr)
{
return (void *)((uintptr_t)ptr & ~(0xfull << 56ull));
}
static void *ptr_after_free_generic (void *ptr)
{
return ptr;
}
static void * __attribute__ ((unused))
ptr_after_free_resolver (unsigned long a0, const unsigned long *a1)
{
unsigned long hwcap2 = __ifunc_hwcap (_IFUNC_ARG_AT_HWCAP2, a0, a1);
if (hwcap2 & HWCAP2_MTE)
return (void *)ptr_after_free_mte;
return (void *)ptr_after_free_generic;
}
void *support_ptr_after_free (void *ptr)
__attribute__ ((ifunc ("ptr_after_free_resolver")));
+4
View File
@@ -43,6 +43,9 @@ endif
endif
ifeq ($(subdir),malloc)
sysdep_routines += \
malloc-ifuncs \
# sysdep_routines
sysdep_malloc_debug_routines += \
hugepages \
# sysdep_malloc_debug_routines
@@ -57,5 +60,6 @@ endif
ifeq ($(subdir),support)
libsupport-sysdep_routines += \
support-address-diff \
support-use-after-free \
# libsupport-sysdep_routines
endif
+2
View File
@@ -16,6 +16,8 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <string.h>
static __always_inline void *
clear_memory (INTERNAL_SIZE_T *d, unsigned long clearsize)
{
-73
View File
@@ -1,73 +0,0 @@
/* libc-internal interface for tagged (colored) memory support.
Copyright (C) 2020-2026 Free Software Foundation, Inc.
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; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef _GENERIC_LIBC_MTAG_H
#define _GENERIC_LIBC_MTAG_H 1
/* Generic bindings for systems that do not support memory tagging. */
/* Used to ensure additional alignment when objects need to have distinct
tags. */
#define __MTAG_GRANULE_SIZE 1
/* Non-zero if memory obtained via morecore (sbrk) is not tagged. */
#define __MTAG_SBRK_UNTAGGED 0
/* Extra flags to pass to mmap() to request a tagged region of memory. */
#define __MTAG_MMAP_FLAGS 0
/* Memory tagging target hooks are only called when memory tagging is
enabled at runtime. The generic definitions here must not be used. */
void __libc_mtag_link_error (void);
/* Set the tags for a region of memory, which must have size and alignment
that are multiples of __MTAG_GRANULE_SIZE. Size cannot be zero. */
static inline void *
__libc_mtag_tag_region (void *p, size_t n)
{
__libc_mtag_link_error ();
return p;
}
/* Optimized equivalent to __libc_mtag_tag_region followed by memset to 0. */
static inline void *
__libc_mtag_tag_zero_region (void *p, size_t n)
{
__libc_mtag_link_error ();
return memset (p, 0, n);
}
/* Convert address P to a pointer that is tagged correctly for that
location. */
static inline void *
__libc_mtag_address_get_tag (void *p)
{
__libc_mtag_link_error ();
return p;
}
/* Assign a new (random) tag to a pointer P (does not adjust the tag on
the memory addressed). */
static inline void *
__libc_mtag_new_tag (void *p)
{
__libc_mtag_link_error ();
return p;
}
#endif /* _GENERIC_LIBC_MTAG_H */
+42 -1
View File
@@ -1,4 +1,5 @@
/* Define MALLOC_ALIGNMENT for malloc. Generic version.
/* Define INTERNAL_SIZE_T and MALLOC_ALIGNMENT for malloc.
Generic version.
Copyright (C) 2017-2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
@@ -19,6 +20,44 @@
#ifndef _GENERIC_MALLOC_ALIGNMENT_H
#define _GENERIC_MALLOC_ALIGNMENT_H
#include <stddef.h>
/* INTERNAL_SIZE_T is the word-size used for internal bookkeeping of
chunk sizes.
The default version is the same as size_t.
While not strictly necessary, it is best to define this as an
unsigned type, even if size_t is a signed type. This may avoid some
artificial size limitations on some systems.
On a 64-bit machine, you may be able to reduce malloc overhead by
defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' at the
expense of not being able to handle more than 2^32 of malloced
space. If this limitation is acceptable, you are encouraged to set
this unless you are on a platform requiring 16byte alignments. In
this case the alignment requirements turn out to negate any
potential advantages of decreasing size_t word size.
Implementors: Beware of the possible combinations of:
- INTERNAL_SIZE_T might be signed or unsigned, might be 32 or 64 bits,
and might be the same width as int or as long
- size_t might have different width and signedness as INTERNAL_SIZE_T
- int and long might be 32 or 64 bits, and might be the same width
To deal with this, most comparisons and difference computations
among INTERNAL_SIZE_Ts should cast them to unsigned long, being
aware of the fact that casting an unsigned int to a wider long does
not sign-extend. (This also makes checking for negative numbers
awkward.) Some of these casts result in harmless compiler warnings
on some systems. */
#ifndef INTERNAL_SIZE_T
# define INTERNAL_SIZE_T size_t
#endif
/* The corresponding word size. */
#define SIZE_SZ (sizeof (INTERNAL_SIZE_T))
/* MALLOC_ALIGNMENT is the minimum alignment for malloc'ed chunks. It
must be a power of two at least 2 * SIZE_SZ, even on machines for
which smaller alignments would suffice. It may be defined as larger
@@ -27,5 +66,7 @@
#define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \
? __alignof__ (long double) : 2 * SIZE_SZ)
/* The corresponding bit mask value. */
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
#endif /* !defined(_GENERIC_MALLOC_ALIGNMENT_H) */
+71
View File
@@ -0,0 +1,71 @@
/* Malloc chunk struct: generic version.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _GENERIC_MALLOC_CHUNK_H
#define _GENERIC_MALLOC_CHUNK_H
#include <malloc-alignment.h>
/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
/* The chunk header is two SIZE_SZ elements, but this is used widely, so
we define it here for clarity later. */
#define CHUNK_HDR_SZ (2 * SIZE_SZ)
/* The smallest possible chunk */
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use. */
#define PREV_INUSE 0x1
/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2
/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
from a non-main arena. This is only set immediately before handing
the chunk to the user, if necessary. */
#define NON_MAIN_ARENA 0x4
/*
Bits to mask off when extracting size.
Note: IS_MMAPPED is intentionally not masked off from size field in
macros for which mmapped chunks should never be seen. This should
cause helpful core dumps to occur if it is tried by accident by
people extending or adapting this malloc.
*/
#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
#endif /* _GENERIC_MALLOC_CHUNK_H */
+122
View File
@@ -0,0 +1,122 @@
/* Code for ifunc resolvers for malloc: generic version.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#if IS_IN (libc)
#include <malloc/malloc-internal.h>
#include <malloc-ifuncs.h>
# if HAVE_IFUNC
/* These resolvers are used by default unless overridden by a target.
The target-specific resolvers must respect this logic of the default
resolvers, replicating this logic where appropriate.
Any aliases for malloc API functions must be defined here as well
and re-defined along with the target-specific resolvers. */
IFUNC_PROTO (__libc_malloc);
IFUNC_RESOLVER (__libc_malloc, void)
{
return __libc_malloc_core;
}
strong_alias (__libc_malloc, malloc)
IFUNC_PROTO (__libc_calloc);
IFUNC_RESOLVER (__libc_calloc, void)
{
return __libc_calloc_core;
}
weak_alias (__libc_calloc, calloc)
IFUNC_PROTO (__libc_memalign);
IFUNC_RESOLVER (__libc_memalign, void)
{
return __libc_memalign_core;
}
weak_alias (__libc_memalign, memalign)
IFUNC_PROTO (__libc_valloc);
IFUNC_RESOLVER (__libc_valloc, void)
{
return __libc_valloc_core;
}
weak_alias (__libc_valloc, valloc)
IFUNC_PROTO (__libc_pvalloc);
IFUNC_RESOLVER (__libc_pvalloc, void)
{
return __libc_pvalloc_core;
}
weak_alias (__libc_pvalloc, pvalloc)
IFUNC_PROTO (__libc_realloc);
IFUNC_RESOLVER (__libc_realloc, void)
{
return __libc_realloc_core;
}
strong_alias (__libc_realloc, realloc)
IFUNC_PROTO (__libc_free);
IFUNC_RESOLVER (__libc_free, void)
{
return __libc_free_core;
}
# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)
compat_symbol (libc, __libc_free, cfree, GLIBC_2_0);
# endif
strong_alias (__libc_free, free)
IFUNC_PROTO (__malloc_usable_size);
IFUNC_RESOLVER (__malloc_usable_size, void)
{
return __malloc_usable_size_core;
}
weak_alias (__malloc_usable_size, malloc_usable_size)
IFUNC_PROTO (__posix_memalign);
IFUNC_RESOLVER (__posix_memalign, void)
{
return __posix_memalign_core;
}
weak_alias (__posix_memalign, posix_memalign)
IFUNC_PROTO (__aligned_alloc);
IFUNC_RESOLVER (__aligned_alloc, void)
{
return __aligned_alloc_core;
}
weak_alias (__aligned_alloc, aligned_alloc)
IFUNC_PROTO (__free_sized);
IFUNC_RESOLVER (__free_sized, void)
{
return __free_sized_core;
}
weak_alias (__free_sized, free_sized)
IFUNC_PROTO (__free_aligned_sized);
IFUNC_RESOLVER (__free_aligned_sized, void)
{
return __free_aligned_sized_core;
}
weak_alias (__free_aligned_sized, free_aligned_sized)
# endif /* HAVE_IFUNC */
#endif /* IS_IN (libc) */
+75
View File
@@ -0,0 +1,75 @@
/* Definitions for ifunc resolvers for malloc: generic version.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _GENERIC_MALLOC_IFUNCS_H
#define _GENERIC_MALLOC_IFUNCS_H
#if HAVE_IFUNC
#include <stddef.h>
#include <sys/cdefs.h>
#include <shlib-compat.h>
/* Core implementations of malloc functions. An ifunc resolver must
use this implementations as a fallback option. Other implementations
may internally call these core function. */
void *__libc_malloc_core (size_t);
libc_hidden_proto (__libc_malloc_core)
void *__libc_calloc_core (size_t, size_t);
libc_hidden_proto (__libc_calloc_core)
void *__libc_memalign_core (size_t, size_t);
libc_hidden_proto (__libc_memalign_core)
void *__libc_valloc_core (size_t);
libc_hidden_proto (__libc_valloc_core)
void *__libc_pvalloc_core (size_t);
libc_hidden_proto (__libc_pvalloc_core)
void *__libc_realloc_core (void *, size_t);
libc_hidden_proto (__libc_realloc_core)
void __libc_free_core (void *);
libc_hidden_proto (__libc_free_core)
size_t __malloc_usable_size_core (void *);
libc_hidden_proto (__malloc_usable_size_core)
/* For additions of POSIX. */
int __posix_memalign_core (void **, size_t, size_t);
libc_hidden_proto (__posix_memalign_core)
/* For ISO C17. */
void *__aligned_alloc_core (size_t, size_t);
libc_hidden_proto (__aligned_alloc_core)
/* For ISO C23. */
void __free_sized_core (void *, size_t);
libc_hidden_proto (__free_sized_core)
void __free_aligned_sized_core (void *, size_t, size_t);
libc_hidden_proto (__free_aligned_sized_core)
/* Macros for defining ifunc resolvers for malloc functions. */
#define IFUNC_RESOLVER_NAME(fn) fn ## _resolver
#define STR(x) #x
#define XSTR(x) STR(x)
#define IFUNC_PROTO(fn) \
__typeof (fn ## _core) fn \
__attribute__ ((ifunc (XSTR(IFUNC_RESOLVER_NAME(fn)))))
#define IFUNC_RESOLVER(fn, ...) \
static __attribute_used__ \
__typeof (fn ## _core) *IFUNC_RESOLVER_NAME(fn) (__VA_ARGS__)
#endif /* HAVE_IFUNC */
#endif /* _GENERIC_MALLOC_IFUNCS_H */
+24
View File
@@ -0,0 +1,24 @@
/* Definition for malloc init: generic version.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _GENERIC_MALLOC_INIT_H
#define _GENERIC_MALLOC_INIT_H
#define ARCH_INIT_MALLOC()
#endif /* _GENERIC_MALLOC_INIT_H */
+32 -36
View File
@@ -1,5 +1,4 @@
/* Define INTERNAL_SIZE_T, SIZE_SZ, MALLOC_ALIGNMENT and MALLOC_ALIGN_MASK
for malloc.
/* Size-related definitions for malloc: generic version.
Copyright (C) 2021-2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
@@ -20,45 +19,42 @@
#ifndef _GENERIC_MALLOC_SIZE_H
#define _GENERIC_MALLOC_SIZE_H
/* INTERNAL_SIZE_T is the word-size used for internal bookkeeping of
chunk sizes.
#include <malloc-chunk.h>
#include <stdint.h>
#include <sys/cdefs.h>
The default version is the same as size_t.
/* The smallest size we can malloc is an aligned minimal chunk. */
#define MINSIZE \
(unsigned long)(((MIN_CHUNK_SIZE + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
While not strictly necessary, it is best to define this as an
unsigned type, even if size_t is a signed type. This may avoid some
artificial size limitations on some systems.
/* Pad request bytes into a usable size -- internal version. Note: This must
be a macro that evaluates to a compile time constant if passed a literal
constant. */
#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
On a 64-bit machine, you may be able to reduce malloc overhead by
defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' at the
expense of not being able to handle more than 2^32 of malloced
space. If this limitation is acceptable, you are encouraged to set
this unless you are on a platform requiring 16byte alignments. In
this case the alignment requirements turn out to negate any
potential advantages of decreasing size_t word size.
/* Check if REQ overflows when padded and aligned and if the resulting
value is less than PTRDIFF_T. Returns the requested size or
MINSIZE in case the value is less than MINSIZE, or SIZE_MAX if any
of the previous checks fail. */
static __always_inline __attribute_maybe_unused__ size_t
checked_request2size (size_t req) __nonnull (1)
{
if (__glibc_unlikely (req > PTRDIFF_MAX))
return SIZE_MAX;
return request2size (req);
}
Implementors: Beware of the possible combinations of:
- INTERNAL_SIZE_T might be signed or unsigned, might be 32 or 64 bits,
and might be the same width as int or as long
- size_t might have different width and signedness as INTERNAL_SIZE_T
- int and long might be 32 or 64 bits, and might be the same width
/* Like chunksize, but do not mask SIZE_BITS. */
#define chunksize_nomask(p) ((p)->mchunk_size)
To deal with this, most comparisons and difference computations
among INTERNAL_SIZE_Ts should cast them to unsigned long, being
aware of the fact that casting an unsigned int to a wider long does
not sign-extend. (This also makes checking for negative numbers
awkward.) Some of these casts result in harmless compiler warnings
on some systems. */
#ifndef INTERNAL_SIZE_T
# define INTERNAL_SIZE_T size_t
#endif
/* Get size, ignoring use bits. */
#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))
/* The corresponding word size. */
#define SIZE_SZ (sizeof (INTERNAL_SIZE_T))
#include <malloc-alignment.h>
/* The corresponding bit mask value. */
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
/* This is the size of the real usable data in the chunk. Not valid for
dumped heap chunks. */
#define memsize(p) (chunksize (p) - CHUNK_HDR_SZ + SIZE_SZ)
#endif /* _GENERIC_MALLOC_SIZE_H */
+1 -1
View File
@@ -16,7 +16,7 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include "address-diff.h"
#include "test-pointer.h"
#include <libc-pointer-arith.h>
+24
View File
@@ -0,0 +1,24 @@
/* Support functions testing malloc: generic version.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#include "test-pointer.h"
void *support_ptr_after_free (void *ptr)
{
return ptr;
}
+14
View File
@@ -19,6 +19,20 @@
#ifndef _I386_MALLOC_ALIGNMENT_H
#define _I386_MALLOC_ALIGNMENT_H
#include <stddef.h>
/* INTERNAL_SIZE_T is the word-size used for internal bookkeeping of
chunk sizes. See sysdeps/generic/malloc-alignment.h for details. */
#ifndef INTERNAL_SIZE_T
# define INTERNAL_SIZE_T size_t
#endif
/* The corresponding word size. */
#define SIZE_SZ (sizeof (INTERNAL_SIZE_T))
#define MALLOC_ALIGNMENT 16
/* The corresponding bit mask value. */
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
#endif /* !defined(_I386_MALLOC_ALIGNMENT_H) */
+38
View File
@@ -371,6 +371,44 @@ ifeq ($(subdir),stdlib)
gen-as-const-headers += ucontext_i.sym
endif
ifeq ($(subdir),malloc)
tests-for-mte += \
tst-mte-malloc \
tst-mte-malloc-static \
tst-mte-realloc \
tst-mte-realloc-static \
# tests-for-mte
tests += $(tests-for-mte)
tests-static += \
tst-mte-malloc-static \
tst-mte-realloc-static \
# tests-static
CFLAGS-tst-mte-malloc.o += -march=armv9-a+memtag
CFLAGS-tst-mte-malloc-static.o += -march=armv9-a+memtag
CFLAGS-tst-mte-realloc.o += -march=armv9-a+memtag
CFLAGS-tst-mte-realloc-static.o += -march=armv9-a+memtag
# Add MTE tunable to all malloc tests except malloc-check and mcheck variants
tests-malloc-memtag = \
$(tests) \
$(tests-malloc-hugetlb1:%=%-malloc-hugetlb1) \
$(tests-malloc-hugetlb2:%=%-malloc-hugetlb2) \
$(tests-malloc-largetcache:%=%-malloc-largetcache) \
# tests-malloc-memtag
tests-exclude-malloc-check += $(tests-for-mte)
tests-exclude-mcheck += $(tests-for-mte)
define tests-malloc-memtag-ENVS
$(1)-TUNABLES += glibc.mem.aarch64_mte=sync
endef
$(foreach t,$(tests-malloc-memtag),$(eval $(call tests-malloc-memtag-ENVS,$(t))))
endif # ifeq ($(subdir),malloc)
abi-variants := lp64
abi-variants += lp64_be
+19 -27
View File
@@ -20,7 +20,6 @@
#include <cpu-features.h>
#include <sys/auxv.h>
#include <elf/dl-hwcaps.h>
#include <sys/prctl.h>
#include <sys/utsname.h>
#include <dl-tunables-parse.h>
#include <dl-symbol-redir-ifunc.h>
@@ -60,6 +59,19 @@ get_midr_from_mcpu (const struct tunable_str_t *mcpu)
return UINT64_MAX;
}
static void
TUNABLE_CALLBACK (set_aarch64_mte) (tunable_val_t *val)
{
if (tunable_strcmp_cte (val, "auto"))
GL (dl_aarch64_mte) = MTE_TUNABLE_AUTO;
else if (tunable_strcmp_cte (val, "sync"))
GL (dl_aarch64_mte) = MTE_TUNABLE_SYNC;
else if (tunable_strcmp_cte (val, "async"))
GL (dl_aarch64_mte) = MTE_TUNABLE_ASYNC;
else
GL (dl_aarch64_mte) = MTE_TUNABLE_NONE;
}
static inline void
init_cpu_features (struct cpu_features *cpu_features)
{
@@ -96,32 +108,12 @@ init_cpu_features (struct cpu_features *cpu_features)
if (cpu_features->bti)
GLRO (dl_aarch64_bti) = TUNABLE_GET (glibc, cpu, aarch64_bti, uint64_t, 0);
/* Setup memory tagging support if the HW and kernel support it, and if
the user has requested it. */
cpu_features->mte_state = 0;
#ifdef USE_MTAG
int mte_state = TUNABLE_GET (glibc, mem, tagging, unsigned, 0);
cpu_features->mte_state = (GLRO (dl_hwcap2) & HWCAP2_MTE) ? mte_state : 0;
/* If we lack the MTE feature, disable the tunable, since it will
otherwise cause instructions that won't run on this CPU to be used. */
TUNABLE_SET (glibc, mem, tagging, cpu_features->mte_state);
if (cpu_features->mte_state & 4)
/* Enable choosing system-preferred faulting mode. */
__prctl (PR_SET_TAGGED_ADDR_CTRL,
(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC
| MTE_ALLOWED_TAGS),
0, 0, 0);
else if (cpu_features->mte_state & 2)
__prctl (PR_SET_TAGGED_ADDR_CTRL,
(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | MTE_ALLOWED_TAGS),
0, 0, 0);
else if (cpu_features->mte_state)
__prctl (PR_SET_TAGGED_ADDR_CTRL,
(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_ASYNC | MTE_ALLOWED_TAGS),
0, 0, 0);
#endif
/* Check if MTE is supported. */
if (GLRO (dl_hwcap2) & HWCAP2_MTE)
TUNABLE_GET (glibc, mem, aarch64_mte, tunable_val_t *,
TUNABLE_CALLBACK (set_aarch64_mte));
else
GL (dl_aarch64_mte) = MTE_TUNABLE_NONE;
/* Check if SVE is supported. */
cpu_features->sve = GLRO (dl_hwcap) & HWCAP_SVE;
@@ -35,3 +35,19 @@ PROCINFO_CLASS unsigned long _dl_aarch64_gcs
,
# endif
#endif
#if !IS_IN (ldconfig)
# if !defined PROCINFO_DECL && defined SHARED
._dl_aarch64_mte
# else
PROCINFO_CLASS int _dl_aarch64_mte
# endif
# ifndef PROCINFO_DECL
= 0
# endif
# if !defined SHARED || defined PROCINFO_DECL
;
# else
,
# endif
#endif
@@ -36,6 +36,9 @@
# define GCS_POLICY_OPTIONAL 2
# endif
void __mte_init (void);
rtld_hidden_proto (__mte_init)
/* Must be on a top-level stack frame that does not return. */
static inline void __attribute__((always_inline))
aarch64_libc_setup_tls (void)
@@ -72,6 +75,9 @@ aarch64_libc_setup_tls (void)
_dl_fatal_printf ("failed to lock GCS: %d\n", -ret);
}
}
__mte_init ();
}
# define ARCH_SETUP_IREL() apply_irel ()
@@ -0,0 +1,110 @@
/* AArch64 test helper functions for MTE.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef TST_MTE_HELPER_H
#define TST_MTE_HELPER_H
#include <support/check.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#define GRANULE_SIZE 16
/* Extract logical tag from pointer PTR. */
static __always_inline
uint64_t get_logical_tag (const void *ptr)
{
uint64_t t = (uint64_t)ptr;
return t >> 56ul & 0xf;
}
/* Load allocation tag from memory pointed-to by the PTR pointer. */
static __always_inline
uint64_t get_allocation_tag (const void *ptr)
{
uint64_t t;
asm volatile ("ldg %0, [%1]" : "=r" (t) : "r" (ptr));
return t >> 56ul & 0xf;
}
/* Read the Tag Check Override bit. */
static __always_inline
uint64_t get_pstate_tco (void) {
uint64_t t;
asm volatile ("mrs %0, tco" : "=r" (t));
return t;
}
static __always_inline
bool check_tags (void *tm)
{
size_t len = malloc_usable_size (tm);
TEST_VERIFY (len % GRANULE_SIZE == 0);
uint64_t ltag = get_logical_tag (tm);
TEST_VERIFY (ltag != 0);
for (size_t offset = 0; offset < len; offset += GRANULE_SIZE)
{
const char *g = (char *)tm + offset;
uint64_t atag = get_allocation_tag (g);
TEST_COMPARE (ltag, atag);
if (ltag != atag)
{
printf ("tagged ptr: %p usable size: %zu\n", tm, len);
printf ("tags mismatch at offset %zu: logical=%lu, allocation=%lu\n",
offset, ltag, atag);
return false;
}
}
return ltag != 0;
}
static __always_inline
void check_mte_enabled (void)
{
/* Check if MTE is supported. */
if (!(getauxval (AT_HWCAP2) & HWCAP2_MTE))
FAIL_UNSUPPORTED ("kernel or CPU does not support HWCAP2_MTE");
/* Check if Tag Check Override bit is set. */
if (get_pstate_tco () != 0)
FAIL_UNSUPPORTED ("MTE tag check override is enabled");
/* Check applied MTE params. */
uint64_t x = (uint64_t) prctl (PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
uint64_t status = (x & 1ul);
uint64_t mode = (x & PR_MTE_TCF_MASK) >> PR_MTE_TCF_SHIFT;
uint64_t tags = (x & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT;
printf ("MTE status: %4lx\n", status);
printf ("MTE mode: %4lx\n", mode);
printf ("MTE tags: %4lx\n", tags);
/* This test should be run in sync mode for tag checks. */
TEST_VERIFY (status == 1);
TEST_VERIFY (mode == PR_MTE_TCF_SYNC >> PR_MTE_TCF_SHIFT);
TEST_VERIFY (tags == 0xfffe);
}
#endif // TST_MTE_HELPER_H
@@ -0,0 +1 @@
#include "tst-mte-malloc.c"
@@ -0,0 +1,154 @@
/* AArch64 tests for heap memory tagging.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#include <support/check.h>
#include <support/support.h>
#include <support/xsignal.h>
#include <support/test-driver.h>
#include <array_length.h>
#include "tst-mte-helper.h"
/* Characteristic malloc sizes to cover various allocation methods. */
size_t sizes[] = {
1,
16, 40, 64, 120,
128, 500, 1000,
1050, 4096, 5000, 65000,
131072, 2000000
};
static void check_malloc (size_t len)
{
printf ("testing malloc with req size %zu\n", len);
void *tm = malloc (len);
if (!check_tags (tm))
printf ("tagged pointer?: %016lx\n", (uintptr_t)tm);
free (tm);
}
static void check_calloc (size_t len)
{
size_t num = len / sizeof (uint64_t) + 1;
printf ("testing calloc with req size %zu\n", num * sizeof (uint64_t));
uint64_t *tm = calloc (num, sizeof (uint64_t));
if (check_tags (tm))
for (int n = 0; n < num; n ++)
TEST_VERIFY_EXIT (tm[n] == 0);
else
printf ("tagged pointer?: %016lx\n", (uintptr_t)tm);
free (tm);
}
static void check_memalign (size_t len, size_t alignment)
{
printf ("testing memalign(%zu) with req size %zu\n", alignment, len);
void *tm = memalign (alignment, len);
if (!check_tags (tm))
printf ("tagged pointer?: %016lx\n", (uintptr_t)tm);
free (tm);
}
static void check_valloc (size_t len)
{
printf ("testing valloc with req size %zu\n", len);
void *tm = valloc (len);
if (!check_tags (tm))
printf ("tagged pointer?: %016lx\n", (uintptr_t)tm);
free_sized (tm, len);
}
static void check_pvalloc (size_t len)
{
printf ("testing pvalloc with req size %zu\n", len);
void *tm = pvalloc (len);
if (!check_tags (tm))
printf ("tagged pointer?: %016lx\n", (uintptr_t)tm);
free_sized (tm, len);
}
static void check_posix_memalign (size_t len, size_t alignment)
{
printf ("testing posix_memalign(%zu) with req size %zu\n", alignment, len);
void *p = NULL;
int err = posix_memalign (&p, alignment, len);
if (err)
perror ("posix_memalign");
TEST_VERIFY (p != NULL);
TEST_VERIFY (err == 0);
if (!check_tags (p))
printf ("tagged pointer?: %016lx\n", (uintptr_t)p);
free_aligned_sized (p, alignment, len);
}
static void check_aligned_alloc (size_t len, size_t alignment)
{
printf ("testing aligned_alloc(%zu) with req size %zu\n", alignment, len);
void *tm = aligned_alloc (alignment, len);
if (!check_tags (tm))
printf ("tagged pointer?: %016lx\n", (uintptr_t)tm);
free_aligned_sized (tm, alignment, len);
}
static int
do_test (void)
{
/* Check if MTE is supported, configured and enabled. */
check_mte_enabled ();
array_foreach_const (plen, sizes)
check_malloc (*plen);
array_foreach_const (plen, sizes)
check_calloc (*plen);
array_foreach_const (plen, sizes)
{
check_memalign (*plen, 2);
check_memalign (*plen, 4);
check_memalign (*plen, 8);
check_memalign (*plen, 16);
check_memalign (*plen, 32);
}
array_foreach_const (plen, sizes)
check_valloc (*plen);
array_foreach_const (plen, sizes)
check_pvalloc (*plen);
array_foreach_const (plen, sizes)
{
check_posix_memalign (*plen, sizeof (void *) * 1);
check_posix_memalign (*plen, sizeof (void *) * 2);
check_posix_memalign (*plen, sizeof (void *) * 4);
}
array_foreach_const (plen, sizes)
{
check_aligned_alloc (*plen, 2);
check_aligned_alloc (*plen, 4);
check_aligned_alloc (*plen, 8);
check_aligned_alloc (*plen, 16);
check_aligned_alloc (*plen, 32);
}
return 0;
}
#include <support/test-driver.c>
@@ -0,0 +1 @@
#include "tst-mte-realloc.c"
@@ -0,0 +1,72 @@
/* AArch64 tests for heap memory tagging.
Copyright (C) 2026 Free Software Foundation, Inc.
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; if not, see
<https://www.gnu.org/licenses/>. */
#include <support/check.h>
#include <support/support.h>
#include <support/xsignal.h>
#include <support/test-driver.h>
#include <array_length.h>
#include "tst-mte-helper.h"
/* Characteristic malloc sizes to cover various allocation methods. */
size_t sizes[] = {
1,
16, 40, 64, 120,
128, 500, 1000,
1050, 4096, 5000, 65000,
131072, 2000000
};
static void check_realloc (size_t len)
{
/* Tagged pointers. */
void *tm, *new_tm;
printf ("testing realloc (NULL) for req size %zu\n", len);
tm = realloc (NULL, len);
check_tags (tm);
/* Reduce size. */
printf ("testing realloc (decreased size) for req size %zu\n", len);
new_tm = realloc (tm, len / 2 + 1);
check_tags (new_tm);
/* Increase size. */
printf ("testing realloc (increased size) for req size %zu\n", len);
new_tm = realloc (new_tm, len + 2);
check_tags (new_tm);
free (new_tm);
}
static int
do_test (void)
{
/* Check if MTE is supported, configured and enabled. */
check_mte_enabled ();
array_foreach_const (plen, sizes)
check_realloc (*plen);
return 0;
}
#include <support/test-driver.c>