Hello. As I previously agreed with Jakub, I prepared patch which adds no_sanitize function attribute (same what clang support).
That encompasses following changes: 1) all no_sanitize_* function attributes are parsed and stored to no_sanitize_flags in DECL_ATTRIBUTES 2) instead of flag_sanitize & X, let's call sanitize_flags_p (X), where the DECL_ATTRIBUTES are checked within the function 3) I prepared many test-cases which test every (almost) single sub-option of UBSAN and all functions are decorated with no_sanitize attribute disabling all sanitization except the one tested in a particular test 4) documentation entry is introduced Misc changes: a) I would like to rename SANITIZE_NONDEFAULT to SANITIZE_UNDEFINED_NONDEFAULT b) Documentation fix for -sanitize=bounds-strict is added Patch can bootstrap on ppc64le-redhat-linux and survives regression tests. Martin
>From 046ce41931ea1d4cb51c14b74f2bcddc2cb4c6ca Mon Sep 17 00:00:00 2001 From: marxin <mli...@suse.cz> Date: Wed, 21 Dec 2016 11:13:34 +0100 Subject: [PATCH] Implement no_sanitize function attribute gcc/cp/ChangeLog: 2016-12-22 Martin Liska <mli...@suse.cz> * class.c (build_base_path): Use sanitize_flags_p predicate. * cp-gimplify.c (cp_genericize_r): Likewise. (cp_genericize_tree): Likewise. (cp_genericize): Likewise. * cp-ubsan.c (cp_ubsan_instrument_vptr_p): Likewise. * decl.c (compute_array_index_type): Likewise. (start_preparsed_function): Likewise. * decl2.c (one_static_initialization_or_destruction): Likewise. * init.c (build_vec_init): Likewise. * lambda.c (maybe_add_lambda_conv_op): Use add_no_sanitize_value. * typeck.c (cp_build_binary_op): Use sanitize_flags_p. (build_static_cast_1): Likewise. gcc/c-family/ChangeLog: 2016-12-22 Martin Liska <mli...@suse.cz> * c-attribs.c (add_no_sanitize_value): New function. (handle_no_sanitize_attribute): Likewise. (handle_no_sanitize_address_attribute): Use add_no_sanitize_value. (handle_no_sanitize_thread_attribute): New function. (handle_no_address_safety_analysis_attribute): Use add_no_sanitize_value. (handle_no_sanitize_undefined_attribute): Likewise. * c-common.h (add_no_sanitize_value): Declare. * c-ubsan.c (ubsan_instrument_division): Use sanitize_flags_p. (ubsan_instrument_shift): Likewise. (ubsan_instrument_bounds): Likewise. (ubsan_maybe_instrument_array_ref): Likewise. (ubsan_maybe_instrument_reference_or_call): Likewise. * c-ubsan.h (do_ubsan_in_current_function): Remove declaration. gcc/testsuite/ChangeLog: 2016-12-22 Martin Liska <mli...@suse.cz> * c-c++-common/ubsan/align-10.c: New test. * c-c++-common/ubsan/attrib-5.c: New test. * c-c++-common/ubsan/attrib-6.c: New test. * c-c++-common/ubsan/bounds-14.c: New test. * c-c++-common/ubsan/div-by-zero-8.c: New test. * c-c++-common/ubsan/float-cast-overflow-11.c: New test. * c-c++-common/ubsan/float-div-by-zero-2.c: New test. * c-c++-common/ubsan/load-bool-enum-2.c: New test. * c-c++-common/ubsan/nonnull-6.c: New test. * c-c++-common/ubsan/null-12.c: New test. * c-c++-common/ubsan/object-size-11.c: New test. * c-c++-common/ubsan/overflow-3.c: New test. * c-c++-common/ubsan/unreachable-5.c: New test. * c-c++-common/ubsan/vla-5.c: New test. * g++.dg/ubsan/return-8.C: New test. * g++.dg/ubsan/vptr-12.C: New test. * gcc.dg/ubsan/bounds-4.c: New test. * gcc.dg/ubsan/c99-shift-7.c: New test. * gcc.dg/ubsan/c99-shift-8.c: New test. gcc/ChangeLog: 2016-12-22 Martin Liska <mli...@suse.cz> * asan.c (asan_sanitize_stack_p): Use sanitize_flags_p. (gate_asan): Likewise. * asan.h (asan_no_sanitize_address_p): Remove. * builtins.def: Use renamed SANITIZE_UNDEFINED_NONDEFAULT. * common.opt: Likewise. * convert.c (convert_to_integer_1): Use sanitize_flags_p. * doc/extend.texi: Document the new attribute. * doc/invoke.texi: Improve documentation of -sanitize=bounds-strict. * flag-types.h (enum sanitize_code): Rename SANITIZE_NONDEFAULT to SANITIZE_UNDEFINED_NONDEFAULT. * gcc.c (sanitize_spec_function): Use the renamed enum value. * gimple-fold.c (optimize_atomic_compare_exchange_p): Use sanitize_flags_p. * gimplify.c (gimplify_decl_expr): Likewise. (gimplify_function_tree): Likewise. * ipa-inline.c (sanitize_attrs_match_for_inline_p): Likewise. * opts.c (parse_no_sanitize_attribute): New function. (common_handle_option): Use the renamed enum value. * opts.h (parse_no_sanitize_attribute): Declare. * tree.c (sanitize_flags_p): New function. * tree.h (sanitize_flags_p): New declaration. * tsan.c (gate): Use sanitize_flags_p. * ubsan.c (ubsan_expand_null_ifn): Likewise. (instrument_mem_ref): Likewise. (instrument_bool_enum_load): Likewise. (do_ubsan_in_current_function): Remove. (pass_ubsan::execute): Use sanitize_flags_p. * ubsan.h (do_ubsan_in_current_function): Remove. gcc/c/ChangeLog: 2016-12-22 Martin Liska <mli...@suse.cz> * c-convert.c (convert): Use sanitize_flags_p. * c-decl.c (grokdeclarator): Likewise. * c-typeck.c (convert_for_assignment): Likewise. (c_finish_return): Likewise. (build_binary_op): Likewise. --- gcc/asan.c | 14 +- gcc/asan.h | 7 - gcc/builtins.def | 3 +- gcc/c-family/c-attribs.c | 99 +++++++- gcc/c-family/c-common.h | 1 + gcc/c-family/c-ubsan.c | 22 +- gcc/c-family/c-ubsan.h | 3 - gcc/c/c-convert.c | 5 +- gcc/c/c-decl.c | 5 +- gcc/c/c-typeck.c | 15 +- gcc/common.opt | 2 +- gcc/convert.c | 3 +- gcc/cp/class.c | 3 +- gcc/cp/cp-gimplify.c | 18 +- gcc/cp/cp-ubsan.c | 2 +- gcc/cp/decl.c | 5 +- gcc/cp/decl2.c | 2 +- gcc/cp/init.c | 3 +- gcc/cp/lambda.c | 4 +- gcc/cp/typeck.c | 15 +- gcc/doc/extend.texi | 12 + gcc/doc/invoke.texi | 2 + gcc/flag-types.h | 4 +- gcc/gcc.c | 3 +- gcc/gimple-fold.c | 2 +- gcc/gimplify.c | 5 +- gcc/ipa-inline.c | 10 +- gcc/opts.c | 31 ++- gcc/opts.h | 2 + gcc/testsuite/c-c++-common/ubsan/align-10.c | 56 +++++ gcc/testsuite/c-c++-common/ubsan/attrib-5.c | 12 + gcc/testsuite/c-c++-common/ubsan/attrib-6.c | 15 ++ gcc/testsuite/c-c++-common/ubsan/bounds-14.c | 17 ++ gcc/testsuite/c-c++-common/ubsan/div-by-zero-8.c | 9 + .../c-c++-common/ubsan/float-cast-overflow-11.c | 205 ++++++++++++++++ .../c-c++-common/ubsan/float-div-by-zero-2.c | 27 +++ .../c-c++-common/ubsan/load-bool-enum-2.c | 31 +++ gcc/testsuite/c-c++-common/ubsan/nonnull-6.c | 40 ++++ gcc/testsuite/c-c++-common/ubsan/null-12.c | 13 + gcc/testsuite/c-c++-common/ubsan/object-size-11.c | 131 +++++++++++ gcc/testsuite/c-c++-common/ubsan/overflow-3.c | 262 +++++++++++++++++++++ gcc/testsuite/c-c++-common/ubsan/unreachable-5.c | 11 + gcc/testsuite/c-c++-common/ubsan/vla-5.c | 14 ++ gcc/testsuite/g++.dg/ubsan/return-8.C | 29 +++ gcc/testsuite/g++.dg/ubsan/vptr-12.C | 185 +++++++++++++++ gcc/testsuite/gcc.dg/ubsan/bounds-4.c | 20 ++ gcc/testsuite/gcc.dg/ubsan/c99-shift-7.c | 11 + gcc/testsuite/gcc.dg/ubsan/c99-shift-8.c | 12 + gcc/tree.c | 17 ++ gcc/tree.h | 4 + gcc/tsan.c | 12 +- gcc/ubsan.c | 48 ++-- gcc/ubsan.h | 1 - 53 files changed, 1345 insertions(+), 139 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/ubsan/align-10.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/attrib-5.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/attrib-6.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/bounds-14.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/div-by-zero-8.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-11.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-2.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/load-bool-enum-2.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/nonnull-6.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/null-12.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/object-size-11.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/overflow-3.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/unreachable-5.c create mode 100644 gcc/testsuite/c-c++-common/ubsan/vla-5.c create mode 100644 gcc/testsuite/g++.dg/ubsan/return-8.C create mode 100644 gcc/testsuite/g++.dg/ubsan/vptr-12.C create mode 100644 gcc/testsuite/gcc.dg/ubsan/bounds-4.c create mode 100644 gcc/testsuite/gcc.dg/ubsan/c99-shift-7.c create mode 100644 gcc/testsuite/gcc.dg/ubsan/c99-shift-8.c diff --git a/gcc/asan.c b/gcc/asan.c index 53acff0a2fb..8ec6b75d29f 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -304,9 +304,7 @@ asan_mark_p (gimple *stmt, enum asan_mark_flags flag) bool asan_sanitize_stack_p (void) { - return ((flag_sanitize & SANITIZE_ADDRESS) - && ASAN_STACK - && !asan_no_sanitize_address_p ()); + return (sanitize_flags_p (SANITIZE_ADDRESS) && ASAN_STACK); } /* Checks whether section SEC should be sanitized. */ @@ -3067,11 +3065,9 @@ asan_instrument (void) } static bool -gate_asan (void) +gate_asan (function *fn) { - return (flag_sanitize & SANITIZE_ADDRESS) != 0 - && !lookup_attribute ("no_sanitize_address", - DECL_ATTRIBUTES (current_function_decl)); + return sanitize_flags_p (SANITIZE_ADDRESS, fn->decl); } namespace { @@ -3098,7 +3094,7 @@ public: /* opt_pass methods: */ opt_pass * clone () { return new pass_asan (m_ctxt); } - virtual bool gate (function *) { return gate_asan (); } + virtual bool gate (function *fn) { return gate_asan (fn); } virtual unsigned int execute (function *) { return asan_instrument (); } }; // class pass_asan @@ -3134,7 +3130,7 @@ public: {} /* opt_pass methods: */ - virtual bool gate (function *) { return !optimize && gate_asan (); } + virtual bool gate (function *fn) { return !optimize && gate_asan (fn); } virtual unsigned int execute (function *) { return asan_instrument (); } }; // class pass_asan_O0 diff --git a/gcc/asan.h b/gcc/asan.h index 355a350bfeb..bbd81a1ebe2 100644 --- a/gcc/asan.h +++ b/gcc/asan.h @@ -140,13 +140,6 @@ asan_sanitize_use_after_scope (void) return (flag_sanitize_address_use_after_scope && asan_sanitize_stack_p ()); } -static inline bool -asan_no_sanitize_address_p (void) -{ - return lookup_attribute ("no_sanitize_address", - DECL_ATTRIBUTES (current_function_decl)); -} - /* Return true if DECL should be guarded on the stack. */ static inline bool diff --git a/gcc/builtins.def b/gcc/builtins.def index 24b34e80cf1..60d0e2a8147 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -236,7 +236,8 @@ along with GCC; see the file COPYING3. If not see DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \ true, true, true, ATTRS, true, \ (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \ - | SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT) \ + | SANITIZE_UNDEFINED \ + | SANITIZE_UNDEFINED_NONDEFAULT) \ || flag_sanitize_coverage)) #undef DEF_CILKPLUS_BUILTIN diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index f5adadee3af..1231b10c9d7 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -51,8 +51,11 @@ static tree handle_common_attribute (tree *, tree, tree, int, bool *); static tree handle_noreturn_attribute (tree *, tree, tree, int, bool *); static tree handle_hot_attribute (tree *, tree, tree, int, bool *); static tree handle_cold_attribute (tree *, tree, tree, int, bool *); +static tree handle_no_sanitize_attribute (tree *, tree, tree, int, bool *); static tree handle_no_sanitize_address_attribute (tree *, tree, tree, int, bool *); +static tree handle_no_sanitize_thread_attribute (tree *, tree, tree, + int, bool *); static tree handle_no_address_safety_analysis_attribute (tree *, tree, tree, int, bool *); static tree handle_no_sanitize_undefined_attribute (tree *, tree, tree, int, @@ -285,11 +288,14 @@ const struct attribute_spec c_common_attribute_table[] = 0, 0, true, false, false, handle_no_address_safety_analysis_attribute, false }, + { "no_sanitize", 1, 1, true, false, false, + handle_no_sanitize_attribute, + false }, { "no_sanitize_address", 0, 0, true, false, false, handle_no_sanitize_address_attribute, false }, { "no_sanitize_thread", 0, 0, true, false, false, - handle_no_sanitize_address_attribute, + handle_no_sanitize_thread_attribute, false }, { "no_sanitize_undefined", 0, 0, true, false, false, handle_no_sanitize_undefined_attribute, @@ -547,6 +553,60 @@ handle_cold_attribute (tree *node, tree name, tree ARG_UNUSED (args), return NULL_TREE; } +/* Add FLAGS for a function NODE to no_sanitize_flags in DECL_ATTRIBUTES. */ + +void +add_no_sanitize_value (tree node, unsigned int flags) +{ + tree attr = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES (node)); + if (attr) + { + unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr)); + flags |= old_value; + } + + DECL_ATTRIBUTES (node) + = tree_cons (get_identifier ("no_sanitize_flags"), + build_int_cst (integer_type_node, flags), + DECL_ATTRIBUTES (node)); +} + +/* Handle a "no_sanitize" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_no_sanitize_attribute (tree *node, tree name, tree args, int, + bool *no_add_attrs) +{ + tree id = TREE_VALUE (args); + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + return NULL_TREE; + } + + if (TREE_CODE (id) != STRING_CST) + { + error ("no_sanitize argument not a string"); + return NULL_TREE; + } + + char *error_value = NULL; + char *string = ASTRDUP (TREE_STRING_POINTER (id)); + unsigned int flags = parse_no_sanitize_attribute (string, &error_value); + + if (error_value) + { + error ("wrong argument: \"%s\"", error_value); + return NULL_TREE; + } + + add_no_sanitize_value (*node, flags); + + return NULL_TREE; +} + /* Handle a "no_sanitize_address" attribute; arguments as in struct attribute_spec.handler. */ @@ -559,10 +619,31 @@ handle_no_sanitize_address_attribute (tree *node, tree name, tree, int, warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } + else + add_no_sanitize_value (*node, SANITIZE_ADDRESS); + + return NULL_TREE; +} + +/* Handle a "no_sanitize_thread" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_no_sanitize_thread_attribute (tree *node, tree name, tree, int, + bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + else + add_no_sanitize_value (*node, SANITIZE_THREAD); return NULL_TREE; } + /* Handle a "no_address_safety_analysis" attribute; arguments as in struct attribute_spec.handler. */ @@ -571,12 +652,13 @@ handle_no_address_safety_analysis_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) { if (TREE_CODE (*node) != FUNCTION_DECL) - warning (OPT_Wattributes, "%qE attribute ignored", name); - else if (!lookup_attribute ("no_sanitize_address", DECL_ATTRIBUTES (*node))) - DECL_ATTRIBUTES (*node) - = tree_cons (get_identifier ("no_sanitize_address"), - NULL_TREE, DECL_ATTRIBUTES (*node)); - *no_add_attrs = true; + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + else + add_no_sanitize_value (*node, SANITIZE_ADDRESS); + return NULL_TREE; } @@ -592,6 +674,9 @@ handle_no_sanitize_undefined_attribute (tree *node, tree name, tree, int, warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } + else + add_no_sanitize_value (*node, + SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT); return NULL_TREE; } diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index a23193ee9db..23f4a64ca5a 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1550,6 +1550,7 @@ extern enum flt_eval_method excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method); extern int c_flt_eval_method (bool ts18661_p); +extern void add_no_sanitize_value (tree node, unsigned int flags); #if CHECKING_P namespace selftest { diff --git a/gcc/c-family/c-ubsan.c b/gcc/c-family/c-ubsan.c index 6f93d80a746..11b88b44cc3 100644 --- a/gcc/c-family/c-ubsan.c +++ b/gcc/c-family/c-ubsan.c @@ -49,11 +49,11 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1) op1 = unshare_expr (op1); if (TREE_CODE (type) == INTEGER_TYPE - && (flag_sanitize & SANITIZE_DIVIDE)) + && sanitize_flags_p (SANITIZE_DIVIDE)) t = fold_build2 (EQ_EXPR, boolean_type_node, op1, build_int_cst (type, 0)); else if (TREE_CODE (type) == REAL_TYPE - && (flag_sanitize & SANITIZE_FLOAT_DIVIDE)) + && sanitize_flags_p (SANITIZE_FLOAT_DIVIDE)) t = fold_build2 (EQ_EXPR, boolean_type_node, op1, build_real (type, dconst0)); else @@ -61,7 +61,7 @@ ubsan_instrument_division (location_t loc, tree op0, tree op1) /* We check INT_MIN / -1 only for signed types. */ if (TREE_CODE (type) == INTEGER_TYPE - && (flag_sanitize & SANITIZE_DIVIDE) + && sanitize_flags_p (SANITIZE_DIVIDE) && !TYPE_UNSIGNED (type)) { tree x; @@ -131,7 +131,7 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, Also punt on bit-fields. */ if (TYPE_OVERFLOW_WRAPS (type0) || GET_MODE_BITSIZE (TYPE_MODE (type0)) != TYPE_PRECISION (type0) - || (flag_sanitize & SANITIZE_SHIFT_BASE) == 0) + || !sanitize_flags_p (SANITIZE_SHIFT_BASE)) ; /* For signed x << y, in C99/C11, the following: @@ -177,7 +177,7 @@ ubsan_instrument_shift (location_t loc, enum tree_code code, tree else_t = void_node; if (tt) { - if ((flag_sanitize & SANITIZE_SHIFT_EXPONENT) == 0) + if (!sanitize_flags_p (SANITIZE_SHIFT_EXPONENT)) { t = fold_build1 (TRUTH_NOT_EXPR, boolean_type_node, t); t = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, t, tt); @@ -300,7 +300,7 @@ ubsan_instrument_bounds (location_t loc, tree array, tree *index, /* Detect flexible array members and suchlike, unless -fsanitize=bounds-strict. */ tree base = get_base_address (array); - if ((flag_sanitize & SANITIZE_BOUNDS_STRICT) == 0 + if (!sanitize_flags_p (SANITIZE_BOUNDS_STRICT) && TREE_CODE (array) == COMPONENT_REF && base && (INDIRECT_REF_P (base) || TREE_CODE (base) == MEM_REF)) { @@ -372,7 +372,7 @@ void ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one) { if (!ubsan_array_ref_instrumented_p (*expr_p) - && do_ubsan_in_current_function ()) + && sanitize_flags_p (SANITIZE_BOUNDS | SANITIZE_BOUNDS_STRICT)) { tree op0 = TREE_OPERAND (*expr_p, 0); tree op1 = TREE_OPERAND (*expr_p, 1); @@ -392,7 +392,7 @@ static tree ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype, enum ubsan_null_ckind ckind) { - if (!do_ubsan_in_current_function ()) + if (!sanitize_flags_p (SANITIZE_ALIGNMENT | SANITIZE_NULL)) return NULL_TREE; tree type = TREE_TYPE (ptype); @@ -400,7 +400,7 @@ ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype, bool instrument = false; unsigned int mina = 0; - if (flag_sanitize & SANITIZE_ALIGNMENT) + if (sanitize_flags_p (SANITIZE_ALIGNMENT)) { mina = min_align_of_type (type); if (mina <= 1) @@ -418,7 +418,7 @@ ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype, } else { - if ((flag_sanitize & SANITIZE_NULL) && TREE_CODE (op) == ADDR_EXPR) + if (sanitize_flags_p (SANITIZE_NULL) && TREE_CODE (op) == ADDR_EXPR) { bool strict_overflow_p = false; /* tree_single_nonzero_warnv_p will not return true for non-weak @@ -434,7 +434,7 @@ ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype, flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks; } - else if (flag_sanitize & SANITIZE_NULL) + else if (sanitize_flags_p (SANITIZE_NULL)) instrument = true; if (mina && mina > 1) { diff --git a/gcc/c-family/c-ubsan.h b/gcc/c-family/c-ubsan.h index 30d4f971a7e..8a36ac6c088 100644 --- a/gcc/c-family/c-ubsan.h +++ b/gcc/c-family/c-ubsan.h @@ -31,7 +31,4 @@ extern void ubsan_maybe_instrument_array_ref (tree *, bool); extern void ubsan_maybe_instrument_reference (tree); extern void ubsan_maybe_instrument_member_call (tree, bool); -/* Declare this here as well as in ubsan.h. */ -extern bool do_ubsan_in_current_function (void); - #endif /* GCC_C_UBSAN_H */ diff --git a/gcc/c/c-convert.c b/gcc/c/c-convert.c index 4167c3461f3..8a146aafa33 100644 --- a/gcc/c/c-convert.c +++ b/gcc/c/c-convert.c @@ -106,10 +106,9 @@ convert (tree type, tree expr) case INTEGER_TYPE: case ENUMERAL_TYPE: - if (flag_sanitize & SANITIZE_FLOAT_CAST + if (sanitize_flags_p (SANITIZE_FLOAT_CAST) && TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE - && COMPLETE_TYPE_P (type) - && do_ubsan_in_current_function ()) + && COMPLETE_TYPE_P (type)) { if (in_late_binary_op) expr = save_expr (expr); diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index db293fe60d1..cc0b9e77ce6 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -6051,9 +6051,8 @@ grokdeclarator (const struct c_declarator *declarator, with known value. */ this_size_varies = size_varies = true; warn_variable_length_array (name, size); - if (flag_sanitize & SANITIZE_VLA - && decl_context == NORMAL - && do_ubsan_in_current_function ()) + if (sanitize_flags_p (SANITIZE_VLA) + && decl_context == NORMAL) { /* Evaluate the array size only once. */ size = c_save_expr (size); diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index c134280325d..3ee7cde2b39 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -6320,7 +6320,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, if (codel == BOOLEAN_TYPE || codel == COMPLEX_TYPE || (coder == REAL_TYPE && (codel == INTEGER_TYPE || codel == ENUMERAL_TYPE) - && (flag_sanitize & SANITIZE_FLOAT_CAST))) + && sanitize_flags_p (SANITIZE_FLOAT_CAST))) in_late_binary_op = true; ret = convert_and_check (expr_loc != UNKNOWN_LOCATION ? expr_loc : location, type, orig_rhs); @@ -9867,7 +9867,7 @@ c_finish_return (location_t loc, tree retval, tree origtype) || (TREE_CODE (TREE_TYPE (t)) == REAL_TYPE && (TREE_CODE (TREE_TYPE (res)) == INTEGER_TYPE || TREE_CODE (TREE_TYPE (res)) == ENUMERAL_TYPE) - && (flag_sanitize & SANITIZE_FLOAT_CAST))) + && sanitize_flags_p (SANITIZE_FLOAT_CAST))) in_late_binary_op = true; inner = t = convert (TREE_TYPE (res), t); in_late_binary_op = save; @@ -11747,9 +11747,8 @@ build_binary_op (location_t location, enum tree_code code, return error_mark_node; } - if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE - | SANITIZE_FLOAT_DIVIDE)) - && do_ubsan_in_current_function () + if (sanitize_flags_p ((SANITIZE_SHIFT + | SANITIZE_DIVIDE | SANITIZE_FLOAT_DIVIDE)) && (doing_div_or_mod || doing_shift) && !require_constant_value) { @@ -11758,10 +11757,10 @@ build_binary_op (location_t location, enum tree_code code, op1 = c_save_expr (op1); op0 = c_fully_fold (op0, false, NULL); op1 = c_fully_fold (op1, false, NULL); - if (doing_div_or_mod && (flag_sanitize & (SANITIZE_DIVIDE - | SANITIZE_FLOAT_DIVIDE))) + if (doing_div_or_mod && (sanitize_flags_p ((SANITIZE_DIVIDE + | SANITIZE_FLOAT_DIVIDE)))) instrument_expr = ubsan_instrument_division (location, op0, op1); - else if (doing_shift && (flag_sanitize & SANITIZE_SHIFT)) + else if (doing_shift && sanitize_flags_p (SANITIZE_SHIFT)) instrument_expr = ubsan_instrument_shift (location, code, op0, op1); } diff --git a/gcc/common.opt b/gcc/common.opt index de068447d31..039375ab9b2 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -224,7 +224,7 @@ unsigned int flag_sanitize ; What sanitizers should recover from errors Variable -unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN) +unsigned int flag_sanitize_recover = (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT | SANITIZE_KERNEL_ADDRESS) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN) fsanitize-coverage=trace-pc Common Report Var(flag_sanitize_coverage) diff --git a/gcc/convert.c b/gcc/convert.c index 54b0a5d8327..d9b9c418a46 100644 --- a/gcc/convert.c +++ b/gcc/convert.c @@ -917,8 +917,7 @@ convert_to_integer_1 (tree type, tree expr, bool dofold) return build1 (CONVERT_EXPR, type, expr); case REAL_TYPE: - if (flag_sanitize & SANITIZE_FLOAT_CAST - && do_ubsan_in_current_function ()) + if (sanitize_flags_p (SANITIZE_FLOAT_CAST)) { expr = save_expr (expr); tree check = ubsan_instrument_float_cast (loc, type, expr); diff --git a/gcc/cp/class.c b/gcc/cp/class.c index df4d73c4d30..df89aaf8bc5 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -458,7 +458,8 @@ build_base_path (enum tree_code code, else { tree t = expr; - if ((flag_sanitize & SANITIZE_VPTR) && fixed_type_p == 0) + if (sanitize_flags_p (SANITIZE_VPTR) + && fixed_type_p == 0) { t = cp_ubsan_maybe_instrument_cast_to_vbase (input_location, probe, expr); diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index 0678243b36f..f0a3ce9a280 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -1288,8 +1288,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) : OMP_CLAUSE_DEFAULT_PRIVATE); } } - if (flag_sanitize - & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_VPTR)) + if (sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_VPTR)) { /* The point here is to not sanitize static initializers. */ bool no_sanitize_p = wtd->no_sanitize_p; @@ -1476,11 +1475,11 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) *stmt_p = cplus_expand_constant (stmt); *walk_subtrees = 0; } - else if ((flag_sanitize - & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_VPTR)) + else if (sanitize_flags_p ((SANITIZE_NULL + | SANITIZE_ALIGNMENT | SANITIZE_VPTR)) && !wtd->no_sanitize_p) { - if ((flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)) + if (sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT) && TREE_CODE (stmt) == NOP_EXPR && TREE_CODE (TREE_TYPE (stmt)) == REFERENCE_TYPE) ubsan_maybe_instrument_reference (stmt); @@ -1496,9 +1495,9 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) = TREE_CODE (fn) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL && DECL_CONSTRUCTOR_P (TREE_OPERAND (fn, 0)); - if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)) + if (sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT)) ubsan_maybe_instrument_member_call (stmt, is_ctor); - if ((flag_sanitize & SANITIZE_VPTR) && !is_ctor) + if (sanitize_flags_p (SANITIZE_VPTR) && !is_ctor) cp_ubsan_maybe_instrument_member_call (stmt); } } @@ -1525,7 +1524,7 @@ cp_genericize_tree (tree* t_p, bool handle_invisiref_parm_p) cp_walk_tree (t_p, cp_genericize_r, &wtd, NULL); delete wtd.p_set; wtd.bind_expr_stack.release (); - if (flag_sanitize & SANITIZE_VPTR) + if (sanitize_flags_p (SANITIZE_VPTR)) cp_ubsan_instrument_member_accesses (t_p); } @@ -1648,8 +1647,7 @@ cp_genericize (tree fndecl) walk_tree's hash functionality. */ cp_genericize_tree (&DECL_SAVED_TREE (fndecl), true); - if (flag_sanitize & SANITIZE_RETURN - && do_ubsan_in_current_function ()) + if (sanitize_flags_p (SANITIZE_RETURN)) cp_ubsan_maybe_instrument_return (fndecl); /* Do everything else. */ diff --git a/gcc/cp/cp-ubsan.c b/gcc/cp/cp-ubsan.c index 9c8f6e67d54..51f50737a74 100644 --- a/gcc/cp/cp-ubsan.c +++ b/gcc/cp/cp-ubsan.c @@ -32,7 +32,7 @@ cp_ubsan_instrument_vptr_p (tree type) if (!flag_rtti || flag_sanitize_undefined_trap_on_error) return false; - if (!do_ubsan_in_current_function ()) + if (!sanitize_flags_p (SANITIZE_VPTR)) return false; if (type) diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index e83b542d424..afdb337acad 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -9529,8 +9529,7 @@ compute_array_index_type (tree name, tree size, tsubst_flags_t complain) stabilize_vla_size (itype); - if (flag_sanitize & SANITIZE_VLA - && do_ubsan_in_current_function ()) + if (sanitize_flags_p (SANITIZE_VLA)) { /* We have to add 1 -- in the ubsan routine we generate LE_EXPR rather than LT_EXPR. */ @@ -15066,7 +15065,7 @@ start_preparsed_function (tree decl1, tree attrs, int flags) if (!processing_template_decl && DECL_CONSTRUCTOR_P (decl1) - && (flag_sanitize & SANITIZE_VPTR) + && sanitize_flags_p (SANITIZE_VPTR) && !DECL_CLONED_FUNCTION_P (decl1) && !implicit_default_ctor_p (decl1)) cp_ubsan_maybe_initialize_vtbl_ptrs (current_class_ptr); diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 257d21133c1..9dcc038869b 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -3730,7 +3730,7 @@ one_static_initialization_or_destruction (tree decl, tree init, bool initp) if (init) { finish_expr_stmt (init); - if (flag_sanitize & SANITIZE_ADDRESS) + if (sanitize_flags_p (SANITIZE_ADDRESS, decl)) { varpool_node *vnode = varpool_node::get (decl); if (vnode) diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 47428b96f3d..981bb5301de 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -3991,8 +3991,7 @@ build_vec_init (tree base, tree maxindex, tree init, } /* Don't check an array new when -fno-exceptions. */ } - else if (flag_sanitize & SANITIZE_BOUNDS - && do_ubsan_in_current_function ()) + else if (sanitize_flags_p (SANITIZE_BOUNDS)) { /* Make sure the last element of the initializer is in bounds. */ finish_expr_stmt diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index c48cd5201e0..f174a8b3bd8 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -1099,9 +1099,7 @@ maybe_add_lambda_conv_op (tree type) { /* Don't UBsan this function; we're deliberately calling op() with a null object argument. */ - tree attrs = build_tree_list (get_identifier ("no_sanitize_undefined"), - NULL_TREE); - cplus_decl_attributes (&fn, attrs, 0); + add_no_sanitize_value (fn, SANITIZE_UNDEFINED); } add_method (type, fn, NULL_TREE); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 68fe19eeadb..0c25e0cbdfc 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -5167,10 +5167,9 @@ cp_build_binary_op (location_t location, if (build_type == NULL_TREE) build_type = result_type; - if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE - | SANITIZE_FLOAT_DIVIDE)) + if (sanitize_flags_p ((SANITIZE_SHIFT + | SANITIZE_DIVIDE | SANITIZE_FLOAT_DIVIDE)) && !processing_template_decl - && do_ubsan_in_current_function () && (doing_div_or_mod || doing_shift)) { /* OP0 and/or OP1 might have side-effects. */ @@ -5178,8 +5177,8 @@ cp_build_binary_op (location_t location, op1 = cp_save_expr (op1); op0 = fold_non_dependent_expr (op0); op1 = fold_non_dependent_expr (op1); - if (doing_div_or_mod && (flag_sanitize & (SANITIZE_DIVIDE - | SANITIZE_FLOAT_DIVIDE))) + if (doing_div_or_mod + && sanitize_flags_p (SANITIZE_DIVIDE | SANITIZE_FLOAT_DIVIDE)) { /* For diagnostics we want to use the promoted types without shorten_binary_op. So convert the arguments to the @@ -5193,7 +5192,7 @@ cp_build_binary_op (location_t location, } instrument_expr = ubsan_instrument_division (location, cop0, cop1); } - else if (doing_shift && (flag_sanitize & SANITIZE_SHIFT)) + else if (doing_shift && sanitize_flags_p (SANITIZE_SHIFT)) instrument_expr = ubsan_instrument_shift (location, code, op0, op1); } @@ -6707,7 +6706,7 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p, NULL, complain); expr = build_address (expr); - if (flag_sanitize & SANITIZE_VPTR) + if (sanitize_flags_p (SANITIZE_VPTR)) { tree ubsan_check = cp_ubsan_maybe_instrument_downcast (input_location, type, @@ -6851,7 +6850,7 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p, expr = build_base_path (MINUS_EXPR, expr, base, /*nonnull=*/false, complain); - if (flag_sanitize & SANITIZE_VPTR) + if (sanitize_flags_p (SANITIZE_VPTR)) { tree ubsan_check = cp_ubsan_maybe_instrument_downcast (input_location, type, diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 1f303bcb9aa..b5ed8ddebef 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -2894,6 +2894,18 @@ This has a similar effect as the @option{-fno-toplevel-reorder} option, but only applies to the marked symbols. +@item no_sanitize ("@var{sanitize_option}") +@cindex @code{no_sanitize} function attribute +The @code{no_sanitize} attribute on functions is used +to inform the compiler that it should not do sanitization of all options +mentioned in @var{sanitize_option}. A list of values acceptable by +@option{-fsanitize} option can be provided. + +@smallexample +void __attribute__ ((no_sanitize ("alignment", "object-size"))) +f () @{ /* @r{Do something.} */; @} +@end smallexample + @item no_sanitize_address @itemx no_address_safety_analysis @cindex @code{no_sanitize_address} function attribute diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index b7299648879..12d7089d5c4 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -10770,6 +10770,8 @@ This option enables strict instrumentation of array bounds. Most out of bounds accesses are detected, including flexible array members and flexible array member-like arrays. Initializers of variables with static storage are not instrumented. +Unlike other similar options, @option{-fsanitize=bounds-strict} is +not enabled by @option{-fsanitize=undefined}. @item -fsanitize=alignment @opindex fsanitize=alignment diff --git a/gcc/flag-types.h b/gcc/flag-types.h index d69f8f489b8..70f08a554ca 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -246,8 +246,8 @@ enum sanitize_code { | SANITIZE_NONNULL_ATTRIBUTE | SANITIZE_RETURNS_NONNULL_ATTRIBUTE | SANITIZE_OBJECT_SIZE | SANITIZE_VPTR, - SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST - | SANITIZE_BOUNDS_STRICT + SANITIZE_UNDEFINED_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST + | SANITIZE_BOUNDS_STRICT }; /* flag_vtable_verify initialization levels. */ diff --git a/gcc/gcc.c b/gcc/gcc.c index f78acd68606..6e47eb3ddeb 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -9312,7 +9312,8 @@ sanitize_spec_function (int argc, const char **argv) if (strcmp (argv[0], "thread") == 0) return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL; if (strcmp (argv[0], "undefined") == 0) - return ((flag_sanitize & (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT)) + return ((flag_sanitize + & (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT)) && !flag_sanitize_undefined_trap_on_error) ? "" : NULL; if (strcmp (argv[0], "leak") == 0) return ((flag_sanitize diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 9c86f158503..756b16852cf 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -3442,7 +3442,7 @@ optimize_atomic_compare_exchange_p (gimple *stmt) if (gimple_call_num_args (stmt) != 6 || !flag_inline_atomics || !optimize - || (flag_sanitize & (SANITIZE_THREAD | SANITIZE_ADDRESS)) != 0 + || sanitize_flags_p (SANITIZE_THREAD | SANITIZE_ADDRESS) || !gimple_call_builtin_p (stmt, BUILT_IN_NORMAL) || !gimple_vdef (stmt) || !gimple_vuse (stmt)) diff --git a/gcc/gimplify.c b/gcc/gimplify.c index a3001331181..b93507e4084 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -1621,7 +1621,7 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p) } if (asan_sanitize_use_after_scope () - && !asan_no_sanitize_address_p () + && sanitize_flags_p (SANITIZE_ADDRESS) && !is_vla && TREE_ADDRESSABLE (decl) && !TREE_STATIC (decl) @@ -12594,8 +12594,7 @@ gimplify_function_tree (tree fndecl) bind = new_bind; } - if ((flag_sanitize & SANITIZE_THREAD) != 0 - && !lookup_attribute ("no_sanitize_thread", DECL_ATTRIBUTES (fndecl))) + if (sanitize_flags_p (SANITIZE_THREAD)) { gcall *call = gimple_build_call_internal (IFN_TSAN_FUNC_EXIT, 0); gimple *tf = gimple_build_try (seq, call, GIMPLE_TRY_FINALLY); diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index 5f2371c6b26..6ed2f6076f0 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -256,17 +256,11 @@ report_inline_failed_reason (struct cgraph_edge *e) static bool sanitize_attrs_match_for_inline_p (const_tree caller, const_tree callee) { - /* Don't care if sanitizer is disabled */ - if (!(flag_sanitize & SANITIZE_ADDRESS)) - return true; - if (!caller || !callee) return true; - return !!lookup_attribute ("no_sanitize_address", - DECL_ATTRIBUTES (caller)) == - !!lookup_attribute ("no_sanitize_address", - DECL_ATTRIBUTES (callee)); + return sanitize_flags_p (SANITIZE_ADDRESS, caller) + == sanitize_flags_p (SANITIZE_ADDRESS, callee); } /* Used for flags where it is safe to inline when caller's value is diff --git a/gcc/opts.c b/gcc/opts.c index 890da03e6a1..4473fc66d75 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -1582,6 +1582,33 @@ parse_sanitizer_options (const char *p, location_t loc, int scode, return flags; } +unsigned int +parse_no_sanitize_attribute (char *value, char **wrong_argument) +{ + unsigned int flags = 0; + unsigned int i; + char *q = strtok (value, ","); + + while (q != NULL) + { + for (i = 0; sanitizer_opts[i].name != NULL; ++i) + if (strcmp (sanitizer_opts[i].name, q) == 0) + { + flags |= sanitizer_opts[i].flag; + if (sanitizer_opts[i].flag == SANITIZE_UNDEFINED) + flags |= SANITIZE_UNDEFINED_NONDEFAULT; + break; + } + + if (sanitizer_opts[i].name == NULL) + *wrong_argument = q; + + q = strtok (NULL, ","); + } + + return flags; +} + /* Handle target- and language-independent options. Return zero to generate an "unknown option" message. Only options that need extra handling need to be listed here; if you simply want @@ -1818,11 +1845,11 @@ common_handle_option (struct gcc_options *opts, case OPT_fsanitize_recover: if (value) opts->x_flag_sanitize_recover - |= (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT) + |= (SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT) & ~(SANITIZE_UNREACHABLE | SANITIZE_RETURN); else opts->x_flag_sanitize_recover - &= ~(SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT); + &= ~(SANITIZE_UNDEFINED | SANITIZE_UNDEFINED_NONDEFAULT); break; case OPT_O: diff --git a/gcc/opts.h b/gcc/opts.h index b3e64353c8a..680bfa7fd77 100644 --- a/gcc/opts.h +++ b/gcc/opts.h @@ -378,6 +378,8 @@ extern void print_ignored_options (void); extern void handle_common_deferred_options (void); unsigned int parse_sanitizer_options (const char *, location_t, int, unsigned int, int, bool); + +unsigned int parse_no_sanitize_attribute (char *value, char **wrong_argument); extern bool common_handle_option (struct gcc_options *opts, struct gcc_options *opts_set, const struct cl_decoded_option *decoded, diff --git a/gcc/testsuite/c-c++-common/ubsan/align-10.c b/gcc/testsuite/c-c++-common/ubsan/align-10.c new file mode 100644 index 00000000000..73d6078037f --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/align-10.c @@ -0,0 +1,56 @@ +/* Limit this to known non-strict alignment targets. */ +/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */ +/* { dg-options "-fsanitize=alignment" } */ + +struct S { int a; char b; long long c; short d[10]; }; +struct T { char a; long long b; }; +struct U { char a; int b; int c; long long d; struct S e; struct T f; } __attribute__((packed)); +struct V { long long a; struct S b; struct T c; struct U u; } v; + +__attribute__((noinline, noclone,no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) void +f1 (int *p, int *q, char *r, long long *s) +{ + *p = + *q + + *r + + *s; +} + + +__attribute__((noinline, noclone,no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) int +f2 (struct S *p) +{ + return p->a; +} + +__attribute__((noinline, noclone,no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) long long +f3 (struct S *p, int i) +{ + return p->c + + p->d[1] + + p->d[i]; +} + +__attribute__((noinline, noclone,no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) long long +f4 (long long *p) +{ + return *p; +} + +int +main () +{ + f1 (&v.u.b, &v.u.c, &v.u.a, &v.u.d); + if (f2 (&v.u.e) + f3 (&v.u.e, 4) + f4 (&v.u.f.b) != 0) + __builtin_abort (); + return 0; +} + +/* { dg-output "\.c:(14|15):\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */ +/* { dg-output "\.c:16:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment.*" } */ +/* { dg-output "\.c:(13|16):\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" } */ +/* { dg-output "\.c:23:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */ +/* { dg-output "\.c:(29|30):\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */ +/* { dg-output "\.c:30:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */ +/* { dg-output "\.c:31:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment.*" } */ +/* { dg-output "\.c:37:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'long long int', which requires \[48] byte alignment" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/attrib-5.c b/gcc/testsuite/c-c++-common/ubsan/attrib-5.c new file mode 100644 index 00000000000..29cb96e8ebc --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/attrib-5.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=undefined" } */ + +struct S { int a[16]; }; + +__attribute__((no_sanitize(("undefined")))) long long +foo (int *a, long long *b, struct S *c) +{ + return a[1] + *b + c->a[a[0]]; +} + +/* { dg-final { scan-assembler-not "__ubsan_handle" } } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/attrib-6.c b/gcc/testsuite/c-c++-common/ubsan/attrib-6.c new file mode 100644 index 00000000000..fc5afa035d2 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/attrib-6.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=undefined" } */ + +/* Test that we don't instrument functions marked with + no_sanitize_undefined attribute. */ + +struct S { int a[16]; }; + +__attribute__((no_sanitize("signed-integer-overflow,null,alignment"))) long long +foo (int *a, long long *b, struct S *c) +{ + return a[1] + *b + c->a[a[0]]; +} + +/* { dg-final { scan-assembler-not "__ubsan_handle" } } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/bounds-14.c b/gcc/testsuite/c-c++-common/ubsan/bounds-14.c new file mode 100644 index 00000000000..f94634b15f5 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/bounds-14.c @@ -0,0 +1,17 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bounds-strict" } */ + +struct V { int l; int a[1]; }; + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main (void) +{ + /* For strict, do instrument last array in a struct. */ + struct V *v = (struct V *) __builtin_malloc (sizeof (struct V) + 10); + v->a[1] = 1; + + return 0; +} + +/* { dg-output "index 1 out of bounds for type 'int \\\[1\\\]'" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/div-by-zero-8.c b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-8.c new file mode 100644 index 00000000000..6e54217f3a1 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/div-by-zero-8.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-fsanitize=integer-divide-by-zero" } */ + +void +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +foo (void) +{ + int A[-2 / -1] = {}; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-11.c b/gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-11.c new file mode 100644 index 00000000000..2d453f4e0fd --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-11.c @@ -0,0 +1,205 @@ +/* { dg-do run { target { lp64 || ilp32 } } } */ +/* { dg-options "-fsanitize=float-cast-overflow" } */ +/* { dg-additional-options "-msse2 -mfpmath=sse" { target { sse2_runtime && ia32 } } } */ + +#include <limits.h> +#include "float-cast.h" + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main (void) +{ + const double inf = __builtin_inf (); + const double nan = __builtin_nan (""); + volatile double d; + + volatile signed char sc; + d = SCHAR_MIN; + CHECK_BOUNDARY (sc, d); + d = 0.0; + CHECK_BOUNDARY (sc, d); + d = SCHAR_MAX; + CHECK_BOUNDARY (sc, d); + CHECK_NONNUMBERS (sc); + + volatile unsigned char uc; + d = UCHAR_MAX; + CHECK_BOUNDARY (uc, d); + d = 0.0; + CHECK_BOUNDARY (uc, d); + CHECK_NONNUMBERS (uc); + + volatile short int s; + d = SHRT_MIN; + CHECK_BOUNDARY (s, d); + d = 0.0; + CHECK_BOUNDARY (s, d); + d = SHRT_MAX; + CHECK_BOUNDARY (s, d); + CHECK_NONNUMBERS (s); + + volatile unsigned short int us; + d = USHRT_MAX; + CHECK_BOUNDARY (us, d); + d = 0.0; + CHECK_BOUNDARY (us, d); + CHECK_NONNUMBERS (us); + + volatile int i; + d = INT_MIN; + CHECK_BOUNDARY (i, d); + d = 0.0; + CHECK_BOUNDARY (i, d); + d = INT_MAX; + CHECK_BOUNDARY (i, d); + CHECK_NONNUMBERS (i); + + volatile unsigned int u; + d = UINT_MAX; + CHECK_BOUNDARY (u, d); + d = 0.0; + CHECK_BOUNDARY (u, d); + CHECK_NONNUMBERS (u); + + volatile long l; + /* 64-bit vs 32-bit longs matter causes too much of a headache. */ + d = 0.0; + CHECK_BOUNDARY (l, d); + CHECK_NONNUMBERS (l); + + volatile unsigned long ul; + d = 0.0; + CHECK_BOUNDARY (ul, d); + CHECK_NONNUMBERS (ul); + + volatile long long ll; + d = LLONG_MIN; + CHECK_BOUNDARY (ll, d); + d = 0.0; + CHECK_BOUNDARY (ll, d); + d = LLONG_MAX; + CHECK_BOUNDARY (ll, d); + CHECK_NONNUMBERS (ll); + + volatile unsigned long long ull; + d = ULLONG_MAX; + CHECK_BOUNDARY (ull, d); + d = 0.0; + CHECK_BOUNDARY (ull, d); + CHECK_NONNUMBERS (ull); + + return 0; +} + +/* { dg-output "value -133 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -129.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 128.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 132 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 256.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 260 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -32773 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -32769.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 32768 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 32768.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 32772 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 65536 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 65536.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 65540 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long unsigned int'" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-2.c b/gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-2.c new file mode 100644 index 00000000000..6cfec9564a9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/float-div-by-zero-2.c @@ -0,0 +1,27 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=float-divide-by-zero" } */ + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main (void) +{ + volatile float a = 1.3f; + volatile double b = 0.0; + volatile int c = 4; + volatile float res; + + res = a / b; + res = a / 0.0; + res = 2.7f / b; + res = 3.6 / (b = 0.0, b); + res = c / b; + res = b / c; + + return 0; +} + +/* { dg-output "division by zero\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/load-bool-enum-2.c b/gcc/testsuite/c-c++-common/ubsan/load-bool-enum-2.c new file mode 100644 index 00000000000..c969578ee48 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/load-bool-enum-2.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bool,enum" } */ + +#ifndef __cplusplus +#define bool _Bool +#endif +enum A { B = -3, C = 2 } a; +bool b; + +__attribute__((noinline, noclone)) enum A +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +foo (bool *p) +{ + *p = b; /* { dg-output "load-bool-enum-2.c:14:\[^\n\r]*runtime error: \[^\n\r]*load of value 4, which is not a valid value for type '(_B|b)ool'\[^\n\r]*(\n|\r\n|\r)*" } */ + return a; /* { dg-output "\[^\n\r]*load-bool-enum-2.c:15:\[^\n\r]*runtime error: \[^\n\r]*load of value 9, which is not a valid value for type 'A'" { target c++ } } */ +} + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main () +{ + char c = 4; + int d = 9; + if (sizeof (int) != sizeof (a) || sizeof (b) != 1) + return 0; + __builtin_memcpy (&a, &d, sizeof (int)); + __builtin_memcpy (&b, &c, 1); + bool e; + foo (&e); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c b/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c new file mode 100644 index 00000000000..22dbed1d15a --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/nonnull-6.c @@ -0,0 +1,40 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=nonnull-attribute,returns-nonnull-attribute" } */ + +int q, r; +void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h; + +__attribute__((returns_nonnull, nonnull (1, 3),no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,object-size,vptr")))) +void * +foo (void *p, void *q, void *r) +{ + a = p; + b = r; + return q; +} + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,object-size,vptr")))) +bar (const void *a, const void *b) +{ + int c = *(const int *) a; + int d = *(const int *) b; + return c - d; +} + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,object-size,vptr")))) +main () +{ + asm volatile ("" : : : "memory"); + d = foo (c, b, c); + e = foo (e, c, f); + g = foo (c, f, g); + __builtin_memset (d, '\0', q); + return 0; +} + +/* { dg-output "\.c:13:\[0-9]*:\[^\n\r]*null pointer returned from function declared to never return null\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\.c:31:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\.c:32:\[0-9]*:\[^\n\r]*null pointer passed as argument 3, which is declared to never be null\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\.c:33:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/null-12.c b/gcc/testsuite/c-c++-common/ubsan/null-12.c new file mode 100644 index 00000000000..a89a5434fae --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/null-12.c @@ -0,0 +1,13 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=null -w" } */ +/* { dg-shouldfail "ubsan" } */ + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main (void) +{ + int *p = 0; + return *p; +} + +/* { dg-output "load of null pointer of type 'int'" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/object-size-11.c b/gcc/testsuite/c-c++-common/ubsan/object-size-11.c new file mode 100644 index 00000000000..46ff75c6268 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/object-size-11.c @@ -0,0 +1,131 @@ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=undefined" } */ + +/* Sanity-test -fsanitize=object-size. We use -fsanitize=undefined option + to check that this feature doesn't clash with -fsanitize=bounds et al. */ + +#define N 20 + +__attribute__((noinline, noclone)) void +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,vptr")))) +f1 (int i) +{ + volatile int j; + char *p, *orig; + orig = p = (char *) __builtin_calloc (N, 1); + j = *(p + i); + j = p[i]; + p++; + j = p[i - 1]; + j = *(p + i - 1); + __builtin_free (orig); +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,vptr")))) +f2 (int i) +{ + volatile int j; + char a[N]; + __builtin_memset (a, 0, N); + j = *(a + i); + char *p = a; + j = *(p + i); + j = p[i]; + p += 10; + j = *(p + i - 10); + j = p[i - 10]; +} + +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,vptr")))) +f3 (int i) +{ + volatile int j; + int *p = (int *) __builtin_calloc (N, sizeof (*p)); + int *o = &p[i]; + j = *o; + j = o[0]; + __builtin_free (p); +} + +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,vptr")))) +f4 (void) +{ + /* The second argument to __builtin_calloc is intentional. */ + int *p = (int *) __builtin_calloc (3, 1); + *p = 42; + __builtin_free (p); +} + +/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^" } */ + +__attribute__((noinline, noclone)) void +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,vptr")))) +f5 (int *p) +{ + /* This is not instrumented. But don't ICE, etc. */ + volatile int i = p[N]; +} + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,vptr")))) +main () +{ + f1 (N); + f2 (N); + f3 (N); + f4 (); + int *p = (int *) __builtin_calloc (N, sizeof (*p)); + f5 (p); + __builtin_free (p); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/overflow-3.c b/gcc/testsuite/c-c++-common/ubsan/overflow-3.c new file mode 100644 index 00000000000..d061e8ed55b --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/overflow-3.c @@ -0,0 +1,262 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=signed-integer-overflow -fno-sanitize-recover=signed-integer-overflow" } */ + +#ifndef ASM1 +# define ASM1(a) /* Nothing */ +#endif +#ifndef ASM2 +# define ASM2(a, b) /* Nothing */ +#endif + +#define CHECK(A, B) ({ if ((A) != (B)) __builtin_abort (); }) + +#define FN1(T1, T2, OP) \ + ({ \ + T1 a = 14; \ + T2 b = 9; \ + ASM2 (a, b); \ + a OP b; \ + }) + +#define FN2(T, OP) \ + ({ \ + T a = 14; \ + ASM1 (a); \ + a OP 7; \ + }) + +#define FN3(T1, T2, OP) \ + ({ \ + T1 a = 4; \ + T2 b = 1; \ + ASM2 (a, b); \ + ~a OP b; \ + }) + +#define FN4(T1, T2, OP) \ + ({ \ + T1 a = 4; \ + T2 b = 1; \ + ASM2 (a, b); \ + a OP ~b; \ + }) + +#define FN5(T) \ + ({ \ + T a = 77; \ + ASM1 (a); \ + -a; \ + }) + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main (void) +{ + CHECK (FN1 (char, char, +), 23); + CHECK (FN1 (char, char, -), 5); + CHECK (FN1 (char, char, *), 126); + CHECK (FN1 (unsigned char, unsigned char, +), 23); + CHECK (FN1 (unsigned char, unsigned char, -), 5); + CHECK (FN1 (unsigned char, unsigned char, *), 126); + CHECK (FN1 (short, short, +), 23); + CHECK (FN1 (short, short, -), 5); + CHECK (FN1 (short, short, *), 126); + CHECK (FN1 (unsigned short, unsigned short, +), 23); + CHECK (FN1 (unsigned short, unsigned short, -), 5); + CHECK (FN1 (unsigned short, unsigned short, *), 126); + CHECK (FN1 (int, int, +), 23); + CHECK (FN1 (int, int, -), 5); + CHECK (FN1 (int, int, *), 126); + CHECK (FN1 (unsigned int, unsigned int, +), 23); + CHECK (FN1 (unsigned int, unsigned int, -), 5); + CHECK (FN1 (unsigned int, unsigned int, *), 126); + CHECK (FN1 (long int, long int, +), 23); + CHECK (FN1 (long int, long int, -), 5); + CHECK (FN1 (long int, long int, *), 126); + CHECK (FN1 (unsigned long int, unsigned long int, +), 23); + CHECK (FN1 (unsigned long int, unsigned long int, -), 5); + CHECK (FN1 (unsigned long int, unsigned long int, *), 126); + CHECK (FN1 (long long int, long int, +), 23); + CHECK (FN1 (long long int, long int, -), 5); + CHECK (FN1 (long long int, long int, *), 126); + CHECK (FN1 (unsigned long long int, unsigned long long int, +), 23); + CHECK (FN1 (unsigned long long int, unsigned long long int, -), 5); + CHECK (FN1 (unsigned long long int, unsigned long long int, *), 126); + CHECK (FN1 (int, unsigned char, +), 23); + CHECK (FN1 (int, unsigned char, -), 5); + CHECK (FN1 (int, unsigned char, *), 126); + CHECK (FN1 (unsigned char, int, +), 23); + CHECK (FN1 (unsigned char, int, -), 5); + CHECK (FN1 (unsigned char, int, *), 126); + CHECK (FN1 (int, long int, +), 23); + CHECK (FN1 (int, long int, -), 5); + CHECK (FN1 (int, long int, *), 126); + CHECK (FN1 (long int, int, +), 23); + CHECK (FN1 (long int, int, -), 5); + CHECK (FN1 (long int, int, *), 126); + CHECK (FN1 (unsigned int, int, +), 23); + CHECK (FN1 (unsigned int, int, -), 5); + CHECK (FN1 (unsigned int, int, *), 126); + CHECK (FN1 (int, unsigned int, +), 23); + CHECK (FN1 (int, unsigned int, -), 5); + CHECK (FN1 (int, unsigned int, *), 126); + CHECK (FN1 (unsigned long int, int, +), 23); + CHECK (FN1 (unsigned long int, int, -), 5); + CHECK (FN1 (unsigned long int, int, *), 126); + CHECK (FN1 (int, unsigned long int, +), 23); + CHECK (FN1 (int, unsigned long int, -), 5); + CHECK (FN1 (int, unsigned long int, *), 126); + + CHECK (FN2 (char, +), 21); + CHECK (FN2 (char, -), 7); + CHECK (FN2 (char, *), 98); + CHECK (FN2 (unsigned char, +), 21); + CHECK (FN2 (unsigned char, -), 7); + CHECK (FN2 (unsigned char, *), 98); + CHECK (FN2 (short, +), 21); + CHECK (FN2 (short, -), 7); + CHECK (FN2 (short, *), 98); + CHECK (FN2 (unsigned short, +), 21); + CHECK (FN2 (unsigned short, -), 7); + CHECK (FN2 (unsigned short, *), 98); + CHECK (FN2 (int, +), 21); + CHECK (FN2 (int, -), 7); + CHECK (FN2 (int, *), 98); + CHECK (FN2 (unsigned int, +), 21); + CHECK (FN2 (unsigned int, -), 7); + CHECK (FN2 (unsigned int, *), 98); + CHECK (FN2 (long int, +), 21); + CHECK (FN2 (long int, -), 7); + CHECK (FN2 (long int, *), 98); + CHECK (FN2 (unsigned long int, +), 21); + CHECK (FN2 (unsigned long int, -), 7); + CHECK (FN2 (unsigned long int, *), 98); + CHECK (FN2 (long long int, +), 21); + CHECK (FN2 (long long int, -), 7); + CHECK (FN2 (long long int, *), 98); + CHECK (FN2 (unsigned long long int, +), 21); + CHECK (FN2 (unsigned long long int, -), 7); + CHECK (FN2 (unsigned long long int, *), 98); + + CHECK (FN3 (char, char, +), -4); + CHECK (FN3 (char, char, -), -6); + CHECK (FN3 (char, char, *), -5); + CHECK (FN3 (unsigned char, unsigned char, +), -4); + CHECK (FN3 (unsigned char, unsigned char, -), -6); + CHECK (FN3 (unsigned char, unsigned char, *), -5); + CHECK (FN3 (short, short, +), -4); + CHECK (FN3 (short, short, -), -6); + CHECK (FN3 (short, short, *), -5); + CHECK (FN3 (unsigned short, unsigned short, +), -4); + CHECK (FN3 (unsigned short, unsigned short, -), -6); + CHECK (FN3 (unsigned short, unsigned short, *), -5); + CHECK (FN3 (int, int, +), -4); + CHECK (FN3 (int, int, -), -6); + CHECK (FN3 (int, int, *), -5); + CHECK (FN3 (unsigned int, unsigned int, +), -4); + CHECK (FN3 (unsigned int, unsigned int, -), -6); + CHECK (FN3 (unsigned int, unsigned int, *), -5); + CHECK (FN3 (long int, long int, +), -4); + CHECK (FN3 (long int, long int, -), -6); + CHECK (FN3 (long int, long int, *), -5); + CHECK (FN3 (unsigned long int, unsigned long int, +), -4); + CHECK (FN3 (unsigned long int, unsigned long int, -), -6); + CHECK (FN3 (unsigned long int, unsigned long int, *), -5); + CHECK (FN3 (long long int, long int, +), -4); + CHECK (FN3 (long long int, long int, -), -6); + CHECK (FN3 (long long int, long int, *), -5); + CHECK (FN3 (unsigned long long int, unsigned long long int, +), -4); + CHECK (FN3 (unsigned long long int, unsigned long long int, -), -6); + CHECK (FN3 (unsigned long long int, unsigned long long int, *), -5); + CHECK (FN3 (int, unsigned char, +), -4); + CHECK (FN3 (int, unsigned char, -), -6); + CHECK (FN3 (int, unsigned char, *), -5); + CHECK (FN3 (unsigned char, int, +), -4); + CHECK (FN3 (unsigned char, int, -), -6); + CHECK (FN3 (unsigned char, int, *), -5); + CHECK (FN3 (int, long int, +), -4); + CHECK (FN3 (int, long int, -), -6); + CHECK (FN3 (int, long int, *), -5); + CHECK (FN3 (long int, int, +), -4); + CHECK (FN3 (long int, int, -), -6); + CHECK (FN3 (long int, int, *), -5); + CHECK (FN3 (unsigned int, int, +), -4); + CHECK (FN3 (unsigned int, int, -), -6); + CHECK (FN3 (unsigned int, int, *), -5); + CHECK (FN3 (int, unsigned int, +), -4); + CHECK (FN3 (int, unsigned int, -), -6); + CHECK (FN3 (int, unsigned int, *), -5); + CHECK (FN3 (unsigned long int, int, +), -4); + CHECK (FN3 (unsigned long int, int, -), -6); + CHECK (FN3 (unsigned long int, int, *), -5); + CHECK (FN3 (int, unsigned long int, +), -4); + CHECK (FN3 (int, unsigned long int, -), -6); + CHECK (FN3 (int, unsigned long int, *), -5); + + CHECK (FN4 (char, char, +), 2); + CHECK (FN4 (char, char, -), 6); + CHECK (FN4 (char, char, *), -8); + CHECK (FN4 (unsigned char, unsigned char, +), 2); + CHECK (FN4 (unsigned char, unsigned char, -), 6); + CHECK (FN4 (unsigned char, unsigned char, *), -8); + CHECK (FN4 (short, short, +), 2); + CHECK (FN4 (short, short, -), 6); + CHECK (FN4 (short, short, *), -8); + CHECK (FN4 (unsigned short, unsigned short, +), 2); + CHECK (FN4 (unsigned short, unsigned short, -), 6); + CHECK (FN4 (unsigned short, unsigned short, *), -8); + CHECK (FN4 (int, int, +), 2); + CHECK (FN4 (int, int, -), 6); + CHECK (FN4 (int, int, *), -8); + CHECK (FN4 (unsigned int, unsigned int, +), 2); + CHECK (FN4 (unsigned int, unsigned int, -), 6); + CHECK (FN4 (unsigned int, unsigned int, *), -8); + CHECK (FN4 (long int, long int, +), 2); + CHECK (FN4 (long int, long int, -), 6); + CHECK (FN4 (long int, long int, *), -8); + CHECK (FN4 (unsigned long int, unsigned long int, +), 2); + CHECK (FN4 (unsigned long int, unsigned long int, -), 6); + CHECK (FN4 (unsigned long int, unsigned long int, *), -8); + CHECK (FN4 (long long int, long int, +), 2); + CHECK (FN4 (long long int, long int, -), 6); + CHECK (FN4 (long long int, long int, *), -8); + CHECK (FN4 (unsigned long long int, unsigned long long int, +), 2); + CHECK (FN4 (unsigned long long int, unsigned long long int, -), 6); + CHECK (FN4 (unsigned long long int, unsigned long long int, *), -8); + CHECK (FN4 (int, unsigned char, +), 2); + CHECK (FN4 (int, unsigned char, -), 6); + CHECK (FN4 (int, unsigned char, *), -8); + CHECK (FN4 (unsigned char, int, +), 2); + CHECK (FN4 (unsigned char, int, -), 6); + CHECK (FN4 (unsigned char, int, *), -8); + CHECK (FN4 (int, long int, +), 2); + CHECK (FN4 (int, long int, -), 6); + CHECK (FN4 (int, long int, *), -8); + CHECK (FN4 (long int, int, +), 2); + CHECK (FN4 (long int, int, -), 6); + CHECK (FN4 (long int, int, *), -8); + CHECK (FN4 (unsigned int, int, +), 2); + CHECK (FN4 (unsigned int, int, -), 6); + CHECK (FN4 (unsigned int, int, *), -8); + CHECK (FN4 (int, unsigned int, +), 2); + CHECK (FN4 (int, unsigned int, -), 6); + CHECK (FN4 (int, unsigned int, *), -8); + CHECK (FN4 (unsigned long int, int, +), 2); + CHECK (FN4 (unsigned long int, int, -), 6); + CHECK (FN4 (unsigned long int, int, *), -8); + CHECK (FN4 (int, unsigned long int, +), 2); + CHECK (FN4 (int, unsigned long int, -), 6); + CHECK (FN4 (int, unsigned long int, *), -8); + + CHECK (FN5 (char), -77); + CHECK (FN5 (unsigned char), -77); + CHECK (FN5 (short), -77); + CHECK (FN5 (unsigned short), -77); + CHECK (FN5 (int), -77); + CHECK (FN5 (unsigned int), -77); + CHECK (FN5 (long int), -77); + CHECK (FN5 (unsigned long int), -77); + CHECK (FN5 (long long int), -77); + CHECK (FN5 (unsigned long long int), -77); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/ubsan/unreachable-5.c b/gcc/testsuite/c-c++-common/ubsan/unreachable-5.c new file mode 100644 index 00000000000..1f448dae3b5 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/unreachable-5.c @@ -0,0 +1,11 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=unreachable" } */ +/* { dg-shouldfail "ubsan" } */ + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main (void) +{ + __builtin_unreachable (); +} + /* { dg-output "execution reached a __builtin_unreachable\\(\\) call" } */ diff --git a/gcc/testsuite/c-c++-common/ubsan/vla-5.c b/gcc/testsuite/c-c++-common/ubsan/vla-5.c new file mode 100644 index 00000000000..e7f0c3a7273 --- /dev/null +++ b/gcc/testsuite/c-c++-common/ubsan/vla-5.c @@ -0,0 +1,14 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=vla-bound -fno-sanitize-recover=vla-bound" } */ + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main (void) +{ + int x = 1; + /* Check that the size of an array is evaluated only once. */ + int a[++x]; + if (x != 2) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/g++.dg/ubsan/return-8.C b/gcc/testsuite/g++.dg/ubsan/return-8.C new file mode 100644 index 00000000000..10b3c5880be --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/return-8.C @@ -0,0 +1,29 @@ +// { dg-do run } +// { dg-options "-fsanitize=return" } +// { dg-shouldfail "ubsan" } + +struct S { S (); ~S (); }; + +S::S () {} +S::~S () {} + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +foo (int x) +{ + S a; + { + S b; + if (x) + return 1; + } +} + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main () +{ + foo (0); +} + +// { dg-output "execution reached the end of a value-returning function without returning a value" } diff --git a/gcc/testsuite/g++.dg/ubsan/vptr-12.C b/gcc/testsuite/g++.dg/ubsan/vptr-12.C new file mode 100644 index 00000000000..9d4c898b4ca --- /dev/null +++ b/gcc/testsuite/g++.dg/ubsan/vptr-12.C @@ -0,0 +1,185 @@ +// { dg-do run { target { ilp32 || lp64 } } } +// { dg-options "-fsanitize=vptr" } + +struct S +{ + S() : a(0) {} + ~S() {} + int a; + int f() { return 0; } + virtual int v() { return 0; } +}; + +struct T : S +{ + T() : b(0) {} + int b; + int g() { return 0; } + virtual int v() { return 1; } +}; + +struct U : S, T { virtual int v() { return 2; } }; // { dg-warning "direct base .S. inaccessible in .U. due to ambiguity" } +struct V : S {}; + +void +foo () +{ + T t; + (void)t.a; + (void)t.b; + (void)t.f(); + (void)t.g(); + (void)t.v(); + (void)t.S::v(); + + U u; + (void)u.T::a; + (void)u.b; + (void)u.T::f(); + (void)u.g(); + (void)u.v(); + (void)u.T::v(); + (void)((T&)u).S::v(); +} + +T *x; + +__attribute__((noinline, noclone, no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size")))) int +bar (T *p, int q) +{ + switch (q) + { + // These shouldn't fail: + case 0x10: + case 0x20: + case 0x30: + case 0x40: + { + T &r = *p; + break; + } + case 0x21: + case 0x31: + return p->b; + case 0x22: + case 0x32: + return p->g (); + case 0x23: + case 0x33: + x = static_cast<T*>(reinterpret_cast<S*>(p)); + break; + case 0x44: + return reinterpret_cast<U*>(p)->v() - 2; + // These should: + case 0x11: + return p->b; + // { dg-output "\[^\n\r]*vptr-12.C:75:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" } + // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" } + // { dg-output " .. .. .. .. .. .. .. .. .. .. .. .. \[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" } + case 0x12: + return p->g (); + // { dg-output "\[^\n\r]*vptr-12.C:82:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" } + // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" } + // { dg-output " .. .. .. .. .. .. .. .. .. .. .. .. \[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" } + case 0x13: + x = static_cast<T*>(reinterpret_cast<S*>(p)); + break; + // { dg-output "\[^\n\r]*vptr-12.C:89:\[0-9]*: runtime error: downcast of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" } + // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'S'(\n|\r\n|\r)" } + // { dg-output " .. .. .. .. .. .. .. .. .. .. .. .. \[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " vptr for 'S'\[^\n\r]*(\n|\r\n|\r)" } + case 0x34: + return reinterpret_cast<U*>(p)->v() - 2; + // { dg-output "\[^\n\r]*vptr-12.C:97:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'U'(\n|\r\n|\r)" } + // { dg-output "0x\[0-9a-fA-F]*: note: object is base class subobject at offset 16 within object of type 'U'(\n|\r\n|\r)" { target lp64 } } + // { dg-output "0x\[0-9a-fA-F]*: note: object is base class subobject at offset 8 within object of type 'U'(\n|\r\n|\r)" { target ilp32 } } + // { dg-output " .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. \[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " \\^ ~~~~~~~~~~~~~~~~~~~~~~~(\n|\r\n|\r)" { target lp64 } } + // { dg-output " vptr for 'T' base class of 'U'\[^\n\r]*(\n|\r\n|\r)" { target lp64 } } + // { dg-output " \\^ ~~~~~~~~~~~(\n|\r\n|\r)" { target ilp32 } } + // { dg-output " vptr for 'T' base class of 'U'\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } } + case 0x41: + return p->b; + // { dg-output "\[^\n\r]*vptr-12.C:107:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" } + // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" } + // { dg-output " .. .. .. .. .. .. .. .. .. .. .. .. \[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" } + case 0x42: + return p->g (); + // { dg-output "\[^\n\r]*vptr-12.C:114:\[0-9]*: runtime error: member call on address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" } + // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" } + // { dg-output " .. .. .. .. .. .. .. .. .. .. .. .. \[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" } + case 0x43: + x = static_cast<T*>(reinterpret_cast<S*>(p)); + break; + // { dg-output "\[^\n\r]*vptr-12.C:121:\[0-9]*: runtime error: downcast of address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" } + // { dg-output "0x\[0-9a-fA-F]*: note: object is of type 'U'(\n|\r\n|\r)" } + // { dg-output " .. .. .. .. .. .. .. .. .. .. .. .. \[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" } + // { dg-output " vptr for 'U'\[^\n\r]*(\n|\r\n|\r)" } + case 0x51: + return p->b; + // { dg-output "\[^\n\r]*vptr-12.C:129:\[0-9]*: runtime error: member access within address 0x\[0-9a-fA-F]* which does not point to an object of type 'T'(\n|\r\n|\r)" } + // { dg-output "0x\[0-9a-fA-F]*: note: object has invalid vptr(\n|\r\n|\r)" } + // { dg-output " .. .. .. .. 00 00 00 00 00 00 00 00 \[^\n\r]*(\n|\r\n|\r)" { target lp64 } } + // { dg-output " \\^~~~~~~~~~~~~~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" { target lp64 } } + // { dg-output " ?.. .. .. .. ?00 00 00 00 ?.. .. .. .. ?\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } } + // { dg-output " \\^~~~~~~~~~~\[^\n\r]*(\n|\r\n|\r)" { target ilp32 } } + // { dg-output " invalid vptr" } + } + return 0; +} + +char b[sizeof (U)] __attribute__((aligned (__alignof__ (U)))) = {}; + +__attribute__((noinline, noclone, no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size")))) void +baz (int q) +{ + T *p = 0; + S *s = 0; + U *u = 0; + switch (q) + { + case 0x10: case 0x11: case 0x12: case 0x13: + s = new S; + bar (reinterpret_cast<T *>(s), q); + delete s; + break; + case 0x20: case 0x21: case 0x22: case 0x23: + p = new T; + bar (p, q); + delete p; + break; + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: + u = new U; + bar (u, q); + delete u; + break; + case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: + u = new U; + bar (reinterpret_cast<T *>(u), q); + delete u; + break; + case 0x51: + p = reinterpret_cast<T*>(b); + bar (p, q); + break; + } +} + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size")))) +main () +{ + foo (); + for (int q = 0; q < 0x52; q++) + baz (q); +} diff --git a/gcc/testsuite/gcc.dg/ubsan/bounds-4.c b/gcc/testsuite/gcc.dg/ubsan/bounds-4.c new file mode 100644 index 00000000000..03e0b8134f7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/bounds-4.c @@ -0,0 +1,20 @@ +/* PR sanitizer/65280 */ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bounds" } */ + +void +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +foo (int n, int (*b)[n]) +{ + (*b)[n] = 1; +} + +int +__attribute__((no_sanitize(("shift,shift-base,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main () +{ + int a[20]; + foo (3, (int (*)[3]) &a); +} + +/* { dg-output "index 3 out of bounds for type 'int \\\[\\\*\\\]'" } */ diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-7.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-7.c new file mode 100644 index 00000000000..3f2813ecbe7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-7.c @@ -0,0 +1,11 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w -std=c99" } */ + +int +__attribute__((no_sanitize(("shift-exponent,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main (void) +{ + int a = -42; + a << 1; +} +/* { dg-output "left shift of negative value -42" } */ diff --git a/gcc/testsuite/gcc.dg/ubsan/c99-shift-8.c b/gcc/testsuite/gcc.dg/ubsan/c99-shift-8.c new file mode 100644 index 00000000000..d62e9124aac --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/c99-shift-8.c @@ -0,0 +1,12 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift-exponent -w -std=c99" } */ + +int +__attribute__((no_sanitize(("shift-base,integer-divide-by-zero,unreachable,vla-bound,return,null,signed-integer-overflow,bool,enum,float-divide-by-zero,float-cast-overflow,bounds,bounds-strict,alignment,nonnull-attribute,returns-nonnull-attribute,object-size,vptr")))) +main (void) +{ + int b = 43; + volatile int c = 129; + b << c; +} +/* { dg-output "shift exponent 129 is too large for" } */ diff --git a/gcc/tree.c b/gcc/tree.c index 2a603866522..1420c3e4dc7 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -14264,6 +14264,23 @@ nonnull_arg_p (const_tree arg) return false; } +/* Return true when flag_sanitize & FLAG is non-zero. If FN is non-null, + remove all flags mentioned in "no_sanitize_flags" of DECL_ATTRIBUTES. */ + +bool +sanitize_flags_p (unsigned int flag, const_tree fn) +{ + if (fn == NULL) + return false; + + unsigned int result_flags = flag_sanitize & flag; + tree value = lookup_attribute ("no_sanitize_flags", DECL_ATTRIBUTES (fn)); + if (value) + result_flags &= ~tree_to_uhwi (TREE_VALUE (value)); + + return result_flags; +} + /* Combine LOC and BLOCK to a combined adhoc loc, retaining any range information. */ diff --git a/gcc/tree.h b/gcc/tree.h index 62cd7bb19c3..a9185a9e29d 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -4217,6 +4217,10 @@ extern tree merge_dllimport_decl_attributes (tree, tree); /* Handle a "dllimport" or "dllexport" attribute. */ extern tree handle_dll_attribute (tree *, tree, tree, int, bool *); + +extern bool sanitize_flags_p (unsigned int flag, + const_tree fn = current_function_decl); + /* Returns true iff CAND and BASE have equivalent language-specific qualifiers. */ diff --git a/gcc/tsan.c b/gcc/tsan.c index 317bf6d2dac..4832b3387a0 100644 --- a/gcc/tsan.c +++ b/gcc/tsan.c @@ -881,11 +881,9 @@ public: /* opt_pass methods: */ opt_pass * clone () { return new pass_tsan (m_ctxt); } - virtual bool gate (function *) + virtual bool gate (function *fn) { - return ((flag_sanitize & SANITIZE_THREAD) != 0 - && !lookup_attribute ("no_sanitize_thread", - DECL_ATTRIBUTES (current_function_decl))); + return sanitize_flags_p (SANITIZE_THREAD, fn->decl); } virtual unsigned int execute (function *) { return tsan_pass (); } @@ -923,11 +921,9 @@ public: {} /* opt_pass methods: */ - virtual bool gate (function *) + virtual bool gate (function *fn) { - return ((flag_sanitize & SANITIZE_THREAD) != 0 && !optimize - && !lookup_attribute ("no_sanitize_thread", - DECL_ATTRIBUTES (current_function_decl))); + return (sanitize_flags_p (SANITIZE_THREAD, fn->decl) && !optimize); } virtual unsigned int execute (function *) { return tsan_pass (); } diff --git a/gcc/ubsan.c b/gcc/ubsan.c index d5422a1283d..8a6ab13da8b 100644 --- a/gcc/ubsan.c +++ b/gcc/ubsan.c @@ -754,7 +754,7 @@ ubsan_expand_null_ifn (gimple_stmt_iterator *gsip) gsi_insert_before (&gsi, g, GSI_SAME_STMT); } } - check_null = (flag_sanitize & SANITIZE_NULL) != 0; + check_null = sanitize_flags_p (SANITIZE_NULL); if (check_align == NULL_TREE && !check_null) { @@ -1178,13 +1178,13 @@ instrument_mem_ref (tree mem, tree base, gimple_stmt_iterator *iter, { enum ubsan_null_ckind ikind = is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF; unsigned int align = 0; - if (flag_sanitize & SANITIZE_ALIGNMENT) + if (sanitize_flags_p (SANITIZE_ALIGNMENT)) { align = min_align_of_type (TREE_TYPE (base)); if (align <= 1) align = 0; } - if (align == 0 && (flag_sanitize & SANITIZE_NULL) == 0) + if (align == 0 && !sanitize_flags_p (SANITIZE_NULL)) return; tree t = TREE_OPERAND (base, 0); if (!POINTER_TYPE_P (TREE_TYPE (t))) @@ -1350,13 +1350,14 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi) tree type = TREE_TYPE (rhs); tree minv = NULL_TREE, maxv = NULL_TREE; - if (TREE_CODE (type) == BOOLEAN_TYPE && (flag_sanitize & SANITIZE_BOOL)) + if (TREE_CODE (type) == BOOLEAN_TYPE + && sanitize_flags_p (SANITIZE_BOOL)) { minv = boolean_false_node; maxv = boolean_true_node; } else if (TREE_CODE (type) == ENUMERAL_TYPE - && (flag_sanitize & SANITIZE_ENUM) + && sanitize_flags_p (SANITIZE_ENUM) && TREE_TYPE (type) != NULL_TREE && TREE_CODE (TREE_TYPE (type)) == INTEGER_TYPE && (TYPE_PRECISION (TREE_TYPE (type)) @@ -1915,16 +1916,6 @@ instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs) gsi_insert_before (gsi, g, GSI_SAME_STMT); } -/* True if we want to play UBSan games in the current function. */ - -bool -do_ubsan_in_current_function () -{ - return (current_function_decl != NULL_TREE - && !lookup_attribute ("no_sanitize_undefined", - DECL_ATTRIBUTES (current_function_decl))); -} - namespace { const pass_data pass_data_ubsan = @@ -1948,15 +1939,14 @@ public: {} /* opt_pass methods: */ - virtual bool gate (function *) + virtual bool gate (function *fn) { - return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW - | SANITIZE_BOOL | SANITIZE_ENUM - | SANITIZE_ALIGNMENT - | SANITIZE_NONNULL_ATTRIBUTE - | SANITIZE_RETURNS_NONNULL_ATTRIBUTE - | SANITIZE_OBJECT_SIZE) - && do_ubsan_in_current_function (); + return sanitize_flags_p ((SANITIZE_NULL | SANITIZE_SI_OVERFLOW + | SANITIZE_BOOL | SANITIZE_ENUM + | SANITIZE_ALIGNMENT + | SANITIZE_NONNULL_ATTRIBUTE + | SANITIZE_RETURNS_NONNULL_ATTRIBUTE + | SANITIZE_OBJECT_SIZE), fn->decl); } virtual unsigned int execute (function *); @@ -1983,11 +1973,11 @@ pass_ubsan::execute (function *fun) continue; } - if ((flag_sanitize & SANITIZE_SI_OVERFLOW) + if ((sanitize_flags_p (SANITIZE_SI_OVERFLOW, fun->decl)) && is_gimple_assign (stmt)) instrument_si_overflow (gsi); - if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT)) + if (sanitize_flags_p (SANITIZE_NULL | SANITIZE_ALIGNMENT, fun->decl)) { if (gimple_store_p (stmt)) instrument_null (gsi, true); @@ -1995,14 +1985,14 @@ pass_ubsan::execute (function *fun) instrument_null (gsi, false); } - if (flag_sanitize & (SANITIZE_BOOL | SANITIZE_ENUM) + if (sanitize_flags_p (SANITIZE_BOOL | SANITIZE_ENUM, fun->decl) && gimple_assign_load_p (stmt)) { instrument_bool_enum_load (&gsi); bb = gimple_bb (stmt); } - if ((flag_sanitize & SANITIZE_NONNULL_ATTRIBUTE) + if (sanitize_flags_p (SANITIZE_NONNULL_ATTRIBUTE, fun->decl) && is_gimple_call (stmt) && !gimple_call_internal_p (stmt)) { @@ -2010,14 +2000,14 @@ pass_ubsan::execute (function *fun) bb = gimple_bb (stmt); } - if ((flag_sanitize & SANITIZE_RETURNS_NONNULL_ATTRIBUTE) + if (sanitize_flags_p (SANITIZE_RETURNS_NONNULL_ATTRIBUTE, fun->decl) && gimple_code (stmt) == GIMPLE_RETURN) { instrument_nonnull_return (&gsi); bb = gimple_bb (stmt); } - if (flag_sanitize & SANITIZE_OBJECT_SIZE) + if (sanitize_flags_p (SANITIZE_OBJECT_SIZE, fun->decl)) { if (gimple_store_p (stmt)) instrument_object_size (&gsi, true); diff --git a/gcc/ubsan.h b/gcc/ubsan.h index c4ebec946a6..bdfaccf74db 100644 --- a/gcc/ubsan.h +++ b/gcc/ubsan.h @@ -42,7 +42,6 @@ enum ubsan_print_style { UBSAN_PRINT_ARRAY }; -extern bool do_ubsan_in_current_function (void); extern bool ubsan_expand_bounds_ifn (gimple_stmt_iterator *); extern bool ubsan_expand_null_ifn (gimple_stmt_iterator *); extern bool ubsan_expand_objsize_ifn (gimple_stmt_iterator *); -- 2.11.0