Skip to content
Snippets Groups Projects
  • Matthew Malcomson's avatar
    0854b584
    libsanitizer: mid-end: Introduce stack variable handling for HWASAN · 0854b584
    Matthew Malcomson authored
    Handling stack variables has three features.
    
    1) Ensure HWASAN required alignment for stack variables
    
    When tagging shadow memory, we need to ensure that each tag granule is
    only used by one variable at a time.
    
    This is done by ensuring that each tagged variable is aligned to the tag
    granule representation size and also ensure that the end of each
    object is aligned to ensure the start of any other data stored on the
    stack is in a different granule.
    
    This patch ensures the above by forcing the stack pointer to be aligned
    before and after allocating any stack objects. Since we are forcing
    alignment we also use `align_local_variable` to ensure this new alignment
    is advertised properly through SET_DECL_ALIGN.
    
    2) Put tags into each stack variable pointer
    
    Make sure that every pointer to a stack variable includes a tag of some
    sort on it.
    
    The way tagging works is:
      1) For every new stack frame, a random tag is generated.
      2) A base register is formed from the stack pointer value and this
         random tag.
      3) References to stack variables are now formed with RTL describing an
         offset from this base in both tag and value.
    
    The random tag generation is handled by a backend hook.  This hook
    decides whether to introduce a random tag or use the stack background
    based on the parameter hwasan-random-frame-tag.  Using the stack
    background is necessary for testing and bootstrap.  It is necessary
    during bootstrap to avoid breaking the `configure` test program for
    determining stack direction.
    
    Using the stack background means that every stack frame has the initial
    tag of zero and variables are tagged with incrementing tags from 1,
    which also makes debugging a bit easier.
    
    Backend hooks define the size of a tag, the layout of the HWASAN shadow
    memory, and handle emitting the code that inserts and extracts tags from a
    pointer.
    
    3) For each stack variable, tag and untag the shadow stack on function
       prologue and epilogue.
    
    On entry to each function we tag the relevant shadow stack region for
    each stack variable. This stack region is tagged to match the tag added to
    each pointer to that variable.
    
    This is the first patch where we use the HWASAN shadow space, so we need
    to add in the libhwasan initialisation code that creates this shadow
    memory region into the binary we produce.  This instrumentation is done
    in `compile_file`.
    
    When exiting a function we need to ensure the shadow stack for this
    function has no remaining tags.  Without clearing the shadow stack area
    for this stack frame, later function calls could get false positives
    when those later function calls check untagged areas (such as parameters
    passed on the stack) against a shadow stack area with left-over tag.
    
    Hence we ensure that the entire stack frame is cleared on function exit.
    
    config/ChangeLog:
    
    	* bootstrap-hwasan.mk: Disable random frame tags for stack-tagging
    	during bootstrap.
    
    gcc/ChangeLog:
    
    	* asan.c (struct hwasan_stack_var): New.
    	(hwasan_sanitize_p): New.
    	(hwasan_sanitize_stack_p): New.
    	(hwasan_sanitize_allocas_p): New.
    	(initialize_sanitizer_builtins): Define new builtins.
    	(ATTR_NOTHROW_LIST): New macro.
    	(hwasan_current_frame_tag): New.
    	(hwasan_frame_base): New.
    	(stack_vars_base_reg_p): New.
    	(hwasan_maybe_init_frame_base_init): New.
    	(hwasan_record_stack_var): New.
    	(hwasan_get_frame_extent): New.
    	(hwasan_increment_frame_tag): New.
    	(hwasan_record_frame_init): New.
    	(hwasan_emit_prologue): New.
    	(hwasan_emit_untag_frame): New.
    	(hwasan_finish_file): New.
    	(hwasan_truncate_to_tag_size): New.
    	* asan.h (hwasan_record_frame_init): New declaration.
    	(hwasan_record_stack_var): New declaration.
    	(hwasan_emit_prologue): New declaration.
    	(hwasan_emit_untag_frame): New declaration.
    	(hwasan_get_frame_extent): New declaration.
    	(hwasan_maybe_enit_frame_base_init): New declaration.
    	(hwasan_frame_base): New declaration.
    	(stack_vars_base_reg_p): New declaration.
    	(hwasan_current_frame_tag): New declaration.
    	(hwasan_increment_frame_tag): New declaration.
    	(hwasan_truncate_to_tag_size): New declaration.
    	(hwasan_finish_file): New declaration.
    	(hwasan_sanitize_p): New declaration.
    	(hwasan_sanitize_stack_p): New declaration.
    	(hwasan_sanitize_allocas_p): New declaration.
    	(HWASAN_TAG_SIZE): New macro.
    	(HWASAN_TAG_GRANULE_SIZE): New macro.
    	(HWASAN_STACK_BACKGROUND): New macro.
    	* builtin-types.def (BT_FN_VOID_PTR_UINT8_PTRMODE): New.
    	* builtins.def (DEF_SANITIZER_BUILTIN): Enable for HWASAN.
    	* cfgexpand.c (align_local_variable): When using hwasan ensure
    	alignment to tag granule.
    	(align_frame_offset): New.
    	(expand_one_stack_var_at): For hwasan use tag offset.
    	(expand_stack_vars): Record stack objects for hwasan.
    	(expand_one_stack_var_1): Record stack objects for hwasan.
    	(init_vars_expansion): Initialise hwasan state.
    	(expand_used_vars): Emit hwasan prologue and generate hwasan epilogue.
    	(pass_expand::execute): Emit hwasan base initialization if needed.
    	* doc/tm.texi (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
    	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
    	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
    	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
    	* doc/tm.texi.in (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
    	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
    	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
    	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
    	* explow.c (get_dynamic_stack_base): Take new `base` argument.
    	* explow.h (get_dynamic_stack_base): Take new `base` argument.
    	* sanitizer.def (BUILT_IN_HWASAN_INIT): New.
    	(BUILT_IN_HWASAN_TAG_MEM): New.
    	* target.def (target_memtag_tag_size,target_memtag_granule_size,
    	target_memtag_insert_random_tag,target_memtag_add_tag,
    	target_memtag_set_tag,target_memtag_extract_tag,
    	target_memtag_untagged_pointer): New hooks.
    	* targhooks.c (HWASAN_SHIFT): New.
    	(HWASAN_SHIFT_RTX): New.
    	(default_memtag_tag_size): New default hook.
    	(default_memtag_granule_size): New default hook.
    	(default_memtag_insert_random_tag): New default hook.
    	(default_memtag_add_tag): New default hook.
    	(default_memtag_set_tag): New default hook.
    	(default_memtag_extract_tag): New default hook.
    	(default_memtag_untagged_pointer): New default hook.
    	* targhooks.h (default_memtag_tag_size): New default hook.
    	(default_memtag_granule_size): New default hook.
    	(default_memtag_insert_random_tag): New default hook.
    	(default_memtag_add_tag): New default hook.
    	(default_memtag_set_tag): New default hook.
    	(default_memtag_extract_tag): New default hook.
    	(default_memtag_untagged_pointer): New default hook.
    	* toplev.c (compile_file): Call hwasan_finish_file when finished.
    0854b584
    History
    libsanitizer: mid-end: Introduce stack variable handling for HWASAN
    Matthew Malcomson authored
    Handling stack variables has three features.
    
    1) Ensure HWASAN required alignment for stack variables
    
    When tagging shadow memory, we need to ensure that each tag granule is
    only used by one variable at a time.
    
    This is done by ensuring that each tagged variable is aligned to the tag
    granule representation size and also ensure that the end of each
    object is aligned to ensure the start of any other data stored on the
    stack is in a different granule.
    
    This patch ensures the above by forcing the stack pointer to be aligned
    before and after allocating any stack objects. Since we are forcing
    alignment we also use `align_local_variable` to ensure this new alignment
    is advertised properly through SET_DECL_ALIGN.
    
    2) Put tags into each stack variable pointer
    
    Make sure that every pointer to a stack variable includes a tag of some
    sort on it.
    
    The way tagging works is:
      1) For every new stack frame, a random tag is generated.
      2) A base register is formed from the stack pointer value and this
         random tag.
      3) References to stack variables are now formed with RTL describing an
         offset from this base in both tag and value.
    
    The random tag generation is handled by a backend hook.  This hook
    decides whether to introduce a random tag or use the stack background
    based on the parameter hwasan-random-frame-tag.  Using the stack
    background is necessary for testing and bootstrap.  It is necessary
    during bootstrap to avoid breaking the `configure` test program for
    determining stack direction.
    
    Using the stack background means that every stack frame has the initial
    tag of zero and variables are tagged with incrementing tags from 1,
    which also makes debugging a bit easier.
    
    Backend hooks define the size of a tag, the layout of the HWASAN shadow
    memory, and handle emitting the code that inserts and extracts tags from a
    pointer.
    
    3) For each stack variable, tag and untag the shadow stack on function
       prologue and epilogue.
    
    On entry to each function we tag the relevant shadow stack region for
    each stack variable. This stack region is tagged to match the tag added to
    each pointer to that variable.
    
    This is the first patch where we use the HWASAN shadow space, so we need
    to add in the libhwasan initialisation code that creates this shadow
    memory region into the binary we produce.  This instrumentation is done
    in `compile_file`.
    
    When exiting a function we need to ensure the shadow stack for this
    function has no remaining tags.  Without clearing the shadow stack area
    for this stack frame, later function calls could get false positives
    when those later function calls check untagged areas (such as parameters
    passed on the stack) against a shadow stack area with left-over tag.
    
    Hence we ensure that the entire stack frame is cleared on function exit.
    
    config/ChangeLog:
    
    	* bootstrap-hwasan.mk: Disable random frame tags for stack-tagging
    	during bootstrap.
    
    gcc/ChangeLog:
    
    	* asan.c (struct hwasan_stack_var): New.
    	(hwasan_sanitize_p): New.
    	(hwasan_sanitize_stack_p): New.
    	(hwasan_sanitize_allocas_p): New.
    	(initialize_sanitizer_builtins): Define new builtins.
    	(ATTR_NOTHROW_LIST): New macro.
    	(hwasan_current_frame_tag): New.
    	(hwasan_frame_base): New.
    	(stack_vars_base_reg_p): New.
    	(hwasan_maybe_init_frame_base_init): New.
    	(hwasan_record_stack_var): New.
    	(hwasan_get_frame_extent): New.
    	(hwasan_increment_frame_tag): New.
    	(hwasan_record_frame_init): New.
    	(hwasan_emit_prologue): New.
    	(hwasan_emit_untag_frame): New.
    	(hwasan_finish_file): New.
    	(hwasan_truncate_to_tag_size): New.
    	* asan.h (hwasan_record_frame_init): New declaration.
    	(hwasan_record_stack_var): New declaration.
    	(hwasan_emit_prologue): New declaration.
    	(hwasan_emit_untag_frame): New declaration.
    	(hwasan_get_frame_extent): New declaration.
    	(hwasan_maybe_enit_frame_base_init): New declaration.
    	(hwasan_frame_base): New declaration.
    	(stack_vars_base_reg_p): New declaration.
    	(hwasan_current_frame_tag): New declaration.
    	(hwasan_increment_frame_tag): New declaration.
    	(hwasan_truncate_to_tag_size): New declaration.
    	(hwasan_finish_file): New declaration.
    	(hwasan_sanitize_p): New declaration.
    	(hwasan_sanitize_stack_p): New declaration.
    	(hwasan_sanitize_allocas_p): New declaration.
    	(HWASAN_TAG_SIZE): New macro.
    	(HWASAN_TAG_GRANULE_SIZE): New macro.
    	(HWASAN_STACK_BACKGROUND): New macro.
    	* builtin-types.def (BT_FN_VOID_PTR_UINT8_PTRMODE): New.
    	* builtins.def (DEF_SANITIZER_BUILTIN): Enable for HWASAN.
    	* cfgexpand.c (align_local_variable): When using hwasan ensure
    	alignment to tag granule.
    	(align_frame_offset): New.
    	(expand_one_stack_var_at): For hwasan use tag offset.
    	(expand_stack_vars): Record stack objects for hwasan.
    	(expand_one_stack_var_1): Record stack objects for hwasan.
    	(init_vars_expansion): Initialise hwasan state.
    	(expand_used_vars): Emit hwasan prologue and generate hwasan epilogue.
    	(pass_expand::execute): Emit hwasan base initialization if needed.
    	* doc/tm.texi (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
    	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
    	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
    	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
    	* doc/tm.texi.in (TARGET_MEMTAG_TAG_SIZE,TARGET_MEMTAG_GRANULE_SIZE,
    	TARGET_MEMTAG_INSERT_RANDOM_TAG,TARGET_MEMTAG_ADD_TAG,
    	TARGET_MEMTAG_SET_TAG,TARGET_MEMTAG_EXTRACT_TAG,
    	TARGET_MEMTAG_UNTAGGED_POINTER): Document new hooks.
    	* explow.c (get_dynamic_stack_base): Take new `base` argument.
    	* explow.h (get_dynamic_stack_base): Take new `base` argument.
    	* sanitizer.def (BUILT_IN_HWASAN_INIT): New.
    	(BUILT_IN_HWASAN_TAG_MEM): New.
    	* target.def (target_memtag_tag_size,target_memtag_granule_size,
    	target_memtag_insert_random_tag,target_memtag_add_tag,
    	target_memtag_set_tag,target_memtag_extract_tag,
    	target_memtag_untagged_pointer): New hooks.
    	* targhooks.c (HWASAN_SHIFT): New.
    	(HWASAN_SHIFT_RTX): New.
    	(default_memtag_tag_size): New default hook.
    	(default_memtag_granule_size): New default hook.
    	(default_memtag_insert_random_tag): New default hook.
    	(default_memtag_add_tag): New default hook.
    	(default_memtag_set_tag): New default hook.
    	(default_memtag_extract_tag): New default hook.
    	(default_memtag_untagged_pointer): New default hook.
    	* targhooks.h (default_memtag_tag_size): New default hook.
    	(default_memtag_granule_size): New default hook.
    	(default_memtag_insert_random_tag): New default hook.
    	(default_memtag_add_tag): New default hook.
    	(default_memtag_set_tag): New default hook.
    	(default_memtag_extract_tag): New default hook.
    	(default_memtag_untagged_pointer): New default hook.
    	* toplev.c (compile_file): Call hwasan_finish_file when finished.