[glibc/arm/malloc-mte-v1] malloc: Remove currently broken memory tagging
Yury Khrustalev via Glibc-cvs <[email protected]> Thu, 7 May 2026 12:45:19 +0000 (GMT)
| Newsgroups | gmane.comp.lib.glibc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=884f8f93256a3127ff95aadec0b04718d3f3dc8a commit 884f8f93256a3127ff95aadec0b04718d3f3dc8a Author: Yury Khrustalev <[email protected]> Date: Wed Apr 22 13:48:45 2026 +0100 malloc: Remove currently broken memory tagging Remove AArch64-specific code, that is currently broken, from the core malloc implementation. Diff: --- malloc/malloc-check.c | 16 +-- malloc/malloc.c | 160 +++------------------ sysdeps/aarch64/Makefile | 8 +- .../{__mtag_tag_region.S => __mte_tag_region.S} | 4 +- ...g_tag_zero_region.S => __mte_tag_region_zero.S} | 4 +- sysdeps/aarch64/{libc-mtag.h => aarch64-mte.h} | 70 +++++---- sysdeps/generic/libc-mtag.h | 73 ---------- 7 files changed, 61 insertions(+), 274 deletions(-) diff --git a/malloc/malloc-check.c b/malloc/malloc-check.c index ae5025d69a..10259bc7f3 100644 --- a/malloc/malloc-check.c +++ b/malloc/malloc-check.c @@ -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 @@ -228,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); } @@ -278,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 { @@ -313,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 * @@ -355,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 diff --git a/malloc/malloc.c b/malloc/malloc.c index d1f452500e..cea1d75b3e 100644 --- a/malloc/malloc.c +++ b/malloc/malloc.c @@ -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,86 +347,8 @@ 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. -*/ - static int extra_mmap_prot = 0; -static __always_inline void * -tag_region (void *ptr, size_t size) -{ - return ptr; -} - -static __always_inline void * -tag_new_zero_region (void *ptr, size_t size) -{ - return memset (ptr, 0, size); -} - -/* Defined later. */ -static void * -tag_new_usable (void *ptr); - -static __always_inline void * -tag_at (void *ptr) -{ - return ptr; -} - /* MORECORE-related declarations. By default, rely on sbrk */ @@ -1185,38 +1105,15 @@ 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))) +/* Convert a user mem pointer to a chunk address. */ +#define mem2chunk(mem) ((mchunkptr) (((char*)(mem) - CHUNK_HDR_SZ))) /* The smallest possible chunk */ #define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize)) @@ -1353,12 +1250,6 @@ checked_request2size (size_t req) __nonnull (1) dumped heap chunks. */ #define memsize(p) (chunksize (p) - CHUNK_HDR_SZ + SIZE_SZ) -static __always_inline void * -tag_new_usable (void *ptr) -{ - return ptr; -} - /* Huge page used for an mmap chunk. */ #define MMAP_HP 0x1 @@ -3070,7 +2961,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; } @@ -3188,7 +3079,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; @@ -3209,8 +3100,6 @@ __libc_malloc2 (size_t bytes) if (ar_ptr != NULL) __libc_lock_unlock (ar_ptr->mutex); - victim = tag_new_usable (victim); - assert (!victim || chunk_is_mmapped (mem2chunk (victim)) || ar_ptr == arena_for_chunk (mem2chunk (victim))); return victim; @@ -3229,14 +3118,14 @@ __libc_malloc_core (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 @@ -3262,9 +3151,6 @@ __libc_free_core (void *mem) p = mem2chunk (mem); - /* Mark the chunk as belonging to the library again. */ - tag_region (chunk2mem (p), memsize (p)); - INTERNAL_SIZE_T size = chunksize (p); if (__glibc_unlikely (misaligned_chunk (p))) @@ -3368,15 +3254,7 @@ __libc_realloc_core (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) @@ -3420,8 +3298,7 @@ __libc_realloc_core (void *oldmem, size_t bytes) { 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); } } @@ -3514,7 +3391,7 @@ _mid_memalign (size_t alignment, size_t bytes) #if USE_TCACHE void *victim = tcache_get_align (checked_request2size (bytes), alignment); if (victim != NULL) - return tag_new_usable (victim); + return victim; #endif if (SINGLE_THREAD_P) @@ -3522,7 +3399,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); @@ -3540,7 +3417,7 @@ _mid_memalign (size_t alignment, size_t bytes) assert (!p || chunk_is_mmapped (mem2chunk (p)) || ar_ptr == arena_for_chunk (mem2chunk (p))); - return tag_new_usable (p); + return p; } void * @@ -4460,7 +4337,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 */ @@ -4494,10 +4371,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; @@ -4519,8 +4393,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)); @@ -4530,7 +4402,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); } /* diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile index a5e5b3f641..949c87ff1d 100644 --- a/sysdeps/aarch64/Makefile +++ b/sysdeps/aarch64/Makefile @@ -82,8 +82,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 += \ @@ -105,10 +105,6 @@ ifeq ($(subdir),malloc) sysdep_routines += \ malloc-ifuncs \ # sysdep_routines -sysdep_malloc_debug_routines = \ - __mtag_tag_region \ - __mtag_tag_zero_region \ - # sysdep_malloc_debug_routines endif # malloc directory ifeq ($(subdir),support) diff --git a/sysdeps/aarch64/__mtag_tag_region.S b/sysdeps/aarch64/__mte_tag_region.S similarity index 97% rename from sysdeps/aarch64/__mtag_tag_region.S rename to sysdeps/aarch64/__mte_tag_region.S index 85e330812e..1698489fc2 100644 --- a/sysdeps/aarch64/__mtag_tag_region.S +++ b/sysdeps/aarch64/__mte_tag_region.S @@ -37,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 @@ -104,4 +104,4 @@ L(no_zva_loop): st2g dstin, [dstend, -32] ret -END (__libc_mtag_tag_region) +END (__mte_tag_region) diff --git a/sysdeps/aarch64/__mtag_tag_zero_region.S b/sysdeps/aarch64/__mte_tag_region_zero.S similarity index 97% rename from sysdeps/aarch64/__mtag_tag_zero_region.S rename to sysdeps/aarch64/__mte_tag_region_zero.S index 1a84b3e4d4..2f506c9ee8 100644 --- a/sysdeps/aarch64/__mtag_tag_zero_region.S +++ b/sysdeps/aarch64/__mte_tag_region_zero.S @@ -37,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 @@ -104,4 +104,4 @@ L(no_zva_loop): stz2g dstin, [dstend, -32] ret -END (__libc_mtag_tag_zero_region) +END (__mte_tag_region_zero) diff --git a/sysdeps/aarch64/libc-mtag.h b/sysdeps/aarch64/aarch64-mte.h similarity index 57% rename from sysdeps/aarch64/libc-mtag.h rename to sysdeps/aarch64/aarch64-mte.h index 663b866bf8..f42564f528 100644 --- a/sysdeps/aarch64/libc-mtag.h +++ b/sysdeps/aarch64/aarch64-mte.h @@ -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,51 +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 -#if 0 +#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)); + 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; } -/* 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) +/* 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)); +} + +/* 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; - register unsigned long 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)); + asm (".inst 0xd9600000 /* ldg x0, [x0] */" : "+r" (x0)); return x0; } -#endif /* USE_MTAG */ +/* 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_LIBC_MTAG_H */ +#endif /* _AARCH64_MTE_H */ diff --git a/sysdeps/generic/libc-mtag.h b/sysdeps/generic/libc-mtag.h deleted file mode 100644 index 5477bfa17f..0000000000 --- a/sysdeps/generic/libc-mtag.h +++ /dev/null @@ -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 */