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 ();
> [...]

Reply via email to