[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