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

Message ID 20260310141107.43347-3-claudiu.zissulescu-ianculescu@oracle.com (mailing list archive)
State Changes Requested
Headers
Series Enable MTE support for stack |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 fail Test failed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed
redhat-pt-bot/TryBot-32bit success Build for i686

Commit Message

Claudiu Zissulescu March 10, 2026, 2:11 p.m. UTC
  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.

Signed-off-by: Claudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com>
---
 configure                       |  36 +++++++++++
 configure.ac                    |  17 +++++
 sysdeps/aarch64/Makefile        |   9 +++
 sysdeps/aarch64/__longjmp.S     |  33 ++++++++++
 sysdeps/aarch64/tst-mte-jmp.c   | 108 ++++++++++++++++++++++++++++++++
 sysdeps/aarch64/tst-mte-stack.c |  74 ++++++++++++++++++++++
 6 files changed, 277 insertions(+)
 create mode 100644 sysdeps/aarch64/tst-mte-jmp.c
 create mode 100644 sysdeps/aarch64/tst-mte-stack.c
  

Comments

Yury Khrustalev March 11, 2026, 10:54 a.m. UTC | #1
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
  
Yury Khrustalev March 11, 2026, 11:22 a.m. UTC | #2
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.
> 
> ...
>
> 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
>
> ...
>
> +static int
> +do_test (void)
> +{
> +  unsigned long hwcap2;
> +
> +  hwcap2 = getauxval (AT_HWCAP2);
> +  if ((hwcap2 & HWCAP2_MTE) == 0)
> +    return EXIT_UNSUPPORTED;

This check is too late because by the time main() is called, we already
have

  tst-mte-stack: error while loading shared libraries:
    MTE Stack: cannot set stack with PROT_MTE: Invalid argument

on a system without MTE support. Same for another test.

Thanks,
Yury
  
Claudiu Zissulescu March 12, 2026, 10:36 a.m. UTC | #3
>>
>> 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.

I can add a different one. However, the above flag needs to be reworked 
as the name and its documentation is too wide.

>> +      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?
> 
Yes.


>> +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
> 
Noted.

>> +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?

gcc16 complains about the sink value not being use. I'll find a better 
way to deal with that.

> 
>> +LDFLAGS-tst-mte-stack += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=sync
> 
> What about testing sync mode?

We can have the same tests executed with async mode too. However, this 
patch doesn't test the hardware capabilities, but the feature of 
handling mte enabled stacks.

> +
>> +#undef count
>> +#undef tmp
>> +#endif
>> +
> 
> Is this guaranteed to work with stack tagging implemented by other
> compilers? Is there a spec for this ABI?

The above is design to handle the output of the only compiler which can 
generate MTE and compile glibc which I know. Please, let me know if 
there are any other compilers that do that, as well as their 
documentation covering their MTE implementation.


>> +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?

The tag is set by the gcc compiler via -fsanitize=memtag-stack. The 
compiler has it's own set of tests that is checking if it does a good 
job setting the tags. Checking if the compiler does its job here seems 
to me a bit over-engineering.

> 
> Should we also check alloca() result?

I can add a test for alloca. The master plan is the same.

> 
>> +  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.

I can add a message.

Best wishes,
Claudiu
  
Claudiu Zissulescu March 12, 2026, 10:37 a.m. UTC | #4
>> +static int
>> +do_test (void)
>> +{
>> +  unsigned long hwcap2;
>> +
>> +  hwcap2 = getauxval (AT_HWCAP2);
>> +  if ((hwcap2 & HWCAP2_MTE) == 0)
>> +    return EXIT_UNSUPPORTED;
> 
> This check is too late because by the time main() is called, we already
> have
> 
>    tst-mte-stack: error while loading shared libraries:
>      MTE Stack: cannot set stack with PROT_MTE: Invalid argument
> 
> on a system without MTE support. Same for another test.
> 

The above error is given by the dynamic loader, and it will do so for 
any attempt to load and execute an app which sets DT 
_AARCH64_MEMTAG_STACK dynamic tag. Without the dynamic linker check, an 
MTE app, most probably, will just fail with an unrecognized instruction 
exception.

This is happening when one is forcing the execution of the tests in a 
system without MTE support by configuring the glibc build with 
--enable-memory-tagging.

Best wishes,
Claudiu
  
Yury Khrustalev March 16, 2026, 9:16 a.m. UTC | #5
On Thu, Mar 12, 2026 at 12:37:07PM +0200, Claudiu Zissulescu wrote:
> > > +static int
> > > +do_test (void)
> > > +{
> > > +  unsigned long hwcap2;
> > > +
> > > +  hwcap2 = getauxval (AT_HWCAP2);
> > > +  if ((hwcap2 & HWCAP2_MTE) == 0)
> > > +    return EXIT_UNSUPPORTED;
> > 
> > This check is too late because by the time main() is called, we already
> > have
> > 
> >    tst-mte-stack: error while loading shared libraries:
> >      MTE Stack: cannot set stack with PROT_MTE: Invalid argument
> > 
> > on a system without MTE support. Same for another test.
> > 
> 
> The above error is given by the dynamic loader, and it will do so for any
> attempt to load and execute an app which sets DT _AARCH64_MEMTAG_STACK
> dynamic tag. Without the dynamic linker check, an MTE app, most probably,
> will just fail with an unrecognized instruction exception.
> 
> This is happening when one is forcing the execution of the tests in a system
> without MTE support by configuring the glibc build with
> --enable-memory-tagging.

Either way, a test, that is built for some configuration, should run,
and if current runtime platform does not support some feature that is
required for this test, the test should return UNSUPPORTED rather than
break the build.

Thanks,
Yury
  
Yury Khrustalev March 16, 2026, 9:21 a.m. UTC | #6
On Thu, Mar 12, 2026 at 12:36:48PM +0200, Claudiu Zissulescu wrote:
> > > 
> > > 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.
> 
> I can add a different one. However, the above flag needs to be reworked as
> the name and its documentation is too wide.
> 
> > > +      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?
> > 
> Yes.
> 
> 
> > > +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
> > 
> Noted.
> 
> > > +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?
> 
> gcc16 complains about the sink value not being use. I'll find a better way
> to deal with that.
> 
> > 
> > > +LDFLAGS-tst-mte-stack += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=sync
> > 
> > What about testing sync mode?
> 
> We can have the same tests executed with async mode too. However, this patch
> doesn't test the hardware capabilities, but the feature of handling mte
> enabled stacks.

The patch contains code that processes sync and async use cases, but we
only test one part of this code. I was merely pointing out that we can
increase test coverage here.

> 
> > +
> > > +#undef count
> > > +#undef tmp
> > > +#endif
> > > +
> > 
> > Is this guaranteed to work with stack tagging implemented by other
> > compilers? Is there a spec for this ABI?
> 
> The above is design to handle the output of the only compiler which can
> generate MTE and compile glibc which I know. Please, let me know if there
> are any other compilers that do that, as well as their documentation
> covering their MTE implementation.

To be clear, what I was trying to point out here was that I think this code
is, in fact, describing some new ABI, and that to me this means that we should
have some form of documentation describing this ABI.

The best way to do that would be to get this added to the Arm ABI document
along with other quirks related to setjmp / longjmp.

We at Arm can help you describe this but you are in the best position to
explain to us what you need from this MTE use case.

Thanks,
Yury
  

Patch

diff --git a/configure b/configure
index 0841355583..ddb2afd381 100755
--- a/configure
+++ b/configure
@@ -9406,6 +9406,42 @@  have-libgcc_s = $libc_cv_have_libgcc_s"
 
 
 
+# Determine whether the compiler can do stack sanitization using
+# memtags.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -fsanitize=memtag-stack" >&5
+printf %s "checking for -fsanitize=memtag-stack... " >&6; }
+if test ${libc_cv_memtag_stack+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) libc_cv_memtag_stack=no
+if test "$memory_tagging" = yes; then
+  # Only available on architectures that support it.
+  case $host_cpu in
+    aarch64)
+      if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS -Werror -fsanitize=memtag-stack -march=armv8.5-a+memtag -xc /dev/null -S -o /dev/null'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+then :
+  libc_cv_memtag_stack=yes
+else case e in #(
+  e) libc_cv_memtag_stack=no ;;
+esac
+fi
+      ;;
+  esac
+fi
+ ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_memtag_stack" >&5
+printf "%s\n" "$libc_cv_memtag_stack" >&6; }
+config_vars="$config_vars
+have-memtag-sanitizer = $libc_cv_memtag_stack"
+
 enable_gsframe=no
 if test $use_sframe = yes; then
   # SFrame requires to be explicit enabled by the architecture
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],
+		     [libc_cv_memtag_stack=yes],
+		     [libc_cv_memtag_stack=no])
+      ;;
+  esac
+fi
+])
+LIBC_CONFIG_VAR([have-memtag-sanitizer], [$libc_cv_memtag_stack])
+
 enable_gsframe=no
 if test $use_sframe = yes; then
   # SFrame requires to be explicit enabled by the architecture
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
+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
+LDFLAGS-tst-mte-stack += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=sync
+endif
 endif
 
 ifeq ($(subdir),malloc)
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
@@ -25,6 +25,11 @@ 
 
 ENTRY (__longjmp)
 
+#ifdef USE_MTAG
+	.arch armv8.5-a
+        .arch_extension memtag
+#endif
+
 #if IS_IN(libc)
 	/* Disable ZA state of SME in libc.a and libc.so, but not in ld.so.  */
 	CALL_LIBC_ARM_ZA_DISABLE
@@ -136,6 +141,34 @@  L(gcs_done):
 #ifdef PTR_DEMANGLE
 	PTR_DEMANGLE (x4, x4, x3)
 #endif
+
+#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
+
 	mov	sp, x4
 
 	/* longjmp_target probe takes 3 arguments, address of jump buffer
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
@@ -0,0 +1,108 @@ 
+/* Test for longjmp when we use MTE stack sanitation.
+   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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+#include <ucontext.h>
+#include <signal.h>
+
+#include <sys/auxv.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+
+static jmp_buf jumpBuffer;
+
+/* Exception handler for MTE memory fails.  */
+void
+handler (int nSig)
+{
+  /* We hit the exception.  Return error.  */
+  FAIL_EXIT1 ("MTE exception hit");
+}
+
+/* Register an exception handler.  */
+static void
+setHandler (void)
+{
+  signal (SIGSEGV, handler);
+}
+
+/* The longjmp call is an abnormal exit, thus it lets the stack memory
+   mte dirty.  No exception is thrown here.  */
+__attribute__((noinline))
+static void
+foo (void)
+{
+  volatile unsigned char x[15];
+  volatile unsigned char *ptr = &x[0];
+
+  for (int i = 0; i < sizeof (x); ++i)
+    ptr[i] = 0xcc;
+
+  longjmp (jumpBuffer, 1);  // simulate throw
+}
+
+
+/* Reads data, from a pointer of a given size.  No MTE sanitization
+   for this one, any dirty memory should trap here.  */
+__attribute__((noinline, no_sanitize("memtag-stack")))
+static void
+hwasan_read(char *p, int size) {
+  volatile char __attribute__ ((unused)) sink;
+  for (int i = 0; i < size; ++i)
+    sink = p[i];
+}
+
+/* Creates a local array, large enough to include any previously tag
+   dirty stack memory.  */
+__attribute__((noinline, no_sanitize("memtag-stack")))
+static void
+after_catch (void)
+{
+  char x[10000];
+  hwasan_read (&x[0], sizeof(x));
+}
+
+__attribute__((noinline))
+static void f (void) {
+  if (setjmp (jumpBuffer) == 0) {
+    foo ();
+  } else {
+    after_catch ();
+  }
+}
+
+static int
+do_test (void)
+{
+  unsigned long hwcap2;
+
+  hwcap2 = getauxval (AT_HWCAP2);
+  if ((hwcap2 & HWCAP2_MTE) == 0)
+    return EXIT_UNSUPPORTED;
+
+  setHandler ();
+  f ();
+  return 0;
+}
+
+#include <support/test-driver.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
@@ -0,0 +1,74 @@ 
+/* Test for longjmp when we use MTE stack sanitation.
+   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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucontext.h>
+#include <signal.h>
+
+#include <sys/auxv.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+
+/* 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];
+  unsigned long hwcap2;
+
+  hwcap2 = getauxval (AT_HWCAP2);
+  if ((hwcap2 & HWCAP2_MTE) == 0)
+    return EXIT_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>