[PATCH v3 2/2] aarch64: Add memory tagging support for setjmp/longjmp routines

Yury Khrustalev yury.khrustalev@arm.com
Wed Mar 11 10:54:04 GMT 2026


On Tue, Mar 10, 2026 at 04:11:07PM +0200, claudiu.zissulescu-ianculescu@oracle.com wrote:
> From: Claudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com>
> 
> When we perform stack sanitization using AArch64 memory tagging
> instructions, there may be instances where the tagged memory stack is
> not properly cleaned. Consequently, any further use of this memory
> could lead to false tag mismatch exceptions.
> 
> One situation where this can occur is when a sanitized function
> terminates exceptionally, as in the case of a longjmp. Therefore, when
> executing a longjmp, it's essential to clean the tagged stack memory.
> 
> This patch modifies the longjmp implementation by adding stack cleanup
> operations, a corresponding test, and necessary configuration
> adjustments to check if stack sanitization using memory tagging is
> supported by the compiler.
> 
> This patch relies on the USE_MTAG macro, which must be defined through
> the --enable-memory-tagging option.

This flag also enables heap tagging in malloc. We probably shouldn't
enable both with the same flag as the use cases could be quite
different.

>
> ...
>
> diff --git a/configure b/configure
> index 0841355583..ddb2afd381 100755
> --- a/configure
> +++ b/configure

>
> ...
>
> diff --git a/configure.ac b/configure.ac
> index ea81b0ea62..cb74188bf9 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -2148,6 +2148,23 @@ LIBC_CONFIG_VAR([have-libgcc_s], [$libc_cv_have_libgcc_s])
>  AC_SUBST(libc_cv_test_cc_mprefer_vector_width)
>  AC_SUBST(test_enable_cet)
>  
> +# Determine whether the compiler can do stack sanitization using
> +# memtags.
> +AC_CACHE_CHECK(for -fsanitize=memtag-stack, libc_cv_memtag_stack, [dnl
> +libc_cv_memtag_stack=no
> +if test "$memory_tagging" = yes; then
> +  # Only available on architectures that support it.
> +  case $host_cpu in
> +    aarch64)
> +      LIBC_TRY_CC_OPTION([$CFLAGS $CPPFLAGS -Werror -fsanitize=memtag-stack -march=armv8.5-a+memtag],

This check fails on GCC 15.2.1. Will the support be added in GCC 16?

>
> ...
>
> diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
> index dfbc33d2f4..fcb3c60022 100644
> --- a/sysdeps/aarch64/Makefile
> +++ b/sysdeps/aarch64/Makefile
> @@ -86,6 +86,15 @@ tests-internal += \
>    # tests-internal
>  
>  $(objpfx)tst-sme-clone3: $(objpfx)clone3.o $(objpfx)__arm_za_disable.o
> +
> +ifeq (yes,$(have-memtag-sanitizer))
> +tests += tst-mte-jmp \
> +	tst-mte-stack

Nit: this usually should be written as

  +tests += \
  +  tst-mte-jmp \
  +  tst-mte-stack \
  +  # tests

> +CFLAGS-tst-mte-jmp.c += -fsanitize=memtag-stack -march=armv8.5-a+memtag
> +LDFLAGS-tst-mte-jmp += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=sync
> +CFLAGS-tst-mte-stack.c += -fsanitize=memtag-stack -march=armv8.5-a+memtag -w

Probably don't use -w? What are the warnings that need to be suppressed?

> +LDFLAGS-tst-mte-stack += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=sync

What about testing sync mode?

>
> ...
>
> diff --git a/sysdeps/aarch64/__longjmp.S b/sysdeps/aarch64/__longjmp.S
> index c4c8d72e51..62849babb8 100644
> --- a/sysdeps/aarch64/__longjmp.S
> +++ b/sysdeps/aarch64/__longjmp.S
>
> ...
>
> +
> +#ifdef USE_MTAG
> +#define count	x2
> +#define tmp	x2
> +
> +	subps	count, x4, sp
> +	beq	L(tag_done)
> +	tbz	count, 4, L(tag_clean32)
> +	stg	sp, [sp], 16
> +L(tag_clean32):
> +	tbz	count, 5, L(tag_clean64)
> +	st2g	sp, [sp], 32
> +L(tag_clean64):
> +	lsr	count, count, 6
> +	cbz	count, L(tag_done)
> +
> +	mov	tmp, sp
> +L(tag_loop):
> +	st2g	sp, [tmp], 32
> +	st2g	sp, [tmp], 32
> +	cmp	x4, tmp
> +	bhi	L(tag_loop)
> +L(tag_done):
> +
> +#undef count
> +#undef tmp
> +#endif
> +

Is this guaranteed to work with stack tagging implemented by other
compilers? Is there a spec for this ABI?

>
> ...
>
> diff --git a/sysdeps/aarch64/tst-mte-jmp.c b/sysdeps/aarch64/tst-mte-jmp.c
> new file mode 100644
> index 0000000000..664d872d0d
> --- /dev/null
> +++ b/sysdeps/aarch64/tst-mte-jmp.c

>
> ...
>
> diff --git a/sysdeps/aarch64/tst-mte-stack.c b/sysdeps/aarch64/tst-mte-stack.c
> new file mode 100644
> index 0000000000..16777f0c80
> --- /dev/null
> +++ b/sysdeps/aarch64/tst-mte-stack.c
>
> ...
>
> +/* Exception handler for MTE memory fails.  */
> +void
> +handler (int nSig)
> +{
> +  /* We hit the exception.  */
> +  exit (0);
> +}
> +
> +/* Register an exception handler.  */
> +static void
> +setHandler (void)
> +{
> +  signal (SIGSEGV, handler);
> +}
> +
> +void  __attribute__((noinline))
> +use (volatile unsigned char *ptr)
> +{
> +  ptr[0] = 0x41;
> +  ptr[1] = 0x42;
> +}
> +
> +static int
> +do_test (void)
> +{
> +  volatile unsigned char array[15];
> +  volatile unsigned char *ptr = &array[0];

Should there be a check for tag being set in this pointer to the memory
allocated on stack?

Should we also check alloca() result?

> +  unsigned long hwcap2;
> +
> +  hwcap2 = getauxval (AT_HWCAP2);
> +  if ((hwcap2 & HWCAP2_MTE) == 0)
> +    return EXIT_UNSUPPORTED;

Nit: it's usually helpful to print a message to tell why the test is
unsupported.

> +
> +  setHandler();
> +  use (ptr);
> +
> +  /* Write to memory beyond the 16 byte granule (offsest 0x10) MTE should
> +     generate an exception If the offset is less than 0x10 no SIGSEGV will
> +     occur.  */
> +  ptr[0x10] = 0x55;
> +  FAIL_EXIT1 ("MTE exception is not hit");
> +}
> +
> +#include <support/test-driver.c>
> -- 
> 2.53.0
> 

Kind regards,
Yury



More information about the Libc-alpha mailing list