claudiu.zissulescu-iancule...@oracle.com writes: > From: Claudiu Zissulescu <claudiu.zissulescu-iancule...@oracle.com> > > Memory tagging is used for detecting memory safety bugs. On AArch64, the > memory tagging extension (MTE) helps in reducing the overheads of memory > tagging: > - CPU: MTE instructions for efficiently tagging and untagging memory. > - Memory: New memory type, Normal Tagged Memory, added to the Arm > Architecture. > > The MEMory TAGging (MEMTAG) sanitizer uses the same infrastructure as > HWASAN. MEMTAG and HWASAN are both hardware-assisted solutions, and > rely on the same sanitizer machinery in parts. So, define new > constructs that allow MEMTAG and HWASAN to share the infrastructure: > > - hwassist_sanitize_p () is true when either SANITIZE_MEMTAG or > SANITIZE_HWASAN is true. > - hwassist_sanitize_stack_p () is when hwassist_sanitize_p () and > stack variables are to be sanitized. > > MEMTAG and HWASAN do have differences, however, and hence, the need to > conditionalize using memtag_sanitize_p () in the relevant places. E.g., > > - Instead of generating the libcall __hwasan_tag_memory, MEMTAG needs > to invoke the target-specific hook TARGET_MEMTAG_TAG_MEMORY to tag > memory. Similar approach can be seen for handling > handle_builtin_alloca, where instead of doing the gimple > transformations, target hooks are used. > > - Add a new internal function HWASAN_ALLOCA_POISON to handle > dynamically allocated stack when MEMTAG sanitizer is enabled. At > expansion, this allows to, in turn, invoke target-hooks to increment > tag, and use the generated tag to finally tag the dynamically allocated > memory. > > The usual pattern: > irg x0, x0, x0 > subg x0, x0, #16, #0 > creates a tag in x0 and so on. For alloca, we need to apply the > generated tag to the new sp. In absense of an extract tag insn, the > implemenation in expand_HWASAN_ALLOCA_POISON resorts to invoking irg > again. > > gcc/ChangeLog: > > * asan.cc (handle_builtin_stack_restore): Accommodate MEMTAG > sanitizer. > (handle_builtin_alloca): Expand differently if MEMTAG sanitizer. > (get_mem_refs_of_builtin_call): Include MEMTAG along with > HWASAN. > (memtag_sanitize_stack_p): New definition. > (memtag_sanitize_allocas_p): Likewise. > (memtag_memintrin): Likewise. > (hwassist_sanitize_p): Likewise. > (hwassist_sanitize_stack_p): Likewise. > (report_error_func): Include MEMTAG along with HWASAN. > (build_check_stmt): Likewise. > (instrument_derefs): MEMTAG too does not deal with globals yet. > (instrument_builtin_call): > (maybe_instrument_call): Include MEMTAG along with HWASAN. > (asan_expand_mark_ifn): Likewise. > (asan_expand_check_ifn): Likewise. > (asan_expand_poison_ifn): Expand differently if MEMTAG sanitizer. > (asan_instrument): > (hwasan_frame_base): > (hwasan_record_stack_var): > (hwasan_emit_prologue): Expand differently if MEMTAG sanitizer. > (hwasan_emit_untag_frame): Likewise. > * asan.h (hwasan_record_stack_var): > (memtag_sanitize_stack_p): New declaration. > (memtag_sanitize_allocas_p): Likewise. > (hwassist_sanitize_p): Likewise. > (hwassist_sanitize_stack_p): Likewise. > (asan_sanitize_use_after_scope): Include MEMTAG along with > HWASAN. > * cfgexpand.cc (align_local_variable): Likewise. > (expand_one_stack_var_at): Likewise. > (expand_stack_vars): Likewise. > (expand_one_stack_var_1): Likewise. > (init_vars_expansion): Likewise. > (expand_used_vars): Likewise. > (pass_expand::execute): Likewise. > * gimplify.cc (asan_poison_variable): Likewise. > * internal-fn.cc (expand_HWASAN_ALLOCA_POISON): New definition. > (expand_HWASAN_ALLOCA_UNPOISON): Expand differently if MEMTAG > sanitizer. > (expand_HWASAN_MARK): Likewise. > * internal-fn.def (HWASAN_ALLOCA_POISON): Define new. > * params.opt: Document new param. FIXME. > * sanopt.cc (pass_sanopt::execute): Include MEMTAG along with > HWASAN. > * gcc.c (sanitize_spec_function): Add check for memtag-stack.
Some trivial comments below, but otherwise it looks good to me. > > Co-authored-by: Indu Bhagat <indu.bha...@oracle.com> > Signed-off-by: Claudiu Zissulescu <claudiu.zissulescu-iancule...@oracle.com> > --- > gcc/asan.cc | 214 +++++++++++++++++++++++++++++++++----------- > gcc/asan.h | 10 ++- > gcc/cfgexpand.cc | 29 +++--- > gcc/gcc.cc | 2 + > gcc/gimplify.cc | 5 +- > gcc/internal-fn.cc | 68 ++++++++++++-- > gcc/internal-fn.def | 1 + > gcc/params.opt | 4 + > gcc/sanopt.cc | 2 +- > 9 files changed, 258 insertions(+), 77 deletions(-) > > diff --git a/gcc/asan.cc b/gcc/asan.cc > index 748b289d6f9..711e6a71eee 100644 > --- a/gcc/asan.cc > +++ b/gcc/asan.cc > @@ -762,14 +762,15 @@ static void > handle_builtin_stack_restore (gcall *call, gimple_stmt_iterator *iter) > { > if (!iter > - || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ())) > + || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p () > + || memtag_sanitize_allocas_p ())) > return; > > tree restored_stack = gimple_call_arg (call, 0); > > gimple *g; > > - if (hwasan_sanitize_allocas_p ()) > + if (hwasan_sanitize_allocas_p () || memtag_sanitize_allocas_p ()) > { > enum internal_fn fn = IFN_HWASAN_ALLOCA_UNPOISON; > /* There is only one piece of information > `expand_HWASAN_ALLOCA_UNPOISON` > @@ -818,7 +819,8 @@ static void > handle_builtin_alloca (gcall *call, gimple_stmt_iterator *iter) > { > if (!iter > - || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p ())) > + || !(asan_sanitize_allocas_p () || hwasan_sanitize_allocas_p () > + || memtag_sanitize_allocas_p ())) > return; > > gassign *g; > @@ -842,23 +844,31 @@ handle_builtin_alloca (gcall *call, > gimple_stmt_iterator *iter) > e = find_fallthru_edge (gsi_bb (*iter)->succs); > } > > - if (hwasan_sanitize_allocas_p ()) > + if (hwasan_sanitize_allocas_p () || memtag_sanitize_allocas_p ()) > { > gimple_seq stmts = NULL; > location_t loc = gimple_location (gsi_stmt (*iter)); > - /* > - HWASAN needs a different expansion. > + /* HWASAN and MEMTAG need a different expansion. > > addr = __builtin_alloca (size, align); > > - should be replaced by > + in case of HWASAN, should be replaced by > > new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment; > untagged_addr = __builtin_alloca (new_size, align); > tag = __hwasan_choose_alloca_tag (); > addr = ifn_HWASAN_SET_TAG (untagged_addr, tag); > __hwasan_tag_memory (untagged_addr, tag, new_size); > - */ > + > + in case of MEMTAG, should be replaced by > + > + new_size = size rounded up to HWASAN_TAG_GRANULE_SIZE byte alignment; > + untagged_addr = __builtin_alloca (new_size, align); > + addr = ifn_HWASAN_ALLOCA_POISON (untagged_addr, new_size); > + > + where a new tag is chosen and set on untagged_addr when > + HWASAN_ALLOCA_POISON is expanded. */ > + > /* Ensure alignment at least HWASAN_TAG_GRANULE_SIZE bytes so we start > on > a tag granule. */ > align = align > HWASAN_TAG_GRANULE_SIZE ? align : > HWASAN_TAG_GRANULE_SIZE; > @@ -874,23 +884,30 @@ handle_builtin_alloca (gcall *call, > gimple_stmt_iterator *iter) > as_combined_fn (BUILT_IN_ALLOCA_WITH_ALIGN), ptr_type, > new_size, build_int_cst (size_type_node, align)); > > - /* Choose the tag. > - Here we use an internal function so we can choose the tag at expand > - time. We need the decision to be made after stack variables have been > - assigned their tag (i.e. once the hwasan_frame_tag_offset variable has > - been set to one after the last stack variables tag). */ > - tree tag = gimple_build (&stmts, loc, CFN_HWASAN_CHOOSE_TAG, > - unsigned_char_type_node); > - > - /* Add tag to pointer. */ > - tree addr > - = gimple_build (&stmts, loc, CFN_HWASAN_SET_TAG, ptr_type, > - untagged_addr, tag); > + tree addr; > > - /* Tag shadow memory. > - NOTE: require using `untagged_addr` here for libhwasan API. */ > - gimple_build (&stmts, loc, as_combined_fn (BUILT_IN_HWASAN_TAG_MEM), > - void_type_node, untagged_addr, tag, new_size); > + if (memtag_sanitize_p ()) > + addr = gimple_build (&stmts, loc, CFN_HWASAN_ALLOCA_POISON, ptr_type, > + untagged_addr, new_size); > + else > + { > + /* Choose the tag. > + Here we use an internal function so we can choose the tag at expand > + time. We need the decision to be made after stack variables have > been > + assigned their tag (i.e. once the hwasan_frame_tag_offset variable > has > + been set to one after the last stack variables tag). */ > + tree tag = gimple_build (&stmts, loc, CFN_HWASAN_CHOOSE_TAG, > + unsigned_char_type_node); > + > + /* Add tag to pointer. */ > + addr = gimple_build (&stmts, loc, CFN_HWASAN_SET_TAG, ptr_type, > + untagged_addr, tag); > + > + /* Tag shadow memory. > + NOTE: require using `untagged_addr` here for libhwasan API. */ > + gimple_build (&stmts, loc, as_combined_fn (BUILT_IN_HWASAN_TAG_MEM), > + void_type_node, untagged_addr, tag, new_size); > + } > > /* Insert the built up code sequence into the original instruction > stream > the iterator points to. */ > @@ -1104,7 +1121,7 @@ get_mem_refs_of_builtin_call (gcall *call, > for now we choose to just ignore `strlen` calls. > This decision was simply made because that means the special case is > limited to this one case of this one function. */ > - if (hwasan_sanitize_p ()) > + if (hwassist_sanitize_p ()) > return false; > source0 = gimple_call_arg (call, 0); > len = gimple_call_lhs (call); > @@ -1886,6 +1903,83 @@ hwasan_memintrin (void) > return (hwasan_sanitize_p () && param_hwasan_instrument_mem_intrinsics); > } > > +/* MEMoryTAGging sanitizer (MEMTAG) uses a hardware based capability known as > + memory tagging to detect memory safety vulnerabilities. Similar to > HWASAN, > + it is also a probabilistic method. This last sentence is such a key point that I think we should mention it in the command-line documentation. It might help to discourage people from filing CVEs about being able to inject memory faults even when MTE is enabled. > + > + MEMTAG relies on the optional extension in armv8.5a, known as MTE (Memory Nit: seems more correct without the comma > + Tagging Extension). The extension is available in AARCH64 only and AArch64 > + introduces two types of tags: > + - Logical Address Tag - bits 56-59 (TARGET_MEMTAG_TAG_BITSIZE) of the > + virtual address. > + - Allocation Tag - 4 bits for each tag granule > (TARGET_MEMTAG_GRANULE_SIZE > + set to 16 bytes), stored separately. > + Load / store instructions raise an exception if tags differ, thereby > + providing a faster way (than HWASAN) to detect memory safety issues. > + Further, new instructions are available in MTE to manipulate (generate, > + update address with) tags. Load / store instructions with SP base > register > + and immediate offset do not check tags. > + > + PS: Currently, MEMTAG sanitizer is capable of stack (variable / memory) > + tagging only. > + > + In general, detecting stack-related memory bugs requires the compiler to: > + - ensure that each tag granule is only used by one variable at a time. > + This includes alloca. > + - Tag/Color: put tags into each stack variable pointer. > + - Untag: the function epilogue will retag the memory. > + > + MEMTAG sanitizer is based off the HWASAN sanitizer implementation > + internally. Similar to HWASAN: > + - Assigning an independently random tag to each variable is carried out > by > + keeping a tagged base pointer. A tagged base pointer allows > addressing > + variables with (addr offset, tag offset). > + */ > [...] > @@ -4725,15 +4826,20 @@ hwasan_emit_untag_frame (rtx dynamic, rtx vars) > bot_rtx = vars; > } > > - rtx size_rtx = expand_simple_binop (ptr_mode, MINUS, top_rtx, bot_rtx, > - NULL_RTX, /* unsignedp = */0, > - OPTAB_DIRECT); > + rtx size_rtx = simplify_gen_binary (MINUS, ptr_mode, top_rtx, bot_rtx); > + if (!CONST_INT_P (size_rtx)) > + size_rtx = force_reg (ptr_mode, size_rtx); Hmm, yeah, that looks reasonable. > > - rtx fn = init_one_libfunc ("__hwasan_tag_memory"); > - emit_library_call (fn, LCT_NORMAL, VOIDmode, > - bot_rtx, ptr_mode, > - HWASAN_STACK_BACKGROUND, QImode, > - size_rtx, ptr_mode); > + if (memtag_sanitize_p ()) > + emit_insn (targetm.gen_tag_memory (bot_rtx, HWASAN_STACK_BACKGROUND, > size_rtx)); Nit: long line. Thanks, Richard > + else > + { > + rtx fn = init_one_libfunc ("__hwasan_tag_memory"); > + emit_library_call (fn, LCT_NORMAL, VOIDmode, > + bot_rtx, ptr_mode, > + HWASAN_STACK_BACKGROUND, QImode, > + size_rtx, ptr_mode); > + } > > do_pending_stack_adjust (); > return end_sequence (); > [...]