Compare commits

...
Author SHA1 Message Date
Yury Khrustalev 28cea543e9 malloc: aarch64: Add MTE memory tagging
Tag memory on systems that support MTE and when memory
tagging is enabled at runtime.

Currently we implement tagging as a wrapper around core malloc
functions. User pointers are expected to be tagged and internal
malloc pointers are untagged (or rather tagged with 0 tag).

Also add tests that check logical and allocation tags.
2026-05-29 14:40:03 +01:00
Yury Khrustalev bb271571ae 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.
On all targets this macro should initialise extra_mmap_prot.

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 active. 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-29 14:28:47 +01:00
Yury Khrustalev ad6e64ac27 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-29 13:56:01 +01:00
Yury Khrustalev 75dfbc48a5 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-29 09:45:41 +01:00
Yury Khrustalev 9a9bb729c2 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-29 09:42:51 +01:00
Yury Khrustalev d80a4cdcab malloc: aarch64: Add ifuncs for malloc functions
Introduce ifuncs and resolvers for functions pertinent to the
malloc interface on the AArch64 target: malloc, calloc, free,
realloc, memalign, valloc, pvalloc, posix_memalign, aligned_alloc,
free_sized, free_aligned_sized, malloc_usable_size.

A target can define the USE_MULTIARCH_MALLOC macro. In this case
it must provide alternative aliases for the malloc functions that
point to the ifuncs.

This implementation respects the --disable-multi-arch configure
flag. If multi-arch support is disabled, the generic aliases
are used on aarch64.

This patch contains aarch64-specific resolvers. At this moment they
return core implementations but in the future they can be changed
to support for features, e.g. to handle memory tagging.
2026-05-29 09:32:44 +01:00
Yury Khrustalev ef13f935b4 malloc: aarch64: Remove broken memory tagging
Remove the --enable-memory-tagging configure option along with
all associated variables and macros.

Removing the glibc.mem.tagging tunable.

Remove the memory-tagging makefile variable.

Remove the USE_MTAG macro definition and code that is conditionally
compiled when this macro is defined.

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.

