aboutsummaryrefslogtreecommitdiff
path: root/gcc/opts.c
diff options
context:
space:
mode:
authorMatthew Malcomson <matthew.malcomson@arm.com>2020-11-25 16:31:43 +0000
committerMatthew Malcomson <matthew.malcomson@arm.com>2020-11-25 16:35:39 +0000
commit3bd8783207760cc805d8a4e64e5ac92ca508a711 (patch)
tree1d270dc01883654cbe0681e5f06c2aea5c1e460f /gcc/opts.c
parent170e618ef559a9b1220174c1d33cd7e5b1045cc8 (diff)
downloadgcc-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.c98
1 files changed, 82 insertions, 16 deletions
diff --git a/gcc/opts.c b/gcc/opts.c
index 5777491..cc1d0cc 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -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_: