[v3,2/2] aarch64: Add memory tagging support for setjmp/longjmp routines
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
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
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
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
>>
>> 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
>> +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
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
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
@@ -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
@@ -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
@@ -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)
@@ -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
new file mode 100644
@@ -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>
new file mode 100644
@@ -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>