From: Martin Liska Date: Tue, 5 Dec 2017 09:23:25 +0000 (+0100) Subject: invoke.texi: Document the options. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=05abad4cca62d3725175ccf628b74638fe43e043;p=gcc.git invoke.texi: Document the options. gcc/ * doc/invoke.texi: Document the options. * flag-types.h (enum sanitize_code): Add SANITIZE_POINTER_COMPARE and SANITIZE_POINTER_SUBTRACT. * ipa-inline.c (sanitize_attrs_match_for_inline_p): Add handling of SANITIZE_POINTER_COMPARE and SANITIZE_POINTER_SUBTRACT. * opts.c: Define new sanitizer options. * sanitizer.def (BUILT_IN_ASAN_POINTER_COMPARE): Likewise. (BUILT_IN_ASAN_POINTER_SUBTRACT): Likewise. gcc/c/ * c-typeck.c (pointer_diff): Add new argument and instrument pointer subtraction. (build_binary_op): Similar for pointer comparison. gcc/cp/ * typeck.c (pointer_diff): Add new argument and instrument pointer subtraction. (cp_build_binary_op): Create compound expression if doing an instrumentation. gcc/testsuite/ * c-c++-common/asan/pointer-compare-1.c: New test. * c-c++-common/asan/pointer-compare-2.c: New test. * c-c++-common/asan/pointer-subtract-1.c: New test. * c-c++-common/asan/pointer-subtract-2.c: New test. * c-c++-common/asan/pointer-subtract-3.c: New test. * c-c++-common/asan/pointer-subtract-4.c: New test. libsanitizer/ * asan/asan_descriptions.cc: Cherry-pick upstream r319668. * asan/asan_descriptions.h: Likewise. * asan/asan_report.cc: Likewise. * asan/asan_thread.cc: Likewise. * asan/asan_thread.h: Likewise. Co-Authored-By: Jakub Jelinek From-SVN: r255404 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 879fb99b313..3959ec6f54e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,15 @@ +2017-12-05 Martin Liska + Jakub Jelinek + + * doc/invoke.texi: Document the options. + * flag-types.h (enum sanitize_code): Add + SANITIZE_POINTER_COMPARE and SANITIZE_POINTER_SUBTRACT. + * ipa-inline.c (sanitize_attrs_match_for_inline_p): Add handling + of SANITIZE_POINTER_COMPARE and SANITIZE_POINTER_SUBTRACT. + * opts.c: Define new sanitizer options. + * sanitizer.def (BUILT_IN_ASAN_POINTER_COMPARE): Likewise. + (BUILT_IN_ASAN_POINTER_SUBTRACT): Likewise. + 2017-12-05 Julia Koval * common/config/i386/i386-common.c (OPTION_MASK_ISA_AVX512VNNI_SET, diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 1fb0c3d0b7f..8fea42622b2 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,10 @@ +2017-12-05 Martin Liska + Jakub Jelinek + + * c-typeck.c (pointer_diff): Add new argument and instrument + pointer subtraction. + (build_binary_op): Similar for pointer comparison. + 2017-12-01 Jakub Jelinek PR c/79153 diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 922266038af..676dbbd2e2f 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -95,7 +95,7 @@ static tree lookup_field (tree, tree); static int convert_arguments (location_t, vec, tree, vec *, vec *, tree, tree); -static tree pointer_diff (location_t, tree, tree); +static tree pointer_diff (location_t, tree, tree, tree *); static tree convert_for_assignment (location_t, location_t, tree, tree, tree, enum impl_conv, bool, tree, tree, int); static tree valid_compound_expr_initializer (tree, tree); @@ -3768,10 +3768,11 @@ parser_build_binary_op (location_t location, enum tree_code code, } /* Return a tree for the difference of pointers OP0 and OP1. - The resulting tree has type ptrdiff_t. */ + The resulting tree has type ptrdiff_t. If POINTER_SUBTRACT sanitization is + enabled, assign to INSTRUMENT_EXPR call to libsanitizer. */ static tree -pointer_diff (location_t loc, tree op0, tree op1) +pointer_diff (location_t loc, tree op0, tree op1, tree *instrument_expr) { tree restype = ptrdiff_type_node; tree result, inttype; @@ -3815,6 +3816,17 @@ pointer_diff (location_t loc, tree op0, tree op1) pedwarn (loc, OPT_Wpointer_arith, "pointer to a function used in subtraction"); + if (sanitize_flags_p (SANITIZE_POINTER_SUBTRACT)) + { + gcc_assert (current_function_decl != NULL_TREE); + + op0 = save_expr (op0); + op1 = save_expr (op1); + + tree tt = builtin_decl_explicit (BUILT_IN_ASAN_POINTER_SUBTRACT); + *instrument_expr = build_call_expr_loc (loc, tt, 2, op0, op1); + } + /* First do the subtraction, then build the divide operator and only convert at the very end. Do not do default conversions in case restype is a short type. */ @@ -3825,8 +3837,8 @@ pointer_diff (location_t loc, tree op0, tree op1) space, cast the pointers to some larger integer type and do the computations in that type. */ if (TYPE_PRECISION (inttype) > TYPE_PRECISION (TREE_TYPE (op0))) - op0 = build_binary_op (loc, MINUS_EXPR, convert (inttype, op0), - convert (inttype, op1), false); + op0 = build_binary_op (loc, MINUS_EXPR, convert (inttype, op0), + convert (inttype, op1), false); else op0 = build2_loc (loc, POINTER_DIFF_EXPR, inttype, op0, op1); @@ -11113,7 +11125,7 @@ build_binary_op (location_t location, enum tree_code code, if (code0 == POINTER_TYPE && code1 == POINTER_TYPE && comp_target_types (location, type0, type1)) { - ret = pointer_diff (location, op0, op1); + ret = pointer_diff (location, op0, op1, &instrument_expr); goto return_build_binary_op; } /* Handle pointer minus int. Just like pointer plus int. */ @@ -11663,6 +11675,17 @@ build_binary_op (location_t location, enum tree_code code, result_type = type1; pedwarn (location, 0, "comparison between pointer and integer"); } + + if ((code0 == POINTER_TYPE || code1 == POINTER_TYPE) + && sanitize_flags_p (SANITIZE_POINTER_COMPARE)) + { + op0 = save_expr (op0); + op1 = save_expr (op1); + + tree tt = builtin_decl_explicit (BUILT_IN_ASAN_POINTER_COMPARE); + instrument_expr = build_call_expr_loc (location, tt, 2, op0, op1); + } + if ((TREE_CODE (TREE_TYPE (orig_op0)) == BOOLEAN_TYPE || truth_value_p (TREE_CODE (orig_op0))) ^ (TREE_CODE (TREE_TYPE (orig_op1)) == BOOLEAN_TYPE diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 5f7a574e7a5..a5ab703d1d3 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,11 @@ +2017-12-05 Martin Liska + Jakub Jelinek + + * typeck.c (pointer_diff): Add new argument and instrument + pointer subtraction. + (cp_build_binary_op): Create compound expression if doing an + instrumentation. + 2017-12-05 Jakub Jelinek * cp-gimplify.c (cp_maybe_instrument_return): Don't add diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 8454719118e..7210f99fb08 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -54,7 +54,7 @@ static tree rationalize_conditional_expr (enum tree_code, tree, static int comp_ptr_ttypes_real (tree, tree, int); static bool comp_except_types (tree, tree, bool); static bool comp_array_types (const_tree, const_tree, bool); -static tree pointer_diff (location_t, tree, tree, tree, tsubst_flags_t); +static tree pointer_diff (location_t, tree, tree, tree, tsubst_flags_t, tree *); static tree get_delta_difference (tree, tree, bool, bool, tsubst_flags_t); static void casts_away_constness_r (tree *, tree *, tsubst_flags_t); static bool casts_away_constness (tree, tree, tsubst_flags_t); @@ -4329,8 +4329,16 @@ cp_build_binary_op (location_t location, if (code0 == POINTER_TYPE && code1 == POINTER_TYPE && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (type0), TREE_TYPE (type1))) - return pointer_diff (location, op0, op1, - common_pointer_type (type0, type1), complain); + { + result = pointer_diff (location, op0, op1, + common_pointer_type (type0, type1), complain, + &instrument_expr); + if (instrument_expr != NULL) + result = build2 (COMPOUND_EXPR, TREE_TYPE (result), + instrument_expr, result); + + return result; + } /* In all other cases except pointer - int, the usual arithmetic rules apply. */ else if (!(code0 == POINTER_TYPE && code1 == INTEGER_TYPE)) @@ -5019,6 +5027,17 @@ cp_build_binary_op (location_t location, else return error_mark_node; } + + if ((code0 == POINTER_TYPE || code1 == POINTER_TYPE) + && sanitize_flags_p (SANITIZE_POINTER_COMPARE)) + { + op0 = save_expr (op0); + op1 = save_expr (op1); + + tree tt = builtin_decl_explicit (BUILT_IN_ASAN_POINTER_COMPARE); + instrument_expr = build_call_expr_loc (location, tt, 2, op0, op1); + } + break; case UNORDERED_EXPR: @@ -5374,11 +5393,12 @@ cp_pointer_int_sum (location_t loc, enum tree_code resultcode, tree ptrop, } /* Return a tree for the difference of pointers OP0 and OP1. - The resulting tree has type int. */ + The resulting tree has type int. If POINTER_SUBTRACT sanitization is + enabled, assign to INSTRUMENT_EXPR call to libsanitizer. */ static tree pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype, - tsubst_flags_t complain) + tsubst_flags_t complain, tree *instrument_expr) { tree result, inttype; tree restype = ptrdiff_type_node; @@ -5420,6 +5440,15 @@ pointer_diff (location_t loc, tree op0, tree op1, tree ptrtype, else inttype = restype; + if (sanitize_flags_p (SANITIZE_POINTER_SUBTRACT)) + { + op0 = save_expr (op0); + op1 = save_expr (op1); + + tree tt = builtin_decl_explicit (BUILT_IN_ASAN_POINTER_SUBTRACT); + *instrument_expr = build_call_expr_loc (loc, tt, 2, op0, op1); + } + /* First do the subtraction, then build the divide operator and only convert at the very end. Do not do default conversions in case restype is a short type. */ diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index b4e0231602c..b8c8083daa7 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -11034,6 +11034,28 @@ Enable AddressSanitizer for Linux kernel. See @uref{https://github.com/google/kasan/wiki} for more details. The option cannot be combined with @option{-fcheck-pointer-bounds}. +@item -fsanitize=pointer-compare +@opindex fsanitize=pointer-compare +Instrument comparison operation (<, <=, >, >=) with pointer operands. +The option must be combined with either @option{-fsanitize=kernel-address} or +@option{-fsanitize=address} +The option cannot be combined with @option{-fsanitize=thread} +and/or @option{-fcheck-pointer-bounds}. +Note: By default the check is disabled at run time. To enable it, +add @code{detect_invalid_pointer_pairs=1} to the environment variable +@env{ASAN_OPTIONS}. + +@item -fsanitize=pointer-subtract +@opindex fsanitize=pointer-subtract +Instrument subtraction with pointer operands. +The option must be combined with either @option{-fsanitize=kernel-address} or +@option{-fsanitize=address} +The option cannot be combined with @option{-fsanitize=thread} +and/or @option{-fcheck-pointer-bounds}. +Note: By default the check is disabled at run time. To enable it, +add @code{detect_invalid_pointer_pairs=1} to the environment variable +@env{ASAN_OPTIONS}. + @item -fsanitize=thread @opindex fsanitize=thread Enable ThreadSanitizer, a fast data race detector. diff --git a/gcc/flag-types.h b/gcc/flag-types.h index 591b74457cd..3073c661872 100644 --- a/gcc/flag-types.h +++ b/gcc/flag-types.h @@ -246,6 +246,8 @@ enum sanitize_code { SANITIZE_BOUNDS_STRICT = 1UL << 23, SANITIZE_POINTER_OVERFLOW = 1UL << 24, SANITIZE_BUILTIN = 1UL << 25, + SANITIZE_POINTER_COMPARE = 1UL << 26, + SANITIZE_POINTER_SUBTRACT = 1UL << 27, SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index 8f12aa16687..7846e93d119 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -260,8 +260,12 @@ sanitize_attrs_match_for_inline_p (const_tree caller, const_tree callee) if (!caller || !callee) return true; - return sanitize_flags_p (SANITIZE_ADDRESS, caller) - == sanitize_flags_p (SANITIZE_ADDRESS, callee); + return ((sanitize_flags_p (SANITIZE_ADDRESS, caller) + == sanitize_flags_p (SANITIZE_ADDRESS, callee)) + && (sanitize_flags_p (SANITIZE_POINTER_COMPARE, caller) + == sanitize_flags_p (SANITIZE_POINTER_COMPARE, callee)) + && (sanitize_flags_p (SANITIZE_POINTER_SUBTRACT, caller) + == sanitize_flags_p (SANITIZE_POINTER_SUBTRACT, 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 ab3f4aefed3..17579e72373 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -953,6 +953,19 @@ finish_options (struct gcc_options *opts, struct gcc_options *opts_set, if (opts->x_dwarf_split_debug_info) opts->x_debug_generate_pub_sections = 2; + if ((opts->x_flag_sanitize + & (SANITIZE_USER_ADDRESS | SANITIZE_KERNEL_ADDRESS)) == 0) + { + if (opts->x_flag_sanitize & SANITIZE_POINTER_COMPARE) + error_at (loc, + "%<-fsanitize=pointer-compare%> must be combined with " + "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>"); + if (opts->x_flag_sanitize & SANITIZE_POINTER_SUBTRACT) + error_at (loc, + "%<-fsanitize=pointer-subtract%> must be combined with " + "%<-fsanitize=address%> or %<-fsanitize=kernel-address%>"); + } + /* Userspace and kernel ASan conflict with each other. */ if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS) && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS)) @@ -1497,6 +1510,8 @@ const struct sanitizer_opts_s sanitizer_opts[] = SANITIZER_OPT (address, (SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS), true), SANITIZER_OPT (kernel-address, (SANITIZE_ADDRESS | SANITIZE_KERNEL_ADDRESS), true), + SANITIZER_OPT (pointer-compare, SANITIZE_POINTER_COMPARE, true), + SANITIZER_OPT (pointer-subtract, SANITIZE_POINTER_SUBTRACT, true), SANITIZER_OPT (thread, SANITIZE_THREAD, false), SANITIZER_OPT (leak, SANITIZE_LEAK, false), SANITIZER_OPT (shift, SANITIZE_SHIFT, true), diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def index 00e7ae031e6..7d224fa081e 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -175,6 +175,10 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_ALLOCA_POISON, "__asan_alloca_poison", BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST) DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_ALLOCAS_UNPOISON, "__asan_allocas_unpoison", BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_COMPARE, "__sanitizer_ptr_cmp", + BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST) +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_POINTER_SUBTRACT, "__sanitizer_ptr_sub", + BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST) /* Thread Sanitizer */ DEF_SANITIZER_BUILTIN(BUILT_IN_TSAN_INIT, "__tsan_init", diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index aeacec422cc..d040a779955 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2017-12-05 Martin Liska + Jakub Jelinek + + * c-c++-common/asan/pointer-compare-1.c: New test. + * c-c++-common/asan/pointer-compare-2.c: New test. + * c-c++-common/asan/pointer-subtract-1.c: New test. + * c-c++-common/asan/pointer-subtract-2.c: New test. + * c-c++-common/asan/pointer-subtract-3.c: New test. + * c-c++-common/asan/pointer-subtract-4.c: New test. + 2017-12-05 Jakub Jelinek * g++.dg/missing-return.C: Add -O to dg-options. diff --git a/gcc/testsuite/c-c++-common/asan/pointer-compare-1.c b/gcc/testsuite/c-c++-common/asan/pointer-compare-1.c new file mode 100644 index 00000000000..2cc7395241a --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/pointer-compare-1.c @@ -0,0 +1,95 @@ +/* { dg-do run } */ +/* { dg-set-target-env-var ASAN_OPTIONS "detect_invalid_pointer_pairs=1:halt_on_error=0" } */ +/* { dg-options "-fsanitize=address,pointer-compare" } */ + +volatile int v; + +__attribute__((noipa)) void +foo (char *p, char *q) +{ + v = p > q; +} + +char global1[100] = {}, global2[100] = {}; +char __attribute__((used)) smallest_global[5] = {}; +char small_global[7] = {}; +char __attribute__((used)) little_global[10] = {}; +char __attribute__((used)) medium_global[4000] = {}; +char large_global[5000] = {}; +char __attribute__((used)) largest_global[6000] = {}; + +int +main () +{ + /* Heap allocated memory. */ + char *heap1 = (char *)__builtin_malloc (42); + char *heap2 = (char *)__builtin_malloc (42); + + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (heap1, heap2); + __builtin_free (heap1); + __builtin_free (heap2); + + heap1 = (char *)__builtin_malloc (1024); + __asm ("" : "+g" (heap1)); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (heap1, heap1 + 1025); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (heap1 + 1024, heap1 + 1025); + __builtin_free (heap1); + + heap1 = (char *)__builtin_malloc (4096); + __asm ("" : "+g" (heap1)); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (heap1, heap1 + 4097); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (heap1, 0); + + /* Global variables. */ + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (&global1[0], &global2[10]); + + char *p = &small_global[0]; + __asm ("" : "+g" (p)); + foo (p, p); /* OK */ + foo (p, p + 7); /* OK */ + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (p, p + 8); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (p - 1, p); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (p, p - 1); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (p - 1, p + 8); + + p = &large_global[0]; + __asm ("" : "+g" (p)); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (p - 1, p); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (p, p - 1); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (p, &global1[0]); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (p, &small_global[0]); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (p, 0); + + /* Stack variables. */ + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + char stack1, stack2; + foo (&stack1, &stack2); + + /* Mixtures. */ + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (heap1, &stack1); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (heap1, &global1[0]); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (&stack1, &global1[0]); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair" } */ + foo (&stack1, 0); + __builtin_free (heap1); + + return 0; +} diff --git a/gcc/testsuite/c-c++-common/asan/pointer-compare-2.c b/gcc/testsuite/c-c++-common/asan/pointer-compare-2.c new file mode 100644 index 00000000000..5539087e856 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/pointer-compare-2.c @@ -0,0 +1,82 @@ +/* { dg-do run } */ +/* { dg-set-target-env-var ASAN_OPTIONS "detect_invalid_pointer_pairs=1 halt_on_error=1" } */ +/* { dg-options "-fsanitize=address,pointer-compare" } */ + +volatile int v; + +int +foo (char *p) +{ + char *p2 = p + 20; + v = p > p2; + return v; +} + +void +bar (char *p, char *q) +{ + v = p <= q; +} + +void +baz (char *p, char *q) +{ + v = (p != 0 && p < q); +} + +char global[8192] = {}; +char small_global[7] = {}; + +int +main () +{ + /* Heap allocated memory. */ + char *p = (char *)__builtin_malloc (42); + int r = foo (p); + __builtin_free (p); + + p = (char *)__builtin_malloc (1024); + bar (p, p + 1024); + bar (p + 1024, p + 1023); + bar (p + 1, p + 1023); + __builtin_free (p); + + p = (char *)__builtin_malloc (4096); + bar (p, p + 4096); + bar (p + 10, p + 100); + bar (p + 1024, p + 4096); + bar (p + 4095, p + 4096); + bar (p + 4095, p + 4094); + bar (p + 100, p + 4096); + bar (p + 100, p + 4094); + __builtin_free (p); + + /* Global variable. */ + bar (&global[0], &global[1]); + bar (&global[1], &global[2]); + bar (&global[2], &global[1]); + bar (&global[0], &global[100]); + bar (&global[1000], &global[7000]); + bar (&global[500], &global[10]); + p = &global[0]; + bar (p, p + 8192); + p = &global[8000]; + bar (p, p + 192); + + p = &small_global[0]; + bar (p, p + 1); + bar (p, p + 7); + bar (p + 7, p + 1); + bar (p + 6, p + 7); + bar (p + 7, p + 7); + + /* Stack variable. */ + char stack[10000]; + bar (&stack[0], &stack[100]); + bar (&stack[1000], &stack[9000]); + bar (&stack[500], &stack[10]); + + baz (0, &stack[10]); + + return 0; +} diff --git a/gcc/testsuite/c-c++-common/asan/pointer-subtract-1.c b/gcc/testsuite/c-c++-common/asan/pointer-subtract-1.c new file mode 100644 index 00000000000..7cbef811d7b --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/pointer-subtract-1.c @@ -0,0 +1,45 @@ +/* { dg-do run } */ +/* { dg-set-target-env-var ASAN_OPTIONS "detect_invalid_pointer_pairs=1 halt_on_error=0" } */ +/* { dg-options "-fsanitize=address,pointer-subtract" } */ + +volatile __PTRDIFF_TYPE__ v; + +__attribute__((noipa)) void +foo (char *p, char *q) +{ + v = p - q; +} + +char global1[100] = {}, global2[100] = {}; + +int +main () +{ + /* Heap allocated memory. */ + char *heap1 = (char *)__builtin_malloc (42); + char *heap2 = (char *)__builtin_malloc (42); + + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (heap1, heap2); + + /* Global variables. */ + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (&global1[0], &global2[10]); + + /* Stack variables. */ + char stack1, stack2; + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (&stack1, &stack2); + + /* Mixtures. */ + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (heap1, &stack1); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair.*" } */ + foo (heap1, &global1[0]); + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair" } */ + foo (&stack1, &global1[0]); + + __builtin_free (heap1); + __builtin_free (heap2); + return 0; +} diff --git a/gcc/testsuite/c-c++-common/asan/pointer-subtract-2.c b/gcc/testsuite/c-c++-common/asan/pointer-subtract-2.c new file mode 100644 index 00000000000..6b65a16b559 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/pointer-subtract-2.c @@ -0,0 +1,37 @@ +/* { dg-do run } */ +/* { dg-set-target-env-var ASAN_OPTIONS "detect_invalid_pointer_pairs=1 halt_on_error=1" } */ +/* { dg-options "-fsanitize=address,pointer-subtract" } */ + +volatile __PTRDIFF_TYPE__ v; + +void +bar (char *p, char *q) +{ + v = q - p; + v = p - q; +} + +char global[10000] = {}; + +int +main () +{ + /* Heap allocated memory. */ + char *p = (char *)__builtin_malloc (42); + bar (p, p + 20); + __builtin_free (p); + + /* Global variable. */ + bar (&global[0], &global[100]); + bar (&global[1000], &global[9000]); + bar (&global[500], &global[10]); + bar (&global[0], &global[10000]); + + /* Stack variable. */ + char stack[10000]; + bar (&stack[0], &stack[100]); + bar (&stack[1000], &stack[9000]); + bar (&stack[500], &stack[10]); + + return 0; +} diff --git a/gcc/testsuite/c-c++-common/asan/pointer-subtract-3.c b/gcc/testsuite/c-c++-common/asan/pointer-subtract-3.c new file mode 100644 index 00000000000..5cbcda92503 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/pointer-subtract-3.c @@ -0,0 +1,43 @@ +/* { dg-do run { target pthread_h } } */ +/* { dg-set-target-env-var ASAN_OPTIONS "detect_invalid_pointer_pairs=1:halt_on_error=1" } */ +/* { dg-options "-fsanitize=address,pointer-subtract" } */ +/* { dg-additional-options "-pthread" { target pthread } } */ + +#include +#include + +char *pointers[2]; +pthread_barrier_t bar; + +void * +thread_main (void *n) +{ + char local; + + __UINTPTR_TYPE__ id = (__UINTPTR_TYPE__) n; + pointers[id] = &local; + pthread_barrier_wait (&bar); + pthread_barrier_wait (&bar); + + return 0; +} + +int +main () +{ + pthread_t threads[2]; + pthread_barrier_init (&bar, NULL, 3); + pthread_create (&threads[0], NULL, thread_main, (void *) 0); + pthread_create (&threads[1], NULL, thread_main, (void *) 1); + pthread_barrier_wait (&bar); + + /* This case is not handled yet. */ + volatile __PTRDIFF_TYPE__ r = pointers[0] - pointers[1]; + + pthread_barrier_wait (&bar); + pthread_join (threads[0], NULL); + pthread_join (threads[1], NULL); + pthread_barrier_destroy (&bar); + + return 0; +} diff --git a/gcc/testsuite/c-c++-common/asan/pointer-subtract-4.c b/gcc/testsuite/c-c++-common/asan/pointer-subtract-4.c new file mode 100644 index 00000000000..820f0aa64b0 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/pointer-subtract-4.c @@ -0,0 +1,43 @@ +/* { dg-do run { target pthread_h } } */ +/* { dg-shouldfail "asan" } */ +/* { dg-set-target-env-var ASAN_OPTIONS "detect_invalid_pointer_pairs=1:halt_on_error=1" } */ +/* { dg-options "-fsanitize=address,pointer-subtract" } */ +/* { dg-additional-options "-pthread" { target pthread } } */ + +#include +#include + +char *pointer; +pthread_barrier_t bar; + +void * +thread_main (void *n) +{ + char local; + (void) n; + pointer = &local; + pthread_barrier_wait (&bar); + pthread_barrier_wait (&bar); + + return 0; +} + +int +main () +{ + pthread_t thread; + pthread_barrier_init (&bar, NULL, 2); + pthread_create (&thread, NULL, thread_main, NULL); + pthread_barrier_wait (&bar); + + char local; + char *parent_pointer = &local; + + /* { dg-output "ERROR: AddressSanitizer: invalid-pointer-pair" } */ + volatile __PTRDIFF_TYPE__ r = parent_pointer - pointer; + pthread_barrier_wait (&bar); + pthread_join (thread, NULL); + pthread_barrier_destroy (&bar); + + return 0; +} diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog index 497c4eaed5c..e5cad19bb5e 100644 --- a/libsanitizer/ChangeLog +++ b/libsanitizer/ChangeLog @@ -1,3 +1,12 @@ +2017-12-05 Martin Liska + Jakub Jelinek + + * asan/asan_descriptions.cc: Cherry-pick upstream r319668. + * asan/asan_descriptions.h: Likewise. + * asan/asan_report.cc: Likewise. + * asan/asan_thread.cc: Likewise. + * asan/asan_thread.h: Likewise. + 2017-11-17 Igor Tsimbalist * acinclude.m4: Add enable.m4 and cet.m4. diff --git a/libsanitizer/asan/asan_descriptions.cc b/libsanitizer/asan/asan_descriptions.cc index d46962adf27..c856a653742 100644 --- a/libsanitizer/asan/asan_descriptions.cc +++ b/libsanitizer/asan/asan_descriptions.cc @@ -333,6 +333,26 @@ void GlobalAddressDescription::Print(const char *bug_type) const { } } +bool GlobalAddressDescription::PointsInsideTheSameVariable( + const GlobalAddressDescription &other) const { + if (size == 0 || other.size == 0) return false; + + for (uptr i = 0; i < size; i++) { + const __asan_global &a = globals[i]; + for (uptr j = 0; j < other.size; j++) { + const __asan_global &b = other.globals[j]; + if (a.beg == b.beg && + a.beg <= addr && + b.beg <= other.addr && + (addr + access_size) < (a.beg + a.size) && + (other.addr + other.access_size) < (b.beg + b.size)) + return true; + } + } + + return false; +} + void StackAddressDescription::Print() const { Decorator d; char tname[128]; diff --git a/libsanitizer/asan/asan_descriptions.h b/libsanitizer/asan/asan_descriptions.h index 0fbb531492a..12d9ac5f70b 100644 --- a/libsanitizer/asan/asan_descriptions.h +++ b/libsanitizer/asan/asan_descriptions.h @@ -143,6 +143,10 @@ struct GlobalAddressDescription { u8 size; void Print(const char *bug_type = "") const; + + // Returns true when this descriptions points inside the same global variable + // as other. Descriptions can have different address within the variable + bool PointsInsideTheSameVariable(const GlobalAddressDescription &other) const; }; bool GetGlobalAddressInformation(uptr addr, uptr access_size, diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc index 51bad6e45e3..261ec1aece5 100644 --- a/libsanitizer/asan/asan_report.cc +++ b/libsanitizer/asan/asan_report.cc @@ -295,17 +295,58 @@ static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, in_report.ReportError(error); } +static bool IsInvalidPointerPair(uptr a1, uptr a2) { + if (a1 == a2) + return false; + + // 256B in shadow memory can be iterated quite fast + static const uptr kMaxOffset = 2048; + + uptr left = a1 < a2 ? a1 : a2; + uptr right = a1 < a2 ? a2 : a1; + uptr offset = right - left; + if (offset <= kMaxOffset) + return __asan_region_is_poisoned(left, offset); + + AsanThread *t = GetCurrentThread(); + + // check whether left is a stack memory pointer + if (uptr shadow_offset1 = t->GetStackVariableShadowStart(left)) { + uptr shadow_offset2 = t->GetStackVariableShadowStart(right); + return shadow_offset2 == 0 || shadow_offset1 != shadow_offset2; + } + + // check whether left is a heap memory address + HeapAddressDescription hdesc1, hdesc2; + if (GetHeapAddressInformation(left, 0, &hdesc1) && + hdesc1.chunk_access.access_type == kAccessTypeInside) + return !GetHeapAddressInformation(right, 0, &hdesc2) || + hdesc2.chunk_access.access_type != kAccessTypeInside || + hdesc1.chunk_access.chunk_begin != hdesc2.chunk_access.chunk_begin; + + // check whether left is an address of a global variable + GlobalAddressDescription gdesc1, gdesc2; + if (GetGlobalAddressInformation(left, 0, &gdesc1)) + return !GetGlobalAddressInformation(right - 1, 0, &gdesc2) || + !gdesc1.PointsInsideTheSameVariable(gdesc2); + + if (t->GetStackVariableShadowStart(right) || + GetHeapAddressInformation(right, 0, &hdesc2) || + GetGlobalAddressInformation(right - 1, 0, &gdesc2)) + return true; + + // At this point we know nothing about both a1 and a2 addresses. + return false; +} + static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { if (!flags()->detect_invalid_pointer_pairs) return; uptr a1 = reinterpret_cast(p1); uptr a2 = reinterpret_cast(p2); - AsanChunkView chunk1 = FindHeapChunkByAddress(a1); - AsanChunkView chunk2 = FindHeapChunkByAddress(a2); - bool valid1 = chunk1.IsAllocated(); - bool valid2 = chunk2.IsAllocated(); - if (!valid1 || !valid2 || !chunk1.Eq(chunk2)) { + + if (IsInvalidPointerPair(a1, a2)) { GET_CALLER_PC_BP_SP; - return ReportInvalidPointerPair(pc, bp, sp, a1, a2); + ReportInvalidPointerPair(pc, bp, sp, a1, a2); } } // ----------------------- Mac-specific reports ----------------- {{{1 diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc index d0fdf6e9847..f817a10db27 100644 --- a/libsanitizer/asan/asan_thread.cc +++ b/libsanitizer/asan/asan_thread.cc @@ -315,7 +315,7 @@ bool AsanThread::GetStackFrameAccessByAddr(uptr addr, access->frame_descr = (const char *)((uptr*)bottom)[1]; return true; } - uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr. + uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr. uptr mem_ptr = RoundDownTo(aligned_addr, SHADOW_GRANULARITY); u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); u8 *shadow_bottom = (u8*)MemToShadow(bottom); @@ -344,6 +344,29 @@ bool AsanThread::GetStackFrameAccessByAddr(uptr addr, return true; } +uptr AsanThread::GetStackVariableShadowStart(uptr addr) { + uptr bottom = 0; + if (AddrIsInStack(addr)) { + bottom = stack_bottom(); + } else if (has_fake_stack()) { + bottom = fake_stack()->AddrIsInFakeStack(addr); + CHECK(bottom); + } else + return 0; + + uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr. + u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); + u8 *shadow_bottom = (u8*)MemToShadow(bottom); + + while (shadow_ptr >= shadow_bottom && + (*shadow_ptr != kAsanStackLeftRedzoneMagic && + *shadow_ptr != kAsanStackMidRedzoneMagic && + *shadow_ptr != kAsanStackRightRedzoneMagic)) + shadow_ptr--; + + return (uptr)shadow_ptr + 1; +} + bool AsanThread::AddrIsInStack(uptr addr) { const auto bounds = GetStackBounds(); return addr >= bounds.bottom && addr < bounds.top; diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h index f7a91f3e73b..187cb13e85b 100644 --- a/libsanitizer/asan/asan_thread.h +++ b/libsanitizer/asan/asan_thread.h @@ -88,6 +88,9 @@ class AsanThread { }; bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access); + // Returns a pointer to the start of the stack variable's shadow memory. + uptr GetStackVariableShadowStart(uptr addr); + bool AddrIsInStack(uptr addr); void DeleteFakeStack(int tid) {