diff options
author | Matthew Malcomson <matthew.malcomson@arm.com> | 2020-11-25 16:31:43 +0000 |
---|---|---|
committer | Matthew Malcomson <matthew.malcomson@arm.com> | 2020-11-25 16:35:39 +0000 |
commit | 3bd8783207760cc805d8a4e64e5ac92ca508a711 (patch) | |
tree | 1d270dc01883654cbe0681e5f06c2aea5c1e460f /gcc/opts.c | |
parent | 170e618ef559a9b1220174c1d33cd7e5b1045cc8 (diff) | |
download | gcc-3bd8783207760cc805d8a4e64e5ac92ca508a711.zip gcc-3bd8783207760cc805d8a4e64e5ac92ca508a711.tar.gz gcc-3bd8783207760cc805d8a4e64e5ac92ca508a711.tar.bz2 |
libsanitizer: options: Add hwasan flags and argument parsing
These flags can't be used at the same time as any of the other
sanitizers.
We add an equivalent flag to -static-libasan in -static-libhwasan to
ensure static linking.
The -fsanitize=kernel-hwaddress option is for compiling targeting the
kernel. This flag has defaults to match the LLVM implementation and
sets some other behaviors to work in the kernel (e.g. accounting for
the fact that the stack pointer will have 0xff in the top byte and to not
call the userspace library initialisation routines).
The defaults are that we do not sanitize variables on the stack and
always recover from a detected bug.
Since we are introducing a few more conflicts between sanitizer flags we
refactor the checking for such conflicts to use a helper function which
makes checking for such conflicts more easy and consistent.
We introduce a backend hook `targetm.memtag.can_tag_addresses` that
indicates to the mid-end whether a target has a feature like AArch64 TBI
where the top byte of an address is ignored.
Without this feature hwasan sanitization is not done.
gcc/ChangeLog:
* common.opt (flag_sanitize_recover): Default for kernel
hwaddress.
(static-libhwasan): New cli option.
* config/aarch64/aarch64.c (aarch64_can_tag_addresses): New.
(TARGET_MEMTAG_CAN_TAG_ADDRESSES): New.
* config/gnu-user.h (LIBHWASAN_EARLY_SPEC): hwasan equivalent of
asan command line flags.
* cppbuiltin.c (define_builtin_macros_for_compilation_flags):
Add hwasan equivalent of __SANITIZE_ADDRESS__.
* doc/invoke.texi: Document hwasan command line flags.
* doc/tm.texi: Document new hook.
* doc/tm.texi.in: Document new hook.
* flag-types.h (enum sanitize_code): New sanitizer values.
* gcc.c (STATIC_LIBHWASAN_LIBS): New macro.
(LIBHWASAN_SPEC): New macro.
(LIBHWASAN_EARLY_SPEC): New macro.
(SANITIZER_EARLY_SPEC): Update to include hwasan.
(SANITIZER_SPEC): Update to include hwasan.
(sanitize_spec_function): Use hwasan options.
* opts.c (finish_options): Describe conflicts between address
sanitizers.
(find_sanitizer_argument): New.
(report_conflicting_sanitizer_options): New.
(sanitizer_opts): Introduce new sanitizer flags.
(common_handle_option): Add defaults for kernel sanitizer.
* params.opt (hwasan--instrument-stack): New
(hwasan-random-frame-tag): New
(hwasan-instrument-allocas): New
(hwasan-instrument-reads): New
(hwasan-instrument-writes): New
(hwasan-instrument-mem-intrinsics): New
* target.def (HOOK_PREFIX): Add new hook.
(can_tag_addresses): Add new hook under memtag prefix.
* targhooks.c (default_memtag_can_tag_addresses): New.
* targhooks.h (default_memtag_can_tag_addresses): New decl.
* toplev.c (process_options): Ensure hwasan only on
architectures that advertise the possibility.
Diffstat (limited to 'gcc/opts.c')
-rw-r--r-- | gcc/opts.c | 98 |
1 files changed, 82 insertions, 16 deletions
@@ -823,6 +823,57 @@ control_options_for_live_patching (struct gcc_options *opts, /* --help option argument if set. */ vec<const char *> help_option_arguments; +/* Return the string name describing a sanitizer argument which has been + provided on the command line and has set this particular flag. */ +const char * +find_sanitizer_argument (struct gcc_options *opts, unsigned int flags) +{ + for (int i = 0; sanitizer_opts[i].name != NULL; ++i) + { + /* Need to find the sanitizer_opts element which: + a) Could have set the flags requested. + b) Has been set on the command line. + + Can have (a) without (b) if the flag requested is e.g. + SANITIZE_ADDRESS, since both -fsanitize=address and + -fsanitize=kernel-address set this flag. + + Can have (b) without (a) by requesting more than one sanitizer on the + command line. */ + if ((sanitizer_opts[i].flag & opts->x_flag_sanitize) + != sanitizer_opts[i].flag) + continue; + if ((sanitizer_opts[i].flag & flags) != flags) + continue; + return sanitizer_opts[i].name; + } + return NULL; +} + + +/* Report an error to the user about sanitizer options they have requested + which have set conflicting flags. + + LEFT and RIGHT indicate sanitizer flags which conflict with each other, this + function reports an error if both have been set in OPTS->x_flag_sanitize and + ensures the error identifies the requested command line options that have + set these flags. */ +static void +report_conflicting_sanitizer_options (struct gcc_options *opts, location_t loc, + unsigned int left, unsigned int right) +{ + unsigned int left_seen = (opts->x_flag_sanitize & left); + unsigned int right_seen = (opts->x_flag_sanitize & right); + if (left_seen && right_seen) + { + const char* left_arg = find_sanitizer_argument (opts, left_seen); + const char* right_arg = find_sanitizer_argument (opts, right_seen); + gcc_assert (left_arg && right_arg); + error_at (loc, + "%<-fsanitize=%s%> is incompatible with %<-fsanitize=%s%>", + left_arg, right_arg); + } +} /* After all options at LOC have been read into OPTS and OPTS_SET, finalize settings of those options and diagnose incompatible @@ -1074,22 +1125,22 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set, "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>"); } - /* Userspace and kernel ASan conflict with each other. */ - if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS) - && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS)) - error_at (loc, "%qs is incompatible with %qs", - "-fsanitize=address", "-fsanitize=kernel-address"); + /* Address sanitizers conflict with the thread sanitizer. */ + report_conflicting_sanitizer_options (opts, loc, SANITIZE_THREAD, + SANITIZE_ADDRESS | SANITIZE_HWADDRESS); + /* The leak sanitizer conflicts with the thread sanitizer. */ + report_conflicting_sanitizer_options (opts, loc, SANITIZE_LEAK, + SANITIZE_THREAD); - /* And with TSan. */ - if ((opts->x_flag_sanitize & SANITIZE_ADDRESS) - && (opts->x_flag_sanitize & SANITIZE_THREAD)) - error_at (loc, "%qs is incompatible with %qs", - "-fsanitize=thread", "-fsanitize=address|kernel-address"); + /* No combination of HWASAN and ASAN work together. */ + report_conflicting_sanitizer_options (opts, loc, + SANITIZE_HWADDRESS, SANITIZE_ADDRESS); - if ((opts->x_flag_sanitize & SANITIZE_LEAK) - && (opts->x_flag_sanitize & SANITIZE_THREAD)) - error_at (loc, "%qs is incompatible with %qs", - "-fsanitize=leak", "-fsanitize=thread"); + /* The userspace and kernel address sanitizers conflict with each other. */ + report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_HWADDRESS, + SANITIZE_KERNEL_HWADDRESS); + report_conflicting_sanitizer_options (opts, loc, SANITIZE_USER_ADDRESS, + SANITIZE_KERNEL_ADDRESS); /* Check error recovery for -fsanitize-recover option. */ for (int i = 0; sanitizer_opts[i].name != NULL; ++i) @@ -1108,9 +1159,10 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set, if (opts->x_flag_sanitize & ~(SANITIZE_LEAK | SANITIZE_UNREACHABLE)) opts->x_flag_aggressive_loop_optimizations = 0; - /* Enable -fsanitize-address-use-after-scope if address sanitizer is + /* Enable -fsanitize-address-use-after-scope if either address sanitizer is enabled. */ - if (opts->x_flag_sanitize & SANITIZE_USER_ADDRESS) + if (opts->x_flag_sanitize + & (SANITIZE_USER_ADDRESS | SANITIZE_USER_HWADDRESS)) SET_OPTION_IF_UNSET (opts, opts_set, flag_sanitize_address_use_after_scope, true); @@ -1724,8 +1776,13 @@ const struct sanitizer_opts_s sanitizer_opts[] = #define SANITIZER_OPT(name, flags, recover) \ { #name, flags, sizeof #name - 1, recover } SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true), + SANITIZER_OPT (hwaddress, (SANITIZE_HWADDRESS | SANITIZE_USER_HWADDRESS), + true), SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS), true), + SANITIZER_OPT (kernel-hwaddress, + (SANITIZE_HWADDRESS | SANITIZE_KERNEL_HWADDRESS), + true), SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true), SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true), SANITIZER_OPT (thread, SANITIZE_THREAD, false), @@ -2304,6 +2361,15 @@ common_handle_option (struct gcc_options *opts, SET_OPTION_IF_UNSET (opts, opts_set, param_asan_protect_allocas, 0); SET_OPTION_IF_UNSET (opts, opts_set, param_asan_use_after_return, 0); } + if (opts->x_flag_sanitize & SANITIZE_KERNEL_HWADDRESS) + { + SET_OPTION_IF_UNSET (opts, opts_set, + param_hwasan_instrument_stack, 0); + SET_OPTION_IF_UNSET (opts, opts_set, + param_hwasan_random_frame_tag, 0); + SET_OPTION_IF_UNSET (opts, opts_set, + param_hwasan_instrument_allocas, 0); + } break; case OPT_fsanitize_recover_: |