mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-10 12:05:30 +08:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4189e8060e | ||
|
|
43bd158d89 | ||
|
|
fe1173fcf4 | ||
|
|
acce1ee5a0 | ||
|
|
23d806003d | ||
|
|
884f8f9325 | ||
|
|
f33b8e0df5 | ||
|
|
d59e08f09b | ||
|
|
7ca145fabe | ||
|
|
1b27e7732f |
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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@
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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@:>@])],
|
||||
|
||||
@@ -116,11 +116,6 @@ glibc {
|
||||
}
|
||||
|
||||
mem {
|
||||
tagging {
|
||||
type: INT_32
|
||||
minval: 0
|
||||
maxval: 255
|
||||
}
|
||||
decorate_maps {
|
||||
type: INT_32
|
||||
minval: 0
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
@@ -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:
|
||||
|
||||
@@ -34,4 +34,9 @@ glibc {
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
mem {
|
||||
aarch64_mte {
|
||||
type: STRING
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) */
|
||||
@@ -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 */
|
||||
@@ -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)
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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>
|
||||
|
||||
@@ -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")));
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 */
|
||||
@@ -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) */
|
||||
|
||||
@@ -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 */
|
||||
@@ -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) */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user