Remove AArch64-specific code pertinent to memory tagging, that is
currently broken, from the core malloc implementation. We keep
the assembly code, since we are going to need it in the future. to
preserve Git history.
2026-05-29 09:32:27 +01:00
44 changed files with 1399 additions and 669 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
@@ -185,9 +185,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
+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)
@@ -248,24 +250,13 @@ static void tcache_key_initialize (void);
void
__ptmalloc_init (void)
{
/* Perform any target-specific initialisation. */
ARCH_INIT_MALLOC ();
#if USE_TCACHE
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
@@ -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) != 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) != 0)
return -2;
h->mprotect_size = new_size;
+12 -28
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))
#define CHAR_OFFSET(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
@@ -58,7 +54,7 @@ malloc_check_get_size (void *mem)
unsigned char magic = magicbyte (p);
for (size = CHUNK_HDR_SZ + memsize (p) - 1;
(c = *SAFE_CHAR_OFFSET (p, size)) != magic;
(c = *CHAR_OFFSET (p, size)) != magic;
size -= c)
{
if (c <= 0 || size < (c + CHUNK_HDR_SZ))
@@ -95,9 +91,9 @@ mem2mem_check (void *ptr, size_t req_sz)
if (block_sz == magic)
--block_sz;
*SAFE_CHAR_OFFSET (m_ptr, i) = block_sz;
*CHAR_OFFSET (m_ptr, i) = block_sz;
}
*SAFE_CHAR_OFFSET (m_ptr, req_sz) = magic;
*CHAR_OFFSET (m_ptr, req_sz) = magic;
return (void *) m_ptr;
}
@@ -131,7 +127,7 @@ mem2chunk_check (void *mem, unsigned char **magic_p)
return NULL;
for (sz = CHUNK_HDR_SZ + memsize (p) - 1;
(c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
(c = *CHAR_OFFSET (p, sz)) != magic;
sz -= c)
{
if (c == 0 || sz < (c + CHUNK_HDR_SZ))
@@ -156,7 +152,7 @@ mem2chunk_check (void *mem, unsigned char **magic_p)
return NULL;
for (sz = CHUNK_HDR_SZ + memsize (p) - 1;
(c = *SAFE_CHAR_OFFSET (p, sz)) != magic;
(c = *CHAR_OFFSET (p, sz)) != magic;
sz -= c)
{
if (c == 0 || sz < (c + CHUNK_HDR_SZ))
@@ -164,7 +160,7 @@ mem2chunk_check (void *mem, unsigned char **magic_p)
}
}
unsigned char* safe_p = SAFE_CHAR_OFFSET (p, sz);
unsigned char* safe_p = CHAR_OFFSET (p, sz);
*safe_p ^= 0xFF;
if (magic_p)
*magic_p = safe_p;
@@ -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
+3
View File
@@ -24,6 +24,9 @@
#include <malloc-size.h>
#include <hugepages.h>
#include <calloc-clear-memory.h>
#if defined(USE_MULTIARCH)
# include <malloc-ifuncs.h>
#endif
/* Called in the parent process before a fork. */
void __malloc_fork_lock_parent (void) attribute_hidden;
+61 -319
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,97 +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;
}
static int extra_mmap_prot = 0;
/*
@@ -591,6 +499,7 @@ libc_hidden_proto (__libc_free)
set to zero.
*/
void* __libc_calloc(size_t, size_t);
libc_hidden_proto (__libc_calloc)
/*
realloc(void* p, size_t n)
@@ -643,6 +552,7 @@ libc_hidden_proto (__libc_memalign)
size of the system. If the pagesize is unknown, 4096 is used.
*/
void* __libc_valloc(size_t);
libc_hidden_proto (__libc_valloc)
@@ -677,6 +587,7 @@ struct mallinfo __libc_mallinfo(void);
round up n to nearest pagesize.
*/
void* __libc_pvalloc(size_t);
libc_hidden_proto (__libc_pvalloc)
/*
malloc_trim(size_t pad);
@@ -720,6 +631,7 @@ int __malloc_trim(size_t);
*/
size_t __malloc_usable_size(void*);
libc_hidden_proto (__malloc_usable_size)
/*
malloc_stats();
@@ -749,6 +661,7 @@ void __malloc_stats(void);
POSIX wrapper like memalign(), checking for validity of size.
*/
int __posix_memalign(void **, size_t, size_t);
libc_hidden_proto (__posix_memalign)
#endif /* IS_IN (libc) */
/*
@@ -1016,12 +929,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);
@@ -1068,27 +979,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.)
@@ -1194,137 +1085,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)))
@@ -1375,30 +1164,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
@@ -2227,9 +1992,7 @@ sysmalloc_mmap (INTERNAL_SIZE_T nb, size_t pagesize, int extra_flags)
size_t padding = MALLOC_ALIGNMENT - CHUNK_HDR_SZ;
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_flags);
char *mm = (char *) MMAP (NULL, size, extra_mmap_prot, extra_flags);
if (mm == MAP_FAILED)
return mm;
if (extra_flags == 0)
@@ -2268,9 +2031,7 @@ sysmalloc_mmap_fallback (size_t *s, size_t size, size_t minsize,
if (size < minsize)
size = minsize;
char *mbrk = (char *) (MMAP (NULL, size,
mtag_mmap_flags | PROT_READ | PROT_WRITE,
extra_flags));
char *mbrk = (char *) MMAP (NULL, size, extra_mmap_prot, extra_flags);
if (mbrk == MAP_FAILED)
return MAP_FAILED;
@@ -3106,7 +2867,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;
}
@@ -3224,7 +2985,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;
@@ -3245,8 +3006,6 @@ __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;
@@ -3265,14 +3024,14 @@ __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
@@ -3296,16 +3055,8 @@ __libc_free (void *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)))
@@ -3368,11 +3119,6 @@ __libc_realloc (void *oldmem, size_t bytes)
}
#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);
@@ -3414,15 +3160,7 @@ __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)
@@ -3464,10 +3202,8 @@ __libc_realloc (void *oldmem, size_t bytes)
newp = __libc_malloc (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);
memcpy (newp, oldmem, memsize (oldp));
_int_free_chunk (ar_ptr, oldp, chunksize (oldp), 0);
}
}
@@ -3497,8 +3233,11 @@ libc_hidden_def (__libc_memalign)
/* For ISO C17. */
void *
weak_function
aligned_alloc (size_t alignment, size_t bytes)
__aligned_alloc (size_t alignment, size_t bytes);
libc_hidden_proto (__aligned_alloc)
void *
__aligned_alloc (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. */
@@ -3510,11 +3249,15 @@ aligned_alloc (size_t alignment, size_t bytes)
return _mid_memalign (alignment, bytes);
}
libc_hidden_def (__aligned_alloc)
/* For ISO C23. */
void
weak_function
free_sized (void *ptr, __attribute_maybe_unused__ size_t size)
__free_sized (void *ptr, __attribute_maybe_unused__ size_t size);
libc_hidden_proto (__free_sized)
void
__free_sized (void *ptr, __attribute_maybe_unused__ size_t size)
{
/* We do not perform validation that size is the same as the original
requested size at this time. We leave that to the sanitizers. We
@@ -3523,11 +3266,16 @@ free_sized (void *ptr, __attribute_maybe_unused__ size_t size)
free (ptr);
}
libc_hidden_def (__free_sized)
/* For ISO C23. */
void
weak_function
free_aligned_sized (void *ptr, __attribute_maybe_unused__ size_t alignment,
__free_aligned_sized (void *ptr, __attribute_maybe_unused__ size_t alignment,
__attribute_maybe_unused__ size_t size);
libc_hidden_proto (__free_aligned_sized)
void
__free_aligned_sized (void *ptr, __attribute_maybe_unused__ size_t alignment,
__attribute_maybe_unused__ size_t size)
{
/* We do not perform validation that size and alignment is the same as
@@ -3537,6 +3285,7 @@ free_aligned_sized (void *ptr, __attribute_maybe_unused__ size_t alignment,
free (ptr);
}
libc_hidden_def (__free_aligned_sized)
static void *
_mid_memalign (size_t alignment, size_t bytes)
@@ -3551,7 +3300,7 @@ _mid_memalign (size_t alignment, size_t 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)
@@ -3559,7 +3308,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);
@@ -3577,7 +3326,7 @@ _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 *
@@ -3585,6 +3334,7 @@ __libc_valloc (size_t bytes)
{
return _mid_memalign (GLRO (dl_pagesize), bytes);
}
libc_hidden_def (__libc_valloc)
void *
__libc_pvalloc (size_t bytes)
@@ -3602,6 +3352,7 @@ __libc_pvalloc (size_t bytes)
return _mid_memalign (pagesize, rounded_bytes & -pagesize);
}
libc_hidden_def (__libc_pvalloc)
static void * __attribute_noinline__
__libc_calloc2 (size_t sz)
@@ -3668,12 +3419,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 */
@@ -3720,9 +3465,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));
}
}
@@ -3731,17 +3473,13 @@ __libc_calloc (size_t n, size_t elem_size)
tc_idx = large_csize2tidx (nb);
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)));
}
return memset (mem, 0, memsize (mem2chunk (mem)));
}
}
#endif
return __libc_calloc2 (bytes);
}
libc_hidden_def (__libc_calloc)
#endif /* IS_IN (libc) */
/*
@@ -4494,7 +4232,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 */
@@ -4528,10 +4266,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;
@@ -4553,8 +4288,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));
@@ -4564,7 +4297,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);
}
/*
@@ -4743,6 +4476,7 @@ __malloc_usable_size (void *m)
return 0;
return musable (m);
}
libc_hidden_def (__malloc_usable_size)
#endif /* IS_IN (libc) */
/*
@@ -5270,6 +5004,7 @@ __posix_memalign (void **memptr, size_t alignment, size_t size)
*memptr = mem;
return 0;
}
libc_hidden_def (__posix_memalign)
#endif /* IS_IN (libc) */
@@ -5432,6 +5167,9 @@ __malloc_info (int options, FILE *fp)
}
#if IS_IN (libc)
/* See sysdeps/generic/malloc-ifuncs.h for details. */
# if !defined (USE_MULTIARCH_MALLOC)
strong_alias (__libc_malloc, malloc)
strong_alias (__libc_realloc, realloc)
strong_alias (__libc_free, free)
@@ -5441,6 +5179,10 @@ weak_alias (__posix_memalign, posix_memalign)
weak_alias (__libc_valloc, valloc)
weak_alias (__libc_pvalloc, pvalloc)
weak_alias (__malloc_usable_size, malloc_usable_size)
weak_alias (__aligned_alloc, aligned_alloc)
weak_alias (__free_sized, free_sized)
weak_alias (__free_aligned_sized, free_aligned_sized)
#endif /* !USE_MULTIARCH_MALLOC */
weak_alias (__malloc_info, malloc_info)
weak_alias (__libc_mallinfo, mallinfo)
+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 *
-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 Memory Tagging Extension (MTE) this
tunable allows to select tag check fault mode (MTE mode).
Supported 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
+4 -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 += \
@@ -104,10 +105,7 @@ $(objpfx)tst-sme-za-disable-fail: $(objpfx)__arm_za_disable.o
endif
ifeq ($(subdir),malloc)
sysdep_malloc_debug_routines = \
__mtag_tag_region \
__mtag_tag_zero_region \
# sysdep_malloc_debug_routines
endif # malloc directory
ifeq ($(subdir),support)
@@ -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 */
+9 -2
View File
@@ -59,17 +59,24 @@ 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;
bool sve2;
bool mte;
};
#endif /* _CPU_FEATURES_AARCH64_H */
+1 -2
View File
@@ -45,10 +45,9 @@ _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 ("sve2", GLRO (dl_aarch64_cpu_features).sve2);
print_cpu_features_value ("mte", GLRO (dl_aarch64_cpu_features).mte);
print_cpu_features_value ("zva_size",
GLRO (dl_aarch64_cpu_features).zva_size);
+67
View File
@@ -0,0 +1,67 @@
/* 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)
{
if (!GLRO (dl_aarch64_cpu_features).mte)
return;
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
}
}
}
+9 -2
View File
@@ -18,5 +18,12 @@ sysdep_routines += \
memset_zva64 \
strlen_asimd \
strlen_generic \
# sysdep_routines
endif
# sysdep_routines
endif # ifeq ($(subdir),string)
ifeq ($(subdir),malloc)
sysdep_routines += \
malloc-ifuncs \
malloc-mte \
# sysdep_routines
endif # ifeq ($(subdir),malloc)
+148
View File
@@ -0,0 +1,148 @@
/* 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 <stdint.h>
#include <malloc-ifuncs.h>
#include <ldsodefs.h>
/* 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) fn ## _ifunc \
__attribute__ ((ifunc (XSTR(IFUNC_RESOLVER_NAME(fn)))))
#define IFUNC_RESOLVER(fn, ...) \
static __attribute_used__ \
__typeof (fn) *IFUNC_RESOLVER_NAME(fn) (__VA_ARGS__)
#define MTE_ACTIVE \
(GLRO (dl_aarch64_cpu_features).mte) && \
(GL (dl_aarch64_mte) != MTE_TUNABLE_NONE)
IFUNC_PROTO (__libc_malloc);
IFUNC_RESOLVER (__libc_malloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __libc_malloc_mte;
return __libc_malloc;
}
strong_alias (__libc_malloc_ifunc, malloc)
IFUNC_PROTO (__libc_calloc);
IFUNC_RESOLVER (__libc_calloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __libc_calloc_mte;
return __libc_calloc;
}
weak_alias (__libc_calloc_ifunc, calloc)
IFUNC_PROTO (__libc_memalign);
IFUNC_RESOLVER (__libc_memalign, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __libc_memalign_mte;
return __libc_memalign;
}
weak_alias (__libc_memalign_ifunc, memalign)
IFUNC_PROTO (__libc_valloc);
IFUNC_RESOLVER (__libc_valloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __libc_valloc_mte;
return __libc_valloc;
}
weak_alias (__libc_valloc_ifunc, valloc)
IFUNC_PROTO (__libc_pvalloc);
IFUNC_RESOLVER (__libc_pvalloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __libc_pvalloc_mte;
return __libc_pvalloc;
}
weak_alias (__libc_pvalloc_ifunc, pvalloc)
IFUNC_PROTO (__libc_realloc);
IFUNC_RESOLVER (__libc_realloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __libc_realloc_mte;
return __libc_realloc;
}
strong_alias (__libc_realloc_ifunc, realloc)
IFUNC_PROTO (__libc_free);
IFUNC_RESOLVER (__libc_free, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __libc_free_mte;
return __libc_free;
}
strong_alias (__libc_free_ifunc, free)
IFUNC_PROTO (__malloc_usable_size);
IFUNC_RESOLVER (__malloc_usable_size, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __malloc_usable_size_mte;
return __malloc_usable_size;
}
weak_alias (__malloc_usable_size_ifunc, malloc_usable_size)
IFUNC_PROTO (__posix_memalign);
IFUNC_RESOLVER (__posix_memalign, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __posix_memalign_mte;
return __posix_memalign;
}
weak_alias (__posix_memalign_ifunc, posix_memalign)
IFUNC_PROTO (__aligned_alloc);
IFUNC_RESOLVER (__aligned_alloc, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __aligned_alloc_mte;
return __aligned_alloc;
}
weak_alias (__aligned_alloc_ifunc, aligned_alloc)
IFUNC_PROTO (__free_sized);
IFUNC_RESOLVER (__free_sized, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __free_sized_mte;
return __free_sized;
}
weak_alias (__free_sized_ifunc, free_sized)
IFUNC_PROTO (__free_aligned_sized);
IFUNC_RESOLVER (__free_aligned_sized, uint64_t arg0, uint64_t arg1[])
{
if (MTE_ACTIVE)
return __free_aligned_sized_mte;
return __free_aligned_sized;
}
weak_alias (__free_aligned_sized_ifunc, free_aligned_sized)
#endif /* IS_IN (libc) */
+93
View File
@@ -0,0 +1,93 @@
/* Definitions 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/>. */
#ifndef _AARCH64_MALLOC_IFUNCS_H
#define _AARCH64_MALLOC_IFUNCS_H
#define USE_MULTIARCH_MALLOC 1
#include <stddef.h>
#include <sys/cdefs.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 (size_t);
libc_hidden_proto (__libc_malloc)
void *__libc_calloc (size_t, size_t);
libc_hidden_proto (__libc_calloc)
void *__libc_memalign (size_t, size_t);
libc_hidden_proto (__libc_memalign)
void *__libc_valloc (size_t);
libc_hidden_proto (__libc_valloc)
void *__libc_pvalloc (size_t);
libc_hidden_proto (__libc_pvalloc)
void *__libc_realloc (void *, size_t);
libc_hidden_proto (__libc_realloc)
void __libc_free (void *);
libc_hidden_proto (__libc_free)
size_t __malloc_usable_size (void *);
libc_hidden_proto (__malloc_usable_size)
/* For additions of POSIX. */
int __posix_memalign (void **, size_t, size_t);
libc_hidden_proto (__posix_memalign)
/* For ISO C17. */
void *__aligned_alloc (size_t, size_t);
libc_hidden_proto (__aligned_alloc)
/* For ISO C23. */
void __free_sized (void *, size_t);
libc_hidden_proto (__free_sized)
void __free_aligned_sized (void *, size_t, size_t);
libc_hidden_proto (__free_aligned_sized)
/* MTE implementations of malloc functions. */
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)
/* For additions of POSIX: MTE version. */
int __posix_memalign_mte (void **, size_t, size_t);
libc_hidden_proto (__posix_memalign_mte)
/* For ISO C17: MTE version. */
void *__aligned_alloc_mte (size_t, size_t);
libc_hidden_proto (__aligned_alloc_mte)
/* For ISO C23: MTE version. */
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_IFUNCS_H */
+34
View File
@@ -0,0 +1,34 @@
/* 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 MTE_ACTIVE \
(GLRO (dl_aarch64_cpu_features).mte) && \
(GL (dl_aarch64_mte) != MTE_TUNABLE_NONE)
#define ARCH_INIT_MALLOC() \
extra_mmap_prot = PROT_READ | PROT_WRITE; \
if (MTE_ACTIVE) \
{ \
extra_mmap_prot |= PROT_MTE; \
__always_fail_morecore = true; \
}
#endif /* _AARCH64_MALLOC_INIT_H */
+176
View File
@@ -0,0 +1,176 @@
/* Implementation for MTE (memory tagging) wrappers 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 "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 (ptr); \
tagfun (__mte_new_tag (ptr), size); \
})
#define UNTAG_MEM(non_null_ptr) ({ \
void *untagged = __mte_clear_tag (non_null_ptr); \
size_t size = __malloc_usable_size (untagged); \
__mte_tag_region (untagged, size); \
})
void *__libc_malloc_mte (size_t bytes)
{
void *untagged = __libc_malloc (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 (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 (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 (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 (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 (untagged_oldmem);
untagged_oldmem = __mte_tag_region (untagged_oldmem, size_old);
/* Call realloc core. */
void *untagged_newmem = __libc_realloc (untagged_oldmem, bytes);
if (untagged_newmem == NULL)
return NULL;
size_t size_new = __malloc_usable_size (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 = UNTAG_MEM (tagged);
/* Call free core. */
__libc_free (untagged);
}
libc_hidden_def (__libc_free_mte)
size_t __malloc_usable_size_mte (void *tagged)
{
/* Clear only logical tag to allow accessing internal malloc
structures via offset from this pointer. */
void *untagged = __mte_clear_tag (tagged);
return __malloc_usable_size (untagged);
}
libc_hidden_def (__malloc_usable_size_mte)
int __posix_memalign_mte (void **memptr, size_t alignment, size_t size)
{
/* Call core function. */
int err = __posix_memalign (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 (alignment, bytes);
return TAG_MEM (untagged, __mte_tag_region);
}
libc_hidden_def (__aligned_alloc_mte)
void __free_sized_mte (void *tagged, size_t size)
{
/* Mark the chunk as belonging to the library again. */
void *untagged = UNTAG_MEM (tagged);
/* Call core function. */
__free_sized (untagged, size);
}
libc_hidden_def (__free_sized_mte)
void __free_aligned_sized_mte (void *tagged, size_t alignment, size_t size)
{
/* Mark the chunk as belonging to the library again. */
void *untagged = UNTAG_MEM (tagged);
/* Call core function. */
__free_aligned_sized (untagged, alignment, size);
}
libc_hidden_def (__free_aligned_sized_mte)
+69
View File
@@ -0,0 +1,69 @@
/* 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 <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 */
-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) */
+70
View File
@@ -0,0 +1,70 @@
/* 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 */
+37
View File
@@ -0,0 +1,37 @@
/* 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
/* Targets should define this macro if they provide ifuncs for
malloc functions. When USE_MULTIARCH_MALLOC is defined, the
following functions should be implemented via ifuncs:
malloc, calloc, free, realloc
memalign, valloc, pvalloc
posix_memalign
malloc_usable_size
aligned_alloc, free_sized, free_aligned_sized
*/
#if defined(USE_MULTIARCH_MALLOC) || !defined(USE_MULTIARCH)
# undef USE_MULTIARCH_MALLOC
#endif
#endif /* GENERIC_MALLOC_IFUNCS_H */
+25
View File
@@ -0,0 +1,25 @@
/* 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() \
extra_mmap_prot = PROT_READ | PROT_WRITE;
#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 */
+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) */
+43
View File
@@ -371,6 +371,49 @@ ifeq ($(subdir),stdlib)
gen-as-const-headers += ucontext_i.sym
endif
ifeq ($(subdir),malloc)
ifneq ($(multi-arch),no)
# Special test that check MTE memory tagging
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
tests-exclude-malloc-check += $(tests-for-mte)
tests-exclude-mcheck += $(tests-for-mte)
tests-exclude-hugetlb1 += $(tests-for-mte)
tests-exclude-hugetlb2 += $(tests-for-mte)
endif # ifneq ($(multi-arch),no)
# Add MTE tunable to all malloc tests except malloc-check and mcheck variants
tests-malloc-mte = \
$(tests) \
$(tests-malloc-hugetlb1:%=%-malloc-hugetlb1) \
$(tests-malloc-hugetlb2:%=%-malloc-hugetlb2) \
$(tests-malloc-largetcache:%=%-malloc-largetcache) \
# tests-malloc-mte
define tests-malloc-mte-ENVS
$(1)-TUNABLES += glibc.mem.aarch64_mte=sync
endef
$(foreach t,$(tests-malloc-mte),$(eval $(call tests-malloc-mte-ENVS,$(t))))
endif # ifeq ($(subdir),malloc)
abi-variants := lp64
abi-variants += lp64_be
+20 -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,13 @@ 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. */
cpu_features->mte = GLRO (dl_hwcap2) & HWCAP2_MTE;
if (cpu_features->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>