From 765936bb9b531f528bab8c038ffdf508da18c7e8 Mon Sep 17 00:00:00 2001 From: Indu Bhagat Date: Wed, 9 Jul 2025 15:33:14 +0300 Subject: [PATCH] asan: add new memtag sanitizer Add new command line option -fsanitize=memtag-stack with the following new params: --param memtag-instrument-alloca [0,1] (default 1) to use MTE insns for enabling dynamic checking of stack allocas. Along with the new SANITIZE_MEMTAG_STACK, define a SANITIZE_MEMTAG which will be set if any kind of memtag sanitizer is in effect (e.g., later we may add -fsanitize=memtag-globals). Add errors to convey that memtag sanitizer does not work with hwaddress and address sanitizers. Also error out if memtag ISA extension is not enabled. MEMTAG sanitizer will use the HWASAN machinery, but with a few differences: - The tags are always generated at runtime by the hardware, so -fsanitize=memtag-stack enforces a --param hwasan-random-frame-tag=1 Add documentation in gcc/doc/invoke.texi. gcc/ * builtins.def: Adjust the macro to include the new SANTIZIE_MEMTAG_STACK. * flag-types.h (enum sanitize_code): Add new enumerator for SANITIZE_MEMTAG and SANITIZE_MEMTAG_STACK. * opts.cc (finish_options): memtag-stack sanitizer conflicts with hwaddress and address sanitizers. (sanitizer_opts): Add new memtag-stack sanitizer. (parse_sanitizer_options): memtag-stack sanitizer cannot recover. * params.opt: Add new params for memtag-stack sanitizer. * doc/invoke.texi: Update documentation. Signed-off-by: Claudiu Zissulescu Co-authored-by: Claudiu Zissulescu --- gcc/builtins.def | 1 + gcc/doc/invoke.texi | 14 +++++++++++++- gcc/flag-types.h | 4 ++++ gcc/opts.cc | 22 +++++++++++++++++++++- gcc/params.opt | 4 ++++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/gcc/builtins.def b/gcc/builtins.def index 7cd5353bcb1..714abb9cbc7 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -257,6 +257,7 @@ along with GCC; see the file COPYING3. If not see true, true, true, ATTRS, true, \ (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \ | SANITIZE_HWADDRESS \ + | SANITIZE_MEMTAG_STACK \ | SANITIZE_UNDEFINED \ | SANITIZE_UNDEFINED_NONDEFAULT) \ || flag_sanitize_coverage)) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 4635d860a62..3ee16f01651 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -17698,7 +17698,7 @@ When using stack instrumentation, decide tags for stack variables using a deterministic sequence beginning at a random tag for each frame. With this parameter unset tags are chosen using the same sequence but beginning from 1. This is enabled by default for @option{-fsanitize=hwaddress} and unavailable -for @option{-fsanitize=kernel-hwaddress}. +for @option{-fsanitize=kernel-hwaddress} and @option{-fsanitize=memtag-stack}. To disable it use @option{--param hwasan-random-frame-tag=0}. @item hwasan-instrument-allocas @@ -17731,6 +17731,11 @@ and @option{-fsanitize=kernel-hwaddress}. To disable instrumentation of builtin functions use @option{--param hwasan-instrument-mem-intrinsics=0}. +@item memtag-instrument-allocas +Enable hardware-assisted memory tagging of dynamically sized stack-allocated +variables. This kind of code generation is enabled by default when using +@option{-fsanitize=memtag-stack}. + @item use-after-scope-direct-emission-threshold If the size of a local variable in bytes is smaller or equal to this number, directly poison (or unpoison) shadow memory instead of using @@ -18684,6 +18689,13 @@ possible by specifying the command-line options @option{--param hwasan-instrument-allocas=1} respectively. Using a random frame tag is not implemented for kernel instrumentation. +@opindex fsanitize=memtag-stack +@item -fsanitize=memtag-stack +Use Memory Tagging Extension instructions instead of instrumentation +to allow the detection of memory errors. Similar to HWASAN, it is +also a probabilistic method. This option is available only on those +AArch64 architectures that support Memory Tagging Extensions. + @opindex fsanitize=pointer-compare @item -fsanitize=pointer-compare Instrument comparison operation (<, <=, >, >=) with pointer operands. diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 44a90becb27..f7cb3a2be41 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -338,6 +338,10 @@ enum sanitize_code { SANITIZE_KERNEL_HWADDRESS = 1UL << 30, /* Shadow Call Stack. */ SANITIZE_SHADOW_CALL_STACK = 1UL << 31, + /* Memory Tagging for Stack. */ + SANITIZE_MEMTAG_STACK = 1ULL << 32, + /* Memory Tagging. */ + SANITIZE_MEMTAG = SANITIZE_MEMTAG_STACK, SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN diff --git a/gcc/opts.cc b/gcc/opts.cc index abbf7cedff3..b9378a96dd2 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -1319,6 +1319,24 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set, report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_ADDRESS, SANITIZE_KERNEL_ADDRESS); + /* Sanitizers using Memory-Tagging Extension conflict with HWASAN and + ASAN. */ + report_conflicting_sanitizer_options (opts, loc, SANITIZE_MEMTAG, + SANITIZE_HWADDRESS); + report_conflicting_sanitizer_options (opts, loc, SANITIZE_MEMTAG, + SANITIZE_ADDRESS); + + /* Memtag sanitizer implies HWASAN but with tags always generated by + the hardware randomly. */ + if ((opts->x_flag_sanitize & SANITIZE_MEMTAG_STACK) + && opts->x_param_hwasan_random_frame_tag == 0) + { + warning_at (loc, OPT_fsanitize_, + "%<--param hwasan-random-frame-tag=0%> is ignored when " + "%<-fsanitize=memtag-stack%> is present"); + opts->x_param_hwasan_random_frame_tag = 1; + } + /* Check error recovery for -fsanitize-recover option. */ for (int i = 0; sanitizer_opts[i].name != NULL; ++i) if ((opts->x_flag_sanitize_recover & sanitizer_opts[i].flag) @@ -2183,6 +2201,7 @@ const struct sanitizer_opts_s sanitizer_opts[] = SANITIZER_OPT (pointer-overflow, SANITIZE_POINTER_OVERFLOW, true, true), SANITIZER_OPT (builtin, SANITIZE_BUILTIN, true, true), SANITIZER_OPT (shadow-call-stack, SANITIZE_SHADOW_CALL_STACK, false, false), + SANITIZER_OPT (memtag-stack, SANITIZE_MEMTAG_STACK, false, false), SANITIZER_OPT (all, ~sanitize_code_type (0), true, true), #undef SANITIZER_OPT { NULL, sanitize_code_type (0), 0UL, false, false } @@ -2321,7 +2340,8 @@ parse_sanitizer_options (const char *p, location_t loc, int scode, else if (code == OPT_fsanitize_recover_) flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK | SANITIZE_UNREACHABLE | SANITIZE_RETURN - | SANITIZE_SHADOW_CALL_STACK); + | SANITIZE_SHADOW_CALL_STACK + | SANITIZE_MEMTAG_STACK); else /* if (code == OPT_fsanitize_trap_) */ flags |= (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT); diff --git a/gcc/params.opt b/gcc/params.opt index ad5ee887367..cf3b387ad7b 100644 --- a/gcc/params.opt +++ b/gcc/params.opt @@ -98,6 +98,10 @@ Enable hwasan instrumentation of store operations. Common Joined UInteger Var(param_hwasan_instrument_mem_intrinsics) Init(1) IntegerRange(0, 1) Param Optimization Enable hwasan instrumentation of builtin functions. +-param=memtag-instrument-allocas= +Target Joined UInteger Var(param_memtag_instrument_allocas) Init(1) IntegerRange(0, 1) Param +When sanitizing using MTE instructions, add checks for all stack allocas. + -param=avg-loop-niter= Common Joined UInteger Var(param_avg_loop_niter) Init(10) IntegerRange(1, 65536) Param Optimization Average number of iterations of a loop. -- 2.47.3