+2015-10-21 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * bootstrap-asan.mk: Replace ASAN_OPTIONS=detect_leaks with
+ LSAN_OPTIONS=detect_leaks.
+
+2014-11-17 H.J. Lu <hongjiu.lu@intel.com>
+
+ PR bootstrap/63888
+ * bootstrap-asan.mk (ASAN_OPTIONS): Export "detect_leaks=0".
+
2015-06-26 Release Manager
* GCC 4.9.3 released.
# This option enables -fsanitize=address for stage2 and stage3.
+# Suppress LeakSanitizer in bootstrap.
+export LSAN_OPTIONS="detect_leaks=0"
+
STAGE2_CFLAGS += -fsanitize=address
STAGE3_CFLAGS += -fsanitize=address
POSTSTAGE1_LDFLAGS += -fsanitize=address -static-libasan \
+2015-02-09 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64981
+ * builtins.c (expand_builtin): Call targetm.expand_builtin
+ for BUILT_IN_MD builtins regardless of asan_intercepted_p.
+
+2016-04-23 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/70712
+ * cfgexpand.c (expand_stack_vars): Fix typo.
+
+2016-04-08 Maxim Ostapenko <m.ostapenko@samsung.com>
+
+ PR sanitizer/70541
+ * asan.c (instrument_derefs): If we get unknown location, extract it
+ with EXPR_LOCATION.
+ (maybe_instrument_call): Instrument gimple_call's arguments if needed.
+
+2016-02-18 Jakub Jelinek <jakub@redhat.com>
+ Martin Liska <mliska@suse.cz>
+
+ PR sanitizer/69863
+ * cfgexpand.c (asan_sanitize_stack_p): New function.
+ (partition_stack_vars): Use the function.
+ (expand_stack_vars): Likewise.
+ (defer_stack_allocation): Likewise.
+ (expand_used_vars): Likewise.
+
+2016-02-04 Martin Liska <mliska@suse.cz>
+
+ PR sanitizer/69276
+ * asan.c (has_stmt_been_instrumented_p): Instrument gimple calls
+ that are gimple_store_p.
+ (maybe_instrument_call): Likewise.
+
+2016-01-06 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/69099
+ * convert.c (convert_to_integer_1): Adjust call to
+ ubsan_instrument_float_cast. Use NULL_TREE instead of NULL.
+ * ubsan.c (ubsan_instrument_float_cast): Drop the ARG parameter. Use
+ EXPR instead of ARG.
+ * ubsan.h (ubsan_instrument_float_cast): Adjust declaration.
+
+2016-01-01 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/69055
+ * ubsan.c (ubsan_instrument_float_cast): Call
+ initialize_sanitizer_builtins.
+
+2015-12-12 Paolo Bonzini <bonzini@gnu.org>
+
+ PR sanitizer/68418
+ * c-family/c-ubsan.c (ubsan_instrument_shift): Disable
+ sanitization of left shifts for wrapping signed types as well.
+
+2015-10-29 Richard Biener <rguenther@suse.de>
+
+ PR middle-end/56956
+ * fold-const.c (fold_cond_expr_with_comparison): Do not fold
+ unsigned conditonal negation to ABS_EXPR.
+
+2015-08-21 Yury Gribov <y.gribov@samsung.com>
+
+ * sanitizer.def (BUILT_IN_UBSAN_HANDLE_NONNULL_ARG,
+ BUILT_IN_UBSAN_HANDLE_NONNULL_ARG): Fix builtin types.
+
+2013-08-07 Ilya Enkovich <ilya.enkovich@intel.com>
+
+ * config/elfos.h (ASM_DECLARE_OBJECT_NAME): Use decl size
+ instead of type size.
+ (ASM_FINISH_DECLARE_OBJECT): Likewise.
+
+2015-11-23 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * opts.c (finish_options): Allow -fsanitize-recover=address for
+ userspace sanitization.
+ * asan.c (asan_expand_check_ifn): Redefine recover_p.
+ * doc/invoke.texi (fsanitize-recover): Update documentation.
+
+2015-10-21 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * asan.c (asan_emit_stack_protection): Don't pass local stack to
+ asan_stack_malloc_[n] anymore. Check if asan_stack_malloc_[n] returned
+ NULL and use local stack than.
+ (asan_finish_file): Insert __asan_version_mismatch_check_v[n] call
+ in addition to __asan_init.
+ * sanitizer.def (BUILT_IN_ASAN_INIT): Rename to __asan_init.
+ (BUILT_IN_ASAN_VERSION_MISMATCH_CHECK): Add new builtin call.
+ * asan.h (asan_intercepted_p): Handle new string builtins.
+ * ubsan.c (ubsan_use_new_style_p): New function.
+ (ubsan_instrument_float_cast): If location is unknown, assign
+ input_location to loc. Propagate loc to ubsan_create_data if
+ ubsan_use_new_style_p returned true.
+
+2015-06-20 Marek Polacek <polacek@redhat.com>
+
+ * common.opt (fsanitize-undefined-trap-on-error): Add Driver.
+
+2015-06-08 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/66452
+ * toplev.c (check_global_declaration): Don't warn about artificial
+ decls.
+
+2015-05-07 Marek Polacek <polacek@redhat.com>
+ Martin Uecker <uecker@eecs.berkeley.edu>
+
+ * doc/invoke.texi: Document -fsanitize=bounds-strict.
+ * flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS_STRICT, or it
+ into SANITIZE_NONDEFAULT.
+ * opts.c (common_handle_option): Handle -fsanitize=bounds-strict.
+
+2015-04-09 Jakub Jelinek <jakub@redhat.com>
+
+ PR tree-optimization/65709
+ * ubsan.c (instrument_mem_ref): Use TREE_TYPE (base) instead of
+ TREE_TYPE (TREE_TYPE (t)).
+
+2015-03-27 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/65583
+ * ubsan.c (ubsan_create_edge): New function.
+ (instrument_bool_enum_load): Call it.
+ (instrument_nonnull_arg): Likewise.
+ (instrument_nonnull_return): Likewise.
+ (instrument_object_size): Likewise.
+
+2015-03-10 Marek Polacek <polacek@redhat.com>
+ Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/65367
+ * ubsan.c (ubsan_expand_objsize_ifn): Update GSI instead of GSI_ORIG
+ when only removing the statement. Handle expanding UBSAN_OBJECT_SIZE
+ separately.
+
+2015-03-07 Marek Polacek <polacek@redhat.com>
+ Martin Uecker <uecker@eecs.berkeley.edu>
+
+ PR sanitizer/65280
+ * doc/invoke.texi: Update description of -fsanitize=bounds.
+
+2015-02-12 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/65019
+ * ubsan.c (ubsan_expand_objsize_ifn): Always return true.
+
+2015-01-27 Jakub Jelinek <jakub@redhat.com>
+ Yury Gribov <y.gribov@samsung.com>
+
+ PR ubsan/64741
+ * ubsan.c (ubsan_source_location): Refactor code.
+ (ubsan_type_descriptor): Update type size. Refactor code.
+
+2015-01-06 Jakub Jelinek <jakub@redhat.com>
+
+ * opts.c (common_handle_option): Add support for
+ -fno-sanitize=all and -f{,no-}sanitize-recover=all.
+ * doc/invoke.texi: Document -fno-sanitize=all,
+ -f{,no-}sanitize-recover=all. Document that
+ -fsanitize=float-cast-overflow is not enabled
+ by -fsanitize=undefined. Fix up documentation
+ of -f{,no-}sanitize-recover.
+
+2015-01-05 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64344
+ * ubsan.h (ubsan_instrument_float_cast): Add ARG argument.
+ * ubsan.c (ubsan_instrument_float_cast): Add ARG argument, pass
+ it to libubsan handler instead of EXPR. Fold comparisons earlier,
+ if the result is integer_zerop, return NULL_TREE.
+ * convert.c (convert_to_integer): Pass expr as ARG.
+
+2014-12-04 Marek Polacek <polacek@redhat.com>
+
+ PR middle-end/56917
+ * fold-const.c (fold_unary_loc): Perform the negation in A's type
+ when transforming ~ (A - 1) or ~ (A + -1) to -A.
+
+2014-12-01 Marek Polacek <polacek@redhat.com>
+ Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64121
+ * ubsan.c (instrument_object_size): Stop searching if the base
+ occurs in abnormal phi.
+
+2014-12-01 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63956
+ * ubsan.c (is_ubsan_builtin_p): Check also built-in class.
+
+2015-04-22 Yury Gribov <y.gribov@samsung.com>
+
+ * doc/invoke.texi (-fsanitize-sections): Update description.
+ * asan.c (set_sanitized_sections): Parse incoming arg.
+ (section_sanitized_p): Support wildcards.
+
+2015-04-17 Yury Gribov <y.gribov@samsung.com>
+
+ * asan.c (set_sanitized_sections): New function.
+ (section_sanitized_p): Ditto.
+ (asan_protect_global): Optionally sanitize user-defined
+ sections.
+ * asan.h (set_sanitized_sections): Declare new function.
+ * common.opt (fsanitize-sections): New option.
+ * doc/invoke.texi (-fsanitize-sections): Document new option.
+ * opts-global.c (handle_common_deferred_options): Handle new
+ option.
+
+2015-04-01 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ PR target/65624
+ * config/aarch64/aarch64-builtins.c (aarch64_simd_expand_builtin): Increase
+ args array size by one to avoid buffer overflow.
+
+2015-03-16 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ PR sanitizer/64820
+ * cfgexpand.c (align_base): New function.
+ (alloc_stack_frame_space): Call it.
+ (expand_stack_vars): Align prev_frame to be sure
+ data->asan_vec elements aligned properly.
+
+2014-11-26 Jakub Jelinek <jakub@redhat.com>
+
+ * gcc.c (SANITIZER_SPEC): Don't error on -fsanitize=thread
+ without -pie or -shared, error on -fsanitize=thread -static instead.
+
+2014-11-26 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63788
+ * asan.c (initialize_sanitizer_builtins): Add BT_FN_SIZE_CONST_PTR_INT
+ var. Conditionally build BUILT_IN_OBJECT_SIZE decl.
+ (ATTR_PURE_NOTHROW_LEAF_LIST): Define.
+
+2014-11-19 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63690
+ * ubsan.c (instrument_object_size): Check for MEM_REF.
+
+2014-11-19 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63879
+ * fold-const.c (negate_expr_p) <case NEGATE_EXPR>: Return
+ !TYPE_OVERFLOW_SANITIZED.
+ (fold_negate_expr) <case INTEGER_CST>: Fold when overflow
+ does not trap and when overflow wraps, or when SANITIZE_SI_OVERFLOW
+ is 0.
+
+2014-11-19 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/63520
+ * internal-fn.c (expand_ubsan_result_store): New function.
+ (expand_addsub_overflow, expand_neg_overflow, expand_mul_overflow):
+ Use it instead of just emit_move_insn.
+
+2014-11-19 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/63913
+ * ubsan.c: Include tree-eh.h.
+ (instrument_bool_enum_load): Handle loads that can throw.
+
+2014-11-18 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63866
+ * asan.c (asan_global_struct): Create a TYPE_DECL for "__asan_global",
+ put it into TYPE_NAME and TYPE_STUB_DECL.
+ * ubsan.c (ubsan_type_descriptor_type): New variable.
+ Function renamed to ...
+ (ubsan_get_type_descriptor_type): ... this. Cache
+ return value in ubsan_type_descriptor_type variable.
+ Create a TYPE_DECL for "__ubsan_type_descriptor", put it into
+ TYPE_NAME and TYPE_STUB_DECL.
+ (ubsan_get_source_location_type): Create a TYPE_DECL for
+ "__ubsan_source_location", put it into TYPE_NAME and TYPE_STUB_DECL.
+ (ubsan_type_descriptor, ubsan_create_data): Call
+ ubsan_get_type_descriptor_type instead of ubsan_type_descriptor_type.
+ Create a TYPE_DECL for name, put it into TYPE_NAME and TYPE_STUB_DECL.
+
+2014-11-18 Marat Zakirov <m.zakirov@samsung.com>
+
+ * opts.c (finish_options): Disable aggressive opts for sanitizer.
+ (common_handle_option): Move code to finish_options.
+
+2014-11-18 Yury Gribov <y.gribov@samsung.com>
+
+ PR sanitizer/63802
+ * stor-layout.c (min_align_of_type): Respect user alignment
+ more.
+
+2014-11-14 Jakub Jelinek <jakub@redhat.com>
+ Marek Polacek <polacek@redhat.com>
+
+ * sanopt.c: Include tree-ssa-operands.h.
+ (struct sanopt_info): Add has_freeing_call_p,
+ has_freeing_call_computed_p, imm_dom_path_with_freeing_call_p,
+ imm_dom_path_with_freeing_call_computed_p, freeing_call_events,
+ being_visited_p fields.
+ (struct sanopt_ctx): Add asan_check_map field.
+ (imm_dom_path_with_freeing_call, maybe_optimize_ubsan_null_ifn,
+ maybe_optimize_asan_check_ifn): New functions.
+ (sanopt_optimize_walker): Use them, optimize even ASAN_CHECK
+ internal calls.
+ (pass_sanopt::execute): Call sanopt_optimize even for
+ -fsanitize=address.
+ * gimple.c (nonfreeing_call_p): Return true for non-ECF_LEAF
+ internal calls.
+
+2014-10-15 Jakub Jelinek <jakub@redhat.com>
+
+ * tree-ssa-reassoc.c (optimize_range_tests_diff): Perform
+ MINUS_EXPR in unsigned type to avoid undefined behavior.
+
+2014-08-12 Yury Gribov <y.gribov@samsung.com>
+
+ * internal-fn.c (init_internal_fns): Fix off-by-one.
+
+2014-11-14 Marek Polacek <polacek@redhat.com>
+
+ * fold-const.c (fold_negate_expr): Don't fold INTEGER_CST if
+ that overflows when SANITIZE_SI_OVERFLOW is on. Guard -(-A)
+ folding with TYPE_OVERFLOW_SANITIZED.
+
+2014-11-14 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63839
+ * asan.c (ATTR_CONST_NORETURN_NOTHROW_LEAF_LIST,
+ ATTR_COLD_CONST_NORETURN_NOTHROW_LEAF_LIST): Define.
+ * builtin-attrs.def (ATTR_COLD_CONST_NORETURN_NOTHROW_LEAF_LIST):
+ Define.
+ * builtins.c (fold_builtin_0): Don't include ubsan.h. Don't
+ instrument BUILT_IN_UNREACHABLE here.
+ * sanitizer.def (BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE): Make
+ const.
+ * sanopt.c (pass_sanopt::execute): Instrument BUILT_IN_UNREACHABLE.
+ * tree-ssa-ccp.c (optimize_unreachable): Bail out if
+ SANITIZE_UNREACHABLE.
+ * ubsan.c (ubsan_instrument_unreachable): Rewrite for GIMPLE.
+ * ubsan.h (ubsan_instrument_unreachable): Adjust declaration.
+
+2014-11-12 Marek Polacek <polacek@redhat.com>
+
+ * fold-const.c (fold_binary_loc): Don't fold if the result
+ is undefined.
+ * match.pd (A + (-B) -> A - B, A - (-B) -> A + B,
+ -(-A) -> A): Likewise.
+
+2014-10-09 Marek Polacek <polacek@redhat.com>
+
+ * doc/invoke.texi: Document -fsanitize=bool and -fsanitize=enum.
+
+2014-11-06 Marek Polacek <polacek@redhat.com>
+
+ * sanopt.c (sanopt_optimize_walker): Limit removal of the checks.
+ Remove vector limit.
+
+2014-11-04 Marek Polacek <polacek@redhat.com>
+
+ * Makefile.in (OBJS): Add sanopt.o.
+ (GTFILES): Add sanopt.c.
+ * asan.h (asan_expand_check_ifn): Declare.
+ * asan.c (asan_expand_check_ifn): No longer static.
+ (class pass_sanopt, pass_sanopt::execute, make_pass_sanopt): Move...
+ * sanopt.c: ...here. New file.
+
+2014-11-04 Marek Polacek <polacek@redhat.com>
+
+ * ubsan.c (instrument_object_size): Optimize [x & CST] array accesses.
+
+2014-10-31 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/63697
+ * tree-vrp.c (simplify_internal_call_using_ranges): For subcode ==
+ MINUS_EXPR, check overflow on vr0.min - vr1.max and vr0.max - vr1.min
+ instead of vr0.min - vr1.min and vr0.max - vr1.max.
+
+2014-10-28 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * asan.h (asan_intercepted_p): New function.
+ * asan.c (asan_mem_ref_hasher::hash): Remove MEM_REF access size from
+ hash value construction. Call iterative_hash_expr instead of explicit
+ hash building.
+ (asan_mem_ref_hasher::equal): Change condition.
+ (has_mem_ref_been_instrumented): Likewise.
+ (update_mem_ref_hash_table): Likewise.
+ (maybe_update_mem_ref_hash_table): New function.
+ (instrument_strlen_call): Removed.
+ (get_mem_refs_of_builtin_call): Handle new parameter.
+ (instrument_builtin_call): Call maybe_update_mem_ref_hash_table instead
+ of instrument_mem_region_access if intercepted_p is true.
+ (instrument_mem_region_access): Instrument only base with len instead of
+ base and end with 1.
+ (build_check_stmt): Remove start_instrumented and end_instrumented
+ parameters.
+ (enum asan_check_flags): Remove ASAN_CHECK_START_INSTRUMENTED and
+ ASAN_CHECK_END_INSTRUMENTED. Change ASAN_CHECK_LAST.
+ (asan_expand_check_ifn): Remove start_instrumented and end_instrumented.
+ * builtins.c (expand_builtin): Include asan.h. Don't expand string/memory
+ builtin functions that have interceptors if ASan is enabled.
+
+2014-10-28 Yury Gribov <y.gribov@samsung.com>
+
+ * asan.c (report_error_func): Add noabort path.
+ (check_func): Ditto. Formatting.
+ (asan_expand_check_ifn): Handle noabort path.
+ * common.opt (flag_sanitize_recover): Add SANITIZE_KERNEL_ADDRESS
+ to default value.
+ * doc/invoke.texi (-fsanitize-recover=): Mention KASan.
+ * opts.c (finish_options): Reword comment.
+ * sanitizer.def: Add noabort ASan builtins.
+
+2014-10-28 Yury Gribov <y.gribov@samsung.com>
+
+ * asan.c (set_asan_shadow_offset): New function.
+ (asan_shadow_offset): Likewise.
+ (asan_emit_stack_protection): Call asan_shadow_offset.
+ (build_shadow_mem_access): Likewise.
+ * asan.h (set_asan_shadow_offset): Declare.
+ * common.opt (fasan-shadow-offset): New option.
+ (frandom-seed): Fixed parameter name.
+ * doc/invoke.texi (fasan-shadow-offset): Describe new option.
+ (frandom-seed): Fixed parameter name.
+ * opts-global.c (handle_common_deferred_options): Handle
+ -fasan-shadow-offset.
+ * opts.c (common_handle_option): Likewise.
+
+2014-10-22 Jakub Jelinek <jakub@redhat.com>
+ Yury Gribov <y.gribov@samsung.com>
+
+ * common.opt (flag_sanitize_recover): New variable.
+ (fsanitize-recover): Remove Var/Init, deprecate.
+ (fsanitize-recover=): New option.
+ * doc/invoke.texi (fsanitize-recover): Update docs.
+ * opts.c (finish_options): Use opts->x_flag_sanitize
+ instead of flag_sanitize. Prohibit -fsanitize-recover
+ for anything besides UBSan. Formatting.
+ (common_handle_option): Handle OPT_fsanitize_recover_
+ and OPT_fsanitize_recover. Use opts->x_flag_sanitize
+ instead of flag_sanitize.
+ * asan.c (pass_sanopt::execute): Fix up formatting.
+ * ubsan.c (ubsan_expand_bounds_ifn, ubsan_expand_null_ifn,
+ ubsan_expand_objsize_ifn, ubsan_build_overflow_builtin,
+ instrument_bool_enum_load, ubsan_instrument_float_cast,
+ instrument_nonnull_arg, instrument_nonnull_return): Check
+ bits in flag_sanitize_recover bitmask instead of
+ flag_sanitize_recover as bool flag.
+
+2014-10-16 DJ Delorie <dj@redhat.com>
+
+ * flag-types.h (sanitize_code): Don't assume targets have 32-bit
+ integers.
+
+2014-10-13 Marat Zakirov <m.zakirov@samsung.com>
+
+ * asan.c (instrument_derefs): BIT_FIELD_REF added.
+
+2014-10-10 Marek Polacek <polacek@redhat.com>
+
+ * asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE.
+ * doc/invoke.texi: Document -fsanitize=object-size.
+ * flag-types.h (enum sanitize_code): Add SANITIZE_OBJECT_SIZE and
+ or it into SANITIZE_UNDEFINED.
+ * gimple-fold.c (gimple_fold_call): Optimize IFN_UBSAN_OBJECT_SIZE.
+ * internal-fn.c (expand_UBSAN_OBJECT_SIZE): New function.
+ * internal-fn.def (UBSAN_OBJECT_SIZE): Define.
+ * opts.c (common_handle_option): Handle -fsanitize=object-size.
+ * ubsan.c: Include tree-object-size.h.
+ (ubsan_type_descriptor): Call tree_to_uhwi instead of tree_to_shwi.
+ (ubsan_expand_bounds_ifn): Use false instead of 0.
+ (ubsan_expand_objsize_ifn): New function.
+ (instrument_object_size): New function.
+ (pass_ubsan::execute): Add object size instrumentation.
+ * ubsan.h (ubsan_expand_objsize_ifn): Declare.
+
+2014-10-09 Marek Polacek <polacek@redhat.com>
+
+ * doc/invoke.texi: Document -fsanitize=bool and -fsanitize=enum.
+
+2014-10-03 Yury Gribov <y.gribov@samsung.com>
+
+ * asan.c (asan_finish_file): Disable __asan_init calls for KASan;
+ don't emit empty ctors.
+
+2014-09-24 Marek Polacek <polacek@redhat.com>
+
+ PR c/61405
+ PR c/53874
+ * asan.c (maybe_instrument_call): Add default case.
+ * ipa-pure-const.c (special_builtin_state): Likewise.
+ * predict.c (expr_expected_value_1): Likewise.
+ * lto-streamer-out.c (write_symbol): Initialize variable.
+
+2014-09-24 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/63316
+ * asan.c (asan_expand_check_ifn): Fix up align >= 8 optimization.
+
+2015-01-23 Jakub Jelinek <jakub@redhat.com>
+
+ PR driver/64737
+ * gcc.c (print_configuration): Don't print a blank line at the end
+ here...
+ (run_attempt): ... but here unstead.
+
+2015-01-22 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ PR driver/64690
+ * gcc.c (insert_comments): New function.
+ (try_generate_repro): Call it.
+ (append_text): Removed.
+
+2014-09-26 Thomas Schwinge <thomas@codesourcery.com>
+
+ * gcc.c (try_generate_repro): Remove argument "prog". Change all
+ users.
+ (run_attempt): Handle errors of "pex_run" invocation.
+
+2014-09-26 Jakub Jelinek <jakub@redhat.com>
+ Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * common.opt: New option.
+ * doc/invoke.texi: Describe new option.
+ * gcc.c (execute): Don't free first string early, but at the end
+ of the function. Call retry_ice if compiler exited with
+ ICE_EXIT_CODE.
+ (main): Factor out common code.
+ (print_configuration): New function.
+ (files_equal_p): Likewise.
+ (check_repro): Likewise.
+ (run_attempt): Likewise.
+ (do_report_bug): Likewise.
+ (append_text): Likewise.
+ (try_generate_repro): Likewise
+
+2014-08-11 Yury Gribov <y.gribov@samsung.com>
+
+ * gimple.c (gimple_call_fnspec): Support internal functions.
+ (gimple_call_return_flags): Use const.
+ * Makefile.in (GTFILES): Add internal-fn.h to list of GC files.
+ * internal-fn.def: Add fnspec information.
+ * internal-fn.h (internal_fn_fnspec): New function.
+ (init_internal_fns): Declare new function.
+ * internal-fn.c (internal_fn_fnspec_array): New global variable.
+ (init_internal_fns): New function.
+ * tree-core.h: Update macro call.
+ * tree.c (build_common_builtin_nodes): Initialize internal fns.
+
+2014-09-23 Kostya Serebryany <kcc@google.com>
+
+ Update to match the changed asan API.
+ * asan.c (asan_global_struct): Update the __asan_global definition
+ to match the new API.
+ (asan_add_global): Ditto.
+ * sanitizer.def (BUILT_IN_ASAN_INIT): Rename __asan_init_v3
+ to __asan_init_v4.
+
+2014-09-19 Marat Zakirov <m.zakirov@samsung.com>
+
+ * asan.c (build_check_stmt): Alignment arg was added.
+ (asan_expand_check_ifn): Optimization for alignment >= 8.
+
+2014-09-10 Jakub Jelinek <jakub@redhat.com>
+
+ * flag-types.h (enum sanitize_code): Add SANITIZE_NONNULL_ATTRIBUTE
+ and SANITIZE_RETURNS_NONNULL_ATTRIBUTE, or them into SANITIZE_UNDEFINED.
+ * opts.c (common_handle_option): Handle SANITIZE_NONNULL_ATTRIBUTE and
+ SANITIZE_RETURNS_NONNULL_ATTRIBUTE and disable
+ flag_delete_null_pointer_checks for them.
+ * sanitizer.def (BUILT_IN_UBSAN_HANDLE_NONNULL_ARG,
+ BUILT_IN_UBSAN_HANDLE_NONNULL_ARG_ABORT,
+ BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN,
+ BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_ABORT): New.
+ * ubsan.c (instrument_bool_enum_load): Set *gsi back to
+ stmt's iterator.
+ (instrument_nonnull_arg, instrument_nonnull_return): New functions.
+ (pass_ubsan::gate): Return true even for SANITIZE_NONNULL_ATTRIBUTE
+ or SANITIZE_RETURNS_NONNULL_ATTRIBUTE.
+ (pass_ubsan::execute): Call instrument_nonnull_{arg,return}.
+ * doc/invoke.texi (-fsanitize=nonnull-attribute,
+ -fsanitize=returns-nonnull-attribute): Document.
+
+2014-09-10 Jakub Jelinek <jakub@redhat.com>
+
+ * ubsan.h (struct ubsan_mismatch_data): Removed.
+ (ubsan_create_data): Remove MISMATCH argument, add LOCCNT argument.
+ * ubsan.c (ubsan_source_location): For unknown locations,
+ pass { NULL, 0, 0 } instead of { "<unknown>", x, y }.
+ (ubsan_create_data): Remove MISMATCH argument, add LOCCNT argument.
+ Allow more than one location and arbitrary extra arguments passed
+ in ... instead of through MISMATCH pointer.
+ (ubsan_instrument_unreachable, ubsan_expand_bounds_ifn,
+ ubsan_expand_null_ifn, ubsan_build_overflow_builtin,
+ instrument_bool_enum_load, ubsan_instrument_float_cast): Adjust
+ callers.
+
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * opts.c (common_handle_option): Handle -fsanitize=alignment.
+ * ubsan.h (enum ubsan_null_ckind): Add UBSAN_CTOR_CALL.
+ (ubsan_expand_bounds_ifn, ubsan_expand_null_ifn): Change return
+ type to bool.
+ * stor-layout.h (min_align_of_type): New prototype.
+ * asan.c (pass_sanopt::execute): Don't perform gsi_next if
+ ubsan_expand* told us not to do it. Remove the extra gsi_end_p
+ check.
+ * ubsan.c: Include builtins.h.
+ (ubsan_expand_bounds_ifn): Change return type to bool,
+ always return true.
+ (ubsan_expand_null_ifn): Change return type to bool, change
+ argument to gimple_stmt_iterator *. Handle both null and alignment
+ sanitization, take type from ckind argument's type rather than
+ first argument.
+ (instrument_member_call): Removed.
+ (instrument_mem_ref): Remove t argument, add mem and base arguments.
+ Handle both null and alignment sanitization, don't say whole
+ struct access is member access. Build 3 argument IFN_UBSAN_NULL
+ call instead of 2 argument.
+ (instrument_null): Adjust instrument_mem_ref caller. Don't
+ instrument calls here.
+ (pass_ubsan::gate, pass_ubsan::execute): Handle SANITIZE_ALIGNMENT
+ like SANITIZE_NULL.
+ * stor-layout.c (min_align_of_type): New function.
+ * flag-types.h (enum sanitize_code): Add SANITIZE_ALIGNMENT.
+ Or it into SANITIZE_UNDEFINED.
+ * doc/invoke.texi (-fsanitize=alignment): Document.
+
+2014-08-11 Yury Gribov <y.gribov@samsung.com>
+
+ * asan.c (asan_check_flags): New enum.
+ (build_check_stmt_with_calls): Removed function.
+ (build_check_stmt): Split inlining logic to
+ asan_expand_check_ifn.
+ (instrument_derefs): Rename parameter.
+ (instrument_mem_region_access): Rename parameter.
+ (instrument_strlen_call): Likewise.
+ (asan_expand_check_ifn): New function.
+ (asan_instrument): Remove old code.
+ (pass_sanopt::execute): Change handling of
+ asan-instrumentation-with-call-threshold.
+ (asan_clear_shadow): Fix formatting.
+ (asan_function_start): Likewise.
+ (asan_emit_stack_protection): Likewise.
+ * doc/invoke.texi (asan-instrumentation-with-call-threshold):
+ Update description.
+ * internal-fn.c (expand_ASAN_CHECK): New function.
+ * internal-fn.def (ASAN_CHECK): New internal function.
+ * params.def (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD):
+ Update description.
+ (PARAM_ASAN_USE_AFTER_RETURN): Likewise.
+ * tree.c: Small comment fix.
+
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * opts.c (common_handle_option): Handle -fsanitize=alignment.
+ * ubsan.h (enum ubsan_null_ckind): Add UBSAN_CTOR_CALL.
+ (ubsan_expand_bounds_ifn, ubsan_expand_null_ifn): Change return
+ type to bool.
+ * stor-layout.h (min_align_of_type): New prototype.
+ * asan.c (pass_sanopt::execute): Don't perform gsi_next if
+ ubsan_expand* told us not to do it. Remove the extra gsi_end_p
+ check.
+ * ubsan.c: Include builtins.h.
+ (ubsan_expand_bounds_ifn): Change return type to bool,
+ always return true.
+ (ubsan_expand_null_ifn): Change return type to bool, change
+ argument to gimple_stmt_iterator *. Handle both null and alignment
+ sanitization, take type from ckind argument's type rather than
+ first argument.
+ (instrument_member_call): Removed.
+ (instrument_mem_ref): Remove t argument, add mem and base arguments.
+ Handle both null and alignment sanitization, don't say whole
+ struct access is member access. Build 3 argument IFN_UBSAN_NULL
+ call instead of 2 argument.
+ (instrument_null): Adjust instrument_mem_ref caller. Don't
+ instrument calls here.
+ (pass_ubsan::gate, pass_ubsan::execute): Handle SANITIZE_ALIGNMENT
+ like SANITIZE_NULL.
+ * stor-layout.c (min_align_of_type): New function.
+ * flag-types.h (enum sanitize_code): Add SANITIZE_ALIGNMENT.
+ Or it into SANITIZE_UNDEFINED.
+ * doc/invoke.texi (-fsanitize=alignment): Document.
+
+2014-07-03 Marek Polacek <polacek@redhat.com>
+
+ * doc/invoke.texi (-fsanitize=bounds): Tweak wording.
+ (-fsanitize=float-divide-by-zero): Move to the table with
+ -fsanitize=undefined suboptions.
+ (-fsanitize=float-cast-overflow): Likewise.
+
+2014-06-30 Marek Polacek <polacek@redhat.com>
+
+ * convert.c (convert_to_integer): Don't instrument conversions if the
+ function has no_sanitize_undefined attribute.
+ * ubsan.c: Don't run the ubsan pass if the function has
+ no_sanitize_undefined attribute.
+
+2014-06-30 Jakub Jelinek <jakub@redhat.com>
+
+ * doc/invoke.texi (-fsanitize=bounds): Move to the table with
+ -fsanitize=undefined suboptions.
+
+2014-06-20 Marek Polacek <polacek@redhat.com>
+
+ * asan.c (pass_sanopt::execute): Handle IFN_UBSAN_BOUNDS.
+ * flag-types.h (enum sanitize_code): Add SANITIZE_BOUNDS and or it
+ into SANITIZE_UNDEFINED.
+ * doc/invoke.texi: Describe -fsanitize=bounds.
+ * gimplify.c (gimplify_call_expr): Add gimplification of internal
+ functions created in the FEs.
+ * internal-fn.c: Move "internal-fn.h" after "tree.h".
+ (expand_UBSAN_BOUNDS): New function.
+ * internal-fn.def (UBSAN_BOUNDS): New internal function.
+ * internal-fn.h: Don't define internal functions here.
+ * opts.c (common_handle_option): Add -fsanitize=bounds.
+ * sanitizer.def (BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
+ BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT): Add.
+ * tree-core.h: Define internal functions here.
+ (struct tree_base): Add ifn field.
+ * tree-pretty-print.c: Include "internal-fn.h".
+ (dump_generic_node): Handle functions without CALL_EXPR_FN.
+ * tree.c (get_callee_fndecl): Likewise.
+ (build_call_expr_internal_loc): New function.
+ * tree.def (CALL_EXPR): Update description.
+ * tree.h (CALL_EXPR_IFN): Define.
+ (build_call_expr_internal_loc): Declare.
+ * ubsan.c (get_ubsan_type_info_for_type): Return 0 for non-arithmetic
+ types.
+ (ubsan_type_descriptor): Change bool parameter to enum
+ ubsan_print_style. Adjust the code. Add handling of
+ UBSAN_PRINT_ARRAY.
+ (ubsan_expand_bounds_ifn): New function.
+ (ubsan_expand_null_ifn): Adjust ubsan_type_descriptor call.
+ (ubsan_build_overflow_builtin): Likewise.
+ (instrument_bool_enum_load): Likewise.
+ (ubsan_instrument_float_cast): Likewise.
+ * ubsan.h (enum ubsan_print_style): New enum.
+ (ubsan_expand_bounds_ifn): Declare.
+ (ubsan_type_descriptor): Adjust declaration. Use a default parameter.
+
+2014-05-23 Marek Polacek <polacek@redhat.com>
+ Jakub Jelinek <jakub@redhat.com>
+
+ * builtins.def: Change SANITIZE_FLOAT_DIVIDE to SANITIZE_NONDEFAULT.
+ * gcc.c (sanitize_spec_function): Likewise.
+ * convert.c (convert_to_integer): Include "ubsan.h". Add
+ floating-point to integer instrumentation.
+ * doc/invoke.texi: Document -fsanitize=float-cast-overflow.
+ * flag-types.h (enum sanitize_code): Add SANITIZE_FLOAT_CAST and
+ SANITIZE_NONDEFAULT.
+ * opts.c (common_handle_option): Handle -fsanitize=float-cast-overflow.
+ * sanitizer.def (BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
+ BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT): Add.
+ * ubsan.c: Include "realmpfr.h" and "dfp.h".
+ (get_ubsan_type_info_for_type): Handle REAL_TYPEs.
+ (ubsan_type_descriptor): Set tkind to 0xffff for types other than
+ float/double/long double.
+ (ubsan_instrument_float_cast): New function.
+ * ubsan.h (ubsan_instrument_float_cast): Declare.
+
+2014-05-02 Marek Polacek <polacek@redhat.com>
+
+ * doc/invoke.texi: Describe -fsanitize=float-divide-by-zero.
+
+2014-04-30 Marek Polacek <polacek@redhat.com>
+
+ * gcc.c (sanitize_spec_function): Handle SANITIZE_FLOAT_DIVIDE.
+ * builtins.def: Initialize builtins even for SANITIZE_FLOAT_DIVIDE.
+ * flag-types.h (enum sanitize_code): Add SANITIZE_FLOAT_DIVIDE.
+ * opts.c (common_handle_option): Add -fsanitize=float-divide-by-zero.
+
+2014-04-22 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/60275
+ * common.opt (fsanitize-recover, fsanitize-undefined-trap-on-error):
+ New options.
+ * gcc.c (sanitize_spec_function): Don't return "" for "undefined"
+ if flag_sanitize_undefined_trap_on_error.
+ * sanitizer.def (BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT,
+ BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT,
+ BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE_ABORT,
+ BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT,
+ BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW_ABORT,
+ BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW_ABORT,
+ BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW_ABORT,
+ BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW_ABORT,
+ BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT): New builtins.
+ * ubsan.c (ubsan_instrument_unreachable): Return
+ __builtin_trap () if flag_sanitize_undefined_trap_on_error.
+ (ubsan_expand_null_ifn): Emit __builtin_trap ()
+ if flag_sanitize_undefined_trap_on_error and
+ __ubsan_handle_type_mismatch_abort if !flag_sanitize_recover.
+ (ubsan_expand_null_ifn, ubsan_build_overflow_builtin,
+ instrument_bool_enum_load): Emit __builtin_trap () if
+ flag_sanitize_undefined_trap_on_error and
+ __builtin_handle_*_abort () if !flag_sanitize_recover.
+ * doc/invoke.texi (-fsanitize-recover,
+ -fsanitize-undefined-trap-on-error): Document.
+
+2014-04-22 Lin Zuojian <manjian2006@gmail.com>
+
+ PR middle-end/60281
+ * asan.c (asan_emit_stack_protection): Force the base to align to
+ appropriate bits if STRICT_ALIGNMENT. Set shadow_mem align to
+ appropriate bits if STRICT_ALIGNMENT.
+ * cfgexpand.c (expand_stack_vars): Set base_align appropriately
+ when asan is on.
+ (expand_used_vars): Leave a space in the stack frame for alignment
+ if STRICT_ALIGNMENT.
+
+2014-05-21 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/61272
+ * ubsan.c (is_ubsan_builtin_p): Turn assert into a condition.
+
2015-10-27 Caroline Tice <cmtice@google.com>
(from Richard Biener)
asan.o \
tsan.o \
ubsan.o \
+ sanopt.o \
tree-call-cdce.o \
tree-cfg.o \
tree-cfgcleanup.o \
$(srcdir)/asan.c \
$(srcdir)/ubsan.c \
$(srcdir)/tsan.c \
+ $(srcdir)/sanopt.c \
$(srcdir)/ipa-devirt.c \
$(srcdir)/internal-fn.h \
@all_gtfiles@
#include "ubsan.h"
#include "predict.h"
#include "params.h"
+#include "builtins.h"
+#include "fnmatch.h"
/* AddressSanitizer finds out-of-bounds and use-after-free bugs
with <2x slowdown on average.
// 1 if it has dynamic initialization, 0 otherwise.
uptr __has_dynamic_init;
+
+ // A pointer to struct that contains source location, could be NULL.
+ __asan_global_source_location *__location;
}
A destructor function that calls the runtime asan library function
_asan_unregister_globals is also installed. */
+static unsigned HOST_WIDE_INT asan_shadow_offset_value;
+static bool asan_shadow_offset_computed;
+static vec<char *> sanitized_sections;
+
+/* Sets shadow offset to value in string VAL. */
+
+bool
+set_asan_shadow_offset (const char *val)
+{
+ char *endp;
+
+ errno = 0;
+#ifdef HAVE_LONG_LONG
+ asan_shadow_offset_value = strtoull (val, &endp, 0);
+#else
+ asan_shadow_offset_value = strtoul (val, &endp, 0);
+#endif
+ if (!(*val != '\0' && *endp == '\0' && errno == 0))
+ return false;
+
+ asan_shadow_offset_computed = true;
+
+ return true;
+}
+
+/* Set list of user-defined sections that need to be sanitized. */
+
+void
+set_sanitized_sections (const char *sections)
+{
+ char *pat;
+ unsigned i;
+ FOR_EACH_VEC_ELT (sanitized_sections, i, pat)
+ free (pat);
+ sanitized_sections.truncate (0);
+
+ for (const char *s = sections; *s; )
+ {
+ const char *end;
+ for (end = s; *end && *end != ','; ++end);
+ size_t len = end - s;
+ sanitized_sections.safe_push (xstrndup (s, len));
+ s = *end ? end + 1 : end;
+ }
+}
+
+/* Checks whether section SEC should be sanitized. */
+
+static bool
+section_sanitized_p (const char *sec)
+{
+ char *pat;
+ unsigned i;
+ FOR_EACH_VEC_ELT (sanitized_sections, i, pat)
+ if (fnmatch (pat, sec, FNM_PERIOD) == 0)
+ return true;
+ return false;
+}
+
+/* Returns Asan shadow offset. */
+
+static unsigned HOST_WIDE_INT
+asan_shadow_offset ()
+{
+ if (!asan_shadow_offset_computed)
+ {
+ asan_shadow_offset_computed = true;
+ asan_shadow_offset_value = targetm.asan_shadow_offset ();
+ }
+ return asan_shadow_offset_value;
+}
+
alias_set_type asan_shadow_set = -1;
/* Pointer types to 1 resp. 2 byte integers in shadow memory. A separate
ASAN_CHECK_STORE = 1 << 0,
ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
- ASAN_CHECK_START_INSTRUMENTED = 1 << 3,
- ASAN_CHECK_END_INSTRUMENTED = 1 << 4,
- ASAN_CHECK_LAST = 1 << 5
+ ASAN_CHECK_LAST = 1 << 3
};
/* Hashtable support for memory references used by gimple
inline hashval_t
asan_mem_ref_hasher::hash (const asan_mem_ref *mem_ref)
{
- hashval_t h = iterative_hash_expr (mem_ref->start, 0);
- h = iterative_hash_host_wide_int (mem_ref->access_size, h);
- return h;
+ return iterative_hash_expr (mem_ref->start, 0);
}
/* Compare two memory references. We accept the length of either
asan_mem_ref_hasher::equal (const asan_mem_ref *m1,
const asan_mem_ref *m2)
{
- return (m1->access_size == m2->access_size
- && operand_equal_p (m1->start, m2->start, 0));
+ return operand_equal_p (m1->start, m2->start, 0);
}
static hash_table <asan_mem_ref_hasher> asan_mem_ref_ht;
asan_mem_ref r;
asan_mem_ref_init (&r, ref, access_size);
- return (get_mem_ref_hash_table ().find (&r) != NULL);
+ asan_mem_ref *saved_ref = get_mem_ref_hash_table ()->find (&r);
+ return saved_ref && saved_ref->access_size >= access_size;
}
/* Return true iff the memory reference REF has been instrumented. */
static bool
has_mem_ref_been_instrumented (const asan_mem_ref *ref, tree len)
{
- /* First let's see if the address of the beginning of REF has been
- instrumented. */
- if (!has_mem_ref_been_instrumented (ref))
- return false;
+ HOST_WIDE_INT size_in_bytes
+ = tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
- if (len != 0)
- {
- /* Let's see if the end of the region has been instrumented. */
- if (!has_mem_ref_been_instrumented (asan_mem_ref_get_end (ref, len),
- ref->access_size))
- return false;
- }
- return true;
+ return size_in_bytes != -1
+ && has_mem_ref_been_instrumented (ref->start, size_in_bytes);
}
/* Set REF to the memory reference present in a gimple assignment
asan_mem_ref *dst,
tree *dst_len,
bool *dst_is_store,
- bool *dest_is_deref)
+ bool *dest_is_deref,
+ bool *intercepted_p)
{
gcc_checking_assert (gimple_call_builtin_p (call, BUILT_IN_NORMAL));
bool is_store = true, got_reference_p = false;
HOST_WIDE_INT access_size = 1;
+ *intercepted_p = asan_intercepted_p ((DECL_FUNCTION_CODE (callee)));
+
switch (DECL_FUNCTION_CODE (callee))
{
/* (s, s, n) style memops. */
tree src0_len = NULL_TREE, src1_len = NULL_TREE, dest_len = NULL_TREE;
bool src0_is_store = false, src1_is_store = false,
- dest_is_store = false, dest_is_deref = false;
+ dest_is_store = false, dest_is_deref = false, intercepted_p = true;
if (get_mem_refs_of_builtin_call (stmt,
&src0, &src0_len, &src0_is_store,
&src1, &src1_len, &src1_is_store,
&dest, &dest_len, &dest_is_store,
- &dest_is_deref))
+ &dest_is_deref, &intercepted_p))
{
if (src0.start != NULL_TREE
&& !has_mem_ref_been_instrumented (&src0, src0_len))
return true;
}
}
+ else if (is_gimple_call (stmt) && gimple_store_p (stmt))
+ {
+ asan_mem_ref r;
+ asan_mem_ref_init (&r, NULL, 1);
+
+ r.start = gimple_call_lhs (stmt);
+ r.access_size = int_size_in_bytes (TREE_TYPE (r.start));
+ return has_mem_ref_been_instrumented (&r);
+ }
+
return false;
}
asan_mem_ref r;
asan_mem_ref_init (&r, ref, access_size);
- asan_mem_ref **slot = ht.find_slot (&r, INSERT);
- if (*slot == NULL)
+ asan_mem_ref **slot = ht->find_slot (&r, INSERT);
+ if (*slot == NULL || (*slot)->access_size < access_size)
*slot = asan_mem_ref_new (ref, access_size);
}
snprintf (buf, sizeof buf, "__asan_stack_malloc_%d",
use_after_return_class);
ret = init_one_libfunc (buf);
- rtx addr = convert_memory_address (ptr_mode, base);
- ret = emit_library_call_value (ret, NULL_RTX, LCT_NORMAL, ptr_mode, 2,
+ ret = emit_library_call_value (ret, NULL_RTX, LCT_NORMAL, ptr_mode, 1,
GEN_INT (asan_frame_size
+ base_align_bias),
- TYPE_MODE (pointer_sized_int_node),
- addr, ptr_mode);
+ TYPE_MODE (pointer_sized_int_node));
+ /* __asan_stack_malloc_[n] returns a pointer to fake stack if succeeded
+ and NULL otherwise. Check RET value is NULL here and jump over the
+ BASE reassignment in this case. Otherwise, reassign BASE to RET. */
+ int very_unlikely = REG_BR_PROB_BASE / 2000 - 1;
+ emit_cmp_and_jump_insns (ret, const0_rtx, EQ, NULL_RTX,
+ VOIDmode, 0, lab, very_unlikely);
ret = convert_memory_address (Pmode, ret);
emit_move_insn (base, ret);
emit_label (lab);
NULL_RTX, 1, OPTAB_DIRECT);
shadow_base
= plus_constant (Pmode, shadow_base,
- targetm.asan_shadow_offset ()
+ asan_shadow_offset ()
+ (base_align_bias >> ASAN_SHADOW_SHIFT));
gcc_assert (asan_shadow_set != -1
&& (ASAN_RED_ZONE_SIZE >> ASAN_SHADOW_SHIFT) == 4);
to be an array of such vars, putting padding in there
breaks this assumption. */
|| (DECL_SECTION_NAME (decl) != NULL_TREE
- && !DECL_HAS_IMPLICIT_SECTION_NAME_P (decl))
+ && !DECL_HAS_IMPLICIT_SECTION_NAME_P (decl)
+ && !section_sanitized_p (TREE_STRING_POINTER (DECL_SECTION_NAME (decl))))
|| DECL_SIZE (decl) == 0
|| ASAN_RED_ZONE_SIZE * BITS_PER_UNIT > MAX_OFILE_ALIGNMENT
|| !valid_constant_size_p (DECL_SIZE_UNIT (decl))
- || DECL_ALIGN_UNIT (decl) > 2 * ASAN_RED_ZONE_SIZE)
+ || DECL_ALIGN_UNIT (decl) > 2 * ASAN_RED_ZONE_SIZE
+ || TREE_TYPE (decl) == ubsan_get_source_location_type ())
return false;
rtl = DECL_RTL (decl);
IS_STORE is either 1 (for a store) or 0 (for a load). */
static tree
-report_error_func (bool is_store, HOST_WIDE_INT size_in_bytes, int *nargs)
-{
- static enum built_in_function report[2][6]
- = { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
- BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
- BUILT_IN_ASAN_REPORT_LOAD16, BUILT_IN_ASAN_REPORT_LOAD_N },
- { BUILT_IN_ASAN_REPORT_STORE1, BUILT_IN_ASAN_REPORT_STORE2,
- BUILT_IN_ASAN_REPORT_STORE4, BUILT_IN_ASAN_REPORT_STORE8,
- BUILT_IN_ASAN_REPORT_STORE16, BUILT_IN_ASAN_REPORT_STORE_N } };
+report_error_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
+ int *nargs)
+{
+ static enum built_in_function report[2][2][6]
+ = { { { BUILT_IN_ASAN_REPORT_LOAD1, BUILT_IN_ASAN_REPORT_LOAD2,
+ BUILT_IN_ASAN_REPORT_LOAD4, BUILT_IN_ASAN_REPORT_LOAD8,
+ BUILT_IN_ASAN_REPORT_LOAD16, BUILT_IN_ASAN_REPORT_LOAD_N },
+ { BUILT_IN_ASAN_REPORT_STORE1, BUILT_IN_ASAN_REPORT_STORE2,
+ BUILT_IN_ASAN_REPORT_STORE4, BUILT_IN_ASAN_REPORT_STORE8,
+ BUILT_IN_ASAN_REPORT_STORE16, BUILT_IN_ASAN_REPORT_STORE_N } },
+ { { BUILT_IN_ASAN_REPORT_LOAD1_NOABORT,
+ BUILT_IN_ASAN_REPORT_LOAD2_NOABORT,
+ BUILT_IN_ASAN_REPORT_LOAD4_NOABORT,
+ BUILT_IN_ASAN_REPORT_LOAD8_NOABORT,
+ BUILT_IN_ASAN_REPORT_LOAD16_NOABORT,
+ BUILT_IN_ASAN_REPORT_LOAD_N_NOABORT },
+ { BUILT_IN_ASAN_REPORT_STORE1_NOABORT,
+ BUILT_IN_ASAN_REPORT_STORE2_NOABORT,
+ BUILT_IN_ASAN_REPORT_STORE4_NOABORT,
+ BUILT_IN_ASAN_REPORT_STORE8_NOABORT,
+ BUILT_IN_ASAN_REPORT_STORE16_NOABORT,
+ BUILT_IN_ASAN_REPORT_STORE_N_NOABORT } } };
if (size_in_bytes == -1)
{
*nargs = 2;
- return builtin_decl_implicit (report[is_store][5]);
+ return builtin_decl_implicit (report[recover_p][is_store][5]);
}
*nargs = 1;
- return builtin_decl_implicit (report[is_store][exact_log2 (size_in_bytes)]);
+ int size_log2 = exact_log2 (size_in_bytes);
+ return builtin_decl_implicit (report[recover_p][is_store][size_log2]);
}
/* Construct a function tree for __asan_{load,store}{1,2,4,8,16,_n}.
IS_STORE is either 1 (for a store) or 0 (for a load). */
static tree
-check_func (bool is_store, int size_in_bytes, int *nargs)
-{
- static enum built_in_function check[2][6]
- = { { BUILT_IN_ASAN_LOAD1, BUILT_IN_ASAN_LOAD2,
- BUILT_IN_ASAN_LOAD4, BUILT_IN_ASAN_LOAD8,
- BUILT_IN_ASAN_LOAD16, BUILT_IN_ASAN_LOADN },
- { BUILT_IN_ASAN_STORE1, BUILT_IN_ASAN_STORE2,
- BUILT_IN_ASAN_STORE4, BUILT_IN_ASAN_STORE8,
- BUILT_IN_ASAN_STORE16, BUILT_IN_ASAN_STOREN } };
+check_func (bool is_store, bool recover_p, HOST_WIDE_INT size_in_bytes,
+ int *nargs)
+{
+ static enum built_in_function check[2][2][6]
+ = { { { BUILT_IN_ASAN_LOAD1, BUILT_IN_ASAN_LOAD2,
+ BUILT_IN_ASAN_LOAD4, BUILT_IN_ASAN_LOAD8,
+ BUILT_IN_ASAN_LOAD16, BUILT_IN_ASAN_LOADN },
+ { BUILT_IN_ASAN_STORE1, BUILT_IN_ASAN_STORE2,
+ BUILT_IN_ASAN_STORE4, BUILT_IN_ASAN_STORE8,
+ BUILT_IN_ASAN_STORE16, BUILT_IN_ASAN_STOREN } },
+ { { BUILT_IN_ASAN_LOAD1_NOABORT,
+ BUILT_IN_ASAN_LOAD2_NOABORT,
+ BUILT_IN_ASAN_LOAD4_NOABORT,
+ BUILT_IN_ASAN_LOAD8_NOABORT,
+ BUILT_IN_ASAN_LOAD16_NOABORT,
+ BUILT_IN_ASAN_LOADN_NOABORT },
+ { BUILT_IN_ASAN_STORE1_NOABORT,
+ BUILT_IN_ASAN_STORE2_NOABORT,
+ BUILT_IN_ASAN_STORE4_NOABORT,
+ BUILT_IN_ASAN_STORE8_NOABORT,
+ BUILT_IN_ASAN_STORE16_NOABORT,
+ BUILT_IN_ASAN_STOREN_NOABORT } } };
if (size_in_bytes == -1)
{
*nargs = 2;
- return builtin_decl_implicit (check[is_store][5]);
+ return builtin_decl_implicit (check[recover_p][is_store][5]);
}
*nargs = 1;
- return builtin_decl_implicit (check[is_store][exact_log2 (size_in_bytes)]);
+ int size_log2 = exact_log2 (size_in_bytes);
+ return builtin_decl_implicit (check[recover_p][is_store][size_log2]);
}
/* Split the current basic block and create a condition statement
}
/* Build
- (base_addr >> ASAN_SHADOW_SHIFT) + targetm.asan_shadow_offset (). */
+ (base_addr >> ASAN_SHADOW_SHIFT) + asan_shadow_offset (). */
static tree
build_shadow_mem_access (gimple_stmt_iterator *gsi, location_t location,
gimple_set_location (g, location);
gsi_insert_after (gsi, g, GSI_NEW_STMT);
- t = build_int_cst (uintptr_type, targetm.asan_shadow_offset ());
+ t = build_int_cst (uintptr_type, asan_shadow_offset ());
g = gimple_build_assign_with_ops (PLUS_EXPR,
make_ssa_name (uintptr_type, NULL),
gimple_assign_lhs (g), t);
build_check_stmt (location_t loc, tree base, tree len,
HOST_WIDE_INT size_in_bytes, gimple_stmt_iterator *iter,
bool is_non_zero_len, bool before_p, bool is_store,
- bool is_scalar_access, unsigned int align = 0,
- bool start_instrumented = false,
- bool end_instrumented = false)
+ bool is_scalar_access, unsigned int align = 0)
{
gimple_stmt_iterator gsi = *iter;
gimple g;
gcc_assert (!(size_in_bytes > 0 && !is_non_zero_len));
- if (start_instrumented && end_instrumented)
- {
- if (!before_p)
- gsi_next (iter);
- return;
- }
-
gsi = *iter;
base = unshare_expr (base);
flags |= ASAN_CHECK_NON_ZERO_LEN;
if (is_scalar_access)
flags |= ASAN_CHECK_SCALAR_ACCESS;
- if (start_instrumented)
- flags |= ASAN_CHECK_START_INSTRUMENTED;
- if (end_instrumented)
- flags |= ASAN_CHECK_END_INSTRUMENTED;
- g = gimple_build_call_internal (IFN_ASAN_CHECK, 3,
+ g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
build_int_cst (integer_type_node, flags),
- base, len);
+ base, len,
+ build_int_cst (integer_type_node,
+ align / BITS_PER_UNIT));
gimple_set_location (g, loc);
if (before_p)
gsi_insert_before (&gsi, g, GSI_SAME_STMT);
tree type, base;
HOST_WIDE_INT size_in_bytes;
+ if (location == UNKNOWN_LOCATION)
+ location = EXPR_LOCATION (t);
type = TREE_TYPE (t);
switch (TREE_CODE (t))
case INDIRECT_REF:
case MEM_REF:
case VAR_DECL:
+ case BIT_FIELD_REF:
break;
/* FALLTHRU */
default:
}
+/* Insert a memory reference into the hash table if access length
+ can be determined in compile time. */
+
+static void
+maybe_update_mem_ref_hash_table (tree base, tree len)
+{
+ if (!POINTER_TYPE_P (TREE_TYPE (base))
+ || !INTEGRAL_TYPE_P (TREE_TYPE (len)))
+ return;
+
+ HOST_WIDE_INT size_in_bytes = tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
+
+ if (size_in_bytes != -1)
+ update_mem_ref_hash_table (base, size_in_bytes);
+}
+
/* Instrument an access to a contiguous memory region that starts at
the address pointed to by BASE, over a length of LEN (expressed in
the sizeof (*BASE) bytes). ITER points to the instruction before
|| integer_zerop (len))
return;
- /* If the beginning of the memory region has already been
- instrumented, do not instrument it. */
- bool start_instrumented = has_mem_ref_been_instrumented (base, 1);
-
- /* If the end of the memory region has already been instrumented, do
- not instrument it. */
- tree end = asan_mem_ref_get_end (base, len);
- bool end_instrumented = has_mem_ref_been_instrumented (end, 1);
-
HOST_WIDE_INT size_in_bytes = tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
- build_check_stmt (location, base, len, size_in_bytes, iter,
- /*is_non_zero_len*/size_in_bytes > 0, /*before_p*/true,
- is_store, /*is_scalar_access*/false, /*align*/0,
- start_instrumented, end_instrumented);
-
- update_mem_ref_hash_table (base, 1);
- if (size_in_bytes != -1)
- update_mem_ref_hash_table (end, 1);
+ if ((size_in_bytes == -1)
+ || !has_mem_ref_been_instrumented (base, size_in_bytes))
+ {
+ build_check_stmt (location, base, len, size_in_bytes, iter,
+ /*is_non_zero_len*/size_in_bytes > 0, /*before_p*/true,
+ is_store, /*is_scalar_access*/false, /*align*/0);
+ }
+ maybe_update_mem_ref_hash_table (base, len);
*iter = gsi_for_stmt (gsi_stmt (*iter));
}
-/* Instrument the call (to the builtin strlen function) pointed to by
- ITER.
-
- This function instruments the access to the first byte of the
- argument, right before the call. After the call it instruments the
- access to the last byte of the argument; it uses the result of the
- call to deduce the offset of that last byte.
-
- Upon completion, iff the call has actually been instrumented, this
- function returns TRUE and *ITER points to the statement logically
- following the built-in strlen function call *ITER was initially
- pointing to. Otherwise, the function returns FALSE and *ITER
- remains unchanged. */
-
-static bool
-instrument_strlen_call (gimple_stmt_iterator *iter)
-{
- gimple g;
- gimple call = gsi_stmt (*iter);
- gcc_assert (is_gimple_call (call));
-
- tree callee = gimple_call_fndecl (call);
- gcc_assert (is_builtin_fn (callee)
- && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (callee) == BUILT_IN_STRLEN);
-
- location_t loc = gimple_location (call);
-
- tree len = gimple_call_lhs (call);
- if (len == NULL)
- /* Some passes might clear the return value of the strlen call;
- bail out in that case. Return FALSE as we are not advancing
- *ITER. */
- return false;
- gcc_assert (INTEGRAL_TYPE_P (TREE_TYPE (len)));
-
- len = maybe_cast_to_ptrmode (loc, len, iter, /*before_p*/false);
-
- tree str_arg = gimple_call_arg (call, 0);
- bool start_instrumented = has_mem_ref_been_instrumented (str_arg, 1);
-
- tree cptr_type = build_pointer_type (char_type_node);
- g = gimple_build_assign_with_ops (NOP_EXPR,
- make_ssa_name (cptr_type, NULL),
- str_arg, NULL);
- gimple_set_location (g, loc);
- gsi_insert_before (iter, g, GSI_SAME_STMT);
- str_arg = gimple_assign_lhs (g);
-
- build_check_stmt (loc, str_arg, NULL_TREE, 1, iter,
- /*is_non_zero_len*/true, /*before_p=*/true,
- /*is_store=*/false, /*is_scalar_access*/true, /*align*/0,
- start_instrumented, start_instrumented);
-
- g = gimple_build_assign_with_ops (POINTER_PLUS_EXPR,
- make_ssa_name (cptr_type, NULL),
- str_arg,
- len);
- gimple_set_location (g, loc);
- gsi_insert_after (iter, g, GSI_NEW_STMT);
-
- build_check_stmt (loc, gimple_assign_lhs (g), NULL_TREE, 1, iter,
- /*is_non_zero_len*/true, /*before_p=*/false,
- /*is_store=*/false, /*is_scalar_access*/true, /*align*/0);
-
- return true;
-}
-
/* Instrument the call to a built-in memory access function that is
pointed to by the iterator ITER.
gcc_checking_assert (gimple_call_builtin_p (call, BUILT_IN_NORMAL));
- tree callee = gimple_call_fndecl (call);
location_t loc = gimple_location (call);
- if (DECL_FUNCTION_CODE (callee) == BUILT_IN_STRLEN)
- iter_advanced_p = instrument_strlen_call (iter);
- else
- {
- asan_mem_ref src0, src1, dest;
- asan_mem_ref_init (&src0, NULL, 1);
- asan_mem_ref_init (&src1, NULL, 1);
- asan_mem_ref_init (&dest, NULL, 1);
+ asan_mem_ref src0, src1, dest;
+ asan_mem_ref_init (&src0, NULL, 1);
+ asan_mem_ref_init (&src1, NULL, 1);
+ asan_mem_ref_init (&dest, NULL, 1);
- tree src0_len = NULL_TREE, src1_len = NULL_TREE, dest_len = NULL_TREE;
- bool src0_is_store = false, src1_is_store = false,
- dest_is_store = false, dest_is_deref = false;
+ tree src0_len = NULL_TREE, src1_len = NULL_TREE, dest_len = NULL_TREE;
+ bool src0_is_store = false, src1_is_store = false, dest_is_store = false,
+ dest_is_deref = false, intercepted_p = true;
- if (get_mem_refs_of_builtin_call (call,
- &src0, &src0_len, &src0_is_store,
- &src1, &src1_len, &src1_is_store,
- &dest, &dest_len, &dest_is_store,
- &dest_is_deref))
+ if (get_mem_refs_of_builtin_call (call,
+ &src0, &src0_len, &src0_is_store,
+ &src1, &src1_len, &src1_is_store,
+ &dest, &dest_len, &dest_is_store,
+ &dest_is_deref, &intercepted_p))
+ {
+ if (dest_is_deref)
{
- if (dest_is_deref)
- {
- instrument_derefs (iter, dest.start, loc, dest_is_store);
- gsi_next (iter);
- iter_advanced_p = true;
- }
- else if (src0_len || src1_len || dest_len)
- {
- if (src0.start != NULL_TREE)
- instrument_mem_region_access (src0.start, src0_len,
- iter, loc, /*is_store=*/false);
- if (src1.start != NULL_TREE)
- instrument_mem_region_access (src1.start, src1_len,
- iter, loc, /*is_store=*/false);
- if (dest.start != NULL_TREE)
- instrument_mem_region_access (dest.start, dest_len,
- iter, loc, /*is_store=*/true);
- *iter = gsi_for_stmt (call);
- gsi_next (iter);
- iter_advanced_p = true;
- }
+ instrument_derefs (iter, dest.start, loc, dest_is_store);
+ gsi_next (iter);
+ iter_advanced_p = true;
+ }
+ else if (!intercepted_p
+ && (src0_len || src1_len || dest_len))
+ {
+ if (src0.start != NULL_TREE)
+ instrument_mem_region_access (src0.start, src0_len,
+ iter, loc, /*is_store=*/false);
+ if (src1.start != NULL_TREE)
+ instrument_mem_region_access (src1.start, src1_len,
+ iter, loc, /*is_store=*/false);
+ if (dest.start != NULL_TREE)
+ instrument_mem_region_access (dest.start, dest_len,
+ iter, loc, /*is_store=*/true);
+
+ *iter = gsi_for_stmt (call);
+ gsi_next (iter);
+ iter_advanced_p = true;
+ }
+ else
+ {
+ if (src0.start != NULL_TREE)
+ maybe_update_mem_ref_hash_table (src0.start, src0_len);
+ if (src1.start != NULL_TREE)
+ maybe_update_mem_ref_hash_table (src1.start, src1_len);
+ if (dest.start != NULL_TREE)
+ maybe_update_mem_ref_hash_table (dest.start, dest_len);
}
}
return iter_advanced_p;
case BUILT_IN_TRAP:
/* Don't instrument these. */
return false;
+ default:
+ break;
}
}
tree decl = builtin_decl_implicit (BUILT_IN_ASAN_HANDLE_NO_RETURN);
gimple_set_location (g, gimple_location (stmt));
gsi_insert_before (iter, g, GSI_SAME_STMT);
}
- return false;
+
+ bool instrumented = false;
+ if (gimple_store_p (stmt))
+ {
+ tree ref_expr = gimple_call_lhs (stmt);
+ instrument_derefs (iter, ref_expr,
+ gimple_location (stmt),
+ /*is_store=*/true);
+
+ instrumented = true;
+ }
+
+ /* Walk through gimple_call arguments and check them id needed. */
+ unsigned args_num = gimple_call_num_args (stmt);
+ for (unsigned i = 0; i < args_num; ++i)
+ {
+ tree arg = gimple_call_arg (stmt, i);
+ /* If ARG is not a non-aggregate register variable, compiler in general
+ creates temporary for it and pass it as argument to gimple call.
+ But in some cases, e.g. when we pass by value a small structure that
+ fits to register, compiler can avoid extra overhead by pulling out
+ these temporaries. In this case, we should check the argument. */
+ if (!is_gimple_reg (arg) && !is_gimple_min_invariant (arg))
+ {
+ instrument_derefs (iter, arg,
+ gimple_location (stmt),
+ /*is_store=*/false);
+ instrumented = true;
+ }
+ }
+ if (instrumented)
+ gsi_next (iter);
+ return instrumented;
}
/* Walk each instruction of all basic block and instrument those that
const void *__name;
const void *__module_name;
uptr __has_dynamic_init;
+ __asan_global_source_location *__location;
} type. */
static tree
asan_global_struct (void)
{
- static const char *field_names[6]
+ static const char *field_names[7]
= { "__beg", "__size", "__size_with_redzone",
- "__name", "__module_name", "__has_dynamic_init" };
- tree fields[6], ret;
+ "__name", "__module_name", "__has_dynamic_init", "__location"};
+ tree fields[7], ret;
int i;
ret = make_node (RECORD_TYPE);
- for (i = 0; i < 6; i++)
+ for (i = 0; i < 7; i++)
{
fields[i]
= build_decl (UNKNOWN_LOCATION, FIELD_DECL,
if (i)
DECL_CHAIN (fields[i - 1]) = fields[i];
}
+ tree type_decl = build_decl (input_location, TYPE_DECL,
+ get_identifier ("__asan_global"), ret);
+ DECL_IGNORED_P (type_decl) = 1;
+ DECL_ARTIFICIAL (type_decl) = 1;
TYPE_FIELDS (ret) = fields[0];
- TYPE_NAME (ret) = get_identifier ("__asan_global");
+ TYPE_NAME (ret) = type_decl;
+ TYPE_STUB_DECL (ret) = type_decl;
layout_type (ret);
return ret;
}
int has_dynamic_init = vnode ? vnode->dynamically_initialized : 0;
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE,
build_int_cst (uptr, has_dynamic_init));
+ tree locptr = NULL_TREE;
+ location_t loc = DECL_SOURCE_LOCATION (decl);
+ expanded_location xloc = expand_location (loc);
+ if (xloc.file != NULL)
+ {
+ static int lasanloccnt = 0;
+ char buf[25];
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LASANLOC", ++lasanloccnt);
+ tree var = build_decl (UNKNOWN_LOCATION, VAR_DECL, get_identifier (buf),
+ ubsan_get_source_location_type ());
+ TREE_STATIC (var) = 1;
+ TREE_PUBLIC (var) = 0;
+ DECL_ARTIFICIAL (var) = 1;
+ DECL_IGNORED_P (var) = 1;
+ pretty_printer filename_pp;
+ pp_string (&filename_pp, xloc.file);
+ tree str = asan_pp_string (&filename_pp);
+ tree ctor = build_constructor_va (TREE_TYPE (var), 3,
+ NULL_TREE, str, NULL_TREE,
+ build_int_cst (unsigned_type_node,
+ xloc.line), NULL_TREE,
+ build_int_cst (unsigned_type_node,
+ xloc.column));
+ TREE_CONSTANT (ctor) = 1;
+ TREE_STATIC (ctor) = 1;
+ DECL_INITIAL (var) = ctor;
+ varpool_node::finalize_decl (var);
+ locptr = fold_convert (uptr, build_fold_addr_expr (var));
+ }
+ else
+ locptr = build_int_cst (uptr, 0);
+ CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, locptr);
init = build_constructor (type, vinner);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init);
}
pointer_sized_int_node, NULL_TREE);
tree BT_FN_VOID_INT
= build_function_type_list (void_type_node, integer_type_node, NULL_TREE);
+ tree BT_FN_SIZE_CONST_PTR_INT
+ = build_function_type_list (size_type_node, const_ptr_type_node,
+ integer_type_node, NULL_TREE);
tree BT_FN_BOOL_VPTR_PTR_IX_INT_INT[5];
tree BT_FN_IX_CONST_VPTR_INT[5];
tree BT_FN_IX_VPTR_IX_INT[5];
#define ATTR_TMPURE_NOTHROW_LEAF_LIST ECF_TM_PURE | ATTR_NOTHROW_LEAF_LIST
#undef ATTR_NORETURN_NOTHROW_LEAF_LIST
#define ATTR_NORETURN_NOTHROW_LEAF_LIST ECF_NORETURN | ATTR_NOTHROW_LEAF_LIST
+#undef ATTR_CONST_NORETURN_NOTHROW_LEAF_LIST
+#define ATTR_CONST_NORETURN_NOTHROW_LEAF_LIST \
+ ECF_CONST | ATTR_NORETURN_NOTHROW_LEAF_LIST
#undef ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST
#define ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST \
ECF_TM_PURE | ATTR_NORETURN_NOTHROW_LEAF_LIST
#undef ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST
#define ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST \
/* ECF_COLD missing */ ATTR_NORETURN_NOTHROW_LEAF_LIST
+#undef ATTR_COLD_CONST_NORETURN_NOTHROW_LEAF_LIST
+#define ATTR_COLD_CONST_NORETURN_NOTHROW_LEAF_LIST \
+ /* ECF_COLD missing */ ATTR_CONST_NORETURN_NOTHROW_LEAF_LIST
+#undef ATTR_PURE_NOTHROW_LEAF_LIST
+#define ATTR_PURE_NOTHROW_LEAF_LIST ECF_PURE | ATTR_NOTHROW_LEAF_LIST
#undef DEF_SANITIZER_BUILTIN
#define DEF_SANITIZER_BUILTIN(ENUM, NAME, TYPE, ATTRS) \
decl = add_builtin_function ("__builtin_" NAME, TYPE, ENUM, \
#include "sanitizer.def"
+ /* -fsanitize=object-size uses __builtin_object_size, but that might
+ not be available for e.g. Fortran at this point. We use
+ DEF_SANITIZER_BUILTIN here only as a convenience macro. */
+ if ((flag_sanitize & SANITIZE_OBJECT_SIZE)
+ && !builtin_decl_implicit_p (BUILT_IN_OBJECT_SIZE))
+ DEF_SANITIZER_BUILTIN (BUILT_IN_OBJECT_SIZE, "object_size",
+ BT_FN_SIZE_CONST_PTR_INT,
+ ATTR_PURE_NOTHROW_LEAF_LIST)
+
#undef DEF_SANITIZER_BUILTIN
}
{
tree fn = builtin_decl_implicit (BUILT_IN_ASAN_INIT);
append_to_statement_list (build_call_expr (fn, 0), &asan_ctor_statements);
+ fn = builtin_decl_implicit (BUILT_IN_ASAN_VERSION_MISMATCH_CHECK);
+ append_to_statement_list (build_call_expr (fn, 0), &asan_ctor_statements);
}
FOR_EACH_DEFINED_VARIABLE (vnode)
if (TREE_ASM_WRITTEN (vnode->decl)
/* Expand the ASAN_{LOAD,STORE} builtins. */
-static bool
+bool
asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
{
gimple g = gsi_stmt (*iter);
location_t loc = gimple_location (g);
+ bool recover_p;
+ if (flag_sanitize & SANITIZE_USER_ADDRESS)
+ recover_p = (flag_sanitize_recover & SANITIZE_USER_ADDRESS) != 0;
+ else
+ recover_p = (flag_sanitize_recover & SANITIZE_KERNEL_ADDRESS) != 0;
HOST_WIDE_INT flags = tree_to_shwi (gimple_call_arg (g, 0));
gcc_assert (flags < ASAN_CHECK_LAST);
bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
bool is_store = (flags & ASAN_CHECK_STORE) != 0;
bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
- bool start_instrumented = (flags & ASAN_CHECK_START_INSTRUMENTED) != 0;
- bool end_instrumented = (flags & ASAN_CHECK_END_INSTRUMENTED) != 0;
tree base = gimple_call_arg (g, 1);
tree len = gimple_call_arg (g, 2);
+ HOST_WIDE_INT align = tree_to_shwi (gimple_call_arg (g, 3));
HOST_WIDE_INT size_in_bytes
= is_scalar_access && tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
tree base_addr = gimple_assign_lhs (g);
int nargs;
- tree fun = check_func (is_store, size_in_bytes, &nargs);
+ tree fun = check_func (is_store, recover_p, size_in_bytes, &nargs);
if (nargs == 1)
g = gimple_build_call (fun, 1, base_addr);
else
basic_block then_bb, else_bb;
gsi = create_cond_insert_point (&gsi, /*before_p*/false,
/*then_more_likely_p=*/false,
- /*create_then_fallthru_edge=*/false,
+ /*create_then_fallthru_edge*/recover_p,
&then_bb,
&else_bb);
else
{
/* Slow path for 1, 2 and 4 byte accesses. */
-
- if (!start_instrumented)
+ /* Test (shadow != 0)
+ & ((base_addr & 7) + (real_size_in_bytes - 1)) >= shadow). */
+ tree shadow = build_shadow_mem_access (&gsi, loc, base_addr,
+ shadow_ptr_type);
+ gimple shadow_test = build_assign (NE_EXPR, shadow, 0);
+ gimple_seq seq = NULL;
+ gimple_seq_add_stmt (&seq, shadow_test);
+ /* Aligned (>= 8 bytes) can test just
+ (real_size_in_bytes - 1 >= shadow), as base_addr & 7 is known
+ to be 0. */
+ if (align < 8)
{
- /* Test (shadow != 0)
- & ((base_addr & 7) + (real_size_in_bytes - 1)) >= shadow). */
- tree shadow = build_shadow_mem_access (&gsi, loc, base_addr,
- shadow_ptr_type);
- gimple shadow_test = build_assign (NE_EXPR, shadow, 0);
- gimple_seq seq = NULL;
- gimple_seq_add_stmt (&seq, shadow_test);
- gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, base_addr, 7));
- gimple_seq_add_stmt (&seq, build_type_cast (shadow_type,
- gimple_seq_last (seq)));
+ gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR,
+ base_addr, 7));
+ gimple_seq_add_stmt (&seq,
+ build_type_cast (shadow_type,
+ gimple_seq_last (seq)));
if (real_size_in_bytes > 1)
gimple_seq_add_stmt (&seq,
- build_assign (PLUS_EXPR, gimple_seq_last (seq),
+ build_assign (PLUS_EXPR,
+ gimple_seq_last (seq),
real_size_in_bytes - 1));
- gimple_seq_add_stmt (&seq, build_assign (GE_EXPR,
- gimple_seq_last (seq),
- shadow));
- gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, shadow_test,
- gimple_seq_last (seq)));
- t = gimple_assign_lhs (gimple_seq_last (seq));
- gimple_seq_set_location (seq, loc);
- gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
+ t = gimple_assign_lhs (gimple_seq_last_stmt (seq));
}
+ else
+ t = build_int_cst (shadow_type, real_size_in_bytes - 1);
+ gimple_seq_add_stmt (&seq, build_assign (GE_EXPR, t, shadow));
+ gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, shadow_test,
+ gimple_seq_last (seq)));
+ t = gimple_assign_lhs (gimple_seq_last (seq));
+ gimple_seq_set_location (seq, loc);
+ gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
/* For non-constant, misaligned or otherwise weird access sizes,
- check first and last byte. */
- if (size_in_bytes == -1 && !end_instrumented)
+ check first and last byte. */
+ if (size_in_bytes == -1)
{
g = gimple_build_assign_with_ops (MINUS_EXPR,
make_ssa_name (pointer_sized_int_node, NULL),
shadow));
gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, shadow_test,
gimple_seq_last (seq)));
- if (!start_instrumented)
- gimple_seq_add_stmt (&seq, build_assign (BIT_IOR_EXPR, t,
- gimple_seq_last (seq)));
+ gimple_seq_add_stmt (&seq, build_assign (BIT_IOR_EXPR, t,
+ gimple_seq_last (seq)));
t = gimple_assign_lhs (gimple_seq_last (seq));
gimple_seq_set_location (seq, loc);
gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
/* Generate call to the run-time library (e.g. __asan_report_load8). */
gsi = gsi_start_bb (then_bb);
int nargs;
- tree fun = report_error_func (is_store, size_in_bytes, &nargs);
+ tree fun = report_error_func (is_store, recover_p, size_in_bytes, &nargs);
g = gimple_build_call (fun, nargs, base_addr, len);
gimple_set_location (g, loc);
gsi_insert_after (&gsi, g, GSI_NEW_STMT);
return new pass_asan_O0 (ctxt);
}
-/* Perform optimization of sanitize functions. */
-
-static unsigned int
-execute_sanopt (void)
-{
- basic_block bb;
-
- int asan_num_accesses = 0;
- if (flag_sanitize & SANITIZE_ADDRESS)
- {
- gimple_stmt_iterator gsi;
- FOR_EACH_BB_FN (bb, cfun)
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
- {
- gimple stmt = gsi_stmt (gsi);
- if (is_gimple_call (stmt) && gimple_call_internal_p (stmt)
- && gimple_call_internal_fn (stmt) == IFN_ASAN_CHECK)
- ++asan_num_accesses;
- }
- }
-
- bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
- && (flag_sanitize & SANITIZE_KERNEL_ADDRESS)
- && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
-
- FOR_EACH_BB_FN (bb, cfun)
- {
- gimple_stmt_iterator gsi;
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
- {
- gimple stmt = gsi_stmt (gsi);
- bool no_next = false;
-
- if (!is_gimple_call (stmt))
- {
- gsi_next (&gsi);
- continue;
- }
-
- if (gimple_call_internal_p (stmt))
- {
- enum internal_fn ifn = gimple_call_internal_fn (stmt);
- switch (ifn)
- {
- case IFN_UBSAN_NULL:
- ubsan_expand_null_ifn (gsi);
- break;
- case IFN_ASAN_CHECK:
- {
- no_next = asan_expand_check_ifn (&gsi, use_calls);
- break;
- }
- default:
- break;
- }
- }
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Optimized\n ");
- print_gimple_stmt (dump_file, stmt, 0, dump_flags);
- fprintf (dump_file, "\n");
- }
-
- if (!no_next)
- gsi_next (&gsi);
- }
- }
- return 0;
-}
-
-static bool
-gate_sanopt (void)
-{
- return flag_sanitize;
-}
-
-namespace {
-
-const pass_data pass_data_sanopt =
-{
- GIMPLE_PASS, /* type */
- "sanopt", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
- true, /* has_execute */
- TV_NONE, /* tv_id */
- ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- ( TODO_verify_flow | TODO_verify_stmts
- | TODO_update_ssa ), /* todo_flags_finish */
-};
-
-class pass_sanopt : public gimple_opt_pass
-{
-public:
- pass_sanopt (gcc::context *ctxt)
- : gimple_opt_pass (pass_data_sanopt, ctxt)
- {}
-
- /* opt_pass methods: */
- bool gate () { return gate_sanopt (); }
- unsigned int execute () { return execute_sanopt (); }
-
-}; // class pass_sanopt
-
-} // anon namespace
-
-gimple_opt_pass *
-make_pass_sanopt (gcc::context *ctxt)
-{
- return new pass_sanopt (ctxt);
-}
-
#include "gt-asan.h"
extern bool asan_protect_global (tree);
extern void initialize_sanitizer_builtins (void);
extern tree asan_dynamic_init_call (bool);
+extern bool asan_expand_check_ifn (gimple_stmt_iterator *, bool);
extern gimple_stmt_iterator create_cond_insert_point
(gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
extern alias_set_type asan_shadow_set;
/* Shadow memory is found at
- (address >> ASAN_SHADOW_SHIFT) + targetm.asan_shadow_offset (). */
+ (address >> ASAN_SHADOW_SHIFT) + asan_shadow_offset (). */
#define ASAN_SHADOW_SHIFT 3
/* Red zone size, stack and global variables are padded by ASAN_RED_ZONE_SIZE
return c ? 2 * ASAN_RED_ZONE_SIZE - c : ASAN_RED_ZONE_SIZE;
}
+extern bool set_asan_shadow_offset (const char *);
+
+extern void set_sanitized_sections (const char *);
+
+/* Return TRUE if builtin with given FCODE will be intercepted by
+ libasan. */
+
+static inline bool
+asan_intercepted_p (enum built_in_function fcode)
+{
+ return fcode == BUILT_IN_INDEX
+ || fcode == BUILT_IN_MEMCHR
+ || fcode == BUILT_IN_MEMCMP
+ || fcode == BUILT_IN_MEMCPY
+ || fcode == BUILT_IN_MEMMOVE
+ || fcode == BUILT_IN_MEMSET
+ || fcode == BUILT_IN_STRCASECMP
+ || fcode == BUILT_IN_STRCAT
+ || fcode == BUILT_IN_STRCHR
+ || fcode == BUILT_IN_STRCMP
+ || fcode == BUILT_IN_STRCPY
+ || fcode == BUILT_IN_STRDUP
+ || fcode == BUILT_IN_STRLEN
+ || fcode == BUILT_IN_STRNCASECMP
+ || fcode == BUILT_IN_STRNCAT
+ || fcode == BUILT_IN_STRNCMP
+ || fcode == BUILT_IN_STRNCPY;
+}
#endif /* TREE_ASAN */
ATTR_NULL, ATTR_NOTHROW_LIST)
DEF_ATTR_TREE_LIST (ATTR_SENTINEL_NOTHROW_LEAF_LIST, ATTR_SENTINEL, \
ATTR_NULL, ATTR_NOTHROW_LEAF_LIST)
+DEF_ATTR_TREE_LIST (ATTR_COLD_CONST_NORETURN_NOTHROW_LEAF_LIST, ATTR_CONST,\
+ ATTR_NULL, ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
/* Functions whose pointer parameter(s) are all nonnull. */
DEF_ATTR_TREE_LIST (ATTR_NONNULL_LIST, ATTR_NONNULL, ATTR_NULL, ATTR_NULL)
#include "value-prof.h"
#include "diagnostic-core.h"
#include "builtins.h"
-#include "ubsan.h"
+#include "asan.h"
#include "cilk.h"
if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
+ /* When ASan is enabled, we don't want to expand some memory/string
+ builtins and rely on libsanitizer's hooks. This allows us to avoid
+ redundant checks and be sure, that possible overflow will be detected
+ by ASan. */
+
+ if ((flag_sanitize & SANITIZE_ADDRESS) && asan_intercepted_p (fcode))
+ return expand_call (exp, target, ignore);
+
/* When not optimizing, generate calls to library functions for a certain
set of builtins. */
if (!optimize
case BUILT_IN_CLASSIFY_TYPE:
return fold_builtin_classify_type (NULL_TREE);
- case BUILT_IN_UNREACHABLE:
- if (flag_sanitize & SANITIZE_UNREACHABLE
- && (current_function_decl == NULL
- || !lookup_attribute ("no_sanitize_undefined",
- DECL_ATTRIBUTES (current_function_decl))))
- return ubsan_instrument_unreachable (loc);
- break;
-
default:
break;
}
return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray);
}
+/* Helper function for build_call_* functions; build a CALL_EXPR with
+ indicated RETURN_TYPE, FN, and NARGS, but do not initialize any of
+ the argument slots. */
+
+static tree
+build_call_1 (tree return_type, tree fn, int nargs)
+{
+ tree t;
+
+ t = build_vl_exp (CALL_EXPR, nargs + 3);
+ TREE_TYPE (t) = return_type;
+ CALL_EXPR_FN (t) = fn;
+ CALL_EXPR_STATIC_CHAIN (t) = NULL;
+
+ return t;
+}
+
+/* Build internal call expression. This is just like CALL_EXPR, except
+ its CALL_EXPR_FN is NULL. It will get gimplified later into ordinary
+ internal function. */
+
+tree
+build_call_expr_internal_loc (location_t loc, enum internal_fn ifn,
+ tree type, int n, ...)
+{
+ va_list ap;
+ int i;
+
+ tree fn = build_call_1 (type, NULL_TREE, n);
+ va_start (ap, n);
+ for (i = 0; i < n; i++)
+ CALL_EXPR_ARG (fn, i) = va_arg (ap, tree);
+ va_end (ap);
+ SET_EXPR_LOCATION (fn, loc);
+ CALL_EXPR_IFN (fn) = ifn;
+ return fn;
+}
+
+
/* Construct a CALL_EXPR with type TYPE with FN as the function expression.
N arguments are passed in the array ARGARRAY. */
DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE, \
true, true, true, ATTRS, true, \
(flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
- | SANITIZE_UNDEFINED)))
+ | SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT)))
#undef DEF_CILKPLUS_BUILTIN
#define DEF_CILKPLUS_BUILTIN(ENUM, NAME, TYPE, ATTRS) \
+2015-09-25 Marek Polacek <polacek@redhat.com>
+
+ * c-ubsan.c (ubsan_instrument_division): Remove unnecessary code.
+ (ubsan_instrument_shift): Likewise.
+
+2015-09-25 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/64906
+ * c-ubsan.c (ubsan_instrument_division): Also pre-evaluate OP1.
+
+2015-07-23 Marek Polacek <polacek@redhat.com>
+
+ * c-ubsan.c (ubsan_instrument_division): Use unshare_expr throughout.
+ (ubsan_instrument_shift): Likewise.
+
+2015-07-23 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/66908
+ * c-ubsan.c: Include gimplify.h.
+ (ubsan_instrument_division): Unshare OP0 and OP1.
+ (ubsan_instrument_shift): Likewise.
+
+2015-05-25 Marek Polacek <polacek@redhat.com>
+
+ * c-ubsan.c (ubsan_instrument_shift): Use type0.
+
+2015-05-07 Marek Polacek <polacek@redhat.com>
+ Martin Uecker <uecker@eecs.berkeley.edu>
+
+ * c-ubsan.c (ubsan_instrument_bounds): Don't skip instrumenting
+ flexible member array-like members if SANITIZE_BOUNDS_STRICT.
+
+2015-03-07 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/65280
+ * c-ubsan.c (ubsan_instrument_bounds): Check for COMPONENT_REF
+ before trying to figure out whether we have a flexible array member.
+
+2014-11-28 Marek Polacek <polacek@redhat.com>
+
+ * c-ubsan.c (ubsan_instrument_shift): Use op1_utype for MINUS_EXPR
+ instead of unsigned_type_node.
+
+2014-11-28 Marek Polacek <polacek@redhat.com>
+
+ PR c/63862
+ * c-ubsan.c (ubsan_instrument_shift): Change the type of a MINUS_EXPR
+ to op1_utype.
+ * c-gimplify.c (c_gimplify_expr): Convert right operand of a shift
+ expression to unsigned_type_node.
+
+2014-11-18 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/63813
+ * c-ubsan.c (ubsan_maybe_instrument_reference_or_call): Change type
+ argument to ptype, set type to TREE_TYPE (ptype). Don't call
+ get_pointer_alignment for non-pointers. Use ptype, or if it is
+ reference type, corresponding pointer type, as type of kind
+ argument.
+ (ubsan_maybe_instrument_reference,
+ ubsan_maybe_instrument_member_call): Adjust callers.
+
+2014-10-23 Marek Polacek <polacek@redhat.com>
+
+ * c-ubsan.c (ubsan_instrument_shift): Perform the MINUS_EXPR
+ in unsigned type.
+
+2014-10-22 Jakub Jelinek <jakub@redhat.com>
+ Yury Gribov <y.gribov@samsung.com>
+
+ * c-ubsan.c (ubsan_instrument_division, ubsan_instrument_shift,
+ ubsan_instrument_vla): Check bits in flag_sanitize_recover bitmask
+ instead of flag_sanitize_recover as bool flag.
+
+2014-09-24 Marek Polacek <polacek@redhat.com>
+
+ PR c/61405
+ PR c/53874
+ * c-common.h (struct c_common_resword): Don't define CPP_KEYWORD.
+
+2014-09-10 Jakub Jelinek <jakub@redhat.com>
+
+ * c-ubsan.c (ubsan_instrument_division, ubsan_instrument_shift,
+ ubsan_instrument_vla, ubsan_instrument_return): Adjust
+ ubsan_create_data callers.
+ (ubsan_instrument_bounds): Don't emit UBSAN_BOUNDS at all if
+ index is constant or BIT_AND_EXPR with constant mask and is
+ small enough for the bound.
+ * c-gimplify.c (ubsan_walk_array_refs_r): For ADDR_EXPR of
+ ARRAY_REF, make sure the inner ARRAY_REF is not walked again.
+
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * c-common.h (min_align_of_type): Removed prototype.
+ * c-common.c (min_align_of_type): Removed.
+ * c-ubsan.h (ubsan_maybe_instrument_reference,
+ ubsan_maybe_instrument_member_call): New prototypes.
+ * c-ubsan.c: Include stor-layout.h and builtins.h.
+ (ubsan_maybe_instrument_reference_or_call,
+ ubsan_maybe_instrument_reference, ubsan_maybe_instrument_call): New
+ functions.
+
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * c-common.h (min_align_of_type): Removed prototype.
+ * c-common.c (min_align_of_type): Removed.
+ * c-ubsan.h (ubsan_maybe_instrument_reference,
+ ubsan_maybe_instrument_member_call): New prototypes.
+ * c-ubsan.c: Include stor-layout.h and builtins.h.
+ (ubsan_maybe_instrument_reference_or_call,
+ ubsan_maybe_instrument_reference, ubsan_maybe_instrument_call): New
+ functions.
+
+2014-07-15 Marek Polacek <polacek@redhat.com>
+
+ * c-ubsan.c (ubsan_instrument_bounds): Don't instrument if
+ TYPE_MAX_VALUE is NULL.
+
+2014-04-30 Marek Polacek <polacek@redhat.com>
+
+ * c-ubsan.c (ubsan_instrument_division): Handle REAL_TYPEs. Perform
+ INT_MIN / -1 sanitization only for integer types.
+
+2014-04-22 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/60275
+ * c-ubsan.c (ubsan_instrument_return): Return __builtin_trap ()
+ if flag_sanitize_undefined_trap_on_error.
+ (ubsan_instrument_division, ubsan_instrument_shift,
+ ubsan_instrument_vla): Likewise. Use __ubsan_handle_*_abort ()
+ if !flag_sanitize_recover.
+
2015-06-26 Release Manager
* GCC 4.9.3 released.
return -1;
}
\f
-/* Return the least alignment required for type TYPE. */
-
-unsigned int
-min_align_of_type (tree type)
-{
- unsigned int align = TYPE_ALIGN (type);
- align = MIN (align, BIGGEST_ALIGNMENT);
- if (!TYPE_USER_ALIGN (type))
- {
-#ifdef BIGGEST_FIELD_ALIGNMENT
- align = MIN (align, BIGGEST_FIELD_ALIGNMENT);
-#endif
- unsigned int field_align = align;
-#ifdef ADJUST_FIELD_ALIGN
- tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, type);
- field_align = ADJUST_FIELD_ALIGN (field, field_align);
-#endif
- align = MIN (align, field_align);
- }
- return align / BITS_PER_UNIT;
-}
/* Compute the value of 'sizeof (TYPE)' or '__alignof__ (TYPE)', where
the IS_SIZEOF parameter indicates which operator is being applied.
/* Extra cpp_ttype values for C++. */
-/* A token type for keywords, as opposed to ordinary identifiers. */
-#define CPP_KEYWORD ((enum cpp_ttype) (N_TTYPES + 1))
-
/* A token type for template-ids. If a template-id is processed while
parsing tentatively, it is replaced with a CPP_TEMPLATE_ID token;
the value of the CPP_TEMPLATE_ID is whatever was returned by
extern tree c_save_expr (tree);
extern tree c_common_truthvalue_conversion (location_t, tree);
extern void c_apply_type_quals_to_decl (int, tree);
-extern unsigned int min_align_of_type (tree);
extern tree c_sizeof_or_alignof_type (location_t, tree, bool, bool, int);
extern tree c_alignof_expr (location_t, tree);
/* Print an error message for invalid operands to arith operation CODE.
#include "c-pretty-print.h"
#include "cgraph.h"
#include "cilk.h"
+#include "c-ubsan.h"
+#include "pointer-set.h"
/* The gimplification pass converts the language-dependent trees
(ld-trees) emitted by the parser into language-independent trees
walk back up, we check that they fit our constraints, and copy them
into temporaries if not. */
+/* Callback for c_genericize. */
+
+static tree
+ubsan_walk_array_refs_r (tree *tp, int *walk_subtrees, void *data)
+{
+ struct pointer_set_t *pset = (struct pointer_set_t *) data;
+
+ /* Since walk_tree doesn't call the callback function on the decls
+ in BIND_EXPR_VARS, we have to walk them manually. */
+ if (TREE_CODE (*tp) == BIND_EXPR)
+ {
+ for (tree decl = BIND_EXPR_VARS (*tp); decl; decl = DECL_CHAIN (decl))
+ {
+ if (TREE_STATIC (decl))
+ {
+ *walk_subtrees = 0;
+ continue;
+ }
+ walk_tree (&DECL_INITIAL (decl), ubsan_walk_array_refs_r, pset,
+ pset);
+ walk_tree (&DECL_SIZE (decl), ubsan_walk_array_refs_r, pset, pset);
+ walk_tree (&DECL_SIZE_UNIT (decl), ubsan_walk_array_refs_r, pset,
+ pset);
+ }
+ }
+ else if (TREE_CODE (*tp) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (*tp, 0)) == ARRAY_REF)
+ {
+ ubsan_maybe_instrument_array_ref (&TREE_OPERAND (*tp, 0), true);
+ /* Make sure ubsan_maybe_instrument_array_ref is not called again
+ on the ARRAY_REF, the above call might not instrument anything
+ as the index might be constant or masked, so ensure it is not
+ walked again and walk its subtrees manually. */
+ tree aref = TREE_OPERAND (*tp, 0);
+ pset->add (aref);
+ *walk_subtrees = 0;
+ walk_tree (&TREE_OPERAND (aref, 0), ubsan_walk_array_refs_r, pset, pset);
+ walk_tree (&TREE_OPERAND (aref, 1), ubsan_walk_array_refs_r, pset, pset);
+ walk_tree (&TREE_OPERAND (aref, 2), ubsan_walk_array_refs_r, pset, pset);
+ walk_tree (&TREE_OPERAND (aref, 3), ubsan_walk_array_refs_r, pset, pset);
+ }
+ else if (TREE_CODE (*tp) == ARRAY_REF)
+ ubsan_maybe_instrument_array_ref (tp, false);
+ return NULL_TREE;
+}
+
/* Gimplification of statement trees. */
/* Convert the tree representation of FNDECL from C frontend trees to
int local_dump_flags;
struct cgraph_node *cgn;
+ if (flag_sanitize & SANITIZE_BOUNDS)
+ {
+ struct pointer_set_t *pset = pointer_set_create ();
+ walk_tree (&DECL_SAVED_TREE (fndecl), ubsan_walk_array_refs_r, pset,
+ pset);
+ pointer_set_destroy (pset);
+ }
+
/* Dump the C-specific tree IR. */
dump_orig = dump_begin (TDI_original, &local_dump_flags);
if (dump_orig)
switch (code)
{
+ case LSHIFT_EXPR:
+ case RSHIFT_EXPR:
+ {
+ /* We used to convert the right operand of a shift-expression
+ to an integer_type_node in the FEs. But it is unnecessary
+ and not desirable for diagnostics and sanitizers. We keep
+ this here to not pessimize the code, but we convert to an
+ unsigned type, because negative shift counts are undefined
+ anyway.
+ We should get rid of this conversion when we have a proper
+ type demotion/promotion pass. */
+ tree *op1_p = &TREE_OPERAND (*expr_p, 1);
+ if (TREE_CODE (TREE_TYPE (*op1_p)) != VECTOR_TYPE
+ && TYPE_MAIN_VARIANT (TREE_TYPE (*op1_p)) != unsigned_type_node)
+ *op1_p = convert (unsigned_type_node, *op1_p);
+ break;
+ }
+
case DECL_EXPR:
/* This is handled mostly by gimplify.c, but we have to deal with
not warning about int x = x; as it is a GCC extension to turn off
}
break;
}
-
+
case CILK_SPAWN_STMT:
- gcc_assert
- (fn_contains_cilk_spawn_p (cfun)
+ gcc_assert
+ (fn_contains_cilk_spawn_p (cfun)
&& cilk_detect_spawn_and_unwrap (expr_p));
-
+
/* If errors are seen, then just process it as a CALL_EXPR. */
if (!seen_error ())
return (enum gimplify_status) gimplify_cilk_spawn (expr_p);
-
+
case MODIFY_EXPR:
case INIT_EXPR:
case CALL_EXPR:
#include "c-family/c-common.h"
#include "c-family/c-ubsan.h"
#include "asan.h"
+#include "internal-fn.h"
+#include "stor-layout.h"
+#include "builtins.h"
+#include "gimplify.h"
/* Instrument division by zero and INT_MIN / -1. If not instrumenting,
return NULL_TREE. */
gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (op0))
== TYPE_MAIN_VARIANT (TREE_TYPE (op1)));
- /* TODO: REAL_TYPE is not supported yet. */
- if (TREE_CODE (type) != INTEGER_TYPE)
- return NULL_TREE;
+ op0 = unshare_expr (op0);
+ op1 = unshare_expr (op1);
- t = fold_build2 (EQ_EXPR, boolean_type_node,
- op1, build_int_cst (type, 0));
+ if (TREE_CODE (type) == INTEGER_TYPE
+ && (flag_sanitize & 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))
+ t = fold_build2 (EQ_EXPR, boolean_type_node,
+ op1, build_real (type, dconst0));
+ else
+ return NULL_TREE;
/* We check INT_MIN / -1 only for signed types. */
- if (!TYPE_UNSIGNED (type))
+ if (TREE_CODE (type) == INTEGER_TYPE
+ && (flag_sanitize & SANITIZE_DIVIDE)
+ && !TYPE_UNSIGNED (type))
{
tree x;
- tt = fold_build2 (EQ_EXPR, boolean_type_node, op1,
+ tt = fold_build2 (EQ_EXPR, boolean_type_node, unshare_expr (op1),
build_int_cst (type, -1));
x = fold_build2 (EQ_EXPR, boolean_type_node, op0,
TYPE_MIN_VALUE (type));
/* In case we have a SAVE_EXPR in a conditional context, we need to
make sure it gets evaluated before the condition. */
- t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
- tree data = ubsan_create_data ("__ubsan_overflow_data",
- &loc, NULL,
- ubsan_type_descriptor (type, false),
- NULL_TREE);
- data = build_fold_addr_expr_loc (loc, data);
- tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW);
- tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0),
- ubsan_encode_value (op1));
- t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_zero_node);
+ t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), unshare_expr (op0), t);
+ t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), unshare_expr (op1), t);
+ if (flag_sanitize_undefined_trap_on_error)
+ tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data = ubsan_create_data ("__ubsan_overflow_data", 1, &loc,
+ ubsan_type_descriptor (type), NULL_TREE,
+ NULL_TREE);
+ data = build_fold_addr_expr_loc (loc, data);
+ enum built_in_function bcode
+ = (flag_sanitize_recover & SANITIZE_DIVIDE)
+ ? BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW
+ : BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT;
+ tt = builtin_decl_explicit (bcode);
+ op0 = unshare_expr (op0);
+ op1 = unshare_expr (op1);
+ tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0),
+ ubsan_encode_value (op1));
+ }
+ t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_node);
return t;
}
HOST_WIDE_INT op0_prec = TYPE_PRECISION (type0);
tree uprecm1 = build_int_cst (op1_utype, op0_prec - 1);
+ op0 = unshare_expr (op0);
+ op1 = unshare_expr (op1);
+
t = fold_convert_loc (loc, op1_utype, op1);
t = fold_build2 (GT_EXPR, boolean_type_node, t, uprecm1);
+ /* If this is not a signed operation, don't perform overflow checks.
+ Also punt on bit-fields. */
+ if (!INTEGRAL_TYPE_P (type0)
+ || TYPE_OVERFLOW_WRAPS (type0)
+ || GET_MODE_BITSIZE (TYPE_MODE (type0)) != TYPE_PRECISION (type0))
+ ;
+
/* For signed x << y, in C99/C11, the following:
(unsigned) x >> (uprecm1 - y)
if non-zero, is undefined. */
- if (code == LSHIFT_EXPR
- && !TYPE_UNSIGNED (type0)
- && flag_isoc99)
+ else if (code == LSHIFT_EXPR && flag_isoc99 && cxx_dialect < cxx11)
{
- tree x = fold_build2 (MINUS_EXPR, unsigned_type_node, uprecm1,
- fold_convert (op1_utype, op1));
+ tree x = fold_build2 (MINUS_EXPR, op1_utype, uprecm1,
+ fold_convert (op1_utype, unshare_expr (op1)));
tt = fold_convert_loc (loc, unsigned_type_for (type0), op0);
tt = fold_build2 (RSHIFT_EXPR, TREE_TYPE (tt), tt, x);
tt = fold_build2 (NE_EXPR, boolean_type_node, tt,
build_int_cst (TREE_TYPE (tt), 0));
}
- /* For signed x << y, in C++11/C++14, the following:
+ /* For signed x << y, in C++11 and later, the following:
x < 0 || ((unsigned) x >> (uprecm1 - y))
if > 1, is undefined. */
- if (code == LSHIFT_EXPR
- && !TYPE_UNSIGNED (TREE_TYPE (op0))
- && (cxx_dialect == cxx11 || cxx_dialect == cxx1y))
+ else if (code == LSHIFT_EXPR && cxx_dialect >= cxx11)
{
- tree x = fold_build2 (MINUS_EXPR, unsigned_type_node, uprecm1,
- fold_convert (op1_utype, op1));
- tt = fold_convert_loc (loc, unsigned_type_for (type0), op0);
+ tree x = fold_build2 (MINUS_EXPR, op1_utype, uprecm1,
+ fold_convert (op1_utype, unshare_expr (op1)));
+ tt = fold_convert_loc (loc, unsigned_type_for (type0),
+ unshare_expr (op0));
tt = fold_build2 (RSHIFT_EXPR, TREE_TYPE (tt), tt, x);
tt = fold_build2 (GT_EXPR, boolean_type_node, tt,
build_int_cst (TREE_TYPE (tt), 1));
- x = fold_build2 (LT_EXPR, boolean_type_node, op0,
+ x = fold_build2 (LT_EXPR, boolean_type_node, unshare_expr (op0),
build_int_cst (type0, 0));
tt = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, x, tt);
}
/* In case we have a SAVE_EXPR in a conditional context, we need to
make sure it gets evaluated before the condition. */
- t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
- tree data = ubsan_create_data ("__ubsan_shift_data",
- &loc, NULL,
- ubsan_type_descriptor (type0, false),
- ubsan_type_descriptor (type1, false),
- NULL_TREE);
-
- data = build_fold_addr_expr_loc (loc, data);
-
+ t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), unshare_expr (op0), t);
t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t,
tt ? tt : integer_zero_node);
- tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS);
- tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0),
- ubsan_encode_value (op1));
- t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_zero_node);
+
+ if (flag_sanitize_undefined_trap_on_error)
+ tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data = ubsan_create_data ("__ubsan_shift_data", 1, &loc,
+ ubsan_type_descriptor (type0),
+ ubsan_type_descriptor (type1), NULL_TREE,
+ NULL_TREE);
+ data = build_fold_addr_expr_loc (loc, data);
+
+ enum built_in_function bcode
+ = (flag_sanitize_recover & SANITIZE_SHIFT)
+ ? BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS
+ : BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT;
+ tt = builtin_decl_explicit (bcode);
+ op0 = unshare_expr (op0);
+ op1 = unshare_expr (op1);
+ tt = build_call_expr_loc (loc, tt, 3, data, ubsan_encode_value (op0),
+ ubsan_encode_value (op1));
+ }
+ t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_node);
return t;
}
tree t, tt;
t = fold_build2 (LE_EXPR, boolean_type_node, size, build_int_cst (type, 0));
- tree data = ubsan_create_data ("__ubsan_vla_data",
- &loc, NULL,
- ubsan_type_descriptor (type, false),
- NULL_TREE);
- data = build_fold_addr_expr_loc (loc, data);
- tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE);
- tt = build_call_expr_loc (loc, tt, 2, data, ubsan_encode_value (size));
- t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_zero_node);
+ if (flag_sanitize_undefined_trap_on_error)
+ tt = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data = ubsan_create_data ("__ubsan_vla_data", 1, &loc,
+ ubsan_type_descriptor (type), NULL_TREE,
+ NULL_TREE);
+ data = build_fold_addr_expr_loc (loc, data);
+ enum built_in_function bcode
+ = (flag_sanitize_recover & SANITIZE_VLA)
+ ? BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE
+ : BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE_ABORT;
+ tt = builtin_decl_explicit (bcode);
+ tt = build_call_expr_loc (loc, tt, 2, data, ubsan_encode_value (size));
+ }
+ t = fold_build3 (COND_EXPR, void_type_node, t, tt, void_node);
return t;
}
tree
ubsan_instrument_return (location_t loc)
{
+ if (flag_sanitize_undefined_trap_on_error)
+ return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
/* It is possible that PCH zapped table with definitions of sanitizer
builtins. Reinitialize them if needed. */
initialize_sanitizer_builtins ();
- tree data = ubsan_create_data ("__ubsan_missing_return_data", &loc,
- NULL, NULL_TREE);
+ tree data = ubsan_create_data ("__ubsan_missing_return_data", 1, &loc,
+ NULL_TREE, NULL_TREE);
tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_MISSING_RETURN);
return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data));
}
+
+/* Instrument array bounds for ARRAY_REFs. We create special builtin,
+ that gets expanded in the sanopt pass, and make an array dimension
+ of it. ARRAY is the array, *INDEX is an index to the array.
+ Return NULL_TREE if no instrumentation is emitted.
+ IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR. */
+
+tree
+ubsan_instrument_bounds (location_t loc, tree array, tree *index,
+ bool ignore_off_by_one)
+{
+ tree type = TREE_TYPE (array);
+ tree domain = TYPE_DOMAIN (type);
+
+ if (domain == NULL_TREE || TYPE_MAX_VALUE (domain) == NULL_TREE)
+ return NULL_TREE;
+
+ tree bound = TYPE_MAX_VALUE (domain);
+ if (ignore_off_by_one)
+ bound = fold_build2 (PLUS_EXPR, TREE_TYPE (bound), bound,
+ build_int_cst (TREE_TYPE (bound), 1));
+
+ /* Detect flexible array members and suchlike, unless
+ -fsanitize=bounds-strict. */
+ tree base = get_base_address (array);
+ if ((flag_sanitize & SANITIZE_BOUNDS_STRICT) == 0
+ && TREE_CODE (array) == COMPONENT_REF
+ && base && (TREE_CODE (base) == INDIRECT_REF
+ || TREE_CODE (base) == MEM_REF))
+ {
+ tree next = NULL_TREE;
+ tree cref = array;
+
+ /* Walk all structs/unions. */
+ while (TREE_CODE (cref) == COMPONENT_REF)
+ {
+ if (TREE_CODE (TREE_TYPE (TREE_OPERAND (cref, 0))) == RECORD_TYPE)
+ for (next = DECL_CHAIN (TREE_OPERAND (cref, 1));
+ next && TREE_CODE (next) != FIELD_DECL;
+ next = DECL_CHAIN (next))
+ ;
+ if (next)
+ /* Not a last element. Instrument it. */
+ break;
+ /* Ok, this is the last field of the structure/union. But the
+ aggregate containing the field must be the last field too,
+ recursively. */
+ cref = TREE_OPERAND (cref, 0);
+ }
+ if (!next)
+ /* Don't instrument this flexible array member-like array in non-strict
+ -fsanitize=bounds mode. */
+ return NULL_TREE;
+ }
+
+ /* Don't emit instrumentation in the most common cases. */
+ tree idx = NULL_TREE;
+ if (TREE_CODE (*index) == INTEGER_CST)
+ idx = *index;
+ else if (TREE_CODE (*index) == BIT_AND_EXPR
+ && TREE_CODE (TREE_OPERAND (*index, 1)) == INTEGER_CST)
+ idx = TREE_OPERAND (*index, 1);
+ if (idx
+ && TREE_CODE (bound) == INTEGER_CST
+ && tree_int_cst_sgn (idx) >= 0
+ && tree_int_cst_le (idx, bound))
+ return NULL_TREE;
+
+ *index = save_expr (*index);
+ /* Create a "(T *) 0" tree node to describe the array type. */
+ tree zero_with_type = build_int_cst (build_pointer_type (type), 0);
+ return build_call_expr_internal_loc (loc, IFN_UBSAN_BOUNDS,
+ void_type_node, 3, zero_with_type,
+ *index, bound);
+}
+
+/* Return true iff T is an array that was instrumented by SANITIZE_BOUNDS. */
+
+bool
+ubsan_array_ref_instrumented_p (const_tree t)
+{
+ if (TREE_CODE (t) != ARRAY_REF)
+ return false;
+
+ tree op1 = TREE_OPERAND (t, 1);
+ return TREE_CODE (op1) == COMPOUND_EXPR
+ && TREE_CODE (TREE_OPERAND (op1, 0)) == CALL_EXPR
+ && CALL_EXPR_FN (TREE_OPERAND (op1, 0)) == NULL_TREE
+ && CALL_EXPR_IFN (TREE_OPERAND (op1, 0)) == IFN_UBSAN_BOUNDS;
+}
+
+/* Instrument an ARRAY_REF, if it hasn't already been instrumented.
+ IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR. */
+
+void
+ubsan_maybe_instrument_array_ref (tree *expr_p, bool ignore_off_by_one)
+{
+ if (!ubsan_array_ref_instrumented_p (*expr_p)
+ && current_function_decl != NULL_TREE
+ && !lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES (current_function_decl)))
+ {
+ tree op0 = TREE_OPERAND (*expr_p, 0);
+ tree op1 = TREE_OPERAND (*expr_p, 1);
+ tree e = ubsan_instrument_bounds (EXPR_LOCATION (*expr_p), op0, &op1,
+ ignore_off_by_one);
+ if (e != NULL_TREE)
+ {
+ tree t = copy_node (*expr_p);
+ TREE_OPERAND (t, 1) = build2 (COMPOUND_EXPR, TREE_TYPE (op1),
+ e, op1);
+ *expr_p = t;
+ }
+ }
+}
+
+static tree
+ubsan_maybe_instrument_reference_or_call (location_t loc, tree op, tree ptype,
+ enum ubsan_null_ckind ckind)
+{
+ if (current_function_decl == NULL_TREE
+ || lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES (current_function_decl)))
+ return NULL_TREE;
+
+ tree type = TREE_TYPE (ptype);
+ tree orig_op = op;
+ bool instrument = false;
+ unsigned int mina = 0;
+
+ if (flag_sanitize & SANITIZE_ALIGNMENT)
+ {
+ mina = min_align_of_type (type);
+ if (mina <= 1)
+ mina = 0;
+ }
+ while ((TREE_CODE (op) == NOP_EXPR
+ || TREE_CODE (op) == NON_LVALUE_EXPR)
+ && TREE_CODE (TREE_TYPE (op)) == POINTER_TYPE)
+ op = TREE_OPERAND (op, 0);
+ if (TREE_CODE (op) == NOP_EXPR
+ && TREE_CODE (TREE_TYPE (op)) == REFERENCE_TYPE)
+ {
+ if (mina && mina > min_align_of_type (TREE_TYPE (TREE_TYPE (op))))
+ instrument = true;
+ }
+ else
+ {
+ if ((flag_sanitize & SANITIZE_NULL) && TREE_CODE (op) == ADDR_EXPR)
+ {
+ bool strict_overflow_p = false;
+ /* tree_single_nonzero_warnv_p will not return true for non-weak
+ non-automatic decls with -fno-delete-null-pointer-checks,
+ which is disabled during -fsanitize=null. We don't want to
+ instrument those, just weak vars though. */
+ int save_flag_delete_null_pointer_checks
+ = flag_delete_null_pointer_checks;
+ flag_delete_null_pointer_checks = 1;
+ if (!tree_single_nonzero_warnv_p (op, &strict_overflow_p)
+ || strict_overflow_p)
+ instrument = true;
+ flag_delete_null_pointer_checks
+ = save_flag_delete_null_pointer_checks;
+ }
+ else if (flag_sanitize & SANITIZE_NULL)
+ instrument = true;
+ if (mina && mina > 1)
+ {
+ if (!POINTER_TYPE_P (TREE_TYPE (op))
+ || mina > get_pointer_alignment (op) / BITS_PER_UNIT)
+ instrument = true;
+ }
+ }
+ if (!instrument)
+ return NULL_TREE;
+ op = save_expr (orig_op);
+ gcc_assert (POINTER_TYPE_P (ptype));
+ if (TREE_CODE (ptype) == REFERENCE_TYPE)
+ ptype = build_pointer_type (TREE_TYPE (ptype));
+ tree kind = build_int_cst (ptype, ckind);
+ tree align = build_int_cst (pointer_sized_int_node, mina);
+ tree call
+ = build_call_expr_internal_loc (loc, IFN_UBSAN_NULL, void_type_node,
+ 3, op, kind, align);
+ TREE_SIDE_EFFECTS (call) = 1;
+ return fold_build2 (COMPOUND_EXPR, TREE_TYPE (op), call, op);
+}
+
+/* Instrument a NOP_EXPR to REFERENCE_TYPE if needed. */
+
+void
+ubsan_maybe_instrument_reference (tree stmt)
+{
+ tree op = TREE_OPERAND (stmt, 0);
+ op = ubsan_maybe_instrument_reference_or_call (EXPR_LOCATION (stmt), op,
+ TREE_TYPE (stmt),
+ UBSAN_REF_BINDING);
+ if (op)
+ TREE_OPERAND (stmt, 0) = op;
+}
+
+/* Instrument a CALL_EXPR to a method if needed. */
+
+void
+ubsan_maybe_instrument_member_call (tree stmt, bool is_ctor)
+{
+ if (call_expr_nargs (stmt) == 0)
+ return;
+ tree op = CALL_EXPR_ARG (stmt, 0);
+ if (op == error_mark_node
+ || !POINTER_TYPE_P (TREE_TYPE (op)))
+ return;
+ op = ubsan_maybe_instrument_reference_or_call (EXPR_LOCATION (stmt), op,
+ TREE_TYPE (op),
+ is_ctor ? UBSAN_CTOR_CALL
+ : UBSAN_MEMBER_CALL);
+ if (op)
+ CALL_EXPR_ARG (stmt, 0) = op;
+}
extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree);
extern tree ubsan_instrument_vla (location_t, tree);
extern tree ubsan_instrument_return (location_t);
+extern tree ubsan_instrument_bounds (location_t, tree, tree *, bool);
+extern bool ubsan_array_ref_instrumented_p (const_tree);
+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);
#endif /* GCC_C_UBSAN_H */
+2016-01-06 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/69099
+ * c-convert.c (convert) [INTEGER_TYPE]: Drop ARG. Don't pass ARG to
+ ubsan_instrument_float_cast. Fold EXPR. Use NULL_TREE instead of
+ NULL.
+
+2015-09-04 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/67279
+ * c-typeck.c (build_binary_op): Don't instrument static initializers.
+
+2015-01-05 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64344
+ * c-typeck.c (convert_for_assignment, c_finish_return): For
+ -fsanitize=float-cast-overflow casts from REAL_TYPE to integer/enum
+ types also set in_late_binary_op around convert call.
+ * c-convert.c (convert): For -fsanitize=float-cast-overflow REAL_TYPE
+ to integral type casts, if not in_late_binary_op, pass c_fully_fold
+ result on expr as last argument to ubsan_instrument_float_cast,
+ if in_late_binary_op, don't use c_save_expr but save_expr.
+
+2014-12-17 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64289
+ * c-convert.c: Include ubsan.h.
+ (convert): For real -> integral casts and
+ -fsanitize=float-cast-overflow don't call convert_to_integer, but
+ instead instrument the float cast directly.
+
+2014-11-28 Marek Polacek <polacek@redhat.com>
+
+ PR c/63862
+ * c-typeck.c (build_binary_op) <RSHIFT_EXPR, LSHIFT_EXPR>: Don't
+ convert the right operand to integer type.
+
+2014-09-24 Marek Polacek <polacek@redhat.com>
+
+ PR c/61405
+ PR c/53874
+ * c-parser.c: Don't define CPP_KEYWORD.
+ (c_parser_switch_statement): Pass original type to c_finish_case.
+ * c-tree.h (c_finish_case): Update declaration.
+ * c-typeck.c (c_finish_case): Add TYPE parameter. Pass it
+ conditionally to c_do_switch_warnings.
+
+2014-06-30 Marek Polacek <polacek@redhat.com>
+
+ * c-decl.c (grokdeclarator): Don't instrument VLAs if the function
+ has no_sanitize_undefined attribute.
+
+2014-04-30 Marek Polacek <polacek@redhat.com>
+
+ * c-typeck.c (build_binary_op): Call ubsan_instrument_division
+ also when SANITIZE_FLOAT_DIVIDE is on.
+
2015-06-26 Release Manager
* GCC 4.9.3 released.
#include "c-tree.h"
#include "langhooks.h"
#include "target.h"
+#include "ubsan.h"
/* Change of width--truncation and extension of integers or reals--
is represented with NOP_EXPR. Proper functioning of many things
case INTEGER_TYPE:
case ENUMERAL_TYPE:
+ if (flag_sanitize & SANITIZE_FLOAT_CAST
+ && TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE
+ && COMPLETE_TYPE_P (type)
+ && current_function_decl != NULL_TREE
+ && !lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES (current_function_decl)))
+ {
+ if (in_late_binary_op)
+ expr = save_expr (expr);
+ else
+ {
+ expr = c_save_expr (expr);
+ expr = c_fully_fold (expr, false, NULL);
+ }
+ tree check = ubsan_instrument_float_cast (loc, type, expr);
+ expr = fold_build1 (FIX_TRUNC_EXPR, type, expr);
+ if (check == NULL_TREE)
+ return expr;
+ return fold_build2 (COMPOUND_EXPR, TREE_TYPE (expr), check, expr);
+ }
ret = convert_to_integer (type, e);
goto maybe_fold;
this_size_varies = size_varies = true;
warn_variable_length_array (name, size);
if (flag_sanitize & SANITIZE_VLA
- && decl_context == NORMAL)
+ && decl_context == NORMAL
+ && current_function_decl != NULL_TREE
+ && !lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES
+ (current_function_decl)))
{
/* Evaluate the array size only once. */
size = c_save_expr (size);
C++). It would then be possible to share more of the C and C++
lexer code, if desired. */
-/* The following local token type is used. */
-
-/* A keyword. */
-#define CPP_KEYWORD ((enum cpp_ttype) (N_TTYPES + 1))
-
/* More information about the type of a CPP_NAME token. */
typedef enum c_id_kind {
/* An ordinary identifier. */
save_break = c_break_label;
c_break_label = NULL_TREE;
body = c_parser_c99_block_statement (parser);
- c_finish_case (body);
+ c_finish_case (body, ce.original_type);
if (c_break_label)
{
location_t here = c_parser_peek_token (parser)->location;
extern tree build_compound_literal (location_t, tree, tree, bool);
extern void check_compound_literal_type (location_t, struct c_type_name *);
extern tree c_start_case (location_t, location_t, tree);
-extern void c_finish_case (tree);
+extern void c_finish_case (tree, tree);
extern tree build_asm_expr (location_t, tree, tree, tree, tree, tree, bool);
extern tree build_asm_stmt (tree, tree);
extern int c_types_compatible_p (tree, tree);
{
tree ret;
bool save = in_late_binary_op;
- if (codel == BOOLEAN_TYPE || codel == COMPLEX_TYPE)
+ if (codel == BOOLEAN_TYPE || codel == COMPLEX_TYPE
+ || (coder == REAL_TYPE
+ && (codel == INTEGER_TYPE || codel == ENUMERAL_TYPE)
+ && (flag_sanitize & SANITIZE_FLOAT_CAST)))
in_late_binary_op = true;
ret = convert_and_check (expr_loc != UNKNOWN_LOCATION
? expr_loc : location, type, orig_rhs);
- if (codel == BOOLEAN_TYPE || codel == COMPLEX_TYPE)
- in_late_binary_op = save;
+ in_late_binary_op = save;
return ret;
}
save = in_late_binary_op;
if (TREE_CODE (TREE_TYPE (res)) == BOOLEAN_TYPE
- || TREE_CODE (TREE_TYPE (res)) == COMPLEX_TYPE)
+ || TREE_CODE (TREE_TYPE (res)) == COMPLEX_TYPE
+ || (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)))
in_late_binary_op = true;
inner = t = convert (TREE_TYPE (res), t);
in_late_binary_op = save;
return label;
}
-/* Finish the switch statement. */
+/* Finish the switch statement. TYPE is the original type of the
+ controlling expression of the switch, or NULL_TREE. */
void
-c_finish_case (tree body)
+c_finish_case (tree body, tree type)
{
struct c_switch *cs = c_switch_stack;
location_t switch_location;
/* Emit warnings as needed. */
switch_location = EXPR_LOCATION (cs->switch_expr);
c_do_switch_warnings (cs->cases, switch_location,
- TREE_TYPE (cs->switch_expr),
+ type ? type : TREE_TYPE (cs->switch_expr),
SWITCH_COND (cs->switch_expr));
/* Pop the stack. */
/* Use the type of the value to be shifted. */
result_type = type0;
- /* Convert the non vector shift-count to an integer, regardless
- of size of value being shifted. */
- if (TREE_CODE (TREE_TYPE (op1)) != VECTOR_TYPE
- && TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
- op1 = convert (integer_type_node, op1);
/* Avoid converting op1 to result_type later. */
converted = 1;
}
/* Use the type of the value to be shifted. */
result_type = type0;
- /* Convert the non vector shift-count to an integer, regardless
- of size of value being shifted. */
- if (TREE_CODE (TREE_TYPE (op1)) != VECTOR_TYPE
- && TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
- op1 = convert (integer_type_node, op1);
/* Avoid converting op1 to result_type later. */
converted = 1;
}
return error_mark_node;
}
- if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE))
+ if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE
+ | SANITIZE_FLOAT_DIVIDE))
&& current_function_decl != 0
&& !lookup_attribute ("no_sanitize_undefined",
DECL_ATTRIBUTES (current_function_decl))
- && (doing_div_or_mod || doing_shift))
+ && (doing_div_or_mod || doing_shift)
+ && !require_constant_value)
{
/* OP0 and/or OP1 might have side-effects. */
op0 = c_save_expr (op0);
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))
+ if (doing_div_or_mod && (flag_sanitize & (SANITIZE_DIVIDE
+ | SANITIZE_FLOAT_DIVIDE)))
instrument_expr = ubsan_instrument_division (location, op0, op1);
else if (doing_shift && (flag_sanitize & SANITIZE_SHIFT))
instrument_expr = ubsan_instrument_shift (location, code, op0, op1);
return align / BITS_PER_UNIT;
}
+/* Align given offset BASE with ALIGN. Truncate up if ALIGN_UP is true,
+ down otherwise. Return truncated BASE value. */
+
+static inline unsigned HOST_WIDE_INT
+align_base (HOST_WIDE_INT base, unsigned HOST_WIDE_INT align, bool align_up)
+{
+ return align_up ? (base + align - 1) & -align : base & -align;
+}
+
/* Allocate SIZE bytes at byte alignment ALIGN from the stack frame.
Return the frame offset. */
{
HOST_WIDE_INT offset, new_frame_offset;
- new_frame_offset = frame_offset;
if (FRAME_GROWS_DOWNWARD)
{
- new_frame_offset -= size + frame_phase;
- new_frame_offset &= -align;
- new_frame_offset += frame_phase;
+ new_frame_offset
+ = align_base (frame_offset - frame_phase - size,
+ align, false) + frame_phase;
offset = new_frame_offset;
}
else
{
- new_frame_offset -= frame_phase;
- new_frame_offset += align - 1;
- new_frame_offset &= -align;
- new_frame_offset += frame_phase;
+ new_frame_offset
+ = align_base (frame_offset - frame_phase, align, true) + frame_phase;
offset = new_frame_offset;
new_frame_offset += size;
}
}
}
+/* Return true if the current function should have its stack frame
+ protected by address sanitizer. */
+
+static inline bool
+asan_sanitize_stack_p (void)
+{
+ return ((flag_sanitize & SANITIZE_ADDRESS)
+ && ASAN_STACK
+ && !lookup_attribute ("no_sanitize_address",
+ DECL_ATTRIBUTES (current_function_decl)));
+}
+
/* A subroutine of expand_used_vars. Binpack the variables into
partitions constrained by the interference graph. The overall
algorithm used is as follows:
sizes, as the shorter vars wouldn't be adequately protected.
Don't do that for "large" (unsupported) alignment objects,
those aren't protected anyway. */
- if ((flag_sanitize & SANITIZE_ADDRESS) && ASAN_STACK && isize != jsize
+ if (asan_sanitize_stack_p () && isize != jsize
&& ialign * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
break;
if (alignb * BITS_PER_UNIT <= MAX_SUPPORTED_STACK_ALIGNMENT)
{
base = virtual_stack_vars_rtx;
- if ((flag_sanitize & SANITIZE_ADDRESS) && ASAN_STACK && pred)
+ if (asan_sanitize_stack_p () && pred)
{
- HOST_WIDE_INT prev_offset = frame_offset;
+ HOST_WIDE_INT prev_offset
+ = align_base (frame_offset,
+ MAX (alignb, ASAN_RED_ZONE_SIZE),
+ !FRAME_GROWS_DOWNWARD);
tree repr_decl = NULL_TREE;
-
offset
= alloc_stack_frame_space (stack_vars[i].size
+ ASAN_RED_ZONE_SIZE,
MAX (alignb, ASAN_RED_ZONE_SIZE));
+
data->asan_vec.safe_push (prev_offset);
data->asan_vec.safe_push (offset + stack_vars[i].size);
/* Find best representative of the partition.
/* If stack protection is enabled, *all* stack variables must be deferred,
so that we can re-order the strings to the top of the frame.
Similarly for Address Sanitizer. */
- if (flag_stack_protect || ((flag_sanitize & SANITIZE_ADDRESS) && ASAN_STACK))
+ if (flag_stack_protect || asan_sanitize_stack_p ())
return true;
/* We handle "large" alignment via dynamic allocation. We want to handle
expand_stack_vars (stack_protect_decl_phase_2, &data);
}
- if ((flag_sanitize & SANITIZE_ADDRESS) && ASAN_STACK)
+ if (asan_sanitize_stack_p ())
/* Phase 3, any partitions that need asan protection
in addition to phase 1 and 2. */
expand_stack_vars (asan_decl_phase_3, &data);
Variable
unsigned int flag_sanitize
+; What sanitizers should recover from errors
+Variable
+unsigned int flag_sanitize_recover = SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT | SANITIZE_KERNEL_ADDRESS
+
; Flag whether a prefix has been added to dump_base_name
Variable
bool dump_base_name_prefixed = false
Common Driver Report Joined
Select what to sanitize
+fasan-shadow-offset=
+Common Joined RejectNegative Var(common_deferred_options) Defer
+-fasan-shadow-offset=<number> Use custom shadow memory offset.
+
+fsanitize-sections=
+Common Joined RejectNegative Var(common_deferred_options) Defer
+-fsanitize-sections=<sec1,sec2,...> Sanitize global variables
+in user-defined sections.
+
+fsanitize-recover=
+Common Report Joined
+After diagnosing undefined behavior attempt to continue execution
+
+fsanitize-recover
+Common Report
+This switch is deprecated; use -fsanitize-recover= instead
+
+fsanitize-undefined-trap-on-error
+Common Driver Report Var(flag_sanitize_undefined_trap_on_error) Init(0)
+Use trap instead of a library function for undefined behavior sanitization
+
fasynchronous-unwind-tables
Common Report Var(flag_asynchronous_unwind_tables) Optimization
Generate unwind tables that are exact at each instruction boundary
Common Report Var(flag_dump_noaddr)
Suppress output of addresses in debugging dumps
+freport-bug
+Common Driver Var(flag_report_bug)
+Collect and dump debug information into temporary file if ICE in C/C++
+compiler occured.
+
fdump-passes
Common Var(flag_dump_passes) Init(0)
Dump optimization passes
frandom-seed=
Common Joined RejectNegative Var(common_deferred_options) Defer
--frandom-seed=<string> Make compile reproducible using <string>
+-frandom-seed=<number> Make compile reproducible using <number>
; This switch causes the command line that was used to create an
; object file to be recorded into the object file. The exact format
tree exp, builtin_simd_arg *args)
{
rtx pat;
- tree arg[SIMD_MAX_BUILTIN_ARGS];
- rtx op[SIMD_MAX_BUILTIN_ARGS];
+ tree arg[SIMD_MAX_BUILTIN_ARGS + 1];
+ rtx op[SIMD_MAX_BUILTIN_ARGS + 1]; /* First element for result operand. */
enum machine_mode tmode = insn_data[icode].operand[0].mode;
enum machine_mode mode[SIMD_MAX_BUILTIN_ARGS];
int argc = 0;
aarch64_simd_builtin_datum *d =
&aarch64_simd_builtin_data[fcode - (AARCH64_SIMD_BUILTIN_BASE + 1)];
enum insn_code icode = d->code;
- builtin_simd_arg args[SIMD_MAX_BUILTIN_ARGS];
+ builtin_simd_arg args[SIMD_MAX_BUILTIN_ARGS + 1];
int num_args = insn_data[d->code].n_operands;
int is_void = 0;
int k;
#define GLIBC_DYNAMIC_LINKER "/lib/ld-linux-aarch64%{mbig-endian:_be}%{mabi=ilp32:_ilp32}.so.1"
+#undef ASAN_CC1_SPEC
+#define ASAN_CC1_SPEC "%{%:sanitize(address):-funwind-tables}"
+
+#undef CC1_SPEC
+#define CC1_SPEC GNU_USER_TARGET_CC1_SPEC ASAN_CC1_SPEC
+
#define CPP_SPEC "%{pthread:-D_REENTRANT}"
#define LINUX_TARGET_LINK_SPEC "%{h*} \
return false;
}
+/* Implement the TARGET_ASAN_SHADOW_OFFSET hook. */
+
+static unsigned HOST_WIDE_INT
+aarch64_asan_shadow_offset (void)
+{
+ return (HOST_WIDE_INT_1 << 36);
+}
+
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST aarch64_address_cost
#undef TARGET_SCHED_MACRO_FUSION_PAIR_P
#define TARGET_SCHED_MACRO_FUSION_PAIR_P aarch_macro_fusion_pair_p
+#undef TARGET_ASAN_SHADOW_OFFSET
+#define TARGET_ASAN_SHADOW_OFFSET aarch64_asan_shadow_offset
+
struct gcc_target targetm = TARGET_INITIALIZER;
#include "gt-aarch64.h"
&& (DECL) && DECL_SIZE (DECL)) \
{ \
size_directive_output = 1; \
- size = int_size_in_bytes (TREE_TYPE (DECL)); \
+ size = tree_to_uhwi (DECL_SIZE_UNIT (DECL)); \
ASM_OUTPUT_SIZE_DIRECTIVE (FILE, NAME, size); \
} \
\
&& !size_directive_output) \
{ \
size_directive_output = 1; \
- size = int_size_in_bytes (TREE_TYPE (DECL)); \
+ size = tree_to_uhwi (DECL_SIZE_UNIT (DECL)); \
ASM_OUTPUT_SIZE_DIRECTIVE (FILE, name, size); \
} \
} \
#include "diagnostic-core.h"
#include "target.h"
#include "langhooks.h"
+#include "ubsan.h"
/* Convert EXPR to some pointer or reference type TYPE.
EXPR must be pointer, reference, integer, enumeral, or literal zero;
tree intype = TREE_TYPE (expr);
unsigned int inprec = element_precision (intype);
unsigned int outprec = element_precision (type);
+ location_t loc = EXPR_LOCATION (expr);
/* An INTEGER_TYPE cannot be incomplete, but an ENUMERAL_TYPE can
be. Consider `enum E = { a, b = (enum E) 3 };'. */
return build1 (CONVERT_EXPR, type, expr);
case REAL_TYPE:
- return build1 (FIX_TRUNC_EXPR, type, expr);
+ if (flag_sanitize & SANITIZE_FLOAT_CAST
+ && current_function_decl != NULL_TREE
+ && !lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES (current_function_decl)))
+ {
+ expr = save_expr (expr);
+ tree check = ubsan_instrument_float_cast (loc, type, expr);
+ expr = build1 (FIX_TRUNC_EXPR, type, expr);
+ if (check == NULL_TREE)
+ return expr;
+ return fold_build2 (COMPOUND_EXPR, TREE_TYPE (expr), check, expr);
+ }
+ else
+ return build1 (FIX_TRUNC_EXPR, type, expr);
case FIXED_POINT_TYPE:
return build1 (FIXED_CONVERT_EXPR, type, expr);
+2015-11-25 Jason Merrill <jason@redhat.com>
+
+ * lambda.c (maybe_add_lambda_conv_op): Only set
+ no_sanitize_undefined if SANITIZE_NULL.
+
+2015-11-25 Jason Merrill <jason@redhat.com>
+
+ PR c++/67941
+ * lambda.c (maybe_add_lambda_conv_op): Mark _FUN as
+ no_sanitize_undefined.
+
+2015-07-31 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/66977
+ * typeck.c (get_member_function_from_ptrfunc): Don't sanitize
+ RSHIFT_EXPR.
+
+2015-02-12 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64984
+ * except.c (check_noexcept_r): Return NULL for internal
+ calls.
+
+2014-12-02 Marek Polacek <polacek@redhat.com>
+
+ * constexpr.c (cxx_eval_check_shift_p): New function.
+ (cxx_eval_binary_expression): Call it. Set NON_CONSTANT_P if it
+ returns true.
+
+2014-12-01 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63956
+ * constexpr.c: Include ubsan.h.
+ (cxx_eval_call_expression): Bail out for IFN_UBSAN_{NULL,BOUNDS}
+ internal functions and for ubsan builtins.
+ * error.c: Include internal-fn.h.
+ (dump_expr): Add printing of internal functions.
+
+2014-11-28 Marek Polacek <polacek@redhat.com>
+
+ PR c/63862
+ * typeck.c (cp_build_binary_op) <RSHIFT_EXPR, LSHIFT_EXPR>: Don't
+ convert the right operand to integer type.
+
+2014-09-24 Marek Polacek <polacek@redhat.com>
+
+ PR c/61405
+ PR c/53874
+ * semantics.c (finish_switch_cond): Call unlowered_expr_type.
+ * tree.c (bot_manip): Add default case.
+ * parser.c (cp_parser_primary_expression): Cast the controlling
+ expression of a switch to an int.
+ (cp_parser_unqualified_id): Likewise.
+
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * cp-gimplify.c (cp_genericize_r): For -fsanitize=null and/or
+ -fsanitize=alignment call ubsan_maybe_instrument_reference
+ for casts to REFERENCE_TYPE and ubsan_maybe_instrument_member_call
+ for calls to member functions.
+
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * cp-gimplify.c (cp_genericize_r): For -fsanitize=null and/or
+ -fsanitize=alignment call ubsan_maybe_instrument_reference
+ for casts to REFERENCE_TYPE and ubsan_maybe_instrument_member_call
+ for calls to member functions.
+
+2014-06-30 Marek Polacek <polacek@redhat.com>
+
+ * cp-gimplify.c (cp_genericize): Don't instrument returns if the
+ function has no_sanitize_undefined attribute.
+ * decl.c (compute_array_index_type): Don't instrument VLAs if the
+ function has no_sanitize_undefined attribute.
+
+2014-04-30 Marek Polacek <polacek@redhat.com>
+
+ * typeck.c (cp_build_binary_op): Call ubsan_instrument_division
+ even when SANITIZE_FLOAT_DIVIDE is on. Set doing_div_or_mod even
+ for non-integer types.
+
2015-08-17 Jason Merrill <jason@redhat.com>
PR c++/66957
*stmt_p = size_one_node;
return NULL;
}
+ else if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
+ {
+ if (TREE_CODE (stmt) == NOP_EXPR
+ && TREE_CODE (TREE_TYPE (stmt)) == REFERENCE_TYPE)
+ ubsan_maybe_instrument_reference (stmt);
+ else if (TREE_CODE (stmt) == CALL_EXPR)
+ {
+ tree fn = CALL_EXPR_FN (stmt);
+ if (fn != NULL_TREE
+ && !error_operand_p (fn)
+ && POINTER_TYPE_P (TREE_TYPE (fn))
+ && TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) == METHOD_TYPE)
+ {
+ bool is_ctor
+ = TREE_CODE (fn) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
+ && DECL_CONSTRUCTOR_P (TREE_OPERAND (fn, 0));
+ ubsan_maybe_instrument_member_call (stmt, is_ctor);
+ }
+ }
+ }
pointer_set_insert (p_set, *stmt_p);
/* If a function that should end with a return in non-void
function doesn't obviously end with return, add ubsan
- instrmentation code to verify it at runtime. */
+ instrumentation code to verify it at runtime. */
static void
cp_ubsan_maybe_instrument_return (tree fndecl)
walk_tree's hash functionality. */
cp_genericize_tree (&DECL_SAVED_TREE (fndecl));
- if (flag_sanitize & SANITIZE_RETURN)
+ if (flag_sanitize & SANITIZE_RETURN
+ && current_function_decl != NULL_TREE
+ && !lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES (current_function_decl)))
cp_ubsan_maybe_instrument_return (fndecl);
/* Do everything else. */
throw_bad_array_length (), void_zero_node);
finish_expr_stmt (comp);
}
- else if (flag_sanitize & SANITIZE_VLA)
+ else if (flag_sanitize & SANITIZE_VLA
+ && current_function_decl != NULL_TREE
+ && !lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES
+ (current_function_decl)))
{
/* From C++1y onwards, we throw an exception on a negative
length size of an array; see above. */
#include "pointer-set.h"
#include "c-family/c-objc.h"
#include "ubsan.h"
+#include "internal-fn.h"
#include <new> // For placement-new.
tree fn = CALL_EXPR_FN (t);
bool skipfirst = false;
+ /* Deal with internal functions. */
+ if (fn == NULL_TREE)
+ {
+ pp_string (pp, internal_fn_name (CALL_EXPR_IFN (t)));
+ dump_call_expr_args (pp, t, flags, skipfirst);
+ break;
+ }
+
if (TREE_CODE (fn) == ADDR_EXPR)
fn = TREE_OPERAND (fn, 0);
{
tree t = *tp;
enum tree_code code = TREE_CODE (t);
- if (code == CALL_EXPR
+ if ((code == CALL_EXPR && CALL_EXPR_FN (t))
|| code == AGGR_INIT_EXPR)
{
/* We can only use the exception specification of the called function
if (generic_lambda_p)
fn = add_inherited_template_parms (fn, DECL_TI_TEMPLATE (callop));
+ if (flag_sanitize & SANITIZE_NULL)
+ {
+ /* 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_method (type, fn, NULL_TREE);
if (nested)
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
- switch (token->type)
+ switch ((int) token->type)
{
/* literal:
integer-literal
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
- switch (token->type)
+ switch ((int) token->type)
{
case CPP_NAME:
{
#include "gimplify.h"
#include "bitmap.h"
#include "omp-low.h"
+#include "ubsan.h"
static bool verify_constant (tree, bool, bool *, bool *);
#define VERIFY_CONSTANT(X) \
error ("switch quantity not an integer");
cond = error_mark_node;
}
- orig_type = TREE_TYPE (cond);
+ /* We want unlowered type here to handle enum bit-fields. */
+ orig_type = unlowered_expr_type (cond);
if (cond != error_mark_node)
{
/* [stmt.switch]
constexpr_call *entry;
bool depth_ok;
+ if (fun == NULL_TREE)
+ switch (CALL_EXPR_IFN (t))
+ {
+ case IFN_UBSAN_NULL:
+ case IFN_UBSAN_BOUNDS:
+ return void_node;
+ default:
+ error_at (loc, "call to internal function");
+ *non_constant_p = true;
+ return t;
+ }
+
if (TREE_CODE (fun) != FUNCTION_DECL)
{
/* Might be a constexpr function pointer. */
}
if (DECL_CLONED_FUNCTION_P (fun))
fun = DECL_CLONED_FUNCTION (fun);
+
+ if (is_ubsan_builtin_p (fun))
+ return void_node;
+
if (is_builtin_fn (fun))
return cxx_eval_builtin_function_call (old_call, t, allow_non_constant,
addr, non_constant_p, overflow_p);
return r;
}
+/* Check whether the shift operation with code CODE and type TYPE on LHS
+ and RHS is undefined. If it is, give an error with an explanation,
+ and return true; return false otherwise. */
+
+static bool
+cxx_eval_check_shift_p (location_t loc, enum tree_code code, tree type,
+ tree lhs, tree rhs)
+{
+ if ((code != LSHIFT_EXPR && code != RSHIFT_EXPR)
+ || TREE_CODE (lhs) != INTEGER_CST
+ || TREE_CODE (rhs) != INTEGER_CST)
+ return false;
+
+ tree lhstype = TREE_TYPE (lhs);
+ unsigned HOST_WIDE_INT uprec = TYPE_PRECISION (TREE_TYPE (lhs));
+
+ /* [expr.shift] The behavior is undefined if the right operand
+ is negative, or greater than or equal to the length in bits
+ of the promoted left operand. */
+ if (tree_int_cst_sgn (rhs) == -1)
+ {
+ error_at (loc, "right operand of shift expression %q+E is negative",
+ build2_loc (loc, code, type, lhs, rhs));
+ return true;
+ }
+
+ if (compare_tree_int (rhs, uprec) >= 0)
+ {
+ error_at (loc, "right operand of shift expression %q+E is >= than "
+ "the precision of the left operand",
+ build2_loc (loc, code, type, lhs, rhs));
+ return true;
+ }
+
+ /* The value of E1 << E2 is E1 left-shifted E2 bit positions; [...]
+ if E1 has a signed type and non-negative value, and E1x2^E2 is
+ representable in the corresponding unsigned type of the result type,
+ then that value, converted to the result type, is the resulting value;
+ otherwise, the behavior is undefined. */
+ if (code == LSHIFT_EXPR && !TYPE_UNSIGNED (lhstype)
+ && (cxx_dialect >= cxx11))
+ {
+ if (tree_int_cst_sgn (lhs) == -1)
+ {
+ error_at (loc, "left operand of shift expression %q+E is negative",
+ build2_loc (loc, code, type, lhs, rhs));
+ return true;
+ }
+ /* For signed x << y the following:
+ (unsigned) x >> ((prec (lhs) - 1) - y)
+ if > 1, is undefined. The right-hand side of this formula
+ is the highest bit of the LHS that can be set (starting from 0),
+ so that the shift doesn't overflow. We then right-shift the LHS
+ to see whether any other bit is set making the original shift
+ undefined -- the result is not representable in the corresponding
+ unsigned type. */
+ tree t = build_int_cst (unsigned_type_node, uprec - 1);
+ t = fold_build2 (MINUS_EXPR, unsigned_type_node, t, rhs);
+ tree ulhs = fold_convert (unsigned_type_for (lhstype), lhs);
+ t = fold_build2 (RSHIFT_EXPR, TREE_TYPE (ulhs), ulhs, t);
+ if (tree_int_cst_lt (integer_one_node, t))
+ {
+ error_at (loc, "shift expression %q+E overflows",
+ build2_loc (loc, code, type, lhs, rhs));
+ return true;
+ }
+ }
+ return false;
+}
+
/* Subroutine of cxx_eval_constant_expression.
Like cxx_eval_unary_expression, except for binary expressions. */
allow_non_constant, addr,
non_constant_p, overflow_p);
VERIFY_CONSTANT (rhs);
- if (lhs == orig_lhs && rhs == orig_rhs)
- return t;
- r = fold_build2 (TREE_CODE (t), TREE_TYPE (t), lhs, rhs);
+ location_t loc = EXPR_LOCATION (t);
+ enum tree_code code = TREE_CODE (t);
+ tree type = TREE_TYPE (t);
+ r = fold_binary_loc (loc, code, type, lhs, rhs);
+ if (r == NULL_TREE)
+ {
+ if (lhs == orig_lhs && rhs == orig_rhs)
+ r = t;
+ else
+ r = build2_loc (loc, code, type, lhs, rhs);
+ }
+ else if (cxx_eval_check_shift_p (loc, code, type, lhs, rhs))
+ *non_constant_p = true;
VERIFY_CONSTANT (r);
return r;
}
case BUILT_IN_FILE:
case BUILT_IN_LINE:
SET_EXPR_LOCATION (*tp, input_location);
+ default:
+ break;
}
}
return t;
idx = build1 (NOP_EXPR, vtable_index_type, e3);
switch (TARGET_PTRMEMFUNC_VBIT_LOCATION)
{
+ int flag_sanitize_save;
case ptrmemfunc_vbit_in_pfn:
e1 = cp_build_binary_op (input_location,
BIT_AND_EXPR, idx, integer_one_node,
e1 = cp_build_binary_op (input_location,
BIT_AND_EXPR, delta, integer_one_node,
complain);
+ /* Don't instrument the RSHIFT_EXPR we're about to create because
+ we're going to use DELTA number of times, and that wouldn't play
+ well with SAVE_EXPRs therein. */
+ flag_sanitize_save = flag_sanitize;
+ flag_sanitize = 0;
delta = cp_build_binary_op (input_location,
RSHIFT_EXPR, delta, integer_one_node,
complain);
+ flag_sanitize = flag_sanitize_save;
if (delta == error_mark_node)
return error_mark_node;
break;
enum tree_code tcode0 = code0, tcode1 = code1;
tree cop1 = fold_non_dependent_expr_sfinae (op1, tf_none);
cop1 = maybe_constant_value (cop1);
-
- if (tcode0 == INTEGER_TYPE)
- doing_div_or_mod = true;
-
+ doing_div_or_mod = true;
warn_for_div_by_zero (location, cop1);
if (tcode0 == COMPLEX_TYPE || tcode0 == VECTOR_TYPE)
{
tree cop1 = fold_non_dependent_expr_sfinae (op1, tf_none);
cop1 = maybe_constant_value (cop1);
-
- if (code0 == INTEGER_TYPE)
- doing_div_or_mod = true;
+ doing_div_or_mod = true;
warn_for_div_by_zero (location, cop1);
}
warning (0, "right shift count >= width of type");
}
}
- /* Convert the shift-count to an integer, regardless of
- size of value being shifted. */
- if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
- op1 = cp_convert (integer_type_node, op1, complain);
/* Avoid converting op1 to result_type later. */
converted = 1;
}
warning (0, "left shift count >= width of type");
}
}
- /* Convert the shift-count to an integer, regardless of
- size of value being shifted. */
- if (TYPE_MAIN_VARIANT (TREE_TYPE (op1)) != integer_type_node)
- op1 = cp_convert (integer_type_node, op1, complain);
/* Avoid converting op1 to result_type later. */
converted = 1;
}
if (build_type == NULL_TREE)
build_type = result_type;
- if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE))
+ if ((flag_sanitize & (SANITIZE_SHIFT | SANITIZE_DIVIDE
+ | SANITIZE_FLOAT_DIVIDE))
&& !processing_template_decl
&& current_function_decl != 0
&& !lookup_attribute ("no_sanitize_undefined",
tf_none));
op1 = maybe_constant_value (fold_non_dependent_expr_sfinae (op1,
tf_none));
- if (doing_div_or_mod && (flag_sanitize & SANITIZE_DIVIDE))
+ if (doing_div_or_mod && (flag_sanitize & (SANITIZE_DIVIDE
+ | SANITIZE_FLOAT_DIVIDE)))
{
/* For diagnostics we want to use the promoted types without
shorten_binary_op. So convert the arguments to the
@item Debugging Options
@xref{Debugging Options,,Options for Debugging Your Program or GCC}.
@gccoptlist{-d@var{letters} -dumpspecs -dumpmachine -dumpversion @gol
--fsanitize=@var{style} @gol
+-fsanitize=@var{style} -fsanitize-recover -fsanitize-recover=@var{style} @gol
+-fasan-shadow-offset=@var{number} -fsanitize-sections=@var{s1,s2,...} @gol
+-fsanitize-undefined-trap-on-error @gol
-fdbg-cnt-list -fdbg-cnt=@var{counter-value-list} @gol
-fdisable-ipa-@var{pass_name} @gol
-fdisable-rtl-@var{pass_name} @gol
-fmem-report -fpre-ipa-mem-report -fpost-ipa-mem-report -fprofile-arcs @gol
-fopt-info @gol
-fopt-info-@var{options}@r{[}=@var{file}@r{]} @gol
--frandom-seed=@var{string} -fsched-verbose=@var{n} @gol
+-frandom-seed=@var{number} -fsched-verbose=@var{n} @gol
-fsel-sched-verbose -fsel-sched-dump-cfg -fsel-sched-pipelining-verbose @gol
-fstack-usage -ftest-coverage -ftime-report -fvar-tracking @gol
-fvar-tracking-assignments -fvar-tracking-assignments-toggle @gol
@item -fsanitize=shift
@opindex fsanitize=shift
-
This option enables checking that the result of a shift operation is
not undefined. Note that what exactly is considered undefined differs
slightly between C and C++, as well as between ISO C90 and C99, etc.
@item -fsanitize=integer-divide-by-zero
@opindex fsanitize=integer-divide-by-zero
-
Detect integer division by zero as well as @code{INT_MIN / -1} division.
@item -fsanitize=unreachable
@opindex fsanitize=unreachable
-
With this option, the compiler will turn the @code{__builtin_unreachable}
call into a diagnostics message call instead. When reaching the
@code{__builtin_unreachable} call, the behavior is undefined.
@item -fsanitize=vla-bound
@opindex fsanitize=vla-bound
-
This option instructs the compiler to check that the size of a variable
length array is positive. This option does not have any effect in
@option{-std=c++1y} mode, as the standard requires the exception be thrown
@item -fsanitize=null
@opindex fsanitize=null
-
This option enables pointer checking. Particularly, the application
built with this option turned on will issue an error message when it
tries to dereference a NULL pointer, or if a reference (possibly an
-rvalue reference) is bound to a NULL pointer.
+rvalue reference) is bound to a NULL pointer, or if a method is invoked
+on an object pointed by a NULL pointer.
@item -fsanitize=return
@opindex fsanitize=return
-
This option enables return statement checking. Programs
built with this option turned on will issue an error message
when the end of a non-void function is reached without actually
@item -fsanitize=signed-integer-overflow
@opindex fsanitize=signed-integer-overflow
-
This option enables signed integer overflow checking. We check that
the result of @code{+}, @code{*}, and both unary and binary @code{-}
does not overflow in the signed arithmetics. Note, integer promotion
a++;
@end smallexample
+@item -fsanitize=bounds
+@opindex fsanitize=bounds
+This option enables instrumentation of array bounds. Various out of bounds
+accesses are detected. Flexible array members, flexible array member-like
+arrays, and initializers of variables with static storage are not instrumented.
+
+@item -fsanitize=bounds-strict
+@opindex fsanitize=bounds-strict
+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.
+
+@item -fsanitize=alignment
+@opindex fsanitize=alignment
+
+This option enables checking of alignment of pointers when they are
+dereferenced, or when a reference is bound to insufficiently aligned target,
+or when a method or constructor is invoked on insufficiently aligned object.
+
+@item -fsanitize=object-size
+@opindex fsanitize=object-size
+This option enables instrumentation of memory references using the
+@code{__builtin_object_size} function. Various out of bounds pointer
+accesses are detected.
+
+@item -fsanitize=float-divide-by-zero
+@opindex fsanitize=float-divide-by-zero
+Detect floating-point division by zero. Unlike other similar options,
+@option{-fsanitize=float-divide-by-zero} is not enabled by
+@option{-fsanitize=undefined}, since floating-point division by zero can
+be a legitimate way of obtaining infinities and NaNs.
+
+@item -fsanitize=float-cast-overflow
+@opindex fsanitize=float-cast-overflow
+This option enables floating-point type to integer conversion checking.
+We check that the result of the conversion does not overflow.
+Unlike other similar options, @option{-fsanitize=float-cast-overflow} is
+not enabled by @option{-fsanitize=undefined}.
+This option does not work well with @code{FE_INVALID} exceptions enabled.
+
+@item -fsanitize=nonnull-attribute
+@opindex fsanitize=nonnull-attribute
+
+This option enables instrumentation of calls, checking whether null values
+are not passed to arguments marked as requiring a non-null value by the
+@code{nonnull} function attribute.
+
+@item -fsanitize=returns-nonnull-attribute
+@opindex fsanitize=returns-nonnull-attribute
+
+This option enables instrumentation of return statements in functions
+marked with @code{returns_nonnull} function attribute, to detect returning
+of null values from such functions.
+
+@item -fsanitize=bool
+@opindex fsanitize=bool
+
+This option enables instrumentation of loads from bool. If a value other
+than 0/1 is loaded, a run-time error is issued.
+
+@item -fsanitize=enum
+@opindex fsanitize=enum
+
+This option enables instrumentation of loads from an enum type. If
+a value outside the range of values for the enum type is loaded,
+a run-time error is issued.
+
@end table
While @option{-ftrapv} causes traps for signed overflows to be emitted,
@option{-fsanitize=undefined} gives a diagnostic message.
This currently works only for the C family of languages.
+@item -fno-sanitize=all
+@opindex fno-sanitize=all
+
+This option disables all previously enabled sanitizers.
+@option{-fsanitize=all} is not allowed, as some sanitizers cannot be used
+together.
+
+@item -fasan-shadow-offset=@var{number}
+@opindex fasan-shadow-offset
+This option forces GCC to use custom shadow offset in AddressSanitizer checks.
+It is useful for experimenting with different shadow memory layouts in
+Kernel AddressSanitizer.
+
+@item -fsanitize-sections=@var{s1,s2,...}
+@opindex fsanitize-sections
+Sanitize global variables in selected user-defined sections. @var{si} may
+contain wildcards.
+
+@item -fsanitize-recover@r{[}=@var{opts}@r{]}
+@opindex fsanitize-recover
+@opindex fno-sanitize-recover
+@option{-fsanitize-recover=} controls error recovery mode for sanitizers
+mentioned in comma-separated list of @var{opts}. Enabling this option
+for a sanitizer component would cause it to attempt to continue
+running the program as if no error happened. This means multiple
+runtime errors can be reported in a single program run, and the exit
+code of the program may indicate success even when errors
+have been reported. The @option{-fno-sanitize-recover=} can be used to alter
+this behavior, only the first detected error will be reported
+and program will exit after that with non-zero exit code.
+
+Currently this feature only works for @option{-fsanitize=undefined} (and its suboptions
+except for @option{-fsanitize=unreachable} and @option{-fsanitize=return}),
+@option{-fsanitize=float-cast-overflow}, @option{-fsanitize=float-divide-by-zero},
+@option{-fsanitize=kernel-address} and @option{-fsanitize=address}.
+For these sanitizers error recovery is turned on by default, except @option{-fsanitize=address},
+for which this feature is experimental.
+@option{-fsanitize-recover=all} and @option{-fno-sanitize-recover=all} is also
+accepted, the former enables recovery for all sanitizers that support it,
+the latter disables recovery for all sanitizers that support it.
+
+Syntax without explicit @var{opts} parameter is deprecated. It is equivalent to
+@option{-fsanitize-recover=undefined,float-cast-overflow,float-divide-by-zero}.
+Similarly @option{-fno-sanitize-recover} is equivalent to
+@option{-fno-sanitize-recover=undefined,float-cast-overflow,float-divide-by-zero}.
+
+@item -fsanitize-undefined-trap-on-error
+@opindex fsanitize-undefined-trap-on-error
+The @option{-fsanitize-undefined-trap-on-error} instructs the compiler to
+report undefined behavior using @code{__builtin_trap ()} rather than
+a @code{libubsan} library routine. The advantage of this is that the
+@code{libubsan} library is not needed and will not be linked in, so this
+is usable even for use in freestanding environments.
+
@item -fdump-final-insns@r{[}=@var{file}@r{]}
@opindex fdump-final-insns
Dump the final internal representation (RTL) to @var{file}. If the
different compiler binaries and/or different
text / bss / data / heap / stack / dso start locations.
+@item -freport-bug
+@opindex freport-bug
+Collect and dump debug information into temporary file if ICE in C/C++
+compiler occured.
+
@item -fdump-unnumbered
@opindex fdump-unnumbered
When doing debugging dumps, suppress instruction numbers and address output.
ignored. Thus only the @file{vec.miss} is produced which contains
dumps from the vectorizer about missed opportunities.
-@item -frandom-seed=@var{string}
+@item -frandom-seed=@var{number}
@opindex frandom-seed
This option provides a seed that GCC uses in place of
random numbers in generating certain symbol names
produce them. You can use the @option{-frandom-seed} option to produce
reproducibly identical object files.
-The @var{string} should be different for every file you compile.
+The @var{number} should be different for every file you compile.
@item -fsched-verbose=@var{n}
@opindex fsched-verbose
SANITIZE_SI_OVERFLOW = 1 << 11,
SANITIZE_BOOL = 1 << 12,
SANITIZE_ENUM = 1 << 13,
+ SANITIZE_FLOAT_DIVIDE = 1 << 14,
+ SANITIZE_FLOAT_CAST = 1 << 15,
+ SANITIZE_BOUNDS = 1UL << 16,
+ SANITIZE_ALIGNMENT = 1UL << 17,
+ SANITIZE_NONNULL_ATTRIBUTE = 1UL << 18,
+ SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1UL << 19,
+ SANITIZE_OBJECT_SIZE = 1UL << 20,
+ SANITIZE_BOUNDS_STRICT = 1UL << 21,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
| SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
| SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
+ | SANITIZE_BOUNDS | SANITIZE_ALIGNMENT
+ | SANITIZE_NONNULL_ATTRIBUTE
+ | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
+ | SANITIZE_OBJECT_SIZE,
+ SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
+ | SANITIZE_BOUNDS_STRICT
};
/* flag_vtable_verify initialization levels. */
&& TYPE_OVERFLOW_WRAPS (type));
case FIXED_CST:
- case NEGATE_EXPR:
return true;
+ case NEGATE_EXPR:
+ return !TYPE_OVERFLOW_SANITIZED (type);
+
case REAL_CST:
/* We want to canonicalize to positive real constants. Pretend
that only negative ones can be easily negated. */
case INTEGER_CST:
tem = fold_negate_const (t, type);
if (TREE_OVERFLOW (tem) == TREE_OVERFLOW (t)
- || !TYPE_OVERFLOW_TRAPS (type))
+ || (!TYPE_OVERFLOW_TRAPS (type)
+ && TYPE_OVERFLOW_WRAPS (type))
+ || (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0)
return tem;
break;
break;
case NEGATE_EXPR:
- return TREE_OPERAND (t, 0);
+ if (!TYPE_OVERFLOW_SANITIZED (type))
+ return TREE_OPERAND (t, 0);
+ break;
case PLUS_EXPR:
if (!HONOR_SIGN_DEPENDENT_ROUNDING (TYPE_MODE (type))
case GE_EXPR:
case GT_EXPR:
if (TYPE_UNSIGNED (TREE_TYPE (arg1)))
- arg1 = fold_convert_loc (loc, signed_type_for
- (TREE_TYPE (arg1)), arg1);
+ break;
tem = fold_build1_loc (loc, ABS_EXPR, TREE_TYPE (arg1), arg1);
return pedantic_non_lvalue_loc (loc, fold_convert_loc (loc, type, tem));
case UNLE_EXPR:
case LE_EXPR:
case LT_EXPR:
if (TYPE_UNSIGNED (TREE_TYPE (arg1)))
- arg1 = fold_convert_loc (loc, signed_type_for
- (TREE_TYPE (arg1)), arg1);
+ break;
tem = fold_build1_loc (loc, ABS_EXPR, TREE_TYPE (arg1), arg1);
return negate_expr (fold_convert_loc (loc, type, tem));
default:
}
}
/* A - (-B) -> A + B */
- if (TREE_CODE (arg1) == NEGATE_EXPR)
+ if (TREE_CODE (arg1) == NEGATE_EXPR
+ && (!INTEGRAL_TYPE_P (type)
+ || TYPE_OVERFLOW_WRAPS (type)
+ || (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0))
return fold_build2_loc (loc, PLUS_EXPR, type, op0,
fold_convert_loc (loc, type,
TREE_OPERAND (arg1, 0)));
/* (-A) - B -> (-B) - A where B is easily negated and we can swap. */
if (TREE_CODE (arg0) == NEGATE_EXPR
&& negate_expr_p (arg1)
- && reorder_operands_p (arg0, arg1))
+ && reorder_operands_p (arg0, arg1)
+ && (!INTEGRAL_TYPE_P (type)
+ || TYPE_OVERFLOW_WRAPS (type)
+ || (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0))
return fold_build2_loc (loc, MINUS_EXPR, type,
fold_convert_loc (loc, type,
negate_expr (arg1)),
/* A - B -> A + (-B) if B is easily negatable. */
if (negate_expr_p (arg1)
+ && (!INTEGRAL_TYPE_P (type)
+ || TYPE_OVERFLOW_WRAPS (type)
+ || (flag_sanitize & SANITIZE_SI_OVERFLOW) == 0)
&& ((FLOAT_TYPE_P (type)
/* Avoid this transformation if B is a positive REAL_CST. */
&& (TREE_CODE (arg1) != REAL_CST
static const char *convert_filename (const char *, int, int);
#endif
+static void try_generate_repro (const char **argv);
static const char *getenv_spec_function (int, const char **);
static const char *if_exists_spec_function (int, const char **);
static const char *if_exists_else_spec_function (int, const char **);
%{!nostdlib:%{!nodefaultlibs:%{%:sanitize(address):" LIBASAN_SPEC "\
%{static:%ecannot specify -static with -fsanitize=address}}\
%{%:sanitize(thread):" LIBTSAN_SPEC "\
- %{!pie:%{!shared:%e-fsanitize=thread linking must be done with -pie or -shared}}}\
+ %{static:%ecannot specify -static with -fsanitize=thread}}\
%{%:sanitize(undefined):" LIBUBSAN_SPEC "}\
%{%:sanitize(leak):" LIBLSAN_SPEC "}}}"
#endif
}
}
- if (string != commands[i].prog)
+ if (i && string != commands[i].prog)
free (CONST_CAST (char *, string));
}
else if (WIFEXITED (status)
&& WEXITSTATUS (status) >= MIN_FATAL_STATUS)
{
+ /* For ICEs in cc1, cc1obj, cc1plus see if it is
+ reproducible or not. */
+ const char *p;
+ if (flag_report_bug
+ && WEXITSTATUS (status) == ICE_EXIT_CODE
+ && i == 0
+ && (p = strrchr (commands[0].argv[0], DIR_SEPARATOR))
+ && ! strncmp (p + 1, "cc1", 3))
+ try_generate_repro (commands[0].argv);
if (WEXITSTATUS (status) > greatest_status)
greatest_status = WEXITSTATUS (status);
ret_code = -1;
}
}
+ if (commands[0].argv[0] != commands[0].prog)
+ free (CONST_CAST (char *, commands[0].argv[0]));
+
return ret_code;
}
}
switches[switchnum].validated = true;
}
\f
+/* Print GCC configuration (e.g. version, thread model, target,
+ configuration_arguments) to a given FILE. */
+
+static void
+print_configuration (FILE *file)
+{
+ int n;
+ const char *thrmod;
+
+ fnotice (file, "Target: %s\n", spec_machine);
+ fnotice (file, "Configured with: %s\n", configuration_arguments);
+
+#ifdef THREAD_MODEL_SPEC
+ /* We could have defined THREAD_MODEL_SPEC to "%*" by default,
+ but there's no point in doing all this processing just to get
+ thread_model back. */
+ obstack_init (&obstack);
+ do_spec_1 (THREAD_MODEL_SPEC, 0, thread_model);
+ obstack_1grow (&obstack, '\0');
+ thrmod = XOBFINISH (&obstack, const char *);
+#else
+ thrmod = thread_model;
+#endif
+
+ fnotice (file, "Thread model: %s\n", thrmod);
+
+ /* compiler_version is truncated at the first space when initialized
+ from version string, so truncate version_string at the first space
+ before comparing. */
+ for (n = 0; version_string[n]; n++)
+ if (version_string[n] == ' ')
+ break;
+
+ if (! strncmp (version_string, compiler_version, n)
+ && compiler_version[n] == 0)
+ fnotice (file, "gcc version %s %s\n", version_string,
+ pkgversion_string);
+ else
+ fnotice (file, "gcc driver version %s %sexecuting gcc version %s\n",
+ version_string, pkgversion_string, compiler_version);
+
+}
+
+#define RETRY_ICE_ATTEMPTS 3
+
+/* Returns true if FILE1 and FILE2 contain equivalent data, 0 otherwise. */
+
+static bool
+files_equal_p (char *file1, char *file2)
+{
+ struct stat st1, st2;
+ off_t n, len;
+ int fd1, fd2;
+ const int bufsize = 8192;
+ char *buf = XNEWVEC (char, bufsize);
+
+ fd1 = open (file1, O_RDONLY);
+ fd2 = open (file2, O_RDONLY);
+
+ if (fd1 < 0 || fd2 < 0)
+ goto error;
+
+ if (fstat (fd1, &st1) < 0 || fstat (fd2, &st2) < 0)
+ goto error;
+
+ if (st1.st_size != st2.st_size)
+ goto error;
+
+ for (n = st1.st_size; n; n -= len)
+ {
+ len = n;
+ if ((int) len > bufsize / 2)
+ len = bufsize / 2;
+
+ if (read (fd1, buf, len) != (int) len
+ || read (fd2, buf + bufsize / 2, len) != (int) len)
+ {
+ goto error;
+ }
+
+ if (memcmp (buf, buf + bufsize / 2, len) != 0)
+ goto error;
+ }
+
+ free (buf);
+ close (fd1);
+ close (fd2);
+
+ return 1;
+
+error:
+ free (buf);
+ close (fd1);
+ close (fd2);
+ return 0;
+}
+
+/* Check that compiler's output doesn't differ across runs.
+ TEMP_STDOUT_FILES and TEMP_STDERR_FILES are arrays of files, containing
+ stdout and stderr for each compiler run. Return true if all of
+ TEMP_STDOUT_FILES and TEMP_STDERR_FILES are equivalent. */
+
+static bool
+check_repro (char **temp_stdout_files, char **temp_stderr_files)
+{
+ int i;
+ for (i = 0; i < RETRY_ICE_ATTEMPTS - 2; ++i)
+ {
+ if (!files_equal_p (temp_stdout_files[i], temp_stdout_files[i + 1])
+ || !files_equal_p (temp_stderr_files[i], temp_stderr_files[i + 1]))
+ {
+ fnotice (stderr, "The bug is not reproducible, so it is"
+ " likely a hardware or OS problem.\n");
+ break;
+ }
+ }
+ return i == RETRY_ICE_ATTEMPTS - 2;
+}
+
+enum attempt_status {
+ ATTEMPT_STATUS_FAIL_TO_RUN,
+ ATTEMPT_STATUS_SUCCESS,
+ ATTEMPT_STATUS_ICE
+};
+
+
+/* Run compiler with arguments NEW_ARGV to reproduce the ICE, storing stdout
+ to OUT_TEMP and stderr to ERR_TEMP. If APPEND is TRUE, append to OUT_TEMP
+ and ERR_TEMP instead of truncating. If EMIT_SYSTEM_INFO is TRUE, also write
+ GCC configuration into to ERR_TEMP. Return ATTEMPT_STATUS_FAIL_TO_RUN if
+ compiler failed to run, ATTEMPT_STATUS_ICE if compiled ICE-ed and
+ ATTEMPT_STATUS_SUCCESS otherwise. */
+
+static enum attempt_status
+run_attempt (const char **new_argv, const char *out_temp,
+ const char *err_temp, int emit_system_info, int append)
+{
+
+ if (emit_system_info)
+ {
+ FILE *file_out = fopen (err_temp, "a");
+ print_configuration (file_out);
+ fputs ("\n", file_out);
+ fclose (file_out);
+ }
+
+ int exit_status;
+ const char *errmsg;
+ struct pex_obj *pex;
+ int err;
+ int pex_flags = PEX_USE_PIPES | PEX_LAST;
+ enum attempt_status status = ATTEMPT_STATUS_FAIL_TO_RUN;
+
+ if (append)
+ pex_flags |= PEX_STDOUT_APPEND | PEX_STDERR_APPEND;
+
+ pex = pex_init (PEX_USE_PIPES, new_argv[0], NULL);
+ if (!pex)
+ fatal_error ("pex_init failed: %m");
+
+ errmsg = pex_run (pex, pex_flags, new_argv[0],
+ CONST_CAST2 (char *const *, const char **, &new_argv[1]), out_temp,
+ err_temp, &err);
+ if (errmsg != NULL)
+ {
+ if (err == 0)
+ fatal_error (errmsg);
+ else
+ {
+ errno = err;
+ pfatal_with_name (errmsg);
+ }
+ }
+
+ if (!pex_get_status (pex, 1, &exit_status))
+ goto out;
+
+ switch (WEXITSTATUS (exit_status))
+ {
+ case ICE_EXIT_CODE:
+ status = ATTEMPT_STATUS_ICE;
+ break;
+
+ case SUCCESS_EXIT_CODE:
+ status = ATTEMPT_STATUS_SUCCESS;
+ break;
+
+ default:
+ ;
+ }
+
+out:
+ pex_free (pex);
+ return status;
+}
+
+/* This routine reads lines from IN file, adds C++ style comments
+ at the begining of each line and writes result into OUT. */
+
+static void
+insert_comments (const char *file_in, const char *file_out)
+{
+ FILE *in = fopen (file_in, "rb");
+ FILE *out = fopen (file_out, "wb");
+ char line[256];
+
+ bool add_comment = true;
+ while (fgets (line, sizeof (line), in))
+ {
+ if (add_comment)
+ fputs ("// ", out);
+ fputs (line, out);
+ add_comment = strchr (line, '\n') != NULL;
+ }
+
+ fclose (in);
+ fclose (out);
+}
+
+/* This routine adds preprocessed source code into the given ERR_FILE.
+ To do this, it adds "-E" to NEW_ARGV and execute RUN_ATTEMPT routine to
+ add information in report file. RUN_ATTEMPT should return
+ ATTEMPT_STATUS_SUCCESS, in other case we cannot generate the report. */
+
+static void
+do_report_bug (const char **new_argv, const int nargs,
+ char **out_file, char **err_file)
+{
+ int i, status;
+ int fd = open (*out_file, O_RDWR | O_APPEND);
+ if (fd < 0)
+ return;
+ write (fd, "\n//", 3);
+ for (i = 0; i < nargs; i++)
+ {
+ write (fd, " ", 1);
+ write (fd, new_argv[i], strlen (new_argv[i]));
+ }
+ write (fd, "\n\n", 2);
+ close (fd);
+ new_argv[nargs] = "-E";
+ new_argv[nargs + 1] = NULL;
+
+ status = run_attempt (new_argv, *out_file, *err_file, 0, 1);
+
+ if (status == ATTEMPT_STATUS_SUCCESS)
+ {
+ fnotice (stderr, "Preprocessed source stored into %s file,"
+ " please attach this to your bugreport.\n", *out_file);
+ /* Make sure it is not deleted. */
+ free (*out_file);
+ *out_file = NULL;
+ }
+}
+
+/* Try to reproduce ICE. If bug is reproducible, generate report .err file
+ containing GCC configuration, backtrace, compiler's command line options
+ and preprocessed source code. */
+
+static void
+try_generate_repro (const char **argv)
+{
+ int i, nargs, out_arg = -1, quiet = 0, attempt;
+ const char **new_argv;
+ char *temp_files[RETRY_ICE_ATTEMPTS * 2];
+ char **temp_stdout_files = &temp_files[0];
+ char **temp_stderr_files = &temp_files[RETRY_ICE_ATTEMPTS];
+
+ if (gcc_input_filename == NULL || ! strcmp (gcc_input_filename, "-"))
+ return;
+
+ for (nargs = 0; argv[nargs] != NULL; ++nargs)
+ /* Only retry compiler ICEs, not preprocessor ones. */
+ if (! strcmp (argv[nargs], "-E"))
+ return;
+ else if (argv[nargs][0] == '-' && argv[nargs][1] == 'o')
+ {
+ if (out_arg == -1)
+ out_arg = nargs;
+ else
+ return;
+ }
+ /* If the compiler is going to output any time information,
+ it might varry between invocations. */
+ else if (! strcmp (argv[nargs], "-quiet"))
+ quiet = 1;
+ else if (! strcmp (argv[nargs], "-ftime-report"))
+ return;
+
+ if (out_arg == -1 || !quiet)
+ return;
+
+ memset (temp_files, '\0', sizeof (temp_files));
+ new_argv = XALLOCAVEC (const char *, nargs + 4);
+ memcpy (new_argv, argv, (nargs + 1) * sizeof (const char *));
+ new_argv[nargs++] = "-frandom-seed=0";
+ new_argv[nargs++] = "-fdump-noaddr";
+ new_argv[nargs] = NULL;
+ if (new_argv[out_arg][2] == '\0')
+ new_argv[out_arg + 1] = "-";
+ else
+ new_argv[out_arg] = "-o-";
+
+ int status;
+ for (attempt = 0; attempt < RETRY_ICE_ATTEMPTS; ++attempt)
+ {
+ int emit_system_info = 0;
+ int append = 0;
+ temp_stdout_files[attempt] = make_temp_file (".out");
+ temp_stderr_files[attempt] = make_temp_file (".err");
+
+ if (attempt == RETRY_ICE_ATTEMPTS - 1)
+ {
+ append = 1;
+ emit_system_info = 1;
+ }
+
+ status = run_attempt (new_argv, temp_stdout_files[attempt],
+ temp_stderr_files[attempt], emit_system_info,
+ append);
+
+ if (status != ATTEMPT_STATUS_ICE)
+ {
+ fnotice (stderr, "The bug is not reproducible, so it is"
+ " likely a hardware or OS problem.\n");
+ goto out;
+ }
+ }
+
+ if (!check_repro (temp_stdout_files, temp_stderr_files))
+ goto out;
+
+ {
+ /* Insert commented out backtrace into report file. */
+ char **stderr_commented = &temp_stdout_files[RETRY_ICE_ATTEMPTS - 1];
+ insert_comments (temp_stderr_files[RETRY_ICE_ATTEMPTS - 1],
+ *stderr_commented);
+
+ /* In final attempt we append compiler options and preprocesssed code to last
+ generated .out file with configuration and backtrace. */
+ char **output = &temp_stdout_files[RETRY_ICE_ATTEMPTS - 1];
+ do_report_bug (new_argv, nargs, stderr_commented, output);
+ }
+
+out:
+ for (i = 0; i < RETRY_ICE_ATTEMPTS * 2; i++)
+ if (temp_files[i])
+ {
+ unlink (temp_stdout_files[i]);
+ free (temp_stdout_files[i]);
+ }
+}
+
/* Search for a file named NAME trying various prefixes including the
user's -B prefix and some standard ones.
Return the absolute file name found. If nothing is found, return NAME. */
if (verbose_flag)
{
- int n;
- const char *thrmod;
-
- fnotice (stderr, "Target: %s\n", spec_machine);
- fnotice (stderr, "Configured with: %s\n", configuration_arguments);
-
-#ifdef THREAD_MODEL_SPEC
- /* We could have defined THREAD_MODEL_SPEC to "%*" by default,
- but there's no point in doing all this processing just to get
- thread_model back. */
- obstack_init (&obstack);
- do_spec_1 (THREAD_MODEL_SPEC, 0, thread_model);
- obstack_1grow (&obstack, '\0');
- thrmod = XOBFINISH (&obstack, const char *);
-#else
- thrmod = thread_model;
-#endif
-
- fnotice (stderr, "Thread model: %s\n", thrmod);
-
- /* compiler_version is truncated at the first space when initialized
- from version string, so truncate version_string at the first space
- before comparing. */
- for (n = 0; version_string[n]; n++)
- if (version_string[n] == ' ')
- break;
-
- if (! strncmp (version_string, compiler_version, n)
- && compiler_version[n] == 0)
- fnotice (stderr, "gcc version %s %s\n", version_string,
- pkgversion_string);
- else
- fnotice (stderr, "gcc driver version %s %sexecuting gcc version %s\n",
- version_string, pkgversion_string, compiler_version);
-
+ print_configuration (stderr);
if (n_infiles == 0)
return (0);
}
if (strcmp (argv[0], "thread") == 0)
return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
if (strcmp (argv[0], "undefined") == 0)
- return (flag_sanitize & SANITIZE_UNDEFINED) ? "" : NULL;
+ return ((flag_sanitize & (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT))
+ && !flag_sanitize_undefined_trap_on_error) ? "" : NULL;
if (strcmp (argv[0], "leak") == 0)
return ((flag_sanitize
& (SANITIZE_ADDRESS | SANITIZE_LEAK | SANITIZE_THREAD))
gimple_call_arg (stmt, 1),
gimple_call_arg (stmt, 2));
break;
+ case IFN_UBSAN_OBJECT_SIZE:
+ if (integer_all_onesp (gimple_call_arg (stmt, 2))
+ || (TREE_CODE (gimple_call_arg (stmt, 1)) == INTEGER_CST
+ && TREE_CODE (gimple_call_arg (stmt, 2)) == INTEGER_CST
+ && tree_int_cst_le (gimple_call_arg (stmt, 1),
+ gimple_call_arg (stmt, 2))))
+ {
+ gsi_replace (gsi, gimple_build_nop (), true);
+ unlink_stmt_vdef (stmt);
+ release_defs (stmt);
+ return true;
+ }
+ break;
case IFN_UBSAN_CHECK_ADD:
subcode = PLUS_EXPR;
break;
default:
return true;
}
+ else if (gimple_call_internal_p (call)
+ && gimple_call_flags (call) & ECF_LEAF)
+ return true;
return false;
}
if (! EXPR_HAS_LOCATION (*expr_p))
SET_EXPR_LOCATION (*expr_p, input_location);
+ /* Gimplify internal functions created in the FEs. */
+ if (CALL_EXPR_FN (*expr_p) == NULL_TREE)
+ {
+ nargs = call_expr_nargs (*expr_p);
+ enum internal_fn ifn = CALL_EXPR_IFN (*expr_p);
+ auto_vec<tree> vargs (nargs);
+
+ for (i = 0; i < nargs; i++)
+ {
+ gimplify_arg (&CALL_EXPR_ARG (*expr_p, i), pre_p,
+ EXPR_LOCATION (*expr_p));
+ vargs.quick_push (CALL_EXPR_ARG (*expr_p, i));
+ }
+ gimple call = gimple_build_call_internal_vec (ifn, vargs);
+ gimplify_seq_add_stmt (pre_p, call);
+ return GS_ALL_DONE;
+ }
+
/* This may be a call to a builtin function.
Builtin function calls may be transformed into different
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "internal-fn.h"
#include "tree.h"
+#include "internal-fn.h"
#include "stor-layout.h"
#include "expr.h"
#include "optabs.h"
gcc_unreachable ();
}
+/* This should get expanded in the sanopt pass. */
+
+static void
+expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED)
+{
+ gcc_unreachable ();
+}
+
+/* This should get expanded in the sanopt pass. */
+
+static void
+expand_UBSAN_OBJECT_SIZE (gimple stmt ATTRIBUTE_UNUSED)
+{
+ gcc_unreachable ();
+}
+
+
+/* Helper for expand_*_overflow. Store RES into TARGET. */
+
+static void
+expand_ubsan_result_store (rtx target, rtx res)
+{
+ if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target))
+ /* If this is a scalar in a register that is stored in a wider mode
+ than the declared mode, compute the result into its declared mode
+ and then convert to the wider mode. Our value is the computed
+ expression. */
+ convert_move (SUBREG_REG (target), res, SUBREG_PROMOTED_SIGN (target));
+ else
+ emit_move_insn (target, res);
+}
+
/* Add sub/add overflow checking to the statement STMT.
CODE says whether the operation is +, or -. */
emit_label (done_label);
if (lhs)
- emit_move_insn (target, res);
+ expand_ubsan_result_store (target, res);
}
/* Add negate overflow checking to the statement STMT. */
emit_label (done_label);
if (lhs)
- emit_move_insn (target, res);
+ expand_ubsan_result_store (target, res);
}
/* Add mul overflow checking to the statement STMT. */
rtx do_overflow = gen_label_rtx ();
rtx hipart_different = gen_label_rtx ();
- int hprec = GET_MODE_PRECISION (hmode);
+ unsigned int hprec = GET_MODE_PRECISION (hmode);
rtx hipart0 = expand_shift (RSHIFT_EXPR, mode, op0, hprec,
NULL_RTX, 0);
hipart0 = gen_lowpart (hmode, hipart0);
double_int arg0_min, arg0_max;
if (get_range_info (arg0, &arg0_min, &arg0_max) == VR_RANGE)
{
- if (arg0_max.sle (double_int::max_value (hprec, false))
- && double_int::min_value (hprec, false).sle (arg0_min))
+ unsigned int mprec0 = wi::min_precision (arg0_min, SIGNED);
+ unsigned int mprec1 = wi::min_precision (arg0_max, SIGNED);
+ if (mprec0 <= hprec && mprec1 <= hprec)
op0_small_p = true;
- else if (arg0_max.sle (double_int::max_value (hprec, true))
- && (~double_int::max_value (hprec,
- true)).sle (arg0_min))
+ else if (mprec0 <= hprec + 1 && mprec1 <= hprec + 1)
op0_medium_p = true;
if (!arg0_min.is_negative ())
op0_sign = 0;
double_int arg1_min, arg1_max;
if (get_range_info (arg1, &arg1_min, &arg1_max) == VR_RANGE)
{
- if (arg1_max.sle (double_int::max_value (hprec, false))
- && double_int::min_value (hprec, false).sle (arg1_min))
+ unsigned int mprec0 = wi::min_precision (arg1_min, SIGNED);
+ unsigned int mprec1 = wi::min_precision (arg1_max, SIGNED);
+ if (mprec0 <= hprec && mprec1 <= hprec)
op1_small_p = true;
- else if (arg1_max.sle (double_int::max_value (hprec, true))
- && (~double_int::max_value (hprec,
- true)).sle (arg1_min))
+ else if (mprec0 <= hprec + 1 && mprec1 <= hprec + 1)
op1_medium_p = true;
if (!arg1_min.is_negative ())
op1_sign = 0;
emit_label (done_label);
if (lhs)
- emit_move_insn (target, res);
+ expand_ubsan_result_store (target, res);
}
/* Expand UBSAN_CHECK_ADD call STMT. */
DEF_INTERNAL_FN (MASK_STORE, ECF_LEAF, NULL)
DEF_INTERNAL_FN (ANNOTATE, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW, ".W.")
+DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL)
DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL)
DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
-DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".W..")
+DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".W...")
#ifndef GCC_INTERNAL_FN_H
#define GCC_INTERNAL_FN_H
-enum internal_fn {
-#define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) IFN_##CODE,
-#include "internal-fn.def"
-#undef DEF_INTERNAL_FN
- IFN_LAST
-};
+#include "coretypes.h"
/* Initialize internal function tables. */
*looping = true;
*state = IPA_CONST;
return true;
+ default:
+ break;
}
return false;
}
{
const char *name;
enum gcc_plugin_symbol_kind kind;
- enum gcc_plugin_symbol_visibility visibility;
+ enum gcc_plugin_symbol_visibility visibility = GCCPV_DEFAULT;
unsigned slot_num;
unsigned HOST_WIDEST_INT size;
const char *comdat;
#include "toplev.h"
#include "tree-pass.h"
#include "context.h"
+#include "asan.h"
typedef const char *const_char_p; /* For DEF_VEC_P. */
stack_limit_rtx = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (opt->arg));
break;
+ case OPT_fasan_shadow_offset_:
+ if (!(flag_sanitize & SANITIZE_KERNEL_ADDRESS))
+ error ("-fasan-shadow-offset should only be used "
+ "with -fsanitize=kernel-address");
+ if (!set_asan_shadow_offset (opt->arg))
+ error ("unrecognized shadow offset %qs", opt->arg);
+ break;
+
+ case OPT_fsanitize_sections_:
+ set_sanitized_sections (opt->arg);
+ break;
+
default:
gcc_unreachable ();
}
if (opts->x_dwarf_split_debug_info)
opts->x_debug_generate_pub_sections = 1;
- /* Userspace and kernel ASan conflict with each other and with TSan. */
+ /* Userspace and kernel ASan conflict with each other. */
- if ((flag_sanitize & SANITIZE_USER_ADDRESS)
- && (flag_sanitize & SANITIZE_KERNEL_ADDRESS))
+ if ((opts->x_flag_sanitize & SANITIZE_USER_ADDRESS)
+ && (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS))
error_at (loc,
- "-fsanitize=address is incompatible with "
- "-fsanitize=kernel-address");
+ "-fsanitize=address is incompatible with "
+ "-fsanitize=kernel-address");
- if ((flag_sanitize & SANITIZE_ADDRESS)
- && (flag_sanitize & SANITIZE_THREAD))
+ /* And with TSan. */
+
+ if ((opts->x_flag_sanitize & SANITIZE_ADDRESS)
+ && (opts->x_flag_sanitize & SANITIZE_THREAD))
error_at (loc,
- "-fsanitize=address and -fsanitize=kernel-address "
- "are incompatible with -fsanitize=thread");
+ "-fsanitize=address and -fsanitize=kernel-address "
+ "are incompatible with -fsanitize=thread");
+
+ /* Error recovery is not allowed for LSan and TSan. */
+
+ if (opts->x_flag_sanitize_recover & SANITIZE_THREAD)
+ error_at (loc, "-fsanitize-recover=thread is not supported");
+
+ if (opts->x_flag_sanitize_recover & SANITIZE_LEAK)
+ error_at (loc, "-fsanitize-recover=leak is not supported");
+
+ /* When instrumenting the pointers, we don't want to remove
+ the null pointer checks. */
+ if (opts->x_flag_sanitize & (SANITIZE_NULL | SANITIZE_NONNULL_ATTRIBUTE
+ | SANITIZE_RETURNS_NONNULL_ATTRIBUTE))
+ opts->x_flag_delete_null_pointer_checks = 0;
+
+ /* Aggressive compiler optimizations may cause false negatives. */
+ if (opts->x_flag_sanitize)
+ {
+ opts->x_flag_aggressive_loop_optimizations = 0;
+ opts->x_flag_strict_overflow = 0;
+ }
}
#define LEFT_COLUMN 27
break;
case OPT_fsanitize_:
+ case OPT_fsanitize_recover_:
{
const char *p = arg;
+ unsigned int *flag
+ = code == OPT_fsanitize_ ? &opts->x_flag_sanitize
+ : &opts->x_flag_sanitize_recover;
while (*p != 0)
{
static const struct
sizeof "signed-integer-overflow" -1 },
{ "bool", SANITIZE_BOOL, sizeof "bool" - 1 },
{ "enum", SANITIZE_ENUM, sizeof "enum" - 1 },
+ { "float-divide-by-zero", SANITIZE_FLOAT_DIVIDE,
+ sizeof "float-divide-by-zero" - 1 },
+ { "float-cast-overflow", SANITIZE_FLOAT_CAST,
+ sizeof "float-cast-overflow" - 1 },
+ { "bounds", SANITIZE_BOUNDS, sizeof "bounds" - 1 },
+ { "bounds-strict", SANITIZE_BOUNDS | SANITIZE_BOUNDS_STRICT,
+ sizeof "bounds-strict" - 1 },
+ { "alignment", SANITIZE_ALIGNMENT, sizeof "alignment" - 1 },
+ { "nonnull-attribute", SANITIZE_NONNULL_ATTRIBUTE,
+ sizeof "nonnull-attribute" - 1 },
+ { "returns-nonnull-attribute",
+ SANITIZE_RETURNS_NONNULL_ATTRIBUTE,
+ sizeof "returns-nonnull-attribute" - 1 },
+ { "object-size", SANITIZE_OBJECT_SIZE,
+ sizeof "object-size" - 1 },
+ { "all", ~0, sizeof "all" - 1 },
{ NULL, 0, 0 }
};
const char *comma;
&& memcmp (p, spec[i].name, len) == 0)
{
/* Handle both -fsanitize and -fno-sanitize cases. */
- if (value)
- flag_sanitize |= spec[i].flag;
+ if (value && spec[i].flag == ~0U)
+ {
+ if (code == OPT_fsanitize_)
+ error_at (loc, "-fsanitize=all option is not valid");
+ else
+ *flag |= ~(SANITIZE_USER_ADDRESS | SANITIZE_THREAD
+ | SANITIZE_LEAK);
+ }
+ else if (value)
+ *flag |= spec[i].flag;
else
- flag_sanitize &= ~spec[i].flag;
+ *flag &= ~spec[i].flag;
found = true;
break;
}
if (! found)
error_at (loc,
- "unrecognized argument to -fsanitize= option: %q.*s",
- (int) len, p);
+ "unrecognized argument to -fsanitize%s= option: %q.*s",
+ code == OPT_fsanitize_ ? "" : "-recover", (int) len, p);
if (comma == NULL)
break;
p = comma + 1;
}
- /* When instrumenting the pointers, we don't want to remove
- the null pointer checks. */
- if (flag_sanitize & SANITIZE_NULL)
- opts->x_flag_delete_null_pointer_checks = 0;
+ if (code != OPT_fsanitize_)
+ break;
/* Kernel ASan implies normal ASan but does not yet support
all features. */
- if (flag_sanitize & SANITIZE_KERNEL_ADDRESS)
+ if (opts->x_flag_sanitize & SANITIZE_KERNEL_ADDRESS)
{
maybe_set_param_value (PARAM_ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD, 0,
opts->x_param_values,
break;
}
+ case OPT_fasan_shadow_offset_:
+ /* Deferred. */
+ break;
+
+ case OPT_fsanitize_recover:
+ if (value)
+ opts->x_flag_sanitize_recover
+ |= SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT;
+ else
+ opts->x_flag_sanitize_recover
+ &= ~(SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT);
+ break;
+
case OPT_O:
case OPT_Os:
case OPT_Ofast:
"asan-instrumentation-with-call-threshold",
"Use callbacks instead of inline code if number of accesses "
"in function becomes greater or equal to this number",
- INT_MAX, 0, INT_MAX)
+ 7000, 0, INT_MAX)
DEFPARAM (PARAM_UNINIT_CONTROL_DEP_ATTEMPTS,
"uninit-control-dep-attempts",
if (predictor)
*predictor = PRED_COMPARE_AND_SWAP;
return boolean_true_node;
+ default:
+ break;
}
}
for other FEs by asan.c. */
/* Address Sanitizer */
-DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init_v3",
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init",
+ BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_VERSION_MISMATCH_CHECK,
+ "__asan_version_mismatch_check_v6",
BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
/* Do not reorder the BUILT_IN_ASAN_{REPORT,CHECK}* builtins, e.g. cfgcleanup.c
relies on this order. */
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE_N, "__asan_report_store_n",
BT_FN_VOID_PTR_PTRMODE,
ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD1_NOABORT,
+ "__asan_report_load1_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD2_NOABORT,
+ "__asan_report_load2_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD4_NOABORT,
+ "__asan_report_load4_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD8_NOABORT,
+ "__asan_report_load8_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD16_NOABORT,
+ "__asan_report_load16_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_LOAD_N_NOABORT,
+ "__asan_report_load_n_noabort",
+ BT_FN_VOID_PTR_PTRMODE,
+ ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE1_NOABORT,
+ "__asan_report_store1_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE2_NOABORT,
+ "__asan_report_store2_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE4_NOABORT,
+ "__asan_report_store4_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE8_NOABORT,
+ "__asan_report_store8_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE16_NOABORT,
+ "__asan_report_store16_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REPORT_STORE_N_NOABORT,
+ "__asan_report_store_n_noabort",
+ BT_FN_VOID_PTR_PTRMODE,
+ ATTR_TMPURE_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD1, "__asan_load1",
BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD2, "__asan_load2",
BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STOREN, "__asan_storeN",
BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD1_NOABORT, "__asan_load1_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD2_NOABORT, "__asan_load2_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD4_NOABORT, "__asan_load4_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD8_NOABORT, "__asan_load8_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOAD16_NOABORT, "__asan_load16_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_LOADN_NOABORT, "__asan_loadN_noabort",
+ BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STORE1_NOABORT, "__asan_store1_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STORE2_NOABORT, "__asan_store2_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STORE4_NOABORT, "__asan_store4_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STORE8_NOABORT, "__asan_store8_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STORE16_NOABORT, "__asan_store16_noabort",
+ BT_FN_VOID_PTR, ATTR_TMPURE_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_STOREN_NOABORT, "__asan_storeN_noabort",
+ BT_FN_VOID_PTR_PTRMODE, ATTR_TMPURE_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_REGISTER_GLOBALS,
"__asan_register_globals",
BT_FN_VOID_PTR_PTRMODE, ATTR_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE,
"__ubsan_handle_builtin_unreachable",
BT_FN_VOID_PTR,
- ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+ ATTR_COLD_CONST_NORETURN_NOTHROW_LEAF_LIST)
DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_MISSING_RETURN,
"__ubsan_handle_missing_return",
BT_FN_VOID_PTR,
"__ubsan_handle_load_invalid_value",
BT_FN_VOID_PTR_PTR,
ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW_ABORT,
+ "__ubsan_handle_divrem_overflow_abort",
+ BT_FN_VOID_PTR_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_SHIFT_OUT_OF_BOUNDS_ABORT,
+ "__ubsan_handle_shift_out_of_bounds_abort",
+ BT_FN_VOID_PTR_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE_ABORT,
+ "__ubsan_handle_vla_bound_not_positive_abort",
+ BT_FN_VOID_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT,
+ "__ubsan_handle_type_mismatch_abort",
+ BT_FN_VOID_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW_ABORT,
+ "__ubsan_handle_add_overflow_abort",
+ BT_FN_VOID_PTR_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW_ABORT,
+ "__ubsan_handle_sub_overflow_abort",
+ BT_FN_VOID_PTR_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW_ABORT,
+ "__ubsan_handle_mul_overflow_abort",
+ BT_FN_VOID_PTR_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW_ABORT,
+ "__ubsan_handle_negate_overflow_abort",
+ BT_FN_VOID_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT,
+ "__ubsan_handle_load_invalid_value_abort",
+ BT_FN_VOID_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
+ "__ubsan_handle_float_cast_overflow",
+ BT_FN_VOID_PTR_PTR,
+ ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
+ "__ubsan_handle_float_cast_overflow_abort",
+ BT_FN_VOID_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS,
+ "__ubsan_handle_out_of_bounds",
+ BT_FN_VOID_PTR_PTR,
+ ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT,
+ "__ubsan_handle_out_of_bounds_abort",
+ BT_FN_VOID_PTR_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_ARG,
+ "__ubsan_handle_nonnull_arg",
+ BT_FN_VOID_PTR,
+ ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_ARG_ABORT,
+ "__ubsan_handle_nonnull_arg_abort",
+ BT_FN_VOID_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN,
+ "__ubsan_handle_nonnull_return",
+ BT_FN_VOID_PTR,
+ ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_ABORT,
+ "__ubsan_handle_nonnull_return_abort",
+ BT_FN_VOID_PTR,
+ ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
--- /dev/null
+/* Optimize and expand sanitizer functions.
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Contributed by Marek Polacek <polacek@redhat.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "hash-table.h"
+#include "predict.h"
+#include "vec.h"
+#include "hashtab.h"
+#include "hash-set.h"
+#include "tm.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "hash-map.h"
+#include "plugin-api.h"
+#include "tree-pass.h"
+#include "asan.h"
+#include "gimple-pretty-print.h"
+#include "tm_p.h"
+#include "langhooks.h"
+#include "ubsan.h"
+#include "params.h"
+#include "tree-ssa-operands.h"
+
+
+/* This is used to carry information about basic blocks. It is
+ attached to the AUX field of the standard CFG block. */
+
+struct sanopt_info
+{
+ /* True if this BB might call (directly or indirectly) free/munmap
+ or similar operation. */
+ bool has_freeing_call_p;
+
+ /* True if HAS_FREEING_CALL_P flag has been computed. */
+ bool has_freeing_call_computed_p;
+
+ /* True if there is a block with HAS_FREEING_CALL_P flag set
+ on any path between an immediate dominator of BB, denoted
+ imm(BB), and BB. */
+ bool imm_dom_path_with_freeing_call_p;
+
+ /* True if IMM_DOM_PATH_WITH_FREEING_CALL_P has been computed. */
+ bool imm_dom_path_with_freeing_call_computed_p;
+
+ /* Number of possibly freeing calls encountered in this bb
+ (so far). */
+ uint64_t freeing_call_events;
+
+ /* True if BB is currently being visited during computation
+ of IMM_DOM_PATH_WITH_FREEING_CALL_P flag. */
+ bool being_visited_p;
+
+ /* True if this BB has been visited in the dominator walk. */
+ bool visited_p;
+};
+
+/* This is used to carry various hash maps and variables used
+ in sanopt_optimize_walker. */
+
+struct sanopt_ctx
+{
+ /* This map maps a pointer (the first argument of UBSAN_NULL) to
+ a vector of UBSAN_NULL call statements that check this pointer. */
+ hash_map<tree, auto_vec<gimple> > null_check_map;
+
+ /* This map maps a pointer (the second argument of ASAN_CHECK) to
+ a vector of ASAN_CHECK call statements that check the access. */
+ hash_map<tree, auto_vec<gimple> > asan_check_map;
+
+ /* Number of IFN_ASAN_CHECK statements. */
+ int asan_num_accesses;
+};
+
+
+/* Return true if there might be any call to free/munmap operation
+ on any path in between DOM (which should be imm(BB)) and BB. */
+
+static bool
+imm_dom_path_with_freeing_call (basic_block bb, basic_block dom)
+{
+ sanopt_info *info = (sanopt_info *) bb->aux;
+ edge e;
+ edge_iterator ei;
+
+ if (info->imm_dom_path_with_freeing_call_computed_p)
+ return info->imm_dom_path_with_freeing_call_p;
+
+ info->being_visited_p = true;
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ sanopt_info *pred_info = (sanopt_info *) e->src->aux;
+
+ if (e->src == dom)
+ continue;
+
+ if ((pred_info->imm_dom_path_with_freeing_call_computed_p
+ && pred_info->imm_dom_path_with_freeing_call_p)
+ || (pred_info->has_freeing_call_computed_p
+ && pred_info->has_freeing_call_p))
+ {
+ info->imm_dom_path_with_freeing_call_computed_p = true;
+ info->imm_dom_path_with_freeing_call_p = true;
+ info->being_visited_p = false;
+ return true;
+ }
+ }
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ sanopt_info *pred_info = (sanopt_info *) e->src->aux;
+
+ if (e->src == dom)
+ continue;
+
+ if (pred_info->has_freeing_call_computed_p)
+ continue;
+
+ gimple_stmt_iterator gsi;
+ for (gsi = gsi_start_bb (e->src); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+
+ if (is_gimple_call (stmt) && !nonfreeing_call_p (stmt))
+ {
+ pred_info->has_freeing_call_p = true;
+ break;
+ }
+ }
+
+ pred_info->has_freeing_call_computed_p = true;
+ if (pred_info->has_freeing_call_p)
+ {
+ info->imm_dom_path_with_freeing_call_computed_p = true;
+ info->imm_dom_path_with_freeing_call_p = true;
+ info->being_visited_p = false;
+ return true;
+ }
+ }
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ if (e->src == dom)
+ continue;
+
+ basic_block src;
+ for (src = e->src; src != dom; )
+ {
+ sanopt_info *pred_info = (sanopt_info *) src->aux;
+ if (pred_info->being_visited_p)
+ break;
+ basic_block imm = get_immediate_dominator (CDI_DOMINATORS, src);
+ if (imm_dom_path_with_freeing_call (src, imm))
+ {
+ info->imm_dom_path_with_freeing_call_computed_p = true;
+ info->imm_dom_path_with_freeing_call_p = true;
+ info->being_visited_p = false;
+ return true;
+ }
+ src = imm;
+ }
+ }
+
+ info->imm_dom_path_with_freeing_call_computed_p = true;
+ info->imm_dom_path_with_freeing_call_p = false;
+ info->being_visited_p = false;
+ return false;
+}
+
+/* Optimize away redundant UBSAN_NULL calls. */
+
+static bool
+maybe_optimize_ubsan_null_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+ gcc_assert (gimple_call_num_args (stmt) == 3);
+ tree ptr = gimple_call_arg (stmt, 0);
+ tree cur_align = gimple_call_arg (stmt, 2);
+ gcc_assert (TREE_CODE (cur_align) == INTEGER_CST);
+ bool remove = false;
+
+ auto_vec<gimple> &v = ctx->null_check_map.get_or_insert (ptr);
+ if (v.is_empty ())
+ {
+ /* For this PTR we don't have any UBSAN_NULL stmts recorded, so there's
+ nothing to optimize yet. */
+ v.safe_push (stmt);
+ return false;
+ }
+
+ /* We already have recorded a UBSAN_NULL check for this pointer. Perhaps we
+ can drop this one. But only if this check doesn't specify stricter
+ alignment. */
+ while (!v.is_empty ())
+ {
+ gimple g = v.last ();
+ /* Remove statements for BBs that have been already processed. */
+ sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+ if (si->visited_p)
+ {
+ v.pop ();
+ continue;
+ }
+
+ /* At this point we shouldn't have any statements that aren't dominating
+ the current BB. */
+ tree align = gimple_call_arg (g, 2);
+ int kind = tree_to_shwi (gimple_call_arg (g, 1));
+ /* If this is a NULL pointer check where we had segv anyway, we can
+ remove it. */
+ if (integer_zerop (align)
+ && (kind == UBSAN_LOAD_OF
+ || kind == UBSAN_STORE_OF
+ || kind == UBSAN_MEMBER_ACCESS))
+ remove = true;
+ /* Otherwise remove the check in non-recovering mode, or if the
+ stmts have same location. */
+ else if (integer_zerop (align))
+ remove = (flag_sanitize_recover & SANITIZE_NULL) == 0
+ || flag_sanitize_undefined_trap_on_error
+ || gimple_location (g) == gimple_location (stmt);
+ else if (tree_int_cst_le (cur_align, align))
+ remove = (flag_sanitize_recover & SANITIZE_ALIGNMENT) == 0
+ || flag_sanitize_undefined_trap_on_error
+ || gimple_location (g) == gimple_location (stmt);
+ if (!remove && gimple_bb (g) == gimple_bb (stmt)
+ && tree_int_cst_compare (cur_align, align) == 0)
+ v.pop ();
+ break;
+ }
+
+ if (!remove)
+ v.safe_push (stmt);
+ return remove;
+}
+
+/* Optimize away redundant ASAN_CHECK calls. */
+
+static bool
+maybe_optimize_asan_check_ifn (struct sanopt_ctx *ctx, gimple stmt)
+{
+ gcc_assert (gimple_call_num_args (stmt) == 4);
+ tree ptr = gimple_call_arg (stmt, 1);
+ tree len = gimple_call_arg (stmt, 2);
+ basic_block bb = gimple_bb (stmt);
+ sanopt_info *info = (sanopt_info *) bb->aux;
+
+ if (TREE_CODE (len) != INTEGER_CST)
+ return false;
+ if (integer_zerop (len))
+ return false;
+
+ gimple_set_uid (stmt, info->freeing_call_events);
+
+ auto_vec<gimple> &v = ctx->asan_check_map.get_or_insert (ptr);
+ if (v.is_empty ())
+ {
+ /* For this PTR we don't have any ASAN_CHECK stmts recorded, so there's
+ nothing to optimize yet. */
+ v.safe_push (stmt);
+ return false;
+ }
+
+ /* We already have recorded a ASAN_CHECK check for this pointer. Perhaps
+ we can drop this one. But only if this check doesn't specify larger
+ size. */
+ while (!v.is_empty ())
+ {
+ gimple g = v.last ();
+ /* Remove statements for BBs that have been already processed. */
+ sanopt_info *si = (sanopt_info *) gimple_bb (g)->aux;
+ if (si->visited_p)
+ v.pop ();
+ else
+ break;
+ }
+
+ unsigned int i;
+ gimple g;
+ gimple to_pop = NULL;
+ bool remove = false;
+ basic_block last_bb = bb;
+ bool cleanup = false;
+
+ FOR_EACH_VEC_ELT_REVERSE (v, i, g)
+ {
+ basic_block gbb = gimple_bb (g);
+ sanopt_info *si = (sanopt_info *) gbb->aux;
+ if (gimple_uid (g) < si->freeing_call_events)
+ {
+ /* If there is a potentially freeing call after g in gbb, we should
+ remove it from the vector, can't use in optimization. */
+ cleanup = true;
+ continue;
+ }
+
+ if (TREE_CODE (len) != INTEGER_CST)
+ {
+ /* If there is some stmts not followed by freeing call event
+ for ptr already in the current bb, no need to insert anything.
+ Non-constant len is treated just as length 1. */
+ if (gbb == bb)
+ return false;
+ break;
+ }
+
+ tree glen = gimple_call_arg (g, 2);
+ /* If we've checked only smaller length than we want to check now,
+ we can't remove the current stmt. If g is in the same basic block,
+ we want to remove it though, as the current stmt is better. */
+ if (tree_int_cst_lt (glen, len))
+ {
+ if (gbb == bb)
+ {
+ to_pop = g;
+ cleanup = true;
+ }
+ continue;
+ }
+
+ while (last_bb != gbb)
+ {
+ /* Paths from last_bb to bb have been checked before.
+ gbb is necessarily a dominator of last_bb, but not necessarily
+ immediate dominator. */
+ if (((sanopt_info *) last_bb->aux)->freeing_call_events)
+ break;
+
+ basic_block imm = get_immediate_dominator (CDI_DOMINATORS, last_bb);
+ gcc_assert (imm);
+ if (imm_dom_path_with_freeing_call (last_bb, imm))
+ break;
+
+ last_bb = imm;
+ }
+ if (last_bb == gbb)
+ remove = true;
+ break;
+ }
+
+ if (cleanup)
+ {
+ unsigned int j = 0, l = v.length ();
+ for (i = 0; i < l; i++)
+ if (v[i] != to_pop
+ && (gimple_uid (v[i])
+ == ((sanopt_info *)
+ gimple_bb (v[i])->aux)->freeing_call_events))
+ {
+ if (i != j)
+ v[j] = v[i];
+ j++;
+ }
+ v.truncate (j);
+ }
+
+ if (!remove)
+ v.safe_push (stmt);
+ return remove;
+}
+
+/* Try to optimize away redundant UBSAN_NULL and ASAN_CHECK calls.
+
+ We walk blocks in the CFG via a depth first search of the dominator
+ tree; we push unique UBSAN_NULL or ASAN_CHECK statements into a vector
+ in the NULL_CHECK_MAP or ASAN_CHECK_MAP hash maps as we enter the
+ blocks. When leaving a block, we mark the block as visited; then
+ when checking the statements in the vector, we ignore statements that
+ are coming from already visited blocks, because these cannot dominate
+ anything anymore. CTX is a sanopt context. */
+
+static void
+sanopt_optimize_walker (basic_block bb, struct sanopt_ctx *ctx)
+{
+ basic_block son;
+ gimple_stmt_iterator gsi;
+ sanopt_info *info = (sanopt_info *) bb->aux;
+ bool asan_check_optimize
+ = (flag_sanitize & SANITIZE_ADDRESS)
+ && ((flag_sanitize & flag_sanitize_recover
+ & SANITIZE_KERNEL_ADDRESS) == 0);
+
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+ {
+ gimple stmt = gsi_stmt (gsi);
+ bool remove = false;
+
+ if (!is_gimple_call (stmt))
+ {
+ /* Handle asm volatile or asm with "memory" clobber
+ the same as potentionally freeing call. */
+ if (gimple_code (stmt) == GIMPLE_ASM
+ && asan_check_optimize
+ && (gimple_asm_clobbers_memory_p (stmt)
+ || gimple_asm_volatile_p (stmt)))
+ info->freeing_call_events++;
+ gsi_next (&gsi);
+ continue;
+ }
+
+ if (asan_check_optimize && !nonfreeing_call_p (stmt))
+ info->freeing_call_events++;
+
+ if (gimple_call_internal_p (stmt))
+ switch (gimple_call_internal_fn (stmt))
+ {
+ case IFN_UBSAN_NULL:
+ remove = maybe_optimize_ubsan_null_ifn (ctx, stmt);
+ break;
+ case IFN_ASAN_CHECK:
+ if (asan_check_optimize)
+ remove = maybe_optimize_asan_check_ifn (ctx, stmt);
+ if (!remove)
+ ctx->asan_num_accesses++;
+ break;
+ default:
+ break;
+ }
+
+ if (remove)
+ {
+ /* Drop this check. */
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Optimizing out\n ");
+ print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+ fprintf (dump_file, "\n");
+ }
+ unlink_stmt_vdef (stmt);
+ gsi_remove (&gsi, true);
+ }
+ else
+ gsi_next (&gsi);
+ }
+
+ if (asan_check_optimize)
+ {
+ info->has_freeing_call_p = info->freeing_call_events != 0;
+ info->has_freeing_call_computed_p = true;
+ }
+
+ for (son = first_dom_son (CDI_DOMINATORS, bb);
+ son;
+ son = next_dom_son (CDI_DOMINATORS, son))
+ sanopt_optimize_walker (son, ctx);
+
+ /* We're leaving this BB, so mark it to that effect. */
+ info->visited_p = true;
+}
+
+/* Try to remove redundant sanitizer checks in function FUN. */
+
+static int
+sanopt_optimize (function *fun)
+{
+ struct sanopt_ctx ctx;
+ ctx.asan_num_accesses = 0;
+
+ /* Set up block info for each basic block. */
+ alloc_aux_for_blocks (sizeof (sanopt_info));
+
+ /* We're going to do a dominator walk, so ensure that we have
+ dominance information. */
+ calculate_dominance_info (CDI_DOMINATORS);
+
+ /* Recursively walk the dominator tree optimizing away
+ redundant checks. */
+ sanopt_optimize_walker (ENTRY_BLOCK_PTR_FOR_FN (cfun), &ctx);
+
+ free_aux_for_blocks ();
+
+ return ctx.asan_num_accesses;
+}
+
+/* Perform optimization of sanitize functions. */
+
+namespace {
+
+const pass_data pass_data_sanopt =
+{
+ GIMPLE_PASS, /* type */
+ "sanopt", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ true, /* has_gate */
+ true, /* has_execute */
+ TV_NONE, /* tv_id */
+ ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_sanopt : public gimple_opt_pass
+{
+public:
+ pass_sanopt (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_sanopt, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate () { return flag_sanitize; }
+ virtual unsigned int execute ();
+
+}; // class pass_sanopt
+
+unsigned int
+pass_sanopt::execute ()
+{
+ basic_block bb;
+ int asan_num_accesses = 0;
+
+ /* Try to remove redundant checks. */
+ if (optimize
+ && (flag_sanitize
+ & (SANITIZE_NULL | SANITIZE_ALIGNMENT | SANITIZE_ADDRESS)))
+ asan_num_accesses = sanopt_optimize (fun);
+ else if (flag_sanitize & SANITIZE_ADDRESS)
+ {
+ gimple_stmt_iterator gsi;
+ FOR_EACH_BB_FN (bb, cfun)
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+ if (is_gimple_call (stmt) && gimple_call_internal_p (stmt)
+ && gimple_call_internal_fn (stmt) == IFN_ASAN_CHECK)
+ ++asan_num_accesses;
+ }
+ }
+
+ bool use_calls = ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD < INT_MAX
+ && asan_num_accesses >= ASAN_INSTRUMENTATION_WITH_CALL_THRESHOLD;
+
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ gimple_stmt_iterator gsi;
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
+ {
+ gimple stmt = gsi_stmt (gsi);
+ bool no_next = false;
+
+ if (!is_gimple_call (stmt))
+ {
+ gsi_next (&gsi);
+ continue;
+ }
+
+ if (gimple_call_internal_p (stmt))
+ {
+ enum internal_fn ifn = gimple_call_internal_fn (stmt);
+ switch (ifn)
+ {
+ case IFN_UBSAN_NULL:
+ no_next = ubsan_expand_null_ifn (&gsi);
+ break;
+ case IFN_UBSAN_BOUNDS:
+ no_next = ubsan_expand_bounds_ifn (&gsi);
+ break;
+ case IFN_UBSAN_OBJECT_SIZE:
+ no_next = ubsan_expand_objsize_ifn (&gsi);
+ break;
+ case IFN_ASAN_CHECK:
+ no_next = asan_expand_check_ifn (&gsi, use_calls);
+ break;
+ default:
+ break;
+ }
+ }
+ else if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+ {
+ tree callee = gimple_call_fndecl (stmt);
+ switch (DECL_FUNCTION_CODE (callee))
+ {
+ case BUILT_IN_UNREACHABLE:
+ if (flag_sanitize & SANITIZE_UNREACHABLE
+ && !lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES (cfun->decl)))
+ no_next = ubsan_instrument_unreachable (&gsi);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Expanded\n ");
+ print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+ fprintf (dump_file, "\n");
+ }
+
+ if (!no_next)
+ gsi_next (&gsi);
+ }
+ }
+ return 0;
+}
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_sanopt (gcc::context *ctxt)
+{
+ return new pass_sanopt (ctxt);
+}
gcc_assert (!TYPE_ALIAS_SET_KNOWN_P (type));
}
+/* Return the least alignment required for type TYPE. */
+
+unsigned int
+min_align_of_type (tree type)
+{
+ unsigned int align = TYPE_ALIGN (type);
+ if (!TYPE_USER_ALIGN (type))
+ {
+ align = MIN (align, BIGGEST_ALIGNMENT);
+#ifdef BIGGEST_FIELD_ALIGNMENT
+ align = MIN (align, BIGGEST_FIELD_ALIGNMENT);
+#endif
+ unsigned int field_align = align;
+#ifdef ADJUST_FIELD_ALIGN
+ tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
+ type);
+ field_align = ADJUST_FIELD_ALIGN (field, field_align);
+ ggc_free (field);
+#endif
+ align = MIN (align, field_align);
+ }
+ return align / BITS_PER_UNIT;
+}
+
/* Vector types need to re-check the target flags each time we report
the machine mode. We need to do this because attribute target can
change the result of vector_mode_supported_p and have_regs_of_mode
node, does nothing except for the first time. */
extern void layout_type (tree);
+/* Return the least alignment in bytes required for type TYPE. */
+extern unsigned int min_align_of_type (tree);
+
/* Construct various nodes representing fract or accum data types. */
extern tree make_fract_type (int, int, int);
extern tree make_accum_type (int, int, int);
+2015-02-09 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64981
+ * gcc.dg/asan/pr64981.c: New test.
+
+2016-04-23 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/70712
+ * c-c++-common/asan/pr70712.c: New test.
+
+2016-04-08 Maxim Ostapenko <m.ostapenko@samsung.com>
+
+ PR sanitizer/70541
+ * c-c++-common/asan/pr70541.c: New test.
+
+2016-02-04 Martin Liska <mliska@suse.cz>
+
+ * g++.dg/asan/pr69276.C: New test.
+
+2016-01-06 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/69099
+ * gcc.dg/ubsan/float-cast-overflow-atomic.c: New test.
+
+2016-01-01 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/69055
+ * gfortran.dg/pr69055.f90: New test.
+
+2015-12-12 Paolo Bonzini <bonzini@gnu.org>
+
+ PR sanitizer/68418
+ * gcc.dg/ubsan/c99-wrapv-shift-1.c,
+ gcc.dg/ubsan/c99-wrapv-shift-2.c: New testcases.
+
+2015-10-29 Richard Biener <rguenther@suse.de>
+
+ PR middle-end/56956
+ * c-c++-common/ubsan/pr56956.c: New testcase.
+
+2015-09-25 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/bounds-11.c: New test.
+ * c-c++-common/ubsan/bounds-12.c: New test.
+
+2015-09-25 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/64906
+ * c-c++-common/ubsan/pr64906.c: New test.
+
+2015-09-17 Bernd Edlinger <bernd.edlinger@hotmail.de>
+
+ PR sanitizer/64078
+ * c-c++-common/ubsan/object-size-9.c (s): Add alignment attribute.
+ (f2, f3): Make the function static.
+ * c-c++-common/ubsan/object-size-10.c (a, b): Add alignment attribute.
+
+2015-09-04 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/67279
+ * gcc.dg/ubsan/pr67279.c: New test.
+
+2015-07-31 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/66977
+ * g++.dg/ubsan/pr66977.C: New test.
+
+2015-07-23 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/66908
+ * c-c++-common/ubsan/pr66908.c: New test.
+
+2013-08-07 Ilya Enkovich <ilya.enkovich@intel.com>
+
+ * gcc.target/i386/struct-size.c: New.
+
+2015-11-23 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * c-c++-common/asan/halt_on_error-1.c: New test.
+ * c-c++-common/asan/halt_on_error-2.c: Likewise.
+
+2015-10-23 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ PR sanitizer/68042
+ * c-c++-common/asan/memcmp-1.c: Adjust test to pass on Darwin.
+ * c-c++-common/asan/sanity-check-pure-c-1.c: Likewise.
+
+2015-10-21 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * c-c++-common/ubsan/float-cast-overflow-10.c: Adjust test.
+ * c-c++-common/ubsan/float-cast-overflow-8.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-9.c: Likewise.
+ * g++.dg/asan/default-options-1.C: Likewise.
+
+2015-06-08 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/66452
+ * g++.dg/ubsan/pr66452.C: New test.
+
+2015-05-07 Marek Polacek <polacek@redhat.com>
+ Martin Uecker <uecker@eecs.berkeley.edu>
+
+ * c-c++-common/ubsan/bounds-10.c: New test.
+
+2015-04-09 Jakub Jelinek <jakub@redhat.com>
+
+ PR tree-optimization/65709
+ * c-c++-common/ubsan/align-9.c: New test.
+
+2013-04-09 Paul Thomas <pault@gcc.gnu.org>
+
+2015-03-27 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/65583
+ * g++.dg/ubsan/pr65583.C: New test.
+
+2015-03-10 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/65367
+ * c-c++-common/ubsan/pr65367.c: New test.
+
+2015-03-07 Marek Polacek <polacek@redhat.com>
+ Martin Uecker <uecker@eecs.berkeley.edu>
+
+ PR sanitizer/65280
+ * c-c++-common/ubsan/bounds-1.c: Add testing of flexible array
+ member-like arrays.
+ * c-c++-common/ubsan/bounds-8.c: New test.
+ * c-c++-common/ubsan/bounds-9.c: New test.
+ * gcc.dg/ubsan/bounds-2.c: New test.
+
+2015-03-05 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/bounds-6.c: Use dg-do run.
+
+2015-02-20 Jakub Jelinek <jakub@redhat.com>
+
+ PR bootstrap/63888
+ * c-c++-common/asan/pr63888.c: New test.
+
+2015-02-12 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/65000
+ * g++.dg/ubsan/pr65000.C: New test.
+
+2015-02-12 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64984
+ * g++.dg/ubsan/pr64984.C: New test.
+
+2015-02-12 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/65019
+ * g++.dg/ubsan/pr65019.C: New test.
+
+2015-02-11 Marek Polacek <polacek@redhat.com>
+
+ * g++.dg/ubsan/shift-1.C: New test.
+ * gcc.dg/ubsan/c-shift-2.c: New test.
+ * c-c++-common/ubsan/shift-5.c: Remove file.
+
+2015-01-20 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64632
+ * g++.dg/ubsan/pr64632.C: New test.
+
+2015-01-06 Jakub Jelinek <jakub@redhat.com>
+
+ * c-c++-common/asan/sanitize-all-1.c: New test.
+ * c-c++-common/ubsan/sanitize-all-1.c: New test.
+ * c-c++-common/ubsan/sanitize-all-2.c: New test.
+ * c-c++-common/ubsan/sanitize-all-3.c: New test.
+ * c-c++-common/ubsan/sanitize-all-4.c: New test.
+
+2015-01-05 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64344
+ * c-c++-common/ubsan/pr64344-1.c: New test.
+ * c-c++-common/ubsan/pr64344-2.c: New test.
+
+2014-12-27 Segher Boessenkool <segher@kernel.crashing.org>
+
+ * lib/ubsan-dg.exp (check_effective_target_fsanitize_undefined):
+ Check if testcases run without errors, not just if they compile.
+
+2014-12-22 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
+
+ * lib/ubsan-dg.exp: Add library path for libstdc++.
+
+2014-12-17 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64289
+ * c-c++-common/ubsan/pr64289.c: New test.
+
+2014-12-04 Marek Polacek <polacek@redhat.com>
+
+ PR middle-end/56917
+ * c-c++-common/ubsan/pr56917.c: New test.
+
+2014-12-02 Marek Polacek <polacek@redhat.com>
+
+ * g++.dg/cpp0x/constexpr-shift1.C: New test.
+ * g++.dg/cpp1y/constexpr-shift1.C: New test.
+ * g++.dg/ubsan/pr63956.C: Add dg-errors.
+
+2014-12-01 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/64121
+ * c-c++-common/ubsan/pr64121.c: New test.
+
+2014-12-01 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63956
+ * c-c++-common/ubsan/shift-5.c: Add xfails.
+ * g++.dg/ubsan/div-by-zero-1.C: Don't use -w. Add xfail.
+ * g++.dg/ubsan/pr63956.C: New test.
+
+2015-04-22 Yury Gribov <y.gribov@samsung.com>
+
+ * c-c++-common/asan/user-section-1.c: New test.
+ * c-c++-common/asan/user-section-2.c: New test.
+ * c-c++-common/asan/user-section-3.c: New test.
+
+2015-05-07 Marek Polacek <polacek@redhat.com>
+
+ PR testsuite/66046
+ * c-c++-common/ubsan/align-6.c: Don't match trailing newlines in the
+ last dg-output.
+ * c-c++-common/ubsan/align-7.c: Likewise.
+ * c-c++-common/ubsan/bounds-8.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-9.c: Likewise.
+ * c-c++-common/ubsan/load-bool-enum.c: Likewise.
+ * c-c++-common/ubsan/null-1.c: Likewise.
+ * c-c++-common/ubsan/null-10.c: Likewise.
+ * c-c++-common/ubsan/null-11.c: Likewise.
+ * c-c++-common/ubsan/null-2.c: Likewise.
+ * c-c++-common/ubsan/null-3.c: Likewise.
+ * c-c++-common/ubsan/null-4.c: Likewise.
+ * c-c++-common/ubsan/null-5.c: Likewise.
+ * c-c++-common/ubsan/null-6.c: Likewise.
+ * c-c++-common/ubsan/null-7.c: Likewise.
+ * c-c++-common/ubsan/null-8.c: Likewise.
+ * c-c++-common/ubsan/null-9.c: Likewise.
+ * c-c++-common/ubsan/object-size-1.c: Likewise.
+ * c-c++-common/ubsan/object-size-10.c: Likewise.
+ * c-c++-common/ubsan/object-size-4.c: Likewise.
+ * c-c++-common/ubsan/object-size-5.c: Likewise.
+ * c-c++-common/ubsan/object-size-7.c: Likewise.
+ * c-c++-common/ubsan/object-size-8.c: Likewise.
+ * c-c++-common/ubsan/object-size-9.c: Likewise.
+ * c-c++-common/ubsan/overflow-add-2.c: Likewise.
+ * c-c++-common/ubsan/overflow-int128.c: Likewise.
+ * c-c++-common/ubsan/overflow-mul-2.c: Likewise.
+ * c-c++-common/ubsan/overflow-mul-4.c: Likewise.
+ * c-c++-common/ubsan/overflow-negate-1.c: Likewise.
+ * c-c++-common/ubsan/overflow-negate-3.c: Likewise.
+ * c-c++-common/ubsan/overflow-sub-2.c: Likewise.
+ * c-c++-common/ubsan/overflow-sub-4.c: Likewise.
+ * c-c++-common/ubsan/pr59333.c: Likewise.
+ * c-c++-common/ubsan/pr59667.c: Likewise.
+ * c-c++-common/ubsan/pr60613-2.c: Likewise.
+ * c-c++-common/ubsan/pr60636.c: Likewise.
+ * c-c++-common/ubsan/pr63802.c: Likewise.
+ * c-c++-common/ubsan/recovery-1.c: Likewise.
+ * c-c++-common/ubsan/recovery-3.c: Likewise.
+ * c-c++-common/ubsan/shift-1.c: Likewise.
+ * c-c++-common/ubsan/shift-2.c: Likewise.
+ * c-c++-common/ubsan/shift-4.c: Likewise.
+ * c-c++-common/ubsan/shift-7.c: Likewise.
+ * c-c++-common/ubsan/undefined-2.c: Likewise.
+ * c-c++-common/ubsan/vla-1.c: Likewise.
+ * g++.dg/ubsan/null-1.C: Likewise.
+ * g++.dg/ubsan/null-3.C: Likewise.
+ * g++.dg/ubsan/null-4.C: Likewise.
+ * g++.dg/ubsan/vptr-8.C: Likewise.
+ * g++.dg/ubsan/vptr-9.C: Likewise.
+ * gcc.dg/ubsan/bounds-2.c: Likewise.
+ * gcc.dg/ubsan/object-size-9.c: Likewise.
+
+2015-04-17 Yury Gribov <y.gribov@samsung.com>
+
+ * c-c++-common/asan/user-section-1.c: New test.
+
+2015-04-01 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ PR target/65624
+ * gcc.target/aarch64/pr65624.c: New test.
+
+2015-03-16 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ PR sanitizer/64820
+ * c-c++-common/asan/pr64820.c: New test.
+
+2014-11-28 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/shift-8.c: New test.
+
+2014-11-28 Marek Polacek <polacek@redhat.com>
+
+ PR c/63862
+ * gcc.c-torture/execute/shiftopt-1.c: Don't XFAIL anymore.
+ * c-c++-common/ubsan/shift-7.c: New test.
+
+2014-11-26 Jakub Jelinek <jakub@redhat.com>
+
+ * lib/tsan-dg.exp (check_effective_target_fsanitize_thread,
+ tsan_init): Don't use -fPIE or -pie.
+
+2014-11-19 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
+
+ PR sanitizer/63939
+ * c-c++-common/asan/global-overflow-1.c: Allow extra spaces after
+ stack pointer address, to fit darwin output when symbolizer is not
+ present.
+ * c-c++-common/asan/heap-overflow-1.c: Likewise.
+ * c-c++-common/asan/memcmp-1.c: Likewise.
+ * c-c++-common/asan/misalign-1.c: Likewise.
+ * c-c++-common/asan/misalign-2.c: Likewise.
+ * c-c++-common/asan/null-deref-1.c: Likewise.
+ * c-c++-common/asan/stack-overflow-1.c: Likewise.
+ * c-c++-common/asan/strlen-overflow-1.c: Likewise.
+ * c-c++-common/asan/strncpy-overflow-1.c: Likewise.
+ * c-c++-common/asan/use-after-free-1.c: Likewise.
+ * g++.dg/asan/deep-stack-uaf-1.C: Likewise.
+ * g++.dg/asan/deep-tail-call-1.C: Likewise.
+ * g++.dg/asan/large-func-test-1.C: Likewise.
+
+2014-11-19 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
+
+ PR sanitizer/63939
+ * g++.dg/asan/large-func-test-1.C: Ajust dg-output.
+
+2014-11-19 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
+
+ PR sanitizer/63939
+ * c-c++-common/asan/heap-overflow-1.c: Ajust dg-output.
+ * c-c++-common/asan/memcmp-1.c: Likewise.
+ * c-c++-common/asan/strncpy-overflow-1.c: Likewise.
+ * c-c++-common/asan/use-after-free-1.c: Likewise.
+
+2014-11-19 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63690
+ * gcc.dg/ubsan/pr63690.c: New test.
+
+2014-11-19 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
+
+ PR sanitizer/62132
+ * c-c++-common/asan/misalign-1.c: Pass -fno-omit-frame-pointer on
+ darwin, adjust dg-output.
+ * c-c++-common/asan/misalign-2.c: Likewise.
+
+2014-11-19 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63879
+ * c-c++-common/ubsan/pr63879-1.c: New test.
+ * c-c++-common/ubsan/pr63879-2.c: New test.
+
+2014-11-19 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/63520
+ * c-c++-common/ubsan/pr63520.c: New test.
+
+2014-11-19 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/63913
+ * g++.dg/ubsan/pr63913.C: New test.
+
+2014-11-18 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/63813
+ * g++.dg/ubsan/pr63813.C: New test.
+
+2014-11-18 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63866
+ * c-c++-common/ubsan/pr63866.c: New test.
+
+2014-11-18 Marat Zakirov <m.zakirov@samsung.com>
+
+ * c-c++-common/asan/aggressive-opts.c: New test.
+
+2014-11-18 Yury Gribov <y.gribov@samsung.com>
+
+ PR sanitizer/63802
+ * c-c++-common/ubsan/pr63802.c: New test.
+
+2014-11-14 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/overflow-negate-3.c: New test.
+
+2014-11-14 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/63839
+ * c-c++-common/ubsan/pr63839.c: New test.
+ * c-c++-common/ubsan/unreachable-2.c: New test.
+
+2014-11-12 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/overflow-sub-4.c: New test.
+ * c-c++-common/ubsan/overflow-sub-2.c: Adjust dg-output.
+ * c-c++-common/ubsan/overflow-int128.c: Likewise.
+
+2014-11-12 H.J. Lu <hongjiu.lu@intel.com>
+
+ PR testsuite/63830
+ * c-c++-common/asan/strlen-overflow-1.c (main): Avoid tail call.
+
+2014-11-10 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/align-7.c: Skip for -flto -fno-fat-lto-objects.
+ * c-c++-common/ubsan/align-8.c: Likewise.
+ * g++.dg/ubsan/null-4.C: Likewise.
+ * g++.dg/ubsan/null-5.C: Likewise.
+
+2014-11-06 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/align-2.c: Add dg-output.
+ * c-c++-common/ubsan/align-4.c: Likewise.
+ * c-c++-common/ubsan/align-6.c: New test.
+ * c-c++-common/ubsan/align-7.c: New test.
+ * c-c++-common/ubsan/align-8.c: New test.
+ * g++.dg/ubsan/null-1.C: Add dg-output.
+ * g++.dg/ubsan/null-2.C: Likewise.
+ * g++.dg/ubsan/null-3.C: New test.
+ * g++.dg/ubsan/null-4.C: New test.
+ * g++.dg/ubsan/null-5.C: New test.
+
+2014-11-04 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/align-2.c: Remove dg-output.
+ * c-c++-common/ubsan/align-4.c: Likewise.
+ * g++.dg/ubsan/null-1.C: Likewise.
+ * g++.dg/ubsan/null-2.C: Likewise.
+
+2014-11-04 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/object-size-10.c: New test.
+
+2014-10-31 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/63697
+ * c-c++-common/ubsan/overflow-sub-3.c: New test.
+
+2014-10-28 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * c-c++-common/asan/no-redundant-instrumentation-1.c: Updated test.
+ * c-c++-common/asan/no-redundant-instrumentation-4.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-5.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-6.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-7.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-8.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-2.c: Removed.
+ * c-c++-common/asan/no-redundant-instrumentation-9.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-10.c: New test.
+ * c-c++-common/asan/no-redundant-instrumentation-11.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-12.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-13.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-14.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-15.c: Likewise.
+ * c-c++-common/asan/pr63638.c: Likewise.
+
+2014-10-28 Yury Gribov <y.gribov@samsung.com>
+
+ * c-c++-common/asan/kasan-recover-1.c: New test.
+ * c-c++-common/asan/kasan-recover-2.c: New test.
+ * c-c++-common/asan/instrument-with-calls-1.c: Get rid of -save-temps.
+ * c-c++-common/asan/instrument-with-calls-2.c: Likewise.
+ * c-c++-common/asan/instrument-with-calls-3.c: Likewise.
+ * c-c++-common/asan/kasan-recover-1.c: Likewise.
+ * c-c++-common/asan/kasan-recover-2.c: Likewise.
+ * c-c++-common/asan/no-asan-globals.c: Likewise.
+ * c-c++-common/asan/no-instrument-reads.c: Likewise.
+ * c-c++-common/asan/no-instrument-writes.c: Likewise.
+ * c-c++-common/asan/no-use-after-return.c: Likewise.
+
+2014-10-28 Yury Gribov <y.gribov@samsung.com>
+
+ * c-c++-common/asan/shadow-offset-1.c: New test.
+
+2014-10-23 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/undefined-2.c: New test.
+
+2014-10-22 Jakub Jelinek <jakub@redhat.com>
+ Yury Gribov <y.gribov@samsung.com>
+
+ * c-c++-common/ubsan/align-1.c: Update cmdline options.
+ * c-c++-common/ubsan/align-3.c: Likewise.
+ * c-c++-common/ubsan/bounds-1.c: Likewise.
+ * c-c++-common/ubsan/div-by-zero-7.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-10.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-7.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-8.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-9.c: Likewise.
+ * c-c++-common/ubsan/nonnull-2.c: Likewise.
+ * c-c++-common/ubsan/nonnull-3.c: Likewise.
+ * c-c++-common/ubsan/object-size-3.c: Likewise.
+ * c-c++-common/ubsan/overflow-1.c: Likewise.
+ * c-c++-common/ubsan/overflow-add-1.c: Likewise.
+ * c-c++-common/ubsan/overflow-add-3.c: Likewise.
+ * c-c++-common/ubsan/overflow-mul-1.c: Likewise.
+ * c-c++-common/ubsan/overflow-mul-3.c: Likewise.
+ * c-c++-common/ubsan/overflow-negate-2.c: Likewise.
+ * c-c++-common/ubsan/overflow-sub-1.c: Likewise.
+ * c-c++-common/ubsan/pr59503.c: Likewise.
+ * c-c++-common/ubsan/pr60613-1.c: Likewise.
+ * c-c++-common/ubsan/save-expr-1.c: Likewise.
+ * c-c++-common/ubsan/shift-3.c: Likewise.
+ * c-c++-common/ubsan/shift-6.c: Likewise.
+ * c-c++-common/ubsan/undefined-1.c: Likewise.
+ * c-c++-common/ubsan/vla-2.c: Likewise.
+ * c-c++-common/ubsan/vla-3.c: Likewise.
+ * c-c++-common/ubsan/vla-4.c: Likewise.
+ * g++.dg/ubsan/cxx11-shift-1.C: Likewise.
+ * g++.dg/ubsan/return-2.C: Likewise.
+ * c-c++-common/ubsan/recovery-1.c: New test.
+ * c-c++-common/ubsan/recovery-2.c: New test.
+ * c-c++-common/ubsan/recovery-3.c: New test.
+ * c-c++-common/ubsan/recovery-common.inc: New file.
+
+2014-09-19 Marat Zakirov <m.zakirov@samsung.com>
+
+ * c-c++-common/asan/bitfield-5.c: New test.
+
+2014-10-10 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/object-size-1.c: New test.
+ * c-c++-common/ubsan/object-size-2.c: New test.
+ * c-c++-common/ubsan/object-size-3.c: New test.
+ * c-c++-common/ubsan/object-size-4.c: New test.
+ * c-c++-common/ubsan/object-size-5.c: New test.
+ * c-c++-common/ubsan/object-size-6.c: New test.
+ * c-c++-common/ubsan/object-size-7.c: New test.
+ * c-c++-common/ubsan/object-size-8.c: New test.
+ * c-c++-common/ubsan/object-size-9.c: New test.
+ * g++.dg/ubsan/object-size-1.C: New test.
+ * gcc.dg/ubsan/object-size-9.c: New test.
+
+2014-09-24 Marek Polacek <polacek@redhat.com>
+
+ PR c/61405
+ PR c/53874
+ * c-c++-common/pr53874.c: New test.
+ * c-c++-common/pr61405.c: New test.
+
+2014-09-24 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/63316
+ * c-c++-common/asan/pr63316.c: New test.
+
+2014-09-19 Marat Zakirov <m.zakirov@samsung.com>
+
+ * c-c++-common/asan/red-align-1.c: New test.
+ * c-c++-common/asan/red-align-2.c: New test.
+
+2014-09-10 Jakub Jelinek <jakub@redhat.com>
+
+ * c-c++-common/ubsan/attrib-3.c: New test.
+ * c-c++-common/ubsan/nonnull-1.c: New test.
+ * c-c++-common/ubsan/nonnull-2.c: New test.
+ * c-c++-common/ubsan/nonnull-3.c: New test.
+ * c-c++-common/ubsan/nonnull-4.c: New test.
+ * c-c++-common/ubsan/nonnull-5.c: New test.
+
+2014-08-28 Yury Gribov <y.gribov@samsung.com>
+
+ * c-c++-common/asan/pr62089.c: Fix test on 32-bit platforms.
+
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * c-c++-common/ubsan/align-1.c: New test.
+ * c-c++-common/ubsan/align-2.c: New test.
+ * c-c++-common/ubsan/align-3.c: New test.
+ * c-c++-common/ubsan/align-4.c: New test.
+ * c-c++-common/ubsan/align-5.c: New test.
+ * c-c++-common/ubsan/attrib-4.c: New test.
+ * g++.dg/ubsan/align-1.C: New test.
+ * g++.dg/ubsan/align-2.C: New test.
+ * g++.dg/ubsan/align-3.C: New test.
+ * g++.dg/ubsan/attrib-1.C: New test.
+ * g++.dg/ubsan/null-1.C: New test.
+ * g++.dg/ubsan/null-2.C: New test.
+
+2014-08-11 Yury Gribov <y.gribov@samsung.com>
+
+ * c-c++-common/asan/inc.c: Update test.
+ * c-c++-common/asan/instrument-with-calls-2.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-1.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-2.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-3.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-4.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-5.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-6.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-7.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-8.c: Likewise.
+ * c-c++-common/asan/no-redundant-instrumentation-9.c: Likewise.
+
+2014-08-01 Jakub Jelinek <jakub@redhat.com>
+
+ * c-c++-common/ubsan/align-1.c: New test.
+ * c-c++-common/ubsan/align-2.c: New test.
+ * c-c++-common/ubsan/align-3.c: New test.
+ * c-c++-common/ubsan/align-4.c: New test.
+ * c-c++-common/ubsan/align-5.c: New test.
+ * c-c++-common/ubsan/attrib-4.c: New test.
+ * g++.dg/ubsan/align-1.C: New test.
+ * g++.dg/ubsan/align-2.C: New test.
+ * g++.dg/ubsan/align-3.C: New test.
+ * g++.dg/ubsan/attrib-1.C: New test.
+ * g++.dg/ubsan/null-1.C: New test.
+ * g++.dg/ubsan/null-2.C: New test.
+
+2014-07-23 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/bounds-2.c (fn4): Adjust to check the array size
+ in the structure.
+
+2014-07-17 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * c-c++-common/ubsan/bounds-2.c: Change output pattern.
+ * c-c++-common/ubsan/bounds-5.c: Likewise.
+ * c-c++-common/ubsan/bounds-6.c: Likewise.
+ * c-c++-common/ubsan/bounds-7.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-1.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-10.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-2.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-3.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-4.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-5.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-6.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-8.c: Likewise.
+ * gcc.dg/ubsan/float-cast-overflow-bf.c: Likewise.
+ * g++.dg/ubsan/float-cast-overflow-bf.C: Likewise.
+
+2014-07-15 Marek Polacek <polacek@redhat.com>
+
+ * gcc.dg/ubsan/bounds-1.c: New test.
+
+2014-06-30 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/attrib-2.c: New test.
+ * g++.dg/ubsan/return-3.C: New test.
+
+2014-06-27 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/bounds-2.c: Adjust dg-output.
+ (fn1): Remove store to out-of-bounds location. Add memory barrier.
+ (fn2): Likewise.
+ (fn5): Likewise.
+ (fn6): Likewise.
+ (fn7): Likewise.
+ (fn8): Likewise.
+ (fn9): Likewise.
+ (fn11): Likewise.
+ * c-c++-common/ubsan/bounds-5.c (fn1): Remove store to out-of-bounds
+ location. Add memory barrier.
+ (fn2): Likewise.
+ (fn3): Likewise.
+ (fn4): Likewise.
+ (fn5): Likewise.
+ * c-c++-common/ubsan/bounds-7.c: New test.
+
+2014-06-20 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/bounds-1.c: New test.
+ * c-c++-common/ubsan/bounds-2.c: New test.
+ * c-c++-common/ubsan/bounds-3.c: New test.
+ * c-c++-common/ubsan/bounds-4.c: New test.
+ * c-c++-common/ubsan/bounds-5.c: New test.
+ * c-c++-common/ubsan/bounds-6.c: New test.
+
+2014-05-27 Marek Polacek <polacek@redhat.com>
+
+ PR testsuite/61319
+ * c-c++-common/ubsan/float-cast.h: Conditionally define LLONG_MAX,
+ LLONG_MIN, and ULLONG_MAX.
+
+2014-05-27 Dominique d'Humieres <dominiq@lps.ens.fr>
+
+ PR testsuite/61319
+ * c-c++-common/ubsan/float-cast-overflow-1.c: Make the sign of
+ -nan optional.
+ * c-c++-common/ubsan/float-cast-overflow-2.c: Likewise.
+ * c-c++-common/ubsan/float-cast-overflow-4.c: Likewise.
+
+2014-05-23 Marek Polacek <polacek@redhat.com>
+ Jakub Jelinek <jakub@redhat.com>
+
+ * c-c++-common/ubsan/float-cast-overflow-1.c: New test.
+ * c-c++-common/ubsan/float-cast-overflow-10.c: New test.
+ * c-c++-common/ubsan/float-cast-overflow-2.c: New test.
+ * c-c++-common/ubsan/float-cast-overflow-3.c: New test.
+ * c-c++-common/ubsan/float-cast-overflow-4.c: New test.
+ * c-c++-common/ubsan/float-cast-overflow-5.c: New test.
+ * c-c++-common/ubsan/float-cast-overflow-6.c: New test.
+ * c-c++-common/ubsan/float-cast-overflow-7.c: New test.
+ * c-c++-common/ubsan/float-cast-overflow-7.h: New file.
+ * c-c++-common/ubsan/float-cast-overflow-8.c: New test.
+ * c-c++-common/ubsan/float-cast-overflow-9.c: New test.
+ * c-c++-common/ubsan/float-cast.h: New file.
+ * g++.dg/ubsan/float-cast-overflow-bf.C: New test.
+ * gcc.dg/ubsan/float-cast-overflow-bf.c: New test.
+
+2014-04-30 Marek Polacek <polacek@redhat.com>
+
+ * c-c++-common/ubsan/div-by-zero-5.c: Fix formatting.
+ * c-c++-common/ubsan/float-div-by-zero-1.c: New test.
+
+2014-04-22 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/60275
+ * g++.dg/ubsan/return-2.C: Revert 2014-03-24 changes, add
+ -fno-sanitize-recover to dg-options.
+ * g++.dg/ubsan/cxx11-shift-1.C: Remove c++11 target restriction,
+ add -std=c++11 to dg-options.
+ * g++.dg/ubsan/cxx11-shift-2.C: Likewise.
+ * g++.dg/ubsan/cxx1y-vla.C: Remove c++1y target restriction,
+ add -std=c++1y to dg-options.
+ * c-c++-common/ubsan/undefined-1.c: Revert 2014-03-24 changes, add
+ -fno-sanitize-recover to dg-options.
+ * c-c++-common/ubsan/overflow-sub-1.c: Likewise.
+ * c-c++-common/ubsan/vla-4.c: Likewise.
+ * c-c++-common/ubsan/pr59503.c: Likewise.
+ * c-c++-common/ubsan/vla-3.c: Likewise.
+ * c-c++-common/ubsan/save-expr-1.c: Likewise.
+ * c-c++-common/ubsan/overflow-add-1.c: Likewise.
+ * c-c++-common/ubsan/shift-3.c: Likewise.
+ * c-c++-common/ubsan/overflow-1.c: Likewise.
+ * c-c++-common/ubsan/overflow-negate-2.c: Likewise.
+ * c-c++-common/ubsan/vla-2.c: Likewise.
+ * c-c++-common/ubsan/overflow-mul-1.c: Likewise.
+ * c-c++-common/ubsan/pr60613-1.c: Likewise.
+ * c-c++-common/ubsan/shift-6.c: Likewise.
+ * c-c++-common/ubsan/overflow-mul-3.c: Likewise.
+ * c-c++-common/ubsan/overflow-add-3.c: New test.
+ * c-c++-common/ubsan/overflow-add-4.c: New test.
+ * c-c++-common/ubsan/div-by-zero-6.c: New test.
+ * c-c++-common/ubsan/div-by-zero-7.c: New test.
+
+2014-04-15 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * c-c++-common/asan/null-deref-1.c: Change regexp to pass test
+ under qemu-arm.
+ * c-c++-common/ubsan/div-by-zero-1.c: Likewise.
+ * c-c++-common/ubsan/div-by-zero-2.c: Likewise.
+ * c-c++-common/ubsan/div-by-zero-3.c: Likewise.
+ * c-c++-common/ubsan/load-bool-enum.c (foo): Likewise.
+ * c-c++-common/ubsan/null-1.c: Likewise.
+ * c-c++-common/ubsan/null-10.c: Likewise.
+ * c-c++-common/ubsan/null-11.c: Likewise.
+ * c-c++-common/ubsan/null-2.c: Likewise.
+ * c-c++-common/ubsan/null-3.c: Likewise.
+ * c-c++-common/ubsan/null-4.c: Likewise.
+ * c-c++-common/ubsan/null-5.c: Likewise.
+ * c-c++-common/ubsan/null-6.c: Likewise.
+ * c-c++-common/ubsan/null-7.c: Likewise.
+ * c-c++-common/ubsan/null-8.c: Likewise.
+ * c-c++-common/ubsan/null-9.c: Likewise.
+ * c-c++-common/ubsan/overflow-add-2.c: Likewise.
+ * c-c++-common/ubsan/overflow-int128.c: Likewise.
+ * c-c++-common/ubsan/overflow-mul-2.c: Likewise.
+ * c-c++-common/ubsan/overflow-mul-4.c: Likewise.
+ * c-c++-common/ubsan/overflow-negate-1.c: Likewise.
+ * c-c++-common/ubsan/overflow-sub-2.c: Likewise.
+ * c-c++-common/ubsan/pr59333.c: Likewise.
+ * c-c++-common/ubsan/pr59667.c: Likewise.
+ * c-c++-common/ubsan/pr60613-2.c: Likewise.
+ * c-c++-common/ubsan/pr60636.c: Likewise.
+ * c-c++-common/ubsan/shift-1.c: Likewise.
+ * c-c++-common/ubsan/shift-2.c: Likewise.
+ * c-c++-common/ubsan/vla-1.c: Likewise.
+
+2014-05-30 Jakub Jelinek <jakub@redhat.com>
+
+ * g++.dg/asan/asan_test.C: Add -std=c++11 and
+ -DSANITIZER_USE_DEJAGNU_GTEST=1 to dg-options, remove
+ -DASAN_USE_DEJAGNU_GTEST=1.
+ * g++.dg/asan/asan_mem_test.cc: Updated from upstream
+ r209283.
+ * g++.dg/asan/asan_oob_test.cc: Likewise.
+ * g++.dg/asan/sanitizer_test_utils.h: Likewise.
+ * g++.dg/asan/asan_str_test.cc: Likewise.
+ * g++.dg/asan/asan_test_utils.h: Likewise.
+ * g++.dg/asan/sanitizer_test_config.h: Likewise.
+ * g++.dg/asan/asan_test.cc: Likewise. Allow size 12
+ for long double.
+ * g++.dg/asan/sanitizer_pthread_wrappers.h: New file.
+ Imported from upstream r209283.
+ * g++.dg/asan/asan_test_config.h: Likewise.
+
+2014-05-22 Kostya Serebryany <kcc@google.com>
+
+ * c-c++-common/tsan/mutexset1.c: Update the test to match
+ upstream r209283.
+ * g++.dg/asan/symbolize-callback-1.C: Delete the deprecated test.
+
+2014-05-21 Marek Polacek <polacek@redhat.com>
+
+ PR sanitizer/61272
+ * g++.dg/ubsan/pr61272.C: New test.
+
+2014-05-14 Yury Gribov <y.gribov@samsung.com>
+
+ PR sanitizer/61100
+ * c-c++-common/asan/asan-interface-1.c: New test.
+ * lib/asan-dg.exp (asan_include_flags): New function.
+ (asan_init): Call asan_include_flags to obtain path
+ to sanitizer headers.
+
2015-10-22 Paul Thomas <pault@gcc.gnu.org>
PR fortran/58754
--- /dev/null
+/* { dg-options "-fdump-tree-asan" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O3" } } */
+
+int ext;
+
+int
+Perl_do_sv_dump()
+{
+ int freq[10];
+ int i;
+ int max = 0;
+
+ if (max < ext)
+ max = ext;
+
+ for (i = 0; i <= max; i++)
+ if (freq[i])
+ ext = 0;
+
+ if (i > 20)
+ return freq[i];
+ else
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "ASAN_CHECK" 2 "asan1" } } */
+/* { dg-final { cleanup-tree-dump "asan1" } } */
/* { dg-do run { target { *-*-linux* } } } */
-#include <stdbool.h>
#include <sanitizer/asan_interface.h>
int main() {
--- /dev/null
+/* Check BIT_FIELD_REF. */
+
+/* { dg-do run } */
+/* { dg-shouldfail "asan" } */
+
+struct A
+{
+ int y : 20;
+ int x : 13;
+};
+
+int __attribute__ ((noinline, noclone))
+f (void *p) {
+ return ((struct A *)p)->x != 0;
+}
+
+int
+main ()
+{
+ int a = 0;
+ return f (&a);
+}
+
+/* { dg-output "ERROR: AddressSanitizer: stack-buffer-overflow" } */
}
/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+ thread T0.*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*global-overflow-1.c:20|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r).*" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*global-overflow-1.c:20|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r).*" } */
/* { dg-output "0x\[0-9a-f\]+ is located 0 bytes to the right of global variable" } */
/* { dg-output ".*YYY\[^\n\r]* of size 10\[^\n\r]*(\n|\r\n|\r)" } */
--- /dev/null
+/* Test recovery mode. */
+/* { dg-do run } */
+/* { dg-options "-fsanitize-recover=address" } */
+/* { dg-set-target-env-var ASAN_OPTIONS "halt_on_error=false" } */
+
+#include <string.h>
+
+volatile int ten = 10;
+
+int main() {
+ char x[10];
+ memset(x, 0, ten + 1);
+ asm volatile ("" : : : "memory");
+ volatile int res = x[ten];
+ x[ten] = res + 3;
+ res = x[ten];
+ return 0;
+}
+
+/* { dg-output "WRITE of size 11 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r).*" } */
+/* { dg-output "\[^\n\r]*READ of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r).*" } */
+/* { dg-output "\[^\n\r]*WRITE of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r).*" } */
+/* { dg-output "\[^\n\r]*READ of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r).*" } */
--- /dev/null
+/* Test recovery mode. */
+/* { dg-do run } */
+/* { dg-options "-fsanitize-recover=address" } */
+/* { dg-set-target-env-var ASAN_OPTIONS "halt_on_error=true" } */
+/* { dg-shouldfail "asan" } */
+
+#include <string.h>
+
+volatile int ten = 10;
+
+int main() {
+ char x[10];
+ memset(x, 0, ten + 1);
+ asm volatile ("" : : : "memory");
+ volatile int res = x[ten];
+ x[ten] = res + 3;
+ res = x[ten];
+ return 0;
+}
+
+/* { dg-output "WRITE of size 11 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r).*" } */
+/* { dg-prune-output "\[^\n\r]*READ of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r).*" } */
+/* { dg-prune-output "\[^\n\r]*WRITE of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r).*" } */
+/* { dg-prune-output "\[^\n\r]*READ of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r).*" } */
}
/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+ thread T0.*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*heap-overflow-1.c:21|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*heap-overflow-1.c:21|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*0x\[0-9a-f\]+ is located 0 bytes to the right of 10-byte region\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*allocated by thread T0 here:\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*(interceptor_|)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*heap-overflow-1.c:19|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*heap-overflow-1.c:19|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-do assemble } */
-/* { dg-options "-fno-sanitize=address -fsanitize=kernel-address --param asan-instrumentation-with-call-threshold=0 -save-temps" } */
+/* { dg-do compile } */
+/* { dg-options "--param asan-instrumentation-with-call-threshold=0" } */
void f(char *a, int *b) {
*b = *a;
/* { dg-final { scan-assembler "__asan_load1" } } */
/* { dg-final { scan-assembler "__asan_store4" } } */
-/* { dg-final { cleanup-saved-temps } } */
-/* { dg-do assemble } */
-/* { dg-options "-fno-sanitize=address -fsanitize=kernel-address --param asan-instrumentation-with-call-threshold=1 -save-temps" } */
+/* { dg-do compile } */
+/* { dg-options "--param asan-instrumentation-with-call-threshold=1" } */
int x;
/* { dg-final { scan-assembler-not "__asan_report_store4" } } */
/* { dg-final { scan-assembler "__asan_load4" } } */
/* { dg-final { scan-assembler-not "__asan_report_load4" } } */
-/* { dg-final { cleanup-saved-temps } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "--param asan-instrumentation-with-call-threshold=0" } */
+
+struct A {
+ char x[7];
+};
+
+void f(struct A *x, struct A *y) {
+ *x = *y;
+}
+
+/* { dg-final { scan-assembler "__asan_loadN" } } */
+/* { dg-final { scan-assembler "__asan_storeN" } } */
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fno-sanitize=address -fsanitize=kernel-address --param asan-instrumentation-with-call-threshold=100" } */
+
+void
+foo (int *p)
+{
+ *p = 0;
+}
+
+/* { dg-final { scan-assembler "__asan_report_store4_noabort" } } */
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fno-sanitize=address -fsanitize=kernel-address" } */
+
+void
+foo (int *p)
+{
+ *p = 0;
+}
+
+/* { dg-final { scan-assembler "__asan_store4_noabort" } } */
+
}
/* { dg-output "ERROR: AddressSanitizer: stack-buffer-overflow.*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*(interceptor_|)memcmp |\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #1 0x\[0-9a-f\]+ (in _*main|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)memcmp|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-do run { target { ilp32 || lp64 } } } */
/* { dg-options "-O2" } */
+/* { dg-additional-options "-fno-omit-frame-pointer" { target *-*-darwin* } } */
/* { dg-shouldfail "asan" } */
struct S { int i; } __attribute__ ((packed));
/* { dg-output "ERROR: AddressSanitizer:\[^\n\r]*on address\[^\n\r]*" } */
/* { dg-output "0x\[0-9a-f\]+ at pc 0x\[0-9a-f\]+ bp 0x\[0-9a-f\]+ sp 0x\[0-9a-f\]+\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*READ of size 4 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*foo(\[^\n\r]*misalign-1.c:10|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*misalign-1.c:34|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*foo(\[^\n\r]*misalign-1.c:1\[01]|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*misalign-1.c:3\[45]|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-do run { target { ilp32 || lp64 } } } */
/* { dg-options "-O2" } */
+/* { dg-additional-options "-fno-omit-frame-pointer" { target *-*-darwin* } } */
/* { dg-shouldfail "asan" } */
struct S { int i; } __attribute__ ((packed));
/* { dg-output "ERROR: AddressSanitizer:\[^\n\r]*on address\[^\n\r]*" } */
/* { dg-output "0x\[0-9a-f\]+ at pc 0x\[0-9a-f\]+ bp 0x\[0-9a-f\]+ sp 0x\[0-9a-f\]+\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*READ of size 4 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*baz(\[^\n\r]*misalign-2.c:22|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*misalign-2.c:34|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*baz(\[^\n\r]*misalign-2.c:2\[23]|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*misalign-2.c:3\[45]|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
-/* { dg-do assemble } */
-/* { dg-options "-save-temps --param asan-globals=0" } */
+/* { dg-do compile } */
+/* { dg-options "--param asan-globals=0" } */
volatile int ten = 10;
}
/* { dg-final { scan-assembler-not "__asan_register_globals" } } */
-/* { dg-final { cleanup-saved-temps } } */
-/* { dg-do assemble } */
-/* { dg-options "--param asan-instrument-reads=0 -save-temps" } */
+/* { dg-do compile } */
+/* { dg-options "--param asan-instrument-reads=0" } */
volatile int ten = 10;
}
/* { dg-final { scan-assembler-not "__asan_load" } } */
-/* { dg-final { cleanup-saved-temps } } */
-/* { dg-do assemble } */
-/* { dg-options "--param asan-instrument-writes=0 -save-temps" } */
+/* { dg-do compile } */
+/* { dg-options "--param asan-instrument-writes=0" } */
volatile int ten = 10;
}
/* { dg-final { scan-assembler-not "__asan_store" } } */
-/* { dg-final { cleanup-saved-temps } } */
/* { dg-do compile } */
/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
-extern char tab[4];
+extern char tab[6];
static int
test0 ()
the initialization. */
foo[i] = 1;
- /*__builtin___asan_report_store_n called once here to instrument
- the store to the memory region of tab. */
+ /* Instrument tab memory region. */
__builtin_memset (tab, 3, sizeof (tab));
- /* There is no instrumentation for the two memset calls below. */
- __builtin_memset (tab, 4, sizeof (tab));
- __builtin_memset (tab, 5, sizeof (tab));
-
- /* There is a call to __builtin___asan_report_store_n and a call
- to __builtin___asan_report_load_n to instrument the store to
- (subset of) the memory region of tab. */
+ /* Instrument tab[1] with access size 3. */
__builtin_memcpy (&tab[1], foo + i, 3);
/* This should not generate a __builtin___asan_report_load1 because
}
/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 3 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 2 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load" 1 "sanopt" } } */
+/* { dg-final { scan-tree-dump-not "__builtin___asan_report_load1" "sanopt" } } */
/* { dg-final { cleanup-tree-dump "sanopt" } } */
--- /dev/null
+/* { dg-options "-fdump-tree-sanopt" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+extern __UINT32_TYPE__ a;
+
+void
+foo ()
+{
+ /* Instrument a with access size 3. */
+ int d = __builtin_memcmp (&a, "123", 3);
+ /* This should generate a __builtin___asan_report_store4, because
+ the reference to a has been instrumented above with access size 3. */
+ a = 1;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store4" 1 "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
--- /dev/null
+/* { dg-options "-fdump-tree-sanopt" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+extern __UINT32_TYPE__ a;
+
+void
+foo ()
+{
+ /* Instrument a with access size 5. */
+ int d = __builtin_memcmp (&a, "12345", 4);
+ /* This should not generate a __builtin___asan_report_store4 because
+ the reference to a has been already instrumented above with access
+ size 5. */
+ a = 1;
+}
+
+/* { dg-final { scan-tree-dump-not "& 7" "sanopt" } } */
+/* { dg-final { scan-tree-dump-not "__builtin___asan_report_store" "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
--- /dev/null
+/* { dg-options "-fdump-tree-sanopt" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+void
+foo (char *p)
+{
+ volatile int zero = 0;
+ __builtin_memcpy (p, "abc", zero);
+ /* This generates a __builtin___asan_report_store1 because we pass volatile
+ zero length into memcpy. */
+ p[0] = 'd';
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 1 "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
--- /dev/null
+/* { dg-options "-fdump-tree-sanopt" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+void
+foo (char *p)
+{
+ __builtin_memcpy (p, "abc", 0);
+ /* This generates a __builtin___asan_report_store1 because we didn't access
+ any byte in previous memcpy because of zero length parameter. */
+ p[0] = 'd';
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 1 "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
--- /dev/null
+/* { dg-options "-fdump-tree-sanopt" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+void
+foo (char *p)
+{
+ __builtin_memcpy (p, "abc", 2);
+ /* This doesn't generate a __builtin___asan_report_store1 because we
+ verified p[0] through p[2] is writable in previous memcpy call. */
+ p[0] = 'd';
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin___asan_report_store1" "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
--- /dev/null
+/* { dg-options "-fdump-tree-sanopt" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+void
+foo (char *p)
+{
+ volatile int two = 2;
+ __builtin_memcpy (p, "abc", two);
+ /* This generates a __builtin___asan_report_store1 because we don't
+ optimize previous memcpy call. */
+ p[0] = 'd';
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 1 "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
+++ /dev/null
-/* This tests that when faced with two references to the same memory
- location in the same basic block, the second reference should not
- be instrumented by the Address Sanitizer. But in case of access to
- overlapping regions we must be precise. */
-
-/* { dg-options "-fdump-tree-sanopt" } */
-/* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
-
-int
-main ()
-{
- char tab[5];
-
- /* Here, we instrument the access at offset 0 and access at offset
- 4. */
- __builtin_memset (tab, 1, sizeof (tab));
- /* We instrumented access at offset 0 above already, so only access
- at offset 3 is instrumented. */
- __builtin_memset (tab, 1, 3);
-}
-
-/* { dg-final { scan-tree-dump-times "& 7" 3 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 2 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report" 2 "sanopt" } } */
-/* { dg-final { cleanup-tree-dump "sanopt" } } */
void
foo (int *a, char *b, char *c)
{
- /* One check for c[0], one check for a[], one check for c, two checks for b. */
+ /* One check for c[0], one check for a[]. */
__builtin_memmove (c, b, a[c[0]]);
- /* For a total of 5 checks. */
+ /* For a total of 2 checks. */
+ int d = c[0] == 1;
}
-/* { dg-final { scan-tree-dump-times "& 7" 5 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 2 "sanopt" } } */
/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 1 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 1 "sanopt" } } */
/* { dg-final { cleanup-tree-dump "sanopt" } } */
void
foo (int *a, char *b, char *c)
{
- /* One check for b[0], one check for a[], 2 checks for c and one checks for b. */
- __builtin_memmove (c, b, a[b[0]]);
- /* For a total of 5 checks. */
+ /* One check for a[]. */
+ __builtin_memmove (c, b, a[0]);
+ /* For a total of 1 checks. */
+ int d = a[0] == 0;
}
-/* { dg-final { scan-tree-dump-times "& 7" 5 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 1 "sanopt" } } */
/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 1 "sanopt" } } */
/* { dg-final { cleanup-tree-dump "sanopt" } } */
void
foo (int *a, char *b, char *c)
{
- /* One check for c[0], one check for a[], one check for c and 2 checks for b. */
+ /* One check for c[0], one check for a[]. */
__builtin_memmove (c, b, a[c[0]]);
- /* One check for a[], one check for c and one check for b. */
+ /* One check for b[0], one check for a[]. */
__builtin_memmove (c, b, a[b[0]]);
- /* For a total of 8 checks. */
+ /* For a total of 4 checks. */
+ int d = c[0] == b[0];
}
-/* { dg-final { scan-tree-dump-times "& 7" 8 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 4 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 2 "sanopt" } } */
/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 2 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 2 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 2 "sanopt" } } */
/* { dg-final { cleanup-tree-dump "sanopt" } } */
/* { dg-do compile } */
/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
-char e[200];
+char e[5];
-struct S
+extern struct S
{
- char a[100];
- char b[100];
+ int a;
+ char b;
} s;
int
foo (int *a, char *b, char *c)
{
- /* 2 checks for s.a, 2 checks for e. */
- int d = __builtin_memcmp (s.a, e, 100);
- /* One check for s.a and one check for e. */
- d += __builtin_memcmp (s.a, e, 200);
- /* For a total of 6 checks. */
- return d;
+ int d = __builtin_memcmp (&s.a, e, 4);
+ /* No check because s.a was instrumented above with access size 4. */
+ return s.a;
}
-/* { dg-final { scan-tree-dump-times "& 7" 6 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 4 "sanopt" } } */
-/* { dg-final { scan-tree-dump-not "__builtin___asan_report_store" "sanopt" } } */
+/* { dg-final { scan-tree-dump-not "& 7" "sanopt" } } */
+/* { dg-final { scan-tree-dump-not "__builtin___asan_report_load4" "sanopt" } } */
/* { dg-final { cleanup-tree-dump "sanopt" } } */
char
foo (int *a, char *b, char *c)
{
- /* One check for b[0], one check for a[], two checks for c and one check for b. */
+ /* One check for b[0], one check for a[]. */
__builtin_memmove (c, b, a[b[0]]);
+ /* One check for c[0], one check for a[]. */
+ __builtin_memmove (b, c, a[c[0]]);
/* No checks here. */
return c[0] + b[0];
- /* For a total of 5 checks. */
+ /* For a total of 4 checks. */
}
-/* { dg-final { scan-tree-dump-times "& 7" 5 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 1 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 4 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 2 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 2 "sanopt" } } */
/* { dg-final { cleanup-tree-dump "sanopt" } } */
+++ /dev/null
-/* { dg-options "-fdump-tree-sanopt" } */
-/* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
-
-__SIZE_TYPE__
-f (char *a)
-{
- a[0] = '1';
- return __builtin_strlen (a);
-}
-
-/* { dg-final { scan-tree-dump-times "__asan_report_load1" 1 "sanopt" } } */
-/* { dg-final { cleanup-tree-dump "sanopt" } } */
-/* { dg-do assemble } */
-/* { dg-options "--param asan-use-after-return=0 -save-temps" } */
+/* { dg-do compile } */
+/* { dg-options "--param asan-use-after-return=0" } */
extern void f(char *);
}
/* { dg-final { scan-assembler-not "__asan_option_detect_stack_use_after_return" } } */
-/* { dg-final { cleanup-saved-temps } } */
/* { dg-output "ERROR: AddressSanitizer:? SEGV on unknown address\[^\n\r]*" } */
/* { dg-output "0x\[0-9a-f\]+ \[^\n\r]*pc 0x\[0-9a-f\]+\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in \[^\n\r]*NullDeref\[^\n\r]* (\[^\n\r]*null-deref-1.c:10|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*null-deref-1.c:15|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]* #0 0x\[0-9a-f\]+ +(in \[^\n\r]*NullDeref\[^\n\r]* (\[^\n\r]*null-deref-1.c:10|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*null-deref-1.c:15|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
--- /dev/null
+/* PR sanitizer/63316 */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=address -O2" } */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void *malloc (__SIZE_TYPE__);
+extern void free (void *);
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+ int *p = (int *) malloc (sizeof (int));
+ *p = 3;
+ asm volatile ("" : : "r" (p) : "memory");
+ free (p);
+ return 0;
+}
--- /dev/null
+/* PR bootstrap/63888 */
+/* { dg-do run } */
+
+__attribute__((noinline, noclone)) int
+foo (int x)
+{
+ int v = 0;
+ switch (x)
+ {
+ case 11: v = 67; break;
+ case 12: v = 68; break;
+ case 13: v = 69; break;
+ }
+ return v;
+}
+
+__attribute__((noinline, noclone)) int
+bar (int x)
+{
+ int v = 0;
+ switch (x)
+ {
+ case 18: v = 67; break;
+ case 19: v = 68; break;
+ case 20: v = 69; break;
+ }
+ return v;
+}
+
+int
+main ()
+{
+ return foo (11) - 67 + bar (19) - 68;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-require-effective-target fstack_protector } */
+/* { dg-options "-fstack-protector-strong" } */
+/* { dg-shouldfail "asan" } */
+
+#ifdef __cplusplus
+extern "C"
+#endif
+const char *
+__asan_default_options ()
+{
+ return "detect_stack_use_after_return=1";
+}
+
+__attribute__((noinline))
+char *Ident(char *x) {
+ return x;
+}
+
+__attribute__((noinline))
+char *Func1() {
+ char local[1 << 12];
+ return Ident(local);
+}
+
+__attribute__((noinline))
+void Func2(char *x) {
+ *x = 1;
+}
+int main(int argc, char **argv) {
+ Func2(Func1());
+ return 0;
+}
+
+/* { dg-output "AddressSanitizer: stack-use-after-return on address 0x\[0-9a-f\]+\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "WRITE of size 1 at .* thread T0.*" } */
+/* { dg-output " #0.*(Func2)?.*pr64820.(c:28)?.*" } */
+/* { dg-output "is located in stack of thread T0 at offset.*" } */
+/* { dg-output "\'local\' <== Memory access at offset 32 is inside this variable" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fno-builtin-malloc -fno-builtin-free" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+/* { dg-shouldfail "asan" } */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+struct Simple {
+ int value;
+};
+
+int f(struct Simple simple) {
+ return simple.value;
+}
+
+int main() {
+ struct Simple *psimple = (struct Simple *) malloc(sizeof(struct Simple));
+ psimple->value = 42;
+ free(psimple);
+ printf("%d\n", f(*psimple));
+ return 0;
+}
+
+/* { dg-output "ERROR: AddressSanitizer:? heap-use-after-free on address\[^\n\r]*" } */
+/* { dg-output "0x\[0-9a-f\]+ at pc 0x\[0-9a-f\]+ bp 0x\[0-9a-f\]+ sp 0x\[0-9a-f\]+\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*READ of size 4 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*pr70541.c:21|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*freed by thread T0 here:\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)free|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*pr70541.c:20|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*previously allocated by thread T0 here:\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*pr70541.c:18|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
--- /dev/null
+/* PR sanitizer/70712 */
+/* { dg-do run } */
+
+struct __attribute__((aligned (64))) S
+{
+ char s[4];
+};
+
+struct T
+{
+ char t[8];
+ char u[480];
+
+};
+
+__attribute__((noinline, noclone)) void
+foo (struct T *p, struct S *q)
+{
+ __builtin_memset (p->t, '\0', sizeof (p->t));
+ __builtin_memset (p->u, '\0', sizeof (p->u));
+ __builtin_memset (q->s, '\0', sizeof (q->s));
+}
+
+int
+main ()
+{
+ struct S s;
+ struct T t;
+ foo (&t, &s);
+ asm volatile ("" : : "r" (&t), "r" (&s) : "memory");
+ return 0;
+}
--- /dev/null
+/* This tests aligment propagation to structure elem and
+ abcense of redundant & 7. */
+
+/* { dg-options "-fdump-tree-sanopt" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+struct st {
+ int a;
+ int b;
+ int c;
+} __attribute__((aligned(16)));
+
+int foo (struct st * s_p)
+{
+ return s_p->a;
+}
+
+/* { dg-final { scan-tree-dump-times "& 7" 0 "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
--- /dev/null
+/* This tests aligment propagation to structure elem and
+ abcense of redundant & 7. */
+
+/* { dg-options "-fdump-tree-sanopt" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+struct st {
+ int a;
+ int b;
+ int c;
+} __attribute__((aligned(16)));
+
+int foo (struct st * s_p)
+{
+ return s_p->b;
+}
+
+/* { dg-final { scan-tree-dump-times "& 7" 1 "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fno-sanitize=all" } */
+
+volatile int ten = 10;
+
+int main() {
+ volatile char x[10];
+ x[ten];
+ return 0;
+}
+
+/* { dg-final { scan-assembler-not "__asan_load" } } */
}
/* { dg-output "heap-use-after-free.*(\n|\r\n|\r)" } */
-/* { dg-output " #0 \[^\n\r]*(in _*(interceptor_|)free|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #0 \[^\n\r]*(in _*(interceptor_|wrap_)free|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output " #1 \[^\n\r]*(in _*main (\[^\n\r]*sanity-check-pure-c-1.c:8|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
-/* { dg-output " #0 \[^\n\r]*(in _*(interceptor_|)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #0 \[^\n\r]*(in _*(interceptor_|wrap_)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output " #1 \[^\n\r]*(in _*main (\[^\n\r]*sanity-check-pure-c-1.c:7|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fno-sanitize=address -fsanitize=kernel-address --param asan-instrumentation-with-call-threshold=100 -fasan-shadow-offset=12345 -fdump-tree-sanopt" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int f (int *p)
+{
+ return *p;
+}
+
+/* { dg-final { scan-tree-dump "12345" "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
}
/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*stack-overflow-1.c:16|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*stack-overflow-1.c:16|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*Address 0x\[0-9a-f\]+ is located in stack of thread T0.*(\n|\r\n|\r)" */
/* { dg-output "\[^\n\r]*in main.*stack-overflow-1.c.*(\n|\r\n|\r)" */
#ifdef __cplusplus
extern "C"
#endif
-
-__attribute__((no_sanitize_address, noinline)) __SIZE_TYPE__
-strlen (const char *p) {
-
- __SIZE_TYPE__ n = 0;
- for (; *p; ++n, ++p);
- return n;
-}
+__SIZE_TYPE__
+strlen (const char *p);
int main () {
char *p = &a[0];
asm ("" : "+r"(p));
__asan_poison_memory_region ((char *)&a[1], 1);
- return __builtin_strlen (a);
+ return __builtin_strlen (a) + 1;
}
-/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+ thread T0.*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*strlen-overflow-1.c:26|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output "READ of size 2 at 0x\[0-9a-f\]+ thread T0.*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*strlen-overflow-1.c:20|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*0x\[0-9a-f\]+ is located 0 bytes to the right of global variable" } */
}
/* { dg-output "WRITE of size \[0-9\]* at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*(interceptor_|)strncpy|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*strncpy-overflow-1.c:11|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)strncpy|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*strncpy-overflow-1.c:11|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*0x\[0-9a-f\]+ is located 0 bytes to the right of 9-byte region\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*allocated by thread T0 here:\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*(interceptor_|)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*strncpy-overflow-1.c:10|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*strncpy-overflow-1.c:10|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "ERROR: AddressSanitizer:? heap-use-after-free on address\[^\n\r]*" } */
/* { dg-output "0x\[0-9a-f\]+ at pc 0x\[0-9a-f\]+ bp 0x\[0-9a-f\]+ sp 0x\[0-9a-f\]+\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*READ of size 1 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*use-after-free-1.c:9|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*use-after-free-1.c:9|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*0x\[0-9a-f\]+ is located 5 bytes inside of 10-byte region .0x\[0-9a-f\]+,0x\[0-9a-f\]+\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*freed by thread T0 here:\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*(interceptor_|)free|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*use-after-free-1.c:8|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)free|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*use-after-free-1.c:8|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*previously allocated by thread T0 here:\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #0 0x\[0-9a-f\]+ (in _*(interceptor_|)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
-/* { dg-output " #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*use-after-free-1.c:7|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +(in _*(interceptor_|wrap_|)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output " #1 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*use-after-free-1.c:7|\[^\n\r]*:0)|\[(\])\[^\n\r]*(\n|\r\n|\r)" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=address -fsanitize-sections=.xxx,.yyy -fdump-tree-sanopt" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int x __attribute__((section(".xxx"))) = 1;
+int y __attribute__((section(".yyy"))) = 1;
+int z __attribute__((section(".zzz"))) = 1;
+
+/* { dg-final { scan-tree-dump "__builtin___asan_unregister_globals \\(.*, 2\\);" "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=address -fsanitize-sections=.x* -fdump-tree-sanopt" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int x __attribute__((section(".x1"))) = 1;
+int y __attribute__((section(".x2"))) = 1;
+int z __attribute__((section(".x3"))) = 1;
+
+/* { dg-final { scan-tree-dump "__builtin___asan_unregister_globals \\(.*, 3\\);" "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
+
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=address -fsanitize-sections=.x* -fsanitize-sections=.y* -fdump-tree-sanopt" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int x __attribute__((section(".x1"))) = 1;
+int y __attribute__((section(".x2"))) = 1;
+int z __attribute__((section(".y1"))) = 1;
+
+/* { dg-final { scan-tree-dump "__builtin___asan_unregister_globals \\(.*, 1\\);" "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
+
--- /dev/null
+/* PR c/53874 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch-enum" } */
+
+enum E { A, B, C };
+struct S { enum E e:2; };
+typedef struct S TS;
+
+int
+fn0 (struct S *s)
+{
+ switch (s->e) /* { dg-warning "enumeration value .C. not handled in switch" } */
+ {
+ case A:
+ return 1;
+ case B:
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+int
+fn1 (TS *s)
+{
+ switch (s->e) /* { dg-warning "enumeration value .C. not handled in switch" } */
+ {
+ case A:
+ return 1;
+ case B:
+ return 2;
+ default:
+ return 0;
+ }
+}
--- /dev/null
+/* PR c/61405 */
+/* { dg-do compile } */
+/* { dg-options "-Wswitch" } */
+
+enum E { A, B, C };
+struct S { enum E e:2; };
+typedef struct S TS;
+
+int
+fn0 (struct S *s)
+{
+ switch (s->e) /* { dg-warning "enumeration value .C. not handled in switch" } */
+ {
+ case A:
+ return 1;
+ case B:
+ return 2;
+ }
+}
+
+int
+fn1 (TS *s)
+{
+ switch (s->e) /* { dg-warning "enumeration value .C. not handled in switch" } */
+ {
+ case A:
+ return 1;
+ case B:
+ return 2;
+ }
+}
/* { dg-output "WARNING: ThreadSanitizer: data race.*(\n|\r\n|\r)" } */
/* { dg-output " Read of size 4 at 0x\[0-9a-f\]+ by thread T1 \\(mutexes: write M\[0-9\]\\):.*" } */
/* { dg-output " Previous write of size 4 at 0x\[0-9a-f\]+ by thread T2:.*" } */
-/* { dg-output " Mutex M\[0-9\] created at:.*" } */
+/* { dg-output " Mutex M\[0-9\] \\(0x.*\\) created at:.*" } */
/* { dg-output " #0 pthread_mutex_init.*" } */
/* { dg-output " #1 main (.*mutexset1.c|\\?{2}):\[0-9]+.*" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */
+
+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; };
+struct V { long long a; struct S b; struct T c; struct U u; } v;
+
+__attribute__((noinline, noclone)) void
+f1 (int *p, int *q, char *r, long long *s)
+{
+ *p = *q + *r + *s;
+}
+
+
+__attribute__((noinline, noclone)) int
+f2 (struct S *p)
+{
+ return p->a;
+}
+
+__attribute__((noinline, noclone)) long long
+f3 (struct S *p, int i)
+{
+ return p->c + p->d[1] + p->d[i];
+}
+
+__attribute__((noinline, noclone)) 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;
+}
--- /dev/null
+/* 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)) void
+f1 (int *p, int *q, char *r, long long *s)
+{
+ *p =
+ *q
+ + *r
+ + *s;
+}
+
+
+__attribute__((noinline, noclone)) int
+f2 (struct S *p)
+{
+ return p->a;
+}
+
+__attribute__((noinline, noclone)) long long
+f3 (struct S *p, int i)
+{
+ return p->c
+ + p->d[1]
+ + p->d[i];
+}
+
+__attribute__((noinline, noclone)) 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" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */
+
+int c;
+
+__attribute__((noinline, noclone)) void
+f1 (int *a, char *b)
+{
+ __builtin_memcpy (a, b, sizeof (*a));
+}
+
+__attribute__((noinline, noclone)) void
+f2 (int *a, char *b)
+{
+ __builtin_memcpy (b, a, sizeof (*a));
+}
+
+__attribute__((noinline, noclone)) void
+f3 (char *b)
+{
+ __builtin_memcpy (&c, b, sizeof (c));
+}
+
+__attribute__((noinline, noclone)) void
+f4 (char *b)
+{
+ __builtin_memcpy (b, &c, sizeof (c));
+}
+
+struct T
+{
+ char a;
+ short b;
+ int c;
+ long d;
+ long long e;
+ short f;
+ float g;
+ double h;
+ long double i;
+} __attribute__((packed));
+
+__attribute__((noinline, noclone)) int
+f5 (struct T *p)
+{
+ return p->a + p->b + p->c + p->d + p->e + p->f + p->g + p->h + p->i;
+}
+
+int
+main ()
+{
+ struct S { int a; char b[sizeof (int) + 1]; } s;
+ s.a = 6;
+ f2 (&s.a, &s.b[1]);
+ f1 (&s.a, &s.b[1]);
+ c = s.a + 1;
+ f4 (&s.b[1]);
+ f3 (&s.b[1]);
+ if (c != 7 || s.a != 6)
+ __builtin_abort ();
+ struct U { long long a; long double b; char c; struct T d; } u;
+ __builtin_memset (&u, 0, sizeof (u));
+ if (f5 (&u.d) != 0)
+ __builtin_abort ();
+ return 0;
+}
--- /dev/null
+/* Limit this to known non-strict alignment targets. */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-options "-fsanitize=null,alignment" } */
+
+#include "align-2.c"
+
+/* { 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 "\[^\n\r]*\.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 "\[^\n\r]*\.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 "\[^\n\r]*\.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 "\[^\n\r]*\.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 "\[^\n\r]*\.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 "\[^\n\r]*\.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 "\[^\n\r]*\.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" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fno-sanitize=null -fsanitize=alignment -O2" } */
+/* Check that when optimizing if we know the alignment is right
+ and we are not doing -fsanitize=null instrumentation we don't
+ instrument the alignment check. */
+
+__attribute__((noinline, noclone)) int
+foo (char *p)
+{
+ p = (char *) __builtin_assume_aligned (p, __alignof__(int));
+ int *q = (int *) p;
+ return *q;
+}
+
+/* { dg-final { scan-assembler-not "__ubsan_handle" } } */
--- /dev/null
+/* Limit this to known non-strict alignment targets. */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-options "-O -fsanitize=alignment -fsanitize-recover=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)) int
+foo (struct S *p)
+{
+ volatile int i;
+ i = p->a;
+ i = p->a;
+ i = p->a;
+ i = p->a;
+ return p->a;
+}
+
+int
+main ()
+{
+ if (foo (&v.u.e))
+ __builtin_abort ();
+ return 0;
+}
+
+/* { dg-output "\.c:14:\[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:15:\[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:16:\[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:17:\[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:18:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment" } */
--- /dev/null
+/* Limit this to known non-strict alignment targets. */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-options "-O -fsanitize=alignment -fno-sanitize-recover=alignment -fdump-tree-sanopt-details" } */
+/* { dg-skip-if "" { *-*-* } { "-flto -fno-fat-lto-objects" } } */
+/* { dg-shouldfail "ubsan" } */
+
+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)) int
+foo (struct S *p)
+{
+ volatile int i;
+ i = p->a;
+ i = p->a;
+ i = p->a;
+ i = p->a;
+ return p->a;
+}
+
+int
+main ()
+{
+ if (foo (&v.u.e))
+ __builtin_abort ();
+ return 0;
+}
+
+/* { dg-output "\.c:16:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct S', which requires \[48] byte alignment" } */
+/* { dg-final { scan-tree-dump-times "Optimizing" 4 "sanopt"} } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
--- /dev/null
+/* Limit this to known non-strict alignment targets. */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-options "-O -fsanitize=alignment -fsanitize-undefined-trap-on-error -fdump-tree-sanopt-details" } */
+/* { dg-skip-if "" { *-*-* } { "-flto -fno-fat-lto-objects" } } */
+/* { dg-shouldfail "ubsan" } */
+
+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)) int
+foo (struct S *p)
+{
+ volatile int i;
+ i = p->a;
+ i = p->a;
+ i = p->a;
+ i = p->a;
+ return p->a;
+}
+
+int
+main ()
+{
+ if (foo (&v.u.e))
+ __builtin_abort ();
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "Optimizing" 4 "sanopt"} } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
--- /dev/null
+/* Limit this to known non-strict alignment targets. */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-options "-O2 -fsanitize=alignment -fsanitize-recover=alignment" } */
+
+__attribute__((noinline, noclone)) void
+foo (void *p, const void *q)
+{
+ *(long int *) p = *(const long int *) q;
+}
+
+int
+main ()
+{
+ struct S { long c; char f[64]; char d; char e[2 * sizeof (long)]; char g[64]; } s;
+ __builtin_memset (&s, '\0', sizeof s);
+ foo (&s.e[0], &s.e[sizeof (long)]);
+ return 0;
+}
+
+/* { dg-output "\.c:8:\[0-9]*: \[^\n\r]*load of misaligned address 0x\[0-9a-fA-F]* for type 'const long int', which requires \[48] byte alignment.*" } */
+/* { dg-output "\.c:8:\[0-9]*: \[^\n\r]*store to misaligned address 0x\[0-9a-fA-F]* for type 'long int', which requires \[48] byte alignment" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow" } */
+
+/* Test that we don't instrument functions marked with
+ no_sanitize_undefined attribute. */
+
+#ifndef __cplusplus
+#define bool _Bool
+#endif
+enum A { B = -3, C = 2 } a;
+bool b;
+
+__attribute__((no_sanitize_undefined))
+static void
+vla_bound (void)
+{
+ int i = -1;
+ volatile int a[i];
+}
+
+__attribute__((no_sanitize_undefined))
+static void
+si_overflow (void)
+{
+ int x = 123, y = 267;
+ volatile int z1 = x + y;
+ volatile int z2 = x - y;
+ volatile int z3 = x * y;
+ volatile int z4 = x / y;
+}
+
+__attribute__((no_sanitize_undefined))
+static void
+null (int *p)
+{
+ *p = 42;
+}
+
+__attribute__((no_sanitize_undefined))
+static void
+retrn (int *p)
+{
+ *p = 42;
+}
+
+__attribute__((no_sanitize_undefined))
+static enum A
+bool_enum (bool *p)
+{
+ *p = b;
+ return a;
+}
+
+__attribute__((no_sanitize_undefined))
+static void
+float_zero (void)
+{
+ volatile float a = 4.2f, b = 0.0f, c;
+ c = a / b;
+}
+
+__attribute__((no_sanitize_undefined))
+static void
+float_cast (void)
+{
+ volatile double d = 300;
+ volatile signed char c;
+ c = d;
+}
+
+/* { dg-final { scan-assembler-not "__ubsan_handle" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Test that we don't instrument functions marked with
+ no_sanitize_undefined attribute. */
+
+__attribute__((no_sanitize_undefined, returns_nonnull))
+char *
+foo (char *x)
+{
+ return x;
+}
+
+__attribute__((nonnull)) void bar (char *, int, char *);
+
+__attribute__((no_sanitize_undefined))
+void
+baz (char *x, int y, char *z)
+{
+ bar (x, y, z);
+}
+
+/* { dg-final { scan-assembler-not "__ubsan_handle" } } */
--- /dev/null
+/* { 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_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" } } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -fno-sanitize-recover=bounds -Wall" } */
+
+/* Don't fail on valid uses. */
+
+struct S { int a[10]; };
+struct T { int l; int a[]; };
+struct U { int l; int a[0]; };
+struct V { int l; int a[1]; };
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_a (volatile int a[])
+{
+ /* This is not instrumented. */
+ a[4] = 5;
+}
+
+__attribute__ ((noinline, noclone))
+int
+foo_i (int i)
+{
+ int a[5] = { };
+ int k = i ? a[i] : i;
+ return k;
+}
+
+int
+main (void)
+{
+ volatile int a[5];
+ a[4] = 1;
+ a[2] = a[3];
+ fn_p (a[4]);
+ fn_a (a);
+
+ int i = 4;
+ a[i] = 1;
+ a[2] = a[i];
+ fn_p (a[i]);
+ foo_i (i);
+
+ const int n = 5;
+ volatile int b[n];
+ b[4] = 1;
+ b[2] = b[3];
+ fn_p (b[4]);
+ fn_a (b);
+
+ volatile int c[n][n][n];
+ c[2][2][2] = 2;
+ i = c[4][4][4];
+
+ volatile struct S s;
+ s.a[9] = 1;
+ i = s.a[9];
+
+ /* Don't instrument flexible array members. */
+ struct T *t = (struct T *) __builtin_malloc (sizeof (struct T) + 10);
+ t->a[1] = 1;
+
+ /* Don't instrument zero-sized arrays (GNU extension). */
+ struct U *u = (struct U *) __builtin_malloc (sizeof (struct U) + 10);
+ u->a[1] = 1;
+
+ /* Don't instrument last array in a struct. */
+ struct V *v = (struct V *) __builtin_malloc (sizeof (struct V) + 10);
+ v->a[1] = 1;
+
+ long int *d[10][5];
+ d[9][0] = (long int *) 0;
+ d[8][3] = d[9][0];
+
+ return 0;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds-strict" } */
+
+struct V { int l; int a[1]; };
+
+int
+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\\\]'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+
+struct S
+{
+ unsigned long a[1];
+ int l;
+};
+
+static inline unsigned long
+fn (const struct S *s, int i)
+{
+ return s->a[i] / i;
+}
+
+int
+main ()
+{
+ struct S s;
+ fn (&s, 1);
+}
+
+/* { dg-output "index 1 out of bounds for type 'long unsigned int \\\[1\\\]'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+
+struct S
+{
+ unsigned long a[1];
+ int l;
+};
+
+static inline unsigned long
+fn (const struct S *s, int i)
+{
+ return s->a[i] << i;
+}
+
+int
+main ()
+{
+ struct S s;
+ fn (&s, 1);
+}
+
+/* { dg-output "index 1 out of bounds for type 'long unsigned int \\\[1\\\]'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test runtime errors. */
+
+struct S { int a[10]; };
+struct T { int a[5]; int s[2]; };
+
+int
+foo_5 (void)
+{
+ return 5;
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn_p (int p)
+{
+ (void) p;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn1 (void)
+{
+ volatile int a[5];
+ asm ("" : : "r" (&a) : "memory");
+ a[2] = a[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn2 (void)
+{
+ volatile int a[5];
+ volatile int j;
+ int i = 5;
+ int *p = &i;
+ asm ("" : : "r" (&a) : "memory");
+ j = a[*p];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn3 (void)
+{
+ volatile int a[5];
+ fn_p (a[5]);
+}
+
+static void __attribute__ ((noinline, noclone))
+fn4 (void)
+{
+ struct T t;
+ asm ("" : : "r" (&t.a) : "memory");
+ t.a[foo_5 ()] = 1;
+}
+
+static void __attribute__ ((noinline, noclone))
+fn5 (void)
+{
+ int i = 5;
+ volatile int a[i];
+ asm ("" : : "r" (&a) : "memory");
+ a[2] = a[i];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn6 (void)
+{
+ int i = 5;
+ volatile int a[i];
+ volatile int j;
+ fn_p (a[i]);
+ asm ("" : : "r" (&a) : "memory");
+ j = a[foo_5 ()];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn7 (void)
+{
+ int n = 5;
+ volatile int i;
+ volatile int c[n][n][n];
+ asm ("" : : "r" (&c[5]) : "memory");
+ i = c[5][2][2];
+ asm ("" : : "r" (&c[2]) : "memory");
+ i = c[2][5][2];
+ asm ("" : : "r" (&c[2]) : "memory");
+ i = c[2][2][5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn8 (void)
+{
+ volatile int i;
+ volatile struct S s;
+ asm ("" : : "r" (&s.a) : "memory");
+ i = s.a[10];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn9 (void)
+{
+ long int *volatile d[10][5];
+ asm ("" : : "r" (&d[10]) : "memory");
+ d[8][3] = d[10][0];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn10 (void)
+{
+ /* Beware of side-effects. */
+ volatile int x = 10;
+ volatile int e[20];
+ e[x++] = 3;
+ if (x != 11)
+ __builtin_abort ();
+ e[x--] = 3;
+ if (x != 10)
+ __builtin_abort ();
+}
+
+static void __attribute__ ((noinline, noclone))
+fn11 (void)
+{
+ char ***volatile f[5];
+ asm ("" : : "r" (&f) : "memory");
+ f[2] = f[5];
+}
+
+static void __attribute__ ((noinline, noclone))
+fn12 (int i)
+{
+ volatile int a[5] = { };
+ int k = i ? a[i] : i;
+}
+
+int
+main (void)
+{
+ fn1 ();
+ fn2 ();
+ fn3 ();
+ fn4 ();
+ fn5 ();
+ fn6 ();
+ fn7 ();
+ fn8 ();
+ fn9 ();
+ fn10 ();
+ fn11 ();
+ fn12 (5);
+ return 0;
+}
+
+/* { dg-output "index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]\\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'int \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 10 out of bounds for type 'long int \\\*\\\[10\\\]\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'char \\\*\\\*\\\*\\\[5\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 5 out of bounds for type 'int \\\[5\\\]'" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Do not generate invalid diagnostics. */
+
+extern const int a[10];
+extern int bar (int);
+void
+foo (int i, int j)
+{
+ bar (a[i] >> j);
+ bar ((unsigned long) a[i] >> j);
+ bar ((short int) (unsigned long) a[i] >> j);
+ bar (j >> a[i]);
+ bar (j >> (unsigned long) a[i]);
+ bar (j >> (short int) (unsigned long) a[i]);
+ bar (a[i] / j);
+ bar ((unsigned long) a[i] / j);
+ bar ((short int) (unsigned long) a[i] / j);
+ bar (j / a[i]);
+ bar (j / (unsigned long) a[i]);
+ bar (j / (short int) (unsigned long) a[i]);
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused" } */
+
+/* Initializers of TREE_STATICs aren't instrumented.
+ But don't ICE on 'em. */
+
+int A[2];
+int *gp = &A[4];
+int *gpi;
+
+int
+main (void)
+{
+ gpi = &A[4];
+ static int *pi = &A[4];
+ return 0;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra -Wno-unused -Wno-array-bounds" } */
+
+/* Test flexible array member-like arrays. Normal flexible array members
+ are tested in bounds-1.c. Test non-strict mode. */
+
+__attribute__ ((noinline, noclone))
+void
+fn1 (void)
+{
+ volatile struct S { char a[1]; char b; } s;
+ volatile int i;
+ asm ("" : : "r" (&s.a) : "memory");
+ i = s.a[0]; // OK
+ asm ("" : : "r" (&s.a) : "memory");
+ i = s.a[1]; // error
+ volatile struct S *p = &s;
+ asm ("" : : "r" (&p->a) : "memory");
+ i = p->a[0]; // OK
+ asm ("" : : "r" (&p->a) : "memory");
+ i = p->a[1]; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn2 (void)
+{
+ struct S { int c; char d[4]; };
+ volatile struct T { int e; struct S f; int g; } t;
+ volatile int i;
+ asm ("" : : "r" (&t.f.d) : "memory");
+ i = t.f.d[3]; // OK
+ asm ("" : : "r" (&t.f.d) : "memory");
+ i = t.f.d[4]; // error
+ volatile struct T *p = &t;
+ asm ("" : : "r" (&p->f.d) : "memory");
+ i = p->f.d[3]; // OK
+ asm ("" : : "r" (&p->f.d) : "memory");
+ i = p->f.d[4]; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn3 (void)
+{
+ volatile struct S { char b; char a[1]; } s;
+ volatile int i;
+ asm ("" : : "r" (&s.a) : "memory");
+ i = s.a[0]; // OK
+ asm ("" : : "r" (&s.a) : "memory");
+ i = s.a[1]; // error
+ volatile struct S *p = &s;
+ asm ("" : : "r" (&p->a) : "memory");
+ i = p->a[0]; // OK
+ asm ("" : : "r" (&p->a) : "memory");
+ i = p->a[1]; // error in strict mode
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn4 (void)
+{
+ volatile struct S { char b; char a[1]; } s;
+ volatile struct T { struct S s; int i; } t;
+ volatile int i;
+ asm ("" : : "r" (&t.s.a) : "memory");
+ i = t.s.a[0]; // OK
+ asm ("" : : "r" (&t.s.a) : "memory");
+ i = t.s.a[1]; // error
+ volatile struct T *pt = &t;
+ asm ("" : : "r" (&pt->s.a) : "memory");
+ i = pt->s.a[0]; // OK
+ asm ("" : : "r" (&pt->s.a) : "memory");
+ i = pt->s.a[1]; // error
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn5 (void)
+{
+ volatile struct S { char b; char a[1]; } s;
+ volatile struct U { int a; struct S s; } u;
+ volatile int i;
+ asm ("" : : "r" (&u.s.a) : "memory");
+ i = u.s.a[0]; // OK
+ asm ("" : : "r" (&u.s.a) : "memory");
+ i = u.s.a[1]; // error
+ volatile struct U *pu = &u;
+ asm ("" : : "r" (&pu->s.a) : "memory");
+ i = pu->s.a[0]; // OK
+ asm ("" : : "r" (&pu->s.a) : "memory");
+ i = pu->s.a[1]; // error in strict mode
+}
+
+int
+main (void)
+{
+ fn1 ();
+ fn2 ();
+ fn3 ();
+ fn4 ();
+ fn5 ();
+ return 0;
+}
+
+/* { dg-output "index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'char \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 1 out of bounds for type 'char \\\[1\\\]'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds -Wall -Wextra" } */
+
+/* Test off-by-one. */
+
+struct S { int a; int b; } s[4], *t;
+struct U { int a[10]; } u[4], *v;
+volatile int *a, *b, *c;
+volatile void *d;
+volatile int e[4][4];
+
+int
+main (void)
+{
+ t = &s[4]; // OK
+ a = &s[4].a; // Error
+ b = &s[4].b; // Error
+ d = &e[4]; // OK
+ c = &e[4][0]; // Error
+ c = &e[3][4]; // OK
+ c = &e[3][3]; // OK
+
+ a = &u[4].a[9]; // Error
+ a = &u[4].a[10]; // Error
+ a = &u[3].a[9]; // OK
+ a = &u[3].a[10]; // OK
+ a = &u[3].a[11]; // Error
+
+ return 0;
+}
+
+/* { dg-output "index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'S \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'int \\\[4\\\]\\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'U \\\[4\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'int \\\[10\\\]'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+
+/* Test negative bounds. */
+
+struct S { int a[10]; };
+
+__attribute__ ((noinline, noclone))
+void
+fn1 (void)
+{
+ volatile int i;
+ int m = -1;
+ volatile int a[7];
+ asm ("" : : "r" (&a) : "memory");
+ i = a[-1];
+ asm ("" : : "r" (&a) : "memory");
+ i = a[m];
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn2 (void)
+{
+ volatile int i;
+ int m = 7;
+ volatile int a[m];
+ asm ("" : : "r" (&a) : "memory");
+ i = a[-1];
+}
+
+__attribute__ ((noinline, noclone))
+void
+fn3 (void)
+{
+ volatile int i;
+ volatile struct S s;
+ asm ("" : : "r" (&s.a) : "memory");
+ i = s.a[-1];
+}
+
+int
+main (void)
+{
+ fn1 ();
+ fn2 ();
+ fn3 ();
+ return 0;
+}
+
+/* { dg-output "index -1 out of bounds for type 'int \\\[7\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index -1 out of bounds for type 'int \\\[7\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index -1 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index -1 out of bounds for type 'int \\\[10\\\]'" } */
--- /dev/null
+/* PR sanitizer/65280 */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+
+int
+main (void)
+{
+ int *t = (int *) __builtin_malloc (sizeof (int) * 10);
+ int (*a)[1] = (int (*)[1]) t;
+ (*a)[2] = 1;
+ return 0;
+}
+
+/* { dg-output "index 2 out of bounds for type 'int \\\[1\\\]'" } */
--- /dev/null
+/* PR sanitizer/65280 */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+/* Origin: Martin Uecker <uecker@eecs.berkeley.edu> */
+
+void
+foo (volatile int (*a)[3])
+{
+ (*a)[3] = 1; // error
+ a[0][0] = 1; // ok
+ a[1][0] = 1; // ok
+ a[1][4] = 1; // error
+}
+
+int
+main ()
+{
+ volatile int a[20];
+ foo ((volatile int (*)[3]) &a);
+ return 0;
+}
+
+/* { dg-output "index 3 out of bounds for type 'int \\\[3\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*index 4 out of bounds for type 'int \\\[3\\\]'" } */
return 0;
}
-/* { dg-output "division by zero(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division by zero" } */
+/* { 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]*" } */
return 0;
}
-/* { dg-output "division by zero(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division by zero(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division by zero" } */
+/* { 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]*" } */
return 0;
}
-/* { dg-output "division of -2147483648 by -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented in type 'int'" } */
+/* { dg-output "division of -2147483648 by -1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented in type 'int'\[^\n\r]*" } */
-/* { dg-do compile} */
+/* { dg-do compile } */
/* { dg-options "-fsanitize=integer-divide-by-zero" } */
void
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-div-by-zero" } */
+
+#include <stdio.h>
+
+int x;
+
+__attribute__((noinline, noclone))
+void
+barrier (void)
+{
+ asm volatile ("" : : : "memory");
+ if (x)
+ __builtin_exit (1);
+}
+
+int
+main (void)
+{
+ volatile int a = 0;
+ volatile long long int b = 0;
+ volatile unsigned int c = 1;
+
+ barrier (); fputs ("1st\n", stderr); barrier ();
+ a / b;
+ barrier (); fputs ("2nd\n", stderr); barrier ();
+ 0 / 0;
+ barrier (); fputs ("3rd\n", stderr); barrier ();
+ a / 0;
+ barrier (); fputs ("4th\n", stderr); barrier ();
+ 0 / b;
+ barrier (); fputs ("5th\n", stderr); barrier ();
+ 2 / --c;
+ barrier (); fputs ("6th\n", stderr); barrier ();
+
+ return 0;
+}
+
+/* { dg-output "1st(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "2nd(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "3rd(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "4th(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "5th(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "6th" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=integer-divide-by-zero -Wno-div-by-zero -fno-sanitize-recover=integer-divide-by-zero" } */
+/* { dg-shouldfail "ubsan" } */
+
+#include <stdio.h>
+
+int x;
+
+__attribute__((noinline, noclone))
+void
+barrier (void)
+{
+ asm volatile ("" : : : "memory");
+ if (++x == 3)
+ __builtin_exit (0);
+}
+
+int
+main (void)
+{
+ volatile int a = 0;
+ volatile long long int b = 0;
+ volatile unsigned int c = 1;
+
+ barrier (); fputs ("1st\n", stderr); barrier ();
+ a / b;
+ barrier (); fputs ("2nd\n", stderr); barrier ();
+ 0 / 0;
+ barrier (); fputs ("3rd\n", stderr); barrier ();
+ a / 0;
+ barrier (); fputs ("4th\n", stderr); barrier ();
+ 0 / b;
+ barrier (); fputs ("5th\n", stderr); barrier ();
+ 2 / --c;
+ barrier (); fputs ("6th\n", stderr); barrier ();
+
+ return 0;
+}
+
+/* { dg-output "1st(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division by zero" } */
--- /dev/null
+/* { 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
+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'" } */
--- /dev/null
+/* { dg-do run { target dfp } } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=float-cast-overflow -fsanitize-recover=float-cast-overflow" } */
+/* { dg-additional-options "-DUSE_INT128" { target int128 } } */
+/* FIXME: When _DecimalXX <-> {signed, unsigned} __int128 conversions are
+ supported, -DBROKEN_DECIMAL_INT128 can be removed. */
+/* { dg-additional-options "-DUSE_DFP -DBROKEN_DECIMAL_INT128" } */
+
+#include "float-cast-overflow-8.c"
+
+/* _Decimal32 */
+/* { dg-output "value <unknown> is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* _Decimal64 */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* _Decimal128 */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-require-effective-target int128 } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#include "float-cast.h"
+
+int
+main (void)
+{
+ const double inf = __builtin_inf ();
+ const double nan = __builtin_nan ("");
+ volatile double d;
+
+ __int128 i;
+ d = INT128_MIN;
+ CHECK_BOUNDARY (i, d);
+ d = 0.0;
+ CHECK_BOUNDARY (i, d);
+ d = INT128_MAX;
+ CHECK_BOUNDARY (i, d);
+ CHECK_NONNUMBERS (i);
+
+ unsigned __int128 u;
+ d = UINT128_MAX;
+ CHECK_BOUNDARY (u, d);
+ d = 0.0;
+ CHECK_BOUNDARY (u, d);
+ CHECK_NONNUMBERS (u);
+
+ return 0;
+}
+
+/* { dg-output "runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value nan is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -?nan is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value inf is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -inf is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -5 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -1.5 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value nan is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -?nan is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value inf is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -inf is outside the range of representable values of type '__int128 unsigned'" } */
--- /dev/null
+/* { dg-do run { target { lp64 || ilp32 } } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#include <limits.h>
+#include "float-cast.h"
+
+int
+main (void)
+{
+ volatile float f;
+
+ volatile signed char s;
+ f = SCHAR_MIN;
+ CHECK_BOUNDARY (s, f);
+ f = 0.0;
+ CHECK_BOUNDARY (s, f);
+ f = SCHAR_MAX;
+ CHECK_BOUNDARY (s, f);
+
+ volatile unsigned char u;
+ f = UCHAR_MAX;
+ CHECK_BOUNDARY (u, f);
+ f = 0.0;
+ CHECK_BOUNDARY (u, f);
+
+ return 0;
+}
+
+/* { dg-output "value -133* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 132 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 260 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type" } */
--- /dev/null
+/* { dg-do run { target { lp64 } } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#include <limits.h>
+#include "float-cast.h"
+
+int
+main (void)
+{
+ const long double inf = __builtin_infl ();
+ const long double nan = __builtin_nanl ("");
+ volatile long double ld;
+
+ volatile int i;
+ ld = INT_MIN;
+ CHECK_BOUNDARY (i, ld);
+ ld = 0.0l;
+ CHECK_BOUNDARY (i, ld);
+ ld = INT_MAX;
+ CHECK_BOUNDARY (i, ld);
+ CHECK_NONNUMBERS (i);
+
+ volatile unsigned int u;
+ ld = UINT_MAX;
+ CHECK_BOUNDARY (u, ld);
+ ld = 0.0l;
+ CHECK_BOUNDARY (u, ld);
+ CHECK_NONNUMBERS (u);
+
+ return 0;
+}
+
+/* { dg-output "value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -?nan is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type" } */
--- /dev/null
+/* { dg-do run { target i?86-*-* x86_64-*-* ia64-*-* } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#include <limits.h>
+#include "float-cast.h"
+
+int
+main (void)
+{
+ volatile __float128 f;
+
+ volatile signed char s;
+ f = SCHAR_MIN;
+ CHECK_BOUNDARY (s, f);
+ f = 0.0q;
+ CHECK_BOUNDARY (s, f);
+ f = SCHAR_MAX;
+ CHECK_BOUNDARY (s, f);
+
+ volatile unsigned char u;
+ f = UCHAR_MAX;
+ CHECK_BOUNDARY (u, f);
+ f = 0.0q;
+ CHECK_BOUNDARY (u, f);
+
+ return 0;
+}
+
+/* { dg-output "value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type" } */
--- /dev/null
+/* { dg-do run { target { { x86_64-*-* ia64-*-* } && { ! { ia32 } } } } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#include <limits.h>
+#include "float-cast.h"
+
+int
+main (void)
+{
+ volatile __float80 f;
+
+ volatile signed char s;
+ f = SCHAR_MIN;
+ CHECK_BOUNDARY (s, f);
+ f = 0.0w;
+ CHECK_BOUNDARY (s, f);
+ f = SCHAR_MAX;
+ CHECK_BOUNDARY (s, f);
+
+ volatile unsigned char u;
+ f = UCHAR_MAX;
+ CHECK_BOUNDARY (u, f);
+ f = 0.0w;
+ CHECK_BOUNDARY (u, f);
+
+ return 0;
+}
+
+/* { dg-output "value -133 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 132 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 260 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=float-cast-overflow -fno-sanitize-recover=float-cast-overflow" } */
+/* FIXME: When _DecimalXX <-> {signed, unsigned} __int128 conversions are
+ supported, -DBROKEN_DECIMAL_INT128 can be removed. */
+/* { dg-additional-options "-DUSE_DFP -DBROKEN_DECIMAL_INT128" { target dfp } } */
+
+#define USE_FLT_DBL_LDBL
+#ifdef __SIZEOF_INT128__
+#define USE_INT128
+#endif
+#ifdef __SIZEOF_FLOAT80__
+#define USE_FLOAT80
+#endif
+#ifdef __SIZEOF_FLOAT128__
+#define USE_FLOAT128
+#endif
+
+#include "float-cast-overflow-7.h"
+
+#define TEST(type1, type2) \
+ if (cvt_##type1##_##type2 (-0.5f) != 0) abort (); \
+ if (cvt_##type1##_##type2 (0.5f) != 0) abort (); \
+ if (cvt_##type1##_##type2 (-0.75f) != 0) abort (); \
+ if (cvt_##type1##_##type2 (0.75f) != 0) abort (); \
+ if (type1##_MIN) \
+ { \
+ /* For RADIX 2 type1##_MIN should be always */ \
+ /* exactly representable in type2. */ \
+ if (type2##_RADIX == 2 \
+ || type1##_MAX <= type2##_MAX) \
+ { \
+ if (cvt_##type1##_##type2 (type1##_MIN) \
+ != type1##_MIN) abort (); \
+ volatile type2 tem = ((type2) -0.75f) + type1##_MIN; \
+ volatile type2 tem2 = ((type2) -1.0f) + type1##_MIN; \
+ if (tem != tem2 \
+ && cvt_##type1##_##type2 ((type2) -0.75f \
+ + type1##_MIN) \
+ != type1##_MIN) abort (); \
+ } \
+ else \
+ { \
+ type2 min = type1##_MIN; \
+ /* tem could be below minimum here due to */ \
+ /* rounding. */ \
+ MAXT add = 1; \
+ while (add) \
+ { \
+ volatile type2 tem = type1##_MIN + (type1) add; \
+ if (tem != min) \
+ break; \
+ MAXT newadd = add * type2##_RADIX; \
+ if (newadd < add || newadd > type1##_MAX) \
+ add = 0; \
+ else \
+ add = newadd; \
+ } \
+ if (add) \
+ { \
+ MAXT newadd \
+ = (-(type1##_MIN + (type1) add)) % add; \
+ volatile type2 tem = type1##_MIN + (type1) newadd;\
+ volatile type2 tem2 = type1##_MIN + (type1) add; \
+ if (tem == tem2) \
+ add = newadd; \
+ else \
+ { \
+ newadd += add; \
+ if (newadd < add || newadd > type1##_MAX) \
+ add = 0; \
+ else \
+ { \
+ tem = type1##_MIN + (type1) newadd; \
+ if (tem == tem2) \
+ add = newadd; \
+ else \
+ add = 0; \
+ } \
+ } \
+ } \
+ if (add \
+ && cvt_##type1##_##type2 (type1##_MIN \
+ + (type1) add) \
+ != type1##_MIN + (type1) add) abort (); \
+ } \
+ } \
+ if (type1##_MAX <= type2##_MAX) \
+ { \
+ if (cvt_##type1##_##type2 (type1##_MAX) != type1##_MAX) \
+ abort (); \
+ volatile type2 tem = ((type2) 0.75f) + type1##_MAX; \
+ volatile type2 tem2 = ((type2) 1.0f) + type1##_MAX; \
+ if (tem < tem2 \
+ && cvt_##type1##_##type2 ((type2) 0.75f + type1##_MAX)\
+ != type1##_MAX) abort (); \
+ } \
+ else \
+ { \
+ type2 max = type1##_MAX; \
+ /* tem could be above maximum here due to rounding. */ \
+ MAXT sub = 1; \
+ while (sub) \
+ { \
+ volatile type2 tem = type1##_MAX - sub; \
+ if (tem != max) \
+ break; \
+ MAXT newsub = sub * type2##_RADIX; \
+ if (newsub < sub || newsub > type1##_MAX) \
+ sub = 0; \
+ else \
+ sub = newsub; \
+ } \
+ if (sub) \
+ { \
+ MAXT newsub = ((type1##_MAX - sub) % sub); \
+ volatile type2 tem = type1##_MAX - newsub; \
+ volatile type2 tem2 = type1##_MAX - sub; \
+ if (tem == tem2) \
+ sub = newsub; \
+ else \
+ { \
+ newsub += sub; \
+ if (newsub < sub || newsub > type1##_MAX) \
+ sub = 0; \
+ else \
+ { \
+ tem = type1##_MAX - newsub; \
+ if (tem == tem2) \
+ sub = newsub; \
+ else \
+ sub = 0; \
+ } \
+ } \
+ } \
+ if (sub \
+ && cvt_##type1##_##type2 (type1##_MAX - sub) \
+ != type1##_MAX - sub) abort (); \
+ }
+
+
+#ifdef si128_MAX
+# define TESTS128(type2) TEST (si128, type2) TEST (ui128, type2)
+#else
+# define TESTS128(type2)
+#endif
+
+#define TESTS(type2) \
+ TEST (sc, type2) TEST (c, type2) TEST (uc, type2) \
+ TEST (ss, type2) TEST (us, type2) \
+ TEST (si, type2) TEST (ui, type2) \
+ TEST (sl, type2) TEST (ul, type2) \
+ TEST (sll, type2) TEST (ull, type2) \
+ TESTS128 (type2)
+
+int
+main ()
+{
+#ifdef f_MAX
+ TESTS (f)
+#endif
+#ifdef d_MAX
+ TESTS (d)
+#endif
+#ifdef ld_MAX
+ TESTS (ld)
+#endif
+#ifdef f80_MAX
+ TESTS (f80)
+#endif
+#ifdef f128_MAX
+ TESTS (f128)
+#endif
+#ifdef BROKEN_DECIMAL_INT128
+# undef TESTS128
+# define TESTS128(type2)
+# undef TWO
+# undef M1U
+# undef MAXS
+# undef MAXT
+# define TWO 2ULL
+# define M1U -1ULL
+# define MAXS (__CHAR_BIT__ * __SIZEOF_LONG_LONG__)
+# define MAXT unsigned long long
+#endif
+#ifdef d32_MAX
+ TESTS (d32)
+#endif
+#ifdef d64_MAX
+ TESTS (d64)
+#endif
+#ifdef d128_MAX
+ TESTS (d128)
+#endif
+ return 0;
+}
--- /dev/null
+#define CVTFN(type1, type2) \
+__attribute__((noinline)) type1 \
+cvt_##type1##_##type2 (type2 x) \
+{ \
+ return x; \
+}
+
+typedef signed char sc;
+#define sc_MIN (-__SCHAR_MAX__ - 1)
+#define sc_MAX __SCHAR_MAX__
+typedef unsigned char uc;
+#define uc_MIN 0
+#define uc_MAX (2U * __SCHAR_MAX__ + 1U)
+typedef char c;
+#define c_MIN ((((char) -1) > (char) 0) ? uc_MIN : sc_MIN)
+#define c_MAX ((((char) -1) > (char) 0) ? uc_MAX : sc_MAX)
+typedef signed short ss;
+#define ss_MIN (-__SHRT_MAX__ - 1)
+#define ss_MAX __SHRT_MAX__
+typedef unsigned short us;
+#define us_MIN 0
+#define us_MAX (2U * __SHRT_MAX__ + 1U)
+typedef signed int si;
+#define si_MIN (-__INT_MAX__ - 1)
+#define si_MAX __INT_MAX__
+typedef unsigned int ui;
+#define ui_MIN 0
+#define ui_MAX (2U * __INT_MAX__ + 1U)
+typedef signed long sl;
+#define sl_MIN (-__LONG_MAX__ - 1L)
+#define sl_MAX __LONG_MAX__
+typedef unsigned long ul;
+#define ul_MIN 0L
+#define ul_MAX (2UL * __LONG_MAX__ + 1UL)
+typedef signed long long sll;
+#define sll_MIN (-__LONG_LONG_MAX__ - 1LL)
+#define sll_MAX __LONG_LONG_MAX__
+typedef unsigned long long ull;
+#define ull_MIN 0LL
+#define ull_MAX (2ULL * __LONG_LONG_MAX__ + 1ULL)
+#ifdef USE_INT128
+typedef signed __int128 si128;
+# define si128_MAX \
+ ((signed __int128) ((((unsigned __int128) 1) \
+ << (__CHAR_BIT__ * __SIZEOF_INT128__ - 1)) - 1))
+# define si128_MIN (-si128_MAX - 1)
+typedef unsigned __int128 ui128;
+#define ui128_MIN ((unsigned __int128) 0)
+#define ui128_MAX (((unsigned __int128) 2) * si128_MAX + 1)
+#endif
+
+#ifdef si128_MAX
+# define CVTS128(type2) CVTFN (si128, type2) CVTFN (ui128, type2)
+#else
+# define CVTS128(type2)
+#endif
+
+#define CVTS(type2) \
+ CVTFN (sc, type2) CVTFN (c, type2) CVTFN (uc, type2) \
+ CVTFN (ss, type2) CVTFN (us, type2) \
+ CVTFN (si, type2) CVTFN (ui, type2) \
+ CVTFN (sl, type2) CVTFN (ul, type2) \
+ CVTFN (sll, type2) CVTFN (ull, type2) \
+ CVTS128 (type2)
+
+#ifdef __SIZEOF_INT128__
+# define TWO ((unsigned __int128) 2)
+# define M1U ((unsigned __int128) -1)
+# define MAXS (__CHAR_BIT__ * __SIZEOF_INT128__)
+# define MAXT unsigned __int128
+#else
+# define TWO 2ULL
+# define M1U -1ULL
+# define MAXS (__CHAR_BIT__ * __SIZEOF_LONG_LONG__)
+# define MAXT unsigned long long
+#endif
+
+#ifdef USE_FLT_DBL_LDBL
+typedef float f;
+#define f_RADIX 2
+#define f_MANT_DIG __FLT_MANT_DIG__
+#define f_MAX ((TWO << (f_MANT_DIG - 1)) - 1)
+typedef double d;
+#define d_RADIX 2
+#define d_MANT_DIG __DBL_MANT_DIG__
+#define d_MAX ((TWO << (d_MANT_DIG - 1)) - 1)
+typedef long double ld;
+#define ld_RADIX 2
+#define ld_MANT_DIG __LDBL_MANT_DIG__
+#define ld_MAX \
+ (ld_MANT_DIG > MAXS ? M1U : (TWO << (ld_MANT_DIG > MAXS \
+ ? 0 : ld_MANT_DIG - 1)) - 1)
+CVTS (f)
+CVTS (d)
+CVTS (ld)
+#endif
+#ifdef USE_FLOAT80
+typedef __float80 f80;
+# define f80_RADIX 2
+# define f80_MANT_DIG 64
+# define f80_MAX ((TWO << (f80_MANT_DIG - 1)) - 1)
+CVTS (f80)
+#endif
+#ifdef USE_FLOAT128
+typedef __float128 f128;
+# define f128_RADIX 2
+# define f128_MANT_DIG 113
+# define f128_MAX \
+ (f128_MANT_DIG > MAXS ? M1U : (TWO << (f128_MANT_DIG > MAXS \
+ ? 0 : f128_MANT_DIG - 1)) - 1)
+CVTS (f128)
+#endif
+#ifdef USE_DFP
+# ifdef __cplusplus
+typedef float _Decimal32 __attribute__((mode(SD)));
+typedef float _Decimal64 __attribute__((mode(DD)));
+typedef float _Decimal128 __attribute__((mode(TD)));
+# endif
+typedef _Decimal32 d32;
+# define d32_RADIX 10
+# define d32_MANT_DIG __DEC32_MANT_DIG__
+# if d32_MANT_DIG == 7
+# define d32_MAX 9999999ULL
+# endif
+typedef _Decimal64 d64;
+# define d64_RADIX 10
+# define d64_MANT_DIG __DEC64_MANT_DIG__
+# if d64_MANT_DIG == 16
+# define d64_MAX 9999999999999999ULL
+# endif
+typedef _Decimal128 d128;
+# define d128_RADIX 10
+# define d128_MANT_DIG __DEC128_MANT_DIG__
+# if d128_MANT_DIG == 34
+# ifdef __SIZEOF_INT128__
+/* #define d128_MAX 0x1ed09bead87c0378d8e63ffffffff */
+# define d128_MAX \
+ ((((unsigned __int128) 0x1ed09bead87c0) << 64) + 0x378d8e63ffffffffULL)
+# else
+# define d128_MAX M1U
+# endif
+# endif
+# ifdef BROKEN_DECIMAL_INT128
+# undef CVTS128
+# define CVTS128(type2)
+# endif
+CVTS (d32)
+CVTS (d64)
+CVTS (d128)
+#endif
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void abort ();
--- /dev/null
+/* { dg-do run } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=float-cast-overflow -DUSE_FLT_DBL_LDBL" } */
+/* { dg-additional-options "-DUSE_INT128" { target int128 } } */
+
+#include "float-cast-overflow-7.h"
+
+#define TEST(type1, type2) \
+ if (type1##_MIN) \
+ { \
+ type2 min = type1##_MIN; \
+ type2 add = -1.0; \
+ while (1) \
+ { \
+ volatile type2 tem = min + add; \
+ if (tem != min) \
+ { \
+ volatile type1 tem3 = cvt_##type1##_##type2 (tem);\
+ break; \
+ } \
+ add = add * type2##_RADIX; \
+ if (min == add) \
+ break; \
+ } \
+ } \
+ else \
+ { \
+ volatile type1 tem3 = cvt_##type1##_##type2 (-1.0f); \
+ } \
+ { \
+ type2 max = type1##_MAX; \
+ type2 add = 1.0; \
+ while (1) \
+ { \
+ volatile type2 tem = max + add; \
+ if (tem != max) \
+ { \
+ volatile type1 tem3 = cvt_##type1##_##type2 (tem); \
+ break; \
+ } \
+ add = add * type2##_RADIX; \
+ if (max == add) \
+ break; \
+ } \
+ }
+
+#ifdef si128_MAX
+# define TESTS128(type2) TEST (si128, type2) TEST (ui128, type2)
+#else
+# define TESTS128(type2)
+#endif
+
+#define TESTS(type2) \
+ TEST (sc, type2) TEST (c, type2) TEST (uc, type2) \
+ TEST (ss, type2) TEST (us, type2) \
+ TEST (si, type2) TEST (ui, type2) \
+ TEST (sl, type2) TEST (ul, type2) \
+ TEST (sll, type2) TEST (ull, type2) \
+ TESTS128 (type2)
+
+int
+main ()
+{
+#ifdef f_MAX
+ TESTS (f)
+#endif
+#ifdef d_MAX
+ TESTS (d)
+#endif
+#ifdef ld_MAX
+ TESTS (ld)
+#endif
+#ifdef f80_MAX
+ TESTS (f80)
+#endif
+#ifdef f128_MAX
+ TESTS (f128)
+#endif
+#ifdef BROKEN_DECIMAL_INT128
+# undef TESTS128
+# define TESTS128(type2)
+# undef TWO
+# undef M1U
+# undef MAXS
+# define TWO 2ULL
+# define M1U -1ULL
+# define MAXS (__CHAR_BIT__ * __SIZEOF_LONG_LONG__)
+#endif
+#ifdef d32_MAX
+ TESTS (d32)
+#endif
+#ifdef d64_MAX
+ TESTS (d64)
+#endif
+#ifdef d128_MAX
+ TESTS (d128)
+#endif
+ return 0;
+}
+
+/* float */
+/* { dg-output "value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value (-129|-1) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { 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 -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { 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 \[0-9.e+-]* is outside the range of representable values of type '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 \[0-9.e+-]* is outside the range of representable values of type 'long 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 \[0-9.e+-]* is outside the range of representable values of type 'long long 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 \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* No error for float and __int128 unsigned max value, as ui128_MAX is +Inf in float. */
+/* double */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value (-129|-1) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { 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 -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { 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 \[0-9.e+-]* is outside the range of representable values of type '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 \[0-9.e+-]* is outside the range of representable values of type 'long 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 \[0-9.e+-]* is outside the range of representable values of type 'long long 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 \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* long double */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value (-129|-1) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { 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 -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { 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 \[0-9.e+-]* is outside the range of representable values of type '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 \[0-9.e+-]* is outside the range of representable values of type 'long 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 \[0-9.e+-]* is outside the range of representable values of type 'long long 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 \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
--- /dev/null
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=float-cast-overflow -fsanitize-recover=float-cast-overflow -DUSE_FLOAT80 -DUSE_FLOAT128" } */
+/* { dg-additional-options "-DUSE_INT128" { target int128 } } */
+
+#include "float-cast-overflow-8.c"
+
+/* __float80 */
+/* { dg-output "value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value (-129|-1) is outside the range of representable values of type '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 -32769 is outside the range of representable values of type 'short 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 \[0-9.e+-]* is outside the range of representable values of type '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 \[0-9.e+-]* is outside the range of representable values of type 'long 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 \[0-9.e+-]* is outside the range of representable values of type 'long long 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 \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target int128 } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target int128 } } */
+/* __float128 */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target int128 } } */
+/* { dg-output "\[^\n\r]*value <unknown> is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target int128 } } */
--- /dev/null
+/* Various macros for -fsanitize=float-cast-overflow testing. */
+
+/* E.g. on CentOS 5 these aren't defined in limits.h. */
+#ifndef LLONG_MAX
+# define LLONG_MAX __LONG_LONG_MAX__
+#endif
+#ifndef LLONG_MIN
+# define LLONG_MIN (-__LONG_LONG_MAX__ - 1LL)
+#endif
+#ifndef ULLONG_MAX
+# define ULLONG_MAX (__LONG_LONG_MAX__ * 2ULL + 1ULL)
+#endif
+
+#define INT128_MAX (__int128) (((unsigned __int128) 1 << ((__SIZEOF_INT128__ * __CHAR_BIT__) - 1)) - 1)
+#define INT128_MIN (-INT128_MAX - 1)
+#define UINT128_MAX ((2 * (unsigned __int128) INT128_MAX) + 1)
+
+#define CHECK_BOUNDARY(VAR, VAL) \
+ (VAR) = (VAL) - 5.0; \
+ (VAR) = (VAL) - 1.5; \
+ (VAR) = (VAL) - 1.0; \
+ (VAR) = (VAL) - 0.75; \
+ (VAR) = (VAL) - 0.5; \
+ (VAR) = (VAL) - 0.0000001; \
+ (VAR) = (VAL) - 0.0; \
+ (VAR) = (VAL); \
+ (VAR) = (VAL) + 0.0; \
+ (VAR) = (VAL) + 0.0000001; \
+ (VAR) = (VAL) + 0.5; \
+ (VAR) = (VAL) + 0.75; \
+ (VAR) = (VAL) + 1.0; \
+ (VAR) = (VAL) + 1.5; \
+ (VAR) = (VAL) + 5.0;
+
+#define CHECK_NONNUMBERS(VAR) \
+ (VAR) = nan; \
+ (VAR) = -nan; \
+ (VAR) = inf; \
+ (VAR) = -inf;
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=float-divide-by-zero" } */
+
+int
+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]*" } */
__attribute__((noinline, noclone)) enum A
foo (bool *p)
{
- *p = b; /* { dg-output "load-bool-enum.c:13:\[^\n\r]*runtime error: load of value 4, which is not a valid value for type '(_B|b)ool'(\n|\r\n|\r)*" } */
- return a; /* { dg-output "\[^\n\r]*load-bool-enum.c:14:\[^\n\r]*runtime error: load of value 9, which is not a valid value for type 'A'(\n|\r\n|\r)*" { target c++ } } */
+ *p = b; /* { dg-output "load-bool-enum.c:13:\[^\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.c:14:\[^\n\r]*runtime error: \[^\n\r]*load of value 9, which is not a valid value for type 'A'" { target c++ } } */
}
int
--- /dev/null
+/* { 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)))
+void *
+foo (void *p, void *q, void *r)
+{
+ a = p;
+ b = r;
+ return q;
+}
+
+int
+bar (const void *a, const void *b)
+{
+ int c = *(const int *) a;
+ int d = *(const int *) b;
+ return c - d;
+}
+
+int
+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:29:\[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:30:\[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:31:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */
+
+int q, r;
+void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h;
+
+__attribute__((returns_nonnull, nonnull (1, 3)))
+void *
+foo (void *p, void *q, void *r)
+{
+ a = p;
+ b = r;
+ return q;
+}
+
+int
+bar (const void *a, const void *b)
+{
+ int c = *(const int *) a;
+ int d = *(const int *) b;
+ return c - d;
+}
+
+int
+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:14:\[0-9]*:\[^\n\r]*null pointer returned from function declared to never return null" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */
+
+int q, r;
+void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h;
+
+__attribute__((returns_nonnull, nonnull (1, 3)))
+void *
+foo (void *p, void *q, void *r)
+{
+ a = p;
+ b = r;
+ return q;
+}
+
+int
+bar (const void *a, const void *b)
+{
+ int c = *(const int *) a;
+ int d = *(const int *) b;
+ return c - d;
+}
+
+int
+main ()
+{
+ asm volatile ("" : : : "memory");
+ d = foo (c, (void *) &r, c);
+ e = foo (e, c, f);
+ g = foo (c, f, g);
+ __builtin_memset (d, '\0', q);
+ return 0;
+}
+
+/* { dg-output "\.c:30:\[0-9]*:\[^\n\r]*null pointer passed as argument 1, which is declared to never be null" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-options "-fsanitize=undefined -fsanitize-undefined-trap-on-error" } */
+
+int q, r;
+void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h;
+
+__attribute__((returns_nonnull, nonnull (1, 3)))
+void *
+foo (void *p, void *q, void *r)
+{
+ a = p;
+ b = r;
+ return q;
+}
+
+int
+bar (const void *a, const void *b)
+{
+ int c = *(const int *) a;
+ int d = *(const int *) b;
+ return c - d;
+}
+
+int
+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;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-options "-fsanitize=undefined -fsanitize-undefined-trap-on-error" } */
+
+int q, r;
+void *a, *b, *c = (void *) &q, *d, *e, *f = (void *) &q, *g, *h;
+
+__attribute__((returns_nonnull, nonnull (1, 3)))
+void *
+foo (void *p, void *q, void *r)
+{
+ a = p;
+ b = r;
+ return q;
+}
+
+int
+bar (const void *a, const void *b)
+{
+ int c = *(const int *) a;
+ int d = *(const int *) b;
+ return c - d;
+}
+
+int
+main ()
+{
+ asm volatile ("" : : : "memory");
+ d = foo (c, (void *) &r, c);
+ e = foo (e, c, f);
+ g = foo (c, f, g);
+ __builtin_memset (d, '\0', q);
+ return 0;
+}
return *p;
}
-/* { dg-output "load of null pointer of type 'int'(\n|\r\n|\r)" } */
+/* { dg-output "load of null pointer of type 'int'" } */
return 0;
}
-/* { dg-output "load of null pointer of type 'short int'(\n|\r\n|\r)" } */
+/* { dg-output "load of null pointer of type 'short int'" } */
return (*s)->i;
}
-/* { dg-output "load of null pointer of type 'struct S \\*'(\n|\r\n|\r)" } */
+/* { dg-output "load of null pointer of type 'struct S \\*'" } */
return ***ppp;
}
-/* { dg-output "load of null pointer of type 'int \\*\\*'(\n|\r\n|\r)" } */
+/* { dg-output "load of null pointer of type 'int \\*\\*'" } */
return foo (*p);
}
-/* { dg-output "load of null pointer of type 'int \\*'(\n|\r\n|\r)" } */
+/* { dg-output "load of null pointer of type 'int \\*'" } */
return 0;
}
-/* { dg-output "load of null pointer of type 'complex double'(\n|\r\n|\r)" } */
+/* { dg-output "load of null pointer of type 'complex double'" } */
return 0;
}
-/* { dg-output "load of null pointer of type 'volatile const complex float'(\n|\r\n|\r)" } */
+/* { dg-output "load of null pointer of type 'volatile const complex float'" } */
return 0;
}
-/* { dg-output "store to null pointer of type 'long unsigned int'(\n|\r\n|\r)" } */
+/* { dg-output "store to null pointer of type 'long unsigned int'" } */
return *gao ();
}
-/* { dg-output "load of null pointer of type 'int'(\n|\r\n|\r)" } */
+/* { dg-output "load of null pointer of type 'int'" } */
return s->i;
}
-/* { dg-output "member access within null pointer of type 'struct S'(\n|\r\n|\r)" } */
+/* { dg-output "member access within null pointer of type 'struct S'" } */
return u->i;
}
-/* { dg-output "member access within null pointer of type 'union U'(\n|\r\n|\r)" } */
+/* { dg-output "member access within null pointer of type 'union U'" } */
--- /dev/null
+/* { 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
+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
+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
+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
+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
+f5 (int *p)
+{
+ /* This is not instrumented. But don't ICE, etc. */
+ volatile int i = p[N];
+}
+
+int
+main ()
+{
+ f1 (N);
+ f2 (N);
+ f3 (N);
+ f4 ();
+ int *p = (int *) __builtin_calloc (N, sizeof (*p));
+ f5 (p);
+ __builtin_free (p);
+ return 0;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+static char a[128] __attribute__ ((aligned(4096)));
+static int b[128] __attribute__ ((aligned(4096)));
+
+__attribute__ ((noinline, noclone)) int
+fn1 (int i)
+{
+ asm ("");
+ return a[i & 127];
+}
+
+__attribute__ ((noinline, noclone)) int
+fn2 (int i)
+{
+ asm ("");
+ return a[i & 128];
+}
+
+/* { dg-output "index 128 out of bounds for type 'char \\\[128\\\]'\[^\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)) int
+fn3 (int i)
+{
+ asm ("");
+ return b[i & 127];
+}
+
+__attribute__ ((noinline, noclone)) int
+fn4 (int i)
+{
+ asm ("");
+ return b[i & 128];
+}
+
+/* { dg-output "\[^\n\r]*index 128 out of bounds for type 'int \\\[128\\\]'\[^\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)) int
+fn5 (int i, int j)
+{
+ asm ("");
+ return b[i & j];
+}
+
+/* { dg-output "\[^\n\r]*index 128 out of bounds for type 'int \\\[128\\\]'\[^\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]*\\^" } */
+
+__attribute__ ((noinline, noclone)) int
+fn6 (int i)
+{
+ asm ("");
+ return b[i & 0];
+}
+
+int
+main (void)
+{
+ fn1 (128);
+ fn2 (128);
+ fn3 (128);
+ fn4 (128);
+ fn5 (128, 127);
+ fn5 (128, 128);
+ fn6 (128);
+ return 0;
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+void
+foo (unsigned long ul)
+{
+ unsigned int u;
+ u = *(unsigned long *) ul;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size -fno-sanitize-recover=object-size" } */
+
+/* Test valid uses. */
+
+#define N 20
+
+__attribute__((noinline, noclone)) void
+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);
+}
+
+__attribute__((noinline, noclone)) void
+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];
+}
+
+__attribute__((noinline, noclone)) void
+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);
+}
+
+int
+main ()
+{
+ f1 (N - 1);
+ f2 (N - 1);
+ f3 (N - 1);
+ return 0;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+/* Test that we instrument flexible array members. */
+
+struct T { int l; int a[]; };
+struct U { int l; int a[0]; };
+
+int
+main (void)
+{
+ volatile int i;
+ struct T *t = (struct T *) __builtin_calloc (sizeof (struct T)
+ + sizeof (int), 1);
+ i = t->a[1];
+
+ struct U *u = (struct U *) __builtin_calloc (sizeof (struct U)
+ + sizeof (int), 1);
+ i = u->a[1];
+ return 0;
+}
+
+/* { dg-output "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]*\\^" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+/* Test structures with -fsanitize=object-size. */
+
+#define N 20
+
+struct S { char *p; int i; };
+struct T { struct S *s; };
+
+__attribute__((noinline, noclone)) void
+f1 (int i)
+{
+ volatile int j;
+ struct S s;
+ s.p = (char *) __builtin_calloc (N, 1);
+ j = s.p[i];
+ j = *(s.p + i);
+ __builtin_free (s.p);
+}
+
+/* { 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]*\\^" } */
+
+int
+main ()
+{
+ f1 (N);
+ f1 (N - 1);
+ return 0;
+}
--- /dev/null
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+char
+foo (void *v)
+{
+ return *(char *) v;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=object-size" } */
+
+#define N 20
+
+struct S { int a; };
+
+__attribute__((noinline, noclone)) struct S
+f1 (int i)
+{
+ struct S a[N];
+ struct S *p = a;
+ struct S s;
+ s = p[i];
+ return s;
+}
+
+int
+main ()
+{
+ f1 (N);
+ return 0;
+}
+
+/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type\[^\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]*\\^" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct S { int a; int b; };
+
+static inline __attribute__((always_inline)) int
+foo (struct S *p)
+{
+ volatile int a;
+ a = p->a; /* OK */
+ return p->b;
+}
+
+int
+bar (void)
+{
+ struct S *p = (struct S *) __builtin_calloc (sizeof (int) + sizeof (int) / 2, 1);
+ return foo (p);
+}
+
+int
+main (void)
+{
+ bar ();
+ return 0;
+}
+
+/* { dg-output "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]*\\^" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+/* Test PARM_DECLs and RESULT_DECLs. */
+
+struct T { char d[8]; int e; };
+struct T t = { "abcdefg", 1 };
+#ifdef __cplusplus
+struct C { C () : d("abcdefg"), e(1) {} C (const C &x) { __builtin_memcpy (d, x.d, 8); e = x.e; } ~C () {} char d[8]; int e; };
+#endif
+struct U { int a : 5; int b : 19; int c : 8; };
+struct S { struct U d[10]; };
+struct S s __attribute__ ((aligned(4096)));
+
+int
+f1 (struct T x, int i)
+{
+ char *p = x.d;
+ p += i;
+ return *p;
+}
+
+/* { 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)" } */
+
+#ifdef __cplusplus
+static struct C
+f2 (int i)
+{
+ struct C x;
+ x.d[i] = 'z';
+ return x;
+}
+
+/* { dg-output "\[^\n\r]*index 12 out of bounds for type 'char \\\[8\\\]'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+
+static struct C
+f3 (int i)
+{
+ struct C x;
+ char *p = x.d;
+ p += i;
+ *p = 'z';
+ return x;
+}
+
+/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */
+
+#endif
+
+int
+f4 (int i)
+{
+ return s.d[i].b;
+}
+
+/* { dg-output "\[^\n\r]*index 12 out of bounds for type 'U \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'unsigned 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)" } */
+
+int
+f5 (int i)
+{
+ struct U *u = s.d;
+ u += i;
+ return u->b;
+}
+
+/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'unsigned 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]*\\^" } */
+
+int
+main (void)
+{
+ f1 (t, 12);
+#ifdef __cplusplus
+ f2 (12);
+ f3 (12);
+#endif
+ f4 (12);
+ f5 (12);
+ return 0;
+}
/* { dg-do run } */
-/* { dg-options "-fsanitize=signed-integer-overflow" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=signed-integer-overflow -fno-sanitize-recover=signed-integer-overflow" } */
#ifndef ASM1
# define ASM1(a) /* Nothing */
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
CHECK (FN1 (char, char, +), 23);
CHECK (FN1 (char, char, -), 5);
CHECK (FN1 (char, char, *), 126);
CHECK (FN5 (unsigned long int), -77);
CHECK (FN5 (long long int), -77);
CHECK (FN5 (unsigned long long int), -77);
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
/* { dg-do run } */
-/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fno-sanitize-recover=signed-integer-overflow" } */
#define SCHAR_MAX __SCHAR_MAX__
#define SHRT_MAX __SHRT_MAX__
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
#if __INT_MAX__ == 2147483647
/* Here, nothing should fail. */
volatile int j = INT_MAX;
d++;
check (d, -32768);
#endif
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
return 0;
}
-/* { dg-output "signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: 1 \\+ 2147483647 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: 2147483547 \\+ 1024 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -1 \\+ -2147483648 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 \\+ -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -2147483548 \\+ -1024 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: 1 \\+ \[^\n\r]* cannot be represented in type 'long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1024 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -1 \\+ -\[^\n\r]* cannot be represented in type 'long int'(\n|\r\n|\r)" } */
+/* { dg-output "signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 1 \\+ 2147483647 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 2147483547 \\+ 1024 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -1 \\+ -2147483648 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 \\+ -1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -2147483548 \\+ -1024 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1 cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 1 \\+ \[^\n\r]* cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1 cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\+ 1024 cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1 cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -1 \\+ -\[^\n\r]* cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1024 cannot be represented in type 'long int'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fno-sanitize-recover=signed-integer-overflow" } */
+/* { dg-shouldfail "ubsan" } */
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-__INT_MAX__ - 1)
+
+int
+main (void)
+{
+ volatile int j = INT_MAX;
+ volatile int i = 1;
+ volatile int k = j + i;
+ return 0;
+}
+
+/* { dg-output "signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fsanitize-undefined-trap-on-error" } */
+/* { dg-shouldfail "ubsan" } */
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-__INT_MAX__ - 1)
+
+int
+main (void)
+{
+ volatile int j = INT_MAX;
+ volatile int i = 1;
+ volatile int k = j + i;
+ return 0;
+}
/* { dg-output "\[^\n\r]*signed integer overflow: 0x7fffffffffffffffffffffffffffff9b \\+ 1024 cannot be represented in type '__int128'(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*signed integer overflow: -1 \\+ 0x80000000000000000000000000000000 cannot be represented in type '__int128'(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*signed integer overflow: 0x80000000000000000000000000000000 \\+ -1 cannot be represented in type '__int128'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: 0x80000000000000000000000000000000 \\+ -1 cannot be represented in type '__int128'(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 0x80000000000000000000000000000000 - 1 cannot be represented in type '__int128'(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*signed integer overflow: 0x80000000000000000000000000000064 \\+ -1024 cannot be represented in type '__int128'(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*signed integer overflow: 0x7fffffffffffffffffffffffffffffff \\* 2 cannot be represented in type '__int128'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*negation of 0x80000000000000000000000000000000 cannot be represented in type '__int128'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of 0x80000000000000000000000000000000 cannot be represented in type '__int128'; cast to an unsigned type to negate this value to itself" } */
/* { dg-do run } */
-/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fno-sanitize-recover=signed-integer-overflow" } */
#define SCHAR_MAX __SCHAR_MAX__
#define SHRT_MAX __SHRT_MAX__
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
/* Test integer promotion. */
#if __SCHAR_MAX__ == 127
volatile signed char a = -2;
o = m * n;
check (o, INT_MIN);
#endif
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
return 0;
}
-/* { dg-output "signed integer overflow: 2147483647 \\* 2 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: 2 \\* 2147483647 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\* 2 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
+/* { dg-output "signed integer overflow: 2147483647 \\* 2 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 2 \\* 2147483647 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: \[^\n\r]* \\* 2 cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*signed integer overflow: 2 \\* \[^\n\r]* cannot be represented in type 'long int'" } */
/* { dg-do run } */
-/* { dg-options "-fsanitize=signed-integer-overflow" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=signed-integer-overflow -fno-sanitize-recover=signed-integer-overflow" } */
__attribute__((noinline, noclone)) long long
mul (long long x, long long y)
int
main ()
{
- fputs ("UBSAN TEST START\n", stderr);
-
unsigned int i;
for (i = 0; i < sizeof (tab) / sizeof (long long); i += 3)
if (mul (tab[i], tab[i + 1]) != tab[i + 2]
|| mul (tab[i + 1], tab[i]) != tab[i + 2])
__builtin_abort ();
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
return 0;
}
-/* { dg-output "overflow-mul-4.c:20:\[^\n\r]*signed integer overflow: 1537228672809129302 \\* 6 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:21:\[^\n\r]*signed integer overflow: -1537228672809129302 \\* -6 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:22:\[^\n\r]*signed integer overflow: 1537228672809129302 \\* -6 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:23:\[^\n\r]*signed integer overflow: -1537228672809129302 \\* 6 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:24:\[^\n\r]*signed integer overflow: 2166572392 \\* 4257126175 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:25:\[^\n\r]*signed integer overflow: -2166572392 \\* -4257126175 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:26:\[^\n\r]*signed integer overflow: 2166572392 \\* -4257126175 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:27:\[^\n\r]*signed integer overflow: -2166572392 \\* 4257126175 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:28:\[^\n\r]*signed integer overflow: 1537228672809129301 \\* 7 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:29:\[^\n\r]*signed integer overflow: -1537228672809129301 \\* -7 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:30:\[^\n\r]*signed integer overflow: 1537228672809129301 \\* -7 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:31:\[^\n\r]*signed integer overflow: -1537228672809129301 \\* 7 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:32:\[^\n\r]*signed integer overflow: 2166572391 \\* 4257126176 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:33:\[^\n\r]*signed integer overflow: -2166572391 \\* -4257126176 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:34:\[^\n\r]*signed integer overflow: 2166572391 \\* -4257126176 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:35:\[^\n\r]*signed integer overflow: -2166572391 \\* 4257126176 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:36:\[^\n\r]*signed integer overflow: 6 \\* 1537228672809129302 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:37:\[^\n\r]*signed integer overflow: -6 \\* -1537228672809129302 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:38:\[^\n\r]*signed integer overflow: -6 \\* 1537228672809129302 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:39:\[^\n\r]*signed integer overflow: 6 \\* -1537228672809129302 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:40:\[^\n\r]*signed integer overflow: 4257126175 \\* 2166572392 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:41:\[^\n\r]*signed integer overflow: -4257126175 \\* -2166572392 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:42:\[^\n\r]*signed integer overflow: -4257126175 \\* 2166572392 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:43:\[^\n\r]*signed integer overflow: 4257126175 \\* -2166572392 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:44:\[^\n\r]*signed integer overflow: 7 \\* 1537228672809129301 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:45:\[^\n\r]*signed integer overflow: -7 \\* -1537228672809129301 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:46:\[^\n\r]*signed integer overflow: -7 \\* 1537228672809129301 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:47:\[^\n\r]*signed integer overflow: 7 \\* -1537228672809129301 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:48:\[^\n\r]*signed integer overflow: 4257126176 \\* 2166572391 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:49:\[^\n\r]*signed integer overflow: -4257126176 \\* -2166572391 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*overflow-mul-4.c:50:\[^\n\r]*signed integer overflow: -4257126176 \\* 2166572391 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
+/* { dg-output "overflow-mul-4.c:20:\[^\n\r]*signed integer overflow: 1537228672809129302 \\* 6 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:21:\[^\n\r]*signed integer overflow: -1537228672809129302 \\* -6 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:22:\[^\n\r]*signed integer overflow: 1537228672809129302 \\* -6 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:23:\[^\n\r]*signed integer overflow: -1537228672809129302 \\* 6 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:24:\[^\n\r]*signed integer overflow: 2166572392 \\* 4257126175 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:25:\[^\n\r]*signed integer overflow: -2166572392 \\* -4257126175 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:26:\[^\n\r]*signed integer overflow: 2166572392 \\* -4257126175 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:27:\[^\n\r]*signed integer overflow: -2166572392 \\* 4257126175 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:28:\[^\n\r]*signed integer overflow: 1537228672809129301 \\* 7 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:29:\[^\n\r]*signed integer overflow: -1537228672809129301 \\* -7 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:30:\[^\n\r]*signed integer overflow: 1537228672809129301 \\* -7 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:31:\[^\n\r]*signed integer overflow: -1537228672809129301 \\* 7 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:32:\[^\n\r]*signed integer overflow: 2166572391 \\* 4257126176 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:33:\[^\n\r]*signed integer overflow: -2166572391 \\* -4257126176 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:34:\[^\n\r]*signed integer overflow: 2166572391 \\* -4257126176 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:35:\[^\n\r]*signed integer overflow: -2166572391 \\* 4257126176 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:36:\[^\n\r]*signed integer overflow: 6 \\* 1537228672809129302 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:37:\[^\n\r]*signed integer overflow: -6 \\* -1537228672809129302 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:38:\[^\n\r]*signed integer overflow: -6 \\* 1537228672809129302 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:39:\[^\n\r]*signed integer overflow: 6 \\* -1537228672809129302 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:40:\[^\n\r]*signed integer overflow: 4257126175 \\* 2166572392 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:41:\[^\n\r]*signed integer overflow: -4257126175 \\* -2166572392 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:42:\[^\n\r]*signed integer overflow: -4257126175 \\* 2166572392 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:43:\[^\n\r]*signed integer overflow: 4257126175 \\* -2166572392 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:44:\[^\n\r]*signed integer overflow: 7 \\* 1537228672809129301 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:45:\[^\n\r]*signed integer overflow: -7 \\* -1537228672809129301 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:46:\[^\n\r]*signed integer overflow: -7 \\* 1537228672809129301 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:47:\[^\n\r]*signed integer overflow: 7 \\* -1537228672809129301 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:48:\[^\n\r]*signed integer overflow: 4257126176 \\* 2166572391 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:49:\[^\n\r]*signed integer overflow: -4257126176 \\* -2166572391 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*overflow-mul-4.c:50:\[^\n\r]*signed integer overflow: -4257126176 \\* 2166572391 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*overflow-mul-4.c:51:\[^\n\r]*signed integer overflow: 4257126176 \\* -2166572391 cannot be represented in type 'long long int'" } */
return 0;
}
-/* { dg-output "negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*negation of -\[^\n\r]* cannot be represented in type 'long int'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*negation of -\[^\n\r]* cannot be represented in type 'long int'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*negation of -\[^\n\r]* cannot be represented in type 'long int'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*negation of -9223372036854775808 cannot be represented in type 'long long int'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*negation of -9223372036854775808 cannot be represented in type 'long long int'; cast to an unsigned type to negate this value to itself(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*negation of -9223372036854775808 cannot be represented in type 'long long int'; cast to an unsigned type to negate this value to itself" } */
+/* { dg-output "negation of -2147483648 cannot be represented in type 'int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -2147483648 cannot be represented in type 'int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -2147483648 cannot be represented in type 'int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -\[^\n\r]* cannot be represented in type 'long int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -\[^\n\r]* cannot be represented in type 'long int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -\[^\n\r]* cannot be represented in type 'long int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -9223372036854775808 cannot be represented in type 'long long int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -9223372036854775808 cannot be represented in type 'long long int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -9223372036854775808 cannot be represented in type 'long long int'\[^\n\r]*; cast to an unsigned type to negate this value to itself" } */
/* { dg-do run } */
-/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fno-sanitize-recover=signed-integer-overflow" } */
#define SCHAR_MIN (-__SCHAR_MAX__ - 1)
#define SHRT_MIN (-__SHRT_MAX__ - 1)
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
volatile signed char c = -SCHAR_MIN;
CHECK (c, -128);
volatile long long lli = LLONG_MIN;
lli = -(unsigned long long) lli;
CHECK (lli, -0x8000000000000000L);
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=signed-integer-overflow" } */
+
+#define INT_MIN (-__INT_MAX__ - 1)
+
+int
+main ()
+{
+ int x = INT_MIN;
+ int y;
+ asm ("" : "+g" (x));
+ y = -(-x);
+ asm ("" : "+g" (y));
+ y = -(-INT_MIN);
+ asm ("" : "+g" (y));
+ return 0;
+}
+
+/* { dg-output "negation of -2147483648 cannot be represented in type 'int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -2147483648 cannot be represented in type 'int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -2147483648 cannot be represented in type 'int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*negation of -2147483648 cannot be represented in type 'int'\[^\n\r]*; cast to an unsigned type to negate this value to itself" } */
/* { dg-do run } */
-/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=signed-integer-overflow -Wno-unused-variable -fno-sanitize-recover=signed-integer-overflow" } */
#define SCHAR_MAX __SCHAR_MAX__
#define SCHAR_MIN (-__SCHAR_MAX__ - 1)
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
#if __INT_MAX__ == 2147483647
/* Here, nothing should fail. */
volatile int i = -1;
d--;
check (d, 32767);
#endif
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
return 0;
}
-/* { dg-output "signed integer overflow: -2147483648 - 1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 \\+ -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -2147483548 \\+ -1024 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 \\+ -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -2147482648 \\+ -1048576 cannot be represented in type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* - 1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1024 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1 cannot be represented in type 'long int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* \\+ -1048576 cannot be represented in type 'long int'" } */
+/* { dg-output "signed integer overflow: -2147483648 - 1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 - 1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -2147483548 - 1024 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -2147483648 - 1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -2147482648 - 1048576 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* - 1 cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* - 1 cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* - 1024 cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* - 1 cannot be represented in type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: -\[^\n\r]* - 1048576 cannot be represented in type 'long int'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=signed-integer-overflow" } */
+
+#define INT_MIN (-__INT_MAX__ - 1)
+
+int
+main ()
+{
+ int x = INT_MIN;
+ int y = 0;
+ int z;
+ asm ("" : "+g" (y));
+ asm ("" : "+g" (x));
+ z = y - (-x);
+ asm ("" : "+g" (z));
+ return 0;
+}
+
+/* { dg-output "negation of -2147483648 cannot be represented in type 'int'\[^\n\r]*; cast to an unsigned type to negate this value to itself\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 0 - -2147483648 cannot be represented in type 'int'" } */
/* PR middle-end/56917 */
/* { dg-do run } */
-/* { dg-options "-fsanitize=undefined" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */
#define INT_MIN (-__INT_MAX__ - 1)
#define LONG_MIN (-__LONG_MAX__ - 1L)
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
if (fn1 (__INT_MAX__ + 1U) != INT_MIN
|| fn2 (__LONG_MAX__ + 1UL) != LONG_MIN
|| fn3 (__LONG_LONG_MAX__ + 1ULL) != LLONG_MIN)
__builtin_abort ();
-
- fputs ("UBSAN TEST END\n", stderr);
- return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined -fsanitize-undefined-trap-on-error" } */
+
+unsigned int __attribute__((noinline,noclone))
+foo (unsigned int x)
+{
+ return x <= __INT_MAX__ ? x : -x;
+}
+
+int
+main ()
+{
+ volatile unsigned int tem = foo (-__INT_MAX__ - 1);
+ return 0;
+}
/* { dg-do run } */
-/* { dg-options "-fsanitize=signed-integer-overflow" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=signed-integer-overflow -fno-sanitize-recover=signed-integer-overflow" } */
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
long long int a = 14;
long int b = 9;
asm volatile ("" : "+r" (a), "+r" (b));
if ((a - b) != 5)
__builtin_abort ();
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
return 0;
}
-/* { dg-output "store to null pointer of type 'float'(\n|\r\n|\r)" } */
+/* { dg-output "store to null pointer of type 'float'" } */
/* PR sanitizer/60613 */
/* { dg-do run } */
-/* { dg-options "-fsanitize=undefined" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */
long long y;
int
main ()
{
- fputs ("UBSAN TEST START\n", stderr);
-
y = 1;
if (foo (8 - 2040) != 8 - 1)
__builtin_abort ();
if (bar (1) != 8 - 1)
__builtin_abort ();
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
return 0;
}
-/* { dg-output "signed integer overflow: 8 \\- -9223372036854775801 cannot be represented in type 'long long int'(\n|\r\n|\r)" } */
+/* { dg-output "signed integer overflow: 8 \\- -9223372036854775801 cannot be represented in type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*signed integer overflow: 8 \\- -9223372036854775802 cannot be represented in type 'long long int'" } */
--- /dev/null
+/* PR sanitizer/63520 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+int a;
+
+void
+foo (void)
+{
+ while (1)
+ {
+ if (a == 1)
+ break;
+ a -= 1;
+ }
+}
--- /dev/null
+/* Limit this to known non-strict alignment targets. */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-options "-fsanitize=alignment" } */
+
+#define __round_mask(x, y) ((__typeof__(x))((y)-1))
+#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
+
+struct test_struct {
+ unsigned long a;
+ int b;
+} __attribute__((__aligned__(64)));
+
+char a[200];
+
+int main ()
+{
+ volatile int x = ((struct test_struct*)(round_up((unsigned long)a, 64) + 16))->b;
+ volatile int y = ((struct test_struct*)(round_up((unsigned long)a, 64) + 15))->b;
+
+ return 0;
+}
+
+/* { dg-output "\.c:18:\[0-9]*: \[^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct test_struct', which requires 64 byte alignment" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unreachable" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-shouldfail "ubsan" } */
+
+static void __attribute__ ((noreturn))
+bar ()
+{
+} /* { dg-warning "function does return" } */
+
+void
+foo ()
+{
+ bar ();
+}
+
+int
+main (void)
+{
+ foo ();
+}
+
+/* { dg-output "execution reached a __builtin_unreachable\\(\\) call" } */
--- /dev/null
+/* PR sanitizer/63866 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined -fdump-ipa-cgraph" } */
+
+int
+foo (int x, int y)
+{
+ return x + y;
+}
+
+/* { dg-final { cleanup-ipa-dump "cgraph" } } */
--- /dev/null
+/* PR sanitizer/63879 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct A
+{
+ int inode;
+} * a;
+int b, c;
+void
+fn1 ()
+{
+ int d = 0;
+ while (b)
+ {
+ if (a->inode)
+ d++;
+ a = 0;
+ }
+ c = d - 1;
+ for (; c >= 0; c--)
+ ;
+}
--- /dev/null
+/* PR sanitizer/63879 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+int a;
+void
+fn1 ()
+{
+ int b = 2;
+ for (; a;)
+ while (b >= 0)
+ b--;
+}
--- /dev/null
+/* PR sanitizer/64121 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined -Wno-pointer-arith" } */
+
+extern int tab[16];
+
+void
+execute (int *ip, int x)
+{
+ int *xp = tab;
+base:
+ if (x)
+ return;
+ *xp++ = *ip;
+ goto *(&&base + *ip);
+}
--- /dev/null
+/* PR sanitizer/64289 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+int
+foo (int a)
+{
+ return (int) (0 ? 0 : a ? a : 0.5);
+}
--- /dev/null
+/* PR sanitizer/64344 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+int
+foo (float x)
+{
+ return __builtin_log ((double ) x);
+}
--- /dev/null
+/* PR sanitizer/64344 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+int
+foo (void)
+{
+ static const int a = 0.5;
+ static const int b = (int) 13.5 + 1;
+ return a + b;
+}
--- /dev/null
+/* PR sanitizer/64906 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=integer-divide-by-zero -O -Werror=maybe-uninitialized" } */
+
+int
+fn1 (int f, int s)
+{
+ int ret = 0;
+ if (f)
+ ret = s / (f ? (unsigned long) 8 : 0);
+ return ret;
+}
--- /dev/null
+/* PR sanitizer/65367 */
+/* { dg-do compile } */
+/* { dg-options "-fno-tree-ccp -fno-tree-copy-prop -fno-tree-dominator-opts -fno-tree-fre -fsanitize=object-size" } */
+
+int
+foo (char *p)
+{
+ return *((const char *) "") - *p;
+}
--- /dev/null
+/* PR sanitizer/66908 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=shift,bounds -O2 -Werror=maybe-uninitialized" } */
+/* { dg-additional-options "-std=gnu90" { target c } } */
+
+struct S { int a[22]; };
+static int const e[22] = { };
+
+void
+foo (struct S const *s, unsigned int m, unsigned int *res)
+{
+ unsigned int i;
+ for (i = 0; i < 22; ++i)
+ res[i] = ((s->a[i] + e[i]) << m);
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined -fsanitize-recover=signed-integer-overflow -w" } */
+
+#include "recovery-common.inc"
+
+/* { dg-output "shift exponent 152 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 153 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 2147483647 \\+ 2 cannot be represented in type 'int'" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=signed-integer-overflow -w" } */
+/* { dg-shouldfail "ubsan" } */
+
+#include "recovery-common.inc"
+
+/* { dg-output "shift exponent 152 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 153 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*(?!.*signed integer overflow)" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined -w" } */
+
+#include "recovery-common.inc"
+
+/* { dg-output "shift exponent 152 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 153 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 2147483647 \\+ 1 cannot be represented in type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*signed integer overflow: 2147483647 \\+ 2 cannot be represented in type 'int'" } */
--- /dev/null
+typedef const unsigned long long int CULLI;
+typedef volatile int VI;
+struct s { signed long int a; };
+
+int
+main (void)
+{
+ volatile int shiftcount = 153;
+ volatile int a = __INT_MAX__;
+ volatile int b = __INT_MAX__;
+
+ a << 152;
+ b << shiftcount;
+ a += 1;
+ b += 2;
+
+ return 0;
+}
+
--- /dev/null
+/* Test -f*sanitize*=all */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=all" } */
+
+int i;
+
+/* { dg-error "-fsanitize=all option is not valid" "" { target *-*-* } 0 } */
--- /dev/null
+/* Test -f*sanitize*=all */
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-skip-if "" { *-*-* } { "-flto -fno-fat-lto-objects" } } */
+/* { dg-options "-fsanitize=undefined,float-cast-overflow,float-divide-by-zero -fno-sanitize=all -fdump-tree-optimized" } */
+
+int a[4];
+
+int
+f1 (int x, int y, int z)
+{
+ return a[x] + (1 << y) + (100 / z);
+}
+
+char *
+f2 (int x)
+{
+ char *p = (char *) __builtin_calloc (64, 1);
+ p[x] = 3;
+ return p;
+}
+
+int
+f3 (int x, int *y, double z, double w)
+{
+ int a[*y];
+ if (x)
+ __builtin_unreachable ();
+ asm volatile ("" : : "r" (&a[0]));
+ return z / w;
+}
+
+int
+main ()
+{
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "__ubsan_" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "UBSAN_CHECK_" "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
--- /dev/null
+/* Test -f*sanitize*=all */
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-skip-if "" { *-*-* } { "-flto -fno-fat-lto-objects" } } */
+/* { dg-options "-fsanitize=undefined -fsanitize-recover=all -fdump-tree-optimized" } */
+
+int a[4];
+
+int
+f1 (int x, int y, int z)
+{
+ return a[x] + (1 << y) + (100 / z);
+}
+
+char *
+f2 (int x)
+{
+ char *p = (char *) __builtin_calloc (64, 1);
+ p[x] = 3;
+ return p;
+}
+
+int
+f3 (int x, int *y, double z, double w)
+{
+ int a[*y];
+ if (x)
+ __builtin_unreachable ();
+ asm volatile ("" : : "r" (&a[0]));
+ return z / w;
+}
+
+int
+main ()
+{
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump "__ubsan_" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__ubsan_\[a-z_\]*_abort" "optimized" } } */
+/* { dg-final { scan-tree-dump "UBSAN_CHECK_" "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
--- /dev/null
+/* Test -f*sanitize*=all */
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-skip-if "" { *-*-* } { "-flto -fno-fat-lto-objects" } } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=all -fdump-tree-optimized" } */
+
+int a[4];
+
+int
+f1 (int x, int y, int z)
+{
+ return a[x] + (1 << y) + (100 / z);
+}
+
+char *
+f2 (int x)
+{
+ char *p = (char *) __builtin_calloc (64, 1);
+ p[x] = 3;
+ return p;
+}
+
+int
+f3 (int x, int *y, double z, double w)
+{
+ int a[*y];
+ if (x)
+ __builtin_unreachable ();
+ asm volatile ("" : : "r" (&a[0]));
+ return z / w;
+}
+
+int
+main ()
+{
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump "__ubsan_\[a-z_\]*_abort" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__ubsan_\[a-z_\]*\[^et\] " "optimized" } } */
+/* { dg-final { scan-tree-dump "UBSAN_CHECK_" "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
/* { dg-do compile } */
-/* { dg-options "-fsanitize=shift -Wall -Werror -O" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=shift -Wall -Werror -O -fno-sanitize-recover=shift" } */
static int x;
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
int o = 1;
int y = x << o;
-
- fputs ("UBSAN TEST END\n", stderr);
return y;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
return 0;
}
-/* { dg-output "shift exponent 152 is too large for \[^\n\r]*-bit type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent 153 is too large for \[^\n\r]*-bit type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent 154 is too large for \[^\n\r]*-bit type 'int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent 524 is too large for \[^\n\r]*-bit type 'long long unsigned int'(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent 370 is too large for \[^\n\r]*-bit type 'int'(\n|\r\n|\r)" } */
+/* { dg-output "shift exponent 152 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 153 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 154 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 524 is too large for \[^\n\r]*-bit type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 370 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*shift exponent 402 is too large for \[^\n\r]*-bit type 'long int'" } */
return 0;
}
-/* { dg-output "shift exponent -3 is negative(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent -4 is negative(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent -5 is negative(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent -6 is negative(\n|\r\n|\r)" } */
+/* { dg-output "shift exponent -3 is negative\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent -4 is negative\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent -5 is negative\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent -6 is negative\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*shift exponent -11 is negative" } */
/* { dg-do run } */
-/* { dg-options "-fsanitize=shift -w" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=shift -w -fno-sanitize-recover=shift" } */
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
unsigned int a = 1;
a <<= 31;
a <<= 1;
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
return 0;
}
-/* { dg-output "shift exponent 120 is too large\[^\n\r]*" } */
+/* { dg-output "shift exponent 120 is too large" } */
+++ /dev/null
-/* { dg-do compile } */
-/* { dg-options "-fsanitize=shift -w" } */
-/* { dg-shouldfail "ubsan" } */
-
-int x;
-int
-foo (void)
-{
- /* None of the following should pass. */
- switch (x)
- {
- case 1 >> -1:
-/* { dg-error "case label does not reduce to an integer constant" "" {target c } 12 } */
-/* { dg-error "is not a constant expression" "" { target c++ } 12 } */
- case -1 >> -1:
-/* { dg-error "case label does not reduce to an integer constant" "" {target c } 15 } */
-/* { dg-error "is not a constant expression" "" { target c++ } 15 } */
- case 1 << -1:
-/* { dg-error "case label does not reduce to an integer constant" "" {target c } 18 } */
-/* { dg-error "is not a constant expression" "" { target c++ } 18 } */
- case -1 << -1:
-/* { dg-error "case label does not reduce to an integer constant" "" {target c } 21 } */
-/* { dg-error "is not a constant expression" "" { target c++ } 21 } */
- case -1 >> 200:
-/* { dg-error "case label does not reduce to an integer constant" "" {target c } 24 } */
-/* { dg-error "is not a constant expression" "" { target c++ } 24 } */
- case 1 << 200:
-/* { dg-error "case label does not reduce to an integer constant" "" {target c } 27 } */
-/* { dg-error "is not a constant expression" "" { target c++ } 27 } */
- return 1;
- }
- return 0;
-}
/* PR sanitizer/58413 */
/* { dg-do run { target int32plus } } */
-/* { dg-options "-fsanitize=shift -w" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=shift -w -fno-sanitize-recover=shift" } */
int x = 7;
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
/* All of the following should pass. */
int A[128 >> 5] = {};
int B[128 << 5] = {};
case 128 >> (4 + 1):
return 1;
}
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
--- /dev/null
+/* PR c/63862 */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=undefined" } */
+
+unsigned long long int __attribute__ ((noinline, noclone))
+foo (unsigned long long int i, unsigned long long int j)
+{
+ asm ("");
+ return i >> j;
+}
+
+unsigned long long int __attribute__ ((noinline, noclone))
+bar (unsigned long long int i, unsigned long long int j)
+{
+ asm ("");
+ return i << j;
+}
+
+int
+main ()
+{
+ foo (1ULL, 0x100000000ULL);
+ bar (1ULL, 0x100000000ULL);
+ return 0;
+}
+
+/* { dg-output "shift exponent 4294967296 is too large for \[^\n\r]*-bit type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 4294967296 is too large for \[^\n\r]*-bit type 'long long unsigned int'" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+/* { dg-additional-options "-std=gnu11" { target c } } */
+/* { dg-additional-options "-std=c++11" { target c++ } } */
+
+signed char
+fn1 (signed char x, unsigned long y)
+{
+ return x << y;
+}
+
+short int
+fn2 (short int x, unsigned long y)
+{
+ return x << y;
+}
+
+int
+fn3 (int x, unsigned long y)
+{
+ return x << y;
+}
+
+long int
+fn4 (long int x, unsigned long y)
+{
+ return x << y;
+}
+
+long long int
+fn5 (long long int x, unsigned long y)
+{
+ return x << y;
+}
+
+signed char
+fn6 (signed char x, unsigned long long y)
+{
+ return x << y;
+}
+
+short int
+fn7 (short int x, unsigned long long y)
+{
+ return x << y;
+}
+
+int
+fn8 (int x, unsigned long long y)
+{
+ return x << y;
+}
+
+long int
+fn9 (long int x, unsigned long long y)
+{
+ return x << y;
+}
+
+long long int
+fn10 (long long int x, unsigned long long y)
+{
+ return x << y;
+}
/* { dg-do run } */
-/* { dg-options "-fsanitize=undefined" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=undefined" } */
int
foo (int x, int y)
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
foo (3, 2);
bar (12, 42);
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
/* { dg-do run } */
-/* { dg-options "-fsanitize=undefined" } */
+/* { dg-options "-fsanitize=undefined -fno-sanitize-recover=signed-integer-overflow" } */
/* { dg-additional-options "-std=gnu11" { target c } } */
/* { dg-additional-options "-std=c++11" { target c++ } } */
-#include <stdio.h>
-
volatile int w, z;
__attribute__ ((noinline, noclone)) int
int
main ()
{
- fputs ("1st\n", stderr);
w = foo (0, -__INT_MAX__);
return 0;
}
-/* { dg-output "1st(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent -\[^\n\r]* is negative\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "shift exponent -\[^\n\r]* is negative" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=unreachable" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-shouldfail "ubsan" } */
+
+int e;
+
+int
+main (void)
+{
+ return e ? 0 : (__builtin_unreachable (), 1);
+}
+
+/* { dg-output "execution reached a __builtin_unreachable\\(\\) call" } */
return 0;
}
-/* { dg-output "variable length array bound evaluates to non-positive value -1(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -5(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -3(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value 0(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value 0(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -6(\n|\r\n|\r)" } */
+/* { dg-output "variable length array bound evaluates to non-positive value -1\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -5\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -3\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value 0\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value 0\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -1\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -6\[^\n\r]*(\n|\r\n|\r)" } */
/* { dg-output "\[^\n\r]*variable length array bound evaluates to non-positive value -4" } */
/* { dg-do run } */
-/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable -fno-sanitize-recover=vla-bound" } */
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
const int t = 0;
struct s {
int x;
/* Don't instrument this one. */
int g[t];
};
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
/* { dg-do run } */
-/* { dg-options "-fsanitize=vla-bound" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=vla-bound -fno-sanitize-recover=vla-bound" } */
/* Don't instrument the arrays here. */
int
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
int a[6] = { };
int ret = foo (3, a);
-
- fputs ("UBSAN TEST END\n", stderr);
return ret;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
/* { dg-do run } */
-/* { dg-options "-fsanitize=vla-bound" } */
-
-#include <stdio.h>
+/* { dg-options "-fsanitize=vla-bound -fno-sanitize-recover=vla-bound" } */
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
int x = 1;
/* Check that the size of an array is evaluated only once. */
int a[++x];
if (x != 2)
__builtin_abort ();
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
// Strictly speaking we are not guaranteed to find such two pointers,
// but given the structure of asan's allocator we will.
static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) {
- vector<char *> v;
+ vector<uintptr_t> v;
bool res = false;
for (size_t i = 0; i < 1000U && !res; i++) {
- v.push_back(new char[size]);
+ v.push_back(reinterpret_cast<uintptr_t>(new char[size]));
if (i == 0) continue;
sort(v.begin(), v.end());
for (size_t j = 1; j < v.size(); j++) {
assert(v[j] > v[j-1]);
if ((size_t)(v[j] - v[j-1]) < size * 2) {
- *x2 = v[j];
- *x1 = v[j-1];
+ *x2 = reinterpret_cast<char*>(v[j]);
+ *x1 = reinterpret_cast<char*>(v[j-1]);
res = true;
break;
}
}
for (size_t i = 0; i < v.size(); i++) {
- if (res && v[i] == *x1) continue;
- if (res && v[i] == *x2) continue;
- delete [] v[i];
+ char *p = reinterpret_cast<char *>(v[i]);
+ if (res && p == *x1) continue;
+ if (res && p == *x2) continue;
+ delete [] p;
}
return res;
}
s1[size - 1] = '\0';
s2[size - 1] = '\0';
EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0));
+
+ // Even if the buffers differ in the first byte, we still assume that
+ // memcmp may access the whole buffer and thus reporting the overflow here:
+ s1[0] = 1;
+ s2[0] = 123;
+ EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0));
+
free(s1);
free(s2);
}
}
}
-#if ASAN_ALLOCATOR_VERSION == 2 // Broken with the asan_allocator1
TEST(AddressSanitizer, LargeOOBRightTest) {
size_t large_power_of_two = 1 << 19;
for (size_t i = 16; i <= 256; i *= 2) {
delete [] p;
}
}
-#endif // ASAN_ALLOCATOR_VERSION == 2
TEST(AddressSanitizer, DISABLED_DemoOOBLeftLow) {
oob_test<U1>(10, -1);
//===----------------------------------------------------------------------===//
#include "asan_test_utils.h"
+#if defined(__APPLE__)
+#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_*
+#endif
+
// Used for string functions tests
static char global_string[] = "global";
static size_t global_string_length = 6;
free(heap_string);
}
-#ifndef __APPLE__
+TEST(AddressSanitizer, WcsLenTest) {
+ EXPECT_EQ(0U, wcslen(Ident(L"")));
+ size_t hello_len = 13;
+ size_t hello_size = (hello_len + 1) * sizeof(wchar_t);
+ EXPECT_EQ(hello_len, wcslen(Ident(L"Hello, World!")));
+ wchar_t *heap_string = Ident((wchar_t*)malloc(hello_size));
+ memcpy(heap_string, L"Hello, World!", hello_size);
+ EXPECT_EQ(hello_len, Ident(wcslen(heap_string)));
+ EXPECT_DEATH(Ident(wcslen(heap_string + 14)), RightOOBReadMessage(0));
+ free(heap_string);
+}
+
+#if SANITIZER_TEST_HAS_STRNLEN
TEST(AddressSanitizer, StrNLenOOBTest) {
size_t size = Ident(123);
char *str = MallocAndMemsetString(size);
EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0));
free(str);
}
-#endif
+#endif // SANITIZER_TEST_HAS_STRNLEN
TEST(AddressSanitizer, StrDupOOBTest) {
size_t size = Ident(42);
typedef char*(*PointerToStrChr1)(const char*, int);
typedef char*(*PointerToStrChr2)(char*, int);
-USED static void RunStrChrTest(PointerToStrChr1 StrChr) {
+UNUSED static void RunStrChrTest(PointerToStrChr1 StrChr) {
size_t size = Ident(100);
char *str = MallocAndMemsetString(size);
str[10] = 'q';
EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
free(str);
}
-USED static void RunStrChrTest(PointerToStrChr2 StrChr) {
+UNUSED static void RunStrChrTest(PointerToStrChr2 StrChr) {
size_t size = Ident(100);
char *str = MallocAndMemsetString(size);
str[10] = 'q';
TEST(AddressSanitizer, StrChrAndIndexOOBTest) {
RunStrChrTest(&strchr);
+#if !defined(_WIN32) // no index() on Windows.
RunStrChrTest(&index);
+#endif
}
TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) {
EXPECT_LT(0, strncmp("baa", "aaa", 1));
EXPECT_LT(0, strncmp("zyx", "", 2));
+#if !defined(_WIN32) // no str[n]casecmp on Windows.
// strcasecmp
EXPECT_EQ(0, strcasecmp("", ""));
EXPECT_EQ(0, strcasecmp("zzz", "zzz"));
EXPECT_LT(0, strncasecmp("xyz", "xyy", 10));
EXPECT_LT(0, strncasecmp("Baa", "aaa", 1));
EXPECT_LT(0, strncasecmp("zyx", "", 2));
+#endif
// memcmp
EXPECT_EQ(0, memcmp("a", "b", 0));
RunStrCmpTest(&strcmp);
}
+#if !defined(_WIN32) // no str[n]casecmp on Windows.
TEST(AddressSanitizer, StrCaseCmpOOBTest) {
RunStrCmpTest(&strcasecmp);
}
+#endif
typedef int(*PointerToStrNCmp)(const char*, const char*, size_t);
void RunStrNCmpTest(PointerToStrNCmp StrNCmp) {
RunStrNCmpTest(&strncmp);
}
+#if !defined(_WIN32) // no str[n]casecmp on Windows.
TEST(AddressSanitizer, StrNCaseCmpOOBTest) {
RunStrNCmpTest(&strncasecmp);
}
+#endif
+
TEST(AddressSanitizer, StrCatOOBTest) {
// strcat() reads strlen(to) bytes from |to| before concatenating.
size_t to_size = Ident(100);
free(array);
}
+#if !defined(_WIN32) // FIXME: Fix and enable on Windows.
TEST(AddressSanitizer, AtoiAndFriendsOOBTest) {
RunAtoiOOBTest(&CallAtoi);
RunAtoiOOBTest(&CallAtol);
RunAtoiOOBTest(&CallAtoll);
}
+#endif
void CallStrtol(const char *nptr, char **endptr, int base) {
Ident(strtol(nptr, endptr, base));
free(array);
}
+#if !defined(_WIN32) // FIXME: Fix and enable on Windows.
TEST(AddressSanitizer, StrtollOOBTest) {
RunStrtolOOBTest(&CallStrtoll);
}
TEST(AddressSanitizer, StrtolOOBTest) {
RunStrtolOOBTest(&CallStrtol);
}
+#endif
// { dg-skip-if "" { *-*-* } { "*" } { "-O2" } }
// { dg-skip-if "" { *-*-* } { "-flto" } { "" } }
// { dg-additional-sources "asan_globals_test-wrapper.cc" }
-// { dg-options "-fsanitize=address -fno-builtin -Wall -Wno-format -Werror -g -DASAN_UAR=0 -DASAN_HAS_EXCEPTIONS=1 -DASAN_HAS_BLACKLIST=0 -DASAN_USE_DEJAGNU_GTEST=1 -lasan -lpthread -ldl" }
+// { dg-options "-std=c++11 -fsanitize=address -fno-builtin -Wall -Wno-format -Werror -g -DASAN_UAR=0 -DASAN_HAS_EXCEPTIONS=1 -DASAN_HAS_BLACKLIST=0 -DSANITIZER_USE_DEJAGNU_GTEST=1 -lasan -lpthread -ldl" }
// { dg-additional-options "-DASAN_NEEDS_SEGV=1" { target { ! arm*-*-* } } }
// { dg-additional-options "-DASAN_LOW_MEMORY=1 -DASAN_NEEDS_SEGV=0" { target arm*-*-* } }
// { dg-additional-options "-DASAN_AVOID_EXPENSIVE_TESTS=1" { target { ! run_expensive_tests } } }
NOINLINE void *malloc_aaa(size_t size) {
void *res = malloc_bbb(size); break_optimization(0); return res;}
-#ifndef __APPLE__
-NOINLINE void *memalign_fff(size_t alignment, size_t size) {
- void *res = memalign/**/(alignment, size); break_optimization(0); return res;}
-NOINLINE void *memalign_eee(size_t alignment, size_t size) {
- void *res = memalign_fff(alignment, size); break_optimization(0); return res;}
-NOINLINE void *memalign_ddd(size_t alignment, size_t size) {
- void *res = memalign_eee(alignment, size); break_optimization(0); return res;}
-NOINLINE void *memalign_ccc(size_t alignment, size_t size) {
- void *res = memalign_ddd(alignment, size); break_optimization(0); return res;}
-NOINLINE void *memalign_bbb(size_t alignment, size_t size) {
- void *res = memalign_ccc(alignment, size); break_optimization(0); return res;}
-NOINLINE void *memalign_aaa(size_t alignment, size_t size) {
- void *res = memalign_bbb(alignment, size); break_optimization(0); return res;}
-#endif // __APPLE__
-
-
NOINLINE void free_ccc(void *p) { free(p); break_optimization(0);}
NOINLINE void free_bbb(void *p) { free_ccc(p); break_optimization(0);}
NOINLINE void free_aaa(void *p) { free_bbb(p); break_optimization(0);}
-
template<typename T>
NOINLINE void uaf_test(int size, int off) {
char *p = (char *)malloc_aaa(size);
*c = 0;
delete c;
-#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__)
+#if SANITIZER_TEST_HAS_POSIX_MEMALIGN
int *pm;
int pm_res = posix_memalign((void**)&pm, kPageSize, kPageSize);
EXPECT_EQ(0, pm_res);
free(pm);
-#endif
+#endif // SANITIZER_TEST_HAS_POSIX_MEMALIGN
-#if !defined(__APPLE__)
+#if SANITIZER_TEST_HAS_MEMALIGN
int *ma = (int*)memalign(kPageSize, kPageSize);
EXPECT_EQ(0U, (uintptr_t)ma % kPageSize);
ma[123] = 0;
free(ma);
-#endif // __APPLE__
+#endif // SANITIZER_TEST_HAS_MEMALIGN
}
TEST(AddressSanitizer, CallocTest) {
free(a);
}
+TEST(AddressSanitizer, CallocReturnsZeroMem) {
+ size_t sizes[] = {16, 1000, 10000, 100000, 2100000};
+ for (size_t s = 0; s < sizeof(sizes)/sizeof(sizes[0]); s++) {
+ size_t size = sizes[s];
+ for (size_t iter = 0; iter < 5; iter++) {
+ char *x = Ident((char*)calloc(1, size));
+ EXPECT_EQ(x[0], 0);
+ EXPECT_EQ(x[size - 1], 0);
+ EXPECT_EQ(x[size / 2], 0);
+ EXPECT_EQ(x[size / 3], 0);
+ EXPECT_EQ(x[size / 4], 0);
+ memset(x, 0x42, size);
+ free(Ident(x));
+#if !defined(_WIN32)
+ // FIXME: OOM on Windows. We should just make this a lit test
+ // with quarantine size set to 1.
+ free(Ident(malloc(Ident(1 << 27)))); // Try to drain the quarantine.
+#endif
+ }
+ }
+}
+
+#if !defined(_WIN32) // No valloc on Windows.
TEST(AddressSanitizer, VallocTest) {
void *a = valloc(100);
EXPECT_EQ(0U, (uintptr_t)a % kPageSize);
free(a);
}
+#endif
-#ifndef __APPLE__
+#if SANITIZER_TEST_HAS_PVALLOC
TEST(AddressSanitizer, PvallocTest) {
char *a = (char*)pvalloc(kPageSize + 100);
EXPECT_EQ(0U, (uintptr_t)a % kPageSize);
a[101] = 1; // we should not report an error here.
free(a);
}
-#endif // __APPLE__
+#endif // SANITIZER_TEST_HAS_PVALLOC
+#if !defined(_WIN32)
+// FIXME: Use an equivalent of pthread_setspecific on Windows.
void *TSDWorker(void *test_key) {
if (test_key) {
pthread_setspecific(*(pthread_key_t*)test_key, (void*)0xfeedface);
PTHREAD_JOIN(th, NULL);
pthread_key_delete(test_key);
}
+#endif
TEST(AddressSanitizer, UAF_char) {
const char *uaf_string = "AddressSanitizer:.*heap-use-after-free";
EXPECT_DEATH(uaf_test<U1>(kLargeMalloc, kLargeMalloc / 2), uaf_string);
}
+TEST(AddressSanitizer, UAF_long_double) {
+ if (sizeof(long double) == sizeof(double)) return;
+ long double *p = Ident(new long double[10]);
+ EXPECT_DEATH(Ident(p)[12] = 0, "WRITE of size 1[026]");
+ EXPECT_DEATH(Ident(p)[0] = Ident(p)[12], "READ of size 1[026]");
+ delete [] Ident(p);
+}
+
+#if !defined(_WIN32)
+struct Packed5 {
+ int x;
+ char c;
+} __attribute__((packed));
+#else
+# pragma pack(push, 1)
+struct Packed5 {
+ int x;
+ char c;
+};
+# pragma pack(pop)
+#endif
+
+TEST(AddressSanitizer, UAF_Packed5) {
+ static_assert(sizeof(Packed5) == 5, "Please check the keywords used");
+ Packed5 *p = Ident(new Packed5[2]);
+ EXPECT_DEATH(p[0] = p[3], "READ of size 5");
+ EXPECT_DEATH(p[3] = p[0], "WRITE of size 5");
+ delete [] Ident(p);
+}
+
#if ASAN_HAS_BLACKLIST
TEST(AddressSanitizer, IgnoreTest) {
int *x = Ident(new int);
} // namespace
#endif
-static void MallocStress(size_t n) {
- uint32_t seed = my_rand();
- for (size_t iter = 0; iter < 10; iter++) {
- vector<void *> vec;
- for (size_t i = 0; i < n; i++) {
- if ((i % 3) == 0) {
- if (vec.empty()) continue;
- size_t idx = my_rand_r(&seed) % vec.size();
- void *ptr = vec[idx];
- vec[idx] = vec.back();
- vec.pop_back();
- free_aaa(ptr);
- } else {
- size_t size = my_rand_r(&seed) % 1000 + 1;
-#ifndef __APPLE__
- size_t alignment = 1 << (my_rand_r(&seed) % 7 + 3);
- char *ptr = (char*)memalign_aaa(alignment, size);
-#else
- char *ptr = (char*) malloc_aaa(size);
-#endif
- vec.push_back(ptr);
- ptr[0] = 0;
- ptr[size-1] = 0;
- ptr[size/2] = 0;
- }
- }
- for (size_t i = 0; i < vec.size(); i++)
- free_aaa(vec[i]);
- }
-}
-
-TEST(AddressSanitizer, MallocStressTest) {
- MallocStress((ASAN_LOW_MEMORY) ? 20000 : 200000);
-}
-
static void TestLargeMalloc(size_t size) {
char buff[1024];
sprintf(buff, "is located 1 bytes to the left of %lu-byte", (long)size);
}
TEST(AddressSanitizer, LargeMallocTest) {
- for (int i = 113; i < (1 << 28); i = i * 2 + 13) {
+ const int max_size = (SANITIZER_WORDSIZE == 32) ? 1 << 26 : 1 << 28;
+ for (int i = 113; i < max_size; i = i * 2 + 13) {
TestLargeMalloc(i);
}
}
-#if ASAN_LOW_MEMORY != 1
TEST(AddressSanitizer, HugeMallocTest) {
-#ifdef __APPLE__
- // It was empirically found out that 1215 megabytes is the maximum amount of
- // memory available to the process under AddressSanitizer on 32-bit Mac 10.6.
- // 32-bit Mac 10.7 gives even less (< 1G).
- // (the libSystem malloc() allows allocating up to 2300 megabytes without
- // ASan).
- size_t n_megs = SANITIZER_WORDSIZE == 32 ? 500 : 4100;
-#else
- size_t n_megs = SANITIZER_WORDSIZE == 32 ? 2600 : 4100;
-#endif
- TestLargeMalloc(n_megs << 20);
+ if (SANITIZER_WORDSIZE != 64 || ASAN_AVOID_EXPENSIVE_TESTS) return;
+ size_t n_megs = 4100;
+ EXPECT_DEATH(Ident((char*)malloc(n_megs << 20))[-1] = 0,
+ "is located 1 bytes to the left|"
+ "AddressSanitizer failed to allocate");
}
-#endif
-#ifndef __APPLE__
+#if SANITIZER_TEST_HAS_MEMALIGN
void MemalignRun(size_t align, size_t size, int idx) {
char *p = (char *)memalign(align, size);
Ident(p)[idx] = 0;
"is located 1 bytes to the right");
}
}
-#endif
-
-TEST(AddressSanitizer, ThreadedMallocStressTest) {
- const int kNumThreads = 4;
- const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000;
- pthread_t t[kNumThreads];
- for (int i = 0; i < kNumThreads; i++) {
- PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress,
- (void*)kNumIterations);
- }
- for (int i = 0; i < kNumThreads; i++) {
- PTHREAD_JOIN(t[i], 0);
- }
-}
+#endif // SANITIZER_TEST_HAS_MEMALIGN
void *ManyThreadsWorker(void *a) {
for (int iter = 0; iter < 100; iter++) {
free(ptr2);
}
+TEST(AddressSanitizer, ReallocFreedPointerTest) {
+ void *ptr = Ident(malloc(42));
+ ASSERT_TRUE(NULL != ptr);
+ free(ptr);
+ EXPECT_DEATH(ptr = realloc(ptr, 77), "attempting double-free");
+}
+
+TEST(AddressSanitizer, ReallocInvalidPointerTest) {
+ void *ptr = Ident(malloc(42));
+ EXPECT_DEATH(ptr = realloc((int*)ptr + 1, 77), "attempting free.*not malloc");
+ free(ptr);
+}
+
TEST(AddressSanitizer, ZeroSizeMallocTest) {
// Test that malloc(0) and similar functions don't return NULL.
void *ptr = Ident(malloc(0));
EXPECT_TRUE(NULL != ptr);
free(ptr);
-#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__)
+#if SANITIZER_TEST_HAS_POSIX_MEMALIGN
int pm_res = posix_memalign(&ptr, 1<<20, 0);
EXPECT_EQ(0, pm_res);
EXPECT_TRUE(NULL != ptr);
free(ptr);
-#endif
+#endif // SANITIZER_TEST_HAS_POSIX_MEMALIGN
int *int_ptr = new int[0];
int *int_ptr2 = new int[0];
EXPECT_TRUE(NULL != int_ptr);
delete[] int_ptr2;
}
-#ifndef __APPLE__
+#if SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE
static const char *kMallocUsableSizeErrorMsg =
"AddressSanitizer: attempting to call malloc_usable_size()";
kMallocUsableSizeErrorMsg);
free(array);
EXPECT_DEATH(malloc_usable_size(array), kMallocUsableSizeErrorMsg);
+ delete int_ptr;
}
-#endif
+#endif // SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE
void WrongFree() {
int *x = (int*)malloc(100 * sizeof(int));
free(x + 1);
}
+#if !defined(_WIN32) // FIXME: This should be a lit test.
TEST(AddressSanitizer, WrongFreeTest) {
- EXPECT_DEATH(WrongFree(),
- "ERROR: AddressSanitizer: attempting free.*not malloc");
+ EXPECT_DEATH(WrongFree(), ASAN_PCRE_DOTALL
+ "ERROR: AddressSanitizer: attempting free.*not malloc"
+ ".*is located 4 bytes inside of 400-byte region"
+ ".*allocated by thread");
}
+#endif
void DoubleFree() {
int *x = (int*)malloc(100 * sizeof(int));
abort();
}
+#if !defined(_WIN32) // FIXME: This should be a lit test.
TEST(AddressSanitizer, DoubleFreeTest) {
EXPECT_DEATH(DoubleFree(), ASAN_PCRE_DOTALL
"ERROR: AddressSanitizer: attempting double-free"
".*freed by thread T0 here"
".*previously allocated by thread T0 here");
}
+#endif
template<int kSize>
NOINLINE void SizedStackTest() {
char a[kSize];
char *A = Ident((char*)&a);
+ const char *expected_death = "AddressSanitizer: stack-buffer-";
for (size_t i = 0; i < kSize; i++)
A[i] = i;
- EXPECT_DEATH(A[-1] = 0, "");
- EXPECT_DEATH(A[-20] = 0, "");
- EXPECT_DEATH(A[-31] = 0, "");
- EXPECT_DEATH(A[kSize] = 0, "");
- EXPECT_DEATH(A[kSize + 1] = 0, "");
- EXPECT_DEATH(A[kSize + 10] = 0, "");
- EXPECT_DEATH(A[kSize + 31] = 0, "");
+ EXPECT_DEATH(A[-1] = 0, expected_death);
+ EXPECT_DEATH(A[-5] = 0, expected_death);
+ EXPECT_DEATH(A[kSize] = 0, expected_death);
+ EXPECT_DEATH(A[kSize + 1] = 0, expected_death);
+ EXPECT_DEATH(A[kSize + 5] = 0, expected_death);
+ if (kSize > 16)
+ EXPECT_DEATH(A[kSize + 31] = 0, expected_death);
}
TEST(AddressSanitizer, SimpleStackTest) {
SizedStackTest<128>();
}
+#if !defined(_WIN32)
+// FIXME: It's a bit hard to write multi-line death test expectations
+// in a portable way. Anyways, this should just be turned into a lit test.
TEST(AddressSanitizer, ManyStackObjectsTest) {
char XXX[10];
char YYY[20];
Ident(YYY);
EXPECT_DEATH(Ident(ZZZ)[-1] = 0, ASAN_PCRE_DOTALL "XXX.*YYY.*ZZZ");
}
+#endif
+
+#if 0 // This test requires online symbolizer.
+// Moved to lit_tests/stack-oob-frames.cc.
+// Reenable here once we have online symbolizer by default.
+NOINLINE static void Frame0(int frame, char *a, char *b, char *c) {
+ char d[4] = {0};
+ char *D = Ident(d);
+ switch (frame) {
+ case 3: a[5]++; break;
+ case 2: b[5]++; break;
+ case 1: c[5]++; break;
+ case 0: D[5]++; break;
+ }
+}
+NOINLINE static void Frame1(int frame, char *a, char *b) {
+ char c[4] = {0}; Frame0(frame, a, b, c);
+ break_optimization(0);
+}
+NOINLINE static void Frame2(int frame, char *a) {
+ char b[4] = {0}; Frame1(frame, a, b);
+ break_optimization(0);
+}
+NOINLINE static void Frame3(int frame) {
+ char a[4] = {0}; Frame2(frame, a);
+ break_optimization(0);
+}
+
+TEST(AddressSanitizer, GuiltyStackFrame0Test) {
+ EXPECT_DEATH(Frame3(0), "located .*in frame <.*Frame0");
+}
+TEST(AddressSanitizer, GuiltyStackFrame1Test) {
+ EXPECT_DEATH(Frame3(1), "located .*in frame <.*Frame1");
+}
+TEST(AddressSanitizer, GuiltyStackFrame2Test) {
+ EXPECT_DEATH(Frame3(2), "located .*in frame <.*Frame2");
+}
+TEST(AddressSanitizer, GuiltyStackFrame3Test) {
+ EXPECT_DEATH(Frame3(3), "located .*in frame <.*Frame3");
+}
+#endif
NOINLINE void LongJmpFunc1(jmp_buf buf) {
// create three red zones for these two stack objects.
longjmp(buf, 1);
}
+NOINLINE void TouchStackFunc() {
+ int a[100]; // long array will intersect with redzones from LongJmpFunc1.
+ int *A = Ident(a);
+ for (int i = 0; i < 100; i++)
+ A[i] = i*i;
+}
+
+// Test that we handle longjmp and do not report false positives on stack.
+TEST(AddressSanitizer, LongJmpTest) {
+ static jmp_buf buf;
+ if (!setjmp(buf)) {
+ LongJmpFunc1(buf);
+ } else {
+ TouchStackFunc();
+ }
+}
+
+#if !defined(_WIN32) // Only basic longjmp is available on Windows.
NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) {
// create three red zones for these two stack objects.
int a;
siglongjmp(buf, 1);
}
-
-NOINLINE void TouchStackFunc() {
- int a[100]; // long array will intersect with redzones from LongJmpFunc1.
- int *A = Ident(a);
- for (int i = 0; i < 100; i++)
- A[i] = i*i;
-}
-
-// Test that we handle longjmp and do not report fals positives on stack.
-TEST(AddressSanitizer, LongJmpTest) {
- static jmp_buf buf;
- if (!setjmp(buf)) {
- LongJmpFunc1(buf);
- } else {
- TouchStackFunc();
- }
-}
-
-#if not defined(__ANDROID__)
+#if !defined(__ANDROID__) && \
+ !defined(__powerpc64__) && !defined(__powerpc__)
+// Does not work on Power:
+// https://code.google.com/p/address-sanitizer/issues/detail?id=185
TEST(AddressSanitizer, BuiltinLongJmpTest) {
static jmp_buf buf;
if (!__builtin_setjmp((void**)buf)) {
TouchStackFunc();
}
}
-#endif // not defined(__ANDROID__)
+#endif // !defined(__ANDROID__) && !defined(__powerpc64__) &&
+ // !defined(__powerpc__)
TEST(AddressSanitizer, UnderscopeLongJmpTest) {
static jmp_buf buf;
TouchStackFunc();
}
}
+#endif
-#ifdef __EXCEPTIONS
+// FIXME: Why does clang-cl define __EXCEPTIONS?
+#if defined(__EXCEPTIONS) && !defined(_WIN32)
NOINLINE void ThrowFunc() {
// create three red zones for these two stack objects.
int a;
PTHREAD_JOIN(t, 0);
}
-#if defined(__i386__) || defined(__x86_64__)
+#if defined(__i686__) || defined(__x86_64__)
+#include <emmintrin.h>
TEST(AddressSanitizer, Store128Test) {
char *a = Ident((char*)malloc(Ident(12)));
char *p = a;
}
#endif
+// FIXME: All tests that use this function should be turned into lit tests.
string RightOOBErrorMessage(int oob_distance, bool is_write) {
assert(oob_distance >= 0);
char expected_str[100];
- sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the right",
- is_write ? "WRITE" : "READ", oob_distance);
+ sprintf(expected_str, ASAN_PCRE_DOTALL
+#if !GTEST_USES_SIMPLE_RE
+ "buffer-overflow.*%s.*"
+#endif
+ "located %d bytes to the right",
+#if !GTEST_USES_SIMPLE_RE
+ is_write ? "WRITE" : "READ",
+#endif
+ oob_distance);
return string(expected_str);
}
return RightOOBErrorMessage(oob_distance, /*is_write*/false);
}
+// FIXME: All tests that use this function should be turned into lit tests.
string LeftOOBErrorMessage(int oob_distance, bool is_write) {
assert(oob_distance > 0);
char expected_str[100];
- sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the left",
- is_write ? "WRITE" : "READ", oob_distance);
+ sprintf(expected_str,
+#if !GTEST_USES_SIMPLE_RE
+ ASAN_PCRE_DOTALL "%s.*"
+#endif
+ "located %d bytes to the left",
+#if !GTEST_USES_SIMPLE_RE
+ is_write ? "WRITE" : "READ",
+#endif
+ oob_distance);
return string(expected_str);
}
PTHREAD_JOIN(t, 0);
}
+#if !defined(_WIN32) // FIXME: This should be a lit test.
TEST(AddressSanitizer, ThreadedTest) {
EXPECT_DEATH(ThreadedTestSpawn(),
ASAN_PCRE_DOTALL
".*Thread T.*created"
".*Thread T.*created");
}
+#endif
void *ThreadedTestFunc(void *unused) {
// Check if prctl(PR_SET_NAME) is supported. Return if not.
#if SANITIZER_WORDSIZE == 32
char *addr = (char*)0x22000000;
#else
+# if defined(__powerpc64__)
+ char *addr = (char*)0x024000800000;
+# else
char *addr = (char*)0x0000100000080000;
+# endif
#endif
EXPECT_DEATH(*addr = 1, "AddressSanitizer: SEGV on unknown");
}
}
}
-#ifdef __EXCEPTIONS
+// FIXME: Why does clang-cl define __EXCEPTIONS?
+#if defined(__EXCEPTIONS) && !defined(_WIN32)
NOINLINE static void StackReuseAndException() {
int large_stack[1000];
Ident(large_stack);
}
#endif
+#if !defined(_WIN32)
TEST(AddressSanitizer, MlockTest) {
EXPECT_EQ(0, mlockall(MCL_CURRENT));
EXPECT_EQ(0, mlock((void*)0x12345, 0x5678));
EXPECT_EQ(0, munlockall());
EXPECT_EQ(0, munlock((void*)0x987, 0x654));
}
+#endif
struct LargeStruct {
int foo[100];
*Ident(&a) = *Ident(&a);
}
-ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
-static void NoAddressSafety() {
+ATTRIBUTE_NO_SANITIZE_ADDRESS
+static void NoSanitizeAddress() {
char *foo = new char[10];
Ident(foo)[10] = 0;
delete [] foo;
}
-TEST(AddressSanitizer, AttributeNoAddressSafetyTest) {
- Ident(NoAddressSafety)();
+TEST(AddressSanitizer, AttributeNoSanitizeAddressTest) {
+ Ident(NoSanitizeAddress)();
}
-// It doesn't work on Android, as calls to new/delete go through malloc/free.
-#if !defined(ANDROID) && !defined(__ANDROID__)
+// The new/delete/etc mismatch checks don't work on Android,
+// as calls to new/delete go through malloc/free.
+// OS X support is tracked here:
+// https://code.google.com/p/address-sanitizer/issues/detail?id=131
+// Windows support is tracked here:
+// https://code.google.com/p/address-sanitizer/issues/detail?id=309
+#if !defined(ANDROID) && !defined(__ANDROID__) && \
+ !defined(__APPLE__) && \
+ !defined(_WIN32)
static string MismatchStr(const string &str) {
return string("AddressSanitizer: alloc-dealloc-mismatch \\(") + str;
}
memcpy(Ident(&a), Ident(&b), sizeof(long double));
memcpy(Ident(&c), Ident(&b), sizeof(long double));
}
+
+#if !defined(_WIN32)
+TEST(AddressSanitizer, pthread_getschedparam) {
+ int policy;
+ struct sched_param param;
+ EXPECT_DEATH(
+ pthread_getschedparam(pthread_self(), &policy, Ident(¶m) + 2),
+ "AddressSanitizer: stack-buffer-.*flow");
+ EXPECT_DEATH(
+ pthread_getschedparam(pthread_self(), Ident(&policy) - 1, ¶m),
+ "AddressSanitizer: stack-buffer-.*flow");
+ int res = pthread_getschedparam(pthread_self(), &policy, ¶m);
+ ASSERT_EQ(0, res);
+}
+#endif
#include <string>
#include <map>
-#if ASAN_USE_DEJAGNU_GTEST
-# include "dejagnu-gtest.h"
-#else
-# include "gtest/gtest.h"
-#endif
-
using std::string;
using std::vector;
using std::map;
#endif
#ifndef ASAN_NEEDS_SEGV
-# error "please define ASAN_NEEDS_SEGV"
-#endif
-
-#ifndef ASAN_LOW_MEMORY
-# define ASAN_LOW_MEMORY 0
+# if defined(_WIN32)
+# define ASAN_NEEDS_SEGV 0
+# else
+# define ASAN_NEEDS_SEGV 1
+# endif
#endif
#ifndef ASAN_AVOID_EXPENSIVE_TESTS
#ifndef ASAN_TEST_UTILS_H
#define ASAN_TEST_UTILS_H
-#if !defined(ASAN_EXTERNAL_TEST_CONFIG)
+#if !defined(SANITIZER_EXTERNAL_TEST_CONFIG)
# define INCLUDED_FROM_ASAN_TEST_UTILS_H
# include "asan_test_config.h"
# undef INCLUDED_FROM_ASAN_TEST_UTILS_H
#endif
#include "sanitizer_test_utils.h"
+#include "sanitizer_pthread_wrappers.h"
+
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
-#include <strings.h>
-#include <pthread.h>
#include <stdint.h>
-#include <setjmp.h>
#include <assert.h>
#include <algorithm>
-#include <sys/mman.h>
+
+#if !defined(_WIN32)
+# include <strings.h>
+# include <sys/mman.h>
+# include <setjmp.h>
+#endif
#ifdef __linux__
# include <sys/prctl.h>
#include <unistd.h>
#endif
-#if defined(__i386__) || defined(__x86_64__)
-#include <emmintrin.h>
-#endif
-
-#ifndef __APPLE__
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
#include <malloc.h>
#endif
-// Check that pthread_create/pthread_join return success.
-#define PTHREAD_CREATE(a, b, c, d) ASSERT_EQ(0, pthread_create(a, b, c, d))
-#define PTHREAD_JOIN(a, b) ASSERT_EQ(0, pthread_join(a, b))
-
#if ASAN_HAS_EXCEPTIONS
# define ASAN_THROW(x) throw (x)
#else
}
// { dg-output "ERROR: AddressSanitizer:? heap-use-after-free on address.*(\n|\r\n|\r)" }
-// { dg-output " #37 0x\[0-9a-f\]+ (in \[^\n\r]*DeepFree\[^\n\r]*36|\[(\]).*(\n|\r\n|\r)" }
-// { dg-output " #99 0x\[0-9a-f\]+ (in \[^\n\r]*DeepFree\[^\n\r]*98|\[(\]).*(\n|\r\n|\r)" }
-// { dg-output " #116 0x\[0-9a-f\]+ (in \[^\n\r]*DeepFree\[^\n\r]*115|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output " #37 0x\[0-9a-f\]+ +(in \[^\n\r]*DeepFree\[^\n\r]*36|\[(\]).*(\n|\r\n|\r)" }
+// { dg-output " #99 0x\[0-9a-f\]+ +(in \[^\n\r]*DeepFree\[^\n\r]*98|\[(\]).*(\n|\r\n|\r)" }
+// { dg-output " #116 0x\[0-9a-f\]+ +(in \[^\n\r]*DeepFree\[^\n\r]*115|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
}
// { dg-output "AddressSanitizer:? global-buffer-overflow.*(\n|\r\n|\r)" }
-// { dg-output " #0 0x\[0-9a-f\]+ (in \[^\n\r]*call4\[^\n\r]*|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
-// { dg-output " #1 0x\[0-9a-f\]+ (in \[^\n\r]*call3\[^\n\r]*|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
-// { dg-output " #2 0x\[0-9a-f\]+ (in \[^\n\r]*call2\[^\n\r]*|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
-// { dg-output " #3 0x\[0-9a-f\]+ (in \[^\n\r]*call1\[^\n\r]*|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
-// { dg-output " #4 0x\[0-9a-f\]+ (in \[^\n\r]*main\[^\n\r]*|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output " #0 0x\[0-9a-f\]+ +(in \[^\n\r]*call4\[^\n\r]*|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output " #1 0x\[0-9a-f\]+ +(in \[^\n\r]*call3\[^\n\r]*|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output " #2 0x\[0-9a-f\]+ +(in \[^\n\r]*call2\[^\n\r]*|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output " #3 0x\[0-9a-f\]+ +(in \[^\n\r]*call1\[^\n\r]*|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output " #4 0x\[0-9a-f\]+ +(in \[^\n\r]*main\[^\n\r]*|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
return 0;
}
-// { dg-output "Using the defaults from __asan_default_options:.* foo=bar.*(\n|\r\n|\r)" }
+// { dg-output "WARNING: found 1 unrecognized flag\\(s\\):(\n|\r\n|\r).*foo(\n|\r\n|\r)" }
// { dg-output "ERROR: AddressSanitizer:? heap-buffer-overflow on address\[^\n\r]*" }
// { dg-output "0x\[0-9a-f\]+ at pc 0x\[0-9a-f\]+ bp 0x\[0-9a-f\]+ sp 0x\[0-9a-f\]+\[^\n\r]*(\n|\r\n|\r)" }
// { dg-output "\[^\n\r]*READ of size 4 at 0x\[0-9a-f\]+ thread T0\[^\n\r]*(\n|\r\n|\r)" }
-// { dg-output " #0 0x\[0-9a-f\]+ (in \[^\n\r]*LargeFunction\[^\n\r]*(large-func-test-1.C:18|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" }
+// { dg-output " #0 0x\[0-9a-f\]+ +(in \[^\n\r]*LargeFunction\[^\n\r]*(large-func-test-1.C:18|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" }
// { dg-output "\[^\n\r]*0x\[0-9a-f\]+ is located 44 bytes to the right of 400-byte region.*(\n|\r\n|\r)" }
// { dg-output "\[^\n\r]*allocated by thread T0 here:\[^\n\r]*(\n|\r\n|\r)" }
-// { dg-output " #0( 0x\[0-9a-f\]+ (in _*(interceptor_|)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
-// { dg-output " #1|) 0x\[0-9a-f\]+ (in (operator new|_*_Zn\[aw\]\[mj\])|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output " #0( 0x\[0-9a-f\]+ +(in _*(interceptor_|)malloc|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
+// { dg-output " #1|) 0x\[0-9a-f\]+ +(in (operator new|(wrap|)_*_Zn\[aw\]\[mj\])|\[(\])\[^\n\r]*(\n|\r\n|\r)" }
--- /dev/null
+/* { dg-do run } */
+/* { dg-shouldfail "asan" } */
+/* { dg-additional-options "-O0 -fno-lto" } */
+
+#include <stdlib.h>
+
+typedef __SIZE_TYPE__ size_t;
+inline void * operator new (size_t, void *p) { return p; }
+
+
+struct vec
+{
+ int size;
+};
+
+struct vnull
+{
+ operator vec() { return vec(); }
+};
+vnull vNULL;
+
+struct A
+{
+ A(): value2 (vNULL), value3 (vNULL) {}
+ int value;
+ vec value2;
+ vec value3;
+};
+
+int main()
+{
+ int *array = (int *)malloc (sizeof (int) * 1);
+ A *a = new (array) A ();
+ free (array);
+}
+
+/* { dg-output "ERROR: AddressSanitizer: heap-buffer-overflow.*(\n|\r\n|\r)" } */
+/* { dg-output " #0 0x\[0-9a-f\]+ +in A::A()" } */
--- /dev/null
+//===-- sanitizer_pthread_wrappers.h ----------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of *Sanitizer runtime.
+// It provides handy wrappers for thread manipulation, that:
+// a) assert on any failure rather than returning an error code
+// b) defines pthread-like interface on platforms where where <pthread.h>
+// is not supplied by default.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_PTHREAD_WRAPPERS_H
+#define SANITIZER_PTHREAD_WRAPPERS_H
+
+#include "sanitizer_test_utils.h"
+
+#if !defined(_WIN32)
+# include <pthread.h>
+// Simply forward the arguments and check that the pthread functions succeed.
+# define PTHREAD_CREATE(a, b, c, d) ASSERT_EQ(0, pthread_create(a, b, c, d))
+# define PTHREAD_JOIN(a, b) ASSERT_EQ(0, pthread_join(a, b))
+#else
+typedef HANDLE pthread_t;
+
+struct PthreadHelperCreateThreadInfo {
+ void *(*start_routine)(void *);
+ void *arg;
+};
+
+inline DWORD WINAPI PthreadHelperThreadProc(void *arg) {
+ PthreadHelperCreateThreadInfo *start_data =
+ reinterpret_cast<PthreadHelperCreateThreadInfo*>(arg);
+ void *ret = (start_data->start_routine)(start_data->arg);
+ delete start_data;
+ return (DWORD)ret;
+}
+
+inline void PTHREAD_CREATE(pthread_t *thread, void *attr,
+ void *(*start_routine)(void *), void *arg) {
+ ASSERT_EQ(0, attr) << "Thread attributes are not supported yet.";
+ PthreadHelperCreateThreadInfo *data = new PthreadHelperCreateThreadInfo;
+ data->start_routine = start_routine;
+ data->arg = arg;
+ *thread = CreateThread(0, 0, PthreadHelperThreadProc, data, 0, 0);
+ ASSERT_NE(nullptr, *thread) << "Failed to create a thread.";
+}
+
+inline void PTHREAD_JOIN(pthread_t thread, void **value_ptr) {
+ ASSERT_EQ(0, value_ptr) << "Nonzero value_ptr is not supported yet.";
+ ASSERT_EQ(WAIT_OBJECT_0, WaitForSingleObject(thread, INFINITE));
+ ASSERT_NE(0, CloseHandle(thread));
+}
+
+inline void pthread_exit(void *retval) {
+ ASSERT_EQ(0, retval) << "Nonzero retval is not supported yet.";
+ ExitThread((DWORD)retval);
+}
+#endif // _WIN32
+
+#endif // SANITIZER_PTHREAD_WRAPPERS_H
--- /dev/null
+//===-- sanitizer_test_config.h ---------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of *Sanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+#if !defined(INCLUDED_FROM_SANITIZER_TEST_UTILS_H)
+# error "This file should be included into sanitizer_test_utils.h only"
+#endif
+
+#ifndef SANITIZER_TEST_CONFIG_H
+#define SANITIZER_TEST_CONFIG_H
+
+#include <vector>
+#include <string>
+#include <map>
+
+#if SANITIZER_USE_DEJAGNU_GTEST
+# include "dejagnu-gtest.h"
+#else
+# include "gtest/gtest.h"
+#endif
+
+#endif // SANITIZER_TEST_CONFIG_H
#define SANITIZER_TEST_UTILS_H
#if defined(_WIN32)
-typedef unsigned __int8 uint8_t;
-typedef unsigned __int16 uint16_t;
-typedef unsigned __int32 uint32_t;
-typedef unsigned __int64 uint64_t;
-typedef __int8 int8_t;
-typedef __int16 int16_t;
-typedef __int32 int32_t;
-typedef __int64 int64_t;
+// <windows.h> should always be the first include on Windows.
+# include <windows.h>
+// MSVS headers define max/min as macros, so std::max/min gets crazy.
+# undef max
+# undef min
+#endif
+
+#if !defined(SANITIZER_EXTERNAL_TEST_CONFIG)
+# define INCLUDED_FROM_SANITIZER_TEST_UTILS_H
+# include "sanitizer_test_config.h"
+# undef INCLUDED_FROM_SANITIZER_TEST_UTILS_H
+#endif
+
+#include <stdint.h>
+
+#if defined(_MSC_VER)
# define NOINLINE __declspec(noinline)
-# define USED
-#else // defined(_WIN32)
+#else // defined(_MSC_VER)
# define NOINLINE __attribute__((noinline))
+#endif // defined(_MSC_VER)
+
+#if !defined(_MSC_VER) || defined(__clang__)
+# define UNUSED __attribute__((unused))
# define USED __attribute__((used))
-#include <stdint.h>
-#endif // defined(_WIN32)
+#else
+# define UNUSED
+# define USED
+#endif
#if !defined(__has_feature)
#define __has_feature(x) 0
#endif
-#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
-# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
+#ifndef ATTRIBUTE_NO_SANITIZE_ADDRESS
+# if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+# define ATTRIBUTE_NO_SANITIZE_ADDRESS \
__attribute__((no_sanitize_address))
-#else
-# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
-#endif
+# else
+# define ATTRIBUTE_NO_SANITIZE_ADDRESS
+# endif
+#endif // ATTRIBUTE_NO_SANITIZE_ADDRESS
#if __LP64__ || defined(_WIN64)
# define SANITIZER_WORDSIZE 64
// Make the compiler thinks that something is going on there.
inline void break_optimization(void *arg) {
+#if !defined(_WIN32) || defined(__clang__)
__asm__ __volatile__("" : : "r" (arg) : "memory");
+#endif
}
// This function returns its parameter but in such a way that compiler
return my_rand_r(&global_seed);
}
+// Set availability of platform-specific functions.
+
+#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__) && !defined(_WIN32)
+# define SANITIZER_TEST_HAS_POSIX_MEMALIGN 1
+#else
+# define SANITIZER_TEST_HAS_POSIX_MEMALIGN 0
+#endif
+
+#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(_WIN32)
+# define SANITIZER_TEST_HAS_MEMALIGN 1
+# define SANITIZER_TEST_HAS_PVALLOC 1
+# define SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE 1
+#else
+# define SANITIZER_TEST_HAS_MEMALIGN 0
+# define SANITIZER_TEST_HAS_PVALLOC 0
+# define SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE 0
+#endif
+
+#if !defined(__APPLE__)
+# define SANITIZER_TEST_HAS_STRNLEN 1
+#else
+# define SANITIZER_TEST_HAS_STRNLEN 0
+#endif
#endif // SANITIZER_TEST_UTILS_H
+++ /dev/null
-// { dg-do run }
-// { dg-skip-if "" { *-*-* } { "*" } { "-O2" } }
-// { dg-options "-fno-builtin-malloc -fno-builtin-free" }
-// { dg-shouldfail "asan" }
-
-#include <stdio.h>
-#include <stdlib.h>
-
-extern "C"
-bool __asan_symbolize(const void *, char *out_buffer, int out_size) {
- snprintf(out_buffer, out_size, "MySymbolizer");
- return true;
-}
-
-int main() {
- char *x = (char*)malloc(10);
- free(x);
- return x[5];
-}
-
-// { dg-output "MySymbolizer" }
--- /dev/null
+// { dg-do compile { target c++11 } }
+
+constexpr int
+fn1 (int i, int j)
+{
+ return i << j; // { dg-error "is negative" }
+}
+
+constexpr int i1 = fn1 (1, -1);
+
+constexpr int
+fn2 (int i, int j)
+{
+ return i << j; // { dg-error "is >= than the precision of the left operand" }
+}
+
+constexpr int i2 = fn2 (1, 200);
+
+constexpr int
+fn3 (int i, int j)
+{
+ return i << j; // { dg-error "is negative" }
+}
+
+constexpr int i3 = fn3 (-1, 2);
+
+constexpr int
+fn4 (int i, int j)
+{
+ return i << j; // { dg-error "overflows" }
+}
+
+constexpr int i4 = fn4 (__INT_MAX__, 2);
+
+constexpr int
+fn5 (int i, int j)
+{
+ return i << j;
+}
+
+constexpr int i5 = fn5 (__INT_MAX__, 1);
+
+constexpr int
+fn6 (unsigned int i, unsigned int j)
+{
+ return i << j; // { dg-error "is >= than the precision of the left operand" }
+}
+
+constexpr int i6 = fn6 (1, -1);
+
+constexpr int
+fn7 (int i, int j)
+{
+ return i >> j; // { dg-error "is negative" }
+}
+
+constexpr int i7 = fn7 (1, -1);
+
+constexpr int
+fn8 (int i, int j)
+{
+ return i >> j;
+}
+
+constexpr int i8 = fn8 (-1, 1);
+
+constexpr int
+fn9 (int i, int j)
+{
+ return i >> j; // { dg-error "is >= than the precision of the left operand" }
+}
+
+constexpr int i9 = fn9 (1, 200);
--- /dev/null
+// { dg-do compile { target c++14 } }
+
+constexpr int p = 1;
+constexpr __PTRDIFF_TYPE__ bar (int a)
+{
+ return ((__PTRDIFF_TYPE__) &p) << a; // { dg-error "is not a constant expression" }
+}
+constexpr __PTRDIFF_TYPE__ r = bar (2);
+constexpr __PTRDIFF_TYPE__ s = bar (0); // { dg-error "conversion from pointer" }
--- /dev/null
+// { dg-do run }
+// { dg-options "-fsanitize=alignment -Wall -Wno-unused-variable -std=c++11" }
+
+typedef const long int L;
+int a = 1;
+L b = 2;
+
+int
+main (void)
+{
+ int *p = &a;
+ L *l = &b;
+
+ int &r = *p;
+ auto &r2 = *p;
+ L &lr = *l;
+
+ // Try an rvalue reference.
+ auto &&r3 = *p;
+
+ // Don't evaluate the reference initializer twice.
+ int i = 1;
+ int *q = &i;
+ int &qr = ++*q;
+ if (i != 2)
+ __builtin_abort ();
+}
--- /dev/null
+// Limit this to known non-strict alignment targets.
+// { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } }
+// { dg-options "-fsanitize=alignment -Wall -Wno-unused-variable -std=c++11" }
+
+typedef const long int L;
+struct S { long int l; char buf[1 + sizeof (int) + sizeof (L)]; } s;
+struct T { char a; int b; long int c; } __attribute__((packed));
+struct U { long int a; struct T b; } u;
+
+int
+main (void)
+{
+ int *p = (int *) &s.buf[1];
+ L *l = (L *) &s.buf[1 + sizeof(int)];
+
+ int &r = *p;
+ auto &r2 = *p;
+ L &lr = *l;
+
+ // Try an rvalue reference.
+ auto &&r3 = *p;
+
+ // Don't evaluate the reference initializer twice.
+ int i = 1;
+ int *q = &i;
+ int &qr = ++*q;
+ if (i != 2)
+ __builtin_abort ();
+
+ int *s = &u.b.b;
+ L *t = &u.b.c;
+ int &r4 = *s;
+ auto &r5 = *s;
+ L &lr2 = *t;
+ auto &&r6 = *s;
+}
+
+// { dg-output "\.C:16:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
+// { dg-output "\.C:17:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
+// { dg-output "\.C:18:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'const L', which requires \[48] byte alignment.*" }
+// { dg-output "\.C:21:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
+// { dg-output "\.C:32:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
+// { dg-output "\.C:33:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment.*" }
+// { dg-output "\.C:34:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'const L', which requires \[48] byte alignment.*" }
+// { dg-output "\.C:35:\[0-9]*:\[\^\n\r]*reference binding to misaligned address 0x\[0-9a-fA-F]* for type 'int', which requires 4 byte alignment" }
--- /dev/null
+// Limit this to known non-strict alignment targets.
+// { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } }
+// { dg-options "-fsanitize=alignment -Wall -Wno-unused-variable -std=c++11" }
+
+#include <new>
+
+struct U
+{
+ int a;
+ void foo () {}
+};
+struct V
+{
+ V () : a (0) {};
+ ~V () { a = 0; };
+ int a;
+ void foo () {}
+ static void bar () {}
+};
+struct S { long int l; char buf[1 + sizeof (U) + 2 * sizeof (V)]; } s;
+
+int
+main (void)
+{
+ U *p = (U *) &s.buf[1];
+ p->foo ();
+ char *q = &s.buf[1 + sizeof (U)];
+ V *u = new (q) V;
+ u->a = 1;
+ u->~V ();
+ V *v = new (&s.buf[1 + sizeof (U) + sizeof (V)]) V;
+ v->foo ();
+ v->bar (); // We don't instrument this right now.
+ v->~V ();
+}
+
+// { dg-output "\.C:26:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct U', which requires 4 byte alignment.*" }
+// { dg-output "\.C:28:\[0-9]*:\[\^\n\r]*constructor call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:14:\[0-9]*:\[\^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:29:\[0-9]*:\[\^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:30:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:15:\[0-9]*:\[\^\n\r]*member access within misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:31:\[0-9]*:\[\^\n\r]*constructor call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:32:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment.*" }
+// { dg-output "\.C:34:\[0-9]*:\[\^\n\r]*member call on misaligned address 0x\[0-9a-fA-F]* for type 'struct V', which requires 4 byte alignment" }
--- /dev/null
+// { dg-do compile }
+// { dg-options "-fsanitize=undefined -Wall -Wno-unused-variable -std=c++11" }
+
+typedef const long int L;
+
+__attribute__((no_sanitize_undefined)) void
+foo (int *p, L *l)
+{
+ int &r = *p;
+ auto &r2 = *p;
+ L &lr = *l;
+ auto &&r3 = *p;
+}
+
+struct U
+{
+ int a;
+ void foo () {}
+};
+
+__attribute__((no_sanitize_undefined)) void
+bar (U *p)
+{
+ p->foo ();
+}
+
+// { dg-final { scan-assembler-not "__ubsan_handle" } }
-/* { dg-do run { target c++11 } } */
-/* { dg-options "-fsanitize=shift -w" } */
-
-#include <stdio.h>
+/* { dg-do run } */
+/* { dg-options "-fsanitize=shift -w -fno-sanitize-recover=shift -std=c++11" } */
int
main (void)
{
- fputs ("UBSAN TEST START\n", stderr);
-
int a = 1;
a <<= 31;
-
- fputs ("UBSAN TEST END\n", stderr);
return 0;
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
-/* { dg-do run { target c++11 } } */
-/* { dg-options "-fsanitize=shift -w" } */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=shift -w -std=c++11" } */
int
main (void)
+++ /dev/null
-/* { dg-do run { target c++1y } } */
-/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable" } */
-/* { dg-shouldfail "ubsan" } */
-
-int
-main (void)
-{
- int y = -18;
- int a[y];
- return 0;
-}
-
-/* { dg-output "terminate called after throwing an instance" } */
/* { dg-do compile } */
-/* { dg-options "-fsanitize=integer-divide-by-zero -w" } */
+/* { dg-options "-fsanitize=integer-divide-by-zero" } */
+
+/* TODO: We expect an error on the invalid case here, because that
+ must be a constant-expression. This will be fixed when we have
+ proper delayed folding. */
void
foo (int i)
{
switch (i)
- case 0 * (1 / 0): /* { dg-error "is not a constant expression" } */
- ;
+ case 0 * (1 / 0): /* { dg-warning "division by zero" } */
+ ; /* { dg-error "division by zero" "" { xfail *-*-* } 10 } */
}
--- /dev/null
+// { dg-do run { target { int32 } } }
+// { dg-options "-fsanitize=float-cast-overflow" }
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-__INT_MAX__ - 1)
+#define UINT_MAX 2 * (unsigned) __INT_MAX__ + 1
+
+struct S
+{
+ int i:1;
+} s;
+
+struct T
+{
+ unsigned int i:1;
+} t;
+
+int
+main (void)
+{
+ volatile double d;
+
+#define CHECK_BOUNDARY(VAR, VAL) \
+ (VAR) = (VAL) - 1.5; \
+ (VAR) = (VAL) - 1.0; \
+ (VAR) = (VAL) - 0.75; \
+ (VAR) = (VAL) - 0.5; \
+ (VAR) = (VAL) - 0.0000001; \
+ (VAR) = (VAL) - 0.0; \
+ (VAR) = (VAL); \
+ (VAR) = (VAL) + 0.0; \
+ (VAR) = (VAL) + 0.0000001; \
+ (VAR) = (VAL) + 0.5; \
+ (VAR) = (VAL) + 0.75; \
+ (VAR) = (VAL) + 1.0; \
+ (VAR) = (VAL) + 1.5;
+
+ /* Signed bit-field. (INT_MIN, INT_MAX) is valid. */
+ d = INT_MIN;
+ CHECK_BOUNDARY (s.i, d);
+ d = 0.0;
+ CHECK_BOUNDARY (s.i, d);
+ d = INT_MAX;
+ CHECK_BOUNDARY (s.i, d);
+
+ /* Unsigned bit-field. (0, UINT_MAX) is valid. */
+ d = UINT_MAX;
+ CHECK_BOUNDARY (t.i, d);
+ d = 0.0;
+ CHECK_BOUNDARY (t.i, d);
+
+ return 0;
+}
+
+/* { dg-output "value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type" } */
--- /dev/null
+// { dg-do run }
+// { dg-options "-fsanitize=null -Wall -Wno-unused-variable -std=c++11" }
+
+typedef const long int L;
+
+int
+main (void)
+{
+ int *p = 0;
+ L *l = 0;
+
+ int &r = *p;
+ auto &r2 = *p;
+ L &lr = *l;
+
+ // Try an rvalue reference.
+ auto &&r3 = *p;
+
+ // Don't evaluate the reference initializer twice.
+ int i = 1;
+ int *q = &i;
+ int &qr = ++*q;
+ if (i != 2)
+ __builtin_abort ();
+}
+
+// { dg-output "reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
+// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
+// { dg-output "\[^\n\r]*reference binding to null pointer of type 'const L'(\n|\r\n|\r)" }
+// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'" }
--- /dev/null
+// Limit this to known non-strict alignment targets.
+// { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } }
+// { dg-options "-fsanitize=null -Wall -Wno-unused-variable -std=c++11" }
+
+#include <new>
+
+struct U
+{
+ int a;
+ void foo () {}
+};
+struct V
+{
+ V () {};
+ ~V () {};
+ int a;
+ void foo () {}
+ static void bar () {}
+};
+struct S { long int l; char buf[1 + sizeof (U) + 2 * sizeof (V)]; } s;
+
+int
+main (void)
+{
+ U *p = 0;
+ p->foo ();
+ char *q = 0;
+ V *u = new (q) V;
+ u->~V ();
+ V *v = new (q) V;
+ v->foo ();
+ v->bar (); // We don't instrument this right now.
+ v->~V ();
+}
+
+// { dg-output "\.C:26:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct U'.*" }
+// { dg-output "\.C:29:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'.*" }
+// { dg-output "\.C:31:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'.*" }
+// { dg-output "\.C:33:\[0-9]*:\[\^\n\r]*member call on null pointer of type 'struct V'" }
--- /dev/null
+// { dg-do run }
+// { dg-options "-fsanitize=null" }
+
+int
+main (void)
+{
+ int *p = 0;
+
+ int &r1 = *p;
+ int &r2 = *p;
+ int &r3 = *p;
+ int &r4 = *p;
+ int &r5 = *p;
+}
+
+// { dg-output "reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
+// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
+// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
+// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" }
+// { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'" }
--- /dev/null
+// { dg-do run }
+// { dg-options "-O -fsanitize=null -fno-sanitize-recover=null -fdump-tree-sanopt-details" }
+// { dg-skip-if "" { *-*-* } { "-flto -fno-fat-lto-objects" } }
+// { dg-shouldfail "ubsan" }
+
+int
+main (void)
+{
+ int *p = 0;
+
+ int &r1 = *p;
+ int &r2 = *p;
+ int &r3 = *p;
+ int &r4 = *p;
+ int &r5 = *p;
+}
+
+// { dg-output "reference binding to null pointer of type 'int'" }
+// { dg-final { scan-tree-dump-times "Optimizing" 4 "sanopt"} }
+// { dg-final { cleanup-tree-dump "sanopt" } }
--- /dev/null
+// { dg-do run }
+// { dg-options "-O -fsanitize=null -fsanitize-undefined-trap-on-error -fdump-tree-sanopt-details" }
+// { dg-skip-if "" { *-*-* } { "-flto -fno-fat-lto-objects" } }
+// { dg-shouldfail "ubsan" }
+
+int
+main (void)
+{
+ int *p = 0;
+
+ int &r1 = *p;
+ int &r2 = *p;
+ int &r3 = *p;
+ int &r4 = *p;
+ int &r5 = *p;
+}
+
+// { dg-final { scan-tree-dump-times "Optimizing" 4 "sanopt"} }
+// { dg-final { cleanup-tree-dump "sanopt" } }
--- /dev/null
+// PR c++/67941
+// { dg-do run { target c++11 } }
+// { dg-options -fsanitize=null }
+
+int main(){ (+[](){})(); }
--- /dev/null
+// { dg-do compile }
+// { dg-options "-fsanitize=undefined -fpermissive" }
+
+struct T { int c; char d[]; };
+
+struct T t = { 1, "a" }; // { dg-warning "initializer-string for array of chars is too long" }
+
+int
+baz (int i)
+{
+ return t.d[i];
+}
+
+int
+main (void)
+{
+ baz (3);
+}
--- /dev/null
+// PR sanitizer/63813
+// { dg-do compile }
+// { dg-options "-fsanitize=undefined -O1" }
+
+struct A {};
+struct B { long foo () const; A &bar () const; };
+
+A &
+B::bar () const
+{
+ return *reinterpret_cast <A *> (foo ());
+}
--- /dev/null
+// PR sanitizer/63956
+// { dg-do compile }
+// { dg-options "-std=c++14 -fsanitize=undefined,float-divide-by-zero,float-cast-overflow" }
+
+#define SA(X) static_assert((X),#X)
+#define INT_MIN (-__INT_MAX__ - 1)
+
+constexpr int
+fn1 (int a, int b)
+{
+ if (b != 2)
+ a <<= b;
+ return a;
+}
+
+constexpr int i1 = fn1 (5, 3);
+constexpr int i2 = fn1 (5, -2); // { dg-error "is negative" }
+constexpr int i3 = fn1 (5, sizeof (int) * __CHAR_BIT__); // { dg-error "is >= than the precision of the left operand" }
+constexpr int i4 = fn1 (5, 256); // { dg-error "is >= than the precision of the left operand" }
+constexpr int i5 = fn1 (5, 2);
+constexpr int i6 = fn1 (-2, 4); // { dg-error "is negative" }
+constexpr int i7 = fn1 (0, 2);
+
+SA (i1 == 40);
+SA (i5 == 5);
+SA (i7 == 0);
+
+constexpr int
+fn2 (int a, int b)
+{
+ if (b != 2)
+ a >>= b;
+ return a;
+}
+
+constexpr int j1 = fn2 (4, 1);
+constexpr int j2 = fn2 (4, -1); // { dg-error "is negative" }
+constexpr int j3 = fn2 (10, sizeof (int) * __CHAR_BIT__); // { dg-error "is >= than the precision of the left operand" }
+constexpr int j4 = fn2 (1, 256); // { dg-error "is >= than the precision of the left operand" }
+constexpr int j5 = fn2 (5, 2);
+constexpr int j6 = fn2 (-2, 4);
+constexpr int j7 = fn2 (0, 4);
+
+SA (j1 == 2);
+SA (j5 == 5);
+SA (j7 == 0);
+
+constexpr int
+fn3 (int a, int b)
+{
+ if (b != 2)
+ a = a / b;
+ return a;
+}
+
+constexpr int k1 = fn3 (8, 4);
+constexpr int k2 = fn3 (7, 0); // { dg-error "is not a constant expression" }
+constexpr int k3 = fn3 (INT_MIN, -1); // { dg-error "overflow in constant expression" }
+
+SA (k1 == 2);
+
+constexpr float
+fn4 (float a, float b)
+{
+ if (b != 2.0)
+ a = a / b;
+ return a;
+}
+
+constexpr float l1 = fn4 (5.0, 3.0);
+constexpr float l2 = fn4 (7.0, 0.0); // { dg-error "is not a constant expression" }
+
+constexpr int
+fn5 (const int *a, int b)
+{
+ if (b != 2)
+ b = a[b];
+ return b;
+}
+
+constexpr int m1[4] = { 1, 2, 3, 4 };
+constexpr int m2 = fn5 (m1, 3);
+constexpr int m3 = fn5 (m1, 4); // { dg-error "array subscript out of bound" }
+
+constexpr int
+fn6 (const int &a, int b)
+{
+ if (b != 2)
+ b = a;
+ return b;
+}
+
+constexpr int
+fn7 (const int *a, int b)
+{
+ if (b != 3)
+ return fn6 (*a, b);
+ return 7;
+}
+
+constexpr int n1 = 7;
+constexpr int n2 = fn7 (&n1, 5);
+constexpr int n3 = fn7 ((const int *) 0, 8); // { dg-error "is not a constant expression" }
+
+constexpr int
+fn8 (int i)
+{
+ constexpr int g[10] = { };
+ return g[i];
+}
+
+constexpr int o1 = fn8 (9);
+constexpr int o2 = fn8 (10); // { dg-error "array subscript out of bound" }
+
+constexpr int
+fn9 (int a, int b)
+{
+ if (b != 0)
+ return a + b;
+ return a;
+}
+
+constexpr int p1 = fn9 (42, 7);
+constexpr int p2 = fn9 (__INT_MAX__, 1); // { dg-error "overflow in constant expression" }
+constexpr int p3 = fn9 (__INT_MAX__, -1);
+constexpr int p4 = fn9 (INT_MIN, 1);
+constexpr int p5 = fn9 (INT_MIN, -1); // { dg-error "overflow in constant expression" }
+
+SA (p1 == 49);
+SA (p3 == __INT_MAX__ - 1);
+SA (p4 == INT_MIN + 1);
+
+constexpr int
+fn10 (int a, int b)
+{
+ if (b != 0)
+ return a * b;
+ return a;
+}
+
+constexpr int q1 = fn10 (10, 10);
+constexpr int q2 = fn10 (__INT_MAX__, 2); // { dg-error "overflow in constant expression" }
+constexpr int q3 = fn10 (INT_MIN, 2); // { dg-error "overflow in constant expression" }
+constexpr int q4 = fn10 (-1, -1);
+
+SA (q1 == 100);
+SA (q4 == 1);
+
+constexpr int
+fn11 (double d)
+{
+ int i = d;
+ if (i != 0)
+ return i;
+ return i * 2;
+}
+
+constexpr int r1 = fn11 (3.4);
+constexpr int r2 = fn11 (__builtin_inf ()); // { dg-error "overflow in constant expression" }
+
+constexpr int
+fn12 (int i)
+{
+ if (i == 42)
+ __builtin_unreachable (); // { dg-error "is not a constant expression" }
+ return i + 10;
+}
+
+constexpr int s1 = fn12 (1);
+constexpr int s2 = fn12 (42);
+
+SA (s1 == 11);
--- /dev/null
+// PR sanitizer/64632
+// { dg-do run }
+// { dg-options "-fsanitize=vptr -fno-sanitize-recover=vptr" }
+
+struct S
+{
+ S () : a(0) {}
+ int a;
+ int f () { return a; }
+ virtual int v () { return 0; }
+};
+
+struct X : virtual S
+{
+ int v () { return 2; }
+};
+
+int
+main ()
+{
+ X x;
+ return x.f ();
+}
--- /dev/null
+// PR sanitizer/64984
+// { dg-do compile }
+// { dg-options "-fsanitize=vptr -std=gnu++11" }
+
+template <typename X, X> struct K
+{
+ static constexpr X v = 0;
+ typedef K t;
+};
+template <typename...> struct A;
+template <typename X, typename Y>
+struct A<X, Y> : Y
+{
+};
+template <typename X> X M ();
+template <typename...> struct B;
+template <typename X, typename Y>
+struct B<X, Y> : K<int, noexcept (static_cast<X>(M<Y>()))>
+{
+};
+template <typename X, typename... Y>
+struct G : A<int, B<X, Y...>>::t
+{
+};
+template <typename X> struct J : G<X, X&&>
+{
+};
+template <typename X> X&& foo (X&);
+template <typename X> X&& bar (X&&);
+template <typename X> struct P
+{
+ P (X& x) : q (x) {}
+ X q;
+};
+template <typename...> struct Q;
+template <typename X>
+struct Q<X> : P<X>
+{
+ typedef P<X> r;
+ X& s (Q&);
+ Q (X& x) : r (x) {}
+ Q (Q&& x) noexcept (J<X>::v) : r (foo<X>(s (x)))
+ {
+ }
+};
+template <typename... X> struct I : Q<X...>
+{
+ I ();
+ I (X&... x) : Q<X...>(x...)
+ {
+ }
+};
+template <typename... X>
+I<X&&...> baz (X&&... x)
+{
+ return I <X&&...> (foo<X>(x)...);
+}
+template <typename X> struct F
+{
+ int p;
+ void operator[] (X&& x)
+ {
+ baz (bar (x));
+ }
+};
+struct U
+{
+ virtual ~U ();
+};
+
+int
+main ()
+{
+ F<U> m;
+ m[U ()];
+}
--- /dev/null
+// PR sanitizer/65000
+// { dg-do compile }
+// { dg-options "-O1 -fsanitize=undefined -fno-sanitize-recover" }
+
+struct B { virtual ~B () {} void foo (); };
+struct C { virtual ~C (); };
+struct A : public virtual C {};
+struct D : A { ~D () { d.foo (); } B d; };
+
+void
+bar ()
+{
+ D a;
+}
--- /dev/null
+// PR sanitizer/65019
+// { dg-do compile }
+// { dg-options "-fsanitize=alignment,object-size,vptr -std=c++11 -O2 -fcompare-debug" }
+
+struct A { };
+struct B { };
+struct C final {
+ C (const A &, int);
+ static B *foo (const A &, int = 1);
+ virtual ~C ();
+ void *c;
+};
+
+B *
+C::foo (const A &x, int y)
+{
+ C *d = new C (x, y);
+ if (d->c == nullptr)
+ delete d;
+}
+
+C::~C ()
+{
+}
--- /dev/null
+// PR sanitizer/65583
+// { dg-do compile }
+// { dg-options "-std=c++11 -fsanitize=undefined" }
+
+namespace std
+{
+ inline namespace __cxx11
+ {
+ }
+ template < typename > class allocator;
+ template < class _CharT > struct char_traits;
+ namespace __cxx11
+ {
+ template < typename _CharT, typename _Traits =
+ char_traits < _CharT >, typename _Alloc =
+ allocator < _CharT > >class basic_string;
+ typedef basic_string < char >string;
+ }
+}
+namespace std
+{
+ template < typename _Tp, _Tp __v > struct integral_constant
+ {
+ static constexpr _Tp value = __v;
+ };
+ typedef integral_constant < bool, true > true_type;
+}
+namespace __gnu_cxx
+{
+ template < typename _Tp > class new_allocator
+ {
+ public:
+ typedef long unsigned size_type;
+ typedef _Tp value_type;
+ template < typename _Tp1 > struct rebind
+ {
+ typedef new_allocator < _Tp1 > other;
+ };
+ };
+}
+namespace std
+{
+ template < typename _Tp > using __allocator_base =
+ __gnu_cxx::new_allocator < _Tp >;
+ template < typename _Tp > class allocator:public __allocator_base < _Tp >
+ {
+ };
+ template < typename _Alloc, typename _Tp > class __alloctr_rebind_helper
+ {
+ template < typename _Alloc2, typename _Tp2 >
+ static constexpr true_type _S_chk (typename _Alloc2::template rebind <
+ _Tp2 >::other *);
+ public:
+ using __type = decltype (_S_chk < _Alloc, _Tp > (nullptr));
+ };
+ template < typename _Alloc, typename _Tp, bool =
+ __alloctr_rebind_helper < _Alloc,
+ _Tp >::__type::value > struct __alloctr_rebind;
+ template < typename _Alloc, typename _Tp > struct __alloctr_rebind <_Alloc,
+ _Tp, true >
+ {
+ typedef typename _Alloc::template rebind < _Tp >::other __type;
+ };
+ template < typename _Alloc > struct allocator_traits
+ {
+ typedef typename _Alloc::value_type value_type;
+ static value_type *_S_pointer_helper (...);
+ typedef decltype (_S_pointer_helper ((_Alloc *) 0)) __pointer;
+ typedef __pointer pointer;
+ template < typename _Tp >
+ static typename _Tp::size_type _S_size_type_helper (_Tp *);
+ typedef decltype (_S_size_type_helper ((_Alloc *) 0)) __size_type;
+ typedef __size_type size_type;
+ template < typename _Tp > using rebind_alloc =
+ typename __alloctr_rebind < _Alloc, _Tp >::__type;
+ };
+}
+namespace __gnu_cxx
+{
+ template < typename _Alloc > struct __alloc_traits:std::allocator_traits <
+ _Alloc >
+ {
+ typedef std::allocator_traits < _Alloc > _Base_type;
+ template < typename _Tp > struct rebind
+ {
+ typedef typename _Base_type::template rebind_alloc < _Tp > other;
+ };
+ };
+}
+namespace std
+{
+ namespace __cxx11
+ {
+ template < typename _CharT, typename _Traits,
+ typename _Alloc > class basic_string
+ {
+ typedef typename __gnu_cxx::__alloc_traits < _Alloc >::template rebind <
+ _CharT >::other _Char_alloc_type;
+ typedef __gnu_cxx::__alloc_traits < _Char_alloc_type > _Alloc_traits;
+ typedef _Char_alloc_type allocator_type;
+ typedef typename _Alloc_traits::size_type size_type;
+ typedef typename _Alloc_traits::pointer pointer;
+ struct _Alloc_hider:allocator_type
+ {
+ _Alloc_hider (pointer __dat, const _Alloc & __a)
+ {
+ }
+ };
+ _Alloc_hider _M_dataplus;
+ union
+ {
+ size_type _M_allocated_capacity;
+ };
+ pointer _M_local_data ()
+ {
+ }
+ void _M_dispose ()
+ {
+ _M_destroy (_M_allocated_capacity);
+ }
+ void _M_destroy (size_type __size) throw ()
+ {
+ }
+ public:
+ basic_string (const _CharT * __s, const _Alloc & __a = _Alloc ()):_M_dataplus (_M_local_data (),
+ __a)
+ {
+ _M_dispose ();
+ }
+ };
+ }
+ class FileHandle
+ {
+ std::string fname;
+ FileHandle (const char *fname);
+ };
+ FileHandle::FileHandle (const char *fname):fname (fname)
+ {
+ }
+}
--- /dev/null
+// PR sanitizer/66452
+// { dg-do compile }
+// { dg-options "-Wall -fsanitize=undefined" }
+
+class A {
+public:
+ A(int);
+};
+class B {
+ A m_fn1() const;
+};
+A B::m_fn1() const {
+ for (int i = 0;i;)
+ ;
+ return 0;
+}
--- /dev/null
+// PR sanitizer/66977
+// { dg-do compile }
+// { dg-options "-fsanitize=shift -Wmaybe-uninitialized -O" }
+
+class Foo {
+
+private:
+
+ int a_;
+
+public:
+
+ Foo (int a) : a_(a) {};
+
+ inline int get_a () { return a_; };
+};
+
+int bar (int (Foo::*get)()) {
+ Foo *A = new Foo(1);
+ int result = (A->*get)();
+ delete (A);
+ return result;
+}
+
+int main () {
+ return bar (&Foo::get_a);
+}
// { dg-do run }
-// { dg-options "-fsanitize=return" }
-
-#include <stdio.h>
+// { dg-options "-fsanitize=return -fno-sanitize-recover=return" }
struct S { S (); ~S (); };
int
main ()
{
- fputs ("UBSAN TEST START\n", stderr);
-
foo (1);
foo (14);
-
- fputs ("UBSAN TEST END\n", stderr);
}
-
-/* { dg-output "UBSAN TEST START(\n|\r\n|\r)UBSAN TEST END" } */
--- /dev/null
+// { dg-do compile }
+// { dg-options "-fsanitize=return" }
+
+struct S { S (); ~S (); };
+
+S::S () {}
+S::~S () {}
+
+__attribute__((no_sanitize_undefined))
+int
+foo (int x)
+{
+ S a;
+ {
+ S b;
+ if (x)
+ return 1;
+ }
+}
+
+int
+main ()
+{
+ foo (0);
+}
+
+// { dg-final { scan-assembler-not "__ubsan_handle" } }
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=shift -w" } */
+/* { dg-shouldfail "ubsan" } */
+
+int
+foo (int x)
+{
+ /* None of the following should pass. */
+ switch (x)
+ {
+ case 1 >> -1: /* { dg-error "is not a constant expression" "" { xfail { *-*-* } } } */
+ case -1 >> -1: /* { dg-error "is not a constant expression" "" { xfail { *-*-* } } } */
+ case 1 << -1: /* { dg-error "is not a constant expression" "" { xfail { *-*-* } } } */
+ case -1 << -1: /* { dg-error "is not a constant expression" "" { xfail { *-*-* } } } */
+ return 1;
+ }
+ return 0;
+}
+
+int
+bar (int x)
+{
+ /* None of the following should pass. */
+ switch (x)
+ {
+ case -1 >> 200: /* { dg-error "is not a constant expression" "" { xfail { *-*-* } } } */
+ case 1 << 200: /* { dg-error "is not a constant expression" "" { xfail { *-*-* } } } */
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+/* PR sanitizer/64981 */
+/* { dg-do compile { target { i?86-*-* x86_64-*-* } } } */
+/* { dg-options "-fsanitize=address -march=x86-64" } */
+
+int
+main ()
+{
+ __builtin_ia32_rdtsc ();
+ return 0;
+}
+
+/* { dg-final { scan-assembler-not "__builtin_ia32_rdtsc" } } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=bounds" } */
+
+struct T { int c; char d[]; } t = { 1, "abcdefg" };
+
+int
+baz (int i)
+{
+ return t.d[i];
+}
--- /dev/null
+/* PR sanitizer/65280 */
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bounds" } */
+
+void
+foo (int n, int (*b)[n])
+{
+ (*b)[n] = 1;
+}
+
+int
+main ()
+{
+ int a[20];
+ foo (3, (int (*)[3]) &a);
+ return 0;
+}
+
+/* { dg-output "index 3 out of bounds for type 'int \\\[\\\*\\\]'" } */
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=shift -w" } */
+/* { dg-shouldfail "ubsan" } */
+
+int
+foo (int x)
+{
+ /* None of the following should pass. */
+ switch (x)
+ {
+ case 1 >> -1: /* { dg-error "case label does not reduce to an integer constant" } */
+ case -1 >> -1: /* { dg-error "case label does not reduce to an integer constant" } */
+ case 1 << -1: /* { dg-error "case label does not reduce to an integer constant" } */
+ case -1 << -1: /* { dg-error "case label does not reduce to an integer constant" } */
+ return 1;
+ }
+ return 0;
+}
+
+int
+bar (int x)
+{
+ /* None of the following should pass. */
+ switch (x)
+ {
+ case -1 >> 200: /* { dg-error "case label does not reduce to an integer constant" } */
+ case 1 << 200: /* { dg-error "case label does not reduce to an integer constant" } */
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=shift -fwrapv -w -std=c99" } */
+
+int
+main (void)
+{
+ int a = -42;
+ a << 1;
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=shift -fwrapv -w -std=c99" } */
+
+int
+main (void)
+{
+ int a = 1;
+ a <<= 31;
+}
--- /dev/null
+/* PR sanitizer/69099 */
+/* { dg-do compile } */
+/* { dg-options "-std=c11 -pedantic-errors -fsanitize=float-cast-overflow" } */
+/* This is gcc.dg/atomic/c11-atomic-exec-2.c. */
+
+extern void abort (void);
+extern void exit (int);
+
+#define CMPLX(X, Y) __builtin_complex ((X), (Y))
+
+#define TEST_COMPOUND(TYPE, LHSVAL, RHSVAL, OP) \
+ do \
+ { \
+ static volatile _Atomic (TYPE) a = (TYPE) (LHSVAL); \
+ if ((a OP##= (RHSVAL)) != (TYPE) ((TYPE) (LHSVAL) OP (RHSVAL))) \
+ abort (); \
+ if (a != (TYPE) ((TYPE) (LHSVAL) OP (RHSVAL))) \
+ abort (); \
+ } \
+ while (0)
+
+#define TEST_COMPOUND_ARITH(LHSVAL, RHSVAL, OP) \
+ do \
+ { \
+ TEST_COMPOUND (_Bool, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (char, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (signed char, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (unsigned char, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (signed short, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (unsigned short, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (signed int, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (unsigned int, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (signed long, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (unsigned long, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (signed long long, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (unsigned long long, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (float, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (double, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (long double, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (_Complex float, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (_Complex double, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (_Complex long double, (LHSVAL), (RHSVAL), OP); \
+ } \
+ while (0)
+
+#define TEST_COMPOUND_INT(LHSVAL, RHSVAL, OP) \
+ do \
+ { \
+ TEST_COMPOUND (_Bool, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (char, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (signed char, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (unsigned char, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (signed short, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (unsigned short, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (signed int, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (unsigned int, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (signed long, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (unsigned long, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (signed long long, (LHSVAL), (RHSVAL), OP); \
+ TEST_COMPOUND (unsigned long long, (LHSVAL), (RHSVAL), OP); \
+ } \
+ while (0)
+
+static void
+test_mult (void)
+{
+ TEST_COMPOUND_ARITH (1, 2, *);
+ TEST_COMPOUND_ARITH (-3, 5, *);
+ TEST_COMPOUND_ARITH (-7, -20, *);
+ TEST_COMPOUND_ARITH (1.25, 3.5, *);
+ TEST_COMPOUND_ARITH (CMPLX (1.5, 2.5), CMPLX (3.5, 4.5), *);
+ TEST_COMPOUND_ARITH (CMPLX (1.5, 2.5), 2, *);
+}
+
+static void
+test_div (void)
+{
+ TEST_COMPOUND_ARITH (1, 2, /);
+ TEST_COMPOUND_ARITH (-6, 3, /);
+ TEST_COMPOUND_ARITH (-70, -10, /);
+ TEST_COMPOUND_ARITH (1.25, 2.5, /);
+ TEST_COMPOUND_ARITH (CMPLX (1.0, 1.0), CMPLX (0.5, 0.5), /);
+ TEST_COMPOUND_ARITH (CMPLX (1.5, 2.5), 2, /);
+}
+
+static void
+test_mod (void)
+{
+ TEST_COMPOUND_INT (1, 2, %);
+ TEST_COMPOUND_INT (-3, 5, %);
+ TEST_COMPOUND_INT (-7, -2, %);
+}
+
+static void
+test_plus (void)
+{
+ TEST_COMPOUND_ARITH (1, 2, +);
+ TEST_COMPOUND_ARITH (-3, 5, +);
+ TEST_COMPOUND_ARITH (-7, -20, +);
+ TEST_COMPOUND_ARITH (1.25, 3.5, +);
+ TEST_COMPOUND_ARITH (CMPLX (1.5, 2.5), CMPLX (3.5, 4.5), +);
+ TEST_COMPOUND_ARITH (CMPLX (1.5, 2.5), 2, +);
+ static int ia[2];
+ TEST_COMPOUND (int *, &ia[1], 1, +);
+ TEST_COMPOUND (int *, &ia[1], -1, +);
+}
+
+static void
+test_minus (void)
+{
+ TEST_COMPOUND_ARITH (1, 2, -);
+ TEST_COMPOUND_ARITH (-3, 5, -);
+ TEST_COMPOUND_ARITH (-7, -20, -);
+ TEST_COMPOUND_ARITH (3.5, 1.25, -);
+ TEST_COMPOUND_ARITH (CMPLX (3.5, 4.5), CMPLX (1.5, 2.5), -);
+ TEST_COMPOUND_ARITH (CMPLX (3.5, 2.5), 2, -);
+ static int ia[2];
+ TEST_COMPOUND (int *, &ia[1], 1, -);
+ TEST_COMPOUND (int *, &ia[1], -1, -);
+}
+
+static void
+test_lshift (void)
+{
+ TEST_COMPOUND_INT (1, 7, <<);
+ TEST_COMPOUND_INT (15, 3, <<);
+}
+
+static void
+test_rshift (void)
+{
+ TEST_COMPOUND_INT (1, 1, >>);
+ TEST_COMPOUND_INT (127, 4, >>);
+}
+
+static void
+test_and (void)
+{
+ TEST_COMPOUND_INT (0x1234, 0x7856, &);
+ TEST_COMPOUND_INT (-1, 0x12345678, &);
+}
+
+static void
+test_xor (void)
+{
+ TEST_COMPOUND_INT (0x1234, 0x7856, ^);
+ TEST_COMPOUND_INT (-1, 0x12345678, ^);
+}
+
+static void
+test_or (void)
+{
+ TEST_COMPOUND_INT (0x1234, 0x7856, |);
+ TEST_COMPOUND_INT (-12345, 0x12345678, |);
+}
+
+int
+main (void)
+{
+ test_mult ();
+ test_div ();
+ test_mod ();
+ test_plus ();
+ test_minus ();
+ test_lshift ();
+ test_rshift ();
+ test_and ();
+ test_xor ();
+ test_or ();
+ exit (0);
+}
--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+struct
+{
+ int i:1;
+} s;
+
+struct
+{
+ unsigned int i:1;
+} t;
+
+int
+main (void)
+{
+ volatile double d;
+
+#define CHECK_BOUNDARY(VAR, VAL) \
+ (VAR) = (VAL) - 1.5; \
+ (VAR) = (VAL) - 1.0; \
+ (VAR) = (VAL) - 0.5; \
+ (VAR) = (VAL) - 0.0000001; \
+ (VAR) = (VAL) - 0.0; \
+ (VAR) = (VAL); \
+ (VAR) = (VAL) + 0.0; \
+ (VAR) = (VAL) + 0.0000001; \
+ (VAR) = (VAL) + 0.5; \
+ (VAR) = (VAL) + 1.0; \
+ (VAR) = (VAL) + 1.5;
+
+ /* Signed bit-field. (-1, 0) is valid. */
+ d = -1.0;
+ CHECK_BOUNDARY (s.i, d);
+ d = 0.0;
+ CHECK_BOUNDARY (s.i, d);
+ d = 1.0;
+ CHECK_BOUNDARY (s.i, d);
+
+ /* Unsigned bit-field. (0, 1) is valid. */
+ d = -1.0;
+ CHECK_BOUNDARY (t.i, d);
+ d = 0.0;
+ CHECK_BOUNDARY (t.i, d);
+ d = 1.0;
+ CHECK_BOUNDARY (t.i, d);
+
+ return 0;
+}
+
+/* { dg-output "value -2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.5 is outside the range of representable values of type" } */
--- /dev/null
+/* { dg-do run } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=undefined" } */
+
+struct T { int c; char d[]; };
+struct T t = { 1, "a" };
+
+int
+baz (int i)
+{
+ return t.d[i];
+}
+
+int
+main (void)
+{
+ baz (2);
+ return 0;
+}
+
+/* { 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]*\\^" } */
--- /dev/null
+/* PR sanitizer/63690 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined" } */
+
+void
+foo (void)
+{
+ (*"c")++;
+}
--- /dev/null
+/* PR sanitizer/67279 */
+/* { dg-do compile } */
+/* { dg-options "-fsanitize=undefined -w" } */
+
+#define INT_MIN (-__INT_MAX__ - 1)
+
+void
+foo (void)
+{
+ static int a1 = 1 << 31;
+ static int a2 = 10 << 30;
+ static int a3 = 100 << 28;
+ static int a4 = INT_MIN / -1;
+}
--- /dev/null
+/* { dg-do compile } */
+
+#include <arm_neon.h>
+
+int32x4_t
+foo (void)
+{
+ int32x4_t vector_int32x4;
+ int16x4_t vector3_int16x4;
+ int16x4_t vector4_int16x4;
+ static int32_t buffer_int32x4[32];
+
+ vector_int32x4 = vld1q_s32(buffer_int32x4);
+ return vqdmlsl_lane_s16(vector_int32x4, vector3_int16x4, vector4_int16x4, 0);
+}
--- /dev/null
+! { dg-do compile }
+! { dg-options "-fsanitize=float-cast-overflow" }
+
+subroutine pr69055
+ implicit none
+ integer :: n
+ real(8) :: b
+ b = huge(1.0D0)
+ n = b
+end subroutine pr69055
regsub -all "(^|\n)\[^\n\]*(: )?At (top level|global scope):\[^\n\]*" $text "" text
regsub -all "(^|\n)\[^\n\]*: (recursively )?required \[^\n\]*" $text "" text
regsub -all "(^|\n)\[^\n\]*: . skipping \[0-9\]* instantiation contexts \[^\n\]*" $text "" text
+ regsub -all "(^|\n)\[^\n\]*: in constexpr expansion \[^\n\]*" $text "" text
regsub -all "(^|\n) inlined from \[^\n\]*" $text "" text
regsub -all "(^|\n)collect2: error: ld returned \[^\n\]*" $text "" text
regsub -all "(^|\n)collect: re(compiling|linking)\[^\n\]*" $text "" text
# code, 0 otherwise.
proc check_effective_target_fthread_sanitizer {} {
- return [check_no_compiler_messages faddress_sanitizer object {
- void foo (void) { }
- } "-fPIE -pie -fsanitize=thread"]
+ return [check_no_compiler_messages fsanitize_thread executable {
+ int main (void) { return 0; }
+ } "-fsanitize=thread"]
}
#
}
if [info exists ALWAYS_CXXFLAGS] {
set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS]
- set ALWAYS_CXXFLAGS [concat "{additional_flags=-fPIE -pie -fsanitize=thread -g}" $ALWAYS_CXXFLAGS]
+ set ALWAYS_CXXFLAGS [concat "{additional_flags=-fsanitize=thread -g}" $ALWAYS_CXXFLAGS]
} else {
if [info exists TEST_ALWAYS_FLAGS] {
- set TEST_ALWAYS_FLAGS "$link_flags -fPIE -pie -fsanitize=thread -g $TEST_ALWAYS_FLAGS"
+ set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=thread -g $TEST_ALWAYS_FLAGS"
} else {
- set TEST_ALWAYS_FLAGS "$link_flags -fPIE -pie -fsanitize=thread -g"
+ set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=thread -g"
}
}
if { $link_flags != "" } {
set individual_timeout 20
if [check_runtime_nocache tsan_works {
int main () { return 0; }
- } "-fPIE -pie -fsanitize=thread -g"] {
+ } "-fsanitize=thread -g"] {
set dg-do-what-default run
} else {
set dg-do-what-default compile
# along with GCC; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
+# Return 1 if compilation with -fsanitize=undefined is error-free for trivial
+# code, 0 otherwise.
+
+proc check_effective_target_fsanitize_undefined {} {
+ return [check_runtime fsanitize_undefined {
+ int main (void) { return 0; }
+ } "-fsanitize=undefined"]
+}
+
#
# ubsan_link_flags -- compute library path and flags to find libubsan.
# (originally from g++.exp)
append flags " -B${gccpath}/libsanitizer/ubsan/ "
append flags " -L${gccpath}/libsanitizer/ubsan/.libs"
append ld_library_path ":${gccpath}/libsanitizer/ubsan/.libs"
+ append ld_library_path ":${gccpath}/libstdc++-v3/src/.libs"
}
} else {
global tool_root_dir
to handle multiple external decls in different scopes. */
&& ! (DECL_NAME (decl) && TREE_USED (DECL_NAME (decl)))
&& ! DECL_EXTERNAL (decl)
+ && ! DECL_ARTIFICIAL (decl)
&& ! TREE_PUBLIC (decl)
/* A volatile variable might be used in some non-obvious way. */
&& ! TREE_THIS_VOLATILE (decl)
annot_expr_ivdep_kind
};
+/* Internal functions. */
+enum internal_fn {
+#define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) IFN_##CODE,
+#include "internal-fn.def"
+#undef DEF_INTERNAL_FN
+ IFN_LAST
+};
/*---------------------------------------------------------------------------
Type definitions
int length;
/* SSA version number. This field is only used with SSA_NAME. */
unsigned int version;
+
+ /* Internal function code. */
+ enum internal_fn ifn;
} GTY((skip(""))) u;
};
#include "dumpfile.h"
#include "value-prof.h"
#include "predict.h"
+#include "internal-fn.h"
#include <new> // For placement-new.
break;
case CALL_EXPR:
- print_call_name (buffer, CALL_EXPR_FN (node), flags);
+ if (CALL_EXPR_FN (node) != NULL_TREE)
+ print_call_name (buffer, CALL_EXPR_FN (node), flags);
+ else
+ pp_string (buffer, internal_fn_name (CALL_EXPR_IFN (node)));
/* Print parameters. */
pp_space (buffer);
edge e;
bool ret;
+ if (flag_sanitize & SANITIZE_UNREACHABLE)
+ return false;
+
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
stmt = gsi_stmt (gsi);
if (!integer_pow2p (tem1))
return false;
+ type = unsigned_type_for (type);
+ tem1 = fold_convert (type, tem1);
+ tem2 = fold_convert (type, tem2);
+ lowi = fold_convert (type, lowi);
mask = fold_build1 (BIT_NOT_EXPR, type, tem1);
- tem1 = fold_binary (MINUS_EXPR, type, rangei->exp, lowi);
+ tem1 = fold_binary (MINUS_EXPR, type,
+ fold_convert (type, rangei->exp), lowi);
tem1 = fold_build2 (BIT_AND_EXPR, type, tem1, mask);
lowj = build_int_cst (type, 0);
if (update_range_test (rangei, rangej, 1, opcode, ops, tem1,
called. */
addr = CALL_EXPR_FN (call);
+ /* If there is no function, return early. */
+ if (addr == NULL_TREE)
+ return NULL_TREE;
+
STRIP_NOPS (addr);
/* If this is a readonly function pointer, extract its initial value. */
/* Call this function after instantiating all builtins that the language
front end cares about. This will build the rest of the builtins
- and internal functions that are relied upon by the tree optimizers and
+ and internal function that are relied upon by the tree optimizers and
the middle-end. */
void
/* Function call. CALL_EXPRs are represented by variably-sized expression
nodes. There are at least three fixed operands. Operand 0 is an
INTEGER_CST node containing the total operand count, the number of
- arguments plus 3. Operand 1 is the function, while operand 2 is
+ arguments plus 3. Operand 1 is the function or NULL, while operand 2 is
is static chain argument, or NULL. The remaining operands are the
arguments to the call. */
DEFTREECODE (CALL_EXPR, "call_expr", tcc_vl_exp, 3)
#define TYPE_OVERFLOW_TRAPS(TYPE) \
(!TYPE_UNSIGNED (TYPE) && flag_trapv)
+ /* True if an overflow is to be preserved for sanitization. */
+#define TYPE_OVERFLOW_SANITIZED(TYPE) \
+ (INTEGRAL_TYPE_P (TYPE) \
+ && !TYPE_OVERFLOW_WRAPS (TYPE) \
+ && (flag_sanitize & SANITIZE_SI_OVERFLOW))
+
/* True if pointer types have undefined overflow. */
#define POINTER_TYPE_OVERFLOW_UNDEFINED (flag_strict_overflow)
#define ASSERT_EXPR_VAR(NODE) TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 0)
#define ASSERT_EXPR_COND(NODE) TREE_OPERAND (ASSERT_EXPR_CHECK (NODE), 1)
-/* CALL_EXPR accessors.
- */
+/* CALL_EXPR accessors. */
#define CALL_EXPR_FN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 1)
#define CALL_EXPR_STATIC_CHAIN(NODE) TREE_OPERAND (CALL_EXPR_CHECK (NODE), 2)
#define CALL_EXPR_ARG(NODE, I) TREE_OPERAND (CALL_EXPR_CHECK (NODE), (I) + 3)
#define call_expr_nargs(NODE) (VL_EXP_OPERAND_LENGTH (NODE) - 3)
+#define CALL_EXPR_IFN(NODE) (CALL_EXPR_CHECK (NODE)->base.u.ifn)
/* CALL_EXPR_ARGP returns a pointer to the argument vector for NODE.
We can't use &CALL_EXPR_ARG (NODE, 0) because that will complain if
build_call_array_loc (UNKNOWN_LOCATION, T1, T2, N, T3)
extern tree build_call_array_loc (location_t, tree, tree, int, const tree *);
extern tree build_call_vec (tree, tree, vec<tree, va_gc> *);
+extern tree build_call_expr_loc_array (location_t, tree, int, tree *);
+extern tree build_call_expr_loc_vec (location_t, tree, vec<tree, va_gc> *);
+extern tree build_call_expr_loc (location_t, tree, int, ...);
+extern tree build_call_expr (tree, int, ...);
+extern tree build_call_expr_internal_loc (location_t, enum internal_fn,
+ tree, int, ...);
+extern tree build_string_literal (int, const char *);
/* Construct various nodes representing data types. */
#include "intl.h"
#include "tree-eh.h"
#include "tree-cfg.h"
+#include "realmpfr.h"
+#include "dfp.h"
+#include "builtins.h"
+#include "tree-object-size.h"
+#include "tree-eh.h"
/* Map from a tree to a VAR_DECL tree. */
}
}
+/* Cached ubsan_get_type_descriptor_type () return value. */
+static GTY(()) tree ubsan_type_descriptor_type;
+
/* Build
struct __ubsan_type_descriptor
{
type. */
static tree
-ubsan_type_descriptor_type (void)
+ubsan_get_type_descriptor_type (void)
{
static const char *field_names[3]
= { "__typekind", "__typeinfo", "__typename" };
tree fields[3], ret;
+
+ if (ubsan_type_descriptor_type)
+ return ubsan_type_descriptor_type;
+
tree itype = build_range_type (sizetype, size_zero_node, NULL_TREE);
tree flex_arr_type = build_array_type (char_type_node, itype);
if (i)
DECL_CHAIN (fields[i - 1]) = fields[i];
}
+ tree type_decl = build_decl (input_location, TYPE_DECL,
+ get_identifier ("__ubsan_type_descriptor"),
+ ret);
+ DECL_IGNORED_P (type_decl) = 1;
+ DECL_ARTIFICIAL (type_decl) = 1;
TYPE_FIELDS (ret) = fields[0];
- TYPE_NAME (ret) = get_identifier ("__ubsan_type_descriptor");
+ TYPE_NAME (ret) = type_decl;
+ TYPE_STUB_DECL (ret) = type_decl;
layout_type (ret);
+ ubsan_type_descriptor_type = ret;
return ret;
}
+/* Cached ubsan_get_source_location_type () return value. */
+static GTY(()) tree ubsan_source_location_type;
+
/* Build
struct __ubsan_source_location
{
}
type. */
-static tree
-ubsan_source_location_type (void)
+tree
+ubsan_get_source_location_type (void)
{
static const char *field_names[3]
= { "__filename", "__line", "__column" };
tree fields[3], ret;
+ if (ubsan_source_location_type)
+ return ubsan_source_location_type;
+
tree const_char_type = build_qualified_type (char_type_node,
TYPE_QUAL_CONST);
if (i)
DECL_CHAIN (fields[i - 1]) = fields[i];
}
+ tree type_decl = build_decl (input_location, TYPE_DECL,
+ get_identifier ("__ubsan_source_location"),
+ ret);
+ DECL_IGNORED_P (type_decl) = 1;
+ DECL_ARTIFICIAL (type_decl) = 1;
TYPE_FIELDS (ret) = fields[0];
- TYPE_NAME (ret) = get_identifier ("__ubsan_source_location");
+ TYPE_NAME (ret) = type_decl;
+ TYPE_STUB_DECL (ret) = type_decl;
layout_type (ret);
+ ubsan_source_location_type = ret;
return ret;
}
ubsan_source_location (location_t loc)
{
expanded_location xloc;
- tree type = ubsan_source_location_type ();
+ tree type = ubsan_get_source_location_type ();
xloc = expand_location (loc);
+ tree str;
if (xloc.file == NULL)
- xloc.file = "<unknown>";
-
- /* Fill in the values from LOC. */
- size_t len = strlen (xloc.file);
- tree str = build_string (len + 1, xloc.file);
- TREE_TYPE (str) = build_array_type (char_type_node,
- build_index_type (size_int (len)));
- TREE_READONLY (str) = 1;
- TREE_STATIC (str) = 1;
- str = build_fold_addr_expr (str);
+ {
+ str = build_int_cst (ptr_type_node, 0);
+ xloc.line = 0;
+ xloc.column = 0;
+ }
+ else
+ {
+ /* Fill in the values from LOC. */
+ size_t len = strlen (xloc.file) + 1;
+ str = build_string (len, xloc.file);
+ TREE_TYPE (str) = build_array_type_nelts (char_type_node, len);
+ TREE_READONLY (str) = 1;
+ TREE_STATIC (str) = 1;
+ str = build_fold_addr_expr (str);
+ }
tree ctor = build_constructor_va (type, 3, NULL_TREE, str, NULL_TREE,
build_int_cst (unsigned_type_node,
xloc.line), NULL_TREE,
get_ubsan_type_info_for_type (tree type)
{
gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
- int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
- gcc_assert (prec != -1);
- return (prec << 1) | !TYPE_UNSIGNED (type);
+ if (TREE_CODE (type) == REAL_TYPE)
+ return tree_to_uhwi (TYPE_SIZE (type));
+ else if (INTEGRAL_TYPE_P (type))
+ {
+ int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
+ gcc_assert (prec != -1);
+ return (prec << 1) | !TYPE_UNSIGNED (type);
+ }
+ else
+ return 0;
}
/* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
descriptor. It first looks into the hash table; if not found,
create the VAR_DECL, put it into the hash table and return the
- ADDR_EXPR of it. TYPE describes a particular type. WANT_POINTER_TYPE_P
- means whether we are interested in the pointer type and not the pointer
- itself. */
+ ADDR_EXPR of it. TYPE describes a particular type. PSTYLE is
+ an enum controlling how we want to print the type. */
tree
-ubsan_type_descriptor (tree type, bool want_pointer_type_p)
+ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle)
{
/* See through any typedefs. */
type = TYPE_MAIN_VARIANT (type);
if (decl != NULL_TREE && varpool_get_node (decl))
return build_fold_addr_expr (decl);
- tree dtype = ubsan_type_descriptor_type ();
+ tree dtype = ubsan_get_type_descriptor_type ();
tree type2 = type;
const char *tname = NULL;
char *pretty_name;
unsigned short tkind, tinfo;
/* Get the name of the type, or the name of the pointer type. */
- if (want_pointer_type_p)
+ if (pstyle == UBSAN_PRINT_POINTER)
{
gcc_assert (POINTER_TYPE_P (type));
type2 = TREE_TYPE (type);
/* If an array, get its type. */
type2 = strip_array_types (type2);
+ if (pstyle == UBSAN_PRINT_ARRAY)
+ {
+ while (POINTER_TYPE_P (type2))
+ deref_depth++, type2 = TREE_TYPE (type2);
+ }
+
if (TYPE_NAME (type2) != NULL)
{
if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
tname = "<unknown>";
/* Decorate the type name with '', '*', "struct", or "union". */
- pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth);
- if (want_pointer_type_p)
+ pretty_name = (char *) alloca (strlen (tname) + 1024 + deref_depth);
+ if (pstyle == UBSAN_PRINT_POINTER)
{
int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
TYPE_VOLATILE (type2) ? "volatile " : "",
pretty_name[pos++] = '\'';
pretty_name[pos] = '\0';
}
+ else if (pstyle == UBSAN_PRINT_ARRAY)
+ {
+ /* Pretty print the array dimensions. */
+ gcc_assert (TREE_CODE (type) == ARRAY_TYPE);
+ tree t = type;
+ int pos = sprintf (pretty_name, "'%s ", tname);
+ while (deref_depth-- > 0)
+ pretty_name[pos++] = '*';
+ while (TREE_CODE (t) == ARRAY_TYPE)
+ {
+ pretty_name[pos++] = '[';
+ tree dom = TYPE_DOMAIN (t);
+ if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST)
+ pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC,
+ tree_to_uhwi (TYPE_MAX_VALUE (dom)) + 1);
+ else
+ /* ??? We can't determine the variable name; print VLA unspec. */
+ pretty_name[pos++] = '*';
+ pretty_name[pos++] = ']';
+ t = TREE_TYPE (t);
+ }
+ pretty_name[pos++] = '\'';
+ pretty_name[pos] = '\0';
+
+ /* Save the tree with stripped types. */
+ type = t;
+ }
else
sprintf (pretty_name, "'%s'", tname);
tkind = 0x0000;
break;
case REAL_TYPE:
- tkind = 0x0001;
+ /* FIXME: libubsan right now only supports float, double and
+ long double type formats. */
+ if (TYPE_MODE (type) == TYPE_MODE (float_type_node)
+ || TYPE_MODE (type) == TYPE_MODE (double_type_node)
+ || TYPE_MODE (type) == TYPE_MODE (long_double_type_node))
+ tkind = 0x0001;
+ else
+ tkind = 0xffff;
break;
default:
tkind = 0xffff;
tinfo = get_ubsan_type_info_for_type (type);
/* Create a new VAR_DECL of type descriptor. */
+ size_t len = strlen (pretty_name) + 1;
+ tree str = build_string (len, pretty_name);
+ TREE_TYPE (str) = build_array_type_nelts (char_type_node, len);
+ TREE_READONLY (str) = 1;
+ TREE_STATIC (str) = 1;
+
char tmp_name[32];
static unsigned int type_var_id_num;
ASM_GENERATE_INTERNAL_LABEL (tmp_name, "Lubsan_type", type_var_id_num++);
DECL_ARTIFICIAL (decl) = 1;
DECL_IGNORED_P (decl) = 1;
DECL_EXTERNAL (decl) = 0;
+ DECL_SIZE (decl)
+ = size_binop (PLUS_EXPR, DECL_SIZE (decl), TYPE_SIZE (TREE_TYPE (str)));
+ DECL_SIZE_UNIT (decl)
+ = size_binop (PLUS_EXPR, DECL_SIZE_UNIT (decl),
+ TYPE_SIZE_UNIT (TREE_TYPE (str)));
- size_t len = strlen (pretty_name);
- tree str = build_string (len + 1, pretty_name);
- TREE_TYPE (str) = build_array_type (char_type_node,
- build_index_type (size_int (len)));
- TREE_READONLY (str) = 1;
- TREE_STATIC (str) = 1;
tree ctor = build_constructor_va (dtype, 3, NULL_TREE,
build_int_cst (short_unsigned_type_node,
tkind), NULL_TREE,
}
/* Create a structure for the ubsan library. NAME is a name of the new
- structure. The arguments in ... are of __ubsan_type_descriptor type
- and there are at most two of them. MISMATCH are data used by ubsan
- pointer checking. */
+ structure. LOCCNT is number of locations, PLOC points to array of
+ locations. The arguments in ... are of __ubsan_type_descriptor type
+ and there are at most two of them, followed by NULL_TREE, followed
+ by optional extra arguments and another NULL_TREE. */
tree
-ubsan_create_data (const char *name, const location_t *ploc,
- const struct ubsan_mismatch_data *mismatch, ...)
+ubsan_create_data (const char *name, int loccnt, const location_t *ploc, ...)
{
va_list args;
tree ret, t;
- tree fields[5];
+ tree fields[6];
vec<tree, va_gc> *saved_args = NULL;
size_t i = 0;
- location_t loc = UNKNOWN_LOCATION;
+ int j;
/* Firstly, create a pointer to type descriptor type. */
- tree td_type = ubsan_type_descriptor_type ();
- TYPE_READONLY (td_type) = 1;
+ tree td_type = ubsan_get_type_descriptor_type ();
td_type = build_pointer_type (td_type);
/* Create the structure type. */
ret = make_node (RECORD_TYPE);
- if (ploc != NULL)
+ for (j = 0; j < loccnt; j++)
{
- loc = LOCATION_LOCUS (*ploc);
+ gcc_checking_assert (i < 2);
fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
- ubsan_source_location_type ());
+ ubsan_get_source_location_type ());
DECL_CONTEXT (fields[i]) = ret;
+ if (i)
+ DECL_CHAIN (fields[i - 1]) = fields[i];
i++;
}
- va_start (args, mismatch);
+ va_start (args, ploc);
for (t = va_arg (args, tree); t != NULL_TREE;
i++, t = va_arg (args, tree))
{
- gcc_checking_assert (i < 3);
+ gcc_checking_assert (i < 4);
/* Save the tree arguments for later use. */
vec_safe_push (saved_args, t);
fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
if (i)
DECL_CHAIN (fields[i - 1]) = fields[i];
}
- va_end (args);
- if (mismatch != NULL)
+ for (t = va_arg (args, tree); t != NULL_TREE;
+ i++, t = va_arg (args, tree))
{
- /* We have to add two more decls. */
- fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
- pointer_sized_int_node);
- DECL_CONTEXT (fields[i]) = ret;
- DECL_CHAIN (fields[i - 1]) = fields[i];
- i++;
-
+ gcc_checking_assert (i < 6);
+ /* Save the tree arguments for later use. */
+ vec_safe_push (saved_args, t);
fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
- unsigned_char_type_node);
+ TREE_TYPE (t));
DECL_CONTEXT (fields[i]) = ret;
- DECL_CHAIN (fields[i - 1]) = fields[i];
- i++;
+ if (i)
+ DECL_CHAIN (fields[i - 1]) = fields[i];
}
+ va_end (args);
+ tree type_decl = build_decl (input_location, TYPE_DECL,
+ get_identifier (name), ret);
+ DECL_IGNORED_P (type_decl) = 1;
+ DECL_ARTIFICIAL (type_decl) = 1;
TYPE_FIELDS (ret) = fields[0];
- TYPE_NAME (ret) = get_identifier (name);
+ TYPE_NAME (ret) = type_decl;
+ TYPE_STUB_DECL (ret) = type_decl;
layout_type (ret);
/* Now, fill in the type. */
tree ctor = build_constructor (ret, v);
/* If desirable, set the __ubsan_source_location element. */
- if (ploc != NULL)
- CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, ubsan_source_location (loc));
+ for (j = 0; j < loccnt; j++)
+ {
+ location_t loc = LOCATION_LOCUS (ploc[j]);
+ CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, ubsan_source_location (loc));
+ }
size_t nelts = vec_safe_length (saved_args);
for (i = 0; i < nelts; i++)
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, t);
}
- if (mismatch != NULL)
- {
- /* Append the pointer data. */
- CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, mismatch->align);
- CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, mismatch->ckind);
- }
-
TREE_CONSTANT (ctor) = 1;
TREE_STATIC (ctor) = 1;
DECL_INITIAL (var) = ctor;
/* Instrument the __builtin_unreachable call. We just call the libubsan
routine instead. */
-tree
-ubsan_instrument_unreachable (location_t loc)
+bool
+ubsan_instrument_unreachable (gimple_stmt_iterator *gsi)
{
- initialize_sanitizer_builtins ();
- tree data = ubsan_create_data ("__ubsan_unreachable_data", &loc, NULL,
- NULL_TREE);
- tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE);
- return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data));
+ gimple g;
+ location_t loc = gimple_location (gsi_stmt (*gsi));
+
+ if (flag_sanitize_undefined_trap_on_error)
+ g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data = ubsan_create_data ("__ubsan_unreachable_data", 1, &loc,
+ NULL_TREE, NULL_TREE);
+ data = build_fold_addr_expr_loc (loc, data);
+ tree fn
+ = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE);
+ g = gimple_build_call (fn, 1, data);
+ }
+ gimple_set_location (g, loc);
+ gsi_replace (gsi, g, false);
+ return false;
}
/* Return true if T is a call to a libubsan routine. */
is_ubsan_builtin_p (tree t)
{
return TREE_CODE (t) == FUNCTION_DECL
+ && DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL
&& strncmp (IDENTIFIER_POINTER (DECL_NAME (t)),
"__builtin___ubsan_", 18) == 0;
}
-/* Expand UBSAN_NULL internal call. */
+/* Create a callgraph edge for statement STMT. */
+
+static void
+ubsan_create_edge (gimple stmt)
+{
+ gcall *call_stmt = dyn_cast <gcall *> (stmt);
+ basic_block bb = gimple_bb (stmt);
+ int freq = compute_call_stmt_bb_frequency (current_function_decl, bb);
+ cgraph_node *node = cgraph_node::get (current_function_decl);
+ tree decl = gimple_call_fndecl (call_stmt);
+ if (decl)
+ node->create_edge (cgraph_node::get_create (decl), call_stmt, bb->count,
+ freq);
+}
+
+/* Expand the UBSAN_BOUNDS special builtin function. */
+
+bool
+ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc = gimple_location (stmt);
+ gcc_assert (gimple_call_num_args (stmt) == 3);
+
+ /* Pick up the arguments of the UBSAN_BOUNDS call. */
+ tree type = TREE_TYPE (TREE_TYPE (gimple_call_arg (stmt, 0)));
+ tree index = gimple_call_arg (stmt, 1);
+ tree orig_index_type = TREE_TYPE (index);
+ tree bound = gimple_call_arg (stmt, 2);
+
+ gimple_stmt_iterator gsi_orig = *gsi;
+
+ /* Create condition "if (index > bound)". */
+ basic_block then_bb, fallthru_bb;
+ gimple_stmt_iterator cond_insert_point
+ = create_cond_insert_point (gsi, false, false, true,
+ &then_bb, &fallthru_bb);
+ index = fold_convert (TREE_TYPE (bound), index);
+ index = force_gimple_operand_gsi (&cond_insert_point, index,
+ true, NULL_TREE,
+ false, GSI_NEW_STMT);
+ gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc);
+ gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+
+ /* Generate __ubsan_handle_out_of_bounds call. */
+ *gsi = gsi_after_labels (then_bb);
+ if (flag_sanitize_undefined_trap_on_error)
+ g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data
+ = ubsan_create_data ("__ubsan_out_of_bounds_data", 1, &loc,
+ ubsan_type_descriptor (type, UBSAN_PRINT_ARRAY),
+ ubsan_type_descriptor (orig_index_type),
+ NULL_TREE, NULL_TREE);
+ data = build_fold_addr_expr_loc (loc, data);
+ enum built_in_function bcode
+ = (flag_sanitize_recover & SANITIZE_BOUNDS)
+ ? BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS
+ : BUILT_IN_UBSAN_HANDLE_OUT_OF_BOUNDS_ABORT;
+ tree fn = builtin_decl_explicit (bcode);
+ tree val = force_gimple_operand_gsi (gsi, ubsan_encode_value (index),
+ true, NULL_TREE, true,
+ GSI_SAME_STMT);
+ g = gimple_build_call (fn, 2, data, val);
+ }
+ gimple_set_location (g, loc);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+
+ /* Get rid of the UBSAN_BOUNDS call from the IR. */
+ unlink_stmt_vdef (stmt);
+ gsi_remove (&gsi_orig, true);
+
+ /* Point GSI to next logical statement. */
+ *gsi = gsi_start_bb (fallthru_bb);
+ return true;
+}
+
+/* Expand UBSAN_NULL internal call. The type is kept on the ckind
+ argument which is a constant, because the middle-end treats pointer
+ conversions as useless and therefore the type of the first argument
+ could be changed to any other pointer type. */
-void
-ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
+bool
+ubsan_expand_null_ifn (gimple_stmt_iterator *gsip)
{
+ gimple_stmt_iterator gsi = *gsip;
gimple stmt = gsi_stmt (gsi);
location_t loc = gimple_location (stmt);
- gcc_assert (gimple_call_num_args (stmt) == 2);
+ gcc_assert (gimple_call_num_args (stmt) == 3);
tree ptr = gimple_call_arg (stmt, 0);
tree ckind = gimple_call_arg (stmt, 1);
+ tree align = gimple_call_arg (stmt, 2);
+ tree check_align = NULL_TREE;
+ bool check_null;
basic_block cur_bb = gsi_bb (gsi);
+ gimple g;
+ if (!integer_zerop (align))
+ {
+ unsigned int ptralign = get_pointer_alignment (ptr) / BITS_PER_UNIT;
+ if (compare_tree_int (align, ptralign) == 1)
+ {
+ check_align = make_ssa_name (pointer_sized_int_node, NULL);
+ g = gimple_build_assign_with_ops (NOP_EXPR, check_align,
+ ptr, NULL_TREE);
+ gimple_set_location (g, loc);
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+ }
+ }
+ check_null = (flag_sanitize & SANITIZE_NULL) != 0;
+
+ if (check_align == NULL_TREE && !check_null)
+ {
+ gsi_remove (gsip, true);
+ /* Unlink the UBSAN_NULLs vops before replacing it. */
+ unlink_stmt_vdef (stmt);
+ return true;
+ }
+
/* Split the original block holding the pointer dereference. */
edge e = split_block (cur_bb, stmt);
/* Update dominance info for the newly created then_bb; note that
fallthru_bb's dominance info has already been updated by
- split_bock. */
+ split_block. */
if (dom_info_available_p (CDI_DOMINATORS))
set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb);
/* Put the ubsan builtin call into the newly created BB. */
- tree fn = builtin_decl_implicit (BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH);
- const struct ubsan_mismatch_data m
- = { build_zero_cst (pointer_sized_int_node), ckind };
- tree data = ubsan_create_data ("__ubsan_null_data",
- &loc, &m,
- ubsan_type_descriptor (TREE_TYPE (ptr), true),
- NULL_TREE);
- data = build_fold_addr_expr_loc (loc, data);
- gimple g = gimple_build_call (fn, 2, data,
- build_zero_cst (pointer_sized_int_node));
- gimple_set_location (g, loc);
+ if (flag_sanitize_undefined_trap_on_error)
+ g = gimple_build_call (builtin_decl_implicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ enum built_in_function bcode
+ = (flag_sanitize_recover & ((check_align ? SANITIZE_ALIGNMENT : 0)
+ | (check_null ? SANITIZE_NULL : 0)))
+ ? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH
+ : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT;
+ tree fn = builtin_decl_implicit (bcode);
+ tree data
+ = ubsan_create_data ("__ubsan_null_data", 1, &loc,
+ ubsan_type_descriptor (TREE_TYPE (ckind),
+ UBSAN_PRINT_POINTER),
+ NULL_TREE,
+ align,
+ fold_convert (unsigned_char_type_node, ckind),
+ NULL_TREE);
+ data = build_fold_addr_expr_loc (loc, data);
+ g = gimple_build_call (fn, 2, data,
+ check_align ? check_align
+ : build_zero_cst (pointer_sized_int_node));
+ }
gimple_stmt_iterator gsi2 = gsi_start_bb (then_bb);
+ gimple_set_location (g, loc);
gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
/* Unlink the UBSAN_NULLs vops before replacing it. */
unlink_stmt_vdef (stmt);
- g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0),
- NULL_TREE, NULL_TREE);
- gimple_set_location (g, loc);
+ if (check_null)
+ {
+ g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0),
+ NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc);
- /* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */
- gsi_replace (&gsi, g, false);
+ /* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */
+ gsi_replace (&gsi, g, false);
+ }
+
+ if (check_align)
+ {
+ if (check_null)
+ {
+ /* Split the block with the condition again. */
+ e = split_block (cond_bb, stmt);
+ basic_block cond1_bb = e->src;
+ basic_block cond2_bb = e->dest;
+
+ /* Make an edge coming from the 'cond1 block' into the 'then block';
+ this edge is unlikely taken, so set up the probability
+ accordingly. */
+ e = make_edge (cond1_bb, then_bb, EDGE_TRUE_VALUE);
+ e->probability = PROB_VERY_UNLIKELY;
+
+ /* Set up the fallthrough basic block. */
+ e = find_edge (cond1_bb, cond2_bb);
+ e->flags = EDGE_FALSE_VALUE;
+ e->count = cond1_bb->count;
+ e->probability = REG_BR_PROB_BASE - PROB_VERY_UNLIKELY;
+
+ /* Update dominance info. */
+ if (dom_info_available_p (CDI_DOMINATORS))
+ {
+ set_immediate_dominator (CDI_DOMINATORS, fallthru_bb, cond1_bb);
+ set_immediate_dominator (CDI_DOMINATORS, then_bb, cond1_bb);
+ }
+
+ gsi2 = gsi_start_bb (cond2_bb);
+ }
+
+ tree mask = build_int_cst (pointer_sized_int_node,
+ tree_to_uhwi (align) - 1);
+ g = gimple_build_assign_with_ops (BIT_AND_EXPR,
+ make_ssa_name (pointer_sized_int_node,
+ NULL),
+ check_align, mask);
+ gimple_set_location (g, loc);
+ if (check_null)
+ gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
+ else
+ gsi_insert_before (&gsi, g, GSI_SAME_STMT);
+
+ g = gimple_build_cond (NE_EXPR, gimple_assign_lhs (g),
+ build_int_cst (pointer_sized_int_node, 0),
+ NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc);
+ if (check_null)
+ gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
+ else
+ /* Replace the UBSAN_NULL with a GIMPLE_COND stmt. */
+ gsi_replace (&gsi, g, false);
+ }
+ return false;
}
-/* Instrument a member call. We check whether 'this' is NULL. */
+/* Expand UBSAN_OBJECT_SIZE internal call. */
-static void
-instrument_member_call (gimple_stmt_iterator *iter)
+bool
+ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi)
{
- tree this_parm = gimple_call_arg (gsi_stmt (*iter), 0);
- tree kind = build_int_cst (unsigned_char_type_node, UBSAN_MEMBER_CALL);
- gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, this_parm, kind);
- gimple_set_location (g, gimple_location (gsi_stmt (*iter)));
- gsi_insert_before (iter, g, GSI_SAME_STMT);
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc = gimple_location (stmt);
+ gcc_assert (gimple_call_num_args (stmt) == 4);
+
+ tree ptr = gimple_call_arg (stmt, 0);
+ tree offset = gimple_call_arg (stmt, 1);
+ tree size = gimple_call_arg (stmt, 2);
+ tree ckind = gimple_call_arg (stmt, 3);
+ gimple_stmt_iterator gsi_orig = *gsi;
+ gimple g;
+
+ /* See if we can discard the check. */
+ if (TREE_CODE (size) != INTEGER_CST
+ || integer_all_onesp (size))
+ /* Yes, __builtin_object_size couldn't determine the
+ object size. */;
+ else
+ {
+ /* if (offset > objsize) */
+ basic_block then_bb, fallthru_bb;
+ gimple_stmt_iterator cond_insert_point
+ = create_cond_insert_point (gsi, false, false, true,
+ &then_bb, &fallthru_bb);
+ g = gimple_build_cond (GT_EXPR, offset, size, NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc);
+ gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT);
+
+ /* Generate __ubsan_handle_type_mismatch call. */
+ *gsi = gsi_after_labels (then_bb);
+ if (flag_sanitize_undefined_trap_on_error)
+ g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data
+ = ubsan_create_data ("__ubsan_objsz_data", 1, &loc,
+ ubsan_type_descriptor (TREE_TYPE (ptr),
+ UBSAN_PRINT_POINTER),
+ NULL_TREE,
+ build_zero_cst (pointer_sized_int_node),
+ ckind,
+ NULL_TREE);
+ data = build_fold_addr_expr_loc (loc, data);
+ enum built_in_function bcode
+ = (flag_sanitize_recover & SANITIZE_OBJECT_SIZE)
+ ? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH
+ : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT;
+ tree p = make_ssa_name (pointer_sized_int_node, NULL);
+ g = gimple_build_assign_with_ops (NOP_EXPR, p, ptr, NULL_TREE);
+ gimple_set_location (g, loc);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ g = gimple_build_call (builtin_decl_explicit (bcode), 2, data, p);
+ }
+ gimple_set_location (g, loc);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+
+ /* Point GSI to next logical statement. */
+ *gsi = gsi_start_bb (fallthru_bb);
+
+ /* Get rid of the UBSAN_OBJECT_SIZE call from the IR. */
+ unlink_stmt_vdef (stmt);
+ gsi_remove (&gsi_orig, true);
+ return true;
+ }
+
+ /* Get rid of the UBSAN_OBJECT_SIZE call from the IR. */
+ unlink_stmt_vdef (stmt);
+ gsi_remove (gsi, true);
+ return true;
}
-/* Instrument a memory reference. T is the pointer, IS_LHS says
+/* Instrument a memory reference. BASE is the base of MEM, IS_LHS says
whether the pointer is on the left hand side of the assignment. */
static void
-instrument_mem_ref (tree t, gimple_stmt_iterator *iter, bool is_lhs)
+instrument_mem_ref (tree mem, tree base, gimple_stmt_iterator *iter,
+ bool is_lhs)
{
enum ubsan_null_ckind ikind = is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF;
- if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (t))))
+ unsigned int align = 0;
+ if (flag_sanitize & SANITIZE_ALIGNMENT)
+ {
+ align = min_align_of_type (TREE_TYPE (base));
+ if (align <= 1)
+ align = 0;
+ }
+ if (align == 0 && (flag_sanitize & SANITIZE_NULL) == 0)
+ return;
+ tree t = TREE_OPERAND (base, 0);
+ if (!POINTER_TYPE_P (TREE_TYPE (t)))
+ return;
+ if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (base)) && mem != base)
ikind = UBSAN_MEMBER_ACCESS;
- tree kind = build_int_cst (unsigned_char_type_node, ikind);
- gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, t, kind);
+ tree kind = build_int_cst (build_pointer_type (TREE_TYPE (base)), ikind);
+ tree alignt = build_int_cst (pointer_sized_int_node, align);
+ gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 3, t, kind, alignt);
gimple_set_location (g, gimple_location (gsi_stmt (*iter)));
gsi_insert_before (iter, g, GSI_SAME_STMT);
}
{
gimple stmt = gsi_stmt (gsi);
tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt);
- t = get_base_address (t);
- const enum tree_code code = TREE_CODE (t);
+ tree base = get_base_address (t);
+ const enum tree_code code = TREE_CODE (base);
if (code == MEM_REF
- && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME)
- instrument_mem_ref (TREE_OPERAND (t, 0), &gsi, is_lhs);
- else if (code == ADDR_EXPR
- && POINTER_TYPE_P (TREE_TYPE (t))
- && TREE_CODE (TREE_TYPE (TREE_TYPE (t))) == METHOD_TYPE)
- instrument_member_call (&gsi);
+ && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME)
+ instrument_mem_ref (t, base, &gsi, is_lhs);
}
/* Build an ubsan builtin call for the signed-integer-overflow
ubsan_build_overflow_builtin (tree_code code, location_t loc, tree lhstype,
tree op0, tree op1)
{
- tree data = ubsan_create_data ("__ubsan_overflow_data", &loc, NULL,
- ubsan_type_descriptor (lhstype, false),
+ if (flag_sanitize_undefined_trap_on_error)
+ return build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+
+ tree data = ubsan_create_data ("__ubsan_overflow_data", 1, &loc,
+ ubsan_type_descriptor (lhstype), NULL_TREE,
NULL_TREE);
enum built_in_function fn_code;
switch (code)
{
case PLUS_EXPR:
- fn_code = BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW;
+ fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+ ? BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW
+ : BUILT_IN_UBSAN_HANDLE_ADD_OVERFLOW_ABORT;
break;
case MINUS_EXPR:
- fn_code = BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW;
+ fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+ ? BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW
+ : BUILT_IN_UBSAN_HANDLE_SUB_OVERFLOW_ABORT;
break;
case MULT_EXPR:
- fn_code = BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW;
+ fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+ ? BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW
+ : BUILT_IN_UBSAN_HANDLE_MUL_OVERFLOW_ABORT;
break;
case NEGATE_EXPR:
- fn_code = BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW;
+ fn_code = (flag_sanitize_recover & SANITIZE_SI_OVERFLOW)
+ ? BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW
+ : BUILT_IN_UBSAN_HANDLE_NEGATE_OVERFLOW_ABORT;
break;
default:
gcc_unreachable ();
update_stmt (stmt);
}
- tree data = ubsan_create_data ("__ubsan_invalid_value_data",
- &loc, NULL,
- ubsan_type_descriptor (type, false),
- NULL_TREE);
- data = build_fold_addr_expr_loc (loc, data);
- tree fn = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE);
-
gsi2 = gsi_after_labels (then_bb);
- tree val = force_gimple_operand_gsi (&gsi2, ubsan_encode_value (urhs),
- true, NULL_TREE, true, GSI_SAME_STMT);
- g = gimple_build_call (fn, 2, data, val);
+ if (flag_sanitize_undefined_trap_on_error)
+ g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data = ubsan_create_data ("__ubsan_invalid_value_data", 1, &loc,
+ ubsan_type_descriptor (type), NULL_TREE,
+ NULL_TREE);
+ data = build_fold_addr_expr_loc (loc, data);
+ enum built_in_function bcode
+ = (flag_sanitize_recover & (TREE_CODE (type) == BOOLEAN_TYPE
+ ? SANITIZE_BOOL : SANITIZE_ENUM))
+ ? BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE
+ : BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT;
+ tree fn = builtin_decl_explicit (bcode);
+
+ tree val = force_gimple_operand_gsi (&gsi2, ubsan_encode_value (urhs),
+ true, NULL_TREE, true,
+ GSI_SAME_STMT);
+ g = gimple_build_call (fn, 2, data, val);
+ }
gimple_set_location (g, loc);
gsi_insert_before (&gsi2, g, GSI_SAME_STMT);
+ ubsan_create_edge (g);
+ *gsi = gsi_for_stmt (stmt);
}
-/* Gate and execute functions for ubsan pass. */
+/* Determine if we can propagate given LOCATION to ubsan_data descriptor to use
+ new style handlers. Libubsan uses heuristics to destinguish between old and
+ new styles and relies on these properties for filename:
+
+ a) Location's filename must not be NULL.
+ b) Location's filename must not be equal to "".
+ c) Location's filename must not be equal to "\1".
+ d) First two bytes of filename must not contain '\xff' symbol. */
-static unsigned int
-ubsan_pass (void)
+static bool
+ubsan_use_new_style_p (location_t loc)
{
- basic_block bb;
- gimple_stmt_iterator gsi;
+ if (loc == UNKNOWN_LOCATION)
+ return false;
- initialize_sanitizer_builtins ();
+ expanded_location xloc = expand_location (loc);
+ if (xloc.file == NULL || strncmp (xloc.file, "\1", 2) == 0
+ || xloc.file == '\0' || xloc.file[0] == '\xff'
+ || xloc.file[1] == '\xff')
+ return false;
- FOR_EACH_BB_FN (bb, cfun)
+ return true;
+}
+
+/* Instrument float point-to-integer conversion. TYPE is an integer type of
+ destination, EXPR is floating-point expression. */
+
+tree
+ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
+{
+ tree expr_type = TREE_TYPE (expr);
+ tree t, tt, fn, min, max;
+ enum machine_mode mode = TYPE_MODE (expr_type);
+ int prec = TYPE_PRECISION (type);
+ bool uns_p = TYPE_UNSIGNED (type);
+ if (loc == UNKNOWN_LOCATION)
+ loc = input_location;
+
+ /* Float to integer conversion first truncates toward zero, so
+ even signed char c = 127.875f; is not problematic.
+ Therefore, we should complain only if EXPR is unordered or smaller
+ or equal than TYPE_MIN_VALUE - 1.0 or greater or equal than
+ TYPE_MAX_VALUE + 1.0. */
+ if (REAL_MODE_FORMAT (mode)->b == 2)
{
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+ /* For maximum, TYPE_MAX_VALUE might not be representable
+ in EXPR_TYPE, e.g. if TYPE is 64-bit long long and
+ EXPR_TYPE is IEEE single float, but TYPE_MAX_VALUE + 1.0 is
+ either representable or infinity. */
+ REAL_VALUE_TYPE maxval = dconst1;
+ SET_REAL_EXP (&maxval, REAL_EXP (&maxval) + prec - !uns_p);
+ real_convert (&maxval, mode, &maxval);
+ max = build_real (expr_type, maxval);
+
+ /* For unsigned, assume -1.0 is always representable. */
+ if (uns_p)
+ min = build_minus_one_cst (expr_type);
+ else
{
- gimple stmt = gsi_stmt (gsi);
- if (is_gimple_debug (stmt) || gimple_clobber_p (stmt))
+ /* TYPE_MIN_VALUE is generally representable (or -inf),
+ but TYPE_MIN_VALUE - 1.0 might not be. */
+ REAL_VALUE_TYPE minval = dconstm1, minval2;
+ SET_REAL_EXP (&minval, REAL_EXP (&minval) + prec - 1);
+ real_convert (&minval, mode, &minval);
+ real_arithmetic (&minval2, MINUS_EXPR, &minval, &dconst1);
+ real_convert (&minval2, mode, &minval2);
+ if (real_compare (EQ_EXPR, &minval, &minval2)
+ && !real_isinf (&minval))
{
- gsi_next (&gsi);
- continue;
+ /* If TYPE_MIN_VALUE - 1.0 is not representable and
+ rounds to TYPE_MIN_VALUE, we need to subtract
+ more. As REAL_MODE_FORMAT (mode)->p is the number
+ of base digits, we want to subtract a number that
+ will be 1 << (REAL_MODE_FORMAT (mode)->p - 1)
+ times smaller than minval. */
+ minval2 = dconst1;
+ gcc_assert (prec > REAL_MODE_FORMAT (mode)->p);
+ SET_REAL_EXP (&minval2,
+ REAL_EXP (&minval2) + prec - 1
+ - REAL_MODE_FORMAT (mode)->p + 1);
+ real_arithmetic (&minval2, MINUS_EXPR, &minval, &minval2);
+ real_convert (&minval2, mode, &minval2);
}
+ min = build_real (expr_type, minval2);
+ }
+ }
+ else if (REAL_MODE_FORMAT (mode)->b == 10)
+ {
+ /* For _Decimal128 up to 34 decimal digits, - sign,
+ dot, e, exponent. */
+ char buf[64];
+ mpfr_t m;
+ int p = REAL_MODE_FORMAT (mode)->p;
+ REAL_VALUE_TYPE maxval, minval;
+
+ /* Use mpfr_snprintf rounding to compute the smallest
+ representable decimal number greater or equal than
+ 1 << (prec - !uns_p). */
+ mpfr_init2 (m, prec + 2);
+ mpfr_set_ui_2exp (m, 1, prec - !uns_p, GMP_RNDN);
+ mpfr_snprintf (buf, sizeof buf, "%.*RUe", p - 1, m);
+ decimal_real_from_string (&maxval, buf);
+ max = build_real (expr_type, maxval);
+
+ /* For unsigned, assume -1.0 is always representable. */
+ if (uns_p)
+ min = build_minus_one_cst (expr_type);
+ else
+ {
+ /* Use mpfr_snprintf rounding to compute the largest
+ representable decimal number less or equal than
+ (-1 << (prec - 1)) - 1. */
+ mpfr_set_si_2exp (m, -1, prec - 1, GMP_RNDN);
+ mpfr_sub_ui (m, m, 1, GMP_RNDN);
+ mpfr_snprintf (buf, sizeof buf, "%.*RDe", p - 1, m);
+ decimal_real_from_string (&minval, buf);
+ min = build_real (expr_type, minval);
+ }
+ mpfr_clear (m);
+ }
+ else
+ return NULL_TREE;
- if ((flag_sanitize & SANITIZE_SI_OVERFLOW)
- && is_gimple_assign (stmt))
- instrument_si_overflow (gsi);
+ t = fold_build2 (UNLE_EXPR, boolean_type_node, expr, min);
+ tt = fold_build2 (UNGE_EXPR, boolean_type_node, expr, max);
+ t = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt);
+ if (integer_zerop (t))
+ return NULL_TREE;
+
+ if (flag_sanitize_undefined_trap_on_error)
+ fn = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ location_t *loc_ptr = NULL;
+ unsigned num_locations = 0;
+ initialize_sanitizer_builtins ();
+ /* Figure out if we can propagate location to ubsan_data and use new
+ style handlers in libubsan. */
+ if (ubsan_use_new_style_p (loc))
+ {
+ loc_ptr = &loc;
+ num_locations = 1;
+ }
+ /* Create the __ubsan_handle_float_cast_overflow fn call. */
+ tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data",
+ num_locations, loc_ptr,
+ ubsan_type_descriptor (expr_type),
+ ubsan_type_descriptor (type), NULL_TREE,
+ NULL_TREE);
+ enum built_in_function bcode
+ = (flag_sanitize_recover & SANITIZE_FLOAT_CAST)
+ ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
+ : BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT;
+ fn = builtin_decl_explicit (bcode);
+ fn = build_call_expr_loc (loc, fn, 2,
+ build_fold_addr_expr_loc (loc, data),
+ ubsan_encode_value (expr, false));
+ }
+
+ return fold_build3 (COND_EXPR, void_type_node, t, fn, integer_zero_node);
+}
+
+/* Instrument values passed to function arguments with nonnull attribute. */
- if (flag_sanitize & SANITIZE_NULL)
+static void
+instrument_nonnull_arg (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc[2];
+ /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
+ while for nonnull sanitization it is clear. */
+ int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
+ flag_delete_null_pointer_checks = 1;
+ loc[0] = gimple_location (stmt);
+ loc[1] = UNKNOWN_LOCATION;
+ for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++)
+ {
+ tree arg = gimple_call_arg (stmt, i);
+ if (POINTER_TYPE_P (TREE_TYPE (arg))
+ && infer_nonnull_range (stmt, arg, false, true))
+ {
+ gimple g;
+ if (!is_gimple_val (arg))
{
- if (gimple_store_p (stmt))
- instrument_null (gsi, true);
- if (gimple_assign_load_p (stmt))
- instrument_null (gsi, false);
+ g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg), NULL),
+ arg);
+ gimple_set_location (g, loc[0]);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ arg = gimple_assign_lhs (g);
}
- if (flag_sanitize & (SANITIZE_BOOL | SANITIZE_ENUM)
- && gimple_assign_load_p (stmt))
- instrument_bool_enum_load (&gsi);
+ basic_block then_bb, fallthru_bb;
+ *gsi = create_cond_insert_point (gsi, true, false, true,
+ &then_bb, &fallthru_bb);
+ g = gimple_build_cond (EQ_EXPR, arg,
+ build_zero_cst (TREE_TYPE (arg)),
+ NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc[0]);
+ gsi_insert_after (gsi, g, GSI_NEW_STMT);
+
+ *gsi = gsi_after_labels (then_bb);
+ if (flag_sanitize_undefined_trap_on_error)
+ g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data = ubsan_create_data ("__ubsan_nonnull_arg_data",
+ 2, loc, NULL_TREE,
+ build_int_cst (integer_type_node,
+ i + 1),
+ NULL_TREE);
+ data = build_fold_addr_expr_loc (loc[0], data);
+ enum built_in_function bcode
+ = (flag_sanitize_recover & SANITIZE_NONNULL_ATTRIBUTE)
+ ? BUILT_IN_UBSAN_HANDLE_NONNULL_ARG
+ : BUILT_IN_UBSAN_HANDLE_NONNULL_ARG_ABORT;
+ tree fn = builtin_decl_explicit (bcode);
+
+ g = gimple_build_call (fn, 1, data);
+ }
+ gimple_set_location (g, loc[0]);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ ubsan_create_edge (g);
+ }
+ *gsi = gsi_for_stmt (stmt);
+ }
+ flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
+}
- gsi_next (&gsi);
+/* Instrument returns in functions with returns_nonnull attribute. */
+
+static void
+instrument_nonnull_return (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc[2];
+ tree arg = gimple_return_retval (stmt);
+ /* infer_nonnull_range needs flag_delete_null_pointer_checks set,
+ while for nonnull return sanitization it is clear. */
+ int save_flag_delete_null_pointer_checks = flag_delete_null_pointer_checks;
+ flag_delete_null_pointer_checks = 1;
+ loc[0] = gimple_location (stmt);
+ loc[1] = UNKNOWN_LOCATION;
+ if (arg
+ && POINTER_TYPE_P (TREE_TYPE (arg))
+ && is_gimple_val (arg)
+ && infer_nonnull_range (stmt, arg, false, true))
+ {
+ basic_block then_bb, fallthru_bb;
+ *gsi = create_cond_insert_point (gsi, true, false, true,
+ &then_bb, &fallthru_bb);
+ gimple g = gimple_build_cond (EQ_EXPR, arg,
+ build_zero_cst (TREE_TYPE (arg)),
+ NULL_TREE, NULL_TREE);
+ gimple_set_location (g, loc[0]);
+ gsi_insert_after (gsi, g, GSI_NEW_STMT);
+
+ *gsi = gsi_after_labels (then_bb);
+ if (flag_sanitize_undefined_trap_on_error)
+ g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0);
+ else
+ {
+ tree data = ubsan_create_data ("__ubsan_nonnull_return_data",
+ 2, loc, NULL_TREE, NULL_TREE);
+ data = build_fold_addr_expr_loc (loc[0], data);
+ enum built_in_function bcode
+ = (flag_sanitize_recover & SANITIZE_RETURNS_NONNULL_ATTRIBUTE)
+ ? BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN
+ : BUILT_IN_UBSAN_HANDLE_NONNULL_RETURN_ABORT;
+ tree fn = builtin_decl_explicit (bcode);
+
+ g = gimple_build_call (fn, 1, data);
}
+ gimple_set_location (g, loc[0]);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ ubsan_create_edge (g);
+ *gsi = gsi_for_stmt (stmt);
}
- return 0;
+ flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks;
}
-static bool
-gate_ubsan (void)
+/* Instrument memory references. Here we check whether the pointer
+ points to an out-of-bounds location. */
+
+static void
+instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs)
{
- return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW
- | SANITIZE_BOOL | SANITIZE_ENUM);
+ gimple stmt = gsi_stmt (*gsi);
+ location_t loc = gimple_location (stmt);
+ tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt);
+ tree type;
+ tree index = NULL_TREE;
+ HOST_WIDE_INT size_in_bytes;
+
+ type = TREE_TYPE (t);
+ if (VOID_TYPE_P (type))
+ return;
+
+ switch (TREE_CODE (t))
+ {
+ case COMPONENT_REF:
+ if (TREE_CODE (t) == COMPONENT_REF
+ && DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1)) != NULL_TREE)
+ {
+ tree repr = DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1));
+ t = build3 (COMPONENT_REF, TREE_TYPE (repr), TREE_OPERAND (t, 0),
+ repr, NULL_TREE);
+ }
+ break;
+ case ARRAY_REF:
+ index = TREE_OPERAND (t, 1);
+ break;
+ case INDIRECT_REF:
+ case MEM_REF:
+ case VAR_DECL:
+ case PARM_DECL:
+ case RESULT_DECL:
+ break;
+ default:
+ return;
+ }
+
+ size_in_bytes = int_size_in_bytes (type);
+ if (size_in_bytes <= 0)
+ return;
+
+ HOST_WIDE_INT bitsize, bitpos;
+ tree offset;
+ enum machine_mode mode;
+ int volatilep = 0, unsignedp = 0;
+ tree inner = get_inner_reference (t, &bitsize, &bitpos, &offset, &mode,
+ &unsignedp, &volatilep, false);
+
+ if (bitpos % BITS_PER_UNIT != 0
+ || bitsize != size_in_bytes * BITS_PER_UNIT)
+ return;
+
+ bool decl_p = DECL_P (inner);
+ tree base;
+ if (decl_p)
+ base = inner;
+ else if (TREE_CODE (inner) == MEM_REF)
+ base = TREE_OPERAND (inner, 0);
+ else
+ return;
+ tree ptr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (t)), t);
+
+ while (TREE_CODE (base) == SSA_NAME)
+ {
+ gimple def_stmt = SSA_NAME_DEF_STMT (base);
+ if (gimple_assign_ssa_name_copy_p (def_stmt)
+ || (gimple_assign_cast_p (def_stmt)
+ && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (def_stmt))))
+ || (is_gimple_assign (def_stmt)
+ && gimple_assign_rhs_code (def_stmt) == POINTER_PLUS_EXPR))
+ {
+ tree rhs1 = gimple_assign_rhs1 (def_stmt);
+ if (TREE_CODE (rhs1) == SSA_NAME
+ && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs1))
+ break;
+ else
+ base = rhs1;
+ }
+ else
+ break;
+ }
+
+ if (!POINTER_TYPE_P (TREE_TYPE (base)) && !DECL_P (base))
+ return;
+
+ tree sizet;
+ tree base_addr = base;
+ gimple bos_stmt = NULL;
+ if (decl_p)
+ base_addr = build1 (ADDR_EXPR,
+ build_pointer_type (TREE_TYPE (base)), base);
+ unsigned HOST_WIDE_INT size = compute_builtin_object_size (base_addr, 0);
+ if (size != (unsigned HOST_WIDE_INT) -1)
+ sizet = build_int_cst (sizetype, size);
+ else if (optimize)
+ {
+ if (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION)
+ loc = input_location;
+ /* Generate __builtin_object_size call. */
+ sizet = builtin_decl_explicit (BUILT_IN_OBJECT_SIZE);
+ sizet = build_call_expr_loc (loc, sizet, 2, base_addr,
+ integer_zero_node);
+ sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true,
+ GSI_SAME_STMT);
+ /* If the call above didn't end up being an integer constant, go one
+ statement back and get the __builtin_object_size stmt. Save it,
+ we might need it later. */
+ if (SSA_VAR_P (sizet))
+ {
+ gsi_prev (gsi);
+ bos_stmt = gsi_stmt (*gsi);
+
+ /* Move on to where we were. */
+ gsi_next (gsi);
+ }
+ }
+ else
+ return;
+
+ /* Generate UBSAN_OBJECT_SIZE (ptr, ptr+sizeof(*ptr)-base, objsize, ckind)
+ call. */
+ /* ptr + sizeof (*ptr) - base */
+ t = fold_build2 (MINUS_EXPR, sizetype,
+ fold_convert (pointer_sized_int_node, ptr),
+ fold_convert (pointer_sized_int_node, base_addr));
+ t = fold_build2 (PLUS_EXPR, sizetype, t, TYPE_SIZE_UNIT (type));
+
+ /* Perhaps we can omit the check. */
+ if (TREE_CODE (t) == INTEGER_CST
+ && TREE_CODE (sizet) == INTEGER_CST
+ && tree_int_cst_le (t, sizet))
+ return;
+
+ if (index != NULL_TREE
+ && TREE_CODE (index) == SSA_NAME
+ && TREE_CODE (sizet) == INTEGER_CST)
+ {
+ gimple def = SSA_NAME_DEF_STMT (index);
+ if (is_gimple_assign (def)
+ && gimple_assign_rhs_code (def) == BIT_AND_EXPR
+ && TREE_CODE (gimple_assign_rhs2 (def)) == INTEGER_CST)
+ {
+ tree cst = gimple_assign_rhs2 (def);
+ tree sz = fold_build2 (EXACT_DIV_EXPR, sizetype, sizet,
+ TYPE_SIZE_UNIT (type));
+ if (tree_int_cst_sgn (cst) >= 0
+ && tree_int_cst_lt (cst, sz))
+ return;
+ }
+ }
+
+ if (bos_stmt && gimple_call_builtin_p (bos_stmt, BUILT_IN_OBJECT_SIZE))
+ ubsan_create_edge (bos_stmt);
+
+ /* We have to emit the check. */
+ t = force_gimple_operand_gsi (gsi, t, true, NULL_TREE, true,
+ GSI_SAME_STMT);
+ ptr = force_gimple_operand_gsi (gsi, ptr, true, NULL_TREE, true,
+ GSI_SAME_STMT);
+ tree ckind = build_int_cst (unsigned_char_type_node,
+ is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF);
+ gimple g = gimple_build_call_internal (IFN_UBSAN_OBJECT_SIZE, 4,
+ ptr, t, sizet, ckind);
+ gimple_set_location (g, loc);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
}
namespace {
{}
/* opt_pass methods: */
- bool gate () { return gate_ubsan (); }
- unsigned int execute () { return ubsan_pass (); }
+ bool gate ()
+ {
+ return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW
+ | SANITIZE_BOOL | SANITIZE_ENUM
+ | SANITIZE_ALIGNMENT
+ | SANITIZE_NONNULL_ATTRIBUTE
+ | SANITIZE_RETURNS_NONNULL_ATTRIBUTE
+ | SANITIZE_OBJECT_SIZE)
+ && current_function_decl != NULL_TREE
+ && !lookup_attribute ("no_sanitize_undefined",
+ DECL_ATTRIBUTES (current_function_decl));
+ }
+
+ virtual unsigned int execute ();
}; // class pass_ubsan
+/* Gate and execute functions for ubsan pass. */
+
+unsigned int
+pass_ubsan::execute ()
+{
+ basic_block bb;
+ gimple_stmt_iterator gsi;
+
+ initialize_sanitizer_builtins ();
+
+ FOR_EACH_BB_FN (bb, cfun)
+ {
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+ {
+ gimple stmt = gsi_stmt (gsi);
+ if (is_gimple_debug (stmt) || gimple_clobber_p (stmt))
+ {
+ gsi_next (&gsi);
+ continue;
+ }
+
+ if ((flag_sanitize & SANITIZE_SI_OVERFLOW)
+ && is_gimple_assign (stmt))
+ instrument_si_overflow (gsi);
+
+ if (flag_sanitize & (SANITIZE_NULL | SANITIZE_ALIGNMENT))
+ {
+ if (gimple_store_p (stmt))
+ instrument_null (gsi, true);
+ if (gimple_assign_load_p (stmt))
+ instrument_null (gsi, false);
+ }
+
+ if (flag_sanitize & (SANITIZE_BOOL | SANITIZE_ENUM)
+ && gimple_assign_load_p (stmt))
+ {
+ instrument_bool_enum_load (&gsi);
+ bb = gimple_bb (stmt);
+ }
+
+ if ((flag_sanitize & SANITIZE_NONNULL_ATTRIBUTE)
+ && is_gimple_call (stmt)
+ && !gimple_call_internal_p (stmt))
+ {
+ instrument_nonnull_arg (&gsi);
+ bb = gimple_bb (stmt);
+ }
+
+ if ((flag_sanitize & SANITIZE_RETURNS_NONNULL_ATTRIBUTE)
+ && gimple_code (stmt) == GIMPLE_RETURN)
+ {
+ instrument_nonnull_return (&gsi);
+ bb = gimple_bb (stmt);
+ }
+
+ if (flag_sanitize & SANITIZE_OBJECT_SIZE)
+ {
+ if (gimple_store_p (stmt))
+ instrument_object_size (&gsi, true);
+ if (gimple_assign_load_p (stmt))
+ instrument_object_size (&gsi, false);
+ }
+
+ gsi_next (&gsi);
+ }
+ }
+ return 0;
+}
+
} // anon namespace
gimple_opt_pass *
UBSAN_STORE_OF,
UBSAN_REF_BINDING,
UBSAN_MEMBER_ACCESS,
- UBSAN_MEMBER_CALL
+ UBSAN_MEMBER_CALL,
+ UBSAN_CTOR_CALL
};
-/* An extra data used by ubsan pointer checking. */
-struct ubsan_mismatch_data {
- tree align;
- tree ckind;
+/* This controls how ubsan prints types. Used in ubsan_type_descriptor. */
+enum ubsan_print_style {
+ UBSAN_PRINT_NORMAL,
+ UBSAN_PRINT_POINTER,
+ UBSAN_PRINT_ARRAY
};
-extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
-extern tree ubsan_instrument_unreachable (location_t);
-extern tree ubsan_create_data (const char *, const location_t *,
- const struct ubsan_mismatch_data *, ...);
-extern tree ubsan_type_descriptor (tree, bool);
+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 *);
+extern bool ubsan_instrument_unreachable (gimple_stmt_iterator *);
+extern tree ubsan_create_data (const char *, int, const location_t *, ...);
+extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);
extern tree ubsan_encode_value (tree, bool = false);
extern bool is_ubsan_builtin_p (tree);
extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);
+extern tree ubsan_instrument_float_cast (location_t, tree, tree);
+extern tree ubsan_get_source_location_type (void);
#endif /* GCC_UBSAN_H */
-
--- /dev/null
+/* Operations with very long integers. -*- C++ -*-
+ Copyright (C) 2012-2013 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef WIDE_INT_H
+#define WIDE_INT_H
+
+/* wide-int.[cc|h] implements a class that efficiently performs
+ mathematical operations on finite precision integers. wide_ints
+ are designed to be transient - they are not for long term storage
+ of values. There is tight integration between wide_ints and the
+ other longer storage GCC representations (rtl and tree).
+
+ The actual precision of a wide_int depends on the flavor. There
+ are three predefined flavors:
+
+ 1) wide_int (the default). This flavor does the math in the
+ precision of its input arguments. It is assumed (and checked)
+ that the precisions of the operands and results are consistent.
+ This is the most efficient flavor. It is not possible to examine
+ bits above the precision that has been specified. Because of
+ this, the default flavor has semantics that are simple to
+ understand and in general model the underlying hardware that the
+ compiler is targetted for.
+
+ This flavor must be used at the RTL level of gcc because there
+ is, in general, not enough information in the RTL representation
+ to extend a value beyond the precision specified in the mode.
+
+ This flavor should also be used at the TREE and GIMPLE levels of
+ the compiler except for the circumstances described in the
+ descriptions of the other two flavors.
+
+ The default wide_int representation does not contain any
+ information inherent about signedness of the represented value,
+ so it can be used to represent both signed and unsigned numbers.
+ For operations where the results depend on signedness (full width
+ multiply, division, shifts, comparisons, and operations that need
+ overflow detected), the signedness must be specified separately.
+
+ 2) offset_int. This is a fixed size representation that is
+ guaranteed to be large enough to compute any bit or byte sized
+ address calculation on the target. Currently the value is 64 + 4
+ bits rounded up to the next number even multiple of
+ HOST_BITS_PER_WIDE_INT (but this can be changed when the first
+ port needs more than 64 bits for the size of a pointer).
+
+ This flavor can be used for all address math on the target. In
+ this representation, the values are sign or zero extended based
+ on their input types to the internal precision. All math is done
+ in this precision and then the values are truncated to fit in the
+ result type. Unlike most gimple or rtl intermediate code, it is
+ not useful to perform the address arithmetic at the same
+ precision in which the operands are represented because there has
+ been no effort by the front ends to convert most addressing
+ arithmetic to canonical types.
+
+ 3) widest_int. This representation is an approximation of
+ infinite precision math. However, it is not really infinite
+ precision math as in the GMP library. It is really finite
+ precision math where the precision is 4 times the size of the
+ largest integer that the target port can represent.
+
+ widest_int is supposed to be wider than any number that it needs to
+ store, meaning that there is always at least one leading sign bit.
+ All widest_int values are therefore signed.
+
+ There are several places in the GCC where this should/must be used:
+
+ * Code that does induction variable optimizations. This code
+ works with induction variables of many different types at the
+ same time. Because of this, it ends up doing many different
+ calculations where the operands are not compatible types. The
+ widest_int makes this easy, because it provides a field where
+ nothing is lost when converting from any variable,
+
+ * There are a small number of passes that currently use the
+ widest_int that should use the default. These should be
+ changed.
+
+ There are surprising features of offset_int and widest_int
+ that the users should be careful about:
+
+ 1) Shifts and rotations are just weird. You have to specify a
+ precision in which the shift or rotate is to happen in. The bits
+ above this precision remain unchanged. While this is what you
+ want, it is clearly is non obvious.
+
+ 2) Larger precision math sometimes does not produce the same
+ answer as would be expected for doing the math at the proper
+ precision. In particular, a multiply followed by a divide will
+ produce a different answer if the first product is larger than
+ what can be represented in the input precision.
+
+ The offset_int and the widest_int flavors are more expensive
+ than the default wide int, so in addition to the caveats with these
+ two, the default is the prefered representation.
+
+ All three flavors of wide_int are represented as a vector of
+ HOST_WIDE_INTs. The default and widest_int vectors contain enough elements
+ to hold a value of MAX_BITSIZE_MODE_ANY_INT bits. offset_int contains only
+ enough elements to hold ADDR_MAX_PRECISION bits. The values are stored
+ in the vector with the least significant HOST_BITS_PER_WIDE_INT bits
+ in element 0.
+
+ The default wide_int contains three fields: the vector (VAL),
+ the precision and a length (LEN). The length is the number of HWIs
+ needed to represent the value. widest_int and offset_int have a
+ constant precision that cannot be changed, so they only store the
+ VAL and LEN fields.
+
+ Since most integers used in a compiler are small values, it is
+ generally profitable to use a representation of the value that is
+ as small as possible. LEN is used to indicate the number of
+ elements of the vector that are in use. The numbers are stored as
+ sign extended numbers as a means of compression. Leading
+ HOST_WIDE_INTs that contain strings of either -1 or 0 are removed
+ as long as they can be reconstructed from the top bit that is being
+ represented.
+
+ The precision and length of a wide_int are always greater than 0.
+ Any bits in a wide_int above the precision are sign-extended from the
+ most significant bit. For example, a 4-bit value 0x8 is represented as
+ VAL = { 0xf...fff8 }. However, as an optimization, we allow other integer
+ constants to be represented with undefined bits above the precision.
+ This allows INTEGER_CSTs to be pre-extended according to TYPE_SIGN,
+ so that the INTEGER_CST representation can be used both in TYPE_PRECISION
+ and in wider precisions.
+
+ There are constructors to create the various forms of wide_int from
+ trees, rtl and constants. For trees you can simply say:
+
+ tree t = ...;
+ wide_int x = t;
+
+ However, a little more syntax is required for rtl constants since
+ they do have an explicit precision. To make an rtl into a
+ wide_int, you have to pair it with a mode. The canonical way to do
+ this is with std::make_pair as in:
+
+ rtx r = ...
+ wide_int x = std::make_pair (r, mode);
+
+ Similarly, a wide_int can only be constructed from a host value if
+ the target precision is given explicitly, such as in:
+
+ wide_int x = wi::shwi (c, prec); // sign-extend X if necessary
+ wide_int y = wi::uhwi (c, prec); // zero-extend X if necessary
+
+ However, offset_int and widest_int have an inherent precision and so
+ can be initialized directly from a host value:
+
+ offset_int x = (int) c; // sign-extend C
+ widest_int x = (unsigned int) c; // zero-extend C
+
+ It is also possible to do arithmetic directly on trees, rtxes and
+ constants. For example:
+
+ wi::add (t1, t2); // add equal-sized INTEGER_CSTs t1 and t2
+ wi::add (t1, 1); // add 1 to INTEGER_CST t1
+ wi::add (r1, r2); // add equal-sized rtx constants r1 and r2
+ wi::lshift (1, 100); // 1 << 100 as a widest_int
+
+ Many binary operations place restrictions on the combinations of inputs,
+ using the following rules:
+
+ - {tree, rtx, wide_int} op {tree, rtx, wide_int} -> wide_int
+ The inputs must be the same precision. The result is a wide_int
+ of the same precision
+
+ - {tree, rtx, wide_int} op (un)signed HOST_WIDE_INT -> wide_int
+ (un)signed HOST_WIDE_INT op {tree, rtx, wide_int} -> wide_int
+ The HOST_WIDE_INT is extended or truncated to the precision of
+ the other input. The result is a wide_int of the same precision
+ as that input.
+
+ - (un)signed HOST_WIDE_INT op (un)signed HOST_WIDE_INT -> widest_int
+ The inputs are extended to widest_int precision and produce a
+ widest_int result.
+
+ - offset_int op offset_int -> offset_int
+ offset_int op (un)signed HOST_WIDE_INT -> offset_int
+ (un)signed HOST_WIDE_INT op offset_int -> offset_int
+
+ - widest_int op widest_int -> widest_int
+ widest_int op (un)signed HOST_WIDE_INT -> widest_int
+ (un)signed HOST_WIDE_INT op widest_int -> widest_int
+
+ Other combinations like:
+
+ - widest_int op offset_int and
+ - wide_int op offset_int
+
+ are not allowed. The inputs should instead be extended or truncated
+ so that they match.
+
+ The inputs to comparison functions like wi::eq_p and wi::lts_p
+ follow the same compatibility rules, although their return types
+ are different. Unary functions on X produce the same result as
+ a binary operation X + X. Shift functions X op Y also produce
+ the same result as X + X; the precision of the shift amount Y
+ can be arbitrarily different from X. */
+
+
+#include <utility>
+#include "system.h"
+#include "hwint.h"
+#include "signop.h"
+#include "insn-modes.h"
+
+#if 0
+#define DEBUG_WIDE_INT
+#endif
+
+/* The MAX_BITSIZE_MODE_ANY_INT is automatically generated by a very
+ early examination of the target's mode file. The WIDE_INT_MAX_ELTS
+ can accomodate at least 1 more bit so that unsigned numbers of that
+ mode can be represented. Note that it is still possible to create
+ fixed_wide_ints that have precisions greater than
+ MAX_BITSIZE_MODE_ANY_INT. This can be useful when representing a
+ double-width multiplication result, for example. */
+#define WIDE_INT_MAX_ELTS \
+ ((MAX_BITSIZE_MODE_ANY_INT + HOST_BITS_PER_WIDE_INT) / HOST_BITS_PER_WIDE_INT)
+
+#define WIDE_INT_MAX_PRECISION (WIDE_INT_MAX_ELTS * HOST_BITS_PER_WIDE_INT)
+
+/* This is the max size of any pointer on any machine. It does not
+ seem to be as easy to sniff this out of the machine description as
+ it is for MAX_BITSIZE_MODE_ANY_INT since targets may support
+ multiple address sizes and may have different address sizes for
+ different address spaces. However, currently the largest pointer
+ on any platform is 64 bits. When that changes, then it is likely
+ that a target hook should be defined so that targets can make this
+ value larger for those targets. */
+#define ADDR_MAX_BITSIZE 64
+
+/* This is the internal precision used when doing any address
+ arithmetic. The '4' is really 3 + 1. Three of the bits are for
+ the number of extra bits needed to do bit addresses and single bit is
+ allow everything to be signed without loosing any precision. Then
+ everything is rounded up to the next HWI for efficiency. */
+#define ADDR_MAX_PRECISION \
+ ((ADDR_MAX_BITSIZE + 4 + HOST_BITS_PER_WIDE_INT - 1) & ~(HOST_BITS_PER_WIDE_INT - 1))
+
+/* The number of HWIs needed to store an offset_int. */
+#define OFFSET_INT_ELTS (ADDR_MAX_PRECISION / HOST_BITS_PER_WIDE_INT)
+
+/* The type of result produced by a binary operation on types T1 and T2.
+ Defined purely for brevity. */
+#define WI_BINARY_RESULT(T1, T2) \
+ typename wi::binary_traits <T1, T2>::result_type
+
+/* The type of result produced by a unary operation on type T. */
+#define WI_UNARY_RESULT(T) \
+ typename wi::unary_traits <T>::result_type
+
+/* Define a variable RESULT to hold the result of a binary operation on
+ X and Y, which have types T1 and T2 respectively. Define VAR to
+ point to the blocks of RESULT. Once the user of the macro has
+ filled in VAR, it should call RESULT.set_len to set the number
+ of initialized blocks. */
+#define WI_BINARY_RESULT_VAR(RESULT, VAL, T1, X, T2, Y) \
+ WI_BINARY_RESULT (T1, T2) RESULT = \
+ wi::int_traits <WI_BINARY_RESULT (T1, T2)>::get_binary_result (X, Y); \
+ HOST_WIDE_INT *VAL = RESULT.write_val ()
+
+/* Similar for the result of a unary operation on X, which has type T. */
+#define WI_UNARY_RESULT_VAR(RESULT, VAL, T, X) \
+ WI_UNARY_RESULT (T) RESULT = \
+ wi::int_traits <WI_UNARY_RESULT (T)>::get_binary_result (X, X); \
+ HOST_WIDE_INT *VAL = RESULT.write_val ()
+
+template <typename T> struct generic_wide_int;
+template <int N> struct fixed_wide_int_storage;
+struct wide_int_storage;
+
+/* An N-bit integer. Until we can use typedef templates, use this instead. */
+#define FIXED_WIDE_INT(N) \
+ generic_wide_int < fixed_wide_int_storage <N> >
+
+typedef generic_wide_int <wide_int_storage> wide_int;
+typedef FIXED_WIDE_INT (ADDR_MAX_PRECISION) offset_int;
+typedef FIXED_WIDE_INT (WIDE_INT_MAX_PRECISION) widest_int;
+
+template <bool SE>
+struct wide_int_ref_storage;
+
+typedef generic_wide_int <wide_int_ref_storage <false> > wide_int_ref;
+
+/* This can be used instead of wide_int_ref if the referenced value is
+ known to have type T. It carries across properties of T's representation,
+ such as whether excess upper bits in a HWI are defined, and can therefore
+ help avoid redundant work.
+
+ The macro could be replaced with a template typedef, once we're able
+ to use those. */
+#define WIDE_INT_REF_FOR(T) \
+ generic_wide_int \
+ <wide_int_ref_storage <wi::int_traits <T>::is_sign_extended> >
+
+namespace wi
+{
+ /* Classifies an integer based on its precision. */
+ enum precision_type {
+ /* The integer has both a precision and defined signedness. This allows
+ the integer to be converted to any width, since we know whether to fill
+ any extra bits with zeros or signs. */
+ FLEXIBLE_PRECISION,
+
+ /* The integer has a variable precision but no defined signedness. */
+ VAR_PRECISION,
+
+ /* The integer has a constant precision (known at GCC compile time)
+ but no defined signedness. */
+ CONST_PRECISION
+ };
+
+ /* This class, which has no default implementation, is expected to
+ provide the following members:
+
+ static const enum precision_type precision_type;
+ Classifies the type of T.
+
+ static const unsigned int precision;
+ Only defined if precision_type == CONST_PRECISION. Specifies the
+ precision of all integers of type T.
+
+ static const bool host_dependent_precision;
+ True if the precision of T depends (or can depend) on the host.
+
+ static unsigned int get_precision (const T &x)
+ Return the number of bits in X.
+
+ static wi::storage_ref *decompose (HOST_WIDE_INT *scratch,
+ unsigned int precision, const T &x)
+ Decompose X as a PRECISION-bit integer, returning the associated
+ wi::storage_ref. SCRATCH is available as scratch space if needed.
+ The routine should assert that PRECISION is acceptable. */
+ template <typename T> struct int_traits;
+
+ /* This class provides a single type, result_type, which specifies the
+ type of integer produced by a binary operation whose inputs have
+ types T1 and T2. The definition should be symmetric. */
+ template <typename T1, typename T2,
+ enum precision_type P1 = int_traits <T1>::precision_type,
+ enum precision_type P2 = int_traits <T2>::precision_type>
+ struct binary_traits;
+
+ /* The result of a unary operation on T is the same as the result of
+ a binary operation on two values of type T. */
+ template <typename T>
+ struct unary_traits : public binary_traits <T, T> {};
+
+ /* Specify the result type for each supported combination of binary
+ inputs. Note that CONST_PRECISION and VAR_PRECISION cannot be
+ mixed, in order to give stronger type checking. When both inputs
+ are CONST_PRECISION, they must have the same precision. */
+ template <>
+ template <typename T1, typename T2>
+ struct binary_traits <T1, T2, FLEXIBLE_PRECISION, FLEXIBLE_PRECISION>
+ {
+ typedef widest_int result_type;
+ };
+
+ template <>
+ template <typename T1, typename T2>
+ struct binary_traits <T1, T2, FLEXIBLE_PRECISION, VAR_PRECISION>
+ {
+ typedef wide_int result_type;
+ };
+
+ template <>
+ template <typename T1, typename T2>
+ struct binary_traits <T1, T2, FLEXIBLE_PRECISION, CONST_PRECISION>
+ {
+ /* Spelled out explicitly (rather than through FIXED_WIDE_INT)
+ so as not to confuse gengtype. */
+ typedef generic_wide_int < fixed_wide_int_storage
+ <int_traits <T2>::precision> > result_type;
+ };
+
+ template <>
+ template <typename T1, typename T2>
+ struct binary_traits <T1, T2, VAR_PRECISION, FLEXIBLE_PRECISION>
+ {
+ typedef wide_int result_type;
+ };
+
+ template <>
+ template <typename T1, typename T2>
+ struct binary_traits <T1, T2, CONST_PRECISION, FLEXIBLE_PRECISION>
+ {
+ /* Spelled out explicitly (rather than through FIXED_WIDE_INT)
+ so as not to confuse gengtype. */
+ typedef generic_wide_int < fixed_wide_int_storage
+ <int_traits <T1>::precision> > result_type;
+ };
+
+ template <>
+ template <typename T1, typename T2>
+ struct binary_traits <T1, T2, CONST_PRECISION, CONST_PRECISION>
+ {
+ /* Spelled out explicitly (rather than through FIXED_WIDE_INT)
+ so as not to confuse gengtype. */
+ STATIC_ASSERT (int_traits <T1>::precision == int_traits <T2>::precision);
+ typedef generic_wide_int < fixed_wide_int_storage
+ <int_traits <T1>::precision> > result_type;
+ };
+
+ template <>
+ template <typename T1, typename T2>
+ struct binary_traits <T1, T2, VAR_PRECISION, VAR_PRECISION>
+ {
+ typedef wide_int result_type;
+ };
+}
+
+/* Public functions for querying and operating on integers. */
+namespace wi
+{
+ template <typename T>
+ unsigned int get_precision (const T &);
+
+ template <typename T1, typename T2>
+ unsigned int get_binary_precision (const T1 &, const T2 &);
+
+ template <typename T1, typename T2>
+ void copy (T1 &, const T2 &);
+
+#define UNARY_PREDICATE \
+ template <typename T> bool
+#define UNARY_FUNCTION \
+ template <typename T> WI_UNARY_RESULT (T)
+#define BINARY_PREDICATE \
+ template <typename T1, typename T2> bool
+#define BINARY_FUNCTION \
+ template <typename T1, typename T2> WI_BINARY_RESULT (T1, T2)
+#define SHIFT_FUNCTION \
+ template <typename T1, typename T2> WI_UNARY_RESULT (T1)
+
+ UNARY_PREDICATE fits_shwi_p (const T &);
+ UNARY_PREDICATE fits_uhwi_p (const T &);
+ UNARY_PREDICATE neg_p (const T &, signop = SIGNED);
+
+ template <typename T>
+ HOST_WIDE_INT sign_mask (const T &);
+
+ BINARY_PREDICATE eq_p (const T1 &, const T2 &);
+ BINARY_PREDICATE ne_p (const T1 &, const T2 &);
+ BINARY_PREDICATE lt_p (const T1 &, const T2 &, signop);
+ BINARY_PREDICATE lts_p (const T1 &, const T2 &);
+ BINARY_PREDICATE ltu_p (const T1 &, const T2 &);
+ BINARY_PREDICATE le_p (const T1 &, const T2 &, signop);
+ BINARY_PREDICATE les_p (const T1 &, const T2 &);
+ BINARY_PREDICATE leu_p (const T1 &, const T2 &);
+ BINARY_PREDICATE gt_p (const T1 &, const T2 &, signop);
+ BINARY_PREDICATE gts_p (const T1 &, const T2 &);
+ BINARY_PREDICATE gtu_p (const T1 &, const T2 &);
+ BINARY_PREDICATE ge_p (const T1 &, const T2 &, signop);
+ BINARY_PREDICATE ges_p (const T1 &, const T2 &);
+ BINARY_PREDICATE geu_p (const T1 &, const T2 &);
+
+ template <typename T1, typename T2>
+ int cmp (const T1 &, const T2 &, signop);
+
+ template <typename T1, typename T2>
+ int cmps (const T1 &, const T2 &);
+
+ template <typename T1, typename T2>
+ int cmpu (const T1 &, const T2 &);
+
+ UNARY_FUNCTION bit_not (const T &);
+ UNARY_FUNCTION neg (const T &);
+ UNARY_FUNCTION neg (const T &, bool *);
+ UNARY_FUNCTION abs (const T &);
+ UNARY_FUNCTION ext (const T &, unsigned int, signop);
+ UNARY_FUNCTION sext (const T &, unsigned int);
+ UNARY_FUNCTION zext (const T &, unsigned int);
+ UNARY_FUNCTION set_bit (const T &, unsigned int);
+
+ BINARY_FUNCTION min (const T1 &, const T2 &, signop);
+ BINARY_FUNCTION smin (const T1 &, const T2 &);
+ BINARY_FUNCTION umin (const T1 &, const T2 &);
+ BINARY_FUNCTION max (const T1 &, const T2 &, signop);
+ BINARY_FUNCTION smax (const T1 &, const T2 &);
+ BINARY_FUNCTION umax (const T1 &, const T2 &);
+
+ BINARY_FUNCTION bit_and (const T1 &, const T2 &);
+ BINARY_FUNCTION bit_and_not (const T1 &, const T2 &);
+ BINARY_FUNCTION bit_or (const T1 &, const T2 &);
+ BINARY_FUNCTION bit_or_not (const T1 &, const T2 &);
+ BINARY_FUNCTION bit_xor (const T1 &, const T2 &);
+ BINARY_FUNCTION add (const T1 &, const T2 &);
+ BINARY_FUNCTION add (const T1 &, const T2 &, signop, bool *);
+ BINARY_FUNCTION sub (const T1 &, const T2 &);
+ BINARY_FUNCTION sub (const T1 &, const T2 &, signop, bool *);
+ BINARY_FUNCTION mul (const T1 &, const T2 &);
+ BINARY_FUNCTION mul (const T1 &, const T2 &, signop, bool *);
+ BINARY_FUNCTION smul (const T1 &, const T2 &, bool *);
+ BINARY_FUNCTION umul (const T1 &, const T2 &, bool *);
+ BINARY_FUNCTION mul_high (const T1 &, const T2 &, signop);
+ BINARY_FUNCTION div_trunc (const T1 &, const T2 &, signop, bool * = 0);
+ BINARY_FUNCTION sdiv_trunc (const T1 &, const T2 &);
+ BINARY_FUNCTION udiv_trunc (const T1 &, const T2 &);
+ BINARY_FUNCTION div_floor (const T1 &, const T2 &, signop, bool * = 0);
+ BINARY_FUNCTION udiv_floor (const T1 &, const T2 &);
+ BINARY_FUNCTION sdiv_floor (const T1 &, const T2 &);
+ BINARY_FUNCTION div_ceil (const T1 &, const T2 &, signop, bool * = 0);
+ BINARY_FUNCTION udiv_ceil (const T1 &, const T2 &);
+ BINARY_FUNCTION div_round (const T1 &, const T2 &, signop, bool * = 0);
+ BINARY_FUNCTION divmod_trunc (const T1 &, const T2 &, signop,
+ WI_BINARY_RESULT (T1, T2) *);
+ BINARY_FUNCTION mod_trunc (const T1 &, const T2 &, signop, bool * = 0);
+ BINARY_FUNCTION smod_trunc (const T1 &, const T2 &);
+ BINARY_FUNCTION umod_trunc (const T1 &, const T2 &);
+ BINARY_FUNCTION mod_floor (const T1 &, const T2 &, signop, bool * = 0);
+ BINARY_FUNCTION umod_floor (const T1 &, const T2 &);
+ BINARY_FUNCTION mod_ceil (const T1 &, const T2 &, signop, bool * = 0);
+ BINARY_FUNCTION mod_round (const T1 &, const T2 &, signop, bool * = 0);
+
+ template <typename T1, typename T2>
+ bool multiple_of_p (const T1 &, const T2 &, signop,
+ WI_BINARY_RESULT (T1, T2) *);
+
+ SHIFT_FUNCTION lshift (const T1 &, const T2 &);
+ SHIFT_FUNCTION lrshift (const T1 &, const T2 &);
+ SHIFT_FUNCTION arshift (const T1 &, const T2 &);
+ SHIFT_FUNCTION rshift (const T1 &, const T2 &, signop sgn);
+ SHIFT_FUNCTION lrotate (const T1 &, const T2 &, unsigned int = 0);
+ SHIFT_FUNCTION rrotate (const T1 &, const T2 &, unsigned int = 0);
+
+#undef SHIFT_FUNCTION
+#undef BINARY_PREDICATE
+#undef BINARY_FUNCTION
+#undef UNARY_PREDICATE
+#undef UNARY_FUNCTION
+
+ bool only_sign_bit_p (const wide_int_ref &, unsigned int);
+ bool only_sign_bit_p (const wide_int_ref &);
+ int clz (const wide_int_ref &);
+ int clrsb (const wide_int_ref &);
+ int ctz (const wide_int_ref &);
+ int exact_log2 (const wide_int_ref &);
+ int floor_log2 (const wide_int_ref &);
+ int ffs (const wide_int_ref &);
+ int popcount (const wide_int_ref &);
+ int parity (const wide_int_ref &);
+
+ template <typename T>
+ unsigned HOST_WIDE_INT extract_uhwi (const T &, unsigned int, unsigned int);
+
+ template <typename T>
+ unsigned int min_precision (const T &, signop);
+}
+
+namespace wi
+{
+ /* Contains the components of a decomposed integer for easy, direct
+ access. */
+ struct storage_ref
+ {
+ storage_ref (const HOST_WIDE_INT *, unsigned int, unsigned int);
+
+ const HOST_WIDE_INT *val;
+ unsigned int len;
+ unsigned int precision;
+
+ /* Provide enough trappings for this class to act as storage for
+ generic_wide_int. */
+ unsigned int get_len () const;
+ unsigned int get_precision () const;
+ const HOST_WIDE_INT *get_val () const;
+ };
+}
+
+inline::wi::storage_ref::storage_ref (const HOST_WIDE_INT *val_in,
+ unsigned int len_in,
+ unsigned int precision_in)
+ : val (val_in), len (len_in), precision (precision_in)
+{
+}
+
+inline unsigned int
+wi::storage_ref::get_len () const
+{
+ return len;
+}
+
+inline unsigned int
+wi::storage_ref::get_precision () const
+{
+ return precision;
+}
+
+inline const HOST_WIDE_INT *
+wi::storage_ref::get_val () const
+{
+ return val;
+}
+
+/* This class defines an integer type using the storage provided by the
+ template argument. The storage class must provide the following
+ functions:
+
+ unsigned int get_precision () const
+ Return the number of bits in the integer.
+
+ HOST_WIDE_INT *get_val () const
+ Return a pointer to the array of blocks that encodes the integer.
+
+ unsigned int get_len () const
+ Return the number of blocks in get_val (). If this is smaller
+ than the number of blocks implied by get_precision (), the
+ remaining blocks are sign extensions of block get_len () - 1.
+
+ Although not required by generic_wide_int itself, writable storage
+ classes can also provide the following functions:
+
+ HOST_WIDE_INT *write_val ()
+ Get a modifiable version of get_val ()
+
+ unsigned int set_len (unsigned int len)
+ Set the value returned by get_len () to LEN. */
+template <typename storage>
+class GTY(()) generic_wide_int : public storage
+{
+public:
+ generic_wide_int ();
+
+ template <typename T>
+ generic_wide_int (const T &);
+
+ template <typename T>
+ generic_wide_int (const T &, unsigned int);
+
+ /* Conversions. */
+ HOST_WIDE_INT to_shwi (unsigned int) const;
+ HOST_WIDE_INT to_shwi () const;
+ unsigned HOST_WIDE_INT to_uhwi (unsigned int) const;
+ unsigned HOST_WIDE_INT to_uhwi () const;
+ HOST_WIDE_INT to_short_addr () const;
+
+ /* Public accessors for the interior of a wide int. */
+ HOST_WIDE_INT sign_mask () const;
+ HOST_WIDE_INT elt (unsigned int) const;
+ unsigned HOST_WIDE_INT ulow () const;
+ unsigned HOST_WIDE_INT uhigh () const;
+ HOST_WIDE_INT slow () const;
+ HOST_WIDE_INT shigh () const;
+
+ template <typename T>
+ generic_wide_int &operator = (const T &);
+
+#define BINARY_PREDICATE(OP, F) \
+ template <typename T> \
+ bool OP (const T &c) const { return wi::F (*this, c); }
+
+#define UNARY_OPERATOR(OP, F) \
+ WI_UNARY_RESULT (generic_wide_int) OP () const { return wi::F (*this); }
+
+#define BINARY_OPERATOR(OP, F) \
+ template <typename T> \
+ WI_BINARY_RESULT (generic_wide_int, T) \
+ OP (const T &c) const { return wi::F (*this, c); }
+
+#define ASSIGNMENT_OPERATOR(OP, F) \
+ template <typename T> \
+ generic_wide_int &OP (const T &c) { return (*this = wi::F (*this, c)); }
+
+#define INCDEC_OPERATOR(OP, DELTA) \
+ generic_wide_int &OP () { *this += DELTA; return *this; }
+
+ UNARY_OPERATOR (operator ~, bit_not)
+ UNARY_OPERATOR (operator -, neg)
+ BINARY_PREDICATE (operator ==, eq_p)
+ BINARY_PREDICATE (operator !=, ne_p)
+ BINARY_OPERATOR (operator &, bit_and)
+ BINARY_OPERATOR (and_not, bit_and_not)
+ BINARY_OPERATOR (operator |, bit_or)
+ BINARY_OPERATOR (or_not, bit_or_not)
+ BINARY_OPERATOR (operator ^, bit_xor)
+ BINARY_OPERATOR (operator +, add)
+ BINARY_OPERATOR (operator -, sub)
+ BINARY_OPERATOR (operator *, mul)
+ ASSIGNMENT_OPERATOR (operator &=, bit_and)
+ ASSIGNMENT_OPERATOR (operator |=, bit_or)
+ ASSIGNMENT_OPERATOR (operator ^=, bit_xor)
+ ASSIGNMENT_OPERATOR (operator +=, add)
+ ASSIGNMENT_OPERATOR (operator -=, sub)
+ ASSIGNMENT_OPERATOR (operator *=, mul)
+ INCDEC_OPERATOR (operator ++, 1)
+ INCDEC_OPERATOR (operator --, -1)
+
+#undef BINARY_PREDICATE
+#undef UNARY_OPERATOR
+#undef BINARY_OPERATOR
+#undef ASSIGNMENT_OPERATOR
+#undef INCDEC_OPERATOR
+
+ char *dump (char *) const;
+
+ static const bool is_sign_extended
+ = wi::int_traits <generic_wide_int <storage> >::is_sign_extended;
+};
+
+template <typename storage>
+inline generic_wide_int <storage>::generic_wide_int () {}
+
+template <typename storage>
+template <typename T>
+inline generic_wide_int <storage>::generic_wide_int (const T &x)
+ : storage (x)
+{
+}
+
+template <typename storage>
+template <typename T>
+inline generic_wide_int <storage>::generic_wide_int (const T &x,
+ unsigned int precision)
+ : storage (x, precision)
+{
+}
+
+/* Return THIS as a signed HOST_WIDE_INT, sign-extending from PRECISION.
+ If THIS does not fit in PRECISION, the information is lost. */
+template <typename storage>
+inline HOST_WIDE_INT
+generic_wide_int <storage>::to_shwi (unsigned int precision) const
+{
+ if (precision < HOST_BITS_PER_WIDE_INT)
+ return sext_hwi (this->get_val ()[0], precision);
+ else
+ return this->get_val ()[0];
+}
+
+/* Return THIS as a signed HOST_WIDE_INT, in its natural precision. */
+template <typename storage>
+inline HOST_WIDE_INT
+generic_wide_int <storage>::to_shwi () const
+{
+ if (is_sign_extended)
+ return this->get_val ()[0];
+ else
+ return to_shwi (this->get_precision ());
+}
+
+/* Return THIS as an unsigned HOST_WIDE_INT, zero-extending from
+ PRECISION. If THIS does not fit in PRECISION, the information
+ is lost. */
+template <typename storage>
+inline unsigned HOST_WIDE_INT
+generic_wide_int <storage>::to_uhwi (unsigned int precision) const
+{
+ if (precision < HOST_BITS_PER_WIDE_INT)
+ return zext_hwi (this->get_val ()[0], precision);
+ else
+ return this->get_val ()[0];
+}
+
+/* Return THIS as an signed HOST_WIDE_INT, in its natural precision. */
+template <typename storage>
+inline unsigned HOST_WIDE_INT
+generic_wide_int <storage>::to_uhwi () const
+{
+ return to_uhwi (this->get_precision ());
+}
+
+/* TODO: The compiler is half converted from using HOST_WIDE_INT to
+ represent addresses to using offset_int to represent addresses.
+ We use to_short_addr at the interface from new code to old,
+ unconverted code. */
+template <typename storage>
+inline HOST_WIDE_INT
+generic_wide_int <storage>::to_short_addr () const
+{
+ return this->get_val ()[0];
+}
+
+/* Return the implicit value of blocks above get_len (). */
+template <typename storage>
+inline HOST_WIDE_INT
+generic_wide_int <storage>::sign_mask () const
+{
+ unsigned int len = this->get_len ();
+ unsigned HOST_WIDE_INT high = this->get_val ()[len - 1];
+ if (!is_sign_extended)
+ {
+ unsigned int precision = this->get_precision ();
+ int excess = len * HOST_BITS_PER_WIDE_INT - precision;
+ if (excess > 0)
+ high <<= excess;
+ }
+ return (HOST_WIDE_INT) (high) < 0 ? -1 : 0;
+}
+
+/* Return the signed value of the least-significant explicitly-encoded
+ block. */
+template <typename storage>
+inline HOST_WIDE_INT
+generic_wide_int <storage>::slow () const
+{
+ return this->get_val ()[0];
+}
+
+/* Return the signed value of the most-significant explicitly-encoded
+ block. */
+template <typename storage>
+inline HOST_WIDE_INT
+generic_wide_int <storage>::shigh () const
+{
+ return this->get_val ()[this->get_len () - 1];
+}
+
+/* Return the unsigned value of the least-significant
+ explicitly-encoded block. */
+template <typename storage>
+inline unsigned HOST_WIDE_INT
+generic_wide_int <storage>::ulow () const
+{
+ return this->get_val ()[0];
+}
+
+/* Return the unsigned value of the most-significant
+ explicitly-encoded block. */
+template <typename storage>
+inline unsigned HOST_WIDE_INT
+generic_wide_int <storage>::uhigh () const
+{
+ return this->get_val ()[this->get_len () - 1];
+}
+
+/* Return block I, which might be implicitly or explicit encoded. */
+template <typename storage>
+inline HOST_WIDE_INT
+generic_wide_int <storage>::elt (unsigned int i) const
+{
+ if (i >= this->get_len ())
+ return sign_mask ();
+ else
+ return this->get_val ()[i];
+}
+
+template <typename storage>
+template <typename T>
+generic_wide_int <storage> &
+generic_wide_int <storage>::operator = (const T &x)
+{
+ storage::operator = (x);
+ return *this;
+}
+
+namespace wi
+{
+ template <>
+ template <typename storage>
+ struct int_traits < generic_wide_int <storage> >
+ : public wi::int_traits <storage>
+ {
+ static unsigned int get_precision (const generic_wide_int <storage> &);
+ static wi::storage_ref decompose (HOST_WIDE_INT *, unsigned int,
+ const generic_wide_int <storage> &);
+ };
+}
+
+template <typename storage>
+inline unsigned int
+wi::int_traits < generic_wide_int <storage> >::
+get_precision (const generic_wide_int <storage> &x)
+{
+ return x.get_precision ();
+}
+
+template <typename storage>
+inline wi::storage_ref
+wi::int_traits < generic_wide_int <storage> >::
+decompose (HOST_WIDE_INT *, unsigned int precision,
+ const generic_wide_int <storage> &x)
+{
+ gcc_checking_assert (precision == x.get_precision ());
+ return wi::storage_ref (x.get_val (), x.get_len (), precision);
+}
+
+/* Provide the storage for a wide_int_ref. This acts like a read-only
+ wide_int, with the optimization that VAL is normally a pointer to
+ another integer's storage, so that no array copy is needed. */
+template <bool SE>
+struct wide_int_ref_storage : public wi::storage_ref
+{
+private:
+ /* Scratch space that can be used when decomposing the original integer.
+ It must live as long as this object. */
+ HOST_WIDE_INT scratch[2];
+
+public:
+ wide_int_ref_storage (const wi::storage_ref &);
+
+ template <typename T>
+ wide_int_ref_storage (const T &);
+
+ template <typename T>
+ wide_int_ref_storage (const T &, unsigned int);
+};
+
+/* Create a reference from an existing reference. */
+template <bool SE>
+inline wide_int_ref_storage <SE>::
+wide_int_ref_storage (const wi::storage_ref &x)
+ : storage_ref (x)
+{}
+
+/* Create a reference to integer X in its natural precision. Note
+ that the natural precision is host-dependent for primitive
+ types. */
+template <bool SE>
+template <typename T>
+inline wide_int_ref_storage <SE>::wide_int_ref_storage (const T &x)
+ : storage_ref (wi::int_traits <T>::decompose (scratch,
+ wi::get_precision (x), x))
+{
+}
+
+/* Create a reference to integer X in precision PRECISION. */
+template <bool SE>
+template <typename T>
+inline wide_int_ref_storage <SE>::wide_int_ref_storage (const T &x,
+ unsigned int precision)
+ : storage_ref (wi::int_traits <T>::decompose (scratch, precision, x))
+{
+}
+
+namespace wi
+{
+ template <>
+ template <bool SE>
+ struct int_traits <wide_int_ref_storage <SE> >
+ {
+ static const enum precision_type precision_type = VAR_PRECISION;
+ /* wi::storage_ref can be a reference to a primitive type,
+ so this is the conservatively-correct setting. */
+ static const bool host_dependent_precision = true;
+ static const bool is_sign_extended = SE;
+ };
+}
+
+namespace wi
+{
+ unsigned int force_to_size (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, unsigned int, unsigned int,
+ signop sgn);
+ unsigned int from_array (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, unsigned int, bool = true);
+}
+
+/* The storage used by wide_int. */
+class GTY(()) wide_int_storage
+{
+private:
+ HOST_WIDE_INT val[WIDE_INT_MAX_ELTS];
+ unsigned int len;
+ unsigned int precision;
+
+public:
+ wide_int_storage ();
+ template <typename T>
+ wide_int_storage (const T &);
+
+ /* The standard generic_wide_int storage methods. */
+ unsigned int get_precision () const;
+ const HOST_WIDE_INT *get_val () const;
+ unsigned int get_len () const;
+ HOST_WIDE_INT *write_val ();
+ void set_len (unsigned int, bool = false);
+
+ static wide_int from (const wide_int_ref &, unsigned int, signop);
+ static wide_int from_array (const HOST_WIDE_INT *, unsigned int,
+ unsigned int, bool = true);
+ static wide_int create (unsigned int);
+
+ /* FIXME: target-dependent, so should disappear. */
+ wide_int bswap () const;
+};
+
+namespace wi
+{
+ template <>
+ struct int_traits <wide_int_storage>
+ {
+ static const enum precision_type precision_type = VAR_PRECISION;
+ /* Guaranteed by a static assert in the wide_int_storage constructor. */
+ static const bool host_dependent_precision = false;
+ static const bool is_sign_extended = true;
+ template <typename T1, typename T2>
+ static wide_int get_binary_result (const T1 &, const T2 &);
+ };
+}
+
+inline wide_int_storage::wide_int_storage () {}
+
+/* Initialize the storage from integer X, in its natural precision.
+ Note that we do not allow integers with host-dependent precision
+ to become wide_ints; wide_ints must always be logically independent
+ of the host. */
+template <typename T>
+inline wide_int_storage::wide_int_storage (const T &x)
+{
+ STATIC_ASSERT (!wi::int_traits<T>::host_dependent_precision);
+ WIDE_INT_REF_FOR (T) xi (x);
+ precision = xi.precision;
+ wi::copy (*this, xi);
+}
+
+inline unsigned int
+wide_int_storage::get_precision () const
+{
+ return precision;
+}
+
+inline const HOST_WIDE_INT *
+wide_int_storage::get_val () const
+{
+ return val;
+}
+
+inline unsigned int
+wide_int_storage::get_len () const
+{
+ return len;
+}
+
+inline HOST_WIDE_INT *
+wide_int_storage::write_val ()
+{
+ return val;
+}
+
+inline void
+wide_int_storage::set_len (unsigned int l, bool is_sign_extended)
+{
+ len = l;
+ if (!is_sign_extended && len * HOST_BITS_PER_WIDE_INT > precision)
+ val[len - 1] = sext_hwi (val[len - 1],
+ precision % HOST_BITS_PER_WIDE_INT);
+}
+
+/* Treat X as having signedness SGN and convert it to a PRECISION-bit
+ number. */
+inline wide_int
+wide_int_storage::from (const wide_int_ref &x, unsigned int precision,
+ signop sgn)
+{
+ wide_int result = wide_int::create (precision);
+ result.set_len (wi::force_to_size (result.write_val (), x.val, x.len,
+ x.precision, precision, sgn));
+ return result;
+}
+
+/* Create a wide_int from the explicit block encoding given by VAL and
+ LEN. PRECISION is the precision of the integer. NEED_CANON_P is
+ true if the encoding may have redundant trailing blocks. */
+inline wide_int
+wide_int_storage::from_array (const HOST_WIDE_INT *val, unsigned int len,
+ unsigned int precision, bool need_canon_p)
+{
+ wide_int result = wide_int::create (precision);
+ result.set_len (wi::from_array (result.write_val (), val, len, precision,
+ need_canon_p));
+ return result;
+}
+
+/* Return an uninitialized wide_int with precision PRECISION. */
+inline wide_int
+wide_int_storage::create (unsigned int precision)
+{
+ wide_int x;
+ x.precision = precision;
+ return x;
+}
+
+template <typename T1, typename T2>
+inline wide_int
+wi::int_traits <wide_int_storage>::get_binary_result (const T1 &x, const T2 &y)
+{
+ /* This shouldn't be used for two flexible-precision inputs. */
+ STATIC_ASSERT (wi::int_traits <T1>::precision_type != FLEXIBLE_PRECISION
+ || wi::int_traits <T2>::precision_type != FLEXIBLE_PRECISION);
+ if (wi::int_traits <T1>::precision_type == FLEXIBLE_PRECISION)
+ return wide_int::create (wi::get_precision (y));
+ else
+ return wide_int::create (wi::get_precision (x));
+}
+
+/* The storage used by FIXED_WIDE_INT (N). */
+template <int N>
+class GTY(()) fixed_wide_int_storage
+{
+private:
+ HOST_WIDE_INT val[(N + HOST_BITS_PER_WIDE_INT + 1) / HOST_BITS_PER_WIDE_INT];
+ unsigned int len;
+
+public:
+ fixed_wide_int_storage ();
+ template <typename T>
+ fixed_wide_int_storage (const T &);
+
+ /* The standard generic_wide_int storage methods. */
+ unsigned int get_precision () const;
+ const HOST_WIDE_INT *get_val () const;
+ unsigned int get_len () const;
+ HOST_WIDE_INT *write_val ();
+ void set_len (unsigned int, bool = false);
+
+ static FIXED_WIDE_INT (N) from (const wide_int_ref &, signop);
+ static FIXED_WIDE_INT (N) from_array (const HOST_WIDE_INT *, unsigned int,
+ bool = true);
+};
+
+namespace wi
+{
+ template <>
+ template <int N>
+ struct int_traits < fixed_wide_int_storage <N> >
+ {
+ static const enum precision_type precision_type = CONST_PRECISION;
+ static const bool host_dependent_precision = false;
+ static const bool is_sign_extended = true;
+ static const unsigned int precision = N;
+ template <typename T1, typename T2>
+ static FIXED_WIDE_INT (N) get_binary_result (const T1 &, const T2 &);
+ };
+}
+
+template <int N>
+inline fixed_wide_int_storage <N>::fixed_wide_int_storage () {}
+
+/* Initialize the storage from integer X, in precision N. */
+template <int N>
+template <typename T>
+inline fixed_wide_int_storage <N>::fixed_wide_int_storage (const T &x)
+{
+ /* Check for type compatibility. We don't want to initialize a
+ fixed-width integer from something like a wide_int. */
+ WI_BINARY_RESULT (T, FIXED_WIDE_INT (N)) *assertion ATTRIBUTE_UNUSED;
+ wi::copy (*this, WIDE_INT_REF_FOR (T) (x, N));
+}
+
+template <int N>
+inline unsigned int
+fixed_wide_int_storage <N>::get_precision () const
+{
+ return N;
+}
+
+template <int N>
+inline const HOST_WIDE_INT *
+fixed_wide_int_storage <N>::get_val () const
+{
+ return val;
+}
+
+template <int N>
+inline unsigned int
+fixed_wide_int_storage <N>::get_len () const
+{
+ return len;
+}
+
+template <int N>
+inline HOST_WIDE_INT *
+fixed_wide_int_storage <N>::write_val ()
+{
+ return val;
+}
+
+template <int N>
+inline void
+fixed_wide_int_storage <N>::set_len (unsigned int l, bool)
+{
+ len = l;
+ /* There are no excess bits in val[len - 1]. */
+ STATIC_ASSERT (N % HOST_BITS_PER_WIDE_INT == 0);
+}
+
+/* Treat X as having signedness SGN and convert it to an N-bit number. */
+template <int N>
+inline FIXED_WIDE_INT (N)
+fixed_wide_int_storage <N>::from (const wide_int_ref &x, signop sgn)
+{
+ FIXED_WIDE_INT (N) result;
+ result.set_len (wi::force_to_size (result.write_val (), x.val, x.len,
+ x.precision, N, sgn));
+ return result;
+}
+
+/* Create a FIXED_WIDE_INT (N) from the explicit block encoding given by
+ VAL and LEN. NEED_CANON_P is true if the encoding may have redundant
+ trailing blocks. */
+template <int N>
+inline FIXED_WIDE_INT (N)
+fixed_wide_int_storage <N>::from_array (const HOST_WIDE_INT *val,
+ unsigned int len,
+ bool need_canon_p)
+{
+ FIXED_WIDE_INT (N) result;
+ result.set_len (wi::from_array (result.write_val (), val, len,
+ N, need_canon_p));
+ return result;
+}
+
+template <int N>
+template <typename T1, typename T2>
+inline FIXED_WIDE_INT (N)
+wi::int_traits < fixed_wide_int_storage <N> >::
+get_binary_result (const T1 &, const T2 &)
+{
+ return FIXED_WIDE_INT (N) ();
+}
+
+/* A reference to one element of a trailing_wide_ints structure. */
+class trailing_wide_int_storage
+{
+private:
+ /* The precision of the integer, which is a fixed property of the
+ parent trailing_wide_ints. */
+ unsigned int m_precision;
+
+ /* A pointer to the length field. */
+ unsigned char *m_len;
+
+ /* A pointer to the HWI array. There are enough elements to hold all
+ values of precision M_PRECISION. */
+ HOST_WIDE_INT *m_val;
+
+public:
+ trailing_wide_int_storage (unsigned int, unsigned char *, HOST_WIDE_INT *);
+
+ /* The standard generic_wide_int storage methods. */
+ unsigned int get_len () const;
+ unsigned int get_precision () const;
+ const HOST_WIDE_INT *get_val () const;
+ HOST_WIDE_INT *write_val ();
+ void set_len (unsigned int, bool = false);
+
+ template <typename T>
+ trailing_wide_int_storage &operator = (const T &);
+};
+
+typedef generic_wide_int <trailing_wide_int_storage> trailing_wide_int;
+
+/* trailing_wide_int behaves like a wide_int. */
+namespace wi
+{
+ template <>
+ struct int_traits <trailing_wide_int_storage>
+ : public int_traits <wide_int_storage> {};
+}
+
+/* An array of N wide_int-like objects that can be put at the end of
+ a variable-sized structure. Use extra_size to calculate how many
+ bytes beyond the sizeof need to be allocated. Use set_precision
+ to initialize the structure. */
+template <int N>
+class GTY(()) trailing_wide_ints
+{
+private:
+ /* The shared precision of each number. */
+ unsigned short m_precision;
+
+ /* The shared maximum length of each number. */
+ unsigned char m_max_len;
+
+ /* The current length of each number. */
+ unsigned char m_len[N];
+
+ /* The variable-length part of the structure, which always contains
+ at least one HWI. Element I starts at index I * M_MAX_LEN. */
+ HOST_WIDE_INT m_val[1];
+
+public:
+ void set_precision (unsigned int);
+ trailing_wide_int operator [] (unsigned int);
+ static size_t extra_size (unsigned int);
+};
+
+inline trailing_wide_int_storage::
+trailing_wide_int_storage (unsigned int precision, unsigned char *len,
+ HOST_WIDE_INT *val)
+ : m_precision (precision), m_len (len), m_val (val)
+{
+}
+
+inline unsigned int
+trailing_wide_int_storage::get_len () const
+{
+ return *m_len;
+}
+
+inline unsigned int
+trailing_wide_int_storage::get_precision () const
+{
+ return m_precision;
+}
+
+inline const HOST_WIDE_INT *
+trailing_wide_int_storage::get_val () const
+{
+ return m_val;
+}
+
+inline HOST_WIDE_INT *
+trailing_wide_int_storage::write_val ()
+{
+ return m_val;
+}
+
+inline void
+trailing_wide_int_storage::set_len (unsigned int len, bool is_sign_extended)
+{
+ *m_len = len;
+ if (!is_sign_extended && len * HOST_BITS_PER_WIDE_INT > m_precision)
+ m_val[len - 1] = sext_hwi (m_val[len - 1],
+ m_precision % HOST_BITS_PER_WIDE_INT);
+}
+
+template <typename T>
+inline trailing_wide_int_storage &
+trailing_wide_int_storage::operator = (const T &x)
+{
+ WIDE_INT_REF_FOR (T) xi (x, m_precision);
+ wi::copy (*this, xi);
+ return *this;
+}
+
+/* Initialize the structure and record that all elements have precision
+ PRECISION. */
+template <int N>
+inline void
+trailing_wide_ints <N>::set_precision (unsigned int precision)
+{
+ m_precision = precision;
+ m_max_len = ((precision + HOST_BITS_PER_WIDE_INT - 1)
+ / HOST_BITS_PER_WIDE_INT);
+}
+
+/* Return a reference to element INDEX. */
+template <int N>
+inline trailing_wide_int
+trailing_wide_ints <N>::operator [] (unsigned int index)
+{
+ return trailing_wide_int_storage (m_precision, &m_len[index],
+ &m_val[index * m_max_len]);
+}
+
+/* Return how many extra bytes need to be added to the end of the structure
+ in order to handle N wide_ints of precision PRECISION. */
+template <int N>
+inline size_t
+trailing_wide_ints <N>::extra_size (unsigned int precision)
+{
+ unsigned int max_len = ((precision + HOST_BITS_PER_WIDE_INT - 1)
+ / HOST_BITS_PER_WIDE_INT);
+ return (N * max_len - 1) * sizeof (HOST_WIDE_INT);
+}
+
+/* This macro is used in structures that end with a trailing_wide_ints field
+ called FIELD. It declares get_NAME() and set_NAME() methods to access
+ element I of FIELD. */
+#define TRAILING_WIDE_INT_ACCESSOR(NAME, FIELD, I) \
+ trailing_wide_int get_##NAME () { return FIELD[I]; } \
+ template <typename T> void set_##NAME (const T &x) { FIELD[I] = x; }
+
+namespace wi
+{
+ /* Implementation of int_traits for primitive integer types like "int". */
+ template <typename T, bool signed_p>
+ struct primitive_int_traits
+ {
+ static const enum precision_type precision_type = FLEXIBLE_PRECISION;
+ static const bool host_dependent_precision = true;
+ static const bool is_sign_extended = true;
+ static unsigned int get_precision (T);
+ static wi::storage_ref decompose (HOST_WIDE_INT *, unsigned int, T);
+ };
+}
+
+template <typename T, bool signed_p>
+inline unsigned int
+wi::primitive_int_traits <T, signed_p>::get_precision (T)
+{
+ return sizeof (T) * CHAR_BIT;
+}
+
+template <typename T, bool signed_p>
+inline wi::storage_ref
+wi::primitive_int_traits <T, signed_p>::decompose (HOST_WIDE_INT *scratch,
+ unsigned int precision, T x)
+{
+ scratch[0] = x;
+ if (signed_p || scratch[0] >= 0 || precision <= HOST_BITS_PER_WIDE_INT)
+ return wi::storage_ref (scratch, 1, precision);
+ scratch[1] = 0;
+ return wi::storage_ref (scratch, 2, precision);
+}
+
+/* Allow primitive C types to be used in wi:: routines. */
+namespace wi
+{
+ template <>
+ struct int_traits <int>
+ : public primitive_int_traits <int, true> {};
+
+ template <>
+ struct int_traits <unsigned int>
+ : public primitive_int_traits <unsigned int, false> {};
+
+#if HOST_BITS_PER_INT != HOST_BITS_PER_WIDE_INT
+ template <>
+ struct int_traits <HOST_WIDE_INT>
+ : public primitive_int_traits <HOST_WIDE_INT, true> {};
+
+ template <>
+ struct int_traits <unsigned HOST_WIDE_INT>
+ : public primitive_int_traits <unsigned HOST_WIDE_INT, false> {};
+#endif
+}
+
+namespace wi
+{
+ /* Stores HWI-sized integer VAL, treating it as having signedness SGN
+ and precision PRECISION. */
+ struct hwi_with_prec
+ {
+ hwi_with_prec (HOST_WIDE_INT, unsigned int, signop);
+ HOST_WIDE_INT val;
+ unsigned int precision;
+ signop sgn;
+ };
+
+ hwi_with_prec shwi (HOST_WIDE_INT, unsigned int);
+ hwi_with_prec uhwi (unsigned HOST_WIDE_INT, unsigned int);
+
+ hwi_with_prec minus_one (unsigned int);
+ hwi_with_prec zero (unsigned int);
+ hwi_with_prec one (unsigned int);
+ hwi_with_prec two (unsigned int);
+}
+
+inline wi::hwi_with_prec::hwi_with_prec (HOST_WIDE_INT v, unsigned int p,
+ signop s)
+ : val (v), precision (p), sgn (s)
+{
+}
+
+/* Return a signed integer that has value VAL and precision PRECISION. */
+inline wi::hwi_with_prec
+wi::shwi (HOST_WIDE_INT val, unsigned int precision)
+{
+ return hwi_with_prec (val, precision, SIGNED);
+}
+
+/* Return an unsigned integer that has value VAL and precision PRECISION. */
+inline wi::hwi_with_prec
+wi::uhwi (unsigned HOST_WIDE_INT val, unsigned int precision)
+{
+ return hwi_with_prec (val, precision, UNSIGNED);
+}
+
+/* Return a wide int of -1 with precision PRECISION. */
+inline wi::hwi_with_prec
+wi::minus_one (unsigned int precision)
+{
+ return wi::shwi (-1, precision);
+}
+
+/* Return a wide int of 0 with precision PRECISION. */
+inline wi::hwi_with_prec
+wi::zero (unsigned int precision)
+{
+ return wi::shwi (0, precision);
+}
+
+/* Return a wide int of 1 with precision PRECISION. */
+inline wi::hwi_with_prec
+wi::one (unsigned int precision)
+{
+ return wi::shwi (1, precision);
+}
+
+/* Return a wide int of 2 with precision PRECISION. */
+inline wi::hwi_with_prec
+wi::two (unsigned int precision)
+{
+ return wi::shwi (2, precision);
+}
+
+namespace wi
+{
+ template <>
+ struct int_traits <wi::hwi_with_prec>
+ {
+ static const enum precision_type precision_type = VAR_PRECISION;
+ /* hwi_with_prec has an explicitly-given precision, rather than the
+ precision of HOST_WIDE_INT. */
+ static const bool host_dependent_precision = false;
+ static const bool is_sign_extended = true;
+ static unsigned int get_precision (const wi::hwi_with_prec &);
+ static wi::storage_ref decompose (HOST_WIDE_INT *, unsigned int,
+ const wi::hwi_with_prec &);
+ };
+}
+
+inline unsigned int
+wi::int_traits <wi::hwi_with_prec>::get_precision (const wi::hwi_with_prec &x)
+{
+ return x.precision;
+}
+
+inline wi::storage_ref
+wi::int_traits <wi::hwi_with_prec>::
+decompose (HOST_WIDE_INT *scratch, unsigned int precision,
+ const wi::hwi_with_prec &x)
+{
+ gcc_checking_assert (precision == x.precision);
+ scratch[0] = x.val;
+ if (x.sgn == SIGNED || x.val >= 0 || precision <= HOST_BITS_PER_WIDE_INT)
+ return wi::storage_ref (scratch, 1, precision);
+ scratch[1] = 0;
+ return wi::storage_ref (scratch, 2, precision);
+}
+
+/* Private functions for handling large cases out of line. They take
+ individual length and array parameters because that is cheaper for
+ the inline caller than constructing an object on the stack and
+ passing a reference to it. (Although many callers use wide_int_refs,
+ we generally want those to be removed by SRA.) */
+namespace wi
+{
+ bool eq_p_large (const HOST_WIDE_INT *, unsigned int,
+ const HOST_WIDE_INT *, unsigned int, unsigned int);
+ bool lts_p_large (const HOST_WIDE_INT *, unsigned int, unsigned int,
+ const HOST_WIDE_INT *, unsigned int);
+ bool ltu_p_large (const HOST_WIDE_INT *, unsigned int, unsigned int,
+ const HOST_WIDE_INT *, unsigned int);
+ int cmps_large (const HOST_WIDE_INT *, unsigned int, unsigned int,
+ const HOST_WIDE_INT *, unsigned int);
+ int cmpu_large (const HOST_WIDE_INT *, unsigned int, unsigned int,
+ const HOST_WIDE_INT *, unsigned int);
+ unsigned int sext_large (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int,
+ unsigned int, unsigned int);
+ unsigned int zext_large (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int,
+ unsigned int, unsigned int);
+ unsigned int set_bit_large (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, unsigned int, unsigned int);
+ unsigned int lshift_large (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, unsigned int, unsigned int);
+ unsigned int lrshift_large (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, unsigned int, unsigned int,
+ unsigned int);
+ unsigned int arshift_large (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, unsigned int, unsigned int,
+ unsigned int);
+ unsigned int and_large (HOST_WIDE_INT *, const HOST_WIDE_INT *, unsigned int,
+ const HOST_WIDE_INT *, unsigned int, unsigned int);
+ unsigned int and_not_large (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, const HOST_WIDE_INT *,
+ unsigned int, unsigned int);
+ unsigned int or_large (HOST_WIDE_INT *, const HOST_WIDE_INT *, unsigned int,
+ const HOST_WIDE_INT *, unsigned int, unsigned int);
+ unsigned int or_not_large (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, const HOST_WIDE_INT *,
+ unsigned int, unsigned int);
+ unsigned int xor_large (HOST_WIDE_INT *, const HOST_WIDE_INT *, unsigned int,
+ const HOST_WIDE_INT *, unsigned int, unsigned int);
+ unsigned int add_large (HOST_WIDE_INT *, const HOST_WIDE_INT *, unsigned int,
+ const HOST_WIDE_INT *, unsigned int, unsigned int,
+ signop, bool *);
+ unsigned int sub_large (HOST_WIDE_INT *, const HOST_WIDE_INT *, unsigned int,
+ const HOST_WIDE_INT *, unsigned int, unsigned int,
+ signop, bool *);
+ unsigned int mul_internal (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, const HOST_WIDE_INT *,
+ unsigned int, unsigned int, signop, bool *,
+ bool);
+ unsigned int divmod_internal (HOST_WIDE_INT *, unsigned int *,
+ HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, unsigned int,
+ const HOST_WIDE_INT *,
+ unsigned int, unsigned int,
+ signop, bool *);
+}
+
+/* Return the number of bits that integer X can hold. */
+template <typename T>
+inline unsigned int
+wi::get_precision (const T &x)
+{
+ return wi::int_traits <T>::get_precision (x);
+}
+
+/* Return the number of bits that the result of a binary operation can
+ hold when the input operands are X and Y. */
+template <typename T1, typename T2>
+inline unsigned int
+wi::get_binary_precision (const T1 &x, const T2 &y)
+{
+ return get_precision (wi::int_traits <WI_BINARY_RESULT (T1, T2)>::
+ get_binary_result (x, y));
+}
+
+/* Copy the contents of Y to X, but keeping X's current precision. */
+template <typename T1, typename T2>
+inline void
+wi::copy (T1 &x, const T2 &y)
+{
+ HOST_WIDE_INT *xval = x.write_val ();
+ const HOST_WIDE_INT *yval = y.get_val ();
+ unsigned int len = y.get_len ();
+ unsigned int i = 0;
+ do
+ xval[i] = yval[i];
+ while (++i < len);
+ x.set_len (len, y.is_sign_extended);
+}
+
+/* Return true if X fits in a HOST_WIDE_INT with no loss of precision. */
+template <typename T>
+inline bool
+wi::fits_shwi_p (const T &x)
+{
+ WIDE_INT_REF_FOR (T) xi (x);
+ return xi.len == 1;
+}
+
+/* Return true if X fits in an unsigned HOST_WIDE_INT with no loss of
+ precision. */
+template <typename T>
+inline bool
+wi::fits_uhwi_p (const T &x)
+{
+ WIDE_INT_REF_FOR (T) xi (x);
+ if (xi.precision <= HOST_BITS_PER_WIDE_INT)
+ return true;
+ if (xi.len == 1)
+ return xi.slow () >= 0;
+ return xi.len == 2 && xi.uhigh () == 0;
+}
+
+/* Return true if X is negative based on the interpretation of SGN.
+ For UNSIGNED, this is always false. */
+template <typename T>
+inline bool
+wi::neg_p (const T &x, signop sgn)
+{
+ WIDE_INT_REF_FOR (T) xi (x);
+ if (sgn == UNSIGNED)
+ return false;
+ return xi.sign_mask () < 0;
+}
+
+/* Return -1 if the top bit of X is set and 0 if the top bit is clear. */
+template <typename T>
+inline HOST_WIDE_INT
+wi::sign_mask (const T &x)
+{
+ WIDE_INT_REF_FOR (T) xi (x);
+ return xi.sign_mask ();
+}
+
+/* Return true if X == Y. X and Y must be binary-compatible. */
+template <typename T1, typename T2>
+inline bool
+wi::eq_p (const T1 &x, const T2 &y)
+{
+ unsigned int precision = get_binary_precision (x, y);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ if (xi.is_sign_extended && yi.is_sign_extended)
+ {
+ /* This case reduces to array equality. */
+ if (xi.len != yi.len)
+ return false;
+ unsigned int i = 0;
+ do
+ if (xi.val[i] != yi.val[i])
+ return false;
+ while (++i != xi.len);
+ return true;
+ }
+ if (__builtin_expect (yi.len == 1, true))
+ {
+ /* XI is only equal to YI if it too has a single HWI. */
+ if (xi.len != 1)
+ return false;
+ /* Excess bits in xi.val[0] will be signs or zeros, so comparisons
+ with 0 are simple. */
+ if (STATIC_CONSTANT_P (yi.val[0] == 0))
+ return xi.val[0] == 0;
+ /* Otherwise flush out any excess bits first. */
+ unsigned HOST_WIDE_INT diff = xi.val[0] ^ yi.val[0];
+ int excess = HOST_BITS_PER_WIDE_INT - precision;
+ if (excess > 0)
+ diff <<= excess;
+ return diff == 0;
+ }
+ return eq_p_large (xi.val, xi.len, yi.val, yi.len, precision);
+}
+
+/* Return true if X != Y. X and Y must be binary-compatible. */
+template <typename T1, typename T2>
+inline bool
+wi::ne_p (const T1 &x, const T2 &y)
+{
+ return !eq_p (x, y);
+}
+
+/* Return true if X < Y when both are treated as signed values. */
+template <typename T1, typename T2>
+inline bool
+wi::lts_p (const T1 &x, const T2 &y)
+{
+ unsigned int precision = get_binary_precision (x, y);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ /* We optimize x < y, where y is 64 or fewer bits. */
+ if (wi::fits_shwi_p (yi))
+ {
+ /* Make lts_p (x, 0) as efficient as wi::neg_p (x). */
+ if (STATIC_CONSTANT_P (yi.val[0] == 0))
+ return neg_p (xi);
+ /* If x fits directly into a shwi, we can compare directly. */
+ if (wi::fits_shwi_p (xi))
+ return xi.to_shwi () < yi.to_shwi ();
+ /* If x doesn't fit and is negative, then it must be more
+ negative than any value in y, and hence smaller than y. */
+ if (neg_p (xi))
+ return true;
+ /* If x is positive, then it must be larger than any value in y,
+ and hence greater than y. */
+ return false;
+ }
+ /* Optimize the opposite case, if it can be detected at compile time. */
+ if (STATIC_CONSTANT_P (xi.len == 1))
+ /* If YI is negative it is lower than the least HWI.
+ If YI is positive it is greater than the greatest HWI. */
+ return !neg_p (yi);
+ return lts_p_large (xi.val, xi.len, precision, yi.val, yi.len);
+}
+
+/* Return true if X < Y when both are treated as unsigned values. */
+template <typename T1, typename T2>
+inline bool
+wi::ltu_p (const T1 &x, const T2 &y)
+{
+ unsigned int precision = get_binary_precision (x, y);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ /* Optimize comparisons with constants. */
+ if (STATIC_CONSTANT_P (yi.len == 1 && yi.val[0] >= 0))
+ return xi.len == 1 && xi.to_uhwi () < (unsigned HOST_WIDE_INT) yi.val[0];
+ if (STATIC_CONSTANT_P (xi.len == 1 && xi.val[0] >= 0))
+ return yi.len != 1 || yi.to_uhwi () > (unsigned HOST_WIDE_INT) xi.val[0];
+ /* Optimize the case of two HWIs. The HWIs are implicitly sign-extended
+ for precisions greater than HOST_BITS_WIDE_INT, but sign-extending both
+ values does not change the result. */
+ if (__builtin_expect (xi.len + yi.len == 2, true))
+ {
+ unsigned HOST_WIDE_INT xl = xi.to_uhwi ();
+ unsigned HOST_WIDE_INT yl = yi.to_uhwi ();
+ return xl < yl;
+ }
+ return ltu_p_large (xi.val, xi.len, precision, yi.val, yi.len);
+}
+
+/* Return true if X < Y. Signedness of X and Y is indicated by SGN. */
+template <typename T1, typename T2>
+inline bool
+wi::lt_p (const T1 &x, const T2 &y, signop sgn)
+{
+ if (sgn == SIGNED)
+ return lts_p (x, y);
+ else
+ return ltu_p (x, y);
+}
+
+/* Return true if X <= Y when both are treated as signed values. */
+template <typename T1, typename T2>
+inline bool
+wi::les_p (const T1 &x, const T2 &y)
+{
+ return !lts_p (y, x);
+}
+
+/* Return true if X <= Y when both are treated as unsigned values. */
+template <typename T1, typename T2>
+inline bool
+wi::leu_p (const T1 &x, const T2 &y)
+{
+ return !ltu_p (y, x);
+}
+
+/* Return true if X <= Y. Signedness of X and Y is indicated by SGN. */
+template <typename T1, typename T2>
+inline bool
+wi::le_p (const T1 &x, const T2 &y, signop sgn)
+{
+ if (sgn == SIGNED)
+ return les_p (x, y);
+ else
+ return leu_p (x, y);
+}
+
+/* Return true if X > Y when both are treated as signed values. */
+template <typename T1, typename T2>
+inline bool
+wi::gts_p (const T1 &x, const T2 &y)
+{
+ return lts_p (y, x);
+}
+
+/* Return true if X > Y when both are treated as unsigned values. */
+template <typename T1, typename T2>
+inline bool
+wi::gtu_p (const T1 &x, const T2 &y)
+{
+ return ltu_p (y, x);
+}
+
+/* Return true if X > Y. Signedness of X and Y is indicated by SGN. */
+template <typename T1, typename T2>
+inline bool
+wi::gt_p (const T1 &x, const T2 &y, signop sgn)
+{
+ if (sgn == SIGNED)
+ return gts_p (x, y);
+ else
+ return gtu_p (x, y);
+}
+
+/* Return true if X >= Y when both are treated as signed values. */
+template <typename T1, typename T2>
+inline bool
+wi::ges_p (const T1 &x, const T2 &y)
+{
+ return !lts_p (x, y);
+}
+
+/* Return true if X >= Y when both are treated as unsigned values. */
+template <typename T1, typename T2>
+inline bool
+wi::geu_p (const T1 &x, const T2 &y)
+{
+ return !ltu_p (x, y);
+}
+
+/* Return true if X >= Y. Signedness of X and Y is indicated by SGN. */
+template <typename T1, typename T2>
+inline bool
+wi::ge_p (const T1 &x, const T2 &y, signop sgn)
+{
+ if (sgn == SIGNED)
+ return ges_p (x, y);
+ else
+ return geu_p (x, y);
+}
+
+/* Return -1 if X < Y, 0 if X == Y and 1 if X > Y. Treat both X and Y
+ as signed values. */
+template <typename T1, typename T2>
+inline int
+wi::cmps (const T1 &x, const T2 &y)
+{
+ unsigned int precision = get_binary_precision (x, y);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ if (wi::fits_shwi_p (yi))
+ {
+ /* Special case for comparisons with 0. */
+ if (STATIC_CONSTANT_P (yi.val[0] == 0))
+ return neg_p (xi) ? -1 : !(xi.len == 1 && xi.val[0] == 0);
+ /* If x fits into a signed HWI, we can compare directly. */
+ if (wi::fits_shwi_p (xi))
+ {
+ HOST_WIDE_INT xl = xi.to_shwi ();
+ HOST_WIDE_INT yl = yi.to_shwi ();
+ return xl < yl ? -1 : xl > yl;
+ }
+ /* If x doesn't fit and is negative, then it must be more
+ negative than any signed HWI, and hence smaller than y. */
+ if (neg_p (xi))
+ return -1;
+ /* If x is positive, then it must be larger than any signed HWI,
+ and hence greater than y. */
+ return 1;
+ }
+ /* Optimize the opposite case, if it can be detected at compile time. */
+ if (STATIC_CONSTANT_P (xi.len == 1))
+ /* If YI is negative it is lower than the least HWI.
+ If YI is positive it is greater than the greatest HWI. */
+ return neg_p (yi) ? 1 : -1;
+ return cmps_large (xi.val, xi.len, precision, yi.val, yi.len);
+}
+
+/* Return -1 if X < Y, 0 if X == Y and 1 if X > Y. Treat both X and Y
+ as unsigned values. */
+template <typename T1, typename T2>
+inline int
+wi::cmpu (const T1 &x, const T2 &y)
+{
+ unsigned int precision = get_binary_precision (x, y);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ /* Optimize comparisons with constants. */
+ if (STATIC_CONSTANT_P (yi.len == 1 && yi.val[0] >= 0))
+ {
+ /* If XI doesn't fit in a HWI then it must be larger than YI. */
+ if (xi.len != 1)
+ return 1;
+ /* Otherwise compare directly. */
+ unsigned HOST_WIDE_INT xl = xi.to_uhwi ();
+ unsigned HOST_WIDE_INT yl = yi.val[0];
+ return xl < yl ? -1 : xl > yl;
+ }
+ if (STATIC_CONSTANT_P (xi.len == 1 && xi.val[0] >= 0))
+ {
+ /* If YI doesn't fit in a HWI then it must be larger than XI. */
+ if (yi.len != 1)
+ return -1;
+ /* Otherwise compare directly. */
+ unsigned HOST_WIDE_INT xl = xi.val[0];
+ unsigned HOST_WIDE_INT yl = yi.to_uhwi ();
+ return xl < yl ? -1 : xl > yl;
+ }
+ /* Optimize the case of two HWIs. The HWIs are implicitly sign-extended
+ for precisions greater than HOST_BITS_WIDE_INT, but sign-extending both
+ values does not change the result. */
+ if (__builtin_expect (xi.len + yi.len == 2, true))
+ {
+ unsigned HOST_WIDE_INT xl = xi.to_uhwi ();
+ unsigned HOST_WIDE_INT yl = yi.to_uhwi ();
+ return xl < yl ? -1 : xl > yl;
+ }
+ return cmpu_large (xi.val, xi.len, precision, yi.val, yi.len);
+}
+
+/* Return -1 if X < Y, 0 if X == Y and 1 if X > Y. Signedness of
+ X and Y indicated by SGN. */
+template <typename T1, typename T2>
+inline int
+wi::cmp (const T1 &x, const T2 &y, signop sgn)
+{
+ if (sgn == SIGNED)
+ return cmps (x, y);
+ else
+ return cmpu (x, y);
+}
+
+/* Return ~x. */
+template <typename T>
+inline WI_UNARY_RESULT (T)
+wi::bit_not (const T &x)
+{
+ WI_UNARY_RESULT_VAR (result, val, T, x);
+ WIDE_INT_REF_FOR (T) xi (x, get_precision (result));
+ for (unsigned int i = 0; i < xi.len; ++i)
+ val[i] = ~xi.val[i];
+ result.set_len (xi.len);
+ return result;
+}
+
+/* Return -x. */
+template <typename T>
+inline WI_UNARY_RESULT (T)
+wi::neg (const T &x)
+{
+ return sub (0, x);
+}
+
+/* Return -x. Indicate in *OVERFLOW if X is the minimum signed value. */
+template <typename T>
+inline WI_UNARY_RESULT (T)
+wi::neg (const T &x, bool *overflow)
+{
+ *overflow = only_sign_bit_p (x);
+ return sub (0, x);
+}
+
+/* Return the absolute value of x. */
+template <typename T>
+inline WI_UNARY_RESULT (T)
+wi::abs (const T &x)
+{
+ return neg_p (x) ? neg (x) : WI_UNARY_RESULT (T) (x);
+}
+
+/* Return the result of sign-extending the low OFFSET bits of X. */
+template <typename T>
+inline WI_UNARY_RESULT (T)
+wi::sext (const T &x, unsigned int offset)
+{
+ WI_UNARY_RESULT_VAR (result, val, T, x);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T) xi (x, precision);
+
+ if (offset <= HOST_BITS_PER_WIDE_INT)
+ {
+ val[0] = sext_hwi (xi.ulow (), offset);
+ result.set_len (1, true);
+ }
+ else
+ result.set_len (sext_large (val, xi.val, xi.len, precision, offset));
+ return result;
+}
+
+/* Return the result of zero-extending the low OFFSET bits of X. */
+template <typename T>
+inline WI_UNARY_RESULT (T)
+wi::zext (const T &x, unsigned int offset)
+{
+ WI_UNARY_RESULT_VAR (result, val, T, x);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T) xi (x, precision);
+
+ /* This is not just an optimization, it is actually required to
+ maintain canonization. */
+ if (offset >= precision)
+ {
+ wi::copy (result, xi);
+ return result;
+ }
+
+ /* In these cases we know that at least the top bit will be clear,
+ so no sign extension is necessary. */
+ if (offset < HOST_BITS_PER_WIDE_INT)
+ {
+ val[0] = zext_hwi (xi.ulow (), offset);
+ result.set_len (1, true);
+ }
+ else
+ result.set_len (zext_large (val, xi.val, xi.len, precision, offset), true);
+ return result;
+}
+
+/* Return the result of extending the low OFFSET bits of X according to
+ signedness SGN. */
+template <typename T>
+inline WI_UNARY_RESULT (T)
+wi::ext (const T &x, unsigned int offset, signop sgn)
+{
+ return sgn == SIGNED ? sext (x, offset) : zext (x, offset);
+}
+
+/* Return an integer that represents X | (1 << bit). */
+template <typename T>
+inline WI_UNARY_RESULT (T)
+wi::set_bit (const T &x, unsigned int bit)
+{
+ WI_UNARY_RESULT_VAR (result, val, T, x);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T) xi (x, precision);
+ if (precision <= HOST_BITS_PER_WIDE_INT)
+ {
+ val[0] = xi.ulow () | ((unsigned HOST_WIDE_INT) 1 << bit);
+ result.set_len (1);
+ }
+ else
+ result.set_len (set_bit_large (val, xi.val, xi.len, precision, bit));
+ return result;
+}
+
+/* Return the mininum of X and Y, treating them both as having
+ signedness SGN. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::min (const T1 &x, const T2 &y, signop sgn)
+{
+ WI_BINARY_RESULT_VAR (result, val ATTRIBUTE_UNUSED, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ if (wi::le_p (x, y, sgn))
+ wi::copy (result, WIDE_INT_REF_FOR (T1) (x, precision));
+ else
+ wi::copy (result, WIDE_INT_REF_FOR (T2) (y, precision));
+ return result;
+}
+
+/* Return the minimum of X and Y, treating both as signed values. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::smin (const T1 &x, const T2 &y)
+{
+ return min (x, y, SIGNED);
+}
+
+/* Return the minimum of X and Y, treating both as unsigned values. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::umin (const T1 &x, const T2 &y)
+{
+ return min (x, y, UNSIGNED);
+}
+
+/* Return the maxinum of X and Y, treating them both as having
+ signedness SGN. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::max (const T1 &x, const T2 &y, signop sgn)
+{
+ WI_BINARY_RESULT_VAR (result, val ATTRIBUTE_UNUSED, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ if (wi::ge_p (x, y, sgn))
+ wi::copy (result, WIDE_INT_REF_FOR (T1) (x, precision));
+ else
+ wi::copy (result, WIDE_INT_REF_FOR (T2) (y, precision));
+ return result;
+}
+
+/* Return the maximum of X and Y, treating both as signed values. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::smax (const T1 &x, const T2 &y)
+{
+ return max (x, y, SIGNED);
+}
+
+/* Return the maximum of X and Y, treating both as unsigned values. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::umax (const T1 &x, const T2 &y)
+{
+ return max (x, y, UNSIGNED);
+}
+
+/* Return X & Y. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::bit_and (const T1 &x, const T2 &y)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ bool is_sign_extended = xi.is_sign_extended && yi.is_sign_extended;
+ if (__builtin_expect (xi.len + yi.len == 2, true))
+ {
+ val[0] = xi.ulow () & yi.ulow ();
+ result.set_len (1, is_sign_extended);
+ }
+ else
+ result.set_len (and_large (val, xi.val, xi.len, yi.val, yi.len,
+ precision), is_sign_extended);
+ return result;
+}
+
+/* Return X & ~Y. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::bit_and_not (const T1 &x, const T2 &y)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ bool is_sign_extended = xi.is_sign_extended && yi.is_sign_extended;
+ if (__builtin_expect (xi.len + yi.len == 2, true))
+ {
+ val[0] = xi.ulow () & ~yi.ulow ();
+ result.set_len (1, is_sign_extended);
+ }
+ else
+ result.set_len (and_not_large (val, xi.val, xi.len, yi.val, yi.len,
+ precision), is_sign_extended);
+ return result;
+}
+
+/* Return X | Y. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::bit_or (const T1 &x, const T2 &y)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ bool is_sign_extended = xi.is_sign_extended && yi.is_sign_extended;
+ if (__builtin_expect (xi.len + yi.len == 2, true))
+ {
+ val[0] = xi.ulow () | yi.ulow ();
+ result.set_len (1, is_sign_extended);
+ }
+ else
+ result.set_len (or_large (val, xi.val, xi.len,
+ yi.val, yi.len, precision), is_sign_extended);
+ return result;
+}
+
+/* Return X | ~Y. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::bit_or_not (const T1 &x, const T2 &y)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ bool is_sign_extended = xi.is_sign_extended && yi.is_sign_extended;
+ if (__builtin_expect (xi.len + yi.len == 2, true))
+ {
+ val[0] = xi.ulow () | ~yi.ulow ();
+ result.set_len (1, is_sign_extended);
+ }
+ else
+ result.set_len (or_not_large (val, xi.val, xi.len, yi.val, yi.len,
+ precision), is_sign_extended);
+ return result;
+}
+
+/* Return X ^ Y. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::bit_xor (const T1 &x, const T2 &y)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ bool is_sign_extended = xi.is_sign_extended && yi.is_sign_extended;
+ if (__builtin_expect (xi.len + yi.len == 2, true))
+ {
+ val[0] = xi.ulow () ^ yi.ulow ();
+ result.set_len (1, is_sign_extended);
+ }
+ else
+ result.set_len (xor_large (val, xi.val, xi.len,
+ yi.val, yi.len, precision), is_sign_extended);
+ return result;
+}
+
+/* Return X + Y. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::add (const T1 &x, const T2 &y)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ if (precision <= HOST_BITS_PER_WIDE_INT)
+ {
+ val[0] = xi.ulow () + yi.ulow ();
+ result.set_len (1);
+ }
+ /* If the precision is known at compile time to be greater than
+ HOST_BITS_PER_WIDE_INT, we can optimize the single-HWI case
+ knowing that (a) all bits in those HWIs are significant and
+ (b) the result has room for at least two HWIs. This provides
+ a fast path for things like offset_int and widest_int.
+
+ The STATIC_CONSTANT_P test prevents this path from being
+ used for wide_ints. wide_ints with precisions greater than
+ HOST_BITS_PER_WIDE_INT are relatively rare and there's not much
+ point handling them inline. */
+ else if (STATIC_CONSTANT_P (precision > HOST_BITS_PER_WIDE_INT)
+ && __builtin_expect (xi.len + yi.len == 2, true))
+ {
+ unsigned HOST_WIDE_INT xl = xi.ulow ();
+ unsigned HOST_WIDE_INT yl = yi.ulow ();
+ unsigned HOST_WIDE_INT resultl = xl + yl;
+ val[0] = resultl;
+ val[1] = (HOST_WIDE_INT) resultl < 0 ? 0 : -1;
+ result.set_len (1 + (((resultl ^ xl) & (resultl ^ yl))
+ >> (HOST_BITS_PER_WIDE_INT - 1)));
+ }
+ else
+ result.set_len (add_large (val, xi.val, xi.len,
+ yi.val, yi.len, precision,
+ UNSIGNED, 0));
+ return result;
+}
+
+/* Return X + Y. Treat X and Y as having the signednes given by SGN
+ and indicate in *OVERFLOW whether the operation overflowed. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::add (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ if (precision <= HOST_BITS_PER_WIDE_INT)
+ {
+ unsigned HOST_WIDE_INT xl = xi.ulow ();
+ unsigned HOST_WIDE_INT yl = yi.ulow ();
+ unsigned HOST_WIDE_INT resultl = xl + yl;
+ if (sgn == SIGNED)
+ *overflow = (((resultl ^ xl) & (resultl ^ yl))
+ >> (precision - 1)) & 1;
+ else
+ *overflow = ((resultl << (HOST_BITS_PER_WIDE_INT - precision))
+ < (xl << (HOST_BITS_PER_WIDE_INT - precision)));
+ val[0] = resultl;
+ result.set_len (1);
+ }
+ else
+ result.set_len (add_large (val, xi.val, xi.len,
+ yi.val, yi.len, precision,
+ sgn, overflow));
+ return result;
+}
+
+/* Return X - Y. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::sub (const T1 &x, const T2 &y)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ if (precision <= HOST_BITS_PER_WIDE_INT)
+ {
+ val[0] = xi.ulow () - yi.ulow ();
+ result.set_len (1);
+ }
+ /* If the precision is known at compile time to be greater than
+ HOST_BITS_PER_WIDE_INT, we can optimize the single-HWI case
+ knowing that (a) all bits in those HWIs are significant and
+ (b) the result has room for at least two HWIs. This provides
+ a fast path for things like offset_int and widest_int.
+
+ The STATIC_CONSTANT_P test prevents this path from being
+ used for wide_ints. wide_ints with precisions greater than
+ HOST_BITS_PER_WIDE_INT are relatively rare and there's not much
+ point handling them inline. */
+ else if (STATIC_CONSTANT_P (precision > HOST_BITS_PER_WIDE_INT)
+ && __builtin_expect (xi.len + yi.len == 2, true))
+ {
+ unsigned HOST_WIDE_INT xl = xi.ulow ();
+ unsigned HOST_WIDE_INT yl = yi.ulow ();
+ unsigned HOST_WIDE_INT resultl = xl - yl;
+ val[0] = resultl;
+ val[1] = (HOST_WIDE_INT) resultl < 0 ? 0 : -1;
+ result.set_len (1 + (((resultl ^ xl) & (xl ^ yl))
+ >> (HOST_BITS_PER_WIDE_INT - 1)));
+ }
+ else
+ result.set_len (sub_large (val, xi.val, xi.len,
+ yi.val, yi.len, precision,
+ UNSIGNED, 0));
+ return result;
+}
+
+/* Return X - Y. Treat X and Y as having the signednes given by SGN
+ and indicate in *OVERFLOW whether the operation overflowed. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::sub (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ if (precision <= HOST_BITS_PER_WIDE_INT)
+ {
+ unsigned HOST_WIDE_INT xl = xi.ulow ();
+ unsigned HOST_WIDE_INT yl = yi.ulow ();
+ unsigned HOST_WIDE_INT resultl = xl - yl;
+ if (sgn == SIGNED)
+ *overflow = (((xl ^ yl) & (resultl ^ xl)) >> (precision - 1)) & 1;
+ else
+ *overflow = ((resultl << (HOST_BITS_PER_WIDE_INT - precision))
+ > (xl << (HOST_BITS_PER_WIDE_INT - precision)));
+ val[0] = resultl;
+ result.set_len (1);
+ }
+ else
+ result.set_len (sub_large (val, xi.val, xi.len,
+ yi.val, yi.len, precision,
+ sgn, overflow));
+ return result;
+}
+
+/* Return X * Y. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::mul (const T1 &x, const T2 &y)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ if (precision <= HOST_BITS_PER_WIDE_INT)
+ {
+ val[0] = xi.ulow () * yi.ulow ();
+ result.set_len (1);
+ }
+ else
+ result.set_len (mul_internal (val, xi.val, xi.len, yi.val, yi.len,
+ precision, UNSIGNED, 0, false));
+ return result;
+}
+
+/* Return X * Y. Treat X and Y as having the signednes given by SGN
+ and indicate in *OVERFLOW whether the operation overflowed. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::mul (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ result.set_len (mul_internal (val, xi.val, xi.len,
+ yi.val, yi.len, precision,
+ sgn, overflow, false));
+ return result;
+}
+
+/* Return X * Y, treating both X and Y as signed values. Indicate in
+ *OVERFLOW whether the operation overflowed. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::smul (const T1 &x, const T2 &y, bool *overflow)
+{
+ return mul (x, y, SIGNED, overflow);
+}
+
+/* Return X * Y, treating both X and Y as unsigned values. Indicate in
+ *OVERFLOW whether the operation overflowed. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::umul (const T1 &x, const T2 &y, bool *overflow)
+{
+ return mul (x, y, UNSIGNED, overflow);
+}
+
+/* Perform a widening multiplication of X and Y, extending the values
+ according to SGN, and return the high part of the result. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::mul_high (const T1 &x, const T2 &y, signop sgn)
+{
+ WI_BINARY_RESULT_VAR (result, val, T1, x, T2, y);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y, precision);
+ result.set_len (mul_internal (val, xi.val, xi.len,
+ yi.val, yi.len, precision,
+ sgn, 0, true));
+ return result;
+}
+
+/* Return X / Y, rouding towards 0. Treat X and Y as having the
+ signedness given by SGN. Indicate in *OVERFLOW if the result
+ overflows. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::div_trunc (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (quotient, quotient_val, T1, x, T2, y);
+ unsigned int precision = get_precision (quotient);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y);
+
+ quotient.set_len (divmod_internal (quotient_val, 0, 0, xi.val, xi.len,
+ precision,
+ yi.val, yi.len, yi.precision,
+ sgn, overflow));
+ return quotient;
+}
+
+/* Return X / Y, rouding towards 0. Treat X and Y as signed values. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::sdiv_trunc (const T1 &x, const T2 &y)
+{
+ return div_trunc (x, y, SIGNED);
+}
+
+/* Return X / Y, rouding towards 0. Treat X and Y as unsigned values. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::udiv_trunc (const T1 &x, const T2 &y)
+{
+ return div_trunc (x, y, UNSIGNED);
+}
+
+/* Return X / Y, rouding towards -inf. Treat X and Y as having the
+ signedness given by SGN. Indicate in *OVERFLOW if the result
+ overflows. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::div_floor (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (quotient, quotient_val, T1, x, T2, y);
+ WI_BINARY_RESULT_VAR (remainder, remainder_val, T1, x, T2, y);
+ unsigned int precision = get_precision (quotient);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y);
+
+ unsigned int remainder_len;
+ quotient.set_len (divmod_internal (quotient_val,
+ &remainder_len, remainder_val,
+ xi.val, xi.len, precision,
+ yi.val, yi.len, yi.precision, sgn,
+ overflow));
+ remainder.set_len (remainder_len);
+ if (wi::neg_p (x, sgn) != wi::neg_p (y, sgn) && remainder != 0)
+ return quotient - 1;
+ return quotient;
+}
+
+/* Return X / Y, rouding towards -inf. Treat X and Y as signed values. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::sdiv_floor (const T1 &x, const T2 &y)
+{
+ return div_floor (x, y, SIGNED);
+}
+
+/* Return X / Y, rouding towards -inf. Treat X and Y as unsigned values. */
+/* ??? Why do we have both this and udiv_trunc. Aren't they the same? */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::udiv_floor (const T1 &x, const T2 &y)
+{
+ return div_floor (x, y, UNSIGNED);
+}
+
+/* Return X / Y, rouding towards +inf. Treat X and Y as having the
+ signedness given by SGN. Indicate in *OVERFLOW if the result
+ overflows. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::div_ceil (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (quotient, quotient_val, T1, x, T2, y);
+ WI_BINARY_RESULT_VAR (remainder, remainder_val, T1, x, T2, y);
+ unsigned int precision = get_precision (quotient);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y);
+
+ unsigned int remainder_len;
+ quotient.set_len (divmod_internal (quotient_val,
+ &remainder_len, remainder_val,
+ xi.val, xi.len, precision,
+ yi.val, yi.len, yi.precision, sgn,
+ overflow));
+ remainder.set_len (remainder_len);
+ if (wi::neg_p (x, sgn) == wi::neg_p (y, sgn) && remainder != 0)
+ return quotient + 1;
+ return quotient;
+}
+
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::udiv_ceil (const T1 &x, const T2 &y)
+{
+ return div_ceil (x, y, UNSIGNED);
+}
+
+/* Return X / Y, rouding towards nearest with ties away from zero.
+ Treat X and Y as having the signedness given by SGN. Indicate
+ in *OVERFLOW if the result overflows. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::div_round (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (quotient, quotient_val, T1, x, T2, y);
+ WI_BINARY_RESULT_VAR (remainder, remainder_val, T1, x, T2, y);
+ unsigned int precision = get_precision (quotient);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y);
+
+ unsigned int remainder_len;
+ quotient.set_len (divmod_internal (quotient_val,
+ &remainder_len, remainder_val,
+ xi.val, xi.len, precision,
+ yi.val, yi.len, yi.precision, sgn,
+ overflow));
+ remainder.set_len (remainder_len);
+
+ if (remainder != 0)
+ {
+ if (sgn == SIGNED)
+ {
+ if (wi::ges_p (wi::abs (remainder),
+ wi::lrshift (wi::abs (y), 1)))
+ {
+ if (wi::neg_p (x, sgn) != wi::neg_p (y, sgn))
+ return quotient - 1;
+ else
+ return quotient + 1;
+ }
+ }
+ else
+ {
+ if (wi::geu_p (remainder, wi::lrshift (y, 1)))
+ return quotient + 1;
+ }
+ }
+ return quotient;
+}
+
+/* Return X / Y, rouding towards 0. Treat X and Y as having the
+ signedness given by SGN. Store the remainder in *REMAINDER_PTR. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::divmod_trunc (const T1 &x, const T2 &y, signop sgn,
+ WI_BINARY_RESULT (T1, T2) *remainder_ptr)
+{
+ WI_BINARY_RESULT_VAR (quotient, quotient_val, T1, x, T2, y);
+ WI_BINARY_RESULT_VAR (remainder, remainder_val, T1, x, T2, y);
+ unsigned int precision = get_precision (quotient);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y);
+
+ unsigned int remainder_len;
+ quotient.set_len (divmod_internal (quotient_val,
+ &remainder_len, remainder_val,
+ xi.val, xi.len, precision,
+ yi.val, yi.len, yi.precision, sgn, 0));
+ remainder.set_len (remainder_len);
+
+ *remainder_ptr = remainder;
+ return quotient;
+}
+
+/* Compute X / Y, rouding towards 0, and return the remainder.
+ Treat X and Y as having the signedness given by SGN. Indicate
+ in *OVERFLOW if the division overflows. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::mod_trunc (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (remainder, remainder_val, T1, x, T2, y);
+ unsigned int precision = get_precision (remainder);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y);
+
+ unsigned int remainder_len;
+ divmod_internal (0, &remainder_len, remainder_val,
+ xi.val, xi.len, precision,
+ yi.val, yi.len, yi.precision, sgn, overflow);
+ remainder.set_len (remainder_len);
+
+ return remainder;
+}
+
+/* Compute X / Y, rouding towards 0, and return the remainder.
+ Treat X and Y as signed values. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::smod_trunc (const T1 &x, const T2 &y)
+{
+ return mod_trunc (x, y, SIGNED);
+}
+
+/* Compute X / Y, rouding towards 0, and return the remainder.
+ Treat X and Y as unsigned values. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::umod_trunc (const T1 &x, const T2 &y)
+{
+ return mod_trunc (x, y, UNSIGNED);
+}
+
+/* Compute X / Y, rouding towards -inf, and return the remainder.
+ Treat X and Y as having the signedness given by SGN. Indicate
+ in *OVERFLOW if the division overflows. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::mod_floor (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (quotient, quotient_val, T1, x, T2, y);
+ WI_BINARY_RESULT_VAR (remainder, remainder_val, T1, x, T2, y);
+ unsigned int precision = get_precision (quotient);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y);
+
+ unsigned int remainder_len;
+ quotient.set_len (divmod_internal (quotient_val,
+ &remainder_len, remainder_val,
+ xi.val, xi.len, precision,
+ yi.val, yi.len, yi.precision, sgn,
+ overflow));
+ remainder.set_len (remainder_len);
+
+ if (wi::neg_p (x, sgn) != wi::neg_p (y, sgn) && remainder != 0)
+ return remainder + y;
+ return remainder;
+}
+
+/* Compute X / Y, rouding towards -inf, and return the remainder.
+ Treat X and Y as unsigned values. */
+/* ??? Why do we have both this and umod_trunc. Aren't they the same? */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::umod_floor (const T1 &x, const T2 &y)
+{
+ return mod_floor (x, y, UNSIGNED);
+}
+
+/* Compute X / Y, rouding towards +inf, and return the remainder.
+ Treat X and Y as having the signedness given by SGN. Indicate
+ in *OVERFLOW if the division overflows. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::mod_ceil (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (quotient, quotient_val, T1, x, T2, y);
+ WI_BINARY_RESULT_VAR (remainder, remainder_val, T1, x, T2, y);
+ unsigned int precision = get_precision (quotient);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y);
+
+ unsigned int remainder_len;
+ quotient.set_len (divmod_internal (quotient_val,
+ &remainder_len, remainder_val,
+ xi.val, xi.len, precision,
+ yi.val, yi.len, yi.precision, sgn,
+ overflow));
+ remainder.set_len (remainder_len);
+
+ if (wi::neg_p (x, sgn) == wi::neg_p (y, sgn) && remainder != 0)
+ return remainder - y;
+ return remainder;
+}
+
+/* Compute X / Y, rouding towards nearest with ties away from zero,
+ and return the remainder. Treat X and Y as having the signedness
+ given by SGN. Indicate in *OVERFLOW if the division overflows. */
+template <typename T1, typename T2>
+inline WI_BINARY_RESULT (T1, T2)
+wi::mod_round (const T1 &x, const T2 &y, signop sgn, bool *overflow)
+{
+ WI_BINARY_RESULT_VAR (quotient, quotient_val, T1, x, T2, y);
+ WI_BINARY_RESULT_VAR (remainder, remainder_val, T1, x, T2, y);
+ unsigned int precision = get_precision (quotient);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y);
+
+ unsigned int remainder_len;
+ quotient.set_len (divmod_internal (quotient_val,
+ &remainder_len, remainder_val,
+ xi.val, xi.len, precision,
+ yi.val, yi.len, yi.precision, sgn,
+ overflow));
+ remainder.set_len (remainder_len);
+
+ if (remainder != 0)
+ {
+ if (sgn == SIGNED)
+ {
+ if (wi::ges_p (wi::abs (remainder),
+ wi::lrshift (wi::abs (y), 1)))
+ {
+ if (wi::neg_p (x, sgn) != wi::neg_p (y, sgn))
+ return remainder + y;
+ else
+ return remainder - y;
+ }
+ }
+ else
+ {
+ if (wi::geu_p (remainder, wi::lrshift (y, 1)))
+ return remainder - y;
+ }
+ }
+ return remainder;
+}
+
+/* Return true if X is a multiple of Y, storing X / Y in *RES if so.
+ Treat X and Y as having the signedness given by SGN. */
+template <typename T1, typename T2>
+inline bool
+wi::multiple_of_p (const T1 &x, const T2 &y, signop sgn,
+ WI_BINARY_RESULT (T1, T2) *res)
+{
+ WI_BINARY_RESULT (T1, T2) remainder;
+ WI_BINARY_RESULT (T1, T2) quotient
+ = divmod_trunc (x, y, sgn, &remainder);
+ if (remainder == 0)
+ {
+ *res = quotient;
+ return true;
+ }
+ return false;
+}
+
+/* Return X << Y. Return 0 if Y is greater than or equal to
+ the precision of X. */
+template <typename T1, typename T2>
+inline WI_UNARY_RESULT (T1)
+wi::lshift (const T1 &x, const T2 &y)
+{
+ WI_UNARY_RESULT_VAR (result, val, T1, x);
+ unsigned int precision = get_precision (result);
+ WIDE_INT_REF_FOR (T1) xi (x, precision);
+ WIDE_INT_REF_FOR (T2) yi (y);
+ /* Handle the simple cases quickly. */
+ if (geu_p (yi, precision))
+ {
+ val[0] = 0;
+ result.set_len (1);
+ }
+ else
+ {
+ unsigned int shift = yi.to_uhwi ();
+ /* For fixed-precision integers like offset_int and widest_int,
+ handle the case where the shift value is constant and the
+ result is a single nonnegative HWI (meaning that we don't
+ need to worry about val[1]). This is particularly common
+ for converting a byte count to a bit count.
+
+ For variable-precision integers like wide_int, handle HWI
+ and sub-HWI integers inline. */
+ if (STATIC_CONSTANT_P (xi.precision > HOST_BITS_PER_WIDE_INT)
+ ? (STATIC_CONSTANT_P (shift < HOST_BITS_PER_WIDE_INT - 1)
+ && xi.len == 1
+ && xi.val[0] <= (HOST_WIDE_INT) ((unsigned HOST_WIDE_INT)
+ HOST_WIDE_INT_MAX >> shift))
+ : precision <= HOST_BITS_PER_WIDE_INT)
+ {
+ val[0] = xi.ulow () << shift;
+ result.set_len (1);
+ }
+ else
+ result.set_len (lshift_large (val, xi.val, xi.len,
+ precision, shift));
+ }
+ return result;
+}
+
+/* Return X >> Y, using a logical shift. Return 0 if Y is greater than
+ or equal to the precision of X. */
+template <typename T1, typename T2>
+inline WI_UNARY_RESULT (T1)
+wi::lrshift (const T1 &x, const T2 &y)
+{
+ WI_UNARY_RESULT_VAR (result, val, T1, x);
+ /* Do things in the precision of the input rather than the output,
+ since the result can be no larger than that. */
+ WIDE_INT_REF_FOR (T1) xi (x);
+ WIDE_INT_REF_FOR (T2) yi (y);
+ /* Handle the simple cases quickly. */
+ if (geu_p (yi, xi.precision))
+ {
+ val[0] = 0;
+ result.set_len (1);
+ }
+ else
+ {
+ unsigned int shift = yi.to_uhwi ();
+ /* For fixed-precision integers like offset_int and widest_int,
+ handle the case where the shift value is constant and the
+ shifted value is a single nonnegative HWI (meaning that all
+ bits above the HWI are zero). This is particularly common
+ for converting a bit count to a byte count.
+
+ For variable-precision integers like wide_int, handle HWI
+ and sub-HWI integers inline. */
+ if (STATIC_CONSTANT_P (xi.precision > HOST_BITS_PER_WIDE_INT)
+ ? xi.len == 1 && xi.val[0] >= 0
+ : xi.precision <= HOST_BITS_PER_WIDE_INT)
+ {
+ val[0] = xi.to_uhwi () >> shift;
+ result.set_len (1);
+ }
+ else
+ result.set_len (lrshift_large (val, xi.val, xi.len, xi.precision,
+ get_precision (result), shift));
+ }
+ return result;
+}
+
+/* Return X >> Y, using an arithmetic shift. Return a sign mask if
+ Y is greater than or equal to the precision of X. */
+template <typename T1, typename T2>
+inline WI_UNARY_RESULT (T1)
+wi::arshift (const T1 &x, const T2 &y)
+{
+ WI_UNARY_RESULT_VAR (result, val, T1, x);
+ /* Do things in the precision of the input rather than the output,
+ since the result can be no larger than that. */
+ WIDE_INT_REF_FOR (T1) xi (x);
+ WIDE_INT_REF_FOR (T2) yi (y);
+ /* Handle the simple cases quickly. */
+ if (geu_p (yi, xi.precision))
+ {
+ val[0] = sign_mask (x);
+ result.set_len (1);
+ }
+ else
+ {
+ unsigned int shift = yi.to_uhwi ();
+ if (xi.precision <= HOST_BITS_PER_WIDE_INT)
+ {
+ val[0] = sext_hwi (xi.ulow () >> shift, xi.precision - shift);
+ result.set_len (1, true);
+ }
+ else
+ result.set_len (arshift_large (val, xi.val, xi.len, xi.precision,
+ get_precision (result), shift));
+ }
+ return result;
+}
+
+/* Return X >> Y, using an arithmetic shift if SGN is SIGNED and a
+ logical shift otherwise. If BITSIZE is nonzero, only use the low
+ BITSIZE bits of Y. */
+template <typename T1, typename T2>
+inline WI_UNARY_RESULT (T1)
+wi::rshift (const T1 &x, const T2 &y, signop sgn)
+{
+ if (sgn == UNSIGNED)
+ return lrshift (x, y);
+ else
+ return arshift (x, y);
+}
+
+/* Return the result of rotating the low WIDTH bits of X left by Y
+ bits and zero-extending the result. Use a full-width rotate if
+ WIDTH is zero. */
+template <typename T1, typename T2>
+WI_UNARY_RESULT (T1)
+wi::lrotate (const T1 &x, const T2 &y, unsigned int width)
+{
+ unsigned int precision = get_binary_precision (x, x);
+ if (width == 0)
+ width = precision;
+ WI_UNARY_RESULT (T2) ymod = umod_trunc (y, width);
+ WI_UNARY_RESULT (T1) left = wi::lshift (x, ymod);
+ WI_UNARY_RESULT (T1) right = wi::lrshift (x, wi::sub (width, ymod));
+ if (width != precision)
+ return wi::zext (left, width) | wi::zext (right, width);
+ return left | right;
+}
+
+/* Return the result of rotating the low WIDTH bits of X right by Y
+ bits and zero-extending the result. Use a full-width rotate if
+ WIDTH is zero. */
+template <typename T1, typename T2>
+WI_UNARY_RESULT (T1)
+wi::rrotate (const T1 &x, const T2 &y, unsigned int width)
+{
+ unsigned int precision = get_binary_precision (x, x);
+ if (width == 0)
+ width = precision;
+ WI_UNARY_RESULT (T2) ymod = umod_trunc (y, width);
+ WI_UNARY_RESULT (T1) right = wi::lrshift (x, ymod);
+ WI_UNARY_RESULT (T1) left = wi::lshift (x, wi::sub (width, ymod));
+ if (width != precision)
+ return wi::zext (left, width) | wi::zext (right, width);
+ return left | right;
+}
+
+/* Return 0 if the number of 1s in X is even and 1 if the number of 1s
+ is odd. */
+inline int
+wi::parity (const wide_int_ref &x)
+{
+ return popcount (x) & 1;
+}
+
+/* Extract WIDTH bits from X, starting at BITPOS. */
+template <typename T>
+inline unsigned HOST_WIDE_INT
+wi::extract_uhwi (const T &x, unsigned int bitpos,
+ unsigned int width)
+{
+ unsigned precision = get_precision (x);
+ if (precision < bitpos + width)
+ precision = bitpos + width;
+ WIDE_INT_REF_FOR (T) xi (x, precision);
+
+ /* Handle this rare case after the above, so that we assert about
+ bogus BITPOS values. */
+ if (width == 0)
+ return 0;
+
+ unsigned int start = bitpos / HOST_BITS_PER_WIDE_INT;
+ unsigned int shift = bitpos % HOST_BITS_PER_WIDE_INT;
+ unsigned HOST_WIDE_INT res = xi.elt (start);
+ res >>= shift;
+ if (shift + width > HOST_BITS_PER_WIDE_INT)
+ {
+ unsigned HOST_WIDE_INT upper = xi.elt (start + 1);
+ res |= upper << (-shift % HOST_BITS_PER_WIDE_INT);
+ }
+ return zext_hwi (res, width);
+}
+
+/* Return the minimum precision needed to store X with sign SGN. */
+template <typename T>
+inline unsigned int
+wi::min_precision (const T &x, signop sgn)
+{
+ if (sgn == SIGNED)
+ return get_precision (x) - clrsb (x);
+ else
+ return get_precision (x) - clz (x);
+}
+
+template<typename T>
+void
+gt_ggc_mx (generic_wide_int <T> *)
+{
+}
+
+template<typename T>
+void
+gt_pch_nx (generic_wide_int <T> *)
+{
+}
+
+template<typename T>
+void
+gt_pch_nx (generic_wide_int <T> *, void (*) (void *, void *), void *)
+{
+}
+
+template<int N>
+void
+gt_ggc_mx (trailing_wide_ints <N> *)
+{
+}
+
+template<int N>
+void
+gt_pch_nx (trailing_wide_ints <N> *)
+{
+}
+
+template<int N>
+void
+gt_pch_nx (trailing_wide_ints <N> *, void (*) (void *, void *), void *)
+{
+}
+
+namespace wi
+{
+ /* Used for overloaded functions in which the only other acceptable
+ scalar type is a pointer. It stops a plain 0 from being treated
+ as a null pointer. */
+ struct never_used1 {};
+ struct never_used2 {};
+
+ wide_int min_value (unsigned int, signop);
+ wide_int min_value (never_used1 *);
+ wide_int min_value (never_used2 *);
+ wide_int max_value (unsigned int, signop);
+ wide_int max_value (never_used1 *);
+ wide_int max_value (never_used2 *);
+
+ /* FIXME: this is target dependent, so should be elsewhere.
+ It also seems to assume that CHAR_BIT == BITS_PER_UNIT. */
+ wide_int from_buffer (const unsigned char *, unsigned int);
+
+#ifndef GENERATOR_FILE
+ void to_mpz (wide_int, mpz_t, signop);
+#endif
+
+ wide_int mask (unsigned int, bool, unsigned int);
+ wide_int shifted_mask (unsigned int, unsigned int, bool, unsigned int);
+ wide_int set_bit_in_zero (unsigned int, unsigned int);
+ wide_int insert (const wide_int &x, const wide_int &y, unsigned int,
+ unsigned int);
+
+ template <typename T>
+ T mask (unsigned int, bool);
+
+ template <typename T>
+ T shifted_mask (unsigned int, unsigned int, bool);
+
+ template <typename T>
+ T set_bit_in_zero (unsigned int);
+
+ unsigned int mask (HOST_WIDE_INT *, unsigned int, bool, unsigned int);
+ unsigned int shifted_mask (HOST_WIDE_INT *, unsigned int, unsigned int,
+ bool, unsigned int);
+ unsigned int from_array (HOST_WIDE_INT *, const HOST_WIDE_INT *,
+ unsigned int, unsigned int, bool);
+}
+
+/* Return a PRECISION-bit integer in which the low WIDTH bits are set
+ and the other bits are clear, or the inverse if NEGATE_P. */
+inline wide_int
+wi::mask (unsigned int width, bool negate_p, unsigned int precision)
+{
+ wide_int result = wide_int::create (precision);
+ result.set_len (mask (result.write_val (), width, negate_p, precision));
+ return result;
+}
+
+/* Return a PRECISION-bit integer in which the low START bits are clear,
+ the next WIDTH bits are set, and the other bits are clear,
+ or the inverse if NEGATE_P. */
+inline wide_int
+wi::shifted_mask (unsigned int start, unsigned int width, bool negate_p,
+ unsigned int precision)
+{
+ wide_int result = wide_int::create (precision);
+ result.set_len (shifted_mask (result.write_val (), start, width, negate_p,
+ precision));
+ return result;
+}
+
+/* Return a PRECISION-bit integer in which bit BIT is set and all the
+ others are clear. */
+inline wide_int
+wi::set_bit_in_zero (unsigned int bit, unsigned int precision)
+{
+ return shifted_mask (bit, 1, false, precision);
+}
+
+/* Return an integer of type T in which the low WIDTH bits are set
+ and the other bits are clear, or the inverse if NEGATE_P. */
+template <typename T>
+inline T
+wi::mask (unsigned int width, bool negate_p)
+{
+ STATIC_ASSERT (wi::int_traits<T>::precision);
+ T result;
+ result.set_len (mask (result.write_val (), width, negate_p,
+ wi::int_traits <T>::precision));
+ return result;
+}
+
+/* Return an integer of type T in which the low START bits are clear,
+ the next WIDTH bits are set, and the other bits are clear, or the
+ inverse if NEGATE_P. */
+template <typename T>
+inline T
+wi::shifted_mask (unsigned int start, unsigned int width, bool negate_p)
+{
+ STATIC_ASSERT (wi::int_traits<T>::precision);
+ T result;
+ result.set_len (shifted_mask (result.write_val (), start, width,
+ negate_p,
+ wi::int_traits <T>::precision));
+ return result;
+}
+
+/* Return an integer of type T in which bit BIT is set and all the
+ others are clear. */
+template <typename T>
+inline T
+wi::set_bit_in_zero (unsigned int bit)
+{
+ return shifted_mask <T> (bit, 1, false);
+}
+
+#endif /* WIDE_INT_H */
+2014-10-28 Yury Gribov <y.gribov@samsung.com>
+
+ * libiberty.h (strtol, strtoul, strtoll, strtoull): New prototypes.
+
+2014-09-26 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * libiberty.h (PEX_STDOUT_APPEND): New flag.
+ (PEX_STDERR_APPEND): Likewise.
+
2015-06-26 Release Manager
* GCC 4.9.3 released.
on Unix. */
#define PEX_BINARY_ERROR 0x80
+/* Append stdout to existing file instead of truncating it. */
+#define PEX_STDOUT_APPEND 0x100
+
+/* Thes same as PEX_STDOUT_APPEND, but for STDERR. */
+#define PEX_STDERR_APPEND 0x200
/* Execute one program. Returns NULL on success. On error returns an
error string (typically just the name of a system call); the error
extern int strverscmp (const char *, const char *);
#endif
+#if defined(HAVE_DECL_STRTOL) && !HAVE_DECL_STRTOL
+extern long int strtol (const char *nptr,
+ char **endptr, int base);
+#endif
+
+#if defined(HAVE_DECL_STRTOUL) && !HAVE_DECL_STRTOUL
+extern unsigned long int strtoul (const char *nptr,
+ char **endptr, int base);
+#endif
+
+#if defined(HAVE_LONG_LONG) && defined(HAVE_DECL_STRTOLL) && !HAVE_DECL_STRTOLL
+__extension__
+extern long long int strtoll (const char *nptr,
+ char **endptr, int base);
+#endif
+
+#if defined(HAVE_LONG_LONG) && defined(HAVE_DECL_STRTOULL) && !HAVE_DECL_STRTOULL
+__extension__
+extern unsigned long long int strtoull (const char *nptr,
+ char **endptr, int base);
+#endif
+
+#if defined(HAVE_DECL_STRVERSCMP) && !HAVE_DECL_STRVERSCMP
+/* Compare version strings. */
+extern int strverscmp (const char *, const char *);
+#endif
+
/* Set the title of a process */
extern void setproctitle (const char *name, ...);
+2016-03-02 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * elf.c (backtrace_initialize): Properly initialize elf_fileline_fn to
+ avoid possible crash.
+ (elf_add): Don't set *fileline_fn to elf_nodebug value in case of
+ missing debug info anymore.
+
2015-06-26 Release Manager
* GCC 4.9.3 released.
{
if (!backtrace_close (descriptor, error_callback, data))
goto fail;
- *fileline_fn = elf_nodebug;
return 1;
}
int ret;
int found_sym;
int found_dwarf;
- fileline elf_fileline_fn;
+ fileline elf_fileline_fn = elf_nodebug;
struct phdr_data pd;
ret = elf_add (state, descriptor, 0, error_callback, data, &elf_fileline_fn,
+2014-09-24 Marek Polacek <polacek@redhat.com>
+
+ PR c/61405
+ PR c/53874
+ * include/cpplib.h (enum cpp_ttype): Define CPP_KEYWORD.
+
2015-06-26 Release Manager
* GCC 4.9.3 released.
TTYPE_TABLE
N_TTYPES,
+ /* A token type for keywords, as opposed to ordinary identifiers. */
+ CPP_KEYWORD,
+
/* Positions in the table. */
CPP_LAST_EQ = CPP_LSHIFT,
CPP_FIRST_DIGRAPH = CPP_HASH,
+2014-10-28 Yury Gribov <y.gribov@samsung.com>
+
+ * strtoll.c: New file.
+ * strtoull.c: New file.
+ * configure.ac: Add long long checks. Add harness for strtoll and
+ strtoull. Check decls for strtol, strtoul, strtoll, strtoull.
+ * Makefile.in (CFILES, CONFIGURED_OFILES): Add strtoll and strtoull.
+ * config.in: Regenerate.
+ * configure: Regenerate.
+ * functions.texi: Regenerate.
+ * testsuite/Makefile.in (check-strtol): New rule.
+ (test-strtol): Likewise.
+ (mostlyclean): Clean up strtol test.
+ * testsuite/test-strtol.c: New test.
+
+2014-09-26 Max Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * pex-common.h (struct pex_funcs): Add new parameter for open_write field.
+ * pex-unix.c (pex_unix_open_write): Add support for new parameter.
+ * pex-djgpp.c (pex_djgpp_open_write): Likewise.
+ * pex-win32.c (pex_win32_open_write): Likewise.
+ * pex-common.c (pex_run_in_environment): Likewise.
+
2015-06-26 Release Manager
* GCC 4.9.3 released.
spaces.c splay-tree.c stack-limit.c stpcpy.c stpncpy.c \
strcasecmp.c strchr.c strdup.c strerror.c strncasecmp.c \
strncmp.c strrchr.c strsignal.c strstr.c strtod.c strtol.c \
- strtoul.c strndup.c strnlen.c strverscmp.c \
- timeval-utils.c tmpnam.c \
+ strtoll.c strtoul.c strtoull.c strndup.c strnlen.c \
+ strverscmp.c timeval-utils.c tmpnam.c \
unlink-if-ordinary.c \
vasprintf.c vfork.c vfprintf.c vprintf.c vsnprintf.c vsprintf.c \
waitpid.c \
./strchr.$(objext) ./strdup.$(objext) ./strncasecmp.$(objext) \
./strncmp.$(objext) ./strndup.$(objext) ./strnlen.$(objext) \
./strrchr.$(objext) ./strstr.$(objext) ./strtod.$(objext) \
- ./strtol.$(objext) ./strtoul.$(objext) ./strverscmp.$(objext) \
- ./tmpnam.$(objext) \
+ ./strtol.$(objext) ./strtoul.$(objext) strtoll.$(objext) \
+ ./strtoull.$(objext) ./tmpnam.$(objext) ./strverscmp.$(objext) \
./vasprintf.$(objext) ./vfork.$(objext) ./vfprintf.$(objext) \
./vprintf.$(objext) ./vsnprintf.$(objext) ./vsprintf.$(objext) \
./waitpid.$(objext)
else true; fi
$(COMPILE.c) $(srcdir)/strtol.c $(OUTPUT_OPTION)
+./strtoll.$(objext): $(srcdir)/strtoll.c config.h $(INCDIR)/safe-ctype.h
+ if [ x"$(PICFLAG)" != x ]; then \
+ $(COMPILE.c) $(PICFLAG) $(srcdir)/strtoll.c -o pic/$@; \
+ else true; fi
+ if [ x"$(NOASANFLAG)" != x ]; then \
+ $(COMPILE.c) $(PICFLAG) $(NOASANFLAG) $(srcdir)/strtoll.c -o noasan/$@; \
+ else true; fi
+ $(COMPILE.c) $(srcdir)/strtoll.c $(OUTPUT_OPTION)
+
./strtoul.$(objext): $(srcdir)/strtoul.c config.h $(INCDIR)/ansidecl.h \
$(INCDIR)/safe-ctype.h
if [ x"$(PICFLAG)" != x ]; then \
else true; fi
$(COMPILE.c) $(srcdir)/strtoul.c $(OUTPUT_OPTION)
+./strtoull.$(objext): $(srcdir)/strtoull.c config.h $(INCDIR)/ansidecl.h \
+ $(INCDIR)/safe-ctype.h
+ if [ x"$(PICFLAG)" != x ]; then \
+ $(COMPILE.c) $(PICFLAG) $(srcdir)/strtoull.c -o pic/$@; \
+ else true; fi
+ if [ x"$(NOASANFLAG)" != x ]; then \
+ $(COMPILE.c) $(PICFLAG) $(NOASANFLAG) $(srcdir)/strtoull.c -o noasan/$@; \
+ else true; fi
+ $(COMPILE.c) $(srcdir)/strtoull.c $(OUTPUT_OPTION)
+
./strverscmp.$(objext): $(srcdir)/strverscmp.c $(INCDIR)/ansidecl.h \
$(INCDIR)/libiberty.h $(INCDIR)/safe-ctype.h
if [ x"$(PICFLAG)" != x ]; then \
don't. */
#undef HAVE_DECL_SNPRINTF
+/* Define to 1 if you have the declaration of `strtol', and to 0 if you don't.
+ */
+#undef HAVE_DECL_STRTOL
+
+/* Define to 1 if you have the declaration of `strtoll', and to 0 if you
+ don't. */
+#undef HAVE_DECL_STRTOLL
+
+/* Define to 1 if you have the declaration of `strtoul', and to 0 if you
+ don't. */
+#undef HAVE_DECL_STRTOUL
+
+/* Define to 1 if you have the declaration of `strtoull', and to 0 if you
+ don't. */
+#undef HAVE_DECL_STRTOULL
+
/* Define to 1 if you have the declaration of `strverscmp', and to 0 if you
don't. */
#undef HAVE_DECL_STRVERSCMP
/* Define to 1 if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
+/* Define if you have the `long long' type. */
+#undef HAVE_LONG_LONG
+
/* Define to 1 if you have the <machine/hal_sysinfo.h> header file. */
#undef HAVE_MACHINE_HAL_SYSINFO_H
/* Define to 1 if you have the `strtol' function. */
#undef HAVE_STRTOL
+/* Define to 1 if you have the `strtoll' function. */
+#undef HAVE_STRTOLL
+
/* Define to 1 if you have the `strtoul' function. */
#undef HAVE_STRTOUL
+/* Define to 1 if you have the `strtoull' function. */
+#undef HAVE_STRTOULL
+
/* Define to 1 if you have the `strverscmp' function. */
#undef HAVE_STRVERSCMP
/* The size of `int', as computed by sizeof. */
#undef SIZEOF_INT
+/* The size of `long', as computed by sizeof. */
+#undef SIZEOF_LONG
+
+/* The size of `long long', as computed by sizeof. */
+#undef SIZEOF_LONG_LONG
+
/* Define if you know the direction of stack growth for your system; otherwise
it will be automatically deduced at run-time. STACK_DIRECTION > 0 => grows
toward higher addresses STACK_DIRECTION < 0 => grows toward lower addresses
fi
-# Determine the size of an int for struct fibnode.
+# Determine sizes of some types.
# The cast to long int works around a bug in the HP C Compiler
# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
_ACEOF
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5
+$as_echo_n "checking size of long... " >&6; }
+if test "${ac_cv_sizeof_long+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_long" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ as_fn_set_status 77
+as_fn_error "cannot compute sizeof (long)
+See \`config.log' for more details." "$LINENO" 5; }; }
+ else
+ ac_cv_sizeof_long=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5
+$as_echo "$ac_cv_sizeof_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+_ACEOF
+
+
+
+# Check for presense of long long
+ac_fn_c_check_type "$LINENO" "long long" "ac_cv_type_long_long" "$ac_includes_default"
+if test "x$ac_cv_type_long_long" = x""yes; then :
+
+$as_echo "#define HAVE_LONG_LONG 1" >>confdefs.h
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5
+$as_echo_n "checking size of long long... " >&6; }
+if test "${ac_cv_sizeof_long_long+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_long_long" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+{ as_fn_set_status 77
+as_fn_error "cannot compute sizeof (long long)
+See \`config.log' for more details." "$LINENO" 5; }; }
+ else
+ ac_cv_sizeof_long_long=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5
+$as_echo "$ac_cv_sizeof_long_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long
+_ACEOF
+
+
+fi
+
# Look for a 64-bit type.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a 64-bit type" >&5
funcs="$funcs strtod"
funcs="$funcs strtol"
funcs="$funcs strtoul"
+funcs="$funcs strtoll"
+funcs="$funcs strtoull"
funcs="$funcs strverscmp"
funcs="$funcs tmpnam"
funcs="$funcs vasprintf"
sbrk setenv setproctitle setrlimit sigsetmask snprintf spawnve spawnvpe \
stpcpy stpncpy strcasecmp strchr strdup \
strerror strncasecmp strndup strnlen strrchr strsignal strstr strtod \
- strtol strtoul strverscmp sysconf sysctl sysmp \
+ strtol strtoul strtoll strtoull strverscmp sysconf sysctl sysmp \
table times tmpnam \
vasprintf vfprintf vprintf vsprintf \
wait3 wait4 waitpid
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_VSNPRINTF $ac_have_decl
_ACEOF
+ac_fn_c_check_decl "$LINENO" "strtol" "ac_cv_have_decl_strtol" "$ac_includes_default"
+if test "x$ac_cv_have_decl_strtol" = x""yes; then :
+ ac_have_decl=1
+else
+ ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_STRTOL $ac_have_decl
+_ACEOF
+ac_fn_c_check_decl "$LINENO" "strtoul" "ac_cv_have_decl_strtoul" "$ac_includes_default"
+if test "x$ac_cv_have_decl_strtoul" = x""yes; then :
+ ac_have_decl=1
+else
+ ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_STRTOUL $ac_have_decl
+_ACEOF
+ac_fn_c_check_decl "$LINENO" "strtoll" "ac_cv_have_decl_strtoll" "$ac_includes_default"
+if test "x$ac_cv_have_decl_strtoll" = x""yes; then :
+ ac_have_decl=1
+else
+ ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_STRTOLL $ac_have_decl
+_ACEOF
+ac_fn_c_check_decl "$LINENO" "strtoull" "ac_cv_have_decl_strtoull" "$ac_includes_default"
+if test "x$ac_cv_have_decl_strtoull" = x""yes; then :
+ ac_have_decl=1
+else
+ ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_STRTOULL $ac_have_decl
+_ACEOF
$as_echo "#define HAVE_SYS_ERRLIST 1" >>confdefs.h
libiberty_AC_DECLARE_ERRNO
-# Determine the size of an int for struct fibnode.
+# Determine sizes of some types.
AC_CHECK_SIZEOF([int])
+AC_CHECK_SIZEOF([long])
+
+# Check for presense of long long
+AC_CHECK_TYPE([long long],
+ [AC_DEFINE(HAVE_LONG_LONG, 1, [Define if you have the `long long' type.]) AC_CHECK_SIZEOF([long long])],
+ [])
# Look for a 64-bit type.
AC_MSG_CHECKING([for a 64-bit type])
funcs="$funcs strtod"
funcs="$funcs strtol"
funcs="$funcs strtoul"
+funcs="$funcs strtoll"
+funcs="$funcs strtoull"
funcs="$funcs strverscmp"
funcs="$funcs tmpnam"
funcs="$funcs vasprintf"
sbrk setenv setproctitle setrlimit sigsetmask snprintf spawnve spawnvpe \
stpcpy stpncpy strcasecmp strchr strdup \
strerror strncasecmp strndup strnlen strrchr strsignal strstr strtod \
- strtol strtoul strverscmp sysconf sysctl sysmp \
+ strtol strtoul strtoll strtoull strverscmp sysconf sysctl sysmp \
table times tmpnam \
vasprintf vfprintf vprintf vsprintf \
wait3 wait4 waitpid)
- AC_CHECK_DECLS([basename(char *), ffs, asprintf, vasprintf, snprintf, vsnprintf])
+ AC_CHECK_DECLS([basename(char *), ffs, asprintf, vasprintf, snprintf, vsnprintf, strtol, strtoul, strtoll, strtoull])
AC_DEFINE(HAVE_SYS_ERRLIST, 1, [Define if you have the sys_errlist variable.])
AC_DEFINE(HAVE_SYS_NERR, 1, [Define if you have the sys_nerr variable.])
AC_DEFINE(HAVE_SYS_SIGLIST, 1, [Define if you have the sys_siglist variable.])
@end deftypefn
+@c strtoll.c:33
+@deftypefn Supplemental {long long int} strtoll (const char *@var{string}, @
+ char **@var{endptr}, int @var{base})
+@deftypefnx Supplemental {unsigned long long int} strtoul (@
+ const char *@var{string}, char **@var{endptr}, int @var{base})
+
+The @code{strtoll} function converts the string in @var{string} to a
+long long integer value according to the given @var{base}, which must be
+between 2 and 36 inclusive, or be the special value 0. If @var{base}
+is 0, @code{strtoll} will look for the prefixes @code{0} and @code{0x}
+to indicate bases 8 and 16, respectively, else default to base 10.
+When the base is 16 (either explicitly or implicitly), a prefix of
+@code{0x} is allowed. The handling of @var{endptr} is as that of
+@code{strtod} above. The @code{strtoull} function is the same, except
+that the converted value is unsigned.
+
+@end deftypefn
+
@c strsignal.c:502
@deftypefn Extension int strtosigno (const char *@var{name})
if (out < 0)
{
out = obj->funcs->open_write (obj, outname,
- (flags & PEX_BINARY_OUTPUT) != 0);
+ (flags & PEX_BINARY_OUTPUT) != 0,
+ (flags & PEX_STDOUT_APPEND) != 0);
if (out < 0)
{
*err = errno;
}
else
{
- errdes = obj->funcs->open_write (obj, errname,
- (flags & PEX_BINARY_ERROR) != 0);
+ errdes = obj->funcs->open_write (obj, errname,
+ (flags & PEX_BINARY_ERROR) != 0,
+ (flags & PEX_STDERR_APPEND) != 0);
if (errdes < 0)
{
*err = errno;
/* Open file NAME for writing. If BINARY is non-zero, open in
binary mode. Return >= 0 on success, -1 on error. */
int (*open_write) (struct pex_obj *, const char */* name */,
- int /* binary */);
+ int /* binary */, int /* append */);
/* Execute a child process. FLAGS, EXECUTABLE, ARGV, ERR are from
pex_run. IN, OUT, ERRDES, TOCLOSE are all descriptors, from
open_read, open_write, or pipe, or they are one of STDIN_FILE_NO,
#endif
static int pex_djgpp_open_read (struct pex_obj *, const char *, int);
-static int pex_djgpp_open_write (struct pex_obj *, const char *, int);
+static int pex_djgpp_open_write (struct pex_obj *, const char *, int, int);
static pid_t pex_djgpp_exec_child (struct pex_obj *, int, const char *,
char * const *, char * const *,
int, int, int, int,
static int
pex_djgpp_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED,
- const char *name, int binary)
+ const char *name, int binary, int append)
{
/* Note that we can't use O_EXCL here because gcc may have already
created the temporary file via make_temp_file. */
+ if (append)
+ return -1;
return open (name,
(O_WRONLY | O_CREAT | O_TRUNC
| (binary ? O_BINARY : O_TEXT)),
static void pex_child_error (struct pex_obj *, const char *, const char *, int)
ATTRIBUTE_NORETURN;
static int pex_unix_open_read (struct pex_obj *, const char *, int);
-static int pex_unix_open_write (struct pex_obj *, const char *, int);
+static int pex_unix_open_write (struct pex_obj *, const char *, int, int);
static pid_t pex_unix_exec_child (struct pex_obj *, int, const char *,
char * const *, char * const *,
int, int, int, int,
static int
pex_unix_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
- int binary ATTRIBUTE_UNUSED)
+ int binary ATTRIBUTE_UNUSED, int append)
{
/* Note that we can't use O_EXCL here because gcc may have already
created the temporary file via make_temp_file. */
- return open (name, O_WRONLY | O_CREAT | O_TRUNC, PUBLIC_MODE);
+ return open (name, O_WRONLY | O_CREAT
+ | (append ? O_APPEND : O_TRUNC), PUBLIC_MODE);
}
/* Close a file. */
}
static int pex_win32_open_read (struct pex_obj *, const char *, int);
-static int pex_win32_open_write (struct pex_obj *, const char *, int);
+static int pex_win32_open_write (struct pex_obj *, const char *, int, int);
static pid_t pex_win32_exec_child (struct pex_obj *, int, const char *,
char * const *, char * const *,
int, int, int, int,
static int
pex_win32_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
- int binary)
+ int binary, int append)
{
/* Note that we can't use O_EXCL here because gcc may have already
created the temporary file via make_temp_file. */
+ if (append)
+ return -1;
return _open (name,
(_O_WRONLY | _O_CREAT | _O_TRUNC
| (binary ? _O_BINARY : _O_TEXT)),
--- /dev/null
+/*-
+ * Copyright (c) 2014 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. [rescinded 22 July 1999]
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+
+@deftypefn Supplemental {long long int} strtoll (const char *@var{string}, @
+ char **@var{endptr}, int @var{base})
+@deftypefnx Supplemental {unsigned long long int} strtoul (@
+ const char *@var{string}, char **@var{endptr}, int @var{base})
+
+The @code{strtoll} function converts the string in @var{string} to a
+long long integer value according to the given @var{base}, which must be
+between 2 and 36 inclusive, or be the special value 0. If @var{base}
+is 0, @code{strtoll} will look for the prefixes @code{0} and @code{0x}
+to indicate bases 8 and 16, respectively, else default to base 10.
+When the base is 16 (either explicitly or implicitly), a prefix of
+@code{0x} is allowed. The handling of @var{endptr} is as that of
+@code{strtod} above. The @code{strtoull} function is the same, except
+that the converted value is unsigned.
+
+@end deftypefn
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <errno.h>
+#ifdef NEED_DECLARATION_ERRNO
+extern int errno;
+#endif
+#include "safe-ctype.h"
+
+#ifdef HAVE_LONG_LONG
+
+__extension__
+typedef unsigned long long ullong_type;
+
+__extension__
+typedef long long llong_type;
+
+/* FIXME: It'd be nice to configure around these, but the include files are too
+ painful. These macros should at least be more portable than hardwired hex
+ constants. */
+
+#ifndef ULLONG_MAX
+#define ULLONG_MAX (~(ullong_type)0) /* 0xFFFFFFFFFFFFFFFF */
+#endif
+
+#ifndef LLONG_MAX
+#define LLONG_MAX ((llong_type)(ULLONG_MAX >> 1)) /* 0x7FFFFFFFFFFFFFFF */
+#endif
+
+#ifndef LLONG_MIN
+#define LLONG_MIN (~LLONG_MAX) /* 0x8000000000000000 */
+#endif
+
+/*
+ * Convert a string to a long long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+llong_type
+strtoll(const char *nptr, char **endptr, register int base)
+{
+ register const char *s = nptr;
+ register ullong_type acc;
+ register int c;
+ register ullong_type cutoff;
+ register int neg = 0, any, cutlim;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ do {
+ c = *s++;
+ } while (ISSPACE(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? -(ullong_type)LLONG_MIN : LLONG_MAX;
+ cutlim = cutoff % (ullong_type)base;
+ cutoff /= (ullong_type)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (ISDIGIT(c))
+ c -= '0';
+ else if (ISALPHA(c))
+ c -= ISUPPER(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LLONG_MIN : LLONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *endptr = (char *) (any ? s - 1 : nptr);
+ return (acc);
+}
+
+#endif /* ifdef HAVE_LONG_LONG */
--- /dev/null
+/*
+ * Copyright (c) 2014 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. [rescinded 22 July 1999]
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <errno.h>
+#ifdef NEED_DECLARATION_ERRNO
+extern int errno;
+#endif
+#if 0
+#include <stdlib.h>
+#endif
+#include "ansidecl.h"
+#include "safe-ctype.h"
+
+#ifdef HAVE_LONG_LONG
+
+__extension__
+typedef unsigned long long ullong_type;
+
+#ifndef ULLONG_MAX
+#define ULLONG_MAX (~(ullong_type)0) /* 0xFFFFFFFFFFFFFFFF */
+#endif
+
+/*
+ * Convert a string to an unsigned long long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+ullong_type
+strtoull(const char *nptr, char **endptr, register int base)
+{
+ register const char *s = nptr;
+ register ullong_type acc;
+ register int c;
+ register ullong_type cutoff;
+ register int neg = 0, any, cutlim;
+
+ /*
+ * See strtol for comments as to the logic used.
+ */
+ do {
+ c = *s++;
+ } while (ISSPACE(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+ cutoff = (ullong_type)ULLONG_MAX / (ullong_type)base;
+ cutlim = (ullong_type)ULLONG_MAX % (ullong_type)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (ISDIGIT(c))
+ c -= '0';
+ else if (ISALPHA(c))
+ c -= ISUPPER(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = ULLONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *endptr = (char *) (any ? s - 1 : nptr);
+ return (acc);
+}
+
+#endif /* ifdef HAVE_LONG_LONG */
# CHECK is set to "really_check" or the empty string by configure.
check: @CHECK@
-really-check: check-cplus-dem check-pexecute check-expandargv
+really-check: check-cplus-dem check-pexecute check-expandargv check-strtol
# Run some tests of the demangler.
check-cplus-dem: test-demangle $(srcdir)/demangle-expected
check-expandargv: test-expandargv
./test-expandargv
+# Check the strtol functionality
+check-strtol: test-strtol
+ ./test-strtol
+
TEST_COMPILE = $(CC) @DEFS@ $(LIBCFLAGS) -I.. -I$(INCDIR) $(HDEFINES)
test-demangle: $(srcdir)/test-demangle.c ../libiberty.a
$(TEST_COMPILE) -o test-demangle \
$(TEST_COMPILE) -DHAVE_CONFIG_H -I.. -o test-expandargv \
$(srcdir)/test-expandargv.c ../libiberty.a
+test-strtol: $(srcdir)/test-strtol.c ../libiberty.a
+ $(TEST_COMPILE) -DHAVE_CONFIG_H -I.. -o test-strtol \
+ $(srcdir)/test-strtol.c ../libiberty.a
+
# Standard (either GNU or Cygnus) rules we don't use.
html install-html info install-info clean-info dvi pdf install-pdf \
install etags tags installcheck:
rm -f test-demangle
rm -f test-pexecute
rm -f test-expandargv
+ rm -f test-strtol
rm -f core
clean: mostlyclean
distclean: clean
--- /dev/null
+/* Test program for strtol family of funtions,
+ Copyright (C) 2014 Free Software Foundation, Inc.
+ Written by Yury Gribov <y.gribov@samsung.com>
+
+ This file is part of the libiberty library, which is part of GCC.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ In addition to the permissions in the GNU General Public License, the
+ Free Software Foundation gives you unlimited permission to link the
+ compiled version of this file into combinations with other programs,
+ and to distribute those combinations without any restriction coming
+ from the use of this file. (The General Public License restrictions
+ do apply in other respects; for example, they cover modification of
+ the file, and distribution when not linked into a combined
+ executable.)
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "libiberty.h"
+#include <stdio.h>
+#include <errno.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+
+/* Test input data. */
+
+enum conversion_fun
+{
+ STRTOL,
+ STRTOLL,
+ STRTOUL,
+ STRTOULL,
+};
+
+#ifdef HAVE_LONG_LONG
+typedef unsigned long long integer_type;
+#else
+typedef unsigned long integer_type;
+#endif
+
+struct test_data_t
+{
+ enum conversion_fun fun;
+ const char *nptr;
+ int base;
+ integer_type res;
+ int errnum;
+};
+
+const struct test_data_t test_data[] = {
+ { STRTOL, "0x123", 0, 0x123L, 0 },
+ { STRTOL, "123", 0, 123L, 0 },
+ { STRTOL, "0123", 0, 0123L, 0 },
+ { STRTOL, "0x7FFFFFFF", 0, 0x7fffffffL, 0 },
+ { STRTOL, "-0x80000000", 0, -0x80000000L, 0 },
+ { STRTOUL, "0x123", 0, 0x123UL, 0 },
+ { STRTOUL, "123", 0, 123UL, 0 },
+ { STRTOUL, "0123", 0, 0123UL, 0 },
+ { STRTOUL, "0xFFFFFFFF", 0, 0xffffffffUL, 0 },
+#if SIZEOF_LONG == 4
+ { STRTOL, "0x80000000", 0, 0x7fffffffL, ERANGE },
+ { STRTOL, "-0x80000001", 0, -0x80000000L, ERANGE },
+ { STRTOUL, "0x100000000", 0, 0xffffffffUL, ERANGE },
+#endif
+#ifdef HAVE_LONG_LONG
+ { STRTOLL, "0x123", 0, 0x123LL, 0 },
+ { STRTOLL, "123", 0, 123LL, 0 },
+ { STRTOLL, "0123", 0, 0123LL, 0 },
+ { STRTOLL, "0x7FFFFFFFFFFFFFFF", 0, 0x7fffffffffffffffLL, 0 },
+ { STRTOLL, "-0x8000000000000000", 0, -0x8000000000000000LL, 0 },
+ { STRTOULL, "0x123", 0, 0x123ULL, 0 },
+ { STRTOULL, "123", 0, 123ULL, 0 },
+ { STRTOULL, "0123", 0, 0123ULL, 0 },
+ { STRTOULL, "0xFFFFFFFFFFFFFFFF", 0, 0xffffffffffffffffULL, 0 },
+#if SIZEOF_LONG_LONG == 8
+ { STRTOLL, "0x8000000000000000", 0, 0x7fffffffffffffffLL, ERANGE },
+ { STRTOLL, "-0x8000000000000001", 0, -0x8000000000000000LL, ERANGE },
+ { STRTOULL, "0x10000000000000000", 0, 0xffffffffffffffffULL, ERANGE },
+#endif
+#endif
+};
+
+/* run_tests:
+ Run conversion function
+ Compare results
+ Return number of fails */
+
+int
+run_tests (const struct test_data_t *test_data, size_t ntests)
+{
+ int fails = 0, failed;
+ size_t i;
+
+ for (i = 0; i < ntests; ++i)
+ {
+ integer_type res;
+ int saved_errno;
+
+ errno = 0;
+
+ switch (test_data[i].fun)
+ {
+ case STRTOL:
+ res = strtol (test_data[i].nptr, 0, test_data[i].base);
+ break;
+ case STRTOUL:
+ res = strtoul (test_data[i].nptr, 0, test_data[i].base);
+ break;
+#ifdef HAVE_LONG_LONG
+ case STRTOLL:
+ res = strtoll (test_data[i].nptr, 0, test_data[i].base);
+ break;
+ case STRTOULL:
+ res = strtoull (test_data[i].nptr, 0, test_data[i].base);
+ break;
+#endif
+ }
+
+ saved_errno = errno;
+
+ failed = 0;
+
+ /* Compare result */
+ if (res != test_data[i].res)
+ {
+ printf ("FAIL: test-strtol-%zd. Results don't match.\n", i);
+ failed++;
+ }
+
+ /* Compare errno */
+ if (saved_errno != test_data[i].errnum)
+ {
+ printf ("FAIL: test-strtol-%zd. Errnos don't match.\n", i);
+ failed++;
+ }
+
+ if (!failed)
+ printf ("PASS: test-strtol-%zd.\n", i);
+ else
+ fails++;
+ }
+
+ return fails;
+}
+
+int
+main(int argc, char **argv)
+{
+ int fails;
+ fails = run_tests (test_data, sizeof (test_data) / sizeof (test_data[0]));
+ exit (fails ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+2016-01-19 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/68824
+ * tsan/tsan_interceptors.cc (NEED_TLS_GET_ADDR, __tls_get_addr,
+ InitializeInterceptors): Cherry pick upstream r258119.
+
+2015-11-24 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * include/system/linux/asm/ptrace.h: Move to ...
+ * include/system/asm/ptrace.h: ... this.
+
+2015-11-24 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * include/system/linux/asm/ptrace.h: New header.
+
+2015-11-23 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * All source files: Merge from upstream r253555.
+ * configure.tgt: Enable LSan on aarch64-*-linux* targets. Add new
+ dependences for TSan for aarch64-*-linux* targets.
+ * tsan/Makefile.am: Add new source files.
+ * configure: Regenerate.
+ * tsan/Makefile.in: Likewise.
+
+2015-11-09 Alan Modra <amodra@gmail.com>
+
+ * sanitizer_common/sanitizer_common_interceptors.inc: Update size
+ params for _obstack_begin_1, _obstack_begin, _obstack_newchunk
+ interceptors.
+ * configure.ac: Substitute OBSTACK_DEFS.
+ * asan/Makefile.am: Add OBSTACK_DEFS to DEFS.
+ * tsan/Makefile.am: Likewise.
+ * configure: Regenerate.
+ * Makefile.in: Regenerate.
+ * asan/Makefile.in: Regenerate.
+ * interception/Makefile.in: Regenerate.
+ * libbacktrace/Makefile.in: Regenerate.
+ * lsan/Makefile.in: Regenerate.
+ * sanitizer_common/Makefile.in: Regenerate.
+ * tsan/Makefile.in: Regenerate.
+ * ubsan/Makefile.in: Regenerate.
+
+2015-10-22 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ PR bootstrap/68041
+ * configure.ac (link_sanitizer_common): Link against librt only if it
+ contains shm_open, required by sanitizers.
+ (CXX_ABI_NEEDED): Remove variable.
+ * configure: Regenerate.
+ * ubsan/Makefile.am (libubsan_la_LIBADD): Do not add -lc++abi anymore.
+ * ubsan/Makefile.in: Regenerate.
+
+2015-10-21 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * HOWTO_MERGE: New file.
+
+2015-10-21 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * sanitizer_common/sanitizer_stacktrace.cc (GetCanonicFrame): Assume we
+ compiled code with GCC when extracting the caller PC for ARM if no
+ valid frame pointer is available.
+
+2015-10-21 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ PR bootstrap/63888
+ Reapply:
+ 2015-02-20 Jakub Jelinek <jakub@redhat.com>
+
+ * asan/asan_globals.cc (RegisterGlobal): Disable detect_odr_violation
+ support until it is rewritten upstream.
+
+ * c-c++-common/asan/pr63888.c: New test.
+
+2015-10-21 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ PR sanitizer/63958
+ Reapply:
+ 2014-10-14 David S. Miller <davem@davemloft.net>
+
+ * sanitizer_common/sanitizer_platform_limits_linux.cc (time_t):
+ Define at __kernel_time_t, as needed for sparc.
+ (struct __old_kernel_stat): Don't check if __sparc__ is defined.
+ * libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h
+ (__sanitizer): Define struct___old_kernel_stat_sz,
+ struct_kernel_stat_sz, and struct_kernel_stat64_sz for sparc.
+ (__sanitizer_ipc_perm): Adjust for sparc targets.
+ (__sanitizer_shmid_ds): Likewsie.
+ (__sanitizer_sigaction): Likewise.
+ (IOC_SIZE): Likewsie.
+
+2015-10-21 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
+
+ * All source files: Merge from upstream r250806.
+ * configure.ac (link_sanitizer_common): Add -lrt flag.
+ * configure.tgt: Enable TSAN and LSAN for aarch64-linux targets.
+ Set USE_CXX_ABI_FLAG=true for darwin.
+ * asan/Makefile.am (asan_files): Add new files.
+ (DEFS): Add DCAN_SANITIZE_UB=0 and remove unused and legacy
+ DASAN_FLEXIBLE_MAPPING_AND_OFFSET=0.
+ * asan/Makefile.in: Regenerate.
+ * ubsan/Makefile.am (ubsan_files): Add new files.
+ (DEFS): Add DCAN_SANITIZE_UB=1.
+ (libubsan_la_LIBADD): Add -lc++abi if USE_CXX_ABI_FLAG is true.
+ * ubsan/Makefile.in: Regenerate.
+ * tsan/Makefile.am (tsan_files): Add new files.
+ (DEFS): Add DCAN_SANITIZE_UB=0.
+ * tsan/Makefile.in: Regenerate.
+ * sanitizer_common/Makefile.am (sanitizer_common_files): Add new files.
+ * sanitizer_common/Makefile.in: Regenerate.
+ * asan/libtool-version: Bump the libasan SONAME.
+
+2015-01-25 Venkataramanan Kumar <venkataramanan.kumar@linaro.org>
+
+ * configure.ac (TSAN_TARGET_DEPENDENT_OBJECTS): Define.
+ * configure: Regenerate.
+ * tsan/Makefile.am (EXTRA_libtsan_la_SOURCES): Define.
+ (libtsan_la_DEPENDENCIES): Likewise.
+ * Makefile.in: Regenerate.
+ * asan/Makefile.in: Regenerate.
+ * interception/Makefile.in: Regenerate.
+ * libbacktrace/Makefile.in: Regenerate.
+ * lsan/Makefile.in: Regenerate.
+ * sanitizer_common/Makefile.in: Regenerate.
+ * tsan/Makefile.in: Regenerate.
+ * ubsan/Makefile.in: Regenerate.
+
+2015-02-20 Jakub Jelinek <jakub@redhat.com>
+
+ PR bootstrap/63888
+ * asan/asan_globals.cc (RegisterGlobal): Disable detect_odr_violation
+ support until it is rewritten upstream.
+
+2015-01-20 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/64632
+ * ubsan/ubsan_type_hash.cc: Cherry pick upstream r224972.
+
+2015-04-13 Yury Gribov <y.gribov@samsung.com>
+
+ PR sanitizer/64839
+ * sanitizer_common/sanitizer_platform.h: Cherry pick
+ upstream r234470.
+ * sanitizer_common/sanitizer_platform_limits_posix.cc: Ditto.
+ * configure.ac (RPC_DEFS): Check for precense of RPC headers.
+ * sanitizer_common/Makefile.am (DEFS): Pass info to compiler.
+ * Makefile.in: Regenerate.
+ * asan/Makefile.in: Regenerate.
+ * config.h.in: Regenerate.
+ * configure: Regenerate.
+ * interception/Makefile.in: Regenerate.
+ * libbacktrace/Makefile.in: Regenerate.
+ * lsan/Makefile.in: Regenerate.
+ * sanitizer_common/Makefile.in: Regenerate.
+ * tsan/Makefile.in: Regenerate.
+ * ubsan/Makefile.in: Regenerate.
+
+2014-11-21 Jakub Jelinek <jakub@redhat.com>
+
+ PR target/61137
+ * config/ia64/ia64.c (ia64_attribute_takes_identifier_p): New function.
+ (TARGET_ATTRIBUTE_TAKES_IDENTIFIER_P): Redefine to it.
+
+2014-11-14 Uros Bizjak <ubizjak@gmail.com>
+
+ * sanitizer_common/Makefile.am (AM_CXXFLAGS): Use -std=gnu++11.
+ * asan/Makefile.am (AM_CXXFLAGS): Ditto.
+ * lsan/Makefile.am (AM_CXXFLAGS): Ditto.
+ * interception/Makefile.am (AM_CXXFLAGS): Ditto.
+ * tsan/Makefile.am (AM_CXXFLAGS): Ditto.
+ * libbacktrace/Makefile.am (AM_CXXFLAGS): Ditto.
+ * ubsan/Makefile.am (AM_CXXFLAGS): Ditto.
+ * sanitizer_common/Makefile.in: Regenerate.
+ * asan/Makefile.in: Ditto.
+ * lsan/Makefile.in: Ditto.
+ * interception/Makefile.in: Ditto.
+ * tsan/Makefile.in: Ditto.
+ * libbacktrace/Makefile.in: Ditto.
+ * ubsan/Makefile.in: Ditto.
+
+2014-11-13 Kostya Serebryany <kcc@google.com>
+
+ * All source files: Merge from upstream r221802.
+ * sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
+ (LibbacktraceSymbolizer::SymbolizeData): Replace 'address'
+ with 'start' to follow the new interface.
+ * asan/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
+ * interception/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
+ * libbacktrace/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
+ * lsan/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
+ * sanitizer_common/Makefile.am (sanitizer_common_files): Added new
+ files.
+ (AM_CXXFLAGS): Added -std=c++11.
+ * tsan/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
+ * ubsan/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
+ * asan/Makefile.in: Regenerate.
+ * interception/Makefile.in: Regenerate.
+ * libbacktrace/Makefile.in: Regenerate.
+ * lsan/Makefile.in: Regenerate.
+ * sanitizer_common/Makefile.in: Regenerate.
+ * tsan/Makefile.in: Regenerate.
+ * ubsan/Makefile.in: Regenerate.
+
+2014-10-14 David S. Miller <davem@davemloft.net>
+
+ * sanitizer_common/sanitizer_platform_limits_linux.cc (time_t):
+ Define at __kernel_time_t, as needed for sparc.
+ (struct __old_kernel_stat): Don't check if __sparc__ is defined.
+ * libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h
+ (__sanitizer): Define struct___old_kernel_stat_sz,
+ struct_kernel_stat_sz, and struct_kernel_stat64_sz for sparc.
+ (__sanitizer_ipc_perm): Adjust for sparc targets.
+ (__sanitizer_shmid_ds): Likewsie.
+ (__sanitizer_sigaction): Likewsie.
+ (IOC_SIZE): Likewsie.
+
+2014-10-14 Jakub Jelinek <jakub@redhat.com>
+
+ * ubsan/Makefile.am (DEFS): Add -DPIC.
+ * ubsan/Makefile.in: Regenerated.
+
+2014-09-19 Kostya Serebryany <kcc@google.com>
+
+ * All source files: Merge from upstream r218156.
+ * asan/Makefile.am (asan_files): Added new files.
+ * asan/Makefile.in: Regenerate.
+ * ubsan/Makefile.am (ubsan_files): Added new files.
+ * ubsan/Makefile.in: Regenerate.
+ * tsan/Makefile.am (tsan_files): Added new files.
+ * tsan/Makefile.in: Regenerate.
+ * sanitizer_common/Makefile.am (sanitizer_common_files): Added new
+ files.
+ * sanitizer_common/Makefile.in: Regenerate.
+ * asan/libtool-version: Bump the libasan SONAME.
+
+2014-09-10 Jakub Jelinek <jakub@redhat.com>
+
+ * ubsan/ubsan_handlers.cc, ubsan/ubsan_handlers.h: Cherry pick
+ upstream r215485, r217389, r217391 and r217400.
+
+2014-06-23 Paolo Carlini <paolo.carlini@oracle.com>
+
+ * sanitizer_common/sanitizer_common_interceptors.inc:
+ Cherry pick upstream r211008.
+
+2014-06-11 Richard Biener <rguenther@suse.de>
+
+ * asan/asan_linux.cc: Cherry pick upstream r210012.
+
+2014-05-30 Jakub Jelinek <jakub@redhat.com>
+
+ * sanitizer_common/sanitizer_stacktrace.cc: Cherry pick upstream
+ r209879.
+ * sanitizer_common/sanitizer_common.h: Likewise.
+ * asan/asan_mapping.h: Likewise.
+ * asan/asan_linux.cc: Likewise.
+ * tsan/tsan_mman.cc: Cherry pick upstream r209744.
+ * sanitizer_common/sanitizer_allocator.h: Likewise.
+
+2014-05-23 Marek Polacek <polacek@redhat.com>
+
+ * ubsan/ubsan_value.cc (getFloatValue): Handle 96-bit
+ floating-point types.
+
+2014-05-22 Kostya Serebryany <kcc@google.com>
+
+ * All source files: Merge from upstream r209283.
+ * asan/Makefile.am (asan_files): Added new files.
+ * asan/Makefile.in: Regenerate.
+ * tsan/Makefile.am (tsan_files): Added new files.
+ * tsan/Makefile.in: Regenerate.
+ * sanitizer_common/Makefile.am (sanitizer_common_files): Added new
+ files.
+ * sanitizer_common/Makefile.in: Regenerate.
+
+2014-05-14 Yury Gribov <y.gribov@samsung.com>
+
+ PR sanitizer/61100
+ * Makefile.am (nodist_saninclude_HEADERS): Install
+ public headers.
+ * Makefile.in: Regenerate.
+
2015-06-26 Release Manager
* GCC 4.9.3 released.
--- /dev/null
+In general, merging process should not be very difficult, but we need to
+track various ABI changes and GCC-specific patches carefully. Here is a
+general list of actions required to perform the merge:
+
+* Checkout recent GCC tree.
+* Run merge.sh script from libsanitizer directory.
+* Modify Makefile.am files into asan/tsan/lsan/ubsan/sanitizer_common/interception
+ directories if needed. In particular, you may need to add new source files
+ and remove old ones in source files list, add new flags to {C, CXX}FLAGS if
+ needed and update DEFS with new defined variables. You can find these changes
+ in corresponding CMakeLists.txt and config-ix.cmake files from compiler-rt source
+ directory.
+* Apply all needed GCC-specific patches to libsanitizer (note that some of
+ them might be already included to upstream).
+* Apply all necessary compiler changes. Be especially careful here, you must
+ not break ABI between compiler and library. You can reveal these changes by
+ inspecting the history of AddressSanitizer.cpp and ThreadSanitizer.cpp files
+ from LLVM source tree.
+* Update ASan testsuite with corresponding tests from lib/asan/tests directory.
+ Not all tests can be migrated easily, so you don't need them all to be adapted.
+* Modify configure.ac file if needed (e.g. if you need to add link against new
+ library for sanitizer lilbs).
+* Add new target platforms in configure.tgt script if needed.
+* Bump SONAME for sanitizer libraries in asan/tsan/ubsan libtool-version files
+ if ABI has changed.
+* Regenerate configure script and all Makefiles by autoreconf. You should use
+ exactly the same autoconf and automake versions as for other GCC directories (current
+ versions are written in Makefile.in and configure files).
+* Run regression testing on at least three platforms (e.g. x86-linux-gnu, x86_64-linux-gnu,
+ aarch64-linux-gnu, arm-linux-gnueabi).
+* Run {A, UB}San bootstrap on at least three platforms.
+* Compare ABI of corresponding libclang_rt-asan and newly build libasan libraries.
+ You can use a pretty good libabigail tool (https://sourceware.org/libabigail/index.html)
+ to perform such a comparision. Note, that the list of exported symbols may differ,
+ e.g. because libasan currently does not include UBSan runtime.
+* Split your changes into logical parts (e.g. raw merge, compiler changes, GCC-specific changes
+ in libasan, configure/Makefile changes). The review process has O(N^2) complexity, so you
+ would simplify and probably speed up the review process by doing this.
+* Send your patches for review to GCC Patches Mailing List (gcc-patches@gcc.gnu.org).
-196489
+253555
The first line of this file holds the svn revision number of the
last merge done from the master library sources.
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
+OBSTACK_DEFS = @OBSTACK_DEFS@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
VERSION = @VERSION@
VIEW_FILE = @VIEW_FILE@
abs_builddir = @abs_builddir@
# May be used by toolexeclibdir.
gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
-DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DASAN_HAS_EXCEPTIONS=1 -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 -DASAN_NEEDS_SEGV=1
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DASAN_HAS_EXCEPTIONS=1 -DASAN_NEEDS_SEGV=1 -DCAN_SANITIZE_UB=0 @OBSTACK_DEFS@
if USING_MAC_INTERPOSE
DEFS += -DMAC_INTERPOSE_FUNCTIONS -DMISSING_BLOCKS_SUPPORT
endif
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++11
ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
toolexeclib_LTLIBRARIES = libasan.la
nodist_toolexeclib_HEADERS = libasan_preinit.o
asan_files = \
- asan_allocator2.cc \
- asan_dll_thunk.cc \
+ asan_activation.cc \
+ asan_allocator.cc \
+ asan_debugging.cc \
asan_fake_stack.cc \
+ asan_flags.cc \
asan_globals.cc \
asan_interceptors.cc \
asan_linux.cc \
asan_rtl.cc \
asan_stack.cc \
asan_stats.cc \
+ asan_suppressions.cc \
asan_thread.cc \
- asan_win.cc
+ asan_win.cc \
+ asan_win_dll_thunk.cc \
+ asan_win_dynamic_runtime_thunk.cc
libasan_la_SOURCES = $(asan_files)
libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la
$(top_builddir)/sanitizer_common/libsanitizer_common.la \
$(top_builddir)/lsan/libsanitizer_lsan.la $(am__append_2) \
$(am__append_3) $(am__DEPENDENCIES_1)
-am__objects_1 = asan_allocator2.lo asan_dll_thunk.lo \
- asan_fake_stack.lo asan_globals.lo asan_interceptors.lo \
- asan_linux.lo asan_mac.lo asan_malloc_linux.lo \
- asan_malloc_mac.lo asan_malloc_win.lo asan_new_delete.lo \
- asan_poisoning.lo asan_posix.lo asan_report.lo asan_rtl.lo \
- asan_stack.lo asan_stats.lo asan_thread.lo asan_win.lo
+am__objects_1 = asan_activation.lo asan_allocator.lo asan_debugging.lo \
+ asan_fake_stack.lo asan_flags.lo asan_globals.lo \
+ asan_interceptors.lo asan_linux.lo asan_mac.lo \
+ asan_malloc_linux.lo asan_malloc_mac.lo asan_malloc_win.lo \
+ asan_new_delete.lo asan_poisoning.lo asan_posix.lo \
+ asan_report.lo asan_rtl.lo asan_stack.lo asan_stats.lo \
+ asan_suppressions.lo asan_thread.lo asan_win.lo \
+ asan_win_dll_thunk.lo asan_win_dynamic_runtime_thunk.lo
am_libasan_la_OBJECTS = $(am__objects_1)
libasan_la_OBJECTS = $(am_libasan_la_OBJECTS)
libasan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
CYGPATH_W = @CYGPATH_W@
DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS \
-D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS \
- -DASAN_HAS_EXCEPTIONS=1 -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 \
- -DASAN_NEEDS_SEGV=1 $(am__append_1)
+ -DASAN_HAS_EXCEPTIONS=1 -DASAN_NEEDS_SEGV=1 \
+ -DCAN_SANITIZE_UB=0 @OBSTACK_DEFS@ $(am__append_1)
DEPDIR = @DEPDIR@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
+OBSTACK_DEFS = @OBSTACK_DEFS@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
VERSION = @VERSION@
VIEW_FILE = @VIEW_FILE@
abs_builddir = @abs_builddir@
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
- -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+ -Wno-variadic-macros \
+ $(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=gnu++11
ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
toolexeclib_LTLIBRARIES = libasan.la
nodist_toolexeclib_HEADERS = libasan_preinit.o
asan_files = \
- asan_allocator2.cc \
- asan_dll_thunk.cc \
+ asan_activation.cc \
+ asan_allocator.cc \
+ asan_debugging.cc \
asan_fake_stack.cc \
+ asan_flags.cc \
asan_globals.cc \
asan_interceptors.cc \
asan_linux.cc \
asan_rtl.cc \
asan_stack.cc \
asan_stats.cc \
+ asan_suppressions.cc \
asan_thread.cc \
- asan_win.cc
+ asan_win.cc \
+ asan_win_dll_thunk.cc \
+ asan_win_dynamic_runtime_thunk.cc
libasan_la_SOURCES = $(asan_files)
libasan_la_LIBADD = \
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator2.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_dll_thunk.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_debugging.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_flags.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtl.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stack.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_suppressions.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dll_thunk.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo@am__quote@
.cc.o:
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
--- /dev/null
+//===-- asan_activation.cc --------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan activation/deactivation logic.
+//===----------------------------------------------------------------------===//
+
+#include "asan_activation.h"
+#include "asan_allocator.h"
+#include "asan_flags.h"
+#include "asan_internal.h"
+#include "asan_poisoning.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_flags.h"
+
+namespace __asan {
+
+static struct AsanDeactivatedFlags {
+ AllocatorOptions allocator_options;
+ int malloc_context_size;
+ bool poison_heap;
+ bool coverage;
+ const char *coverage_dir;
+
+ void RegisterActivationFlags(FlagParser *parser, Flags *f, CommonFlags *cf) {
+#define ASAN_ACTIVATION_FLAG(Type, Name) \
+ RegisterFlag(parser, #Name, "", &f->Name);
+#define COMMON_ACTIVATION_FLAG(Type, Name) \
+ RegisterFlag(parser, #Name, "", &cf->Name);
+#include "asan_activation_flags.inc"
+#undef ASAN_ACTIVATION_FLAG
+#undef COMMON_ACTIVATION_FLAG
+
+ RegisterIncludeFlags(parser, cf);
+ }
+
+ void OverrideFromActivationFlags() {
+ Flags f;
+ CommonFlags cf;
+ FlagParser parser;
+ RegisterActivationFlags(&parser, &f, &cf);
+
+ // Copy the current activation flags.
+ allocator_options.CopyTo(&f, &cf);
+ cf.malloc_context_size = malloc_context_size;
+ f.poison_heap = poison_heap;
+ cf.coverage = coverage;
+ cf.coverage_dir = coverage_dir;
+ cf.verbosity = Verbosity();
+ cf.help = false; // this is activation-specific help
+
+ // Check if activation flags need to be overriden.
+ if (const char *env = GetEnv("ASAN_ACTIVATION_OPTIONS")) {
+ parser.ParseString(env);
+ }
+
+ // Override from getprop asan.options.
+ char buf[100];
+ GetExtraActivationFlags(buf, sizeof(buf));
+ parser.ParseString(buf);
+
+ SetVerbosity(cf.verbosity);
+
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (cf.help) parser.PrintFlagDescriptions();
+
+ allocator_options.SetFrom(&f, &cf);
+ malloc_context_size = cf.malloc_context_size;
+ poison_heap = f.poison_heap;
+ coverage = cf.coverage;
+ coverage_dir = cf.coverage_dir;
+ }
+
+ void Print() {
+ Report(
+ "quarantine_size_mb %d, max_redzone %d, poison_heap %d, "
+ "malloc_context_size %d, alloc_dealloc_mismatch %d, "
+ "allocator_may_return_null %d, coverage %d, coverage_dir %s\n",
+ allocator_options.quarantine_size_mb, allocator_options.max_redzone,
+ poison_heap, malloc_context_size,
+ allocator_options.alloc_dealloc_mismatch,
+ allocator_options.may_return_null, coverage, coverage_dir);
+ }
+} asan_deactivated_flags;
+
+static bool asan_is_deactivated;
+
+void AsanDeactivate() {
+ CHECK(!asan_is_deactivated);
+ VReport(1, "Deactivating ASan\n");
+
+ // Stash runtime state.
+ GetAllocatorOptions(&asan_deactivated_flags.allocator_options);
+ asan_deactivated_flags.malloc_context_size = GetMallocContextSize();
+ asan_deactivated_flags.poison_heap = CanPoisonMemory();
+ asan_deactivated_flags.coverage = common_flags()->coverage;
+ asan_deactivated_flags.coverage_dir = common_flags()->coverage_dir;
+
+ // Deactivate the runtime.
+ SetCanPoisonMemory(false);
+ SetMallocContextSize(1);
+ ReInitializeCoverage(false, nullptr);
+
+ AllocatorOptions disabled = asan_deactivated_flags.allocator_options;
+ disabled.quarantine_size_mb = 0;
+ disabled.min_redzone = 16; // Redzone must be at least 16 bytes long.
+ disabled.max_redzone = 16;
+ disabled.alloc_dealloc_mismatch = false;
+ disabled.may_return_null = true;
+ ReInitializeAllocator(disabled);
+
+ asan_is_deactivated = true;
+}
+
+void AsanActivate() {
+ if (!asan_is_deactivated) return;
+ VReport(1, "Activating ASan\n");
+
+ UpdateProcessName();
+
+ asan_deactivated_flags.OverrideFromActivationFlags();
+
+ SetCanPoisonMemory(asan_deactivated_flags.poison_heap);
+ SetMallocContextSize(asan_deactivated_flags.malloc_context_size);
+ ReInitializeCoverage(asan_deactivated_flags.coverage,
+ asan_deactivated_flags.coverage_dir);
+ ReInitializeAllocator(asan_deactivated_flags.allocator_options);
+
+ asan_is_deactivated = false;
+ if (Verbosity()) {
+ Report("Activated with flags:\n");
+ asan_deactivated_flags.Print();
+ }
+}
+
+} // namespace __asan
--- /dev/null
+//===-- asan_activation.h ---------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan activation/deactivation logic.
+//===----------------------------------------------------------------------===//
+
+#ifndef ASAN_ACTIVATION_H
+#define ASAN_ACTIVATION_H
+
+namespace __asan {
+void AsanDeactivate();
+void AsanActivate();
+} // namespace __asan
+
+#endif // ASAN_ACTIVATION_H
--- /dev/null
+//===-- asan_activation_flags.inc -------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A subset of ASan (and common) runtime flags supported at activation time.
+//
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_ACTIVATION_FLAG
+# error "Define ASAN_ACTIVATION_FLAG prior to including this file!"
+#endif
+
+#ifndef COMMON_ACTIVATION_FLAG
+# error "Define COMMON_ACTIVATION_FLAG prior to including this file!"
+#endif
+
+// ASAN_ACTIVATION_FLAG(Type, Name)
+// See COMMON_FLAG in sanitizer_flags.inc for more details.
+
+ASAN_ACTIVATION_FLAG(int, redzone)
+ASAN_ACTIVATION_FLAG(int, max_redzone)
+ASAN_ACTIVATION_FLAG(int, quarantine_size_mb)
+ASAN_ACTIVATION_FLAG(bool, alloc_dealloc_mismatch)
+ASAN_ACTIVATION_FLAG(bool, poison_heap)
+
+COMMON_ACTIVATION_FLAG(bool, allocator_may_return_null)
+COMMON_ACTIVATION_FLAG(int, malloc_context_size)
+COMMON_ACTIVATION_FLAG(bool, coverage)
+COMMON_ACTIVATION_FLAG(const char *, coverage_dir)
+COMMON_ACTIVATION_FLAG(int, verbosity)
+COMMON_ACTIVATION_FLAG(bool, help)
--- /dev/null
+//===-- asan_allocator.cc -------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Implementation of ASan's memory allocator, 2-nd version.
+// This variant uses the allocator from sanitizer_common, i.e. the one shared
+// with ThreadSanitizer and MemorySanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "asan_allocator.h"
+#include "asan_mapping.h"
+#include "asan_poisoning.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_list.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_quarantine.h"
+#include "lsan/lsan_common.h"
+
+namespace __asan {
+
+// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits.
+// We use adaptive redzones: for larger allocation larger redzones are used.
+static u32 RZLog2Size(u32 rz_log) {
+ CHECK_LT(rz_log, 8);
+ return 16 << rz_log;
+}
+
+static u32 RZSize2Log(u32 rz_size) {
+ CHECK_GE(rz_size, 16);
+ CHECK_LE(rz_size, 2048);
+ CHECK(IsPowerOfTwo(rz_size));
+ u32 res = Log2(rz_size) - 4;
+ CHECK_EQ(rz_size, RZLog2Size(res));
+ return res;
+}
+
+static AsanAllocator &get_allocator();
+
+// The memory chunk allocated from the underlying allocator looks like this:
+// L L L L L L H H U U U U U U R R
+// L -- left redzone words (0 or more bytes)
+// H -- ChunkHeader (16 bytes), which is also a part of the left redzone.
+// U -- user memory.
+// R -- right redzone (0 or more bytes)
+// ChunkBase consists of ChunkHeader and other bytes that overlap with user
+// memory.
+
+// If the left redzone is greater than the ChunkHeader size we store a magic
+// value in the first uptr word of the memory block and store the address of
+// ChunkBase in the next uptr.
+// M B L L L L L L L L L H H U U U U U U
+// | ^
+// ---------------------|
+// M -- magic value kAllocBegMagic
+// B -- address of ChunkHeader pointing to the first 'H'
+static const uptr kAllocBegMagic = 0xCC6E96B9;
+
+struct ChunkHeader {
+ // 1-st 8 bytes.
+ u32 chunk_state : 8; // Must be first.
+ u32 alloc_tid : 24;
+
+ u32 free_tid : 24;
+ u32 from_memalign : 1;
+ u32 alloc_type : 2;
+ u32 rz_log : 3;
+ u32 lsan_tag : 2;
+ // 2-nd 8 bytes
+ // This field is used for small sizes. For large sizes it is equal to
+ // SizeClassMap::kMaxSize and the actual size is stored in the
+ // SecondaryAllocator's metadata.
+ u32 user_requested_size;
+ u32 alloc_context_id;
+};
+
+struct ChunkBase : ChunkHeader {
+ // Header2, intersects with user memory.
+ u32 free_context_id;
+};
+
+static const uptr kChunkHeaderSize = sizeof(ChunkHeader);
+static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize;
+COMPILER_CHECK(kChunkHeaderSize == 16);
+COMPILER_CHECK(kChunkHeader2Size <= 16);
+
+// Every chunk of memory allocated by this allocator can be in one of 3 states:
+// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
+// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
+// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone.
+enum {
+ CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it.
+ CHUNK_ALLOCATED = 2,
+ CHUNK_QUARANTINE = 3
+};
+
+struct AsanChunk: ChunkBase {
+ uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
+ uptr UsedSize(bool locked_version = false) {
+ if (user_requested_size != SizeClassMap::kMaxSize)
+ return user_requested_size;
+ return *reinterpret_cast<uptr *>(
+ get_allocator().GetMetaData(AllocBeg(locked_version)));
+ }
+ void *AllocBeg(bool locked_version = false) {
+ if (from_memalign) {
+ if (locked_version)
+ return get_allocator().GetBlockBeginFastLocked(
+ reinterpret_cast<void *>(this));
+ return get_allocator().GetBlockBegin(reinterpret_cast<void *>(this));
+ }
+ return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
+ }
+ bool AddrIsInside(uptr addr, bool locked_version = false) {
+ return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version));
+ }
+};
+
+struct QuarantineCallback {
+ explicit QuarantineCallback(AllocatorCache *cache)
+ : cache_(cache) {
+ }
+
+ void Recycle(AsanChunk *m) {
+ CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
+ atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed);
+ CHECK_NE(m->alloc_tid, kInvalidTid);
+ CHECK_NE(m->free_tid, kInvalidTid);
+ PoisonShadow(m->Beg(),
+ RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
+ kAsanHeapLeftRedzoneMagic);
+ void *p = reinterpret_cast<void *>(m->AllocBeg());
+ if (p != m) {
+ uptr *alloc_magic = reinterpret_cast<uptr *>(p);
+ CHECK_EQ(alloc_magic[0], kAllocBegMagic);
+ // Clear the magic value, as allocator internals may overwrite the
+ // contents of deallocated chunk, confusing GetAsanChunk lookup.
+ alloc_magic[0] = 0;
+ CHECK_EQ(alloc_magic[1], reinterpret_cast<uptr>(m));
+ }
+
+ // Statistics.
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.real_frees++;
+ thread_stats.really_freed += m->UsedSize();
+
+ get_allocator().Deallocate(cache_, p);
+ }
+
+ void *Allocate(uptr size) {
+ return get_allocator().Allocate(cache_, size, 1, false);
+ }
+
+ void Deallocate(void *p) {
+ get_allocator().Deallocate(cache_, p);
+ }
+
+ AllocatorCache *cache_;
+};
+
+typedef Quarantine<QuarantineCallback, AsanChunk> AsanQuarantine;
+typedef AsanQuarantine::Cache QuarantineCache;
+
+void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const {
+ PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic);
+ // Statistics.
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.mmaps++;
+ thread_stats.mmaped += size;
+}
+void AsanMapUnmapCallback::OnUnmap(uptr p, uptr size) const {
+ PoisonShadow(p, size, 0);
+ // We are about to unmap a chunk of user memory.
+ // Mark the corresponding shadow memory as not needed.
+ FlushUnneededASanShadowMemory(p, size);
+ // Statistics.
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.munmaps++;
+ thread_stats.munmaped += size;
+}
+
+// We can not use THREADLOCAL because it is not supported on some of the
+// platforms we care about (OSX 10.6, Android).
+// static THREADLOCAL AllocatorCache cache;
+AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) {
+ CHECK(ms);
+ return &ms->allocator_cache;
+}
+
+QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
+ CHECK(ms);
+ CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache));
+ return reinterpret_cast<QuarantineCache *>(ms->quarantine_cache);
+}
+
+void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) {
+ quarantine_size_mb = f->quarantine_size_mb;
+ min_redzone = f->redzone;
+ max_redzone = f->max_redzone;
+ may_return_null = cf->allocator_may_return_null;
+ alloc_dealloc_mismatch = f->alloc_dealloc_mismatch;
+}
+
+void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
+ f->quarantine_size_mb = quarantine_size_mb;
+ f->redzone = min_redzone;
+ f->max_redzone = max_redzone;
+ cf->allocator_may_return_null = may_return_null;
+ f->alloc_dealloc_mismatch = alloc_dealloc_mismatch;
+}
+
+struct Allocator {
+ static const uptr kMaxAllowedMallocSize =
+ FIRST_32_SECOND_64(3UL << 30, 1UL << 40);
+ static const uptr kMaxThreadLocalQuarantine =
+ FIRST_32_SECOND_64(1 << 18, 1 << 20);
+
+ AsanAllocator allocator;
+ AsanQuarantine quarantine;
+ StaticSpinMutex fallback_mutex;
+ AllocatorCache fallback_allocator_cache;
+ QuarantineCache fallback_quarantine_cache;
+
+ // ------------------- Options --------------------------
+ atomic_uint16_t min_redzone;
+ atomic_uint16_t max_redzone;
+ atomic_uint8_t alloc_dealloc_mismatch;
+
+ // ------------------- Initialization ------------------------
+ explicit Allocator(LinkerInitialized)
+ : quarantine(LINKER_INITIALIZED),
+ fallback_quarantine_cache(LINKER_INITIALIZED) {}
+
+ void CheckOptions(const AllocatorOptions &options) const {
+ CHECK_GE(options.min_redzone, 16);
+ CHECK_GE(options.max_redzone, options.min_redzone);
+ CHECK_LE(options.max_redzone, 2048);
+ CHECK(IsPowerOfTwo(options.min_redzone));
+ CHECK(IsPowerOfTwo(options.max_redzone));
+ }
+
+ void SharedInitCode(const AllocatorOptions &options) {
+ CheckOptions(options);
+ quarantine.Init((uptr)options.quarantine_size_mb << 20,
+ kMaxThreadLocalQuarantine);
+ atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch,
+ memory_order_release);
+ atomic_store(&min_redzone, options.min_redzone, memory_order_release);
+ atomic_store(&max_redzone, options.max_redzone, memory_order_release);
+ }
+
+ void Initialize(const AllocatorOptions &options) {
+ allocator.Init(options.may_return_null);
+ SharedInitCode(options);
+ }
+
+ void ReInitialize(const AllocatorOptions &options) {
+ allocator.SetMayReturnNull(options.may_return_null);
+ SharedInitCode(options);
+ }
+
+ void GetOptions(AllocatorOptions *options) const {
+ options->quarantine_size_mb = quarantine.GetSize() >> 20;
+ options->min_redzone = atomic_load(&min_redzone, memory_order_acquire);
+ options->max_redzone = atomic_load(&max_redzone, memory_order_acquire);
+ options->may_return_null = allocator.MayReturnNull();
+ options->alloc_dealloc_mismatch =
+ atomic_load(&alloc_dealloc_mismatch, memory_order_acquire);
+ }
+
+ // -------------------- Helper methods. -------------------------
+ uptr ComputeRZLog(uptr user_requested_size) {
+ u32 rz_log =
+ user_requested_size <= 64 - 16 ? 0 :
+ user_requested_size <= 128 - 32 ? 1 :
+ user_requested_size <= 512 - 64 ? 2 :
+ user_requested_size <= 4096 - 128 ? 3 :
+ user_requested_size <= (1 << 14) - 256 ? 4 :
+ user_requested_size <= (1 << 15) - 512 ? 5 :
+ user_requested_size <= (1 << 16) - 1024 ? 6 : 7;
+ u32 min_rz = atomic_load(&min_redzone, memory_order_acquire);
+ u32 max_rz = atomic_load(&max_redzone, memory_order_acquire);
+ return Min(Max(rz_log, RZSize2Log(min_rz)), RZSize2Log(max_rz));
+ }
+
+ // We have an address between two chunks, and we want to report just one.
+ AsanChunk *ChooseChunk(uptr addr, AsanChunk *left_chunk,
+ AsanChunk *right_chunk) {
+ // Prefer an allocated chunk over freed chunk and freed chunk
+ // over available chunk.
+ if (left_chunk->chunk_state != right_chunk->chunk_state) {
+ if (left_chunk->chunk_state == CHUNK_ALLOCATED)
+ return left_chunk;
+ if (right_chunk->chunk_state == CHUNK_ALLOCATED)
+ return right_chunk;
+ if (left_chunk->chunk_state == CHUNK_QUARANTINE)
+ return left_chunk;
+ if (right_chunk->chunk_state == CHUNK_QUARANTINE)
+ return right_chunk;
+ }
+ // Same chunk_state: choose based on offset.
+ sptr l_offset = 0, r_offset = 0;
+ CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset));
+ CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset));
+ if (l_offset < r_offset)
+ return left_chunk;
+ return right_chunk;
+ }
+
+ // -------------------- Allocation/Deallocation routines ---------------
+ void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack,
+ AllocType alloc_type, bool can_fill) {
+ if (UNLIKELY(!asan_inited))
+ AsanInitFromRtl();
+ Flags &fl = *flags();
+ CHECK(stack);
+ const uptr min_alignment = SHADOW_GRANULARITY;
+ if (alignment < min_alignment)
+ alignment = min_alignment;
+ if (size == 0) {
+ // We'd be happy to avoid allocating memory for zero-size requests, but
+ // some programs/tests depend on this behavior and assume that malloc
+ // would not return NULL even for zero-size allocations. Moreover, it
+ // looks like operator new should never return NULL, and results of
+ // consecutive "new" calls must be different even if the allocated size
+ // is zero.
+ size = 1;
+ }
+ CHECK(IsPowerOfTwo(alignment));
+ uptr rz_log = ComputeRZLog(size);
+ uptr rz_size = RZLog2Size(rz_log);
+ uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment);
+ uptr needed_size = rounded_size + rz_size;
+ if (alignment > min_alignment)
+ needed_size += alignment;
+ bool using_primary_allocator = true;
+ // If we are allocating from the secondary allocator, there will be no
+ // automatic right redzone, so add the right redzone manually.
+ if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) {
+ needed_size += rz_size;
+ using_primary_allocator = false;
+ }
+ CHECK(IsAligned(needed_size, min_alignment));
+ if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
+ Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n",
+ (void*)size);
+ return allocator.ReturnNullOrDie();
+ }
+
+ AsanThread *t = GetCurrentThread();
+ void *allocated;
+ bool check_rss_limit = true;
+ if (t) {
+ AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
+ allocated =
+ allocator.Allocate(cache, needed_size, 8, false, check_rss_limit);
+ } else {
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *cache = &fallback_allocator_cache;
+ allocated =
+ allocator.Allocate(cache, needed_size, 8, false, check_rss_limit);
+ }
+
+ if (!allocated)
+ return allocator.ReturnNullOrDie();
+
+ if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) {
+ // Heap poisoning is enabled, but the allocator provides an unpoisoned
+ // chunk. This is possible if CanPoisonMemory() was false for some
+ // time, for example, due to flags()->start_disabled.
+ // Anyway, poison the block before using it for anything else.
+ uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated);
+ PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic);
+ }
+
+ uptr alloc_beg = reinterpret_cast<uptr>(allocated);
+ uptr alloc_end = alloc_beg + needed_size;
+ uptr beg_plus_redzone = alloc_beg + rz_size;
+ uptr user_beg = beg_plus_redzone;
+ if (!IsAligned(user_beg, alignment))
+ user_beg = RoundUpTo(user_beg, alignment);
+ uptr user_end = user_beg + size;
+ CHECK_LE(user_end, alloc_end);
+ uptr chunk_beg = user_beg - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+ m->alloc_type = alloc_type;
+ m->rz_log = rz_log;
+ u32 alloc_tid = t ? t->tid() : 0;
+ m->alloc_tid = alloc_tid;
+ CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield?
+ m->free_tid = kInvalidTid;
+ m->from_memalign = user_beg != beg_plus_redzone;
+ if (alloc_beg != chunk_beg) {
+ CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg);
+ reinterpret_cast<uptr *>(alloc_beg)[0] = kAllocBegMagic;
+ reinterpret_cast<uptr *>(alloc_beg)[1] = chunk_beg;
+ }
+ if (using_primary_allocator) {
+ CHECK(size);
+ m->user_requested_size = size;
+ CHECK(allocator.FromPrimary(allocated));
+ } else {
+ CHECK(!allocator.FromPrimary(allocated));
+ m->user_requested_size = SizeClassMap::kMaxSize;
+ uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(allocated));
+ meta[0] = size;
+ meta[1] = chunk_beg;
+ }
+
+ m->alloc_context_id = StackDepotPut(*stack);
+
+ uptr size_rounded_down_to_granularity =
+ RoundDownTo(size, SHADOW_GRANULARITY);
+ // Unpoison the bulk of the memory region.
+ if (size_rounded_down_to_granularity)
+ PoisonShadow(user_beg, size_rounded_down_to_granularity, 0);
+ // Deal with the end of the region if size is not aligned to granularity.
+ if (size != size_rounded_down_to_granularity && CanPoisonMemory()) {
+ u8 *shadow =
+ (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity);
+ *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0;
+ }
+
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.mallocs++;
+ thread_stats.malloced += size;
+ thread_stats.malloced_redzones += needed_size - size;
+ if (needed_size > SizeClassMap::kMaxSize)
+ thread_stats.malloc_large++;
+ else
+ thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++;
+
+ void *res = reinterpret_cast<void *>(user_beg);
+ if (can_fill && fl.max_malloc_fill_size) {
+ uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size);
+ REAL(memset)(res, fl.malloc_fill_byte, fill_size);
+ }
+#if CAN_SANITIZE_LEAKS
+ m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored
+ : __lsan::kDirectlyLeaked;
+#endif
+ // Must be the last mutation of metadata in this function.
+ atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release);
+ ASAN_MALLOC_HOOK(res, size);
+ return res;
+ }
+
+ void AtomicallySetQuarantineFlag(AsanChunk *m, void *ptr,
+ BufferedStackTrace *stack) {
+ u8 old_chunk_state = CHUNK_ALLOCATED;
+ // Flip the chunk_state atomically to avoid race on double-free.
+ if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state,
+ CHUNK_QUARANTINE, memory_order_acquire))
+ ReportInvalidFree(ptr, old_chunk_state, stack);
+ CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state);
+ }
+
+ // Expects the chunk to already be marked as quarantined by using
+ // AtomicallySetQuarantineFlag.
+ void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack,
+ AllocType alloc_type) {
+ CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
+
+ if (m->alloc_type != alloc_type) {
+ if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) {
+ ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type,
+ (AllocType)alloc_type);
+ }
+ }
+
+ CHECK_GE(m->alloc_tid, 0);
+ if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area.
+ CHECK_EQ(m->free_tid, kInvalidTid);
+ AsanThread *t = GetCurrentThread();
+ m->free_tid = t ? t->tid() : 0;
+ m->free_context_id = StackDepotPut(*stack);
+ // Poison the region.
+ PoisonShadow(m->Beg(),
+ RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
+ kAsanHeapFreeMagic);
+
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.frees++;
+ thread_stats.freed += m->UsedSize();
+
+ // Push into quarantine.
+ if (t) {
+ AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
+ AllocatorCache *ac = GetAllocatorCache(ms);
+ quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac), m,
+ m->UsedSize());
+ } else {
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *ac = &fallback_allocator_cache;
+ quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac), m,
+ m->UsedSize());
+ }
+ }
+
+ void Deallocate(void *ptr, uptr delete_size, BufferedStackTrace *stack,
+ AllocType alloc_type) {
+ uptr p = reinterpret_cast<uptr>(ptr);
+ if (p == 0) return;
+
+ uptr chunk_beg = p - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+ if (delete_size && flags()->new_delete_type_mismatch &&
+ delete_size != m->UsedSize()) {
+ ReportNewDeleteSizeMismatch(p, delete_size, stack);
+ }
+ ASAN_FREE_HOOK(ptr);
+ // Must mark the chunk as quarantined before any changes to its metadata.
+ AtomicallySetQuarantineFlag(m, ptr, stack);
+ QuarantineChunk(m, ptr, stack, alloc_type);
+ }
+
+ void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) {
+ CHECK(old_ptr && new_size);
+ uptr p = reinterpret_cast<uptr>(old_ptr);
+ uptr chunk_beg = p - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.reallocs++;
+ thread_stats.realloced += new_size;
+
+ void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true);
+ if (new_ptr) {
+ u8 chunk_state = m->chunk_state;
+ if (chunk_state != CHUNK_ALLOCATED)
+ ReportInvalidFree(old_ptr, chunk_state, stack);
+ CHECK_NE(REAL(memcpy), nullptr);
+ uptr memcpy_size = Min(new_size, m->UsedSize());
+ // If realloc() races with free(), we may start copying freed memory.
+ // However, we will report racy double-free later anyway.
+ REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
+ Deallocate(old_ptr, 0, stack, FROM_MALLOC);
+ }
+ return new_ptr;
+ }
+
+ void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
+ if (CallocShouldReturnNullDueToOverflow(size, nmemb))
+ return allocator.ReturnNullOrDie();
+ void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
+ // If the memory comes from the secondary allocator no need to clear it
+ // as it comes directly from mmap.
+ if (ptr && allocator.FromPrimary(ptr))
+ REAL(memset)(ptr, 0, nmemb * size);
+ return ptr;
+ }
+
+ void ReportInvalidFree(void *ptr, u8 chunk_state, BufferedStackTrace *stack) {
+ if (chunk_state == CHUNK_QUARANTINE)
+ ReportDoubleFree((uptr)ptr, stack);
+ else
+ ReportFreeNotMalloced((uptr)ptr, stack);
+ }
+
+ void CommitBack(AsanThreadLocalMallocStorage *ms) {
+ AllocatorCache *ac = GetAllocatorCache(ms);
+ quarantine.Drain(GetQuarantineCache(ms), QuarantineCallback(ac));
+ allocator.SwallowCache(ac);
+ }
+
+ // -------------------------- Chunk lookup ----------------------
+
+ // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg).
+ AsanChunk *GetAsanChunk(void *alloc_beg) {
+ if (!alloc_beg) return nullptr;
+ if (!allocator.FromPrimary(alloc_beg)) {
+ uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(alloc_beg));
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]);
+ return m;
+ }
+ uptr *alloc_magic = reinterpret_cast<uptr *>(alloc_beg);
+ if (alloc_magic[0] == kAllocBegMagic)
+ return reinterpret_cast<AsanChunk *>(alloc_magic[1]);
+ return reinterpret_cast<AsanChunk *>(alloc_beg);
+ }
+
+ AsanChunk *GetAsanChunkByAddr(uptr p) {
+ void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p));
+ return GetAsanChunk(alloc_beg);
+ }
+
+ // Allocator must be locked when this function is called.
+ AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) {
+ void *alloc_beg =
+ allocator.GetBlockBeginFastLocked(reinterpret_cast<void *>(p));
+ return GetAsanChunk(alloc_beg);
+ }
+
+ uptr AllocationSize(uptr p) {
+ AsanChunk *m = GetAsanChunkByAddr(p);
+ if (!m) return 0;
+ if (m->chunk_state != CHUNK_ALLOCATED) return 0;
+ if (m->Beg() != p) return 0;
+ return m->UsedSize();
+ }
+
+ AsanChunkView FindHeapChunkByAddress(uptr addr) {
+ AsanChunk *m1 = GetAsanChunkByAddr(addr);
+ if (!m1) return AsanChunkView(m1);
+ sptr offset = 0;
+ if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) {
+ // The address is in the chunk's left redzone, so maybe it is actually
+ // a right buffer overflow from the other chunk to the left.
+ // Search a bit to the left to see if there is another chunk.
+ AsanChunk *m2 = nullptr;
+ for (uptr l = 1; l < GetPageSizeCached(); l++) {
+ m2 = GetAsanChunkByAddr(addr - l);
+ if (m2 == m1) continue; // Still the same chunk.
+ break;
+ }
+ if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset))
+ m1 = ChooseChunk(addr, m2, m1);
+ }
+ return AsanChunkView(m1);
+ }
+
+ void PrintStats() {
+ allocator.PrintStats();
+ }
+
+ void ForceLock() {
+ allocator.ForceLock();
+ fallback_mutex.Lock();
+ }
+
+ void ForceUnlock() {
+ fallback_mutex.Unlock();
+ allocator.ForceUnlock();
+ }
+};
+
+static Allocator instance(LINKER_INITIALIZED);
+
+static AsanAllocator &get_allocator() {
+ return instance.allocator;
+}
+
+bool AsanChunkView::IsValid() {
+ return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE;
+}
+uptr AsanChunkView::Beg() { return chunk_->Beg(); }
+uptr AsanChunkView::End() { return Beg() + UsedSize(); }
+uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
+uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
+uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
+
+static StackTrace GetStackTraceFromId(u32 id) {
+ CHECK(id);
+ StackTrace res = StackDepotGet(id);
+ CHECK(res.trace);
+ return res;
+}
+
+StackTrace AsanChunkView::GetAllocStack() {
+ return GetStackTraceFromId(chunk_->alloc_context_id);
+}
+
+StackTrace AsanChunkView::GetFreeStack() {
+ return GetStackTraceFromId(chunk_->free_context_id);
+}
+
+void InitializeAllocator(const AllocatorOptions &options) {
+ instance.Initialize(options);
+}
+
+void ReInitializeAllocator(const AllocatorOptions &options) {
+ instance.ReInitialize(options);
+}
+
+void GetAllocatorOptions(AllocatorOptions *options) {
+ instance.GetOptions(options);
+}
+
+AsanChunkView FindHeapChunkByAddress(uptr addr) {
+ return instance.FindHeapChunkByAddress(addr);
+}
+
+void AsanThreadLocalMallocStorage::CommitBack() {
+ instance.CommitBack(this);
+}
+
+void PrintInternalAllocatorStats() {
+ instance.PrintStats();
+}
+
+void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
+ AllocType alloc_type) {
+ return instance.Allocate(size, alignment, stack, alloc_type, true);
+}
+
+void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) {
+ instance.Deallocate(ptr, 0, stack, alloc_type);
+}
+
+void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack,
+ AllocType alloc_type) {
+ instance.Deallocate(ptr, size, stack, alloc_type);
+}
+
+void *asan_malloc(uptr size, BufferedStackTrace *stack) {
+ return instance.Allocate(size, 8, stack, FROM_MALLOC, true);
+}
+
+void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
+ return instance.Calloc(nmemb, size, stack);
+}
+
+void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) {
+ if (!p)
+ return instance.Allocate(size, 8, stack, FROM_MALLOC, true);
+ if (size == 0) {
+ instance.Deallocate(p, 0, stack, FROM_MALLOC);
+ return nullptr;
+ }
+ return instance.Reallocate(p, size, stack);
+}
+
+void *asan_valloc(uptr size, BufferedStackTrace *stack) {
+ return instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true);
+}
+
+void *asan_pvalloc(uptr size, BufferedStackTrace *stack) {
+ uptr PageSize = GetPageSizeCached();
+ size = RoundUpTo(size, PageSize);
+ if (size == 0) {
+ // pvalloc(0) should allocate one page.
+ size = PageSize;
+ }
+ return instance.Allocate(size, PageSize, stack, FROM_MALLOC, true);
+}
+
+int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
+ BufferedStackTrace *stack) {
+ void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC, true);
+ CHECK(IsAligned((uptr)ptr, alignment));
+ *memptr = ptr;
+ return 0;
+}
+
+uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) {
+ if (!ptr) return 0;
+ uptr usable_size = instance.AllocationSize(reinterpret_cast<uptr>(ptr));
+ if (flags()->check_malloc_usable_size && (usable_size == 0)) {
+ GET_STACK_TRACE_FATAL(pc, bp);
+ ReportMallocUsableSizeNotOwned((uptr)ptr, &stack);
+ }
+ return usable_size;
+}
+
+uptr asan_mz_size(const void *ptr) {
+ return instance.AllocationSize(reinterpret_cast<uptr>(ptr));
+}
+
+void asan_mz_force_lock() {
+ instance.ForceLock();
+}
+
+void asan_mz_force_unlock() {
+ instance.ForceUnlock();
+}
+
+void AsanSoftRssLimitExceededCallback(bool exceeded) {
+ instance.allocator.SetRssLimitIsExceeded(exceeded);
+}
+
+} // namespace __asan
+
+// --- Implementation of LSan-specific functions --- {{{1
+namespace __lsan {
+void LockAllocator() {
+ __asan::get_allocator().ForceLock();
+}
+
+void UnlockAllocator() {
+ __asan::get_allocator().ForceUnlock();
+}
+
+void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
+ *begin = (uptr)&__asan::get_allocator();
+ *end = *begin + sizeof(__asan::get_allocator());
+}
+
+uptr PointsIntoChunk(void* p) {
+ uptr addr = reinterpret_cast<uptr>(p);
+ __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(addr);
+ if (!m) return 0;
+ uptr chunk = m->Beg();
+ if (m->chunk_state != __asan::CHUNK_ALLOCATED)
+ return 0;
+ if (m->AddrIsInside(addr, /*locked_version=*/true))
+ return chunk;
+ if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true),
+ addr))
+ return chunk;
+ return 0;
+}
+
+uptr GetUserBegin(uptr chunk) {
+ __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk);
+ CHECK(m);
+ return m->Beg();
+}
+
+LsanMetadata::LsanMetadata(uptr chunk) {
+ metadata_ = reinterpret_cast<void *>(chunk - __asan::kChunkHeaderSize);
+}
+
+bool LsanMetadata::allocated() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return m->chunk_state == __asan::CHUNK_ALLOCATED;
+}
+
+ChunkTag LsanMetadata::tag() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return static_cast<ChunkTag>(m->lsan_tag);
+}
+
+void LsanMetadata::set_tag(ChunkTag value) {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ m->lsan_tag = value;
+}
+
+uptr LsanMetadata::requested_size() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return m->UsedSize(/*locked_version=*/true);
+}
+
+u32 LsanMetadata::stack_trace_id() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return m->alloc_context_id;
+}
+
+void ForEachChunk(ForEachChunkCallback callback, void *arg) {
+ __asan::get_allocator().ForEachChunk(callback, arg);
+}
+
+IgnoreObjectResult IgnoreObjectLocked(const void *p) {
+ uptr addr = reinterpret_cast<uptr>(p);
+ __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddr(addr);
+ if (!m) return kIgnoreObjectInvalid;
+ if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) {
+ if (m->lsan_tag == kIgnored)
+ return kIgnoreObjectAlreadyIgnored;
+ m->lsan_tag = __lsan::kIgnored;
+ return kIgnoreObjectSuccess;
+ } else {
+ return kIgnoreObjectInvalid;
+ }
+}
+} // namespace __lsan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+// ASan allocator doesn't reserve extra bytes, so normally we would
+// just return "size". We don't want to expose our redzone sizes, etc here.
+uptr __sanitizer_get_estimated_allocated_size(uptr size) {
+ return size;
+}
+
+int __sanitizer_get_ownership(const void *p) {
+ uptr ptr = reinterpret_cast<uptr>(p);
+ return instance.AllocationSize(ptr) > 0;
+}
+
+uptr __sanitizer_get_allocated_size(const void *p) {
+ if (!p) return 0;
+ uptr ptr = reinterpret_cast<uptr>(p);
+ uptr allocated_size = instance.AllocationSize(ptr);
+ // Die if p is not malloced or if it is already freed.
+ if (allocated_size == 0) {
+ GET_STACK_TRACE_FATAL_HERE;
+ ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack);
+ }
+ return allocated_size;
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+// Provide default (no-op) implementation of malloc hooks.
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_malloc_hook(void *ptr, uptr size) {
+ (void)ptr;
+ (void)size;
+}
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_free_hook(void *ptr) {
+ (void)ptr;
+}
+} // extern "C"
+#endif
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// ASan-private header for asan_allocator2.cc.
+// ASan-private header for asan_allocator.cc.
//===----------------------------------------------------------------------===//
#ifndef ASAN_ALLOCATOR_H
#define ASAN_ALLOCATOR_H
+#include "asan_flags.h"
#include "asan_internal.h"
#include "asan_interceptors.h"
+#include "sanitizer_common/sanitizer_allocator.h"
#include "sanitizer_common/sanitizer_list.h"
namespace __asan {
FROM_NEW_BR = 3 // Memory block came from operator new [ ]
};
-static const uptr kNumberOfSizeClasses = 255;
struct AsanChunk;
-void InitializeAllocator();
+struct AllocatorOptions {
+ u32 quarantine_size_mb;
+ u16 min_redzone;
+ u16 max_redzone;
+ u8 may_return_null;
+ u8 alloc_dealloc_mismatch;
+
+ void SetFrom(const Flags *f, const CommonFlags *cf);
+ void CopyTo(Flags *f, CommonFlags *cf);
+};
+
+void InitializeAllocator(const AllocatorOptions &options);
+void ReInitializeAllocator(const AllocatorOptions &options);
+void GetAllocatorOptions(AllocatorOptions *options);
class AsanChunkView {
public:
uptr UsedSize(); // Size requested by the user.
uptr AllocTid();
uptr FreeTid();
- void GetAllocStack(StackTrace *stack);
- void GetFreeStack(StackTrace *stack);
+ bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; }
+ StackTrace GetAllocStack();
+ StackTrace GetFreeStack();
bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
if (addr >= Beg() && (addr + access_size) <= End()) {
*offset = addr - Beg();
uptr size_;
};
+struct AsanMapUnmapCallback {
+ void OnMap(uptr p, uptr size) const;
+ void OnUnmap(uptr p, uptr size) const;
+};
+
+#if SANITIZER_CAN_USE_ALLOCATOR64
+# if defined(__powerpc64__)
+const uptr kAllocatorSpace = 0xa0000000000ULL;
+const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
+# elif defined(__aarch64__)
+// AArch64/SANITIZIER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA
+// so no need to different values for different VMA.
+const uptr kAllocatorSpace = 0x10000000000ULL;
+const uptr kAllocatorSize = 0x10000000000ULL; // 3T.
+# else
+const uptr kAllocatorSpace = 0x600000000000ULL;
+const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
+# endif
+typedef DefaultSizeClassMap SizeClassMap;
+typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/,
+ SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
+#else // Fallback to SizeClassAllocator32.
+static const uptr kRegionSizeLog = 20;
+static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
+# if SANITIZER_WORDSIZE == 32
+typedef FlatByteMap<kNumRegions> ByteMap;
+# elif SANITIZER_WORDSIZE == 64
+typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
+# endif
+typedef CompactSizeClassMap SizeClassMap;
+typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, 16,
+ SizeClassMap, kRegionSizeLog,
+ ByteMap,
+ AsanMapUnmapCallback> PrimaryAllocator;
+#endif // SANITIZER_CAN_USE_ALLOCATOR64
+
+static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses;
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+ SecondaryAllocator> AsanAllocator;
+
+
struct AsanThreadLocalMallocStorage {
uptr quarantine_cache[16];
- uptr allocator2_cache[96 * (512 * 8 + 16)]; // Opaque.
+ AllocatorCache allocator_cache;
void CommitBack();
private:
// These objects are allocated via mmap() and are zero-initialized.
AsanThreadLocalMallocStorage() {}
};
-void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
AllocType alloc_type);
-void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type);
+void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type);
+void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack,
+ AllocType alloc_type);
-void *asan_malloc(uptr size, StackTrace *stack);
-void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack);
-void *asan_realloc(void *p, uptr size, StackTrace *stack);
-void *asan_valloc(uptr size, StackTrace *stack);
-void *asan_pvalloc(uptr size, StackTrace *stack);
+void *asan_malloc(uptr size, BufferedStackTrace *stack);
+void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack);
+void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack);
+void *asan_valloc(uptr size, BufferedStackTrace *stack);
+void *asan_pvalloc(uptr size, BufferedStackTrace *stack);
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
- StackTrace *stack);
+ BufferedStackTrace *stack);
uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp);
uptr asan_mz_size(const void *ptr);
void asan_mz_force_unlock();
void PrintInternalAllocatorStats();
+void AsanSoftRssLimitExceededCallback(bool exceeded);
} // namespace __asan
#endif // ASAN_ALLOCATOR_H
+++ /dev/null
-//===-- asan_allocator2.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Implementation of ASan's memory allocator, 2-nd version.
-// This variant uses the allocator from sanitizer_common, i.e. the one shared
-// with ThreadSanitizer and MemorySanitizer.
-//
-//===----------------------------------------------------------------------===//
-#include "asan_allocator.h"
-
-#include "asan_mapping.h"
-#include "asan_poisoning.h"
-#include "asan_report.h"
-#include "asan_thread.h"
-#include "sanitizer_common/sanitizer_allocator.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "sanitizer_common/sanitizer_list.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_quarantine.h"
-#include "lsan/lsan_common.h"
-
-namespace __asan {
-
-struct AsanMapUnmapCallback {
- void OnMap(uptr p, uptr size) const {
- PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic);
- // Statistics.
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.mmaps++;
- thread_stats.mmaped += size;
- }
- void OnUnmap(uptr p, uptr size) const {
- PoisonShadow(p, size, 0);
- // We are about to unmap a chunk of user memory.
- // Mark the corresponding shadow memory as not needed.
- // Since asan's mapping is compacting, the shadow chunk may be
- // not page-aligned, so we only flush the page-aligned portion.
- uptr page_size = GetPageSizeCached();
- uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size);
- uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size);
- FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
- // Statistics.
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.munmaps++;
- thread_stats.munmaped += size;
- }
-};
-
-#if SANITIZER_WORDSIZE == 64
-#if defined(__powerpc64__)
-const uptr kAllocatorSpace = 0xa0000000000ULL;
-const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
-#else
-const uptr kAllocatorSpace = 0x600000000000ULL;
-const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
-#endif
-typedef DefaultSizeClassMap SizeClassMap;
-typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/,
- SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
-#elif SANITIZER_WORDSIZE == 32
-static const u64 kAddressSpaceSize = 1ULL << 32;
-typedef CompactSizeClassMap SizeClassMap;
-static const uptr kRegionSizeLog = 20;
-static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog;
-typedef SizeClassAllocator32<0, kAddressSpaceSize, 16,
- SizeClassMap, kRegionSizeLog,
- FlatByteMap<kFlatByteMapSize>,
- AsanMapUnmapCallback> PrimaryAllocator;
-#endif
-
-typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
-typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
-typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
- SecondaryAllocator> Allocator;
-
-// We can not use THREADLOCAL because it is not supported on some of the
-// platforms we care about (OSX 10.6, Android).
-// static THREADLOCAL AllocatorCache cache;
-AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) {
- CHECK(ms);
- CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator2_cache));
- return reinterpret_cast<AllocatorCache *>(ms->allocator2_cache);
-}
-
-static Allocator allocator;
-
-static const uptr kMaxAllowedMallocSize =
- FIRST_32_SECOND_64(3UL << 30, 64UL << 30);
-
-static const uptr kMaxThreadLocalQuarantine =
- FIRST_32_SECOND_64(1 << 18, 1 << 20);
-
-// Every chunk of memory allocated by this allocator can be in one of 3 states:
-// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
-// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
-// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone.
-enum {
- CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it.
- CHUNK_ALLOCATED = 2,
- CHUNK_QUARANTINE = 3
-};
-
-// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits.
-// We use adaptive redzones: for larger allocation larger redzones are used.
-static u32 RZLog2Size(u32 rz_log) {
- CHECK_LT(rz_log, 8);
- return 16 << rz_log;
-}
-
-static u32 RZSize2Log(u32 rz_size) {
- CHECK_GE(rz_size, 16);
- CHECK_LE(rz_size, 2048);
- CHECK(IsPowerOfTwo(rz_size));
- u32 res = Log2(rz_size) - 4;
- CHECK_EQ(rz_size, RZLog2Size(res));
- return res;
-}
-
-static uptr ComputeRZLog(uptr user_requested_size) {
- u32 rz_log =
- user_requested_size <= 64 - 16 ? 0 :
- user_requested_size <= 128 - 32 ? 1 :
- user_requested_size <= 512 - 64 ? 2 :
- user_requested_size <= 4096 - 128 ? 3 :
- user_requested_size <= (1 << 14) - 256 ? 4 :
- user_requested_size <= (1 << 15) - 512 ? 5 :
- user_requested_size <= (1 << 16) - 1024 ? 6 : 7;
- return Max(rz_log, RZSize2Log(flags()->redzone));
-}
-
-// The memory chunk allocated from the underlying allocator looks like this:
-// L L L L L L H H U U U U U U R R
-// L -- left redzone words (0 or more bytes)
-// H -- ChunkHeader (16 bytes), which is also a part of the left redzone.
-// U -- user memory.
-// R -- right redzone (0 or more bytes)
-// ChunkBase consists of ChunkHeader and other bytes that overlap with user
-// memory.
-
-// If the left redzone is greater than the ChunkHeader size we store a magic
-// value in the first uptr word of the memory block and store the address of
-// ChunkBase in the next uptr.
-// M B L L L L L L L L L H H U U U U U U
-// | ^
-// ---------------------|
-// M -- magic value kAllocBegMagic
-// B -- address of ChunkHeader pointing to the first 'H'
-static const uptr kAllocBegMagic = 0xCC6E96B9;
-
-struct ChunkHeader {
- // 1-st 8 bytes.
- u32 chunk_state : 8; // Must be first.
- u32 alloc_tid : 24;
-
- u32 free_tid : 24;
- u32 from_memalign : 1;
- u32 alloc_type : 2;
- u32 rz_log : 3;
- u32 lsan_tag : 2;
- // 2-nd 8 bytes
- // This field is used for small sizes. For large sizes it is equal to
- // SizeClassMap::kMaxSize and the actual size is stored in the
- // SecondaryAllocator's metadata.
- u32 user_requested_size;
- u32 alloc_context_id;
-};
-
-struct ChunkBase : ChunkHeader {
- // Header2, intersects with user memory.
- u32 free_context_id;
-};
-
-static const uptr kChunkHeaderSize = sizeof(ChunkHeader);
-static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize;
-COMPILER_CHECK(kChunkHeaderSize == 16);
-COMPILER_CHECK(kChunkHeader2Size <= 16);
-
-struct AsanChunk: ChunkBase {
- uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
- uptr UsedSize(bool locked_version = false) {
- if (user_requested_size != SizeClassMap::kMaxSize)
- return user_requested_size;
- return *reinterpret_cast<uptr *>(
- allocator.GetMetaData(AllocBeg(locked_version)));
- }
- void *AllocBeg(bool locked_version = false) {
- if (from_memalign) {
- if (locked_version)
- return allocator.GetBlockBeginFastLocked(
- reinterpret_cast<void *>(this));
- return allocator.GetBlockBegin(reinterpret_cast<void *>(this));
- }
- return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
- }
- // If we don't use stack depot, we store the alloc/free stack traces
- // in the chunk itself.
- u32 *AllocStackBeg() {
- return (u32*)(Beg() - RZLog2Size(rz_log));
- }
- uptr AllocStackSize() {
- CHECK_LE(RZLog2Size(rz_log), kChunkHeaderSize);
- return (RZLog2Size(rz_log) - kChunkHeaderSize) / sizeof(u32);
- }
- u32 *FreeStackBeg() {
- return (u32*)(Beg() + kChunkHeader2Size);
- }
- uptr FreeStackSize() {
- if (user_requested_size < kChunkHeader2Size) return 0;
- uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY);
- return (available - kChunkHeader2Size) / sizeof(u32);
- }
- bool AddrIsInside(uptr addr, bool locked_version = false) {
- return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version));
- }
-};
-
-bool AsanChunkView::IsValid() {
- return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE;
-}
-uptr AsanChunkView::Beg() { return chunk_->Beg(); }
-uptr AsanChunkView::End() { return Beg() + UsedSize(); }
-uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
-uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
-uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
-
-static void GetStackTraceFromId(u32 id, StackTrace *stack) {
- CHECK(id);
- uptr size = 0;
- const uptr *trace = StackDepotGet(id, &size);
- CHECK(trace);
- stack->CopyFrom(trace, size);
-}
-
-void AsanChunkView::GetAllocStack(StackTrace *stack) {
- GetStackTraceFromId(chunk_->alloc_context_id, stack);
-}
-
-void AsanChunkView::GetFreeStack(StackTrace *stack) {
- GetStackTraceFromId(chunk_->free_context_id, stack);
-}
-
-struct QuarantineCallback;
-typedef Quarantine<QuarantineCallback, AsanChunk> AsanQuarantine;
-typedef AsanQuarantine::Cache QuarantineCache;
-static AsanQuarantine quarantine(LINKER_INITIALIZED);
-static QuarantineCache fallback_quarantine_cache(LINKER_INITIALIZED);
-static AllocatorCache fallback_allocator_cache;
-static SpinMutex fallback_mutex;
-
-QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
- CHECK(ms);
- CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache));
- return reinterpret_cast<QuarantineCache *>(ms->quarantine_cache);
-}
-
-struct QuarantineCallback {
- explicit QuarantineCallback(AllocatorCache *cache)
- : cache_(cache) {
- }
-
- void Recycle(AsanChunk *m) {
- CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
- atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed);
- CHECK_NE(m->alloc_tid, kInvalidTid);
- CHECK_NE(m->free_tid, kInvalidTid);
- PoisonShadow(m->Beg(),
- RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
- kAsanHeapLeftRedzoneMagic);
- void *p = reinterpret_cast<void *>(m->AllocBeg());
- if (p != m) {
- uptr *alloc_magic = reinterpret_cast<uptr *>(p);
- CHECK_EQ(alloc_magic[0], kAllocBegMagic);
- // Clear the magic value, as allocator internals may overwrite the
- // contents of deallocated chunk, confusing GetAsanChunk lookup.
- alloc_magic[0] = 0;
- CHECK_EQ(alloc_magic[1], reinterpret_cast<uptr>(m));
- }
-
- // Statistics.
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.real_frees++;
- thread_stats.really_freed += m->UsedSize();
-
- allocator.Deallocate(cache_, p);
- }
-
- void *Allocate(uptr size) {
- return allocator.Allocate(cache_, size, 1, false);
- }
-
- void Deallocate(void *p) {
- allocator.Deallocate(cache_, p);
- }
-
- AllocatorCache *cache_;
-};
-
-void InitializeAllocator() {
- allocator.Init();
- quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine);
-}
-
-static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
- AllocType alloc_type, bool can_fill) {
- if (!asan_inited)
- __asan_init();
- Flags &fl = *flags();
- CHECK(stack);
- const uptr min_alignment = SHADOW_GRANULARITY;
- if (alignment < min_alignment)
- alignment = min_alignment;
- if (size == 0) {
- // We'd be happy to avoid allocating memory for zero-size requests, but
- // some programs/tests depend on this behavior and assume that malloc would
- // not return NULL even for zero-size allocations. Moreover, it looks like
- // operator new should never return NULL, and results of consecutive "new"
- // calls must be different even if the allocated size is zero.
- size = 1;
- }
- CHECK(IsPowerOfTwo(alignment));
- uptr rz_log = ComputeRZLog(size);
- uptr rz_size = RZLog2Size(rz_log);
- uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment);
- uptr needed_size = rounded_size + rz_size;
- if (alignment > min_alignment)
- needed_size += alignment;
- bool using_primary_allocator = true;
- // If we are allocating from the secondary allocator, there will be no
- // automatic right redzone, so add the right redzone manually.
- if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) {
- needed_size += rz_size;
- using_primary_allocator = false;
- }
- CHECK(IsAligned(needed_size, min_alignment));
- if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
- Report("WARNING: AddressSanitizer failed to allocate %p bytes\n",
- (void*)size);
- return AllocatorReturnNull();
- }
-
- AsanThread *t = GetCurrentThread();
- void *allocated;
- if (t) {
- AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
- allocated = allocator.Allocate(cache, needed_size, 8, false);
- } else {
- SpinMutexLock l(&fallback_mutex);
- AllocatorCache *cache = &fallback_allocator_cache;
- allocated = allocator.Allocate(cache, needed_size, 8, false);
- }
- uptr alloc_beg = reinterpret_cast<uptr>(allocated);
- uptr alloc_end = alloc_beg + needed_size;
- uptr beg_plus_redzone = alloc_beg + rz_size;
- uptr user_beg = beg_plus_redzone;
- if (!IsAligned(user_beg, alignment))
- user_beg = RoundUpTo(user_beg, alignment);
- uptr user_end = user_beg + size;
- CHECK_LE(user_end, alloc_end);
- uptr chunk_beg = user_beg - kChunkHeaderSize;
- AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
- m->alloc_type = alloc_type;
- m->rz_log = rz_log;
- u32 alloc_tid = t ? t->tid() : 0;
- m->alloc_tid = alloc_tid;
- CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield?
- m->free_tid = kInvalidTid;
- m->from_memalign = user_beg != beg_plus_redzone;
- if (alloc_beg != chunk_beg) {
- CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg);
- reinterpret_cast<uptr *>(alloc_beg)[0] = kAllocBegMagic;
- reinterpret_cast<uptr *>(alloc_beg)[1] = chunk_beg;
- }
- if (using_primary_allocator) {
- CHECK(size);
- m->user_requested_size = size;
- CHECK(allocator.FromPrimary(allocated));
- } else {
- CHECK(!allocator.FromPrimary(allocated));
- m->user_requested_size = SizeClassMap::kMaxSize;
- uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(allocated));
- meta[0] = size;
- meta[1] = chunk_beg;
- }
-
- m->alloc_context_id = StackDepotPut(stack->trace, stack->size);
-
- uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY);
- // Unpoison the bulk of the memory region.
- if (size_rounded_down_to_granularity)
- PoisonShadow(user_beg, size_rounded_down_to_granularity, 0);
- // Deal with the end of the region if size is not aligned to granularity.
- if (size != size_rounded_down_to_granularity && fl.poison_heap) {
- u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity);
- *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0;
- }
-
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.mallocs++;
- thread_stats.malloced += size;
- thread_stats.malloced_redzones += needed_size - size;
- uptr class_id = Min(kNumberOfSizeClasses, SizeClassMap::ClassID(needed_size));
- thread_stats.malloced_by_size[class_id]++;
- if (needed_size > SizeClassMap::kMaxSize)
- thread_stats.malloc_large++;
-
- void *res = reinterpret_cast<void *>(user_beg);
- if (can_fill && fl.max_malloc_fill_size) {
- uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size);
- REAL(memset)(res, fl.malloc_fill_byte, fill_size);
- }
-#if CAN_SANITIZE_LEAKS
- m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored
- : __lsan::kDirectlyLeaked;
-#endif
- // Must be the last mutation of metadata in this function.
- atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release);
- ASAN_MALLOC_HOOK(res, size);
- return res;
-}
-
-static void ReportInvalidFree(void *ptr, u8 chunk_state, StackTrace *stack) {
- if (chunk_state == CHUNK_QUARANTINE)
- ReportDoubleFree((uptr)ptr, stack);
- else
- ReportFreeNotMalloced((uptr)ptr, stack);
-}
-
-static void AtomicallySetQuarantineFlag(AsanChunk *m,
- void *ptr, StackTrace *stack) {
- u8 old_chunk_state = CHUNK_ALLOCATED;
- // Flip the chunk_state atomically to avoid race on double-free.
- if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state,
- CHUNK_QUARANTINE, memory_order_acquire))
- ReportInvalidFree(ptr, old_chunk_state, stack);
- CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state);
-}
-
-// Expects the chunk to already be marked as quarantined by using
-// AtomicallySetQuarantineFlag.
-static void QuarantineChunk(AsanChunk *m, void *ptr,
- StackTrace *stack, AllocType alloc_type) {
- CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
-
- if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
- ReportAllocTypeMismatch((uptr)ptr, stack,
- (AllocType)m->alloc_type, (AllocType)alloc_type);
-
- CHECK_GE(m->alloc_tid, 0);
- if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area.
- CHECK_EQ(m->free_tid, kInvalidTid);
- AsanThread *t = GetCurrentThread();
- m->free_tid = t ? t->tid() : 0;
- m->free_context_id = StackDepotPut(stack->trace, stack->size);
- // Poison the region.
- PoisonShadow(m->Beg(),
- RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
- kAsanHeapFreeMagic);
-
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.frees++;
- thread_stats.freed += m->UsedSize();
-
- // Push into quarantine.
- if (t) {
- AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
- AllocatorCache *ac = GetAllocatorCache(ms);
- quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac),
- m, m->UsedSize());
- } else {
- SpinMutexLock l(&fallback_mutex);
- AllocatorCache *ac = &fallback_allocator_cache;
- quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac),
- m, m->UsedSize());
- }
-}
-
-static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
- uptr p = reinterpret_cast<uptr>(ptr);
- if (p == 0) return;
-
- uptr chunk_beg = p - kChunkHeaderSize;
- AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
- ASAN_FREE_HOOK(ptr);
- // Must mark the chunk as quarantined before any changes to its metadata.
- AtomicallySetQuarantineFlag(m, ptr, stack);
- QuarantineChunk(m, ptr, stack, alloc_type);
-}
-
-static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
- CHECK(old_ptr && new_size);
- uptr p = reinterpret_cast<uptr>(old_ptr);
- uptr chunk_beg = p - kChunkHeaderSize;
- AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
-
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.reallocs++;
- thread_stats.realloced += new_size;
-
- void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true);
- if (new_ptr) {
- u8 chunk_state = m->chunk_state;
- if (chunk_state != CHUNK_ALLOCATED)
- ReportInvalidFree(old_ptr, chunk_state, stack);
- CHECK_NE(REAL(memcpy), (void*)0);
- uptr memcpy_size = Min(new_size, m->UsedSize());
- // If realloc() races with free(), we may start copying freed memory.
- // However, we will report racy double-free later anyway.
- REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
- Deallocate(old_ptr, stack, FROM_MALLOC);
- }
- return new_ptr;
-}
-
-// Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg).
-static AsanChunk *GetAsanChunk(void *alloc_beg) {
- if (!alloc_beg) return 0;
- if (!allocator.FromPrimary(alloc_beg)) {
- uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(alloc_beg));
- AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]);
- return m;
- }
- uptr *alloc_magic = reinterpret_cast<uptr *>(alloc_beg);
- if (alloc_magic[0] == kAllocBegMagic)
- return reinterpret_cast<AsanChunk *>(alloc_magic[1]);
- return reinterpret_cast<AsanChunk *>(alloc_beg);
-}
-
-static AsanChunk *GetAsanChunkByAddr(uptr p) {
- void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p));
- return GetAsanChunk(alloc_beg);
-}
-
-// Allocator must be locked when this function is called.
-static AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) {
- void *alloc_beg =
- allocator.GetBlockBeginFastLocked(reinterpret_cast<void *>(p));
- return GetAsanChunk(alloc_beg);
-}
-
-static uptr AllocationSize(uptr p) {
- AsanChunk *m = GetAsanChunkByAddr(p);
- if (!m) return 0;
- if (m->chunk_state != CHUNK_ALLOCATED) return 0;
- if (m->Beg() != p) return 0;
- return m->UsedSize();
-}
-
-// We have an address between two chunks, and we want to report just one.
-AsanChunk *ChooseChunk(uptr addr,
- AsanChunk *left_chunk, AsanChunk *right_chunk) {
- // Prefer an allocated chunk over freed chunk and freed chunk
- // over available chunk.
- if (left_chunk->chunk_state != right_chunk->chunk_state) {
- if (left_chunk->chunk_state == CHUNK_ALLOCATED)
- return left_chunk;
- if (right_chunk->chunk_state == CHUNK_ALLOCATED)
- return right_chunk;
- if (left_chunk->chunk_state == CHUNK_QUARANTINE)
- return left_chunk;
- if (right_chunk->chunk_state == CHUNK_QUARANTINE)
- return right_chunk;
- }
- // Same chunk_state: choose based on offset.
- sptr l_offset = 0, r_offset = 0;
- CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset));
- CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset));
- if (l_offset < r_offset)
- return left_chunk;
- return right_chunk;
-}
-
-AsanChunkView FindHeapChunkByAddress(uptr addr) {
- AsanChunk *m1 = GetAsanChunkByAddr(addr);
- if (!m1) return AsanChunkView(m1);
- sptr offset = 0;
- if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) {
- // The address is in the chunk's left redzone, so maybe it is actually
- // a right buffer overflow from the other chunk to the left.
- // Search a bit to the left to see if there is another chunk.
- AsanChunk *m2 = 0;
- for (uptr l = 1; l < GetPageSizeCached(); l++) {
- m2 = GetAsanChunkByAddr(addr - l);
- if (m2 == m1) continue; // Still the same chunk.
- break;
- }
- if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset))
- m1 = ChooseChunk(addr, m2, m1);
- }
- return AsanChunkView(m1);
-}
-
-void AsanThreadLocalMallocStorage::CommitBack() {
- AllocatorCache *ac = GetAllocatorCache(this);
- quarantine.Drain(GetQuarantineCache(this), QuarantineCallback(ac));
- allocator.SwallowCache(GetAllocatorCache(this));
-}
-
-void PrintInternalAllocatorStats() {
- allocator.PrintStats();
-}
-
-void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
- AllocType alloc_type) {
- return Allocate(size, alignment, stack, alloc_type, true);
-}
-
-void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
- Deallocate(ptr, stack, alloc_type);
-}
-
-void *asan_malloc(uptr size, StackTrace *stack) {
- return Allocate(size, 8, stack, FROM_MALLOC, true);
-}
-
-void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
- if (CallocShouldReturnNullDueToOverflow(size, nmemb))
- return AllocatorReturnNull();
- void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
- // If the memory comes from the secondary allocator no need to clear it
- // as it comes directly from mmap.
- if (ptr && allocator.FromPrimary(ptr))
- REAL(memset)(ptr, 0, nmemb * size);
- return ptr;
-}
-
-void *asan_realloc(void *p, uptr size, StackTrace *stack) {
- if (p == 0)
- return Allocate(size, 8, stack, FROM_MALLOC, true);
- if (size == 0) {
- Deallocate(p, stack, FROM_MALLOC);
- return 0;
- }
- return Reallocate(p, size, stack);
-}
-
-void *asan_valloc(uptr size, StackTrace *stack) {
- return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true);
-}
-
-void *asan_pvalloc(uptr size, StackTrace *stack) {
- uptr PageSize = GetPageSizeCached();
- size = RoundUpTo(size, PageSize);
- if (size == 0) {
- // pvalloc(0) should allocate one page.
- size = PageSize;
- }
- return Allocate(size, PageSize, stack, FROM_MALLOC, true);
-}
-
-int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
- StackTrace *stack) {
- void *ptr = Allocate(size, alignment, stack, FROM_MALLOC, true);
- CHECK(IsAligned((uptr)ptr, alignment));
- *memptr = ptr;
- return 0;
-}
-
-uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) {
- if (ptr == 0) return 0;
- uptr usable_size = AllocationSize(reinterpret_cast<uptr>(ptr));
- if (flags()->check_malloc_usable_size && (usable_size == 0)) {
- GET_STACK_TRACE_FATAL(pc, bp);
- ReportMallocUsableSizeNotOwned((uptr)ptr, &stack);
- }
- return usable_size;
-}
-
-uptr asan_mz_size(const void *ptr) {
- return AllocationSize(reinterpret_cast<uptr>(ptr));
-}
-
-void asan_mz_force_lock() {
- allocator.ForceLock();
- fallback_mutex.Lock();
-}
-
-void asan_mz_force_unlock() {
- fallback_mutex.Unlock();
- allocator.ForceUnlock();
-}
-
-} // namespace __asan
-
-// --- Implementation of LSan-specific functions --- {{{1
-namespace __lsan {
-void LockAllocator() {
- __asan::allocator.ForceLock();
-}
-
-void UnlockAllocator() {
- __asan::allocator.ForceUnlock();
-}
-
-void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
- *begin = (uptr)&__asan::allocator;
- *end = *begin + sizeof(__asan::allocator);
-}
-
-uptr PointsIntoChunk(void* p) {
- uptr addr = reinterpret_cast<uptr>(p);
- __asan::AsanChunk *m = __asan::GetAsanChunkByAddrFastLocked(addr);
- if (!m) return 0;
- uptr chunk = m->Beg();
- if ((m->chunk_state == __asan::CHUNK_ALLOCATED) &&
- m->AddrIsInside(addr, /*locked_version=*/true))
- return chunk;
- return 0;
-}
-
-uptr GetUserBegin(uptr chunk) {
- __asan::AsanChunk *m =
- __asan::GetAsanChunkByAddrFastLocked(chunk);
- CHECK(m);
- return m->Beg();
-}
-
-LsanMetadata::LsanMetadata(uptr chunk) {
- metadata_ = reinterpret_cast<void *>(chunk - __asan::kChunkHeaderSize);
-}
-
-bool LsanMetadata::allocated() const {
- __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
- return m->chunk_state == __asan::CHUNK_ALLOCATED;
-}
-
-ChunkTag LsanMetadata::tag() const {
- __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
- return static_cast<ChunkTag>(m->lsan_tag);
-}
-
-void LsanMetadata::set_tag(ChunkTag value) {
- __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
- m->lsan_tag = value;
-}
-
-uptr LsanMetadata::requested_size() const {
- __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
- return m->UsedSize(/*locked_version=*/true);
-}
-
-u32 LsanMetadata::stack_trace_id() const {
- __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
- return m->alloc_context_id;
-}
-
-void ForEachChunk(ForEachChunkCallback callback, void *arg) {
- __asan::allocator.ForEachChunk(callback, arg);
-}
-
-IgnoreObjectResult IgnoreObjectLocked(const void *p) {
- uptr addr = reinterpret_cast<uptr>(p);
- __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr);
- if (!m) return kIgnoreObjectInvalid;
- if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) {
- if (m->lsan_tag == kIgnored)
- return kIgnoreObjectAlreadyIgnored;
- m->lsan_tag = __lsan::kIgnored;
- return kIgnoreObjectSuccess;
- } else {
- return kIgnoreObjectInvalid;
- }
-}
-} // namespace __lsan
-
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan; // NOLINT
-
-// ASan allocator doesn't reserve extra bytes, so normally we would
-// just return "size". We don't want to expose our redzone sizes, etc here.
-uptr __asan_get_estimated_allocated_size(uptr size) {
- return size;
-}
-
-bool __asan_get_ownership(const void *p) {
- uptr ptr = reinterpret_cast<uptr>(p);
- return (AllocationSize(ptr) > 0);
-}
-
-uptr __asan_get_allocated_size(const void *p) {
- if (p == 0) return 0;
- uptr ptr = reinterpret_cast<uptr>(p);
- uptr allocated_size = AllocationSize(ptr);
- // Die if p is not malloced or if it is already freed.
- if (allocated_size == 0) {
- GET_STACK_TRACE_FATAL_HERE;
- ReportAsanGetAllocatedSizeNotOwned(ptr, &stack);
- }
- return allocated_size;
-}
-
-#if !SANITIZER_SUPPORTS_WEAK_HOOKS
-// Provide default (no-op) implementation of malloc hooks.
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-void __asan_malloc_hook(void *ptr, uptr size) {
- (void)ptr;
- (void)size;
-}
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-void __asan_free_hook(void *ptr) {
- (void)ptr;
-}
-} // extern "C"
-#endif
--- /dev/null
+//===-- asan_debugging.cc -------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file contains various functions that are generally useful to call when
+// using a debugger (LLDB, GDB).
+//===----------------------------------------------------------------------===//
+
+#include "asan_allocator.h"
+#include "asan_flags.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_thread.h"
+
+namespace __asan {
+
+void GetInfoForStackVar(uptr addr, AddressDescription *descr, AsanThread *t) {
+ descr->name[0] = 0;
+ descr->region_address = 0;
+ descr->region_size = 0;
+ descr->region_kind = "stack";
+
+ AsanThread::StackFrameAccess access;
+ if (!t->GetStackFrameAccessByAddr(addr, &access))
+ return;
+ InternalMmapVector<StackVarDescr> vars(16);
+ if (!ParseFrameDescription(access.frame_descr, &vars)) {
+ return;
+ }
+
+ for (uptr i = 0; i < vars.size(); i++) {
+ if (access.offset <= vars[i].beg + vars[i].size) {
+ internal_strncat(descr->name, vars[i].name_pos,
+ Min(descr->name_size, vars[i].name_len));
+ descr->region_address = addr - (access.offset - vars[i].beg);
+ descr->region_size = vars[i].size;
+ return;
+ }
+ }
+}
+
+void GetInfoForHeapAddress(uptr addr, AddressDescription *descr) {
+ AsanChunkView chunk = FindHeapChunkByAddress(addr);
+
+ descr->name[0] = 0;
+ descr->region_address = 0;
+ descr->region_size = 0;
+
+ if (!chunk.IsValid()) {
+ descr->region_kind = "heap-invalid";
+ return;
+ }
+
+ descr->region_address = chunk.Beg();
+ descr->region_size = chunk.UsedSize();
+ descr->region_kind = "heap";
+}
+
+void AsanLocateAddress(uptr addr, AddressDescription *descr) {
+ if (DescribeAddressIfShadow(addr, descr, /* print */ false)) {
+ return;
+ }
+ if (GetInfoForAddressIfGlobal(addr, descr)) {
+ return;
+ }
+ asanThreadRegistry().Lock();
+ AsanThread *thread = FindThreadByStackAddress(addr);
+ asanThreadRegistry().Unlock();
+ if (thread) {
+ GetInfoForStackVar(addr, descr, thread);
+ return;
+ }
+ GetInfoForHeapAddress(addr, descr);
+}
+
+static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
+ bool alloc_stack) {
+ AsanChunkView chunk = FindHeapChunkByAddress(addr);
+ if (!chunk.IsValid()) return 0;
+
+ StackTrace stack(nullptr, 0);
+ if (alloc_stack) {
+ if (chunk.AllocTid() == kInvalidTid) return 0;
+ stack = chunk.GetAllocStack();
+ if (thread_id) *thread_id = chunk.AllocTid();
+ } else {
+ if (chunk.FreeTid() == kInvalidTid) return 0;
+ stack = chunk.GetFreeStack();
+ if (thread_id) *thread_id = chunk.FreeTid();
+ }
+
+ if (trace && size) {
+ size = Min(size, Min(stack.size, kStackTraceMax));
+ for (uptr i = 0; i < size; i++)
+ trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]);
+
+ return size;
+ }
+
+ return 0;
+}
+
+} // namespace __asan
+
+using namespace __asan;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
+ uptr *region_address, uptr *region_size) {
+ AddressDescription descr = { name, name_size, 0, 0, nullptr };
+ AsanLocateAddress(addr, &descr);
+ if (region_address) *region_address = descr.region_address;
+ if (region_size) *region_size = descr.region_size;
+ return descr.region_kind;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
+ return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
+ return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ false);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset) {
+ if (shadow_scale)
+ *shadow_scale = SHADOW_SCALE;
+ if (shadow_offset)
+ *shadow_offset = SHADOW_OFFSET;
+}
+++ /dev/null
-//===-- asan_dll_thunk.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// This file defines a family of thunks that should be statically linked into
-// the DLLs that have ASan instrumentation in order to delegate the calls to the
-// shared runtime that lives in the main binary.
-// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the
-// details.
-//===----------------------------------------------------------------------===//
-
-// Only compile this code when buidling asan_dll_thunk.lib
-// Using #ifdef rather than relying on Makefiles etc.
-// simplifies the build procedure.
-#ifdef ASAN_DLL_THUNK
-
-// ----------------- Helper functions and macros --------------------- {{{1
-extern "C" {
-void *__stdcall GetModuleHandleA(const char *module_name);
-void *__stdcall GetProcAddress(void *module, const char *proc_name);
-void abort();
-}
-
-static void *getRealProcAddressOrDie(const char *name) {
- void *ret = GetProcAddress(GetModuleHandleA(0), name);
- if (!ret)
- abort();
- return ret;
-}
-
-#define WRAP_V_V(name) \
- extern "C" void name() { \
- typedef void (*fntype)(); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- fn(); \
- }
-
-#define WRAP_V_W(name) \
- extern "C" void name(void *arg) { \
- typedef void (*fntype)(void *arg); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- fn(arg); \
- }
-
-#define WRAP_V_WW(name) \
- extern "C" void name(void *arg1, void *arg2) { \
- typedef void (*fntype)(void *, void *); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- fn(arg1, arg2); \
- }
-
-#define WRAP_V_WWW(name) \
- extern "C" void name(void *arg1, void *arg2, void *arg3) { \
- typedef void *(*fntype)(void *, void *, void *); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- fn(arg1, arg2, arg3); \
- }
-
-#define WRAP_W_V(name) \
- extern "C" void *name() { \
- typedef void *(*fntype)(); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- return fn(); \
- }
-
-#define WRAP_W_W(name) \
- extern "C" void *name(void *arg) { \
- typedef void *(*fntype)(void *arg); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- return fn(arg); \
- }
-
-#define WRAP_W_WW(name) \
- extern "C" void *name(void *arg1, void *arg2) { \
- typedef void *(*fntype)(void *, void *); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- return fn(arg1, arg2); \
- }
-
-#define WRAP_W_WWW(name) \
- extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
- typedef void *(*fntype)(void *, void *, void *); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- return fn(arg1, arg2, arg3); \
- }
-
-#define WRAP_W_WWWW(name) \
- extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
- typedef void *(*fntype)(void *, void *, void *, void *); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- return fn(arg1, arg2, arg3, arg4); \
- }
-
-#define WRAP_W_WWWWW(name) \
- extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
- void *arg5) { \
- typedef void *(*fntype)(void *, void *, void *, void *, void *); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- return fn(arg1, arg2, arg3, arg4, arg5); \
- }
-
-#define WRAP_W_WWWWWW(name) \
- extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
- void *arg5, void *arg6) { \
- typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \
- static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
- return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
- }
-// }}}
-
-// ----------------- ASan own interface functions --------------------
-WRAP_W_V(__asan_should_detect_stack_use_after_return)
-
-extern "C" {
- int __asan_option_detect_stack_use_after_return;
-
- // Manually wrap __asan_init as we need to initialize
- // __asan_option_detect_stack_use_after_return afterwards.
- void __asan_init_v3() {
- typedef void (*fntype)();
- static fntype fn = (fntype)getRealProcAddressOrDie("__asan_init_v3");
- fn();
- __asan_option_detect_stack_use_after_return =
- (__asan_should_detect_stack_use_after_return() != 0);
- }
-}
-
-WRAP_V_V(__asan_handle_no_return)
-
-WRAP_V_W(__asan_report_store1)
-WRAP_V_W(__asan_report_store2)
-WRAP_V_W(__asan_report_store4)
-WRAP_V_W(__asan_report_store8)
-WRAP_V_W(__asan_report_store16)
-WRAP_V_WW(__asan_report_store_n)
-
-WRAP_V_W(__asan_report_load1)
-WRAP_V_W(__asan_report_load2)
-WRAP_V_W(__asan_report_load4)
-WRAP_V_W(__asan_report_load8)
-WRAP_V_W(__asan_report_load16)
-WRAP_V_WW(__asan_report_load_n)
-
-WRAP_V_WW(__asan_register_globals)
-WRAP_V_WW(__asan_unregister_globals)
-
-WRAP_W_WW(__asan_stack_malloc_0)
-WRAP_W_WW(__asan_stack_malloc_1)
-WRAP_W_WW(__asan_stack_malloc_2)
-WRAP_W_WW(__asan_stack_malloc_3)
-WRAP_W_WW(__asan_stack_malloc_4)
-WRAP_W_WW(__asan_stack_malloc_5)
-WRAP_W_WW(__asan_stack_malloc_6)
-WRAP_W_WW(__asan_stack_malloc_7)
-WRAP_W_WW(__asan_stack_malloc_8)
-WRAP_W_WW(__asan_stack_malloc_9)
-WRAP_W_WW(__asan_stack_malloc_10)
-
-WRAP_V_WWW(__asan_stack_free_0)
-WRAP_V_WWW(__asan_stack_free_1)
-WRAP_V_WWW(__asan_stack_free_2)
-WRAP_V_WWW(__asan_stack_free_4)
-WRAP_V_WWW(__asan_stack_free_5)
-WRAP_V_WWW(__asan_stack_free_6)
-WRAP_V_WWW(__asan_stack_free_7)
-WRAP_V_WWW(__asan_stack_free_8)
-WRAP_V_WWW(__asan_stack_free_9)
-WRAP_V_WWW(__asan_stack_free_10)
-
-// TODO(timurrrr): Add more interface functions on the as-needed basis.
-
-// ----------------- Memory allocation functions ---------------------
-WRAP_V_W(free)
-WRAP_V_WW(_free_dbg)
-
-WRAP_W_W(malloc)
-WRAP_W_WWWW(_malloc_dbg)
-
-WRAP_W_WW(calloc)
-WRAP_W_WWWWW(_calloc_dbg)
-WRAP_W_WWW(_calloc_impl)
-
-WRAP_W_WW(realloc)
-WRAP_W_WWW(_realloc_dbg)
-WRAP_W_WWW(_recalloc)
-
-WRAP_W_W(_msize)
-
-// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc).
-
-#endif // ASAN_DLL_THUNK
//
// FakeStack is used to detect use-after-return bugs.
//===----------------------------------------------------------------------===//
+
#include "asan_allocator.h"
#include "asan_poisoning.h"
#include "asan_thread.h"
static const u64 kMagic4 = (kMagic2 << 16) | kMagic2;
static const u64 kMagic8 = (kMagic4 << 32) | kMagic4;
+static const u64 kAllocaRedzoneSize = 32UL;
+static const u64 kAllocaRedzoneMask = 31UL;
+
// For small size classes inline PoisonShadow for better performance.
ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) {
CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3.
u64 *shadow = reinterpret_cast<u64*>(MemToShadow(ptr));
if (class_id <= 6) {
- for (uptr i = 0; i < (1U << class_id); i++)
+ for (uptr i = 0; i < (1U << class_id); i++) {
shadow[i] = magic;
+ // Make sure this does not become memset.
+ SanitizerBreakOptimization(nullptr);
+ }
} else {
// The size class is too big, it's cheaper to poison only size bytes.
PoisonShadow(ptr, size, static_cast<u8>(magic));
stack_size_log = kMinStackSizeLog;
if (stack_size_log > kMaxStackSizeLog)
stack_size_log = kMaxStackSizeLog;
+ uptr size = RequiredSize(stack_size_log);
FakeStack *res = reinterpret_cast<FakeStack *>(
- MmapOrDie(RequiredSize(stack_size_log), "FakeStack"));
+ flags()->uar_noreserve ? MmapNoReserveOrDie(size, "FakeStack")
+ : MmapOrDie(size, "FakeStack"));
res->stack_size_log_ = stack_size_log;
- if (common_flags()->verbosity) {
- u8 *p = reinterpret_cast<u8 *>(res);
- Report("T%d: FakeStack created: %p -- %p stack_size_log: %zd \n",
- GetCurrentTidOrInvalid(), p,
- p + FakeStack::RequiredSize(stack_size_log), stack_size_log);
- }
+ u8 *p = reinterpret_cast<u8 *>(res);
+ VReport(1, "T%d: FakeStack created: %p -- %p stack_size_log: %zd; "
+ "mmapped %zdK, noreserve=%d \n",
+ GetCurrentTidOrInvalid(), p,
+ p + FakeStack::RequiredSize(stack_size_log), stack_size_log,
+ size >> 10, flags()->uar_noreserve);
return res;
}
-void FakeStack::Destroy() {
+void FakeStack::Destroy(int tid) {
PoisonAll(0);
- UnmapOrDie(this, RequiredSize(stack_size_log_));
+ if (Verbosity() >= 2) {
+ InternalScopedString str(kNumberOfSizeClasses * 50);
+ for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++)
+ str.append("%zd: %zd/%zd; ", class_id, hint_position_[class_id],
+ NumberOfFrames(stack_size_log(), class_id));
+ Report("T%d: FakeStack destroyed: %s\n", tid, str.data());
+ }
+ uptr size = RequiredSize(stack_size_log_);
+ FlushUnneededASanShadowMemory(reinterpret_cast<uptr>(this), size);
+ UnmapOrDie(this, size);
}
void FakeStack::PoisonAll(u8 magic) {
magic);
}
+#if !defined(_MSC_VER) || defined(__clang__)
ALWAYS_INLINE USED
+#endif
FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id,
uptr real_stack) {
CHECK_LT(class_id, kNumberOfSizeClasses);
*SavedFlagPtr(reinterpret_cast<uptr>(res), class_id) = &flags[pos];
return res;
}
- return 0; // We are out of fake stack.
+ return nullptr; // We are out of fake stack.
}
-uptr FakeStack::AddrIsInFakeStack(uptr ptr) {
+uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr *frame_beg, uptr *frame_end) {
uptr stack_size_log = this->stack_size_log();
uptr beg = reinterpret_cast<uptr>(GetFrame(stack_size_log, 0, 0));
uptr end = reinterpret_cast<uptr>(this) + RequiredSize(stack_size_log);
CHECK_LE(base, ptr);
CHECK_LT(ptr, base + (1UL << stack_size_log));
uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id);
- return base + pos * BytesInSizeClass(class_id);
+ uptr res = base + pos * BytesInSizeClass(class_id);
+ *frame_end = res + BytesInSizeClass(class_id);
+ *frame_beg = res + sizeof(FakeFrame);
+ return res;
}
void FakeStack::HandleNoReturn() {
static FakeStack *GetFakeStack() {
AsanThread *t = GetCurrentThread();
- if (!t) return 0;
+ if (!t) return nullptr;
return t->fake_stack();
}
if (FakeStack *fs = GetTLSFakeStack())
return fs;
if (!__asan_option_detect_stack_use_after_return)
- return 0;
+ return nullptr;
return GetFakeStack();
}
-ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) {
+ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) {
FakeStack *fs = GetFakeStackFast();
- if (!fs) return real_stack;
+ if (!fs) return 0;
+ uptr local_stack;
+ uptr real_stack = reinterpret_cast<uptr>(&local_stack);
FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack);
- if (!ff)
- return real_stack; // Out of fake stack, return the real one.
+ if (!ff) return 0; // Out of fake stack.
uptr ptr = reinterpret_cast<uptr>(ff);
SetShadow(ptr, size, class_id, 0);
return ptr;
}
-ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) {
- if (ptr == real_stack)
- return;
+ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) {
FakeStack::Deallocate(ptr, class_id);
SetShadow(ptr, size, class_id, kMagic8);
}
-} // namespace __asan
+} // namespace __asan
// ---------------------- Interface ---------------- {{{1
+using namespace __asan;
#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \
extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \
- __asan_stack_malloc_##class_id(uptr size, uptr real_stack) { \
- return __asan::OnMalloc(class_id, size, real_stack); \
+ __asan_stack_malloc_##class_id(uptr size) { \
+ return OnMalloc(class_id, size); \
} \
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \
- uptr ptr, uptr size, uptr real_stack) { \
- __asan::OnFree(ptr, class_id, size, real_stack); \
+ uptr ptr, uptr size) { \
+ OnFree(ptr, class_id, size); \
}
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0)
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8)
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9)
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10)
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__asan_get_current_fake_stack() { return GetFakeStackFast(); }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
+ void **end) {
+ FakeStack *fs = reinterpret_cast<FakeStack*>(fake_stack);
+ if (!fs) return nullptr;
+ uptr frame_beg, frame_end;
+ FakeFrame *frame = reinterpret_cast<FakeFrame *>(fs->AddrIsInFakeStack(
+ reinterpret_cast<uptr>(addr), &frame_beg, &frame_end));
+ if (!frame) return nullptr;
+ if (frame->magic != kCurrentStackFrameMagic)
+ return nullptr;
+ if (beg) *beg = reinterpret_cast<void*>(frame_beg);
+ if (end) *end = reinterpret_cast<void*>(frame_end);
+ return reinterpret_cast<void*>(frame->real_stack);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_alloca_poison(uptr addr, uptr size) {
+ uptr LeftRedzoneAddr = addr - kAllocaRedzoneSize;
+ uptr PartialRzAddr = addr + size;
+ uptr RightRzAddr = (PartialRzAddr + kAllocaRedzoneMask) & ~kAllocaRedzoneMask;
+ uptr PartialRzAligned = PartialRzAddr & ~(SHADOW_GRANULARITY - 1);
+ FastPoisonShadow(LeftRedzoneAddr, kAllocaRedzoneSize, kAsanAllocaLeftMagic);
+ FastPoisonShadowPartialRightRedzone(
+ PartialRzAligned, PartialRzAddr % SHADOW_GRANULARITY,
+ RightRzAddr - PartialRzAligned, kAsanAllocaRightMagic);
+ FastPoisonShadow(RightRzAddr, kAllocaRedzoneSize, kAsanAllocaRightMagic);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_allocas_unpoison(uptr top, uptr bottom) {
+ if ((!top) || (top > bottom)) return;
+ REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0,
+ (bottom - top) / SHADOW_GRANULARITY);
+}
+} // extern "C"
// CTOR: create the FakeStack as a single mmap-ed object.
static FakeStack *Create(uptr stack_size_log);
- void Destroy();
+ void Destroy(int tid);
// stack_size_log is at least 15 (stack_size >= 32K).
static uptr SizeRequiredForFlags(uptr stack_size_log) {
void PoisonAll(u8 magic);
// Return the beginning of the FakeFrame or 0 if the address is not ours.
- uptr AddrIsInFakeStack(uptr addr);
+ uptr AddrIsInFakeStack(uptr addr, uptr *frame_beg, uptr *frame_end);
+ USED uptr AddrIsInFakeStack(uptr addr) {
+ uptr t1, t2;
+ return AddrIsInFakeStack(addr, &t1, &t2);
+ }
// Number of bytes in a fake frame of this size class.
static uptr BytesInSizeClass(uptr class_id) {
--- /dev/null
+//===-- asan_flags.cc -------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan flag parsing logic.
+//===----------------------------------------------------------------------===//
+
+#include "asan_activation.h"
+#include "asan_flags.h"
+#include "asan_interface_internal.h"
+#include "asan_stack.h"
+#include "lsan/lsan_common.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "ubsan/ubsan_flags.h"
+#include "ubsan/ubsan_platform.h"
+
+namespace __asan {
+
+Flags asan_flags_dont_use_directly; // use via flags().
+
+static const char *MaybeCallAsanDefaultOptions() {
+ return (&__asan_default_options) ? __asan_default_options() : "";
+}
+
+static const char *MaybeUseAsanDefaultOptionsCompileDefinition() {
+#ifdef ASAN_DEFAULT_OPTIONS
+// Stringize the macro value.
+# define ASAN_STRINGIZE(x) #x
+# define ASAN_STRINGIZE_OPTIONS(options) ASAN_STRINGIZE(options)
+ return ASAN_STRINGIZE_OPTIONS(ASAN_DEFAULT_OPTIONS);
+#else
+ return "";
+#endif
+}
+
+void Flags::SetDefaults() {
+#define ASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "asan_flags.inc"
+#undef ASAN_FLAG
+}
+
+static void RegisterAsanFlags(FlagParser *parser, Flags *f) {
+#define ASAN_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &f->Name);
+#include "asan_flags.inc"
+#undef ASAN_FLAG
+}
+
+void InitializeFlags() {
+ // Set the default values and prepare for parsing ASan and common flags.
+ SetCommonFlagsDefaults();
+ {
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.detect_leaks = CAN_SANITIZE_LEAKS;
+ cf.external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH");
+ cf.malloc_context_size = kDefaultMallocContextSize;
+ cf.intercept_tls_get_addr = true;
+ cf.exitcode = 1;
+ OverrideCommonFlags(cf);
+ }
+ Flags *f = flags();
+ f->SetDefaults();
+
+ FlagParser asan_parser;
+ RegisterAsanFlags(&asan_parser, f);
+ RegisterCommonFlags(&asan_parser);
+
+ // Set the default values and prepare for parsing LSan and UBSan flags
+ // (which can also overwrite common flags).
+#if CAN_SANITIZE_LEAKS
+ __lsan::Flags *lf = __lsan::flags();
+ lf->SetDefaults();
+
+ FlagParser lsan_parser;
+ __lsan::RegisterLsanFlags(&lsan_parser, lf);
+ RegisterCommonFlags(&lsan_parser);
+#endif
+
+#if CAN_SANITIZE_UB
+ __ubsan::Flags *uf = __ubsan::flags();
+ uf->SetDefaults();
+
+ FlagParser ubsan_parser;
+ __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
+ RegisterCommonFlags(&ubsan_parser);
+#endif
+
+ // Override from ASan compile definition.
+ const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition();
+ asan_parser.ParseString(asan_compile_def);
+
+ // Override from user-specified string.
+ const char *asan_default_options = MaybeCallAsanDefaultOptions();
+ asan_parser.ParseString(asan_default_options);
+#if CAN_SANITIZE_UB
+ const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
+ ubsan_parser.ParseString(ubsan_default_options);
+#endif
+
+ // Override from command line.
+ asan_parser.ParseString(GetEnv("ASAN_OPTIONS"));
+#if CAN_SANITIZE_LEAKS
+ lsan_parser.ParseString(GetEnv("LSAN_OPTIONS"));
+#endif
+#if CAN_SANITIZE_UB
+ ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
+#endif
+
+ // Let activation flags override current settings. On Android they come
+ // from a system property. On other platforms this is no-op.
+ if (!flags()->start_deactivated) {
+ char buf[100];
+ GetExtraActivationFlags(buf, sizeof(buf));
+ asan_parser.ParseString(buf);
+ }
+
+ SetVerbosity(common_flags()->verbosity);
+
+ // TODO(eugenis): dump all flags at verbosity>=2?
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (common_flags()->help) {
+ // TODO(samsonov): print all of the flags (ASan, LSan, common).
+ asan_parser.PrintFlagDescriptions();
+ }
+
+ // Flag validation:
+ if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) {
+ Report("%s: detect_leaks is not supported on this platform.\n",
+ SanitizerToolName);
+ Die();
+ }
+ // Make "strict_init_order" imply "check_initialization_order".
+ // TODO(samsonov): Use a single runtime flag for an init-order checker.
+ if (f->strict_init_order) {
+ f->check_initialization_order = true;
+ }
+ CHECK_LE((uptr)common_flags()->malloc_context_size, kStackTraceMax);
+ CHECK_LE(f->min_uar_stack_size_log, f->max_uar_stack_size_log);
+ CHECK_GE(f->redzone, 16);
+ CHECK_GE(f->max_redzone, f->redzone);
+ CHECK_LE(f->max_redzone, 2048);
+ CHECK(IsPowerOfTwo(f->redzone));
+ CHECK(IsPowerOfTwo(f->max_redzone));
+
+ // quarantine_size is deprecated but we still honor it.
+ // quarantine_size can not be used together with quarantine_size_mb.
+ if (f->quarantine_size >= 0 && f->quarantine_size_mb >= 0) {
+ Report("%s: please use either 'quarantine_size' (deprecated) or "
+ "quarantine_size_mb, but not both\n", SanitizerToolName);
+ Die();
+ }
+ if (f->quarantine_size >= 0)
+ f->quarantine_size_mb = f->quarantine_size >> 20;
+ if (f->quarantine_size_mb < 0) {
+ const int kDefaultQuarantineSizeMb =
+ (ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8;
+ f->quarantine_size_mb = kDefaultQuarantineSizeMb;
+ }
+}
+
+} // namespace __asan
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char* __asan_default_options() { return ""; }
+} // extern "C"
+#endif
#define ASAN_FLAGS_H
#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
// ASan flag values can be defined in four ways:
// 1) initialized with default values at startup.
// 3) overriden from string returned by user-specified function
// __asan_default_options().
// 4) overriden from env variable ASAN_OPTIONS.
+// 5) overriden during ASan activation (for now used on Android only).
namespace __asan {
struct Flags {
- // Size (in bytes) of quarantine used to detect use-after-free errors.
- // Lower value may reduce memory usage but increase the chance of
- // false negatives.
- int quarantine_size;
- // Size (in bytes) of redzones around heap objects.
- // Requirement: redzone >= 32, is a power of two.
- int redzone;
- // If set, prints some debugging information and does additional checks.
- bool debug;
- // Controls the way to handle globals (0 - don't detect buffer overflow
- // on globals, 1 - detect buffer overflow, 2 - print data about registered
- // globals).
- int report_globals;
- // If set, attempts to catch initialization order issues.
- bool check_initialization_order;
- // If set, uses custom wrappers and replacements for libc string functions
- // to find more errors.
- bool replace_str;
- // If set, uses custom wrappers for memset/memcpy/memmove intinsics.
- bool replace_intrin;
- // Used on Mac only.
- bool mac_ignore_invalid_free;
- // Enables stack-use-after-return checking at run-time.
- bool detect_stack_use_after_return;
- // The minimal fake stack size log.
- int uar_stack_size_log;
- // ASan allocator flag. max_malloc_fill_size is the maximal amount of bytes
- // that will be filled with malloc_fill_byte on malloc.
- int max_malloc_fill_size, malloc_fill_byte;
- // Override exit status if something was reported.
- int exitcode;
- // If set, user may manually mark memory regions as poisoned or unpoisoned.
- bool allow_user_poisoning;
- // Number of seconds to sleep between printing an error report and
- // terminating application. Useful for debug purposes (when one needs
- // to attach gdb, for example).
- int sleep_before_dying;
- // If set, registers ASan custom segv handler.
- bool handle_segv;
- // If set, allows user register segv handler even if ASan registers one.
- bool allow_user_segv_handler;
- // If set, uses alternate stack for signal handling.
- bool use_sigaltstack;
- // Allow the users to work around the bug in Nvidia drivers prior to 295.*.
- bool check_malloc_usable_size;
- // If set, explicitly unmaps (huge) shadow at exit.
- bool unmap_shadow_on_exit;
- // If set, calls abort() instead of _exit() after printing an error report.
- bool abort_on_error;
- // Print various statistics after printing an error message or if atexit=1.
- bool print_stats;
- // Print the legend for the shadow bytes.
- bool print_legend;
- // If set, prints ASan exit stats even after program terminates successfully.
- bool atexit;
- // If set, coverage information will be dumped at shutdown time if the
- // appropriate instrumentation was enabled.
- bool coverage;
- // By default, disable core dumper on 64-bit - it makes little sense
- // to dump 16T+ core.
- bool disable_core;
- // Allow the tool to re-exec the program. This may interfere badly with the
- // debugger.
- bool allow_reexec;
- // If set, prints not only thread creation stacks for threads in error report,
- // but also thread creation stacks for threads that created those threads,
- // etc. up to main thread.
- bool print_full_thread_history;
- // Poison (or not) the heap memory on [de]allocation. Zero value is useful
- // for benchmarking the allocator or instrumentator.
- bool poison_heap;
- // If true, poison partially addressable 8-byte aligned words (default=true).
- // This flag affects heap and global buffers, but not stack buffers.
- bool poison_partial;
- // Report errors on malloc/delete, new/free, new/delete[], etc.
- bool alloc_dealloc_mismatch;
- // If true, assume that memcmp(p1, p2, n) always reads n bytes before
- // comparing p1 and p2.
- bool strict_memcmp;
- // If true, assume that dynamic initializers can never access globals from
- // other modules, even if the latter are already initialized.
- bool strict_init_order;
+#define ASAN_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "asan_flags.inc"
+#undef ASAN_FLAG
+
+ void SetDefaults();
};
extern Flags asan_flags_dont_use_directly;
inline Flags *flags() {
return &asan_flags_dont_use_directly;
}
-void InitializeFlags(Flags *f, const char *env);
+
+void InitializeFlags();
} // namespace __asan
--- /dev/null
+//===-- asan_flags.inc ------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// ASan runtime flags.
+//
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_FLAG
+# error "Define ASAN_FLAG prior to including this file!"
+#endif
+
+// ASAN_FLAG(Type, Name, DefaultValue, Description)
+// See COMMON_FLAG in sanitizer_flags.inc for more details.
+
+ASAN_FLAG(int, quarantine_size, -1,
+ "Deprecated, please use quarantine_size_mb.")
+ASAN_FLAG(int, quarantine_size_mb, -1,
+ "Size (in Mb) of quarantine used to detect use-after-free "
+ "errors. Lower value may reduce memory usage but increase the "
+ "chance of false negatives.")
+ASAN_FLAG(int, redzone, 16,
+ "Minimal size (in bytes) of redzones around heap objects. "
+ "Requirement: redzone >= 16, is a power of two.")
+ASAN_FLAG(int, max_redzone, 2048,
+ "Maximal size (in bytes) of redzones around heap objects.")
+ASAN_FLAG(
+ bool, debug, false,
+ "If set, prints some debugging information and does additional checks.")
+ASAN_FLAG(
+ int, report_globals, 1,
+ "Controls the way to handle globals (0 - don't detect buffer overflow on "
+ "globals, 1 - detect buffer overflow, 2 - print data about registered "
+ "globals).")
+ASAN_FLAG(bool, check_initialization_order, false,
+ "If set, attempts to catch initialization order issues.")
+ASAN_FLAG(
+ bool, replace_str, true,
+ "If set, uses custom wrappers and replacements for libc string functions "
+ "to find more errors.")
+ASAN_FLAG(bool, replace_intrin, true,
+ "If set, uses custom wrappers for memset/memcpy/memmove intinsics.")
+ASAN_FLAG(bool, mac_ignore_invalid_free, false,
+ "Ignore invalid free() calls to work around some bugs. Used on OS X "
+ "only.")
+ASAN_FLAG(bool, detect_stack_use_after_return, false,
+ "Enables stack-use-after-return checking at run-time.")
+ASAN_FLAG(int, min_uar_stack_size_log, 16, // We can't do smaller anyway.
+ "Minimum fake stack size log.")
+ASAN_FLAG(int, max_uar_stack_size_log,
+ 20, // 1Mb per size class, i.e. ~11Mb per thread
+ "Maximum fake stack size log.")
+ASAN_FLAG(bool, uar_noreserve, false,
+ "Use mmap with 'noreserve' flag to allocate fake stack.")
+ASAN_FLAG(
+ int, max_malloc_fill_size, 0x1000, // By default, fill only the first 4K.
+ "ASan allocator flag. max_malloc_fill_size is the maximal amount of "
+ "bytes that will be filled with malloc_fill_byte on malloc.")
+ASAN_FLAG(int, malloc_fill_byte, 0xbe,
+ "Value used to fill the newly allocated memory.")
+ASAN_FLAG(bool, allow_user_poisoning, true,
+ "If set, user may manually mark memory regions as poisoned or "
+ "unpoisoned.")
+ASAN_FLAG(
+ int, sleep_before_dying, 0,
+ "Number of seconds to sleep between printing an error report and "
+ "terminating the program. Useful for debugging purposes (e.g. when one "
+ "needs to attach gdb).")
+ASAN_FLAG(bool, check_malloc_usable_size, true,
+ "Allows the users to work around the bug in Nvidia drivers prior to "
+ "295.*.")
+ASAN_FLAG(bool, unmap_shadow_on_exit, false,
+ "If set, explicitly unmaps the (huge) shadow at exit.")
+ASAN_FLAG(bool, protect_shadow_gap, true, "If set, mprotect the shadow gap")
+ASAN_FLAG(bool, print_stats, false,
+ "Print various statistics after printing an error message or if "
+ "atexit=1.")
+ASAN_FLAG(bool, print_legend, true, "Print the legend for the shadow bytes.")
+ASAN_FLAG(bool, atexit, false,
+ "If set, prints ASan exit stats even after program terminates "
+ "successfully.")
+ASAN_FLAG(
+ bool, print_full_thread_history, true,
+ "If set, prints thread creation stacks for the threads involved in the "
+ "report and their ancestors up to the main thread.")
+ASAN_FLAG(
+ bool, poison_heap, true,
+ "Poison (or not) the heap memory on [de]allocation. Zero value is useful "
+ "for benchmarking the allocator or instrumentator.")
+ASAN_FLAG(bool, poison_partial, true,
+ "If true, poison partially addressable 8-byte aligned words "
+ "(default=true). This flag affects heap and global buffers, but not "
+ "stack buffers.")
+ASAN_FLAG(bool, poison_array_cookie, true,
+ "Poison (or not) the array cookie after operator new[].")
+
+// Turn off alloc/dealloc mismatch checker on Mac and Windows for now.
+// https://code.google.com/p/address-sanitizer/issues/detail?id=131
+// https://code.google.com/p/address-sanitizer/issues/detail?id=309
+// TODO(glider,timurrrr): Fix known issues and enable this back.
+ASAN_FLAG(bool, alloc_dealloc_mismatch,
+ (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0),
+ "Report errors on malloc/delete, new/free, new/delete[], etc.")
+
+ASAN_FLAG(bool, new_delete_type_mismatch, true,
+ "Report errors on mismatch betwen size of new and delete.")
+ASAN_FLAG(
+ bool, strict_init_order, false,
+ "If true, assume that dynamic initializers can never access globals from "
+ "other modules, even if the latter are already initialized.")
+ASAN_FLAG(
+ bool, start_deactivated, false,
+ "If true, ASan tweaks a bunch of other flags (quarantine, redzone, heap "
+ "poisoning) to reduce memory consumption as much as possible, and "
+ "restores them to original values when the first instrumented module is "
+ "loaded into the process. This is mainly intended to be used on "
+ "Android. ")
+ASAN_FLAG(
+ int, detect_invalid_pointer_pairs, 0,
+ "If non-zero, try to detect operations like <, <=, >, >= and - on "
+ "invalid pointer pairs (e.g. when pointers belong to different objects). "
+ "The bigger the value the harder we try.")
+ASAN_FLAG(
+ bool, detect_container_overflow, true,
+ "If true, honor the container overflow annotations. "
+ "See https://code.google.com/p/address-sanitizer/wiki/ContainerOverflow")
+ASAN_FLAG(int, detect_odr_violation, 2,
+ "If >=2, detect violation of One-Definition-Rule (ODR); "
+ "If ==1, detect ODR-violation only if the two variables "
+ "have different sizes")
+ASAN_FLAG(bool, dump_instruction_bytes, false,
+ "If true, dump 16 bytes starting at the instruction that caused SEGV")
+ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
+ASAN_FLAG(bool, halt_on_error, true,
+ "Crash the program after printing the first error report "
+ "(WARNING: USE AT YOUR OWN RISK!)")
//
// Handle globals.
//===----------------------------------------------------------------------===//
+
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
+#include "asan_suppressions.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
namespace __asan {
// Lazy-initialized and never deleted.
static VectorOfGlobals *dynamic_init_globals;
+// We want to remember where a certain range of globals was registered.
+struct GlobalRegistrationSite {
+ u32 stack_id;
+ Global *g_first, *g_last;
+};
+typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector;
+static GlobalRegistrationSiteVector *global_registration_site_vector;
+
ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) {
FastPoisonShadow(g->beg, g->size_with_redzone, value);
}
}
}
+const uptr kMinimalDistanceFromAnotherGlobal = 64;
+
+static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
+ if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
+ if (addr >= g.beg + g.size_with_redzone) return false;
+ return true;
+}
+
static void ReportGlobal(const Global &g, const char *prefix) {
- Report("%s Global: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
- prefix, (void*)g.beg, g.size, g.size_with_redzone, g.name,
+ Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
+ prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
g.module_name, g.has_dynamic_init);
+ if (g.location) {
+ Report(" location (%p): name=%s[%p], %d %d\n", g.location,
+ g.location->filename, g.location->filename, g.location->line_no,
+ g.location->column_no);
+ }
+}
+
+static u32 FindRegistrationSite(const Global *g) {
+ mu_for_globals.CheckLocked();
+ CHECK(global_registration_site_vector);
+ for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
+ GlobalRegistrationSite &grs = (*global_registration_site_vector)[i];
+ if (g >= grs.g_first && g <= grs.g_last)
+ return grs.stack_id;
+ }
+ return 0;
}
-bool DescribeAddressIfGlobal(uptr addr, uptr size) {
- if (!flags()->report_globals) return false;
+int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites,
+ int max_globals) {
+ if (!flags()->report_globals) return 0;
BlockingMutexLock lock(&mu_for_globals);
- bool res = false;
+ int res = 0;
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
const Global &g = *l->g;
if (flags()->report_globals >= 2)
ReportGlobal(g, "Search");
- res |= DescribeAddressRelativeToGlobal(addr, size, g);
+ if (IsAddressNearGlobal(addr, g)) {
+ globals[res] = g;
+ if (reg_sites)
+ reg_sites[res] = FindRegistrationSite(&g);
+ res++;
+ if (res == max_globals) break;
+ }
}
return res;
}
+bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
+ Global g = {};
+ if (GetGlobalsForAddress(addr, &g, nullptr, 1)) {
+ internal_strncpy(descr->name, g.name, descr->name_size);
+ descr->region_address = g.beg;
+ descr->region_size = g.size;
+ descr->region_kind = "global";
+ return true;
+ }
+ return false;
+}
+
// Register a global variable.
// This function may be called more than once for every global
// so we store the globals in a map.
CHECK(AddrIsInMem(g->beg));
CHECK(AddrIsAlignedByGranularity(g->beg));
CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
- if (flags()->poison_heap)
+ // This "ODR violation" detection is fundamentally incompatible with
+ // how GCC registers globals. Disable as useless until rewritten upstream.
+ if (0 && flags()->detect_odr_violation) {
+ // Try detecting ODR (One Definition Rule) violation, i.e. the situation
+ // where two globals with the same name are defined in different modules.
+ if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) {
+ // This check may not be enough: if the first global is much larger
+ // the entire redzone of the second global may be within the first global.
+ for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
+ if (g->beg == l->g->beg &&
+ (flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
+ !IsODRViolationSuppressed(g->name))
+ ReportODRViolation(g, FindRegistrationSite(g),
+ l->g, FindRegistrationSite(l->g));
+ }
+ }
+ }
+ if (CanPoisonMemory())
PoisonRedZones(*g);
ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals;
l->g = g;
l->next = list_of_all_globals;
list_of_all_globals = l;
if (g->has_dynamic_init) {
- if (dynamic_init_globals == 0) {
+ if (!dynamic_init_globals) {
dynamic_init_globals = new(allocator_for_globals)
VectorOfGlobals(kDynamicInitGlobalsInitialCapacity);
}
static void UnregisterGlobal(const Global *g) {
CHECK(asan_inited);
+ if (flags()->report_globals >= 2)
+ ReportGlobal(*g, "Removed");
CHECK(flags()->report_globals);
CHECK(AddrIsInMem(g->beg));
CHECK(AddrIsAlignedByGranularity(g->beg));
CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
- if (flags()->poison_heap)
+ if (CanPoisonMemory())
PoisonShadowForGlobal(g, 0);
// We unpoison the shadow memory for the global but we do not remove it from
// the list because that would require O(n^2) time with the current list
}
}
-} // namespace __asan
+} // namespace __asan
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
// Register an array of globals.
void __asan_register_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
+ GET_STACK_TRACE_MALLOC;
+ u32 stack_id = StackDepotPut(stack);
BlockingMutexLock lock(&mu_for_globals);
+ if (!global_registration_site_vector)
+ global_registration_site_vector =
+ new(allocator_for_globals) GlobalRegistrationSiteVector(128);
+ GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]};
+ global_registration_site_vector->push_back(site);
+ if (flags()->report_globals >= 2) {
+ PRINT_CURRENT_STACK();
+ Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]);
+ }
for (uptr i = 0; i < n; i++) {
RegisterGlobal(&globals[i]);
}
// initializer can only touch global variables in the same TU.
void __asan_before_dynamic_init(const char *module_name) {
if (!flags()->check_initialization_order ||
- !flags()->poison_heap)
+ !CanPoisonMemory())
return;
bool strict_init_order = flags()->strict_init_order;
CHECK(dynamic_init_globals);
// TU are poisoned. It simply unpoisons all dynamically initialized globals.
void __asan_after_dynamic_init() {
if (!flags()->check_initialization_order ||
- !flags()->poison_heap)
+ !CanPoisonMemory())
return;
CHECK(asan_inited);
BlockingMutexLock lock(&mu_for_globals);
--- /dev/null
+//===-- asan_init_version.h -------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This header defines a versioned __asan_init function to be called at the
+// startup of the instrumented program.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_INIT_VERSION_H
+#define ASAN_INIT_VERSION_H
+
+extern "C" {
+ // Every time the ASan ABI changes we also change the version number in the
+ // __asan_init function name. Objects built with incompatible ASan ABI
+ // versions will not link with run-time.
+ // Changes between ABI versions:
+ // v1=>v2: added 'module_name' to __asan_global
+ // v2=>v3: stack frame description (created by the compiler)
+ // contains the function PC as the 3-rd field (see
+ // DescribeAddressIfStack).
+ // v3=>v4: added '__asan_global_source_location' to __asan_global.
+ // v4=>v5: changed the semantics and format of __asan_stack_malloc_ and
+ // __asan_stack_free_ functions.
+ // v5=>v6: changed the name of the version check symbol
+ #define __asan_version_mismatch_check __asan_version_mismatch_check_v6
+}
+
+#endif // ASAN_INIT_VERSION_H
+++ /dev/null
-//===-- asan_intercepted_functions.h ----------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// ASan-private header containing prototypes for wrapper functions and wrappers
-//===----------------------------------------------------------------------===//
-#ifndef ASAN_INTERCEPTED_FUNCTIONS_H
-#define ASAN_INTERCEPTED_FUNCTIONS_H
-
-#include "sanitizer_common/sanitizer_platform_interceptors.h"
-
-// Use macro to describe if specific function should be
-// intercepted on a given platform.
-#if !SANITIZER_WINDOWS
-# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1
-# define ASAN_INTERCEPT__LONGJMP 1
-# define ASAN_INTERCEPT_STRDUP 1
-# define ASAN_INTERCEPT_INDEX 1
-# define ASAN_INTERCEPT_PTHREAD_CREATE 1
-# define ASAN_INTERCEPT_MLOCKX 1
-#else
-# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
-# define ASAN_INTERCEPT__LONGJMP 0
-# define ASAN_INTERCEPT_STRDUP 0
-# define ASAN_INTERCEPT_INDEX 0
-# define ASAN_INTERCEPT_PTHREAD_CREATE 0
-# define ASAN_INTERCEPT_MLOCKX 0
-#endif
-
-#if SANITIZER_LINUX
-# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1
-#else
-# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0
-#endif
-
-#if !SANITIZER_MAC
-# define ASAN_INTERCEPT_STRNLEN 1
-#else
-# define ASAN_INTERCEPT_STRNLEN 0
-#endif
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-# define ASAN_INTERCEPT_SWAPCONTEXT 1
-#else
-# define ASAN_INTERCEPT_SWAPCONTEXT 0
-#endif
-
-#if !SANITIZER_ANDROID && !SANITIZER_WINDOWS
-# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1
-#else
-# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0
-#endif
-
-#if !SANITIZER_WINDOWS
-# define ASAN_INTERCEPT_SIGLONGJMP 1
-#else
-# define ASAN_INTERCEPT_SIGLONGJMP 0
-#endif
-
-#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS
-# define ASAN_INTERCEPT___CXA_THROW 1
-#else
-# define ASAN_INTERCEPT___CXA_THROW 0
-#endif
-
-#if !SANITIZER_WINDOWS
-# define ASAN_INTERCEPT___CXA_ATEXIT 1
-#else
-# define ASAN_INTERCEPT___CXA_ATEXIT 0
-#endif
-
-#endif // ASAN_INTERCEPTED_FUNCTIONS_H
//
// Intercept various libc functions.
//===----------------------------------------------------------------------===//
-#include "asan_interceptors.h"
+#include "asan_interceptors.h"
#include "asan_allocator.h"
-#include "asan_intercepted_functions.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_poisoning.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
-#include "interception/interception.h"
+#include "asan_suppressions.h"
#include "sanitizer_common/sanitizer_libc.h"
+#if SANITIZER_POSIX
+#include "sanitizer_common/sanitizer_posix.h"
+#endif
+
+#if defined(__i386) && SANITIZER_LINUX
+#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.1"
+#elif defined(__mips__) && SANITIZER_LINUX
+#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2"
+#endif
+
namespace __asan {
// Return true if we can quickly decide that the region is unpoisoned.
return false;
}
+struct AsanInterceptorContext {
+ const char *interceptor_name;
+};
+
// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE,
// and ASAN_WRITE_RANGE as macro instead of function so
// that no extra frames are created, and stack trace contains
// relevant information only.
// We check all shadow bytes.
-#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
+#define ACCESS_MEMORY_RANGE(ctx, offset, size, isWrite) do { \
uptr __offset = (uptr)(offset); \
uptr __size = (uptr)(size); \
uptr __bad = 0; \
+ if (__offset > __offset + __size) { \
+ GET_STACK_TRACE_FATAL_HERE; \
+ ReportStringFunctionSizeOverflow(__offset, __size, &stack); \
+ } \
if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \
(__bad = __asan_region_is_poisoned(__offset, __size))) { \
- GET_CURRENT_PC_BP_SP; \
- __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \
+ AsanInterceptorContext *_ctx = (AsanInterceptorContext *)ctx; \
+ bool suppressed = false; \
+ if (_ctx) { \
+ suppressed = IsInterceptorSuppressed(_ctx->interceptor_name); \
+ if (!suppressed && HaveStackTraceBasedSuppressions()) { \
+ GET_STACK_TRACE_FATAL_HERE; \
+ suppressed = IsStackTraceSuppressed(&stack); \
+ } \
+ } \
+ if (!suppressed) { \
+ GET_CURRENT_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false);\
+ } \
} \
} while (0)
-#define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false)
-#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true)
+#define ASAN_READ_RANGE(ctx, offset, size) \
+ ACCESS_MEMORY_RANGE(ctx, offset, size, false)
+#define ASAN_WRITE_RANGE(ctx, offset, size) \
+ ACCESS_MEMORY_RANGE(ctx, offset, size, true)
+
+#define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \
+ ASAN_READ_RANGE((ctx), (s), \
+ common_flags()->strict_string_checks ? (len) + 1 : (n))
+
+#define ASAN_READ_STRING(ctx, s, n) \
+ ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
// Behavior of functions like "memcpy" or "strcpy" is undefined
// if memory intervals overlap. We report error in this case.
} \
} while (0)
-#define ENSURE_ASAN_INITED() do { \
- CHECK(!asan_init_is_running); \
- if (!asan_inited) { \
- __asan_init(); \
- } \
-} while (0)
-
static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
#if ASAN_INTERCEPT_STRNLEN
- if (REAL(strnlen) != 0) {
+ if (REAL(strnlen)) {
return REAL(strnlen)(s, maxlen);
}
#endif
return 0;
}
-} // namespace __asan
+} // namespace __asan
// ---------------------- Wrappers ---------------- {{{1
using namespace __asan; // NOLINT
DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
-#if !SANITIZER_MAC
-#define ASAN_INTERCEPT_FUNC(name) \
- do { \
- if ((!INTERCEPT_FUNCTION(name) || !REAL(name)) && \
- common_flags()->verbosity > 0) \
- Report("AddressSanitizer: failed to intercept '" #name "'\n"); \
- } while (0)
-#else
-// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
-#define ASAN_INTERCEPT_FUNC(name)
-#endif // SANITIZER_MAC
+#define ASAN_INTERCEPTOR_ENTER(ctx, func) \
+ AsanInterceptorContext _ctx = {#func}; \
+ ctx = (void *)&_ctx; \
+ (void) ctx; \
#define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name)
-#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \
- do { \
- } while (false)
#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
- ASAN_WRITE_RANGE(ptr, size)
-#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) ASAN_READ_RANGE(ptr, size)
-#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
- do { \
- if (asan_init_is_running) return REAL(func)(__VA_ARGS__); \
- ctx = 0; \
- (void) ctx; \
- if (SANITIZER_MAC && !asan_inited) return REAL(func)(__VA_ARGS__); \
- ENSURE_ASAN_INITED(); \
+ ASAN_WRITE_RANGE(ctx, ptr, size)
+#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
+ ASAN_READ_RANGE(ctx, ptr, size)
+#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
+ ASAN_INTERCEPTOR_ENTER(ctx, func); \
+ do { \
+ if (asan_init_is_running) \
+ return REAL(func)(__VA_ARGS__); \
+ if (SANITIZER_MAC && UNLIKELY(!asan_inited)) \
+ return REAL(func)(__VA_ARGS__); \
+ ENSURE_ASAN_INITED(); \
+ } while (false)
+#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
+ do { \
} while (false)
#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
do { \
do { \
} while (false)
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
+// Strict init-order checking is dlopen-hostile:
+// https://code.google.com/p/address-sanitizer/issues/detail?id=178
+#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \
+ if (flags()->strict_init_order) { \
+ StopInitOrderChecking(); \
+ }
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
+#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \
+ CoverageUpdateMapping()
+#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CoverageUpdateMapping()
+#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited)
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
+ if (AsanThread *t = GetCurrentThread()) { \
+ *begin = t->tls_begin(); \
+ *end = t->tls_end(); \
+ } else { \
+ *begin = *end = 0; \
+ }
#include "sanitizer_common/sanitizer_common_interceptors.inc"
-#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s)
-#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(p, s)
+// Syscall interceptors don't have contexts, we don't support suppressions
+// for them.
+#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(nullptr, p, s)
+#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(nullptr, p, s)
#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
do { \
(void)(p); \
} while (false)
#include "sanitizer_common/sanitizer_common_syscalls.inc"
+struct ThreadStartParam {
+ atomic_uintptr_t t;
+ atomic_uintptr_t is_registered;
+};
+
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
- AsanThread *t = (AsanThread*)arg;
+ ThreadStartParam *param = reinterpret_cast<ThreadStartParam *>(arg);
+ AsanThread *t = nullptr;
+ while ((t = reinterpret_cast<AsanThread *>(
+ atomic_load(¶m->t, memory_order_acquire))) == nullptr)
+ internal_sched_yield();
SetCurrentThread(t);
- return t->ThreadStart(GetTid());
+ return t->ThreadStart(GetTid(), ¶m->is_registered);
}
#if ASAN_INTERCEPT_PTHREAD_CREATE
INTERCEPTOR(int, pthread_create, void *thread,
void *attr, void *(*start_routine)(void*), void *arg) {
EnsureMainThreadIDIsCorrect();
- // Strict init-order checking in thread-hostile.
+ // Strict init-order checking is thread-hostile.
if (flags()->strict_init_order)
StopInitOrderChecking();
GET_STACK_TRACE_THREAD;
int detached = 0;
- if (attr != 0)
+ if (attr)
REAL(pthread_attr_getdetachstate)(attr, &detached);
+ ThreadStartParam param;
+ atomic_store(¶m.t, 0, memory_order_relaxed);
+ atomic_store(¶m.is_registered, 0, memory_order_relaxed);
+ int result = REAL(pthread_create)(thread, attr, asan_thread_start, ¶m);
+ if (result == 0) {
+ u32 current_tid = GetCurrentTidOrInvalid();
+ AsanThread *t =
+ AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
+ atomic_store(¶m.t, reinterpret_cast<uptr>(t), memory_order_release);
+ // Wait until the AsanThread object is initialized and the ThreadRegistry
+ // entry is in "started" state. One reason for this is that after this
+ // interceptor exits, the child thread's stack may be the only thing holding
+ // the |arg| pointer. This may cause LSan to report a leak if leak checking
+ // happens at a point when the interceptor has already exited, but the stack
+ // range for the child thread is not yet known.
+ while (atomic_load(¶m.is_registered, memory_order_acquire) == 0)
+ internal_sched_yield();
+ }
+ return result;
+}
- u32 current_tid = GetCurrentTidOrInvalid();
- AsanThread *t = AsanThread::Create(start_routine, arg);
- CreateThreadContextArgs args = { t, &stack };
- asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args);
- return REAL(pthread_create)(thread, attr, asan_thread_start, t);
+INTERCEPTOR(int, pthread_join, void *t, void **arg) {
+ return real_pthread_join(t, arg);
}
+
+DEFINE_REAL_PTHREAD_FUNCTIONS
#endif // ASAN_INTERCEPT_PTHREAD_CREATE
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
+
+#if SANITIZER_ANDROID
+INTERCEPTOR(void*, bsd_signal, int signum, void *handler) {
+ if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) {
+ return REAL(bsd_signal)(signum, handler);
+ }
+ return 0;
+}
+#endif
+
INTERCEPTOR(void*, signal, int signum, void *handler) {
- if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) {
+ if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) {
return REAL(signal)(signum, handler);
}
- return 0;
+ return nullptr;
}
INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact) {
- if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) {
+ if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) {
return REAL(sigaction)(signum, act, oldact);
}
return 0;
}
+
+namespace __sanitizer {
+int real_sigaction(int signum, const void *act, void *oldact) {
+ return REAL(sigaction)(signum, (const struct sigaction *)act,
+ (struct sigaction *)oldact);
+}
+} // namespace __sanitizer
+
#elif SANITIZER_POSIX
// We need to have defined REAL(sigaction) on posix systems.
DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act,
ssize += stack - bottom;
ssize = RoundUpTo(ssize, PageSize);
static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb
- if (ssize && ssize <= kMaxSaneContextStackSize) {
+ if (AddrIsInMem(bottom) && ssize && ssize <= kMaxSaneContextStackSize) {
PoisonShadow(bottom, ssize, 0);
}
}
}
#endif
-// intercept mlock and friends.
-// Since asan maps 16T of RAM, mlock is completely unfriendly to asan.
-// All functions return 0 (success).
-static void MlockIsUnsupported() {
- static bool printed = false;
- if (printed) return;
- printed = true;
- if (common_flags()->verbosity > 0) {
- Printf("INFO: AddressSanitizer ignores "
- "mlock/mlockall/munlock/munlockall\n");
- }
-}
+// memcpy is called during __asan_init() from the internals of printf(...).
+// We do not treat memcpy with to==from as a bug.
+// See http://llvm.org/bugs/show_bug.cgi?id=11763.
+#define ASAN_MEMCPY_IMPL(ctx, to, from, size) do { \
+ if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); \
+ if (asan_init_is_running) { \
+ return REAL(memcpy)(to, from, size); \
+ } \
+ ENSURE_ASAN_INITED(); \
+ if (flags()->replace_intrin) { \
+ if (to != from) { \
+ CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); \
+ } \
+ ASAN_READ_RANGE(ctx, from, size); \
+ ASAN_WRITE_RANGE(ctx, to, size); \
+ } \
+ return REAL(memcpy)(to, from, size); \
+ } while (0)
-INTERCEPTOR(int, mlock, const void *addr, uptr len) {
- MlockIsUnsupported();
- return 0;
-}
-INTERCEPTOR(int, munlock, const void *addr, uptr len) {
- MlockIsUnsupported();
- return 0;
+void *__asan_memcpy(void *to, const void *from, uptr size) {
+ ASAN_MEMCPY_IMPL(nullptr, to, from, size);
}
-INTERCEPTOR(int, mlockall, int flags) {
- MlockIsUnsupported();
- return 0;
-}
+// memset is called inside Printf.
+#define ASAN_MEMSET_IMPL(ctx, block, c, size) do { \
+ if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); \
+ if (asan_init_is_running) { \
+ return REAL(memset)(block, c, size); \
+ } \
+ ENSURE_ASAN_INITED(); \
+ if (flags()->replace_intrin) { \
+ ASAN_WRITE_RANGE(ctx, block, size); \
+ } \
+ return REAL(memset)(block, c, size); \
+ } while (0)
-INTERCEPTOR(int, munlockall, void) {
- MlockIsUnsupported();
- return 0;
+void *__asan_memset(void *block, int c, uptr size) {
+ ASAN_MEMSET_IMPL(nullptr, block, c, size);
}
-static inline int CharCmp(unsigned char c1, unsigned char c2) {
- return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
-}
+#define ASAN_MEMMOVE_IMPL(ctx, to, from, size) do { \
+ if (UNLIKELY(!asan_inited)) \
+ return internal_memmove(to, from, size); \
+ ENSURE_ASAN_INITED(); \
+ if (flags()->replace_intrin) { \
+ ASAN_READ_RANGE(ctx, from, size); \
+ ASAN_WRITE_RANGE(ctx, to, size); \
+ } \
+ return internal_memmove(to, from, size); \
+ } while (0)
-INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
- if (!asan_inited) return internal_memcmp(a1, a2, size);
- ENSURE_ASAN_INITED();
- if (flags()->replace_intrin) {
- if (flags()->strict_memcmp) {
- // Check the entire regions even if the first bytes of the buffers are
- // different.
- ASAN_READ_RANGE(a1, size);
- ASAN_READ_RANGE(a2, size);
- // Fallthrough to REAL(memcmp) below.
- } else {
- unsigned char c1 = 0, c2 = 0;
- const unsigned char *s1 = (const unsigned char*)a1;
- const unsigned char *s2 = (const unsigned char*)a2;
- uptr i;
- for (i = 0; i < size; i++) {
- c1 = s1[i];
- c2 = s2[i];
- if (c1 != c2) break;
- }
- ASAN_READ_RANGE(s1, Min(i + 1, size));
- ASAN_READ_RANGE(s2, Min(i + 1, size));
- return CharCmp(c1, c2);
- }
- }
- return REAL(memcmp(a1, a2, size));
+void *__asan_memmove(void *to, const void *from, uptr size) {
+ ASAN_MEMMOVE_IMPL(nullptr, to, from, size);
}
-#define MEMMOVE_BODY { \
- if (!asan_inited) return internal_memmove(to, from, size); \
- if (asan_init_is_running) { \
- return REAL(memmove)(to, from, size); \
- } \
- ENSURE_ASAN_INITED(); \
- if (flags()->replace_intrin) { \
- ASAN_READ_RANGE(from, size); \
- ASAN_WRITE_RANGE(to, size); \
- } \
- return internal_memmove(to, from, size); \
+INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, memmove);
+ ASAN_MEMMOVE_IMPL(ctx, to, from, size);
}
-INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) MEMMOVE_BODY
-
INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, memcpy);
#if !SANITIZER_MAC
- if (!asan_inited) return internal_memcpy(to, from, size);
- // memcpy is called during __asan_init() from the internals
- // of printf(...).
- if (asan_init_is_running) {
- return REAL(memcpy)(to, from, size);
- }
- ENSURE_ASAN_INITED();
- if (flags()->replace_intrin) {
- if (to != from) {
- // We do not treat memcpy with to==from as a bug.
- // See http://llvm.org/bugs/show_bug.cgi?id=11763.
- CHECK_RANGES_OVERLAP("memcpy", to, size, from, size);
- }
- ASAN_READ_RANGE(from, size);
- ASAN_WRITE_RANGE(to, size);
- }
- // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8, so
- // calling REAL(memcpy) here leads to infinite recursion.
- // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116.
- return internal_memcpy(to, from, size);
+ ASAN_MEMCPY_IMPL(ctx, to, from, size);
#else
// At least on 10.7 and 10.8 both memcpy() and memmove() are being replaced
// with WRAP(memcpy). As a result, false positives are reported for memmove()
// ASAN_OPTIONS=replace_intrin=0, memmove() is still replaced with
// internal_memcpy(), which may lead to crashes, see
// http://llvm.org/bugs/show_bug.cgi?id=16362.
- MEMMOVE_BODY
+ ASAN_MEMMOVE_IMPL(ctx, to, from, size);
#endif // !SANITIZER_MAC
}
INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
- if (!asan_inited) return internal_memset(block, c, size);
- // memset is called inside Printf.
- if (asan_init_is_running) {
- return REAL(memset)(block, c, size);
- }
- ENSURE_ASAN_INITED();
- if (flags()->replace_intrin) {
- ASAN_WRITE_RANGE(block, size);
- }
- return REAL(memset)(block, c, size);
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, memset);
+ ASAN_MEMSET_IMPL(ctx, block, c, size);
}
INTERCEPTOR(char*, strchr, const char *str, int c) {
- if (!asan_inited) return internal_strchr(str, c);
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strchr);
+ if (UNLIKELY(!asan_inited)) return internal_strchr(str, c);
// strchr is called inside create_purgeable_zone() when MallocGuardEdges=1 is
// used.
if (asan_init_is_running) {
ENSURE_ASAN_INITED();
char *result = REAL(strchr)(str, c);
if (flags()->replace_str) {
- uptr bytes_read = (result ? result - str : REAL(strlen)(str)) + 1;
- ASAN_READ_RANGE(str, bytes_read);
+ uptr len = REAL(strlen)(str);
+ uptr bytes_read = (result ? result - str : len) + 1;
+ ASAN_READ_STRING_OF_LEN(ctx, str, len, bytes_read);
}
return result;
}
// For both strcat() and strncat() we need to check the validity of |to|
// argument irrespective of the |from| length.
INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strcat); // NOLINT
ENSURE_ASAN_INITED();
if (flags()->replace_str) {
uptr from_length = REAL(strlen)(from);
- ASAN_READ_RANGE(from, from_length + 1);
+ ASAN_READ_RANGE(ctx, from, from_length + 1);
uptr to_length = REAL(strlen)(to);
- ASAN_READ_RANGE(to, to_length);
- ASAN_WRITE_RANGE(to + to_length, from_length + 1);
+ ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
+ ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
// If the copying actually happens, the |from| string should not overlap
// with the resulting string starting at |to|, which has a length of
// to_length + from_length + 1.
}
INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strncat);
ENSURE_ASAN_INITED();
if (flags()->replace_str) {
uptr from_length = MaybeRealStrnlen(from, size);
uptr copy_length = Min(size, from_length + 1);
- ASAN_READ_RANGE(from, copy_length);
+ ASAN_READ_RANGE(ctx, from, copy_length);
uptr to_length = REAL(strlen)(to);
- ASAN_READ_RANGE(to, to_length);
- ASAN_WRITE_RANGE(to + to_length, from_length + 1);
+ ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
+ ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
if (from_length > 0) {
CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1,
from, copy_length);
}
INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strcpy); // NOLINT
#if SANITIZER_MAC
- if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT
+ if (UNLIKELY(!asan_inited)) return REAL(strcpy)(to, from); // NOLINT
#endif
// strcpy is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (flags()->replace_str) {
uptr from_size = REAL(strlen)(from) + 1;
CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size);
- ASAN_READ_RANGE(from, from_size);
- ASAN_WRITE_RANGE(to, from_size);
+ ASAN_READ_RANGE(ctx, from, from_size);
+ ASAN_WRITE_RANGE(ctx, to, from_size);
}
return REAL(strcpy)(to, from); // NOLINT
}
#if ASAN_INTERCEPT_STRDUP
INTERCEPTOR(char*, strdup, const char *s) {
- if (!asan_inited) return internal_strdup(s);
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strdup);
+ if (UNLIKELY(!asan_inited)) return internal_strdup(s);
ENSURE_ASAN_INITED();
uptr length = REAL(strlen)(s);
if (flags()->replace_str) {
- ASAN_READ_RANGE(s, length + 1);
+ ASAN_READ_RANGE(ctx, s, length + 1);
}
GET_STACK_TRACE_MALLOC;
void *new_mem = asan_malloc(length + 1, &stack);
}
#endif
-INTERCEPTOR(uptr, strlen, const char *s) {
- if (!asan_inited) return internal_strlen(s);
+INTERCEPTOR(SIZE_T, strlen, const char *s) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strlen);
+ if (UNLIKELY(!asan_inited)) return internal_strlen(s);
// strlen is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
return REAL(strlen)(s);
}
ENSURE_ASAN_INITED();
- uptr length = REAL(strlen)(s);
+ SIZE_T length = REAL(strlen)(s);
if (flags()->replace_str) {
- ASAN_READ_RANGE(s, length + 1);
+ ASAN_READ_RANGE(ctx, s, length + 1);
}
return length;
}
-INTERCEPTOR(uptr, wcslen, const wchar_t *s) {
- uptr length = REAL(wcslen)(s);
+INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, wcslen);
+ SIZE_T length = REAL(wcslen)(s);
if (!asan_init_is_running) {
ENSURE_ASAN_INITED();
- ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t));
+ ASAN_READ_RANGE(ctx, s, (length + 1) * sizeof(wchar_t));
}
return length;
}
INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strncpy);
ENSURE_ASAN_INITED();
if (flags()->replace_str) {
uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1);
CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size);
- ASAN_READ_RANGE(from, from_size);
- ASAN_WRITE_RANGE(to, size);
+ ASAN_READ_RANGE(ctx, from, from_size);
+ ASAN_WRITE_RANGE(ctx, to, size);
}
return REAL(strncpy)(to, from, size);
}
#if ASAN_INTERCEPT_STRNLEN
INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strnlen);
ENSURE_ASAN_INITED();
uptr length = REAL(strnlen)(s, maxlen);
if (flags()->replace_str) {
- ASAN_READ_RANGE(s, Min(length + 1, maxlen));
+ ASAN_READ_RANGE(ctx, s, Min(length + 1, maxlen));
}
return length;
}
#endif // ASAN_INTERCEPT_STRNLEN
-static inline bool IsValidStrtolBase(int base) {
- return (base == 0) || (2 <= base && base <= 36);
-}
-
-static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
- CHECK(endptr);
- if (nptr == *endptr) {
- // No digits were found at strtol call, we need to find out the last
- // symbol accessed by strtoll on our own.
- // We get this symbol by skipping leading blanks and optional +/- sign.
- while (IsSpace(*nptr)) nptr++;
- if (*nptr == '+' || *nptr == '-') nptr++;
- *endptr = (char*)nptr;
- }
- CHECK(*endptr >= nptr);
-}
-
INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
char **endptr, int base) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strtol);
ENSURE_ASAN_INITED();
if (!flags()->replace_str) {
return REAL(strtol)(nptr, endptr, base);
}
char *real_endptr;
long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT
- if (endptr != 0) {
- *endptr = real_endptr;
- }
- if (IsValidStrtolBase(base)) {
- FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
- }
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return result;
}
INTERCEPTOR(int, atoi, const char *nptr) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, atoi);
#if SANITIZER_MAC
- if (!asan_inited) return REAL(atoi)(nptr);
+ if (UNLIKELY(!asan_inited)) return REAL(atoi)(nptr);
#endif
ENSURE_ASAN_INITED();
if (!flags()->replace_str) {
// different from int). So, we just imitate this behavior.
int result = REAL(strtol)(nptr, &real_endptr, 10);
FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
+ ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
return result;
}
INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, atol);
#if SANITIZER_MAC
- if (!asan_inited) return REAL(atol)(nptr);
+ if (UNLIKELY(!asan_inited)) return REAL(atol)(nptr);
#endif
ENSURE_ASAN_INITED();
if (!flags()->replace_str) {
char *real_endptr;
long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT
FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
+ ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
return result;
}
#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT
char **endptr, int base) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strtoll);
ENSURE_ASAN_INITED();
if (!flags()->replace_str) {
return REAL(strtoll)(nptr, endptr, base);
}
char *real_endptr;
long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT
- if (endptr != 0) {
- *endptr = real_endptr;
- }
- // If base has unsupported value, strtoll can exit with EINVAL
- // without reading any characters. So do additional checks only
- // if base is valid.
- if (IsValidStrtolBase(base)) {
- FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
- }
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return result;
}
INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, atoll);
ENSURE_ASAN_INITED();
if (!flags()->replace_str) {
return REAL(atoll)(nptr);
char *real_endptr;
long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT
FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1);
+ ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
return result;
}
#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL
INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
void *dso_handle) {
#if SANITIZER_MAC
- if (!asan_inited) return REAL(__cxa_atexit)(func, arg, dso_handle);
+ if (UNLIKELY(!asan_inited)) return REAL(__cxa_atexit)(func, arg, dso_handle);
#endif
ENSURE_ASAN_INITED();
int res = REAL(__cxa_atexit)(func, arg, dso_handle);
- REAL(__cxa_atexit)(AtCxaAtexit, 0, 0);
+ REAL(__cxa_atexit)(AtCxaAtexit, nullptr, nullptr);
return res;
}
#endif // ASAN_INTERCEPT___CXA_ATEXIT
-#if SANITIZER_WINDOWS
-INTERCEPTOR_WINAPI(DWORD, CreateThread,
- void* security, uptr stack_size,
- DWORD (__stdcall *start_routine)(void*), void* arg,
- DWORD thr_flags, void* tid) {
- // Strict init-order checking in thread-hostile.
- if (flags()->strict_init_order)
- StopInitOrderChecking();
- GET_STACK_TRACE_THREAD;
- u32 current_tid = GetCurrentTidOrInvalid();
- AsanThread *t = AsanThread::Create(start_routine, arg);
- CreateThreadContextArgs args = { t, &stack };
- bool detached = false; // FIXME: how can we determine it on Windows?
- asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args);
- return REAL(CreateThread)(security, stack_size,
- asan_thread_start, t, thr_flags, tid);
-}
-
-namespace __asan {
-void InitializeWindowsInterceptors() {
- ASAN_INTERCEPT_FUNC(CreateThread);
+#if ASAN_INTERCEPT_FORK
+INTERCEPTOR(int, fork, void) {
+ ENSURE_ASAN_INITED();
+ if (common_flags()->coverage) CovBeforeFork();
+ int pid = REAL(fork)();
+ if (common_flags()->coverage) CovAfterFork(pid);
+ return pid;
}
-
-} // namespace __asan
-#endif
+#endif // ASAN_INTERCEPT_FORK
// ---------------------- InitializeAsanInterceptors ---------------- {{{1
namespace __asan {
static bool was_called_once;
CHECK(was_called_once == false);
was_called_once = true;
- SANITIZER_COMMON_INTERCEPTORS_INIT;
+ InitializeCommonInterceptors();
// Intercept mem* functions.
- ASAN_INTERCEPT_FUNC(memcmp);
ASAN_INTERCEPT_FUNC(memmove);
ASAN_INTERCEPT_FUNC(memset);
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
ASAN_INTERCEPT_FUNC(strtoll);
#endif
-#if ASAN_INTERCEPT_MLOCKX
- // Intercept mlock/munlock.
- ASAN_INTERCEPT_FUNC(mlock);
- ASAN_INTERCEPT_FUNC(munlock);
- ASAN_INTERCEPT_FUNC(mlockall);
- ASAN_INTERCEPT_FUNC(munlockall);
-#endif
-
// Intecept signal- and jump-related functions.
ASAN_INTERCEPT_FUNC(longjmp);
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
ASAN_INTERCEPT_FUNC(sigaction);
+#if SANITIZER_ANDROID
+ ASAN_INTERCEPT_FUNC(bsd_signal);
+#endif
ASAN_INTERCEPT_FUNC(signal);
#endif
#if ASAN_INTERCEPT_SWAPCONTEXT
// Intercept exception handling functions.
#if ASAN_INTERCEPT___CXA_THROW
- INTERCEPT_FUNCTION(__cxa_throw);
+ ASAN_INTERCEPT_FUNC(__cxa_throw);
#endif
// Intercept threading-related functions
#if ASAN_INTERCEPT_PTHREAD_CREATE
+#if defined(ASAN_PTHREAD_CREATE_VERSION)
+ ASAN_INTERCEPT_FUNC_VER(pthread_create, ASAN_PTHREAD_CREATE_VERSION);
+#else
ASAN_INTERCEPT_FUNC(pthread_create);
#endif
+ ASAN_INTERCEPT_FUNC(pthread_join);
+#endif
// Intercept atexit function.
#if ASAN_INTERCEPT___CXA_ATEXIT
ASAN_INTERCEPT_FUNC(__cxa_atexit);
#endif
- // Some Windows-specific interceptors.
-#if SANITIZER_WINDOWS
- InitializeWindowsInterceptors();
+#if ASAN_INTERCEPT_FORK
+ ASAN_INTERCEPT_FUNC(fork);
#endif
- if (common_flags()->verbosity > 0) {
- Report("AddressSanitizer: libc interceptors initialized\n");
- }
+ InitializePlatformInterceptors();
+
+ VReport(1, "AddressSanitizer: libc interceptors initialized\n");
}
-} // namespace __asan
+} // namespace __asan
#include "asan_internal.h"
#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_platform_interceptors.h"
+
+// Use macro to describe if specific function should be
+// intercepted on a given platform.
+#if !SANITIZER_WINDOWS
+# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1
+# define ASAN_INTERCEPT__LONGJMP 1
+# define ASAN_INTERCEPT_STRDUP 1
+# define ASAN_INTERCEPT_INDEX 1
+# define ASAN_INTERCEPT_PTHREAD_CREATE 1
+# define ASAN_INTERCEPT_FORK 1
+#else
+# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
+# define ASAN_INTERCEPT__LONGJMP 0
+# define ASAN_INTERCEPT_STRDUP 0
+# define ASAN_INTERCEPT_INDEX 0
+# define ASAN_INTERCEPT_PTHREAD_CREATE 0
+# define ASAN_INTERCEPT_FORK 0
+#endif
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1
+#else
+# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0
+#endif
+
+#if !SANITIZER_MAC
+# define ASAN_INTERCEPT_STRNLEN 1
+#else
+# define ASAN_INTERCEPT_STRNLEN 0
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+# define ASAN_INTERCEPT_SWAPCONTEXT 1
+#else
+# define ASAN_INTERCEPT_SWAPCONTEXT 0
+#endif
+
+#if !SANITIZER_WINDOWS
+# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1
+#else
+# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0
+#endif
+
+#if !SANITIZER_WINDOWS
+# define ASAN_INTERCEPT_SIGLONGJMP 1
+#else
+# define ASAN_INTERCEPT_SIGLONGJMP 0
+#endif
+
+// Android bug: https://code.google.com/p/android/issues/detail?id=61799
+#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS && \
+ !(SANITIZER_ANDROID && defined(__i386))
+# define ASAN_INTERCEPT___CXA_THROW 1
+#else
+# define ASAN_INTERCEPT___CXA_THROW 0
+#endif
+
+#if !SANITIZER_WINDOWS
+# define ASAN_INTERCEPT___CXA_ATEXIT 1
+#else
+# define ASAN_INTERCEPT___CXA_ATEXIT 0
+#endif
DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size)
DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size)
DECLARE_REAL(void*, memset, void *block, int c, uptr size)
DECLARE_REAL(char*, strchr, const char *str, int c)
-DECLARE_REAL(uptr, strlen, const char *s)
+DECLARE_REAL(SIZE_T, strlen, const char *s)
DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size)
DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen)
DECLARE_REAL(char*, strstr, const char *s1, const char *s2)
DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact)
+#if !SANITIZER_MAC
+#define ASAN_INTERCEPT_FUNC(name) \
+ do { \
+ if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
+ VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \
+ } while (0)
+#define ASAN_INTERCEPT_FUNC_VER(name, ver) \
+ do { \
+ if ((!INTERCEPT_FUNCTION_VER(name, ver) || !REAL(name))) \
+ VReport( \
+ 1, "AddressSanitizer: failed to intercept '" #name "@@" #ver "'\n"); \
+ } while (0)
+#else
+// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
+#define ASAN_INTERCEPT_FUNC(name)
+#endif // SANITIZER_MAC
+
namespace __asan {
void InitializeAsanInterceptors();
+void InitializePlatformInterceptors();
+
+#define ENSURE_ASAN_INITED() do { \
+ CHECK(!asan_init_is_running); \
+ if (UNLIKELY(!asan_inited)) { \
+ AsanInitFromRtl(); \
+ } \
+} while (0)
} // namespace __asan
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// This header can be included by the instrumented program to fetch
-// data (mostly allocator statistics) from ASan runtime library.
+// This header declares the AddressSanitizer runtime interface functions.
+// The runtime library has to define these functions so the instrumented program
+// could call them.
+//
+// See also include/sanitizer/asan_interface.h
//===----------------------------------------------------------------------===//
#ifndef ASAN_INTERFACE_INTERNAL_H
#define ASAN_INTERFACE_INTERNAL_H
#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "asan_init_version.h"
+
using __sanitizer::uptr;
extern "C" {
// This function should be called at the very beginning of the process,
// before any instrumented code is executed and before any call to malloc.
- // Everytime the asan ABI changes we also change the version number in this
- // name. Objects build with incompatible asan ABI version
- // will not link with run-time.
- // Changes between ABI versions:
- // v1=>v2: added 'module_name' to __asan_global
- // v2=>v3: stack frame description (created by the compiler)
- // contains the function PC as the 3-rd field (see
- // DescribeAddressIfStack).
- SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v3();
- #define __asan_init __asan_init_v3
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_init();
+
+ // This function exists purely to get a linker/loader error when using
+ // incompatible versions of instrumentation and runtime library. Please note
+ // that __asan_version_mismatch_check is a macro that is replaced with
+ // __asan_version_mismatch_check_vXXX at compile-time.
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_version_mismatch_check();
+
+ // This structure is used to describe the source location of a place where
+ // global was defined.
+ struct __asan_global_source_location {
+ const char *filename;
+ int line_no;
+ int column_no;
+ };
// This structure describes an instrumented global variable.
struct __asan_global {
const char *module_name; // Module name as a C string. This pointer is a
// unique identifier of a module.
uptr has_dynamic_init; // Non-zero if the global has dynamic initializer.
+ __asan_global_source_location *location; // Source location of a global,
+ // or NULL if it is unknown.
};
// These two functions should be called by the instrumented code.
void __asan_unpoison_memory_region(void const volatile *addr, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE
- bool __asan_address_is_poisoned(void const volatile *addr);
+ int __asan_address_is_poisoned(void const volatile *addr);
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_region_is_poisoned(uptr beg, uptr size);
void __asan_describe_address(uptr addr);
SANITIZER_INTERFACE_ATTRIBUTE
- void __asan_report_error(uptr pc, uptr bp, uptr sp,
- uptr addr, bool is_write, uptr access_size);
+ int __asan_report_present();
SANITIZER_INTERFACE_ATTRIBUTE
- int __asan_set_error_exit_code(int exit_code);
+ uptr __asan_get_report_pc();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_report_bp();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_report_sp();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_report_address();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ int __asan_get_report_access_type();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_report_access_size();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ const char * __asan_get_report_description();
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ const char * __asan_locate_address(uptr addr, char *name, uptr name_size,
+ uptr *region_address, uptr *region_size);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size,
+ u32 *thread_id);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size,
+ u32 *thread_id);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_report_error(uptr pc, uptr bp, uptr sp,
+ uptr addr, int is_write, uptr access_size, u32 exp);
+
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_set_death_callback(void (*callback)(void));
SANITIZER_INTERFACE_ATTRIBUTE
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
/* OPTIONAL */ void __asan_on_error();
- SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
- /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer,
- int out_size);
-
- SANITIZER_INTERFACE_ATTRIBUTE
- uptr __asan_get_estimated_allocated_size(uptr size);
-
- SANITIZER_INTERFACE_ATTRIBUTE bool __asan_get_ownership(const void *p);
- SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_allocated_size(const void *p);
- SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_current_allocated_bytes();
- SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_heap_size();
- SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_free_bytes();
- SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_unmapped_bytes();
SANITIZER_INTERFACE_ATTRIBUTE void __asan_print_accumulated_stats();
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
/* OPTIONAL */ const char* __asan_default_options();
- SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
- /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size);
- SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
- /* OPTIONAL */ void __asan_free_hook(void *ptr);
-
// Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return
SANITIZER_INTERFACE_ATTRIBUTE
extern int __asan_option_detect_stack_use_after_return;
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ extern uptr *__asan_test_only_reported_buggy_pointer;
+
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_load1(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_load2(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_load4(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_load8(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_load16(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_store1(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_store2(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_store4(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_store8(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_store16(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN(uptr p, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN(uptr p, uptr size);
+
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_load1_noabort(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_load2_noabort(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_load4_noabort(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_load8_noabort(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_load16_noabort(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_store1_noabort(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_store2_noabort(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_store4_noabort(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_store8_noabort(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_store16_noabort(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN_noabort(uptr p, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN_noabort(uptr p, uptr size);
+
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load1(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load2(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load4(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load8(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load16(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store1(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store2(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store4(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store8(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store16(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_loadN(uptr p, uptr size,
+ u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_storeN(uptr p, uptr size,
+ u32 exp);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void* __asan_memcpy(void *dst, const void *src, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void* __asan_memset(void *s, int c, uptr n);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void* __asan_memmove(void* dest, const void* src, uptr n);
+
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_poison_cxx_array_cookie(uptr p);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ uptr __asan_load_cxx_array_cookie(uptr *p);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_poison_intra_object_redzone(uptr p, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_unpoison_intra_object_redzone(uptr p, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_alloca_poison(uptr addr, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_allocas_unpoison(uptr top, uptr bottom);
} // extern "C"
#endif // ASAN_INTERFACE_INTERNAL_H
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_libc.h"
-#define ASAN_DEFAULT_FAILURE_EXITCODE 1
-
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
# error "The AddressSanitizer run-time should not be"
" instrumented by AddressSanitizer"
// Build-time configuration options.
-// If set, asan will install its own SEGV signal handler.
-#ifndef ASAN_NEEDS_SEGV
-# if SANITIZER_ANDROID == 1
-# define ASAN_NEEDS_SEGV 0
-# else
-# define ASAN_NEEDS_SEGV 1
-# endif
-#endif
-
// If set, asan will intercept C++ exception api call(s).
#ifndef ASAN_HAS_EXCEPTIONS
# define ASAN_HAS_EXCEPTIONS 1
#endif
-// If set, asan uses the values of SHADOW_SCALE and SHADOW_OFFSET
-// provided by the instrumented objects. Otherwise constants are used.
-#ifndef ASAN_FLEXIBLE_MAPPING_AND_OFFSET
-# define ASAN_FLEXIBLE_MAPPING_AND_OFFSET 0
-#endif
-
// If set, values like allocator chunk size, as well as defaults for some flags
// will be changed towards less memory overhead.
#ifndef ASAN_LOW_MEMORY
# endif
#endif
-#ifndef ASAN_USE_PREINIT_ARRAY
-# define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID)
+#ifndef ASAN_DYNAMIC
+# ifdef PIC
+# define ASAN_DYNAMIC 1
+# else
+# define ASAN_DYNAMIC 0
+# endif
#endif
// All internal functions in asan reside inside the __asan namespace
// to avoid namespace collisions with the user programs.
-// Seperate namespace also makes it simpler to distinguish the asan run-time
+// Separate namespace also makes it simpler to distinguish the asan run-time
// functions from the instrumented user code in a profile.
namespace __asan {
class AsanThread;
using __sanitizer::StackTrace;
+void AsanInitFromRtl();
+
// asan_rtl.cc
void NORETURN ShowStatsAndAbort();
-void ReplaceOperatorsNewAndDelete();
// asan_malloc_linux.cc / asan_malloc_mac.cc
void ReplaceSystemMalloc();
// asan_linux.cc / asan_mac.cc / asan_win.cc
void *AsanDoesNotSupportStaticLinkage();
+void AsanCheckDynamicRTPrereqs();
+void AsanCheckIncompatibleRT();
-void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
+void AsanOnDeadlySignal(int, void *siginfo, void *context);
+void DisableReexec();
void MaybeReexec();
-bool AsanInterceptsSignal(int signum);
-void SetAlternateSignalStack();
-void UnsetAlternateSignalStack();
-void InstallSignalHandlers();
void ReadContextStack(void *context, uptr *stack, uptr *ssize);
-void AsanPlatformThreadInit();
void StopInitOrderChecking();
// Wrapper for TLS/TSD.
void AppendToErrorMessageBuffer(const char *buffer);
-// Platfrom-specific options.
+void *AsanDlSymNext(const char *sym);
+
+void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);
+
+// Platform-specific options.
#if SANITIZER_MAC
bool PlatformHasDifferentMemcpyAndMemmove();
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \
// Add convenient macro for interface functions that may be represented as
// weak hooks.
#define ASAN_MALLOC_HOOK(ptr, size) \
- if (&__asan_malloc_hook) __asan_malloc_hook(ptr, size)
+ if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size)
#define ASAN_FREE_HOOK(ptr) \
- if (&__asan_free_hook) __asan_free_hook(ptr)
+ if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr)
#define ASAN_ON_ERROR() \
if (&__asan_on_error) __asan_on_error()
const int kAsanStackUseAfterScopeMagic = 0xf8;
const int kAsanGlobalRedzoneMagic = 0xf9;
const int kAsanInternalHeapMagic = 0xfe;
+const int kAsanArrayCookieMagic = 0xac;
+const int kAsanIntraObjectRedzone = 0xbb;
+const int kAsanAllocaLeftMagic = 0xca;
+const int kAsanAllocaRightMagic = 0xcb;
static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
static const uptr kRetiredStackFrameMagic = 0x45E0360E;
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_LINUX
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_freebsd.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
+#include <dlfcn.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <unwind.h>
-#if !SANITIZER_ANDROID
-// FIXME: where to get ucontext on Android?
-#include <sys/ucontext.h>
+#if SANITIZER_FREEBSD
+#include <sys/link_elf.h>
#endif
+#if SANITIZER_ANDROID || SANITIZER_FREEBSD
+#include <ucontext.h>
extern "C" void* _DYNAMIC;
+#else
+#include <sys/ucontext.h>
+#include <link.h>
+#endif
+
+// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
+// 32-bit mode.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \
+ __FreeBSD_version <= 902001 // v9.2
+#define ucontext_t xucontext_t
+#endif
+
+typedef enum {
+ ASAN_RT_VERSION_UNDEFINED = 0,
+ ASAN_RT_VERSION_DYNAMIC,
+ ASAN_RT_VERSION_STATIC,
+} asan_rt_version_t;
+
+// FIXME: perhaps also store abi version here?
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+asan_rt_version_t __asan_rt_version;
+}
namespace __asan {
+void InitializePlatformInterceptors() {}
+
+void DisableReexec() {
+ // No need to re-exec on Linux.
+}
+
void MaybeReexec() {
// No need to re-exec on Linux.
}
return &_DYNAMIC; // defined in link.h
}
-void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
#if SANITIZER_ANDROID
- *pc = *sp = *bp = 0;
-#elif defined(__arm__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.arm_pc;
- *bp = ucontext->uc_mcontext.arm_fp;
- *sp = ucontext->uc_mcontext.arm_sp;
-# elif defined(__hppa__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.sc_iaoq[0];
- /* GCC uses %r3 whenever a frame pointer is needed. */
- *bp = ucontext->uc_mcontext.sc_gr[3];
- *sp = ucontext->uc_mcontext.sc_gr[30];
-# elif defined(__x86_64__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.gregs[REG_RIP];
- *bp = ucontext->uc_mcontext.gregs[REG_RBP];
- *sp = ucontext->uc_mcontext.gregs[REG_RSP];
-# elif defined(__i386__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.gregs[REG_EIP];
- *bp = ucontext->uc_mcontext.gregs[REG_EBP];
- *sp = ucontext->uc_mcontext.gregs[REG_ESP];
-# elif defined(__powerpc__) || defined(__powerpc64__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.regs->nip;
- *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
- // The powerpc{,64}-linux ABIs do not specify r31 as the frame
- // pointer, but GCC always uses r31 when we need a frame pointer.
- *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
-# elif defined(__sparc__)
- ucontext_t *ucontext = (ucontext_t*)context;
- uptr *stk_ptr;
-# if defined (__arch64__)
- *pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
- *sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
- stk_ptr = (uptr *) (*sp + 2047);
- *bp = stk_ptr[15];
-# else
- *pc = ucontext->uc_mcontext.gregs[REG_PC];
- *sp = ucontext->uc_mcontext.gregs[REG_O6];
- stk_ptr = (uptr *) *sp;
- *bp = stk_ptr[15];
-# endif
-# elif defined(__mips__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.gregs[31];
- *bp = ucontext->uc_mcontext.gregs[30];
- *sp = ucontext->uc_mcontext.gregs[29];
+// FIXME: should we do anything for Android?
+void AsanCheckDynamicRTPrereqs() {}
+void AsanCheckIncompatibleRT() {}
#else
-# error "Unsupported arch"
-#endif
+static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size,
+ void *data) {
+ // Continue until the first dynamic library is found
+ if (!info->dlpi_name || info->dlpi_name[0] == 0)
+ return 0;
+
+ // Ignore vDSO
+ if (internal_strncmp(info->dlpi_name, "linux-", sizeof("linux-") - 1) == 0)
+ return 0;
+
+ *(const char **)data = info->dlpi_name;
+ return 1;
+}
+
+static bool IsDynamicRTName(const char *libname) {
+ return internal_strstr(libname, "libclang_rt.asan") ||
+ internal_strstr(libname, "libasan.so");
+}
+
+static void ReportIncompatibleRT() {
+ Report("Your application is linked against incompatible ASan runtimes.\n");
+ Die();
}
-bool AsanInterceptsSignal(int signum) {
- return signum == SIGSEGV && flags()->handle_segv;
+void AsanCheckDynamicRTPrereqs() {
+ if (!ASAN_DYNAMIC)
+ return;
+
+ // Ensure that dynamic RT is the first DSO in the list
+ const char *first_dso_name = nullptr;
+ dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name);
+ if (first_dso_name && !IsDynamicRTName(first_dso_name)) {
+ Report("ASan runtime does not come first in initial library list; "
+ "you should either link runtime to your application or "
+ "manually preload it with LD_PRELOAD.\n");
+ Die();
+ }
}
-void AsanPlatformThreadInit() {
- // Nothing here for now.
+void AsanCheckIncompatibleRT() {
+ if (ASAN_DYNAMIC) {
+ if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) {
+ __asan_rt_version = ASAN_RT_VERSION_DYNAMIC;
+ } else if (__asan_rt_version != ASAN_RT_VERSION_DYNAMIC) {
+ ReportIncompatibleRT();
+ }
+ } else {
+ if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) {
+ // Ensure that dynamic runtime is not present. We should detect it
+ // as early as possible, otherwise ASan interceptors could bind to
+ // the functions in dynamic ASan runtime instead of the functions in
+ // system libraries, causing crashes later in ASan initialization.
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ char filename[128];
+ while (proc_maps.Next(nullptr, nullptr, nullptr, filename,
+ sizeof(filename), nullptr)) {
+ if (IsDynamicRTName(filename)) {
+ Report("Your application is linked against "
+ "incompatible ASan runtimes.\n");
+ Die();
+ }
+ }
+ __asan_rt_version = ASAN_RT_VERSION_STATIC;
+ } else if (__asan_rt_version != ASAN_RT_VERSION_STATIC) {
+ ReportIncompatibleRT();
+ }
+ }
}
+#endif // SANITIZER_ANDROID
#if !SANITIZER_ANDROID
void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
}
#endif
-} // namespace __asan
+void *AsanDlSymNext(const char *sym) {
+ return dlsym(RTLD_NEXT, sym);
+}
+
+} // namespace __asan
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_mac.h"
#include "asan_mapping.h"
#include "asan_stack.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_mac.h"
+
+#if !SANITIZER_IOS
+#include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron
+#else
+extern "C" {
+ extern char ***_NSGetArgv(void);
+}
+#endif
-#include <crt_externs.h> // for _NSGetArgv
#include <dlfcn.h> // for dladdr()
+#include <fcntl.h>
+#include <libkern/OSAtomic.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
+#include <pthread.h>
+#include <stdlib.h> // for free()
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/sysctl.h>
#include <sys/ucontext.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <stdlib.h> // for free()
#include <unistd.h>
-#include <libkern/OSAtomic.h>
namespace __asan {
-void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
- ucontext_t *ucontext = (ucontext_t*)context;
-# if SANITIZER_WORDSIZE == 64
- *pc = ucontext->uc_mcontext->__ss.__rip;
- *bp = ucontext->uc_mcontext->__ss.__rbp;
- *sp = ucontext->uc_mcontext->__ss.__rsp;
-# else
- *pc = ucontext->uc_mcontext->__ss.__eip;
- *bp = ucontext->uc_mcontext->__ss.__ebp;
- *sp = ucontext->uc_mcontext->__ss.__esp;
-# endif // SANITIZER_WORDSIZE
-}
-
-MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED;
-
-MacosVersion GetMacosVersionInternal() {
- int mib[2] = { CTL_KERN, KERN_OSRELEASE };
- char version[100];
- uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
- for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
- // Get the version length.
- CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1);
- CHECK_LT(len, maxlen);
- CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1);
- switch (version[0]) {
- case '9': return MACOS_VERSION_LEOPARD;
- case '1': {
- switch (version[1]) {
- case '0': return MACOS_VERSION_SNOW_LEOPARD;
- case '1': return MACOS_VERSION_LION;
- case '2': return MACOS_VERSION_MOUNTAIN_LION;
- case '3': return MACOS_VERSION_MAVERICKS;
- default: return MACOS_VERSION_UNKNOWN;
- }
- }
- default: return MACOS_VERSION_UNKNOWN;
- }
-}
-
-MacosVersion GetMacosVersion() {
- atomic_uint32_t *cache =
- reinterpret_cast<atomic_uint32_t*>(&cached_macos_version);
- MacosVersion result =
- static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire));
- if (result == MACOS_VERSION_UNINITIALIZED) {
- result = GetMacosVersionInternal();
- atomic_store(cache, result, memory_order_release);
- }
- return result;
-}
+void InitializePlatformInterceptors() {}
bool PlatformHasDifferentMemcpyAndMemmove() {
// On OS X 10.7 memcpy() and memmove() are both resolved
// otherwise the corresponding "NAME=value" string is replaced with
// |name_value|.
void LeakyResetEnv(const char *name, const char *name_value) {
- char ***env_ptr = _NSGetEnviron();
- CHECK(env_ptr);
- char **environ = *env_ptr;
- CHECK(environ);
+ char **env = GetEnviron();
uptr name_len = internal_strlen(name);
- while (*environ != 0) {
- uptr len = internal_strlen(*environ);
+ while (*env != 0) {
+ uptr len = internal_strlen(*env);
if (len > name_len) {
- const char *p = *environ;
+ const char *p = *env;
if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
// Match.
if (name_value) {
// Replace the old value with the new one.
- *environ = const_cast<char*>(name_value);
+ *env = const_cast<char*>(name_value);
} else {
// Shift the subsequent pointers back.
- char **del = environ;
+ char **del = env;
do {
del[0] = del[1];
} while (*del++);
}
}
}
- environ++;
+ env++;
}
}
+static bool reexec_disabled = false;
+
+void DisableReexec() {
+ reexec_disabled = true;
+}
+
+extern "C" double dyldVersionNumber;
+static const double kMinDyldVersionWithAutoInterposition = 360.0;
+
+bool DyldNeedsEnvVariable() {
+ // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if
+ // DYLD_INSERT_LIBRARIES is not set. However, checking OS version via
+ // GetMacosVersion() doesn't work for the simulator. Let's instead check
+ // `dyldVersionNumber`, which is exported by dyld, against a known version
+ // number from the first OS release where this appeared.
+ return dyldVersionNumber < kMinDyldVersionWithAutoInterposition;
+}
+
void MaybeReexec() {
- if (!flags()->allow_reexec) return;
+ if (reexec_disabled) return;
+
// Make sure the dynamic ASan runtime library is preloaded so that the
// wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
// ourselves.
uptr old_env_len = dyld_insert_libraries ?
internal_strlen(dyld_insert_libraries) : 0;
uptr fname_len = internal_strlen(info.dli_fname);
- if (!dyld_insert_libraries ||
- !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) {
+ const char *dylib_name = StripModuleName(info.dli_fname);
+ uptr dylib_name_len = internal_strlen(dylib_name);
+
+ bool lib_is_in_env =
+ dyld_insert_libraries && REAL(strstr)(dyld_insert_libraries, dylib_name);
+ if (DyldNeedsEnvVariable() && !lib_is_in_env) {
// DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
// library.
char program_name[1024];
// Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
}
- if (common_flags()->verbosity >= 1) {
- Report("exec()-ing the program with\n");
- Report("%s=%s\n", kDyldInsertLibraries, new_env);
- Report("to enable ASan wrappers.\n");
- Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n");
- }
+ VReport(1, "exec()-ing the program with\n");
+ VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env);
+ VReport(1, "to enable ASan wrappers.\n");
execv(program_name, *_NSGetArgv());
- } else {
- // DYLD_INSERT_LIBRARIES is set and contains the runtime library.
- if (old_env_len == fname_len) {
- // It's just the runtime library name - fine to unset the variable.
- LeakyResetEnv(kDyldInsertLibraries, NULL);
+
+ // We get here only if execv() failed.
+ Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, "
+ "which is required for ASan to work. ASan tried to set the "
+ "environment variable and re-execute itself, but execv() failed, "
+ "possibly because of sandbox restrictions. Make sure to launch the "
+ "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env);
+ CHECK("execv failed" && 0);
+ }
+
+ if (!lib_is_in_env)
+ return;
+
+ // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
+ // the dylib from the environment variable, because interceptors are installed
+ // and we don't want our children to inherit the variable.
+
+ uptr env_name_len = internal_strlen(kDyldInsertLibraries);
+ // Allocate memory to hold the previous env var name, its value, the '='
+ // sign and the '\0' char.
+ char *new_env = (char*)allocator_for_env.Allocate(
+ old_env_len + 2 + env_name_len);
+ CHECK(new_env);
+ internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
+ internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
+ new_env[env_name_len] = '=';
+ char *new_env_pos = new_env + env_name_len + 1;
+
+ // Iterate over colon-separated pieces of |dyld_insert_libraries|.
+ char *piece_start = dyld_insert_libraries;
+ char *piece_end = NULL;
+ char *old_env_end = dyld_insert_libraries + old_env_len;
+ do {
+ if (piece_start[0] == ':') piece_start++;
+ piece_end = REAL(strchr)(piece_start, ':');
+ if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
+ if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
+ uptr piece_len = piece_end - piece_start;
+
+ char *filename_start =
+ (char *)internal_memrchr(piece_start, '/', piece_len);
+ uptr filename_len = piece_len;
+ if (filename_start) {
+ filename_start += 1;
+ filename_len = piece_len - (filename_start - piece_start);
} else {
- uptr env_name_len = internal_strlen(kDyldInsertLibraries);
- // Allocate memory to hold the previous env var name, its value, the '='
- // sign and the '\0' char.
- char *new_env = (char*)allocator_for_env.Allocate(
- old_env_len + 2 + env_name_len);
- CHECK(new_env);
- internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
- internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
- new_env[env_name_len] = '=';
- char *new_env_pos = new_env + env_name_len + 1;
-
- // Iterate over colon-separated pieces of |dyld_insert_libraries|.
- char *piece_start = dyld_insert_libraries;
- char *piece_end = NULL;
- char *old_env_end = dyld_insert_libraries + old_env_len;
- do {
- if (piece_start[0] == ':') piece_start++;
- piece_end = REAL(strchr)(piece_start, ':');
- if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
- if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
- uptr piece_len = piece_end - piece_start;
-
- // If the current piece isn't the runtime library name,
- // append it to new_env.
- if ((piece_len != fname_len) ||
- (internal_strncmp(piece_start, info.dli_fname, fname_len) != 0)) {
- if (new_env_pos != new_env + env_name_len + 1) {
- new_env_pos[0] = ':';
- new_env_pos++;
- }
- internal_strncpy(new_env_pos, piece_start, piece_len);
- }
- // Move on to the next piece.
- new_env_pos += piece_len;
- piece_start = piece_end;
- } while (piece_start < old_env_end);
-
- // Can't use setenv() here, because it requires the allocator to be
- // initialized.
- // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
- // a separate function called after InitializeAllocator().
- LeakyResetEnv(kDyldInsertLibraries, new_env);
+ filename_start = piece_start;
}
- }
+
+ // If the current piece isn't the runtime library name,
+ // append it to new_env.
+ if ((dylib_name_len != filename_len) ||
+ (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
+ if (new_env_pos != new_env + env_name_len + 1) {
+ new_env_pos[0] = ':';
+ new_env_pos++;
+ }
+ internal_strncpy(new_env_pos, piece_start, piece_len);
+ new_env_pos += piece_len;
+ }
+ // Move on to the next piece.
+ piece_start = piece_end;
+ } while (piece_start < old_env_end);
+
+ // Can't use setenv() here, because it requires the allocator to be
+ // initialized.
+ // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
+ // a separate function called after InitializeAllocator().
+ if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
+ LeakyResetEnv(kDyldInsertLibraries, new_env);
}
// No-op. Mac does not support static linkage anyway.
return 0;
}
-bool AsanInterceptsSignal(int signum) {
- return (signum == SIGSEGV || signum == SIGBUS) && flags()->handle_segv;
-}
+// No-op. Mac does not support static linkage anyway.
+void AsanCheckDynamicRTPrereqs() {}
-void AsanPlatformThreadInit() {
-}
+// No-op. Mac does not support static linkage anyway.
+void AsanCheckIncompatibleRT() {}
void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
UNIMPLEMENTED();
void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
AsanThread *t = GetCurrentThread();
if (!t) {
- t = AsanThread::Create(0, 0);
- CreateThreadContextArgs args = { t, stack };
- asanThreadRegistry().CreateThread(*(uptr*)t, true, parent_tid, &args);
+ t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr,
+ parent_tid, stack, /* detached */ true);
t->Init();
asanThreadRegistry().StartThread(t->tid(), 0, 0);
SetCurrentThread(t);
void asan_dispatch_call_block_and_release(void *block) {
GET_STACK_TRACE_THREAD;
asan_block_context_t *context = (asan_block_context_t*)block;
- if (common_flags()->verbosity >= 2) {
- Report("asan_dispatch_call_block_and_release(): "
- "context: %p, pthread_self: %p\n",
- block, pthread_self());
- }
+ VReport(2,
+ "asan_dispatch_call_block_and_release(): "
+ "context: %p, pthread_self: %p\n",
+ block, pthread_self());
asan_register_worker_thread(context->parent_tid, &stack);
// Call the original dispatcher for the block.
context->func(context->block);
// The caller retains control of the allocated context.
extern "C"
asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
- StackTrace *stack) {
+ BufferedStackTrace *stack) {
asan_block_context_t *asan_ctxt =
(asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
asan_ctxt->block = ctxt;
dispatch_function_t func) { \
GET_STACK_TRACE_THREAD; \
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
- if (common_flags()->verbosity >= 2) { \
+ if (Verbosity() >= 2) { \
Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \
asan_ctxt, pthread_self()); \
- PRINT_CURRENT_STACK(); \
- } \
- return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \
- asan_dispatch_call_block_and_release); \
+ PRINT_CURRENT_STACK(); \
+ } \
+ return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \
+ asan_dispatch_call_block_and_release); \
}
INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
dispatch_function_t func) {
GET_STACK_TRACE_THREAD;
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (common_flags()->verbosity >= 2) {
+ if (Verbosity() >= 2) {
Report("dispatch_after_f: %p\n", asan_ctxt);
PRINT_CURRENT_STACK();
}
dispatch_function_t func) {
GET_STACK_TRACE_THREAD;
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (common_flags()->verbosity >= 2) {
+ if (Verbosity() >= 2) {
Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
asan_ctxt, pthread_self());
PRINT_CURRENT_STACK();
#if !defined(MISSING_BLOCKS_SUPPORT)
extern "C" {
-// FIXME: consolidate these declarations with asan_intercepted_functions.h.
void dispatch_async(dispatch_queue_t dq, void(^work)(void));
void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
void(^work)(void));
INTERCEPTOR(void, dispatch_async,
dispatch_queue_t dq, void(^work)(void)) {
+ ENABLE_FRAME_POINTER;
GET_ASAN_BLOCK(work);
REAL(dispatch_async)(dq, asan_block);
}
INTERCEPTOR(void, dispatch_group_async,
dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
+ ENABLE_FRAME_POINTER;
GET_ASAN_BLOCK(work);
REAL(dispatch_group_async)(dg, dq, asan_block);
}
INTERCEPTOR(void, dispatch_after,
dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
+ ENABLE_FRAME_POINTER;
GET_ASAN_BLOCK(work);
REAL(dispatch_after)(when, queue, asan_block);
}
INTERCEPTOR(void, dispatch_source_set_cancel_handler,
dispatch_source_t ds, void(^work)(void)) {
+ if (!work) {
+ REAL(dispatch_source_set_cancel_handler)(ds, work);
+ return;
+ }
+ ENABLE_FRAME_POINTER;
GET_ASAN_BLOCK(work);
REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
}
INTERCEPTOR(void, dispatch_source_set_event_handler,
dispatch_source_t ds, void(^work)(void)) {
+ ENABLE_FRAME_POINTER;
GET_ASAN_BLOCK(work);
REAL(dispatch_source_set_event_handler)(ds, asan_block);
}
+++ /dev/null
-//===-- asan_mac.h ----------------------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Mac-specific ASan definitions.
-//===----------------------------------------------------------------------===//
-#ifndef ASAN_MAC_H
-#define ASAN_MAC_H
-
-// CF_RC_BITS, the layout of CFRuntimeBase and __CFStrIsConstant are internal
-// and subject to change in further CoreFoundation versions. Apple does not
-// guarantee any binary compatibility from release to release.
-
-// See http://opensource.apple.com/source/CF/CF-635.15/CFInternal.h
-#if defined(__BIG_ENDIAN__)
-#define CF_RC_BITS 0
-#endif
-
-#if defined(__LITTLE_ENDIAN__)
-#define CF_RC_BITS 3
-#endif
-
-// See http://opensource.apple.com/source/CF/CF-635.15/CFRuntime.h
-typedef struct __CFRuntimeBase {
- uptr _cfisa;
- u8 _cfinfo[4];
-#if __LP64__
- u32 _rc;
-#endif
-} CFRuntimeBase;
-
-enum MacosVersion {
- MACOS_VERSION_UNINITIALIZED = 0,
- MACOS_VERSION_UNKNOWN,
- MACOS_VERSION_LEOPARD,
- MACOS_VERSION_SNOW_LEOPARD,
- MACOS_VERSION_LION,
- MACOS_VERSION_MOUNTAIN_LION,
- MACOS_VERSION_MAVERICKS
-};
-
-// Used by asan_malloc_mac.cc and asan_mac.cc
-extern "C" void __CFInitialize();
-
-namespace __asan {
-
-MacosVersion GetMacosVersion();
-void MaybeReplaceCFAllocator();
-
-} // namespace __asan
-
-#endif // ASAN_MAC_H
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_LINUX
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
-#if SANITIZER_ANDROID
-DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size)
-DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
-DECLARE_REAL_AND_INTERCEPTOR(void*, calloc, uptr nmemb, uptr size)
-DECLARE_REAL_AND_INTERCEPTOR(void*, realloc, void *ptr, uptr size)
-DECLARE_REAL_AND_INTERCEPTOR(void*, memalign, uptr boundary, uptr size)
-
-struct MallocDebug {
- void* (*malloc)(uptr bytes);
- void (*free)(void* mem);
- void* (*calloc)(uptr n_elements, uptr elem_size);
- void* (*realloc)(void* oldMem, uptr bytes);
- void* (*memalign)(uptr alignment, uptr bytes);
-};
-
-const MallocDebug asan_malloc_dispatch ALIGNED(32) = {
- WRAP(malloc), WRAP(free), WRAP(calloc), WRAP(realloc), WRAP(memalign)
-};
-
-extern "C" const MallocDebug* __libc_malloc_dispatch;
-
-namespace __asan {
-void ReplaceSystemMalloc() {
- __libc_malloc_dispatch = &asan_malloc_dispatch;
-}
-} // namespace __asan
-
-#else // ANDROID
-
-namespace __asan {
-void ReplaceSystemMalloc() {
-}
-} // namespace __asan
-#endif // ANDROID
-
// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan; // NOLINT
}
INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
- if (!asan_inited) {
+ if (UNLIKELY(!asan_inited)) {
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
const uptr kCallocPoolSize = 1024;
static uptr calloc_memory_for_dlsym[kCallocPoolSize];
return asan_memalign(boundary, size, &stack, FROM_MALLOC);
}
-INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s)
- ALIAS("memalign");
+INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) {
+ GET_STACK_TRACE_MALLOC;
+ return asan_memalign(boundary, size, &stack, FROM_MALLOC);
+}
+
+INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
+ GET_STACK_TRACE_MALLOC;
+ void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
+ DTLS_on_libc_memalign(res, size * boundary);
+ return res;
+}
INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
GET_CURRENT_PC_BP_SP;
__asan_print_accumulated_stats();
}
-#endif // SANITIZER_LINUX
+#if SANITIZER_ANDROID
+// Format of __libc_malloc_dispatch has changed in Android L.
+// While we are moving towards a solution that does not depend on bionic
+// internals, here is something to support both K* and L releases.
+struct MallocDebugK {
+ void *(*malloc)(uptr bytes);
+ void (*free)(void *mem);
+ void *(*calloc)(uptr n_elements, uptr elem_size);
+ void *(*realloc)(void *oldMem, uptr bytes);
+ void *(*memalign)(uptr alignment, uptr bytes);
+ uptr (*malloc_usable_size)(void *mem);
+};
+
+struct MallocDebugL {
+ void *(*calloc)(uptr n_elements, uptr elem_size);
+ void (*free)(void *mem);
+ fake_mallinfo (*mallinfo)(void);
+ void *(*malloc)(uptr bytes);
+ uptr (*malloc_usable_size)(void *mem);
+ void *(*memalign)(uptr alignment, uptr bytes);
+ int (*posix_memalign)(void **memptr, uptr alignment, uptr size);
+ void* (*pvalloc)(uptr size);
+ void *(*realloc)(void *oldMem, uptr bytes);
+ void* (*valloc)(uptr size);
+};
+
+ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
+ WRAP(malloc), WRAP(free), WRAP(calloc),
+ WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
+
+ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
+ WRAP(calloc), WRAP(free), WRAP(mallinfo),
+ WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign),
+ WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc),
+ WRAP(valloc)};
+
+namespace __asan {
+void ReplaceSystemMalloc() {
+ void **__libc_malloc_dispatch_p =
+ (void **)AsanDlSymNext("__libc_malloc_dispatch");
+ if (__libc_malloc_dispatch_p) {
+ // Decide on K vs L dispatch format by the presence of
+ // __libc_malloc_default_dispatch export in libc.
+ void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch");
+ if (default_dispatch_p)
+ *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k;
+ else
+ *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l;
+ }
+}
+} // namespace __asan
+
+#else // SANITIZER_ANDROID
+
+namespace __asan {
+void ReplaceSystemMalloc() {
+}
+} // namespace __asan
+#endif // SANITIZER_ANDROID
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_MAC
-#include <AvailabilityMacros.h>
-#include <CoreFoundation/CFBase.h>
-#include <dlfcn.h>
-#include <malloc/malloc.h>
-#include <sys/mman.h>
-
-#include "asan_allocator.h"
#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_mac.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
-// Similar code is used in Google Perftools,
-// http://code.google.com/p/google-perftools.
-
-// ---------------------- Replacement functions ---------------- {{{1
-using namespace __asan; // NOLINT
-
-// TODO(glider): do we need both zones?
-static malloc_zone_t *system_malloc_zone = 0;
-static malloc_zone_t asan_zone;
-
-INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
- vm_size_t start_size, unsigned zone_flags) {
- if (!asan_inited) __asan_init();
- GET_STACK_TRACE_MALLOC;
- uptr page_size = GetPageSizeCached();
- uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size);
- malloc_zone_t *new_zone =
- (malloc_zone_t*)asan_memalign(page_size, allocated_size,
- &stack, FROM_MALLOC);
- internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone));
- new_zone->zone_name = NULL; // The name will be changed anyway.
- if (GetMacosVersion() >= MACOS_VERSION_LION) {
- // Prevent the client app from overwriting the zone contents.
- // Library functions that need to modify the zone will set PROT_WRITE on it.
- // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
- mprotect(new_zone, allocated_size, PROT_READ);
- }
- return new_zone;
-}
-
-INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
- if (!asan_inited) __asan_init();
- return &asan_zone;
-}
-
-INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
- // FIXME: ASan should support purgeable allocations.
- // https://code.google.com/p/address-sanitizer/issues/detail?id=139
- if (!asan_inited) __asan_init();
- return &asan_zone;
-}
-
-INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
- // FIXME: ASan should support purgeable allocations. Ignoring them is fine
- // for now.
- if (!asan_inited) __asan_init();
-}
-
-INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
- // FIXME: ASan should support purgeable allocations. Ignoring them is fine
- // for now.
- if (!asan_inited) __asan_init();
- // Must return 0 if the contents were not purged since the last call to
- // malloc_make_purgeable().
- return 0;
-}
-
-INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
- if (!asan_inited) __asan_init();
- // Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes.
- size_t buflen = 6 + (name ? internal_strlen(name) : 0);
- InternalScopedBuffer<char> new_name(buflen);
- if (name && zone->introspect == asan_zone.introspect) {
- internal_snprintf(new_name.data(), buflen, "asan-%s", name);
- name = new_name.data();
- }
-
- // Call the system malloc's implementation for both external and our zones,
- // since that appropriately changes VM region protections on the zone.
- REAL(malloc_set_zone_name)(zone, name);
-}
-
-INTERCEPTOR(void *, malloc, size_t size) {
- if (!asan_inited) __asan_init();
- GET_STACK_TRACE_MALLOC;
- void *res = asan_malloc(size, &stack);
- return res;
-}
-
-INTERCEPTOR(void, free, void *ptr) {
- if (!asan_inited) __asan_init();
- if (!ptr) return;
- GET_STACK_TRACE_FREE;
+using namespace __asan;
+#define COMMON_MALLOC_ZONE_NAME "asan"
+#define COMMON_MALLOC_ENTER() ENSURE_ASAN_INITED()
+#define COMMON_MALLOC_SANITIZER_INITIALIZED asan_inited
+#define COMMON_MALLOC_FORCE_LOCK() asan_mz_force_lock()
+#define COMMON_MALLOC_FORCE_UNLOCK() asan_mz_force_unlock()
+#define COMMON_MALLOC_MEMALIGN(alignment, size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = asan_memalign(alignment, size, &stack, FROM_MALLOC)
+#define COMMON_MALLOC_MALLOC(size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = asan_malloc(size, &stack)
+#define COMMON_MALLOC_REALLOC(ptr, size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = asan_realloc(ptr, size, &stack);
+#define COMMON_MALLOC_CALLOC(count, size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = asan_calloc(count, size, &stack);
+#define COMMON_MALLOC_VALLOC(size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
+#define COMMON_MALLOC_FREE(ptr) \
+ GET_STACK_TRACE_FREE; \
asan_free(ptr, &stack, FROM_MALLOC);
-}
-
-INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
- if (!asan_inited) __asan_init();
- GET_STACK_TRACE_MALLOC;
- return asan_realloc(ptr, size, &stack);
-}
-
-INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
- if (!asan_inited) __asan_init();
- GET_STACK_TRACE_MALLOC;
- return asan_calloc(nmemb, size, &stack);
-}
-
-INTERCEPTOR(void *, valloc, size_t size) {
- if (!asan_inited) __asan_init();
- GET_STACK_TRACE_MALLOC;
- return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
-}
-
-INTERCEPTOR(size_t, malloc_good_size, size_t size) {
- if (!asan_inited) __asan_init();
- return asan_zone.introspect->good_size(&asan_zone, size);
-}
-
-INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
- if (!asan_inited) __asan_init();
- CHECK(memptr);
- GET_STACK_TRACE_MALLOC;
- void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC);
- if (result) {
- *memptr = result;
- return 0;
- }
- return -1;
-}
-
-namespace {
-
-// TODO(glider): the mz_* functions should be united with the Linux wrappers,
-// as they are basically copied from there.
-size_t mz_size(malloc_zone_t* zone, const void* ptr) {
- return asan_mz_size(ptr);
-}
-
-void *mz_malloc(malloc_zone_t *zone, size_t size) {
- if (!asan_inited) {
- CHECK(system_malloc_zone);
- return malloc_zone_malloc(system_malloc_zone, size);
- }
- GET_STACK_TRACE_MALLOC;
- return asan_malloc(size, &stack);
-}
-
-void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
- if (!asan_inited) {
- // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
- const size_t kCallocPoolSize = 1024;
- static uptr calloc_memory_for_dlsym[kCallocPoolSize];
- static size_t allocated;
- size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
- void *mem = (void*)&calloc_memory_for_dlsym[allocated];
- allocated += size_in_words;
- CHECK(allocated < kCallocPoolSize);
- return mem;
- }
- GET_STACK_TRACE_MALLOC;
- return asan_calloc(nmemb, size, &stack);
-}
-
-void *mz_valloc(malloc_zone_t *zone, size_t size) {
- if (!asan_inited) {
- CHECK(system_malloc_zone);
- return malloc_zone_valloc(system_malloc_zone, size);
- }
- GET_STACK_TRACE_MALLOC;
- return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
-}
-
-#define GET_ZONE_FOR_PTR(ptr) \
- malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
- const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
-
-void ALWAYS_INLINE free_common(void *context, void *ptr) {
- if (!ptr) return;
- GET_STACK_TRACE_FREE;
- // FIXME: need to retire this flag.
- if (!flags()->mac_ignore_invalid_free) {
- asan_free(ptr, &stack, FROM_MALLOC);
- } else {
- GET_ZONE_FOR_PTR(ptr);
- WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
- return;
- }
-}
-
-// TODO(glider): the allocation callbacks need to be refactored.
-void mz_free(malloc_zone_t *zone, void *ptr) {
- free_common(zone, ptr);
-}
-
-void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
- if (!ptr) {
- GET_STACK_TRACE_MALLOC;
- return asan_malloc(size, &stack);
- } else {
- if (asan_mz_size(ptr)) {
- GET_STACK_TRACE_MALLOC;
- return asan_realloc(ptr, size, &stack);
- } else {
- // We can't recover from reallocating an unknown address, because
- // this would require reading at most |size| bytes from
- // potentially unaccessible memory.
- GET_STACK_TRACE_FREE;
- GET_ZONE_FOR_PTR(ptr);
- ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
- }
- }
-}
-
-void mz_destroy(malloc_zone_t* zone) {
- // A no-op -- we will not be destroyed!
- Report("mz_destroy() called -- ignoring\n");
-}
-
- // from AvailabilityMacros.h
-#if defined(MAC_OS_X_VERSION_10_6) && \
- MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
-void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
- if (!asan_inited) {
- CHECK(system_malloc_zone);
- return malloc_zone_memalign(system_malloc_zone, align, size);
- }
- GET_STACK_TRACE_MALLOC;
- return asan_memalign(align, size, &stack, FROM_MALLOC);
-}
-
-// This function is currently unused, and we build with -Werror.
-#if 0
-void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) {
- // TODO(glider): check that |size| is valid.
- UNIMPLEMENTED();
-}
-#endif
-#endif
-
-kern_return_t mi_enumerator(task_t task, void *,
- unsigned type_mask, vm_address_t zone_address,
- memory_reader_t reader,
- vm_range_recorder_t recorder) {
- // Should enumerate all the pointers we have. Seems like a lot of work.
- return KERN_FAILURE;
-}
-
-size_t mi_good_size(malloc_zone_t *zone, size_t size) {
- // I think it's always safe to return size, but we maybe could do better.
- return size;
-}
-
-boolean_t mi_check(malloc_zone_t *zone) {
- UNIMPLEMENTED();
-}
-
-void mi_print(malloc_zone_t *zone, boolean_t verbose) {
- UNIMPLEMENTED();
-}
-
-void mi_log(malloc_zone_t *zone, void *address) {
- // I don't think we support anything like this
-}
-
-void mi_force_lock(malloc_zone_t *zone) {
- asan_mz_force_lock();
-}
-
-void mi_force_unlock(malloc_zone_t *zone) {
- asan_mz_force_unlock();
-}
-
-void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
- AsanMallocStats malloc_stats;
- FillMallocStatistics(&malloc_stats);
- CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats));
+#define COMMON_MALLOC_SIZE(ptr) \
+ uptr size = asan_mz_size(ptr);
+#define COMMON_MALLOC_FILL_STATS(zone, stats) \
+ AsanMallocStats malloc_stats; \
+ FillMallocStatistics(&malloc_stats); \
+ CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); \
internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t));
-}
-
-#if defined(MAC_OS_X_VERSION_10_6) && \
- MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
-boolean_t mi_zone_locked(malloc_zone_t *zone) {
- // UNIMPLEMENTED();
- return false;
-}
-#endif
-
-} // unnamed namespace
+#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
+ GET_STACK_TRACE_FREE; \
+ ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
+#define COMMON_MALLOC_IGNORE_INVALID_FREE flags()->mac_ignore_invalid_free
+#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \
+ GET_STACK_TRACE_FREE; \
+ WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
+#define COMMON_MALLOC_NAMESPACE __asan
-namespace __asan {
+#include "sanitizer_common/sanitizer_malloc_mac.inc"
-void ReplaceSystemMalloc() {
- static malloc_introspection_t asan_introspection;
- // Ok to use internal_memset, these places are not performance-critical.
- internal_memset(&asan_introspection, 0, sizeof(asan_introspection));
-
- asan_introspection.enumerator = &mi_enumerator;
- asan_introspection.good_size = &mi_good_size;
- asan_introspection.check = &mi_check;
- asan_introspection.print = &mi_print;
- asan_introspection.log = &mi_log;
- asan_introspection.force_lock = &mi_force_lock;
- asan_introspection.force_unlock = &mi_force_unlock;
- asan_introspection.statistics = &mi_statistics;
-
- internal_memset(&asan_zone, 0, sizeof(malloc_zone_t));
-
- // Start with a version 4 zone which is used for OS X 10.4 and 10.5.
- asan_zone.version = 4;
- asan_zone.zone_name = "asan";
- asan_zone.size = &mz_size;
- asan_zone.malloc = &mz_malloc;
- asan_zone.calloc = &mz_calloc;
- asan_zone.valloc = &mz_valloc;
- asan_zone.free = &mz_free;
- asan_zone.realloc = &mz_realloc;
- asan_zone.destroy = &mz_destroy;
- asan_zone.batch_malloc = 0;
- asan_zone.batch_free = 0;
- asan_zone.introspect = &asan_introspection;
-
- // from AvailabilityMacros.h
-#if defined(MAC_OS_X_VERSION_10_6) && \
- MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
- // Switch to version 6 on OSX 10.6 to support memalign.
- asan_zone.version = 6;
- asan_zone.free_definite_size = 0;
- asan_zone.memalign = &mz_memalign;
- asan_introspection.zone_locked = &mi_zone_locked;
#endif
-
- // Register the ASan zone.
- malloc_zone_register(&asan_zone);
-}
-} // namespace __asan
-
-#endif // SANITIZER_MAC
#include <stddef.h>
-// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan; // NOLINT
-// FIXME: Simply defining functions with the same signature in *.obj
-// files overrides the standard functions in *.lib
-// This works well for simple helloworld-like tests but might need to be
-// revisited in the future.
+// MT: Simply defining functions with the same signature in *.obj
+// files overrides the standard functions in the CRT.
+// MD: Memory allocation functions are defined in the CRT .dll,
+// so we have to intercept them before they are called for the first time.
+
+#if ASAN_DYNAMIC
+# define ALLOCATION_FUNCTION_ATTRIBUTE
+#else
+# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+#endif
extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
void free(void *ptr) {
GET_STACK_TRACE_FREE;
return asan_free(ptr, &stack, FROM_MALLOC);
}
-SANITIZER_INTERFACE_ATTRIBUTE
-void _free_dbg(void* ptr, int) {
+ALLOCATION_FUNCTION_ATTRIBUTE
+void _free_dbg(void *ptr, int) {
free(ptr);
}
+ALLOCATION_FUNCTION_ATTRIBUTE
void cfree(void *ptr) {
- CHECK(!"cfree() should not be used on Windows?");
+ CHECK(!"cfree() should not be used on Windows");
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
void *malloc(size_t size) {
GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
-SANITIZER_INTERFACE_ATTRIBUTE
-void* _malloc_dbg(size_t size, int , const char*, int) {
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_malloc_dbg(size_t size, int, const char *, int) {
return malloc(size);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
void *calloc(size_t nmemb, size_t size) {
GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
-SANITIZER_INTERFACE_ATTRIBUTE
-void* _calloc_dbg(size_t n, size_t size, int, const char*, int) {
- return calloc(n, size);
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
+ return calloc(nmemb, size);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
return calloc(nmemb, size);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
void *realloc(void *ptr, size_t size) {
GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
void *_realloc_dbg(void *ptr, size_t size, int) {
CHECK(!"_realloc_dbg should not exist!");
return 0;
}
-SANITIZER_INTERFACE_ATTRIBUTE
-void* _recalloc(void* p, size_t n, size_t elem_size) {
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_recalloc(void *p, size_t n, size_t elem_size) {
if (!p)
return calloc(n, elem_size);
const size_t size = n * elem_size;
return realloc(p, size);
}
-SANITIZER_INTERFACE_ATTRIBUTE
+ALLOCATION_FUNCTION_ATTRIBUTE
size_t _msize(void *ptr) {
GET_CURRENT_PC_BP_SP;
(void)sp;
return asan_malloc_usable_size(ptr, pc, bp);
}
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_expand(void *memblock, size_t size) {
+ // _expand is used in realloc-like functions to resize the buffer if possible.
+ // We don't want memory to stand still while resizing buffers, so return 0.
+ return 0;
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_expand_dbg(void *memblock, size_t size) {
+ return _expand(memblock, size);
+}
+
+// TODO(timurrrr): Might want to add support for _aligned_* allocation
+// functions to detect a bit more bugs. Those functions seem to wrap malloc().
+
int _CrtDbgReport(int, const char*, int,
const char*, const char*, ...) {
ShowStatsAndAbort();
}
} // extern "C"
-using __interception::GetRealFunctionAddress;
-
-// We don't want to include "windows.h" in this file to avoid extra attributes
-// set on malloc/free etc (e.g. dllimport), so declare a few things manually:
-extern "C" int __stdcall VirtualProtect(void* addr, size_t size,
- DWORD prot, DWORD *old_prot);
-const int PAGE_EXECUTE_READWRITE = 0x40;
-
namespace __asan {
void ReplaceSystemMalloc() {
-#if defined(_DLL)
-# ifdef _WIN64
-# error ReplaceSystemMalloc was not tested on x64
-# endif
- char *crt_malloc;
- if (GetRealFunctionAddress("malloc", (void**)&crt_malloc)) {
- // Replace malloc in the CRT dll with a jump to our malloc.
- DWORD old_prot, unused;
- CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot));
- REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16); // just in case.
-
- ptrdiff_t jmp_offset = (char*)malloc - (char*)crt_malloc - 5;
- crt_malloc[0] = 0xE9; // jmp, should be followed by an offset.
- REAL(memcpy)(crt_malloc + 1, &jmp_offset, sizeof(jmp_offset));
-
- CHECK(VirtualProtect(crt_malloc, 16, old_prot, &unused));
-
- // FYI: FlushInstructionCache is needed on Itanium etc but not on x86/x64.
- }
-
- // FIXME: investigate whether anything else is needed.
+#if defined(ASAN_DYNAMIC)
+ // We don't check the result because CRT might not be used in the process.
+ __interception::OverrideFunction("free", (uptr)free);
+ __interception::OverrideFunction("malloc", (uptr)malloc);
+ __interception::OverrideFunction("_malloc_crt", (uptr)malloc);
+ __interception::OverrideFunction("calloc", (uptr)calloc);
+ __interception::OverrideFunction("_calloc_crt", (uptr)calloc);
+ __interception::OverrideFunction("realloc", (uptr)realloc);
+ __interception::OverrideFunction("_realloc_crt", (uptr)realloc);
+ __interception::OverrideFunction("_recalloc", (uptr)_recalloc);
+ __interception::OverrideFunction("_recalloc_crt", (uptr)_recalloc);
+ __interception::OverrideFunction("_msize", (uptr)_msize);
+ __interception::OverrideFunction("_expand", (uptr)_expand);
+
+ // Override different versions of 'operator new' and 'operator delete'.
+ // No need to override the nothrow versions as they just wrap the throw
+ // versions.
+ // FIXME: Unfortunately, MSVC miscompiles the statements that take the
+ // addresses of the array versions of these operators,
+ // see https://connect.microsoft.com/VisualStudio/feedbackdetail/view/946992
+ // We might want to try to work around this by [inline] assembly or compiling
+ // parts of the RTL with Clang.
+ void *(*op_new)(size_t sz) = operator new;
+ void (*op_delete)(void *p) = operator delete;
+ void *(*op_array_new)(size_t sz) = operator new[];
+ void (*op_array_delete)(void *p) = operator delete[];
+ __interception::OverrideFunction("??2@YAPAXI@Z", (uptr)op_new);
+ __interception::OverrideFunction("??3@YAXPAX@Z", (uptr)op_delete);
+ __interception::OverrideFunction("??_U@YAPAXI@Z", (uptr)op_array_new);
+ __interception::OverrideFunction("??_V@YAXPAX@Z", (uptr)op_array_delete);
#endif
}
} // namespace __asan
// || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow ||
// || `[0x000000000000, 0x00007fff7fff]` || LowMem ||
//
-// Default Linux/i386 mapping:
+// Default Linux/i386 mapping on x86_64 machine:
// || `[0x40000000, 0xffffffff]` || HighMem ||
// || `[0x28000000, 0x3fffffff]` || HighShadow ||
// || `[0x24000000, 0x27ffffff]` || ShadowGap ||
// || `[0x20000000, 0x23ffffff]` || LowShadow ||
// || `[0x00000000, 0x1fffffff]` || LowMem ||
//
-// Default Linux/MIPS mapping:
-// || `[0x2aaa8000, 0xffffffff]` || HighMem ||
-// || `[0x0fffd000, 0x2aaa7fff]` || HighShadow ||
-// || `[0x0bffd000, 0x0fffcfff]` || ShadowGap ||
-// || `[0x0aaa8000, 0x0bffcfff]` || LowShadow ||
-// || `[0x00000000, 0x0aaa7fff]` || LowMem ||
+// Default Linux/i386 mapping on i386 machine
+// (addresses starting with 0xc0000000 are reserved
+// for kernel and thus not sanitized):
+// || `[0x38000000, 0xbfffffff]` || HighMem ||
+// || `[0x27000000, 0x37ffffff]` || HighShadow ||
+// || `[0x24000000, 0x26ffffff]` || ShadowGap ||
+// || `[0x20000000, 0x23ffffff]` || LowShadow ||
+// || `[0x00000000, 0x1fffffff]` || LowMem ||
+//
+// Default Linux/MIPS32 mapping:
+// || `[0x2aaa0000, 0xffffffff]` || HighMem ||
+// || `[0x0fff4000, 0x2aa9ffff]` || HighShadow ||
+// || `[0x0bff4000, 0x0fff3fff]` || ShadowGap ||
+// || `[0x0aaa0000, 0x0bff3fff]` || LowShadow ||
+// || `[0x00000000, 0x0aa9ffff]` || LowMem ||
+//
+// Default Linux/MIPS64 mapping:
+// || `[0x4000000000, 0xffffffffff]` || HighMem ||
+// || `[0x2800000000, 0x3fffffffff]` || HighShadow ||
+// || `[0x2400000000, 0x27ffffffff]` || ShadowGap ||
+// || `[0x2000000000, 0x23ffffffff]` || LowShadow ||
+// || `[0x0000000000, 0x1fffffffff]` || LowMem ||
+//
+// Default Linux/AArch64 (39-bit VMA) mapping:
+// || `[0x2000000000, 0x7fffffffff]` || highmem ||
+// || `[0x1400000000, 0x1fffffffff]` || highshadow ||
+// || `[0x1200000000, 0x13ffffffff]` || shadowgap ||
+// || `[0x1000000000, 0x11ffffffff]` || lowshadow ||
+// || `[0x0000000000, 0x0fffffffff]` || lowmem ||
+//
+// Default Linux/AArch64 (42-bit VMA) mapping:
+// || `[0x10000000000, 0x3ffffffffff]` || highmem ||
+// || `[0x0a000000000, 0x0ffffffffff]` || highshadow ||
+// || `[0x09000000000, 0x09fffffffff]` || shadowgap ||
+// || `[0x08000000000, 0x08fffffffff]` || lowshadow ||
+// || `[0x00000000000, 0x07fffffffff]` || lowmem ||
+//
+// Shadow mapping on FreeBSD/x86-64 with SHADOW_OFFSET == 0x400000000000:
+// || `[0x500000000000, 0x7fffffffffff]` || HighMem ||
+// || `[0x4a0000000000, 0x4fffffffffff]` || HighShadow ||
+// || `[0x480000000000, 0x49ffffffffff]` || ShadowGap ||
+// || `[0x400000000000, 0x47ffffffffff]` || LowShadow ||
+// || `[0x000000000000, 0x3fffffffffff]` || LowMem ||
+//
+// Shadow mapping on FreeBSD/i386 with SHADOW_OFFSET == 0x40000000:
+// || `[0x60000000, 0xffffffff]` || HighMem ||
+// || `[0x4c000000, 0x5fffffff]` || HighShadow ||
+// || `[0x48000000, 0x4bffffff]` || ShadowGap ||
+// || `[0x40000000, 0x47ffffff]` || LowShadow ||
+// || `[0x00000000, 0x3fffffff]` || LowMem ||
+//
+// Default Windows/i386 mapping:
+// (the exact location of HighShadow/HighMem may vary depending
+// on WoW64, /LARGEADDRESSAWARE, etc).
+// || `[0x50000000, 0xffffffff]` || HighMem ||
+// || `[0x3a000000, 0x4fffffff]` || HighShadow ||
+// || `[0x36000000, 0x39ffffff]` || ShadowGap ||
+// || `[0x30000000, 0x35ffffff]` || LowShadow ||
+// || `[0x00000000, 0x2fffffff]` || LowMem ||
static const u64 kDefaultShadowScale = 3;
-static const u64 kDefaultShadowOffset32 = 1ULL << 29;
+static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000
static const u64 kDefaultShadowOffset64 = 1ULL << 44;
static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G.
+static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000
+static const u64 kIosShadowOffset64 = 0x130000000;
+static const u64 kIosSimShadowOffset32 = 1ULL << 30;
+static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64;
+static const u64 kAArch64_ShadowOffset64 = 1ULL << 36;
+static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000;
+static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37;
static const u64 kPPC64_ShadowOffset64 = 1ULL << 41;
-static const u64 kMIPS32_ShadowOffset32 = 0x0aaa8000;
-
-#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale;
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset;
-# define SHADOW_SCALE (__asan_mapping_scale)
-# define SHADOW_OFFSET (__asan_mapping_offset)
-#else
-# define SHADOW_SCALE kDefaultShadowScale
-# if SANITIZER_ANDROID
-# define SHADOW_OFFSET (0)
-# else
-# if SANITIZER_WORDSIZE == 32
-# if defined(__mips__)
-# define SHADOW_OFFSET kMIPS32_ShadowOffset32
-# else
-# define SHADOW_OFFSET kDefaultShadowOffset32
-# endif
+static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000
+static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000
+static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
+
+#define SHADOW_SCALE kDefaultShadowScale
+
+
+#if SANITIZER_WORDSIZE == 32
+# if SANITIZER_ANDROID
+# define SHADOW_OFFSET (0)
+# elif defined(__mips__)
+# define SHADOW_OFFSET kMIPS32_ShadowOffset32
+# elif SANITIZER_FREEBSD
+# define SHADOW_OFFSET kFreeBSD_ShadowOffset32
+# elif SANITIZER_WINDOWS
+# define SHADOW_OFFSET kWindowsShadowOffset32
+# elif SANITIZER_IOSSIM
+# define SHADOW_OFFSET kIosSimShadowOffset32
+# elif SANITIZER_IOS
+# define SHADOW_OFFSET kIosShadowOffset32
# else
-# if defined(__powerpc64__)
+# define SHADOW_OFFSET kDefaultShadowOffset32
+# endif
+#else
+# if defined(__aarch64__)
+# define SHADOW_OFFSET kAArch64_ShadowOffset64
+# elif defined(__powerpc64__)
# define SHADOW_OFFSET kPPC64_ShadowOffset64
-# elif SANITIZER_MAC
-# define SHADOW_OFFSET kDefaultShadowOffset64
-# else
-# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
-# endif
+# elif SANITIZER_FREEBSD
+# define SHADOW_OFFSET kFreeBSD_ShadowOffset64
+# elif SANITIZER_MAC
+# define SHADOW_OFFSET kDefaultShadowOffset64
+# elif defined(__mips64)
+# define SHADOW_OFFSET kMIPS64_ShadowOffset64
+# elif SANITIZER_IOSSIM
+# define SHADOW_OFFSET kIosSimShadowOffset64
+# elif SANITIZER_IOS
+# define SHADOW_OFFSET kIosShadowOffset64
+# else
+# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
# endif
-# endif
-#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET
+#endif
#define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE)
#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) + (SHADOW_OFFSET))
// With the zero shadow base we can not actually map pages starting from 0.
// This constant is somewhat arbitrary.
-#define kZeroBaseShadowStart (1 << 18)
+#define kZeroBaseShadowStart 0
+#define kZeroBaseMaxShadowStart (1 << 18)
#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 \
: kZeroBaseShadowStart)
#include "asan_internal.h"
#include "asan_stack.h"
+#include "interception/interception.h"
+
#include <stddef.h>
-namespace __asan {
-// This function is a no-op. We need it to make sure that object file
-// with our replacements will actually be loaded from static ASan
-// run-time library at link-time.
-void ReplaceOperatorsNewAndDelete() { }
-}
+// C++ operators can't have visibility attributes on Windows.
+#if SANITIZER_WINDOWS
+# define CXX_OPERATOR_ATTRIBUTE
+#else
+# define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE
+#endif
using namespace __asan; // NOLINT
-// On Android new() goes through malloc interceptors.
-// See also https://code.google.com/p/address-sanitizer/issues/detail?id=131.
-#if !SANITIZER_ANDROID
+// This code has issues on OSX.
+// See https://code.google.com/p/address-sanitizer/issues/detail?id=131.
// Fake std::nothrow_t to avoid including <new>.
namespace std {
// To make sure that C++ allocation/deallocation operators are overridden on
// OS X we need to intercept them using their mangled names.
#if !SANITIZER_MAC
-INTERCEPTOR_ATTRIBUTE
+// FreeBSD prior v9.2 have wrong definition of 'size_t'.
+// http://svnweb.freebsd.org/base?view=revision&revision=232261
+#if SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
+#include <sys/param.h>
+#if __FreeBSD_version <= 902001 // v9.2
+#define size_t unsigned
+#endif // __FreeBSD_version
+#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
+
+CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
-INTERCEPTOR_ATTRIBUTE
+CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); }
-INTERCEPTOR_ATTRIBUTE
+CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size, std::nothrow_t const&)
{ OPERATOR_NEW_BODY(FROM_NEW); }
-INTERCEPTOR_ATTRIBUTE
+CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size, std::nothrow_t const&)
{ OPERATOR_NEW_BODY(FROM_NEW_BR); }
asan_free(ptr, &stack, type);
#if !SANITIZER_MAC
-INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); }
-INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW_BR); }
-INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY(FROM_NEW); }
-INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr) NOEXCEPT {
+ OPERATOR_DELETE_BODY(FROM_NEW);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr) NOEXCEPT {
+ OPERATOR_DELETE_BODY(FROM_NEW_BR);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(FROM_NEW);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(FROM_NEW_BR);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size) NOEXCEPT {
+ GET_STACK_TRACE_FREE;
+ asan_sized_free(ptr, size, &stack, FROM_NEW);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size) NOEXCEPT {
+ GET_STACK_TRACE_FREE;
+ asan_sized_free(ptr, size, &stack, FROM_NEW_BR);
+}
#else // SANITIZER_MAC
INTERCEPTOR(void, _ZdlPv, void *ptr) {
OPERATOR_DELETE_BODY(FROM_NEW_BR);
}
#endif
-
-#endif
//===----------------------------------------------------------------------===//
#include "asan_poisoning.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_flags.h"
namespace __asan {
+static atomic_uint8_t can_poison_memory;
+
+void SetCanPoisonMemory(bool value) {
+ atomic_store(&can_poison_memory, value, memory_order_release);
+}
+
+bool CanPoisonMemory() {
+ return atomic_load(&can_poison_memory, memory_order_acquire);
+}
+
void PoisonShadow(uptr addr, uptr size, u8 value) {
- if (!flags()->poison_heap) return;
+ if (!CanPoisonMemory()) return;
CHECK(AddrIsAlignedByGranularity(addr));
CHECK(AddrIsInMem(addr));
CHECK(AddrIsAlignedByGranularity(addr + size));
uptr size,
uptr redzone_size,
u8 value) {
- if (!flags()->poison_heap) return;
+ if (!CanPoisonMemory()) return;
CHECK(AddrIsAlignedByGranularity(addr));
CHECK(AddrIsInMem(addr));
FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value);
}
};
+void FlushUnneededASanShadowMemory(uptr p, uptr size) {
+ // Since asan's mapping is compacting, the shadow chunk may be
+ // not page-aligned, so we only flush the page-aligned portion.
+ uptr page_size = GetPageSizeCached();
+ uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size);
+ uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size);
+ FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
+}
+
+void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) {
+ uptr end = ptr + size;
+ if (Verbosity()) {
+ Printf("__asan_%spoison_intra_object_redzone [%p,%p) %zd\n",
+ poison ? "" : "un", ptr, end, size);
+ if (Verbosity() >= 2)
+ PRINT_CURRENT_STACK();
+ }
+ CHECK(size);
+ CHECK_LE(size, 4096);
+ CHECK(IsAligned(end, SHADOW_GRANULARITY));
+ if (!IsAligned(ptr, SHADOW_GRANULARITY)) {
+ *(u8 *)MemToShadow(ptr) =
+ poison ? static_cast<u8>(ptr % SHADOW_GRANULARITY) : 0;
+ ptr |= SHADOW_GRANULARITY - 1;
+ ptr++;
+ }
+ for (; ptr < end; ptr += SHADOW_GRANULARITY)
+ *(u8*)MemToShadow(ptr) = poison ? kAsanIntraObjectRedzone : 0;
+}
+
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
if (!flags()->allow_user_poisoning || size == 0) return;
uptr beg_addr = (uptr)addr;
uptr end_addr = beg_addr + size;
- if (common_flags()->verbosity >= 1) {
- Printf("Trying to poison memory region [%p, %p)\n",
- (void*)beg_addr, (void*)end_addr);
- }
+ VPrintf(3, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr,
+ (void *)end_addr);
ShadowSegmentEndpoint beg(beg_addr);
ShadowSegmentEndpoint end(end_addr);
if (beg.chunk == end.chunk) {
if (!flags()->allow_user_poisoning || size == 0) return;
uptr beg_addr = (uptr)addr;
uptr end_addr = beg_addr + size;
- if (common_flags()->verbosity >= 1) {
- Printf("Trying to unpoison memory region [%p, %p)\n",
- (void*)beg_addr, (void*)end_addr);
- }
+ VPrintf(3, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr,
+ (void *)end_addr);
ShadowSegmentEndpoint beg(beg_addr);
ShadowSegmentEndpoint end(end_addr);
if (beg.chunk == end.chunk) {
}
}
-bool __asan_address_is_poisoned(void const volatile *addr) {
+int __asan_address_is_poisoned(void const volatile *addr) {
return __asan::AddressIsPoisoned((uptr)addr);
}
uptr end = beg + size;
if (!AddrIsInMem(beg)) return beg;
if (!AddrIsInMem(end)) return end;
+ CHECK_LT(beg, end);
uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY);
uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY);
uptr shadow_beg = MemToShadow(aligned_b);
__asan::AddressIsPoisoned(__p + __size - 1))) { \
GET_CURRENT_PC_BP_SP; \
uptr __bad = __asan_region_is_poisoned(__p, __size); \
- __asan_report_error(pc, bp, sp, __bad, isWrite, __size);\
+ __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\
} \
} while (false); \
*p = x;
}
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_poison_cxx_array_cookie(uptr p) {
+ if (SANITIZER_WORDSIZE != 64) return;
+ if (!flags()->poison_array_cookie) return;
+ uptr s = MEM_TO_SHADOW(p);
+ *reinterpret_cast<u8*>(s) = kAsanArrayCookieMagic;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_load_cxx_array_cookie(uptr *p) {
+ if (SANITIZER_WORDSIZE != 64) return *p;
+ if (!flags()->poison_array_cookie) return *p;
+ uptr s = MEM_TO_SHADOW(reinterpret_cast<uptr>(p));
+ u8 sval = *reinterpret_cast<u8*>(s);
+ if (sval == kAsanArrayCookieMagic) return *p;
+ // If sval is not kAsanArrayCookieMagic it can only be freed memory,
+ // which means that we are going to get double-free. So, return 0 to avoid
+ // infinite loop of destructors. We don't want to report a double-free here
+ // though, so print a warning just in case.
+ // CHECK_EQ(sval, kAsanHeapFreeMagic);
+ if (sval == kAsanHeapFreeMagic) {
+ Report("AddressSanitizer: loaded array cookie from free-d memory; "
+ "expect a double-free report\n");
+ return 0;
+ }
+ // The cookie may remain unpoisoned if e.g. it comes from a custom
+ // operator new defined inside a class.
+ return *p;
+}
+
// This is a simplified version of __asan_(un)poison_memory_region, which
// assumes that left border of region to be poisoned is properly aligned.
static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
}
void __asan_poison_stack_memory(uptr addr, uptr size) {
- if (common_flags()->verbosity > 0)
- Report("poisoning: %p %zx\n", (void*)addr, size);
+ VReport(1, "poisoning: %p %zx\n", (void *)addr, size);
PoisonAlignedStackMemory(addr, size, true);
}
void __asan_unpoison_stack_memory(uptr addr, uptr size) {
- if (common_flags()->verbosity > 0)
- Report("unpoisoning: %p %zx\n", (void*)addr, size);
+ VReport(1, "unpoisoning: %p %zx\n", (void *)addr, size);
PoisonAlignedStackMemory(addr, size, false);
}
const void *end_p,
const void *old_mid_p,
const void *new_mid_p) {
- if (common_flags()->verbosity >= 2)
- Printf("contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p,
- new_mid_p);
+ if (!flags()->detect_container_overflow) return;
+ VPrintf(2, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p,
+ new_mid_p);
uptr beg = reinterpret_cast<uptr>(beg_p);
- uptr end= reinterpret_cast<uptr>(end_p);
+ uptr end = reinterpret_cast<uptr>(end_p);
uptr old_mid = reinterpret_cast<uptr>(old_mid_p);
uptr new_mid = reinterpret_cast<uptr>(new_mid_p);
uptr granularity = SHADOW_GRANULARITY;
- CHECK(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end &&
- IsAligned(beg, granularity));
+ if (!(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end &&
+ IsAligned(beg, granularity))) {
+ GET_STACK_TRACE_FATAL_HERE;
+ ReportBadParamsToAnnotateContiguousContainer(beg, end, old_mid, new_mid,
+ &stack);
+ }
CHECK_LE(end - beg,
FIRST_32_SECOND_64(1UL << 30, 1UL << 34)); // Sanity check.
uptr a = RoundDownTo(Min(old_mid, new_mid), granularity);
uptr c = RoundUpTo(Max(old_mid, new_mid), granularity);
uptr d1 = RoundDownTo(old_mid, granularity);
- uptr d2 = RoundUpTo(old_mid, granularity);
+ // uptr d2 = RoundUpTo(old_mid, granularity);
// Currently we should be in this state:
// [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good.
// Make a quick sanity check that we are indeed in this state.
- if (d1 != d2)
- CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1);
+ //
+ // FIXME: Two of these three checks are disabled until we fix
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=258.
+ // if (d1 != d2)
+ // CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1);
if (a + granularity <= d1)
CHECK_EQ(*(u8*)MemToShadow(a), 0);
- if (d2 + granularity <= c && c <= end)
- CHECK_EQ(*(u8 *)MemToShadow(c - granularity),
- kAsanContiguousContainerOOBMagic);
+ // if (d2 + granularity <= c && c <= end)
+ // CHECK_EQ(*(u8 *)MemToShadow(c - granularity),
+ // kAsanContiguousContainerOOBMagic);
uptr b1 = RoundDownTo(new_mid, granularity);
uptr b2 = RoundUpTo(new_mid, granularity);
*(u8*)MemToShadow(b1) = static_cast<u8>(new_mid - b1);
}
}
+
+const void *__sanitizer_contiguous_container_find_bad_address(
+ const void *beg_p, const void *mid_p, const void *end_p) {
+ if (!flags()->detect_container_overflow)
+ return nullptr;
+ uptr beg = reinterpret_cast<uptr>(beg_p);
+ uptr end = reinterpret_cast<uptr>(end_p);
+ uptr mid = reinterpret_cast<uptr>(mid_p);
+ CHECK_LE(beg, mid);
+ CHECK_LE(mid, end);
+ // Check some bytes starting from beg, some bytes around mid, and some bytes
+ // ending with end.
+ uptr kMaxRangeToCheck = 32;
+ uptr r1_beg = beg;
+ uptr r1_end = Min(end + kMaxRangeToCheck, mid);
+ uptr r2_beg = Max(beg, mid - kMaxRangeToCheck);
+ uptr r2_end = Min(end, mid + kMaxRangeToCheck);
+ uptr r3_beg = Max(end - kMaxRangeToCheck, mid);
+ uptr r3_end = end;
+ for (uptr i = r1_beg; i < r1_end; i++)
+ if (AddressIsPoisoned(i))
+ return reinterpret_cast<const void *>(i);
+ for (uptr i = r2_beg; i < mid; i++)
+ if (AddressIsPoisoned(i))
+ return reinterpret_cast<const void *>(i);
+ for (uptr i = mid; i < r2_end; i++)
+ if (!AddressIsPoisoned(i))
+ return reinterpret_cast<const void *>(i);
+ for (uptr i = r3_beg; i < r3_end; i++)
+ if (!AddressIsPoisoned(i))
+ return reinterpret_cast<const void *>(i);
+ return nullptr;
+}
+
+int __sanitizer_verify_contiguous_container(const void *beg_p,
+ const void *mid_p,
+ const void *end_p) {
+ return __sanitizer_contiguous_container_find_bad_address(beg_p, mid_p,
+ end_p) == nullptr;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_poison_intra_object_redzone(uptr ptr, uptr size) {
+ AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, true);
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_unpoison_intra_object_redzone(uptr ptr, uptr size) {
+ AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, false);
+}
+
+// --- Implementation of LSan-specific functions --- {{{1
+namespace __lsan {
+bool WordIsPoisoned(uptr addr) {
+ return (__asan_region_is_poisoned(addr, sizeof(uptr)) != 0);
+}
+}
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_mapping.h"
+#include "sanitizer_common/sanitizer_flags.h"
namespace __asan {
+// Enable/disable memory poisoning.
+void SetCanPoisonMemory(bool value);
+bool CanPoisonMemory();
+
// Poisons the shadow memory for "size" bytes starting from "addr".
void PoisonShadow(uptr addr, uptr size, u8 value);
// performance-critical code with care.
ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
u8 value) {
- DCHECK(flags()->poison_heap);
+ DCHECK(CanPoisonMemory());
uptr shadow_beg = MEM_TO_SHADOW(aligned_beg);
uptr shadow_end = MEM_TO_SHADOW(
aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1;
- REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
+ // FIXME: Page states are different on Windows, so using the same interface
+ // for mapping shadow and zeroing out pages doesn't "just work", so we should
+ // probably provide higher-level interface for these operations.
+ // For now, just memset on Windows.
+ if (value ||
+ SANITIZER_WINDOWS == 1 ||
+ shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
+ REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
+ } else {
+ uptr page_size = GetPageSizeCached();
+ uptr page_beg = RoundUpTo(shadow_beg, page_size);
+ uptr page_end = RoundDownTo(shadow_end, page_size);
+
+ if (page_beg >= page_end) {
+ REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
+ } else {
+ if (page_beg != shadow_beg) {
+ REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg);
+ }
+ if (page_end != shadow_end) {
+ REAL(memset)((void *)page_end, 0, shadow_end - page_end);
+ }
+ ReserveShadowMemoryRange(page_beg, page_end - 1, nullptr);
+ }
+ }
}
ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone(
uptr aligned_addr, uptr size, uptr redzone_size, u8 value) {
- DCHECK(flags()->poison_heap);
+ DCHECK(CanPoisonMemory());
bool poison_partial = flags()->poison_partial;
u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr);
for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) {
}
}
+// Calls __sanitizer::FlushUnneededShadowMemory() on
+// [MemToShadow(p), MemToShadow(p+size)] with proper rounding.
+void FlushUnneededASanShadowMemory(uptr p, uptr size);
+
} // namespace __asan
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_LINUX || SANITIZER_MAC
+#if SANITIZER_POSIX
#include "asan_internal.h"
#include "asan_interceptors.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_posix.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include <pthread.h>
#include <sys/resource.h>
#include <unistd.h>
-static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough.
-
namespace __asan {
-static void MaybeInstallSigaction(int signum,
- void (*handler)(int, siginfo_t *, void *)) {
- if (!AsanInterceptsSignal(signum))
- return;
- struct sigaction sigact;
- REAL(memset)(&sigact, 0, sizeof(sigact));
- sigact.sa_sigaction = handler;
- sigact.sa_flags = SA_SIGINFO;
- if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
- CHECK_EQ(0, REAL(sigaction)(signum, &sigact, 0));
- if (common_flags()->verbosity >= 1) {
- Report("Installed the sigaction for signal %d\n", signum);
- }
-}
-
-static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) {
- uptr addr = (uptr)siginfo->si_addr;
+void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
+ ScopedDeadlySignal signal_scope(GetCurrentThread());
+ int code = (int)((siginfo_t*)siginfo)->si_code;
// Write the first message using the bullet-proof write.
- if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die();
- uptr pc, sp, bp;
- GetPcSpBp(context, &pc, &sp, &bp);
- ReportSIGSEGV(pc, sp, bp, addr);
-}
-
-void SetAlternateSignalStack() {
- stack_t altstack, oldstack;
- CHECK_EQ(0, sigaltstack(0, &oldstack));
- // If the alternate stack is already in place, do nothing.
- if ((oldstack.ss_flags & SS_DISABLE) == 0) return;
- // TODO(glider): the mapped stack should have the MAP_STACK flag in the
- // future. It is not required by man 2 sigaltstack now (they're using
- // malloc()).
- void* base = MmapOrDie(kAltStackSize, __FUNCTION__);
- altstack.ss_sp = base;
- altstack.ss_flags = 0;
- altstack.ss_size = kAltStackSize;
- CHECK_EQ(0, sigaltstack(&altstack, 0));
- if (common_flags()->verbosity > 0) {
- Report("Alternative stack for T%d set: [%p,%p)\n",
- GetCurrentTidOrInvalid(),
- altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size);
+ if (18 != internal_write(2, "ASAN:DEADLYSIGNAL\n", 18)) Die();
+ SignalContext sig = SignalContext::Create(siginfo, context);
+
+ // Access at a reasonable offset above SP, or slightly below it (to account
+ // for x86_64 or PowerPC redzone, ARM push of multiple registers, etc) is
+ // probably a stack overflow.
+ bool IsStackAccess = sig.addr + 512 > sig.sp && sig.addr < sig.sp + 0xFFFF;
+
+#if __powerpc__
+ // Large stack frames can be allocated with e.g.
+ // lis r0,-10000
+ // stdux r1,r1,r0 # store sp to [sp-10000] and update sp by -10000
+ // If the store faults then sp will not have been updated, so test above
+ // will not work, becase the fault address will be more than just "slightly"
+ // below sp.
+ if (!IsStackAccess && IsAccessibleMemoryRange(sig.pc, 4)) {
+ u32 inst = *(unsigned *)sig.pc;
+ u32 ra = (inst >> 16) & 0x1F;
+ u32 opcd = inst >> 26;
+ u32 xo = (inst >> 1) & 0x3FF;
+ // Check for store-with-update to sp. The instructions we accept are:
+ // stbu rs,d(ra) stbux rs,ra,rb
+ // sthu rs,d(ra) sthux rs,ra,rb
+ // stwu rs,d(ra) stwux rs,ra,rb
+ // stdu rs,ds(ra) stdux rs,ra,rb
+ // where ra is r1 (the stack pointer).
+ if (ra == 1 &&
+ (opcd == 39 || opcd == 45 || opcd == 37 || opcd == 62 ||
+ (opcd == 31 && (xo == 247 || xo == 439 || xo == 183 || xo == 181))))
+ IsStackAccess = true;
}
-}
-
-void UnsetAlternateSignalStack() {
- stack_t altstack, oldstack;
- altstack.ss_sp = 0;
- altstack.ss_flags = SS_DISABLE;
- altstack.ss_size = 0;
- CHECK_EQ(0, sigaltstack(&altstack, &oldstack));
- UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
-}
-
-void InstallSignalHandlers() {
- // Set the alternate signal stack for the main thread.
- // This will cause SetAlternateSignalStack to be called twice, but the stack
- // will be actually set only once.
- if (flags()->use_sigaltstack) SetAlternateSignalStack();
- MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV);
- MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV);
+#endif // __powerpc__
+
+ // We also check si_code to filter out SEGV caused by something else other
+ // then hitting the guard page or unmapped memory, like, for example,
+ // unaligned memory access.
+ if (IsStackAccess && (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
+ ReportStackOverflow(sig);
+ else if (signo == SIGFPE)
+ ReportDeadlySignal("FPE", sig);
+ else
+ ReportDeadlySignal("SEGV", sig);
}
// ---------------------- TSD ---------------- {{{1
}
} // namespace __asan
-#endif // SANITIZER_LINUX || SANITIZER_MAC
+#endif // SANITIZER_POSIX
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Call __asan_init at the very early stage of process startup.
-// On Linux we use .preinit_array section (unless PIC macro is defined).
//===----------------------------------------------------------------------===//
#include "asan_internal.h"
-#if ASAN_USE_PREINIT_ARRAY && !defined(PIC)
- // On Linux, we force __asan_init to be called before anyone else
- // by placing it into .preinit_array section.
- // FIXME: do we have anything like this on Mac?
+using namespace __asan;
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
// The symbol is called __local_asan_preinit, because it's not intended to be
// exported.
+ // This code linked into the main executable when -fsanitize=address is in
+ // the link flags. It can only use exported interface functions.
__attribute__((section(".preinit_array"), used))
void (*__local_asan_preinit)(void) = __asan_init;
-#elif SANITIZER_WINDOWS && defined(_DLL)
- // On Windows, when using dynamic CRT (/MD), we can put a pointer
- // to __asan_init into the global list of C initializers.
- // See crt0dat.c in the CRT sources for the details.
- #pragma section(".CRT$XIB", long, read) // NOLINT
- __declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init;
#endif
//
// This file contains error reporting code.
//===----------------------------------------------------------------------===//
+
#include "asan_flags.h"
#include "asan_internal.h"
#include "asan_mapping.h"
// -------------------- User-specified callbacks ----------------- {{{1
static void (*error_report_callback)(const char*);
-static char *error_message_buffer = 0;
+static char *error_message_buffer = nullptr;
static uptr error_message_buffer_pos = 0;
static uptr error_message_buffer_size = 0;
+struct ReportData {
+ uptr pc;
+ uptr sp;
+ uptr bp;
+ uptr addr;
+ bool is_write;
+ uptr access_size;
+ const char *description;
+};
+
+static bool report_happened = false;
+static ReportData report_data = {};
+
void AppendToErrorMessageBuffer(const char *buffer) {
if (error_message_buffer) {
uptr length = internal_strlen(buffer);
buffer, remaining);
error_message_buffer[error_message_buffer_size - 1] = '\0';
// FIXME: reallocate the buffer instead of truncating the message.
- error_message_buffer_pos += remaining > length ? length : remaining;
+ error_message_buffer_pos += Min(remaining, length);
}
}
// ---------------------- Decorator ------------------------------ {{{1
-class Decorator: private __sanitizer::AnsiColorDecorator {
+class Decorator: public __sanitizer::SanitizerCommonDecorator {
public:
- Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
- const char *Warning() { return Red(); }
- const char *EndWarning() { return Default(); }
+ Decorator() : SanitizerCommonDecorator() { }
const char *Access() { return Blue(); }
const char *EndAccess() { return Default(); }
const char *Location() { return Green(); }
switch (byte) {
case kAsanHeapLeftRedzoneMagic:
case kAsanHeapRightRedzoneMagic:
+ case kAsanArrayCookieMagic:
return Red();
case kAsanHeapFreeMagic:
return Magenta();
return Cyan();
case kAsanUserPoisonedMemoryMagic:
case kAsanContiguousContainerOOBMagic:
+ case kAsanAllocaLeftMagic:
+ case kAsanAllocaRightMagic:
return Blue();
case kAsanStackUseAfterScopeMagic:
return Magenta();
return Red();
case kAsanInternalHeapMagic:
return Yellow();
+ case kAsanIntraObjectRedzone:
+ return Yellow();
default:
return Default();
}
}
const char *EndShadowByte() { return Default(); }
+ const char *MemoryByte() { return Magenta(); }
+ const char *EndMemoryByte() { return Default(); }
};
// ---------------------- Helper functions ----------------------- {{{1
-static void PrintShadowByte(const char *before, u8 byte,
- const char *after = "\n") {
+static void PrintMemoryByte(InternalScopedString *str, const char *before,
+ u8 byte, bool in_shadow, const char *after = "\n") {
Decorator d;
- Printf("%s%s%x%x%s%s", before,
- d.ShadowByte(byte), byte >> 4, byte & 15, d.EndShadowByte(), after);
+ str->append("%s%s%x%x%s%s", before,
+ in_shadow ? d.ShadowByte(byte) : d.MemoryByte(),
+ byte >> 4, byte & 15,
+ in_shadow ? d.EndShadowByte() : d.EndMemoryByte(), after);
+}
+
+static void PrintShadowByte(InternalScopedString *str, const char *before,
+ u8 byte, const char *after = "\n") {
+ PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
}
-static void PrintShadowBytes(const char *before, u8 *bytes,
- u8 *guilty, uptr n) {
+static void PrintShadowBytes(InternalScopedString *str, const char *before,
+ u8 *bytes, u8 *guilty, uptr n) {
Decorator d;
- if (before)
- Printf("%s%p:", before, bytes);
+ if (before) str->append("%s%p:", before, bytes);
for (uptr i = 0; i < n; i++) {
u8 *p = bytes + i;
- const char *before = p == guilty ? "[" :
- (p - 1 == guilty && i != 0) ? "" : " ";
+ const char *before =
+ p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " ";
const char *after = p == guilty ? "]" : "";
- PrintShadowByte(before, *p, after);
+ PrintShadowByte(str, before, *p, after);
}
- Printf("\n");
-}
-
-static void PrintLegend() {
- Printf("Shadow byte legend (one shadow byte represents %d "
- "application bytes):\n", (int)SHADOW_GRANULARITY);
- PrintShadowByte(" Addressable: ", 0);
- Printf(" Partially addressable: ");
- for (u8 i = 1; i < SHADOW_GRANULARITY; i++)
- PrintShadowByte("", i, " ");
- Printf("\n");
- PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic);
- PrintShadowByte(" Heap right redzone: ", kAsanHeapRightRedzoneMagic);
- PrintShadowByte(" Freed heap region: ", kAsanHeapFreeMagic);
- PrintShadowByte(" Stack left redzone: ", kAsanStackLeftRedzoneMagic);
- PrintShadowByte(" Stack mid redzone: ", kAsanStackMidRedzoneMagic);
- PrintShadowByte(" Stack right redzone: ", kAsanStackRightRedzoneMagic);
- PrintShadowByte(" Stack partial redzone: ", kAsanStackPartialRedzoneMagic);
- PrintShadowByte(" Stack after return: ", kAsanStackAfterReturnMagic);
- PrintShadowByte(" Stack use after scope: ", kAsanStackUseAfterScopeMagic);
- PrintShadowByte(" Global redzone: ", kAsanGlobalRedzoneMagic);
- PrintShadowByte(" Global init order: ", kAsanInitializationOrderMagic);
- PrintShadowByte(" Poisoned by user: ", kAsanUserPoisonedMemoryMagic);
- PrintShadowByte(" Contiguous container OOB:",
+ str->append("\n");
+}
+
+static void PrintLegend(InternalScopedString *str) {
+ str->append(
+ "Shadow byte legend (one shadow byte represents %d "
+ "application bytes):\n",
+ (int)SHADOW_GRANULARITY);
+ PrintShadowByte(str, " Addressable: ", 0);
+ str->append(" Partially addressable: ");
+ for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " ");
+ str->append("\n");
+ PrintShadowByte(str, " Heap left redzone: ",
+ kAsanHeapLeftRedzoneMagic);
+ PrintShadowByte(str, " Heap right redzone: ",
+ kAsanHeapRightRedzoneMagic);
+ PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic);
+ PrintShadowByte(str, " Stack left redzone: ",
+ kAsanStackLeftRedzoneMagic);
+ PrintShadowByte(str, " Stack mid redzone: ",
+ kAsanStackMidRedzoneMagic);
+ PrintShadowByte(str, " Stack right redzone: ",
+ kAsanStackRightRedzoneMagic);
+ PrintShadowByte(str, " Stack partial redzone: ",
+ kAsanStackPartialRedzoneMagic);
+ PrintShadowByte(str, " Stack after return: ",
+ kAsanStackAfterReturnMagic);
+ PrintShadowByte(str, " Stack use after scope: ",
+ kAsanStackUseAfterScopeMagic);
+ PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic);
+ PrintShadowByte(str, " Global init order: ",
+ kAsanInitializationOrderMagic);
+ PrintShadowByte(str, " Poisoned by user: ",
+ kAsanUserPoisonedMemoryMagic);
+ PrintShadowByte(str, " Container overflow: ",
kAsanContiguousContainerOOBMagic);
- PrintShadowByte(" ASan internal: ", kAsanInternalHeapMagic);
+ PrintShadowByte(str, " Array cookie: ",
+ kAsanArrayCookieMagic);
+ PrintShadowByte(str, " Intra object redzone: ",
+ kAsanIntraObjectRedzone);
+ PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
+ PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic);
+ PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic);
}
-static void PrintShadowMemoryForAddress(uptr addr) {
- if (!AddrIsInMem(addr))
+void MaybeDumpInstructionBytes(uptr pc) {
+ if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached()))
return;
+ InternalScopedString str(1024);
+ str.append("First 16 instruction bytes at pc: ");
+ if (IsAccessibleMemoryRange(pc, 16)) {
+ for (int i = 0; i < 16; ++i) {
+ PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/false, " ");
+ }
+ str.append("\n");
+ } else {
+ str.append("unaccessible\n");
+ }
+ Report("%s", str.data());
+}
+
+static void PrintShadowMemoryForAddress(uptr addr) {
+ if (!AddrIsInMem(addr)) return;
uptr shadow_addr = MemToShadow(addr);
const uptr n_bytes_per_row = 16;
uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
- Printf("Shadow bytes around the buggy address:\n");
+ InternalScopedString str(4096 * 8);
+ str.append("Shadow bytes around the buggy address:\n");
for (int i = -5; i <= 5; i++) {
const char *prefix = (i == 0) ? "=>" : " ";
- PrintShadowBytes(prefix,
- (u8*)(aligned_shadow + i * n_bytes_per_row),
- (u8*)shadow_addr, n_bytes_per_row);
+ PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row),
+ (u8 *)shadow_addr, n_bytes_per_row);
}
- if (flags()->print_legend)
- PrintLegend();
+ if (flags()->print_legend) PrintLegend(&str);
+ Printf("%s", str.data());
}
static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
static const char *MaybeDemangleGlobalName(const char *name) {
// We can spoil names of globals with C linkage, so use an heuristic
// approach to check if the name should be demangled.
- return (name[0] == '_' && name[1] == 'Z')
- ? Symbolizer::Get()->Demangle(name)
- : name;
+ bool should_demangle = false;
+ if (name[0] == '_' && name[1] == 'Z')
+ should_demangle = true;
+ else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
+ should_demangle = true;
+
+ return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
}
// Check if the global is a zero-terminated ASCII string. If so, print it.
-static void PrintGlobalNameIfASCII(const __asan_global &g) {
+static void PrintGlobalNameIfASCII(InternalScopedString *str,
+ const __asan_global &g) {
for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
unsigned char c = *(unsigned char*)p;
if (c == '\0' || !IsASCII(c)) return;
}
if (*(char*)(g.beg + g.size - 1) != '\0') return;
- Printf(" '%s' is ascii string '%s'\n",
- MaybeDemangleGlobalName(g.name), (char*)g.beg);
+ str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
+ (char *)g.beg);
}
-bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
- const __asan_global &g) {
- static const uptr kMinimalDistanceFromAnotherGlobal = 64;
- if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
- if (addr >= g.beg + g.size_with_redzone) return false;
+static const char *GlobalFilename(const __asan_global &g) {
+ const char *res = g.module_name;
+ // Prefer the filename from source location, if is available.
+ if (g.location)
+ res = g.location->filename;
+ CHECK(res);
+ return res;
+}
+
+static void PrintGlobalLocation(InternalScopedString *str,
+ const __asan_global &g) {
+ str->append("%s", GlobalFilename(g));
+ if (!g.location)
+ return;
+ if (g.location->line_no)
+ str->append(":%d", g.location->line_no);
+ if (g.location->column_no)
+ str->append(":%d", g.location->column_no);
+}
+
+static void DescribeAddressRelativeToGlobal(uptr addr, uptr size,
+ const __asan_global &g) {
+ InternalScopedString str(4096);
Decorator d;
- Printf("%s", d.Location());
+ str.append("%s", d.Location());
if (addr < g.beg) {
- Printf("%p is located %zd bytes to the left", (void*)addr, g.beg - addr);
+ str.append("%p is located %zd bytes to the left", (void *)addr,
+ g.beg - addr);
} else if (addr + size > g.beg + g.size) {
if (addr < g.beg + g.size)
addr = g.beg + g.size;
- Printf("%p is located %zd bytes to the right", (void*)addr,
- addr - (g.beg + g.size));
+ str.append("%p is located %zd bytes to the right", (void *)addr,
+ addr - (g.beg + g.size));
} else {
// Can it happen?
- Printf("%p is located %zd bytes inside", (void*)addr, addr - g.beg);
+ str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
+ }
+ str.append(" of global variable '%s' defined in '",
+ MaybeDemangleGlobalName(g.name));
+ PrintGlobalLocation(&str, g);
+ str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
+ str.append("%s", d.EndLocation());
+ PrintGlobalNameIfASCII(&str, g);
+ Printf("%s", str.data());
+}
+
+static bool DescribeAddressIfGlobal(uptr addr, uptr size,
+ const char *bug_type) {
+ // Assume address is close to at most four globals.
+ const int kMaxGlobalsInReport = 4;
+ __asan_global globals[kMaxGlobalsInReport];
+ u32 reg_sites[kMaxGlobalsInReport];
+ int globals_num =
+ GetGlobalsForAddress(addr, globals, reg_sites, ARRAY_SIZE(globals));
+ if (globals_num == 0)
+ return false;
+ for (int i = 0; i < globals_num; i++) {
+ DescribeAddressRelativeToGlobal(addr, size, globals[i]);
+ if (0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
+ reg_sites[i]) {
+ Printf(" registered at:\n");
+ StackDepotGet(reg_sites[i]).Print();
+ }
}
- Printf(" of global variable '%s' from '%s' (0x%zx) of size %zu\n",
- MaybeDemangleGlobalName(g.name), g.module_name, g.beg, g.size);
- Printf("%s", d.EndLocation());
- PrintGlobalNameIfASCII(g);
return true;
}
-bool DescribeAddressIfShadow(uptr addr) {
+bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr, bool print) {
if (AddrIsInMem(addr))
return false;
- static const char kAddrInShadowReport[] =
- "Address %p is located in the %s.\n";
- if (AddrIsInShadowGap(addr)) {
- Printf(kAddrInShadowReport, addr, "shadow gap area");
- return true;
- }
- if (AddrIsInHighShadow(addr)) {
- Printf(kAddrInShadowReport, addr, "high shadow area");
- return true;
- }
- if (AddrIsInLowShadow(addr)) {
- Printf(kAddrInShadowReport, addr, "low shadow area");
+ const char *area_type = nullptr;
+ if (AddrIsInShadowGap(addr)) area_type = "shadow gap";
+ else if (AddrIsInHighShadow(addr)) area_type = "high shadow";
+ else if (AddrIsInLowShadow(addr)) area_type = "low shadow";
+ if (area_type != nullptr) {
+ if (print) {
+ Printf("Address %p is located in the %s area.\n", addr, area_type);
+ } else {
+ CHECK(descr);
+ descr->region_kind = area_type;
+ }
return true;
}
CHECK(0 && "Address is not in memory and not in shadow?");
return ThreadNameWithParenthesis(t, buff, buff_len);
}
-void PrintAccessAndVarIntersection(const char *var_name,
- uptr var_beg, uptr var_size,
- uptr addr, uptr access_size,
- uptr prev_var_end, uptr next_var_beg) {
- uptr var_end = var_beg + var_size;
+static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
+ uptr access_size, uptr prev_var_end,
+ uptr next_var_beg) {
+ uptr var_end = var.beg + var.size;
uptr addr_end = addr + access_size;
- const char *pos_descr = 0;
- // If the variable [var_beg, var_end) is the nearest variable to the
+ const char *pos_descr = nullptr;
+ // If the variable [var.beg, var_end) is the nearest variable to the
// current memory access, indicate it in the log.
- if (addr >= var_beg) {
+ if (addr >= var.beg) {
if (addr_end <= var_end)
pos_descr = "is inside"; // May happen if this is a use-after-return.
else if (addr < var_end)
next_var_beg - addr_end >= addr - var_end)
pos_descr = "overflows";
} else {
- if (addr_end > var_beg)
+ if (addr_end > var.beg)
pos_descr = "partially underflows";
else if (addr >= prev_var_end &&
- addr - prev_var_end >= var_beg - addr_end)
+ addr - prev_var_end >= var.beg - addr_end)
pos_descr = "underflows";
}
- Printf(" [%zd, %zd) '%s'", var_beg, var_beg + var_size, var_name);
+ InternalScopedString str(1024);
+ str.append(" [%zd, %zd)", var.beg, var_end);
+ // Render variable name.
+ str.append(" '");
+ for (uptr i = 0; i < var.name_len; ++i) {
+ str.append("%c", var.name_pos[i]);
+ }
+ str.append("'");
if (pos_descr) {
Decorator d;
// FIXME: we may want to also print the size of the access here,
// but in case of accesses generated by memset it may be confusing.
- Printf("%s <== Memory access at offset %zd %s this variable%s\n",
- d.Location(), addr, pos_descr, d.EndLocation());
+ str.append("%s <== Memory access at offset %zd %s this variable%s\n",
+ d.Location(), addr, pos_descr, d.EndLocation());
} else {
- Printf("\n");
+ str.append("\n");
}
+ Printf("%s", str.data());
}
-struct StackVarDescr {
- uptr beg;
- uptr size;
- const char *name_pos;
- uptr name_len;
-};
+bool ParseFrameDescription(const char *frame_descr,
+ InternalMmapVector<StackVarDescr> *vars) {
+ CHECK(frame_descr);
+ char *p;
+ // This string is created by the compiler and has the following form:
+ // "n alloc_1 alloc_2 ... alloc_n"
+ // where alloc_i looks like "offset size len ObjectName".
+ uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
+ if (n_objects == 0)
+ return false;
+
+ for (uptr i = 0; i < n_objects; i++) {
+ uptr beg = (uptr)internal_simple_strtoll(p, &p, 10);
+ uptr size = (uptr)internal_simple_strtoll(p, &p, 10);
+ uptr len = (uptr)internal_simple_strtoll(p, &p, 10);
+ if (beg == 0 || size == 0 || *p != ' ') {
+ return false;
+ }
+ p++;
+ StackVarDescr var = {beg, size, p, len};
+ vars->push_back(var);
+ p += len;
+ }
+
+ return true;
+}
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
AsanThread *t = FindThreadByStackAddress(addr);
if (!t) return false;
- const uptr kBufSize = 4095;
- char buf[kBufSize];
- uptr offset = 0;
- uptr frame_pc = 0;
- char tname[128];
- const char *frame_descr = t->GetFrameNameByAddr(addr, &offset, &frame_pc);
-
-#ifdef __powerpc64__
- // On PowerPC64, the address of a function actually points to a
- // three-doubleword data structure with the first field containing
- // the address of the function's code.
- frame_pc = *reinterpret_cast<uptr *>(frame_pc);
-#endif
- // This string is created by the compiler and has the following form:
- // "n alloc_1 alloc_2 ... alloc_n"
- // where alloc_i looks like "offset size len ObjectName ".
- CHECK(frame_descr);
Decorator d;
+ char tname[128];
Printf("%s", d.Location());
- Printf("Address %p is located in stack of thread T%d%s "
- "at offset %zu in frame\n",
- addr, t->tid(),
- ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)),
- offset);
+ Printf("Address %p is located in stack of thread T%d%s", addr, t->tid(),
+ ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)));
+
+ // Try to fetch precise stack frame for this access.
+ AsanThread::StackFrameAccess access;
+ if (!t->GetStackFrameAccessByAddr(addr, &access)) {
+ Printf("%s\n", d.EndLocation());
+ return true;
+ }
+ Printf(" at offset %zu in frame%s\n", access.offset, d.EndLocation());
+
// Now we print the frame where the alloca has happened.
// We print this frame as a stack trace with one element.
// The symbolizer may print more than one frame if inlining was involved.
// previously. That's unfortunate, but I have no better solution,
// especially given that the alloca may be from entirely different place
// (e.g. use-after-scope, or different thread's stack).
- StackTrace alloca_stack;
- alloca_stack.trace[0] = frame_pc + 16;
- alloca_stack.size = 1;
+#if defined(__powerpc64__) && defined(__BIG_ENDIAN__)
+ // On PowerPC64 ELFv1, the address of a function actually points to a
+ // three-doubleword data structure with the first field containing
+ // the address of the function's code.
+ access.frame_pc = *reinterpret_cast<uptr *>(access.frame_pc);
+#endif
+ access.frame_pc += 16;
Printf("%s", d.EndLocation());
- PrintStack(&alloca_stack);
+ StackTrace alloca_stack(&access.frame_pc, 1);
+ alloca_stack.Print();
+
+ InternalMmapVector<StackVarDescr> vars(16);
+ if (!ParseFrameDescription(access.frame_descr, &vars)) {
+ Printf("AddressSanitizer can't parse the stack frame "
+ "descriptor: |%s|\n", access.frame_descr);
+ // 'addr' is a stack address, so return true even if we can't parse frame
+ return true;
+ }
+ uptr n_objects = vars.size();
// Report the number of stack objects.
- char *p;
- uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
- CHECK_GT(n_objects, 0);
Printf(" This frame has %zu object(s):\n", n_objects);
// Report all objects in this frame.
- InternalScopedBuffer<StackVarDescr> vars(n_objects);
for (uptr i = 0; i < n_objects; i++) {
- uptr beg, size;
- uptr len;
- beg = (uptr)internal_simple_strtoll(p, &p, 10);
- size = (uptr)internal_simple_strtoll(p, &p, 10);
- len = (uptr)internal_simple_strtoll(p, &p, 10);
- if (beg == 0 || size == 0 || *p != ' ') {
- Printf("AddressSanitizer can't parse the stack frame "
- "descriptor: |%s|\n", frame_descr);
- break;
- }
- p++;
- vars[i].beg = beg;
- vars[i].size = size;
- vars[i].name_pos = p;
- vars[i].name_len = len;
- p += len;
- }
- for (uptr i = 0; i < n_objects; i++) {
- buf[0] = 0;
- internal_strncat(buf, vars[i].name_pos,
- static_cast<uptr>(Min(kBufSize, vars[i].name_len)));
uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
- PrintAccessAndVarIntersection(buf, vars[i].beg, vars[i].size,
- offset, access_size,
+ PrintAccessAndVarIntersection(vars[i], access.offset, access_size,
prev_var_end, next_var_beg);
}
Printf("HINT: this may be a false positive if your program uses "
- "some custom stack unwind mechanism or swapcontext\n"
- " (longjmp and C++ exceptions *are* supported)\n");
+ "some custom stack unwind mechanism or swapcontext\n");
+ if (SANITIZER_WINDOWS)
+ Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
+ else
+ Printf(" (longjmp and C++ exceptions *are* supported)\n");
+
DescribeThread(t);
return true;
}
uptr access_size) {
sptr offset;
Decorator d;
- Printf("%s", d.Location());
+ InternalScopedString str(4096);
+ str.append("%s", d.Location());
if (chunk.AddrIsAtLeft(addr, access_size, &offset)) {
- Printf("%p is located %zd bytes to the left of", (void*)addr, offset);
+ str.append("%p is located %zd bytes to the left of", (void *)addr, offset);
} else if (chunk.AddrIsAtRight(addr, access_size, &offset)) {
if (offset < 0) {
addr -= offset;
offset = 0;
}
- Printf("%p is located %zd bytes to the right of", (void*)addr, offset);
+ str.append("%p is located %zd bytes to the right of", (void *)addr, offset);
} else if (chunk.AddrIsInside(addr, access_size, &offset)) {
- Printf("%p is located %zd bytes inside of", (void*)addr, offset);
+ str.append("%p is located %zd bytes inside of", (void*)addr, offset);
} else {
- Printf("%p is located somewhere around (this is AddressSanitizer bug!)",
- (void*)addr);
+ str.append("%p is located somewhere around (this is AddressSanitizer bug!)",
+ (void *)addr);
}
- Printf(" %zu-byte region [%p,%p)\n", chunk.UsedSize(),
- (void*)(chunk.Beg()), (void*)(chunk.End()));
- Printf("%s", d.EndLocation());
+ str.append(" %zu-byte region [%p,%p)\n", chunk.UsedSize(),
+ (void *)(chunk.Beg()), (void *)(chunk.End()));
+ str.append("%s", d.EndLocation());
+ Printf("%s", str.data());
}
void DescribeHeapAddress(uptr addr, uptr access_size) {
asanThreadRegistry().CheckLocked();
AsanThreadContext *alloc_thread =
GetThreadContextByTidLocked(chunk.AllocTid());
- StackTrace alloc_stack;
- chunk.GetAllocStack(&alloc_stack);
+ StackTrace alloc_stack = chunk.GetAllocStack();
char tname[128];
Decorator d;
- AsanThreadContext *free_thread = 0;
+ AsanThreadContext *free_thread = nullptr;
if (chunk.FreeTid() != kInvalidTid) {
free_thread = GetThreadContextByTidLocked(chunk.FreeTid());
Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
free_thread->tid,
ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
d.EndAllocation());
- StackTrace free_stack;
- chunk.GetFreeStack(&free_stack);
- PrintStack(&free_stack);
+ StackTrace free_stack = chunk.GetFreeStack();
+ free_stack.Print();
Printf("%spreviously allocated by thread T%d%s here:%s\n",
d.Allocation(), alloc_thread->tid,
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
d.EndAllocation());
}
- PrintStack(&alloc_stack);
+ alloc_stack.Print();
DescribeThread(GetCurrentThread());
if (free_thread)
DescribeThread(free_thread);
DescribeThread(alloc_thread);
}
-void DescribeAddress(uptr addr, uptr access_size) {
+static void DescribeAddress(uptr addr, uptr access_size, const char *bug_type) {
// Check if this is shadow or shadow gap.
if (DescribeAddressIfShadow(addr))
return;
CHECK(AddrIsInMem(addr));
- if (DescribeAddressIfGlobal(addr, access_size))
+ if (DescribeAddressIfGlobal(addr, access_size, bug_type))
return;
if (DescribeAddressIfStack(addr, access_size))
return;
}
context->announced = true;
char tname[128];
- Printf("Thread T%d%s", context->tid,
- ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
- Printf(" created by T%d%s here:\n",
- context->parent_tid,
- ThreadNameWithParenthesis(context->parent_tid,
- tname, sizeof(tname)));
- uptr stack_size;
- const uptr *stack_trace = StackDepotGet(context->stack_id, &stack_size);
- PrintStack(stack_trace, stack_size);
+ InternalScopedString str(1024);
+ str.append("Thread T%d%s", context->tid,
+ ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
+ if (context->parent_tid == kInvalidTid) {
+ str.append(" created by unknown thread\n");
+ Printf("%s", str.data());
+ return;
+ }
+ str.append(
+ " created by T%d%s here:\n", context->parent_tid,
+ ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname)));
+ Printf("%s", str.data());
+ StackDepotGet(context->stack_id).Print();
// Recursively described parent thread if needed.
if (flags()->print_full_thread_history) {
AsanThreadContext *parent_context =
// immediately after printing error report.
class ScopedInErrorReport {
public:
- ScopedInErrorReport() {
- static atomic_uint32_t num_calls;
- static u32 reporting_thread_tid;
- if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
+ explicit ScopedInErrorReport(ReportData *report = nullptr,
+ bool fatal = false) {
+ halt_on_error_ = fatal || flags()->halt_on_error;
+
+ if (lock_.TryLock()) {
+ StartReporting(report);
+ return;
+ }
+
+ // ASan found two bugs in different threads simultaneously.
+
+ u32 current_tid = GetCurrentTidOrInvalid();
+ if (reporting_thread_tid_ == current_tid ||
+ reporting_thread_tid_ == kInvalidTid) {
+ // This is either asynch signal or nested error during error reporting.
+ // Fail simple to avoid deadlocks in Report().
+
+ // Can't use Report() here because of potential deadlocks
+ // in nested signal handlers.
+ const char msg[] = "AddressSanitizer: nested bug in the same thread, "
+ "aborting.\n";
+ WriteToFile(kStderrFd, msg, sizeof(msg));
+
+ internal__exit(common_flags()->exitcode);
+ }
+
+ if (halt_on_error_) {
// Do not print more than one report, otherwise they will mix up.
// Error reporting functions shouldn't return at this situation, as
- // they are defined as no-return.
- Report("AddressSanitizer: while reporting a bug found another one."
- "Ignoring.\n");
- u32 current_tid = GetCurrentTidOrInvalid();
- if (current_tid != reporting_thread_tid) {
- // ASan found two bugs in different threads simultaneously. Sleep
- // long enough to make sure that the thread which started to print
- // an error report will finish doing it.
- SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
- }
+ // they are effectively no-returns.
+
+ Report("AddressSanitizer: while reporting a bug found another one. "
+ "Ignoring.\n");
+
+ // Sleep long enough to make sure that the thread which started
+ // to print an error report will finish doing it.
+ SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
+
// If we're still not dead for some reason, use raw _exit() instead of
// Die() to bypass any additional checks.
- internal__exit(flags()->exitcode);
+ internal__exit(common_flags()->exitcode);
+ } else {
+ // The other thread will eventually finish reporting
+ // so it's safe to wait
+ lock_.Lock();
}
- ASAN_ON_ERROR();
- // Make sure the registry and sanitizer report mutexes are locked while
- // we're printing an error report.
- // We can lock them only here to avoid self-deadlock in case of
- // recursive reports.
- asanThreadRegistry().Lock();
- CommonSanitizerReportMutex.Lock();
- reporting_thread_tid = GetCurrentTidOrInvalid();
- Printf("===================================================="
- "=============\n");
+
+ StartReporting(report);
}
- // Destructor is NORETURN, as functions that report errors are.
- NORETURN ~ScopedInErrorReport() {
+
+ ~ScopedInErrorReport() {
// Make sure the current thread is announced.
DescribeThread(GetCurrentThread());
+ // We may want to grab this lock again when printing stats.
+ asanThreadRegistry().Unlock();
// Print memory stats.
if (flags()->print_stats)
__asan_print_accumulated_stats();
if (error_report_callback) {
error_report_callback(error_message_buffer);
}
- Report("ABORTING\n");
- Die();
+ CommonSanitizerReportMutex.Unlock();
+ reporting_thread_tid_ = kInvalidTid;
+ lock_.Unlock();
+ if (halt_on_error_) {
+ Report("ABORTING\n");
+ Die();
+ }
+ }
+
+ private:
+ void StartReporting(ReportData *report) {
+ if (report) report_data = *report;
+ report_happened = true;
+ ASAN_ON_ERROR();
+ // Make sure the registry and sanitizer report mutexes are locked while
+ // we're printing an error report.
+ // We can lock them only here to avoid self-deadlock in case of
+ // recursive reports.
+ asanThreadRegistry().Lock();
+ CommonSanitizerReportMutex.Lock();
+ reporting_thread_tid_ = GetCurrentTidOrInvalid();
+ Printf("===================================================="
+ "=============\n");
}
+
+ static StaticSpinMutex lock_;
+ static u32 reporting_thread_tid_;
+ bool halt_on_error_;
};
-void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) {
+StaticSpinMutex ScopedInErrorReport::lock_;
+u32 ScopedInErrorReport::reporting_thread_tid_;
+
+void ReportStackOverflow(const SignalContext &sig) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: SEGV on unknown address %p"
- " (pc %p sp %p bp %p T%d)\n",
- (void*)addr, (void*)pc, (void*)sp, (void*)bp,
- GetCurrentTidOrInvalid());
+ Report(
+ "ERROR: AddressSanitizer: stack-overflow on address %p"
+ " (pc %p bp %p sp %p T%d)\n",
+ (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, (void *)sig.sp,
+ GetCurrentTidOrInvalid());
Printf("%s", d.EndWarning());
- GET_STACK_TRACE_FATAL(pc, bp);
- PrintStack(&stack);
+ GET_STACK_TRACE_SIGNAL(sig);
+ stack.Print();
+ ReportErrorSummary("stack-overflow", &stack);
+}
+
+void ReportDeadlySignal(const char *description, const SignalContext &sig) {
+ ScopedInErrorReport in_report(/*report*/nullptr, /*fatal*/true);
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report(
+ "ERROR: AddressSanitizer: %s on unknown address %p"
+ " (pc %p bp %p sp %p T%d)\n",
+ description, (void *)sig.addr, (void *)sig.pc, (void *)sig.bp,
+ (void *)sig.sp, GetCurrentTidOrInvalid());
+ if (sig.pc < GetPageSizeCached()) {
+ Report("Hint: pc points to the zero page.\n");
+ }
+ Printf("%s", d.EndWarning());
+ GET_STACK_TRACE_SIGNAL(sig);
+ stack.Print();
+ MaybeDumpInstructionBytes(sig.pc);
Printf("AddressSanitizer can not provide additional info.\n");
- ReportErrorSummary("SEGV", &stack);
+ ReportErrorSummary(description, &stack);
}
-void ReportDoubleFree(uptr addr, StackTrace *free_stack) {
+void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
Printf("%s", d.EndWarning());
CHECK_GT(free_stack->size, 0);
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
- PrintStack(&stack);
+ stack.Print();
DescribeHeapAddress(addr, 1);
ReportErrorSummary("double-free", &stack);
}
-void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) {
+void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
+ BufferedStackTrace *free_stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ char tname[128];
+ u32 curr_tid = GetCurrentTidOrInvalid();
+ Report("ERROR: AddressSanitizer: new-delete-type-mismatch on %p in "
+ "thread T%d%s:\n",
+ addr, curr_tid,
+ ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
+ Printf("%s object passed to delete has wrong type:\n", d.EndWarning());
+ Printf(" size of the allocated type: %zd bytes;\n"
+ " size of the deallocated type: %zd bytes.\n",
+ asan_mz_size(reinterpret_cast<void*>(addr)), delete_size);
+ CHECK_GT(free_stack->size, 0);
+ GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
+ stack.Print();
+ DescribeHeapAddress(addr, 1);
+ ReportErrorSummary("new-delete-type-mismatch", &stack);
+ Report("HINT: if you don't care about these errors you may set "
+ "ASAN_OPTIONS=new_delete_type_mismatch=0\n");
+}
+
+void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
Printf("%s", d.EndWarning());
CHECK_GT(free_stack->size, 0);
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
- PrintStack(&stack);
+ stack.Print();
DescribeHeapAddress(addr, 1);
ReportErrorSummary("bad-free", &stack);
}
-void ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
+void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
AllocType alloc_type,
AllocType dealloc_type) {
static const char *alloc_names[] =
Printf("%s", d.EndWarning());
CHECK_GT(free_stack->size, 0);
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
- PrintStack(&stack);
+ stack.Print();
DescribeHeapAddress(addr, 1);
ReportErrorSummary("alloc-dealloc-mismatch", &stack);
- Report("HINT: if you don't care about these warnings you may set "
+ Report("HINT: if you don't care about these errors you may set "
"ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
}
-void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
+void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
"malloc_usable_size() for pointer which is "
"not owned: %p\n", addr);
Printf("%s", d.EndWarning());
- PrintStack(stack);
+ stack->Print();
DescribeHeapAddress(addr, 1);
ReportErrorSummary("bad-malloc_usable_size", stack);
}
-void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
+void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
+ BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: attempting to call "
- "__asan_get_allocated_size() for pointer which is "
+ "__sanitizer_get_allocated_size() for pointer which is "
"not owned: %p\n", addr);
Printf("%s", d.EndWarning());
- PrintStack(stack);
+ stack->Print();
DescribeHeapAddress(addr, 1);
- ReportErrorSummary("bad-__asan_get_allocated_size", stack);
+ ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
}
-void ReportStringFunctionMemoryRangesOverlap(
- const char *function, const char *offset1, uptr length1,
- const char *offset2, uptr length2, StackTrace *stack) {
+void ReportStringFunctionMemoryRangesOverlap(const char *function,
+ const char *offset1, uptr length1,
+ const char *offset2, uptr length2,
+ BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
char bug_type[100];
"memory ranges [%p,%p) and [%p, %p) overlap\n", \
bug_type, offset1, offset1 + length1, offset2, offset2 + length2);
Printf("%s", d.EndWarning());
- PrintStack(stack);
- DescribeAddress((uptr)offset1, length1);
- DescribeAddress((uptr)offset2, length2);
+ stack->Print();
+ DescribeAddress((uptr)offset1, length1, bug_type);
+ DescribeAddress((uptr)offset2, length2, bug_type);
+ ReportErrorSummary(bug_type, stack);
+}
+
+void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ const char *bug_type = "negative-size-param";
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size);
+ Printf("%s", d.EndWarning());
+ stack->Print();
+ DescribeAddress(offset, size, bug_type);
ReportErrorSummary(bug_type, stack);
}
+void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
+ uptr old_mid, uptr new_mid,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Report("ERROR: AddressSanitizer: bad parameters to "
+ "__sanitizer_annotate_contiguous_container:\n"
+ " beg : %p\n"
+ " end : %p\n"
+ " old_mid : %p\n"
+ " new_mid : %p\n",
+ beg, end, old_mid, new_mid);
+ uptr granularity = SHADOW_GRANULARITY;
+ if (!IsAligned(beg, granularity))
+ Report("ERROR: beg is not aligned by %d\n", granularity);
+ stack->Print();
+ ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
+}
+
+void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
+ const __asan_global *g2, u32 stack_id2) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg);
+ Printf("%s", d.EndWarning());
+ InternalScopedString g1_loc(256), g2_loc(256);
+ PrintGlobalLocation(&g1_loc, *g1);
+ PrintGlobalLocation(&g2_loc, *g2);
+ Printf(" [1] size=%zd '%s' %s\n", g1->size,
+ MaybeDemangleGlobalName(g1->name), g1_loc.data());
+ Printf(" [2] size=%zd '%s' %s\n", g2->size,
+ MaybeDemangleGlobalName(g2->name), g2_loc.data());
+ if (stack_id1 && stack_id2) {
+ Printf("These globals were registered at these points:\n");
+ Printf(" [1]:\n");
+ StackDepotGet(stack_id1).Print();
+ Printf(" [2]:\n");
+ StackDepotGet(stack_id2).Print();
+ }
+ Report("HINT: if you don't care about these errors you may set "
+ "ASAN_OPTIONS=detect_odr_violation=0\n");
+ InternalScopedString error_msg(256);
+ error_msg.append("odr-violation: global '%s' at %s",
+ MaybeDemangleGlobalName(g1->name), g1_loc.data());
+ ReportErrorSummary(error_msg.data());
+}
+
+// ----------------------- CheckForInvalidPointerPair ----------- {{{1
+static NOINLINE void
+ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, uptr a1, uptr a2) {
+ ScopedInErrorReport in_report;
+ const char *bug_type = "invalid-pointer-pair";
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n", a1, a2);
+ Printf("%s", d.EndWarning());
+ GET_STACK_TRACE_FATAL(pc, bp);
+ stack.Print();
+ DescribeAddress(a1, 1, bug_type);
+ DescribeAddress(a2, 1, bug_type);
+ ReportErrorSummary(bug_type, &stack);
+}
+
+static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
+ if (!flags()->detect_invalid_pointer_pairs) return;
+ uptr a1 = reinterpret_cast<uptr>(p1);
+ uptr a2 = reinterpret_cast<uptr>(p2);
+ AsanChunkView chunk1 = FindHeapChunkByAddress(a1);
+ AsanChunkView chunk2 = FindHeapChunkByAddress(a2);
+ bool valid1 = chunk1.IsValid();
+ bool valid2 = chunk2.IsValid();
+ if ((valid1 != valid2) || (valid1 && valid2 && !chunk1.Eq(chunk2))) {
+ GET_CALLER_PC_BP_SP; \
+ return ReportInvalidPointerPair(pc, bp, sp, a1, a2);
+ }
+}
// ----------------------- Mac-specific reports ----------------- {{{1
-void WarnMacFreeUnallocated(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
+ BufferedStackTrace *stack) {
// Just print a warning here.
Printf("free_common(%p) -- attempting to free unallocated memory.\n"
"AddressSanitizer is ignoring this error on Mac OS now.\n",
addr);
PrintZoneForPointer(addr, zone_ptr, zone_name);
- PrintStack(stack);
+ stack->Print();
DescribeHeapAddress(addr, 1);
}
-void ReportMacMzReallocUnknown(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
+ BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
"This is an unrecoverable problem, exiting now.\n",
addr);
PrintZoneForPointer(addr, zone_ptr, zone_name);
- PrintStack(stack);
+ stack->Print();
DescribeHeapAddress(addr, 1);
}
-void ReportMacCfReallocUnknown(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
+ BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
"This is an unrecoverable problem, exiting now.\n",
addr);
PrintZoneForPointer(addr, zone_ptr, zone_name);
- PrintStack(stack);
+ stack->Print();
DescribeHeapAddress(addr, 1);
}
-} // namespace __asan
-
-// --------------------------- Interface --------------------- {{{1
-using namespace __asan; // NOLINT
+void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
+ uptr access_size, u32 exp, bool fatal) {
+ ENABLE_FRAME_POINTER;
-void __asan_report_error(uptr pc, uptr bp, uptr sp,
- uptr addr, bool is_write, uptr access_size) {
- ScopedInErrorReport in_report;
+ // Optimization experiments.
+ // The experiments can be used to evaluate potential optimizations that remove
+ // instrumentation (assess false negatives). Instead of completely removing
+ // some instrumentation, compiler can emit special calls into runtime
+ // (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass
+ // mask of experiments (exp).
+ // The reaction to a non-zero value of exp is to be defined.
+ (void)exp;
// Determine the error type.
const char *bug_descr = "unknown-crash";
switch (*shadow_addr) {
case kAsanHeapLeftRedzoneMagic:
case kAsanHeapRightRedzoneMagic:
+ case kAsanArrayCookieMagic:
bug_descr = "heap-buffer-overflow";
break;
case kAsanHeapFreeMagic:
case kAsanGlobalRedzoneMagic:
bug_descr = "global-buffer-overflow";
break;
+ case kAsanIntraObjectRedzone:
+ bug_descr = "intra-object-overflow";
+ break;
+ case kAsanAllocaLeftMagic:
+ case kAsanAllocaRightMagic:
+ bug_descr = "dynamic-stack-buffer-overflow";
+ break;
}
}
+
+ ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size,
+ bug_descr };
+ ScopedInErrorReport in_report(&report, fatal);
+
Decorator d;
Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: %s on address "
- "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n",
+ "%p at pc %p bp %p sp %p\n",
bug_descr, (void*)addr, pc, bp, sp);
Printf("%s", d.EndWarning());
d.EndAccess());
GET_STACK_TRACE_FATAL(pc, bp);
- PrintStack(&stack);
+ stack.Print();
- DescribeAddress(addr, access_size);
+ DescribeAddress(addr, access_size, bug_descr);
ReportErrorSummary(bug_descr, &stack);
PrintShadowMemoryForAddress(addr);
}
+} // namespace __asan
+
+// --------------------------- Interface --------------------- {{{1
+using namespace __asan; // NOLINT
+
+void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
+ uptr access_size, u32 exp) {
+ ENABLE_FRAME_POINTER;
+ bool fatal = flags()->halt_on_error;
+ ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal);
+}
+
void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
error_report_callback = callback;
if (callback) {
error_message_buffer_size = 1 << 16;
error_message_buffer =
- (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__);
+ (char*)MmapOrDie(error_message_buffer_size, __func__);
error_message_buffer_pos = 0;
}
}
void __asan_describe_address(uptr addr) {
- DescribeAddress(addr, 1);
+ // Thread registry must be locked while we're describing an address.
+ asanThreadRegistry().Lock();
+ DescribeAddress(addr, 1, "");
+ asanThreadRegistry().Unlock();
+}
+
+int __asan_report_present() {
+ return report_happened ? 1 : 0;
+}
+
+uptr __asan_get_report_pc() {
+ return report_data.pc;
+}
+
+uptr __asan_get_report_bp() {
+ return report_data.bp;
+}
+
+uptr __asan_get_report_sp() {
+ return report_data.sp;
+}
+
+uptr __asan_get_report_address() {
+ return report_data.addr;
+}
+
+int __asan_get_report_access_type() {
+ return report_data.is_write ? 1 : 0;
+}
+
+uptr __asan_get_report_access_size() {
+ return report_data.access_size;
+}
+
+const char *__asan_get_report_description() {
+ return report_data.description;
+}
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_ptr_sub(void *a, void *b) {
+ CheckForInvalidPointerPair(a, b);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_ptr_cmp(void *a, void *b) {
+ CheckForInvalidPointerPair(a, b);
}
+} // extern "C"
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
// Provide default implementation of __asan_on_error that does nothing
namespace __asan {
+struct StackVarDescr {
+ uptr beg;
+ uptr size;
+ const char *name_pos;
+ uptr name_len;
+};
+
+struct AddressDescription {
+ char *name;
+ uptr name_size;
+ uptr region_address;
+ uptr region_size;
+ const char *region_kind;
+};
+
+// Returns the number of globals close to the provided address and copies
+// them to "globals" array.
+int GetGlobalsForAddress(uptr addr, __asan_global *globals, u32 *reg_sites,
+ int max_globals);
+bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
// The following functions prints address description depending
// on the memory type (shadow/heap/stack/global).
void DescribeHeapAddress(uptr addr, uptr access_size);
-bool DescribeAddressIfGlobal(uptr addr, uptr access_size);
-bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
- const __asan_global &g);
-bool DescribeAddressIfShadow(uptr addr);
+bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr,
+ bool print = true);
+bool ParseFrameDescription(const char *frame_descr,
+ InternalMmapVector<StackVarDescr> *vars);
bool DescribeAddressIfStack(uptr addr, uptr access_size);
-// Determines memory type on its own.
-void DescribeAddress(uptr addr, uptr access_size);
-
void DescribeThread(AsanThreadContext *context);
// Different kinds of error reports.
-void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr);
-void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack);
-void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack);
-void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
- AllocType alloc_type,
- AllocType dealloc_type);
-void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
- StackTrace *stack);
-void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr,
- StackTrace *stack);
-void NORETURN ReportStringFunctionMemoryRangesOverlap(
- const char *function, const char *offset1, uptr length1,
- const char *offset2, uptr length2, StackTrace *stack);
+void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
+ uptr access_size, u32 exp, bool fatal);
+void ReportStackOverflow(const SignalContext &sig);
+void ReportDeadlySignal(const char *description, const SignalContext &sig);
+void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
+ BufferedStackTrace *free_stack);
+void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
+void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
+void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
+ AllocType alloc_type,
+ AllocType dealloc_type);
+void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack);
+void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
+ BufferedStackTrace *stack);
+void ReportStringFunctionMemoryRangesOverlap(const char *function,
+ const char *offset1, uptr length1,
+ const char *offset2, uptr length2,
+ BufferedStackTrace *stack);
+void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
+ BufferedStackTrace *stack);
+void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
+ uptr old_mid, uptr new_mid,
+ BufferedStackTrace *stack);
+
+void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
+ const __asan_global *g2, u32 stack_id2);
// Mac-specific errors and warnings.
-void WarnMacFreeUnallocated(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
-void NORETURN ReportMacMzReallocUnknown(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
-void NORETURN ReportMacCfReallocUnknown(
- uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
+void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
+ BufferedStackTrace *stack);
+void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
+ const char *zone_name,
+ BufferedStackTrace *stack);
+void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr,
+ const char *zone_name,
+ BufferedStackTrace *stack);
} // namespace __asan
//
// Main file of the ASan run-time library.
//===----------------------------------------------------------------------===//
+
+#include "asan_activation.h"
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_interface_internal.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
+#include "asan_suppressions.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include "lsan/lsan_common.h"
+#include "ubsan/ubsan_init.h"
+#include "ubsan/ubsan_platform.h"
int __asan_option_detect_stack_use_after_return; // Global interface symbol.
+uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan.
namespace __asan {
UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg);
}
}
- if (flags()->coverage)
- __sanitizer_cov_dump();
- if (death_callback)
- death_callback();
- if (flags()->abort_on_error)
- Abort();
- internal__exit(flags()->exitcode);
}
static void AsanCheckFailed(const char *file, int line, const char *cond,
u64 v1, u64 v2) {
- Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n",
- file, line, cond, (uptr)v1, (uptr)v2);
+ Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file,
+ line, cond, (uptr)v1, (uptr)v2);
// FIXME: check for infinite recursion without a thread-local counter here.
- PRINT_CURRENT_STACK();
+ PRINT_CURRENT_STACK_CHECK();
Die();
}
-// -------------------------- Flags ------------------------- {{{1
-static const int kDefaultMallocContextSize = 30;
-
-Flags asan_flags_dont_use_directly; // use via flags().
-
-static const char *MaybeCallAsanDefaultOptions() {
- return (&__asan_default_options) ? __asan_default_options() : "";
-}
-
-static const char *MaybeUseAsanDefaultOptionsCompileDefiniton() {
-#ifdef ASAN_DEFAULT_OPTIONS
-// Stringize the macro value.
-# define ASAN_STRINGIZE(x) #x
-# define ASAN_STRINGIZE_OPTIONS(options) ASAN_STRINGIZE(options)
- return ASAN_STRINGIZE_OPTIONS(ASAN_DEFAULT_OPTIONS);
-#else
- return "";
-#endif
-}
-
-static void ParseFlagsFromString(Flags *f, const char *str) {
- CommonFlags *cf = common_flags();
- ParseCommonFlagsFromString(cf, str);
- CHECK((uptr)cf->malloc_context_size <= kStackTraceMax);
-
- ParseFlag(str, &f->quarantine_size, "quarantine_size");
- ParseFlag(str, &f->redzone, "redzone");
- CHECK_GE(f->redzone, 16);
- CHECK(IsPowerOfTwo(f->redzone));
-
- ParseFlag(str, &f->debug, "debug");
- ParseFlag(str, &f->report_globals, "report_globals");
- ParseFlag(str, &f->check_initialization_order, "check_initialization_order");
-
- ParseFlag(str, &f->replace_str, "replace_str");
- ParseFlag(str, &f->replace_intrin, "replace_intrin");
- ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free");
- ParseFlag(str, &f->detect_stack_use_after_return,
- "detect_stack_use_after_return");
- ParseFlag(str, &f->uar_stack_size_log, "uar_stack_size_log");
- ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size");
- ParseFlag(str, &f->malloc_fill_byte, "malloc_fill_byte");
- ParseFlag(str, &f->exitcode, "exitcode");
- ParseFlag(str, &f->allow_user_poisoning, "allow_user_poisoning");
- ParseFlag(str, &f->sleep_before_dying, "sleep_before_dying");
- ParseFlag(str, &f->handle_segv, "handle_segv");
- ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler");
- ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack");
- ParseFlag(str, &f->check_malloc_usable_size, "check_malloc_usable_size");
- ParseFlag(str, &f->unmap_shadow_on_exit, "unmap_shadow_on_exit");
- ParseFlag(str, &f->abort_on_error, "abort_on_error");
- ParseFlag(str, &f->print_stats, "print_stats");
- ParseFlag(str, &f->print_legend, "print_legend");
- ParseFlag(str, &f->atexit, "atexit");
- ParseFlag(str, &f->coverage, "coverage");
- ParseFlag(str, &f->disable_core, "disable_core");
- ParseFlag(str, &f->allow_reexec, "allow_reexec");
- ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history");
- ParseFlag(str, &f->poison_heap, "poison_heap");
- ParseFlag(str, &f->poison_partial, "poison_partial");
- ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch");
- ParseFlag(str, &f->strict_memcmp, "strict_memcmp");
- ParseFlag(str, &f->strict_init_order, "strict_init_order");
-}
-
-void InitializeFlags(Flags *f, const char *env) {
- CommonFlags *cf = common_flags();
- SetCommonFlagsDefaults(cf);
- cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH");
- cf->malloc_context_size = kDefaultMallocContextSize;
-
- internal_memset(f, 0, sizeof(*f));
- f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28;
- f->redzone = 16;
- f->debug = false;
- f->report_globals = 1;
- f->check_initialization_order = false;
- f->replace_str = true;
- f->replace_intrin = true;
- f->mac_ignore_invalid_free = false;
- f->detect_stack_use_after_return = false; // Also needs the compiler flag.
- f->uar_stack_size_log = 0;
- f->max_malloc_fill_size = 0x1000; // By default, fill only the first 4K.
- f->malloc_fill_byte = 0xbe;
- f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE;
- f->allow_user_poisoning = true;
- f->sleep_before_dying = 0;
- f->handle_segv = ASAN_NEEDS_SEGV;
- f->allow_user_segv_handler = false;
- f->use_sigaltstack = false;
- f->check_malloc_usable_size = true;
- f->unmap_shadow_on_exit = false;
- f->abort_on_error = false;
- f->print_stats = false;
- f->print_legend = true;
- f->atexit = false;
- f->coverage = false;
- f->disable_core = (SANITIZER_WORDSIZE == 64);
- f->allow_reexec = true;
- f->print_full_thread_history = true;
- f->poison_heap = true;
- f->poison_partial = true;
- // Turn off alloc/dealloc mismatch checker on Mac and Windows for now.
- // TODO(glider,timurrrr): Fix known issues and enable this back.
- f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0);
- f->strict_memcmp = true;
- f->strict_init_order = false;
-
- // Override from compile definition.
- ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton());
-
- // Override from user-specified string.
- ParseFlagsFromString(f, MaybeCallAsanDefaultOptions());
- if (cf->verbosity) {
- Report("Using the defaults from __asan_default_options: %s\n",
- MaybeCallAsanDefaultOptions());
- }
-
- // Override from command line.
- ParseFlagsFromString(f, env);
-
-#if !CAN_SANITIZE_LEAKS
- if (cf->detect_leaks) {
- Report("%s: detect_leaks is not supported on this platform.\n",
- SanitizerToolName);
- cf->detect_leaks = false;
- }
-#endif
-
- // Make "strict_init_order" imply "check_initialization_order".
- // TODO(samsonov): Use a single runtime flag for an init-order checker.
- if (f->strict_init_order) {
- f->check_initialization_order = true;
- }
-}
-
// -------------------------- Globals --------------------- {{{1
int asan_inited;
bool asan_init_is_running;
-void (*death_callback)(void);
#if !ASAN_FIXED_MAPPING
uptr kHighMemEnd, kMidMemBeg, kMidMemEnd;
// ---------------------- mmap -------------------- {{{1
// Reserve memory range [beg, end].
-static void ReserveShadowMemoryRange(uptr beg, uptr end) {
+// We need to use inclusive range because end+1 may not be representable.
+void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
CHECK_EQ((beg % GetPageSizeCached()), 0);
CHECK_EQ(((end + 1) % GetPageSizeCached()), 0);
uptr size = end - beg + 1;
- void *res = MmapFixedNoReserve(beg, size);
+ DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb.
+ void *res = MmapFixedNoReserve(beg, size, name);
if (res != (void*)beg) {
Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
"Perhaps you're using ulimit -v\n", size);
Abort();
}
+ if (common_flags()->no_huge_pages_for_shadow)
+ NoHugePagesInRegion(beg, size);
+ if (common_flags()->use_madv_dontdump)
+ DontDumpShadowMemory(beg, size);
}
// --------------- LowLevelAllocateCallbac ---------- {{{1
// -------------------------- Run-time entry ------------------- {{{1
// exported functions
#define ASAN_REPORT_ERROR(type, is_write, size) \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE \
-void __asan_report_ ## type ## size(uptr addr); \
-void __asan_report_ ## type ## size(uptr addr) { \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_ ## type ## size(uptr addr) { \
GET_CALLER_PC_BP_SP; \
- __asan_report_error(pc, bp, sp, addr, is_write, size); \
-}
+ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \
+} \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \
+} \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_ ## type ## size ## _noabort(uptr addr) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \
+} \
ASAN_REPORT_ERROR(load, false, 1)
ASAN_REPORT_ERROR(load, false, 2)
ASAN_REPORT_ERROR(store, true, 8)
ASAN_REPORT_ERROR(store, true, 16)
-#define ASAN_REPORT_ERROR_N(type, is_write) \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE \
-void __asan_report_ ## type ## _n(uptr addr, uptr size); \
-void __asan_report_ ## type ## _n(uptr addr, uptr size) { \
- GET_CALLER_PC_BP_SP; \
- __asan_report_error(pc, bp, sp, addr, is_write, size); \
-}
+#define ASAN_REPORT_ERROR_N(type, is_write) \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_ ## type ## _n(uptr addr, uptr size) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \
+} \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \
+} \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \
+} \
ASAN_REPORT_ERROR_N(load, false)
ASAN_REPORT_ERROR_N(store, true)
+#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \
+ uptr sp = MEM_TO_SHADOW(addr); \
+ uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \
+ : *reinterpret_cast<u16 *>(sp); \
+ if (UNLIKELY(s)) { \
+ if (UNLIKELY(size >= SHADOW_GRANULARITY || \
+ ((s8)((addr & (SHADOW_GRANULARITY - 1)) + size - 1)) >= \
+ (s8)s)) { \
+ if (__asan_test_only_reported_buggy_pointer) { \
+ *__asan_test_only_reported_buggy_pointer = addr; \
+ } else { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \
+ fatal); \
+ } \
+ } \
+ }
+
+#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \
+ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+ void __asan_##type##size(uptr addr) { \
+ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, true) \
+ } \
+ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+ void __asan_exp_##type##size(uptr addr, u32 exp) { \
+ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp, true) \
+ } \
+ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+ void __asan_##type##size ## _noabort(uptr addr) { \
+ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, false) \
+ } \
+
+ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1)
+ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2)
+ASAN_MEMORY_ACCESS_CALLBACK(load, false, 4)
+ASAN_MEMORY_ACCESS_CALLBACK(load, false, 8)
+ASAN_MEMORY_ACCESS_CALLBACK(load, false, 16)
+ASAN_MEMORY_ACCESS_CALLBACK(store, true, 1)
+ASAN_MEMORY_ACCESS_CALLBACK(store, true, 2)
+ASAN_MEMORY_ACCESS_CALLBACK(store, true, 4)
+ASAN_MEMORY_ACCESS_CALLBACK(store, true, 8)
+ASAN_MEMORY_ACCESS_CALLBACK(store, true, 16)
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_loadN(uptr addr, uptr size) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, false, size, 0, true);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_exp_loadN(uptr addr, uptr size, u32 exp) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, false, size, exp, true);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_loadN_noabort(uptr addr, uptr size) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, false, size, 0, false);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_storeN(uptr addr, uptr size) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, true, size, 0, true);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_exp_storeN(uptr addr, uptr size, u32 exp) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, true, size, exp, true);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_storeN_noabort(uptr addr, uptr size) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, true, size, 0, false);
+ }
+}
+
// Force the linker to keep the symbols for various ASan interface functions.
// We want to keep those in the executable in order to let the instrumented
// dynamic libraries access the symbol even if it is not used by the executable
case 3: __asan_report_load4(0); break;
case 4: __asan_report_load8(0); break;
case 5: __asan_report_load16(0); break;
- case 6: __asan_report_store1(0); break;
- case 7: __asan_report_store2(0); break;
- case 8: __asan_report_store4(0); break;
- case 9: __asan_report_store8(0); break;
- case 10: __asan_report_store16(0); break;
- case 12: __asan_register_globals(0, 0); break;
- case 13: __asan_unregister_globals(0, 0); break;
- case 14: __asan_set_death_callback(0); break;
- case 15: __asan_set_error_report_callback(0); break;
- case 16: __asan_handle_no_return(); break;
- case 17: __asan_address_is_poisoned(0); break;
- case 18: __asan_get_allocated_size(0); break;
- case 19: __asan_get_current_allocated_bytes(); break;
- case 20: __asan_get_estimated_allocated_size(0); break;
- case 21: __asan_get_free_bytes(); break;
- case 22: __asan_get_heap_size(); break;
- case 23: __asan_get_ownership(0); break;
- case 24: __asan_get_unmapped_bytes(); break;
- case 25: __asan_poison_memory_region(0, 0); break;
- case 26: __asan_unpoison_memory_region(0, 0); break;
- case 27: __asan_set_error_exit_code(0); break;
- case 30: __asan_before_dynamic_init(0); break;
- case 31: __asan_after_dynamic_init(); break;
- case 32: __asan_poison_stack_memory(0, 0); break;
- case 33: __asan_unpoison_stack_memory(0, 0); break;
- case 34: __asan_region_is_poisoned(0, 0); break;
- case 35: __asan_describe_address(0); break;
+ case 6: __asan_report_load_n(0, 0); break;
+ case 7: __asan_report_store1(0); break;
+ case 8: __asan_report_store2(0); break;
+ case 9: __asan_report_store4(0); break;
+ case 10: __asan_report_store8(0); break;
+ case 11: __asan_report_store16(0); break;
+ case 12: __asan_report_store_n(0, 0); break;
+ case 13: __asan_report_exp_load1(0, 0); break;
+ case 14: __asan_report_exp_load2(0, 0); break;
+ case 15: __asan_report_exp_load4(0, 0); break;
+ case 16: __asan_report_exp_load8(0, 0); break;
+ case 17: __asan_report_exp_load16(0, 0); break;
+ case 18: __asan_report_exp_load_n(0, 0, 0); break;
+ case 19: __asan_report_exp_store1(0, 0); break;
+ case 20: __asan_report_exp_store2(0, 0); break;
+ case 21: __asan_report_exp_store4(0, 0); break;
+ case 22: __asan_report_exp_store8(0, 0); break;
+ case 23: __asan_report_exp_store16(0, 0); break;
+ case 24: __asan_report_exp_store_n(0, 0, 0); break;
+ case 25: __asan_register_globals(nullptr, 0); break;
+ case 26: __asan_unregister_globals(nullptr, 0); break;
+ case 27: __asan_set_death_callback(nullptr); break;
+ case 28: __asan_set_error_report_callback(nullptr); break;
+ case 29: __asan_handle_no_return(); break;
+ case 30: __asan_address_is_poisoned(nullptr); break;
+ case 31: __asan_poison_memory_region(nullptr, 0); break;
+ case 32: __asan_unpoison_memory_region(nullptr, 0); break;
+ case 34: __asan_before_dynamic_init(nullptr); break;
+ case 35: __asan_after_dynamic_init(); break;
+ case 36: __asan_poison_stack_memory(0, 0); break;
+ case 37: __asan_unpoison_stack_memory(0, 0); break;
+ case 38: __asan_region_is_poisoned(0, 0); break;
+ case 39: __asan_describe_address(0); break;
}
}
CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0);
}
-static void ProtectGap(uptr a, uptr size) {
- CHECK_EQ(a, (uptr)Mprotect(a, size));
+static void ProtectGap(uptr addr, uptr size) {
+ if (!flags()->protect_shadow_gap)
+ return;
+ void *res = MmapNoAccess(addr, size, "shadow gap");
+ if (addr == (uptr)res)
+ return;
+ // A few pages at the start of the address space can not be protected.
+ // But we really want to protect as much as possible, to prevent this memory
+ // being returned as a result of a non-FIXED mmap().
+ if (addr == kZeroBaseShadowStart) {
+ uptr step = GetPageSizeCached();
+ while (size > step && addr < kZeroBaseMaxShadowStart) {
+ addr += step;
+ size -= step;
+ void *res = MmapNoAccess(addr, size, "shadow gap");
+ if (addr == (uptr)res)
+ return;
+ }
+ }
+
+ Report("ERROR: Failed to protect the shadow gap. "
+ "ASan cannot proceed correctly. ABORTING.\n");
+ DumpProcessMap();
+ Die();
}
static void PrintAddressSpaceLayout() {
(void*)MEM_TO_SHADOW(kMidShadowEnd));
}
Printf("\n");
- Printf("red_zone=%zu\n", (uptr)flags()->redzone);
- Printf("quarantine_size=%zuM\n", (uptr)flags()->quarantine_size >> 20);
+ Printf("redzone=%zu\n", (uptr)flags()->redzone);
+ Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone);
+ Printf("quarantine_size_mb=%zuM\n", (uptr)flags()->quarantine_size_mb);
Printf("malloc_context_size=%zu\n",
(uptr)common_flags()->malloc_context_size);
- Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE);
- Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY);
- Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET);
+ Printf("SHADOW_SCALE: %d\n", (int)SHADOW_SCALE);
+ Printf("SHADOW_GRANULARITY: %d\n", (int)SHADOW_GRANULARITY);
+ Printf("SHADOW_OFFSET: 0x%zx\n", (uptr)SHADOW_OFFSET);
CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7);
if (kMidMemBeg)
CHECK(kMidShadowBeg > kLowShadowEnd &&
kHighShadowBeg > kMidMemEnd);
}
-} // namespace __asan
+static void AsanInitInternal() {
+ if (LIKELY(asan_inited)) return;
+ SanitizerToolName = "AddressSanitizer";
+ CHECK(!asan_init_is_running && "ASan init calls itself!");
+ asan_init_is_running = true;
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan; // NOLINT
+ CacheBinaryName();
-#if !SANITIZER_SUPPORTS_WEAK_HOOKS
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-const char* __asan_default_options() { return ""; }
-} // extern "C"
-#endif
+ // Initialize flags. This must be done early, because most of the
+ // initialization steps look at flags().
+ InitializeFlags();
-int NOINLINE __asan_set_error_exit_code(int exit_code) {
- int old = flags()->exitcode;
- flags()->exitcode = exit_code;
- return old;
-}
+ AsanCheckIncompatibleRT();
+ AsanCheckDynamicRTPrereqs();
-void NOINLINE __asan_handle_no_return() {
- int local_stack;
- AsanThread *curr_thread = GetCurrentThread();
- CHECK(curr_thread);
- uptr PageSize = GetPageSizeCached();
- uptr top = curr_thread->stack_top();
- uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1);
- static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M
- if (top - bottom > kMaxExpectedCleanupSize) {
- static bool reported_warning = false;
- if (reported_warning)
- return;
- reported_warning = true;
- Report("WARNING: ASan is ignoring requested __asan_handle_no_return: "
- "stack top: %p; bottom %p; size: %p (%zd)\n"
- "False positive error reports may follow\n"
- "For details see "
- "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n",
- top, bottom, top - bottom, top - bottom);
- return;
- }
- PoisonShadow(bottom, top - bottom, 0);
- if (curr_thread->has_fake_stack())
- curr_thread->fake_stack()->HandleNoReturn();
-}
+ SetCanPoisonMemory(flags()->poison_heap);
+ SetMallocContextSize(common_flags()->malloc_context_size);
-void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
- death_callback = callback;
-}
-
-void __asan_init() {
- if (asan_inited) return;
- SanitizerToolName = "AddressSanitizer";
- CHECK(!asan_init_is_running && "ASan init calls itself!");
- asan_init_is_running = true;
InitializeHighMemEnd();
// Make sure we are not statically linked.
AsanDoesNotSupportStaticLinkage();
// Install tool-specific callbacks in sanitizer_common.
- SetDieCallback(AsanDie);
+ AddDieCallback(AsanDie);
SetCheckFailedCallback(AsanCheckFailed);
SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
- // Initialize flags. This must be done early, because most of the
- // initialization steps look at flags().
- const char *options = GetEnv("ASAN_OPTIONS");
- InitializeFlags(flags(), options);
__sanitizer_set_report_path(common_flags()->log_path);
+
+ // Enable UAR detection, if required.
__asan_option_detect_stack_use_after_return =
flags()->detect_stack_use_after_return;
- if (common_flags()->verbosity && options) {
- Report("Parsed ASAN_OPTIONS: %s\n", options);
- }
-
// Re-exec ourselves if we need to set additional env or command line args.
MaybeReexec();
InitializeAsanInterceptors();
+ // Enable system log ("adb logcat") on Android.
+ // Doing this before interceptors are initialized crashes in:
+ // AsanInitInternal -> android_log_write -> __interceptor_strcmp
+ AndroidLogInit();
+
ReplaceSystemMalloc();
- ReplaceOperatorsNewAndDelete();
uptr shadow_start = kLowShadowBeg;
if (kLowShadowBeg)
bool full_shadow_is_available =
MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
-#if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING
+#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \
+ !ASAN_FIXED_MAPPING
if (!full_shadow_is_available) {
kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
}
#endif
- if (common_flags()->verbosity)
- PrintAddressSpaceLayout();
+ if (Verbosity()) PrintAddressSpaceLayout();
- if (flags()->disable_core) {
- DisableCoreDumper();
- }
+ DisableCoreDumperIfNecessary();
if (full_shadow_is_available) {
// mmap the low shadow plus at least one page at the left.
if (kLowShadowBeg)
- ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
+ ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
// mmap the high shadow.
- ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
+ ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
// protect the gap.
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
+ CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
} else if (kMidMemBeg &&
MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) &&
MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
CHECK(kLowShadowBeg != kLowShadowEnd);
// mmap the low shadow plus at least one page at the left.
- ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
+ ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
// mmap the mid shadow.
- ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd);
+ ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow");
// mmap the high shadow.
- ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
+ ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
// protect the gaps.
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
} else {
Report("Shadow memory range interleaves with an existing memory mapping. "
"ASan cannot proceed correctly. ABORTING.\n");
+ Report("ASan shadow was supposed to be located in the [%p-%p] range.\n",
+ shadow_start, kHighShadowEnd);
DumpProcessMap();
Die();
}
AsanTSDInit(PlatformTSDDtor);
- InstallSignalHandlers();
+ InstallDeadlySignalHandlers(AsanOnDeadlySignal);
- // Allocator should be initialized before starting external symbolizer, as
- // fork() on Mac locks the allocator.
- InitializeAllocator();
+ AllocatorOptions allocator_options;
+ allocator_options.SetFrom(flags(), common_flags());
+ InitializeAllocator(allocator_options);
- // Start symbolizer process if necessary.
- if (common_flags()->symbolize) {
- Symbolizer::Init(common_flags()->external_symbolizer_path);
- } else {
- Symbolizer::Disable();
- }
+ MaybeStartBackgroudThread();
+ SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback);
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
// should be set to 1 prior to initializing the threads.
if (flags()->atexit)
Atexit(asan_atexit);
- if (flags()->coverage)
- Atexit(__sanitizer_cov_dump);
+ InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
+
+ // Now that ASan runtime is (mostly) initialized, deactivate it if
+ // necessary, so that it can be re-activated when requested.
+ if (flags()->start_deactivated)
+ AsanDeactivate();
// interceptors
InitTlsSize();
// Create main thread.
- AsanThread *main_thread = AsanThread::Create(0, 0);
- CreateThreadContextArgs create_main_args = { main_thread, 0 };
- u32 main_tid = asanThreadRegistry().CreateThread(
- 0, true, 0, &create_main_args);
- CHECK_EQ(0, main_tid);
+ AsanThread *main_thread = AsanThread::Create(
+ /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ 0,
+ /* stack */ nullptr, /* detached */ true);
+ CHECK_EQ(0, main_thread->tid());
SetCurrentThread(main_thread);
- main_thread->ThreadStart(internal_getpid());
+ main_thread->ThreadStart(internal_getpid(),
+ /* signal_thread_is_registered */ nullptr);
force_interface_symbols(); // no-op.
+ SanitizerInitializeUnwinder();
#if CAN_SANITIZE_LEAKS
__lsan::InitCommonLsan();
}
#endif // CAN_SANITIZE_LEAKS
- if (common_flags()->verbosity) {
- Report("AddressSanitizer Init done\n");
+#if CAN_SANITIZE_UB
+ __ubsan::InitAsPlugin();
+#endif
+
+ InitializeSuppressions();
+
+ VReport(1, "AddressSanitizer Init done\n");
+}
+
+// Initialize as requested from some part of ASan runtime library (interceptors,
+// allocator, etc).
+void AsanInitFromRtl() {
+ AsanInitInternal();
+}
+
+#if ASAN_DYNAMIC
+// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable
+// (and thus normal initializers from .preinit_array or modules haven't run).
+
+class AsanInitializer {
+public: // NOLINT
+ AsanInitializer() {
+ AsanInitFromRtl();
+ }
+};
+
+static AsanInitializer asan_initializer;
+#endif // ASAN_DYNAMIC
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+void NOINLINE __asan_handle_no_return() {
+ int local_stack;
+ AsanThread *curr_thread = GetCurrentThread();
+ uptr PageSize = GetPageSizeCached();
+ uptr top, bottom;
+ if (curr_thread) {
+ top = curr_thread->stack_top();
+ bottom = ((uptr)&local_stack - PageSize) & ~(PageSize - 1);
+ } else {
+ // If we haven't seen this thread, try asking the OS for stack bounds.
+ uptr tls_addr, tls_size, stack_size;
+ GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr,
+ &tls_size);
+ top = bottom + stack_size;
}
+ static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M
+ if (top - bottom > kMaxExpectedCleanupSize) {
+ static bool reported_warning = false;
+ if (reported_warning)
+ return;
+ reported_warning = true;
+ Report("WARNING: ASan is ignoring requested __asan_handle_no_return: "
+ "stack top: %p; bottom %p; size: %p (%zd)\n"
+ "False positive error reports may follow\n"
+ "For details see "
+ "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n",
+ top, bottom, top - bottom, top - bottom);
+ return;
+ }
+ PoisonShadow(bottom, top - bottom, 0);
+ if (curr_thread && curr_thread->has_fake_stack())
+ curr_thread->fake_stack()->HandleNoReturn();
+}
+
+void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
+ SetUserDieCallback(callback);
+}
+
+// Initialize as requested from instrumented application code.
+// We use this call as a trigger to wake up ASan from deactivated state.
+void __asan_init() {
+ AsanActivate();
+ AsanInitInternal();
+}
+
+void __asan_version_mismatch_check() {
+ // Do nothing.
}
// Code for ASan stack trace.
//===----------------------------------------------------------------------===//
#include "asan_internal.h"
-#include "asan_flags.h"
#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_atomic.h"
namespace __asan {
-static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer,
- int out_size) {
- return (&__asan_symbolize) ? __asan_symbolize(pc, out_buffer, out_size)
- : false;
-}
+static atomic_uint32_t malloc_context_size;
-void PrintStack(const uptr *trace, uptr size) {
- StackTrace::PrintStack(trace, size, MaybeCallAsanSymbolize);
+void SetMallocContextSize(u32 size) {
+ atomic_store(&malloc_context_size, size, memory_order_release);
}
-void PrintStack(StackTrace *stack) {
- PrintStack(stack->trace, stack->size);
+u32 GetMallocContextSize() {
+ return atomic_load(&malloc_context_size, memory_order_acquire);
}
} // namespace __asan
// ------------------ Interface -------------- {{{1
-// Provide default implementation of __asan_symbolize that does nothing
-// and may be overriden by user if he wants to use his own symbolization.
-// ASan on Windows has its own implementation of this.
-#if !SANITIZER_WINDOWS && !SANITIZER_SUPPORTS_WEAK_HOOKS
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE
-bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) {
- return false;
-}
-#endif
-
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_print_stack_trace() {
//
// ASan-private header for asan_stack.cc.
//===----------------------------------------------------------------------===//
+
#ifndef ASAN_STACK_H
#define ASAN_STACK_H
namespace __asan {
-void PrintStack(StackTrace *stack);
-void PrintStack(const uptr *trace, uptr size);
+static const u32 kDefaultMallocContextSize = 30;
-} // namespace __asan
+void SetMallocContextSize(u32 size);
+u32 GetMallocContextSize();
// Get the stack trace with the given pc and bp.
// The pc will be in the position 0 of the resulting stack trace.
// The bp may refer to the current frame or to the caller's frame.
+ALWAYS_INLINE
+void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth,
+ uptr pc, uptr bp, void *context,
+ bool fast) {
#if SANITIZER_WINDOWS
-#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
- StackTrace stack; \
- stack.Unwind(max_s, pc, bp, 0, 0, fast)
+ stack->Unwind(max_depth, pc, bp, context, 0, 0, fast);
#else
-#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
- StackTrace stack; \
- { \
- AsanThread *t; \
- stack.size = 0; \
- if (asan_inited) { \
- if ((t = GetCurrentThread()) && !t->isUnwinding()) { \
- uptr stack_top = t->stack_top(); \
- uptr stack_bottom = t->stack_bottom(); \
- ScopedUnwinding unwind_scope(t); \
- stack.Unwind(max_s, pc, bp, stack_top, stack_bottom, fast); \
- } else if (t == 0 && !fast) { \
- /* If GetCurrentThread() has failed, try to do slow unwind anyways. */ \
- stack.Unwind(max_s, pc, bp, 0, 0, false); \
- } \
- } \
+ AsanThread *t;
+ stack->size = 0;
+ if (LIKELY(asan_inited)) {
+ if ((t = GetCurrentThread()) && !t->isUnwinding()) {
+ // On FreeBSD the slow unwinding that leverages _Unwind_Backtrace()
+ // yields the call stack of the signal's handler and not of the code
+ // that raised the signal (as it does on Linux).
+ if (SANITIZER_FREEBSD && t->isInDeadlySignal()) fast = true;
+ uptr stack_top = t->stack_top();
+ uptr stack_bottom = t->stack_bottom();
+ ScopedUnwinding unwind_scope(t);
+ stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom, fast);
+ } else if (!t && !fast) {
+ /* If GetCurrentThread() has failed, try to do slow unwind anyways. */
+ stack->Unwind(max_depth, pc, bp, context, 0, 0, false);
+ }
}
-#endif // SANITIZER_WINDOWS
+#endif // SANITIZER_WINDOWS
+}
+
+} // namespace __asan
// NOTE: A Rule of thumb is to retrieve stack trace in the interceptors
// as early as possible (in functions exposed to the user), as we generally
// don't want stack trace to contain functions from ASan internals.
-#define GET_STACK_TRACE(max_size, fast) \
- GET_STACK_TRACE_WITH_PC_AND_BP(max_size, \
- StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), fast)
+#define GET_STACK_TRACE(max_size, fast) \
+ BufferedStackTrace stack; \
+ if (max_size <= 2) { \
+ stack.size = max_size; \
+ if (max_size > 0) { \
+ stack.top_frame_bp = GET_CURRENT_FRAME(); \
+ stack.trace_buffer[0] = StackTrace::GetCurrentPc(); \
+ if (max_size > 1) \
+ stack.trace_buffer[1] = GET_CALLER_PC(); \
+ } \
+ } else { \
+ GetStackTraceWithPcBpAndContext(&stack, max_size, \
+ StackTrace::GetCurrentPc(), \
+ GET_CURRENT_FRAME(), 0, fast); \
+ }
-#define GET_STACK_TRACE_FATAL(pc, bp) \
- GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp, \
- common_flags()->fast_unwind_on_fatal)
+#define GET_STACK_TRACE_FATAL(pc, bp) \
+ BufferedStackTrace stack; \
+ GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, 0, \
+ common_flags()->fast_unwind_on_fatal)
+
+#define GET_STACK_TRACE_SIGNAL(sig) \
+ BufferedStackTrace stack; \
+ GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, \
+ (sig).pc, (sig).bp, (sig).context, \
+ common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_FATAL_HERE \
GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
+#define GET_STACK_TRACE_CHECK_HERE \
+ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_check)
+
#define GET_STACK_TRACE_THREAD \
GET_STACK_TRACE(kStackTraceMax, true)
-#define GET_STACK_TRACE_MALLOC \
- GET_STACK_TRACE(common_flags()->malloc_context_size, \
- common_flags()->fast_unwind_on_malloc)
+#define GET_STACK_TRACE_MALLOC \
+ GET_STACK_TRACE(GetMallocContextSize(), common_flags()->fast_unwind_on_malloc)
#define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC
#define PRINT_CURRENT_STACK() \
{ \
GET_STACK_TRACE_FATAL_HERE; \
- PrintStack(&stack); \
+ stack.Print(); \
+ }
+
+#define PRINT_CURRENT_STACK_CHECK() \
+ { \
+ GET_STACK_TRACE_CHECK_HERE; \
+ stack.Print(); \
}
-#endif // ASAN_STACK_H
+#endif // ASAN_STACK_H
#include "asan_internal.h"
#include "asan_stats.h"
#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
(mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
mmaps, munmaps);
- PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size);
PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
- PrintMallocStatsArray(" frees by size class: ", freed_by_size);
- PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size);
- Printf("Stats: malloc large: %zu small slow: %zu\n",
- malloc_large, malloc_small_slow);
+ Printf("Stats: malloc large: %zu\n", malloc_large);
}
void AsanStats::MergeFrom(const AsanStats *stats) {
BlockingMutexLock lock(&print_lock);
stats.Print();
StackDepotStats *stack_depot_stats = StackDepotGetStats();
- Printf("Stats: StackDepot: %zd ids; %zdM mapped\n",
- stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20);
+ Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
+ stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20);
PrintInternalAllocatorStats();
}
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
-uptr __asan_get_current_allocated_bytes() {
+uptr __sanitizer_get_current_allocated_bytes() {
AsanStats stats;
GetAccumulatedStats(&stats);
uptr malloced = stats.malloced;
return (malloced > freed) ? malloced - freed : 1;
}
-uptr __asan_get_heap_size() {
+uptr __sanitizer_get_heap_size() {
AsanStats stats;
GetAccumulatedStats(&stats);
return stats.mmaped - stats.munmaped;
}
-uptr __asan_get_free_bytes() {
+uptr __sanitizer_get_free_bytes() {
AsanStats stats;
GetAccumulatedStats(&stats);
uptr total_free = stats.mmaped
- stats.munmaped
- + stats.really_freed
- + stats.really_freed_redzones;
+ + stats.really_freed;
uptr total_used = stats.malloced
+ stats.malloced_redzones;
// Return sane value if total_free < total_used due to racy
return (total_free > total_used) ? total_free - total_used : 1;
}
-uptr __asan_get_unmapped_bytes() {
+uptr __sanitizer_get_unmapped_bytes() {
return 0;
}
uptr freed;
uptr real_frees;
uptr really_freed;
- uptr really_freed_redzones;
uptr reallocs;
uptr realloced;
uptr mmaps;
uptr mmaped;
uptr munmaps;
uptr munmaped;
- uptr mmaped_by_size[kNumberOfSizeClasses];
- uptr malloced_by_size[kNumberOfSizeClasses];
- uptr freed_by_size[kNumberOfSizeClasses];
- uptr really_freed_by_size[kNumberOfSizeClasses];
-
uptr malloc_large;
- uptr malloc_small_slow;
+ uptr malloced_by_size[kNumberOfSizeClasses];
// Ctor for global AsanStats (accumulated stats for dead threads).
explicit AsanStats(LinkerInitialized) { }
--- /dev/null
+//===-- asan_suppressions.cc ----------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Issue suppression and suppression-related functions.
+//===----------------------------------------------------------------------===//
+
+#include "asan_suppressions.h"
+
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+namespace __asan {
+
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char kInterceptorName[] = "interceptor_name";
+static const char kInterceptorViaFunction[] = "interceptor_via_fun";
+static const char kInterceptorViaLibrary[] = "interceptor_via_lib";
+static const char kODRViolation[] = "odr_violation";
+static const char *kSuppressionTypes[] = {
+ kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary,
+ kODRViolation};
+
+extern "C" {
+#if SANITIZER_SUPPORTS_WEAK_HOOKS
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char *__asan_default_suppressions();
+#else
+// No week hooks, provide empty implementation.
+const char *__asan_default_suppressions() { return ""; }
+#endif // SANITIZER_SUPPORTS_WEAK_HOOKS
+} // extern "C"
+
+void InitializeSuppressions() {
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
+ if (&__asan_default_suppressions)
+ suppression_ctx->Parse(__asan_default_suppressions());
+}
+
+bool IsInterceptorSuppressed(const char *interceptor_name) {
+ CHECK(suppression_ctx);
+ Suppression *s;
+ // Match "interceptor_name" suppressions.
+ return suppression_ctx->Match(interceptor_name, kInterceptorName, &s);
+}
+
+bool HaveStackTraceBasedSuppressions() {
+ CHECK(suppression_ctx);
+ return suppression_ctx->HasSuppressionType(kInterceptorViaFunction) ||
+ suppression_ctx->HasSuppressionType(kInterceptorViaLibrary);
+}
+
+bool IsODRViolationSuppressed(const char *global_var_name) {
+ CHECK(suppression_ctx);
+ Suppression *s;
+ // Match "odr_violation" suppressions.
+ return suppression_ctx->Match(global_var_name, kODRViolation, &s);
+}
+
+bool IsStackTraceSuppressed(const StackTrace *stack) {
+ if (!HaveStackTraceBasedSuppressions())
+ return false;
+
+ CHECK(suppression_ctx);
+ Symbolizer *symbolizer = Symbolizer::GetOrInit();
+ Suppression *s;
+ for (uptr i = 0; i < stack->size && stack->trace[i]; i++) {
+ uptr addr = stack->trace[i];
+
+ if (suppression_ctx->HasSuppressionType(kInterceptorViaLibrary)) {
+ // Match "interceptor_via_lib" suppressions.
+ if (const char *module_name = symbolizer->GetModuleNameForPc(addr))
+ if (suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s))
+ return true;
+ }
+
+ if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) {
+ SymbolizedStack *frames = symbolizer->SymbolizePC(addr);
+ for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ const char *function_name = cur->info.function;
+ if (!function_name) {
+ continue;
+ }
+ // Match "interceptor_via_fun" suppressions.
+ if (suppression_ctx->Match(function_name, kInterceptorViaFunction,
+ &s)) {
+ frames->ClearAll();
+ return true;
+ }
+ }
+ frames->ClearAll();
+ }
+ }
+ return false;
+}
+
+} // namespace __asan
--- /dev/null
+//===-- asan_suppressions.h -------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for asan_suppressions.cc.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_SUPPRESSIONS_H
+#define ASAN_SUPPRESSIONS_H
+
+#include "asan_internal.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+
+namespace __asan {
+
+void InitializeSuppressions();
+bool IsInterceptorSuppressed(const char *interceptor_name);
+bool HaveStackTraceBasedSuppressions();
+bool IsStackTraceSuppressed(const StackTrace *stack);
+bool IsODRViolationSuppressed(const char *global_var_name);
+
+} // namespace __asan
+
+#endif // ASAN_SUPPRESSIONS_H
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
#include "lsan/lsan_common.h"
namespace __asan {
// AsanThreadContext implementation.
+struct CreateThreadContextArgs {
+ AsanThread *thread;
+ StackTrace *stack;
+};
+
void AsanThreadContext::OnCreated(void *arg) {
CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg);
if (args->stack)
- stack_id = StackDepotPut(args->stack->trace, args->stack->size);
+ stack_id = StackDepotPut(*args->stack);
thread = args->thread;
thread->set_context(this);
}
void AsanThreadContext::OnFinished() {
// Drop the link to the AsanThread object.
- thread = 0;
+ thread = nullptr;
}
// MIPS requires aligned address
// AsanThread implementation.
-AsanThread *AsanThread::Create(thread_callback_t start_routine,
- void *arg) {
+AsanThread *AsanThread::Create(thread_callback_t start_routine, void *arg,
+ u32 parent_tid, StackTrace *stack,
+ bool detached) {
uptr PageSize = GetPageSizeCached();
uptr size = RoundUpTo(sizeof(AsanThread), PageSize);
- AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__);
+ AsanThread *thread = (AsanThread*)MmapOrDie(size, __func__);
thread->start_routine_ = start_routine;
thread->arg_ = arg;
+ CreateThreadContextArgs args = { thread, stack };
+ asanThreadRegistry().CreateThread(*reinterpret_cast<uptr *>(thread), detached,
+ parent_tid, &args);
return thread;
}
void AsanThread::TSDDtor(void *tsd) {
AsanThreadContext *context = (AsanThreadContext*)tsd;
- if (common_flags()->verbosity >= 1)
- Report("T%d TSDDtor\n", context->tid);
+ VReport(1, "T%d TSDDtor\n", context->tid);
if (context->thread)
context->thread->Destroy();
}
void AsanThread::Destroy() {
- if (common_flags()->verbosity >= 1) {
- Report("T%d exited\n", tid());
- }
+ int tid = this->tid();
+ VReport(1, "T%d exited\n", tid);
malloc_storage().CommitBack();
- if (flags()->use_sigaltstack) UnsetAlternateSignalStack();
- asanThreadRegistry().FinishThread(tid());
+ if (common_flags()->use_sigaltstack) UnsetAlternateSignalStack();
+ asanThreadRegistry().FinishThread(tid);
FlushToDeadThreadStats(&stats_);
// We also clear the shadow on thread destruction because
// some code may still be executing in later TSD destructors
// and we don't want it to have any poisoned stack.
ClearShadowForThreadStackAndTLS();
- DeleteFakeStack();
+ DeleteFakeStack(tid);
uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached());
UnmapOrDie(this, size);
+ DTLS_Destroy();
}
// We want to create the FakeStack lazyly on the first use, but not eralier
FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
uptr stack_size = this->stack_size();
if (stack_size == 0) // stack_size is not yet available, don't use FakeStack.
- return 0;
+ return nullptr;
uptr old_val = 0;
// fake_stack_ has 3 states:
// 0 -- not initialized
// 1 -- being initialized
// ptr -- initialized
// This CAS checks if the state was 0 and if so changes it to state 1,
- // if that was successfull, it initilizes the pointer.
+ // if that was successful, it initializes the pointer.
if (atomic_compare_exchange_strong(
reinterpret_cast<atomic_uintptr_t *>(&fake_stack_), &old_val, 1UL,
memory_order_relaxed)) {
uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size));
- if (flags()->uar_stack_size_log)
- stack_size_log = static_cast<uptr>(flags()->uar_stack_size_log);
+ CHECK_LE(flags()->min_uar_stack_size_log, flags()->max_uar_stack_size_log);
+ stack_size_log =
+ Min(stack_size_log, static_cast<uptr>(flags()->max_uar_stack_size_log));
+ stack_size_log =
+ Max(stack_size_log, static_cast<uptr>(flags()->min_uar_stack_size_log));
fake_stack_ = FakeStack::Create(stack_size_log);
SetTLSFakeStack(fake_stack_);
return fake_stack_;
}
- return 0;
+ return nullptr;
}
void AsanThread::Init() {
+ fake_stack_ = nullptr; // Will be initialized lazily if needed.
+ CHECK_EQ(this->stack_size(), 0U);
SetThreadStackAndTls();
+ CHECK_GT(this->stack_size(), 0U);
CHECK(AddrIsInMem(stack_bottom_));
CHECK(AddrIsInMem(stack_top_ - 1));
ClearShadowForThreadStackAndTLS();
- if (common_flags()->verbosity >= 1) {
- int local = 0;
- Report("T%d: stack [%p,%p) size 0x%zx; local=%p\n",
- tid(), (void*)stack_bottom_, (void*)stack_top_,
- stack_top_ - stack_bottom_, &local);
- }
- fake_stack_ = 0; // Will be initialized lazily if needed.
- AsanPlatformThreadInit();
+ int local = 0;
+ VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
+ (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
+ &local);
}
-thread_return_t AsanThread::ThreadStart(uptr os_id) {
+thread_return_t AsanThread::ThreadStart(
+ uptr os_id, atomic_uintptr_t *signal_thread_is_registered) {
Init();
- asanThreadRegistry().StartThread(tid(), os_id, 0);
- if (flags()->use_sigaltstack) SetAlternateSignalStack();
+ asanThreadRegistry().StartThread(tid(), os_id, nullptr);
+ if (signal_thread_is_registered)
+ atomic_store(signal_thread_is_registered, 1, memory_order_release);
+
+ if (common_flags()->use_sigaltstack) SetAlternateSignalStack();
if (!start_routine_) {
// start_routine_ == 0 if we're on the main thread or on one of the
PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0);
}
-const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset,
- uptr *frame_pc) {
+bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
+ StackFrameAccess *access) {
uptr bottom = 0;
if (AddrIsInStack(addr)) {
bottom = stack_bottom();
} else if (has_fake_stack()) {
bottom = fake_stack()->AddrIsInFakeStack(addr);
CHECK(bottom);
- *offset = addr - bottom;
- *frame_pc = ((uptr*)bottom)[2];
- return (const char *)((uptr*)bottom)[1];
+ access->offset = addr - bottom;
+ access->frame_pc = ((uptr*)bottom)[2];
+ access->frame_descr = (const char *)((uptr*)bottom)[1];
+ return true;
}
uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr.
u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
}
if (shadow_ptr < shadow_bottom) {
- *offset = 0;
- return "UNKNOWN";
+ return false;
}
uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1));
CHECK(ptr[0] == kCurrentStackFrameMagic);
- *offset = addr - (uptr)ptr;
- *frame_pc = ptr[2];
- return (const char*)ptr[1];
+ access->offset = addr - (uptr)ptr;
+ access->frame_pc = ptr[2];
+ access->frame_descr = (const char*)ptr[1];
+ return true;
}
static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
return tctx->thread;
}
}
- return 0;
+ return nullptr;
}
return context->thread;
}
void SetCurrentThread(AsanThread *t) {
CHECK(t->context());
- if (common_flags()->verbosity >= 2) {
- Report("SetCurrentThread: %p for thread %p\n",
- t->context(), (void*)GetThreadSelf());
- }
+ VReport(2, "SetCurrentThread: %p for thread %p\n", t->context(),
+ (void *)GetThreadSelf());
// Make sure we do not reset the current AsanThread.
CHECK_EQ(0, AsanTSDGet());
AsanTSDSet(t->context());
AsanThreadContext *tctx = static_cast<AsanThreadContext *>(
asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress,
(void *)addr));
- return tctx ? tctx->thread : 0;
+ return tctx ? tctx->thread : nullptr;
}
void EnsureMainThreadIDIsCorrect() {
__asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) {
__asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>(
__asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id));
- if (!context) return 0;
+ if (!context) return nullptr;
return context->thread;
}
-} // namespace __asan
+} // namespace __asan
// --- Implementation of LSan-specific functions --- {{{1
namespace __lsan {
void EnsureMainThreadIDIsCorrect() {
__asan::EnsureMainThreadIDIsCorrect();
}
-} // namespace __lsan
+} // namespace __lsan
//
// ASan-private header for asan_thread.cc.
//===----------------------------------------------------------------------===//
+
#ifndef ASAN_THREAD_H
#define ASAN_THREAD_H
#include "asan_allocator.h"
#include "asan_internal.h"
#include "asan_fake_stack.h"
-#include "asan_stack.h"
#include "asan_stats.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
class AsanThreadContext : public ThreadContextBase {
public:
explicit AsanThreadContext(int tid)
- : ThreadContextBase(tid),
- announced(false),
- destructor_iterations(kPthreadDestructorIterations),
- stack_id(0),
- thread(0) {
- }
+ : ThreadContextBase(tid), announced(false),
+ destructor_iterations(GetPthreadDestructorIterations()), stack_id(0),
+ thread(nullptr) {}
bool announced;
u8 destructor_iterations;
u32 stack_id;
AsanThread *thread;
- void OnCreated(void *arg);
- void OnFinished();
+ void OnCreated(void *arg) override;
+ void OnFinished() override;
};
// AsanThreadContext objects are never freed, so we need many of them.
// AsanThread are stored in TSD and destroyed when the thread dies.
class AsanThread {
public:
- static AsanThread *Create(thread_callback_t start_routine, void *arg);
+ static AsanThread *Create(thread_callback_t start_routine, void *arg,
+ u32 parent_tid, StackTrace *stack, bool detached);
static void TSDDtor(void *tsd);
void Destroy();
void Init(); // Should be called from the thread itself.
- thread_return_t ThreadStart(uptr os_id);
+ thread_return_t ThreadStart(uptr os_id,
+ atomic_uintptr_t *signal_thread_is_registered);
uptr stack_top() { return stack_top_; }
uptr stack_bottom() { return stack_bottom_; }
AsanThreadContext *context() { return context_; }
void set_context(AsanThreadContext *context) { context_ = context; }
- const char *GetFrameNameByAddr(uptr addr, uptr *offset, uptr *frame_pc);
+ struct StackFrameAccess {
+ uptr offset;
+ uptr frame_pc;
+ const char *frame_descr;
+ };
+ bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access);
bool AddrIsInStack(uptr addr) {
return addr >= stack_bottom_ && addr < stack_top_;
}
- void DeleteFakeStack() {
+ void DeleteFakeStack(int tid) {
if (!fake_stack_) return;
FakeStack *t = fake_stack_;
- fake_stack_ = 0;
- SetTLSFakeStack(0);
- t->Destroy();
+ fake_stack_ = nullptr;
+ SetTLSFakeStack(nullptr);
+ t->Destroy(tid);
}
bool has_fake_stack() {
FakeStack *fake_stack() {
if (!__asan_option_detect_stack_use_after_return)
- return 0;
+ return nullptr;
if (!has_fake_stack())
return AsyncSignalSafeLazyInitFakeStack();
return fake_stack_;
bool isUnwinding() const { return unwinding_; }
void setUnwinding(bool b) { unwinding_ = b; }
+ // True if we are in a deadly signal handler.
+ bool isInDeadlySignal() const { return in_deadly_signal_; }
+ void setInDeadlySignal(bool b) { in_deadly_signal_ = b; }
+
AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
AsanStats &stats() { return stats_; }
AsanThreadLocalMallocStorage malloc_storage_;
AsanStats stats_;
bool unwinding_;
+ bool in_deadly_signal_;
};
// ScopedUnwinding is a scope for stacktracing member of a context
AsanThread *thread;
};
-struct CreateThreadContextArgs {
+// ScopedDeadlySignal is a scope for handling deadly signals.
+class ScopedDeadlySignal {
+ public:
+ explicit ScopedDeadlySignal(AsanThread *t) : thread(t) {
+ if (thread) thread->setInDeadlySignal(true);
+ }
+ ~ScopedDeadlySignal() {
+ if (thread) thread->setInDeadlySignal(false);
+ }
+
+ private:
AsanThread *thread;
- StackTrace *stack;
};
// Returns a single instance of registry.
// Used to handle fork().
void EnsureMainThreadIDIsCorrect();
-} // namespace __asan
+} // namespace __asan
-#endif // ASAN_THREAD_H
+#endif // ASAN_THREAD_H
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_WINDOWS
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
-#include <dbghelp.h>
#include <stdlib.h>
#include "asan_interceptors.h"
#include "asan_internal.h"
+#include "asan_report.h"
+#include "asan_stack.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mutex.h"
+using namespace __asan; // NOLINT
+
extern "C" {
- SANITIZER_INTERFACE_ATTRIBUTE
- int __asan_should_detect_stack_use_after_return() {
- __asan_init();
- return __asan_option_detect_stack_use_after_return;
- }
+SANITIZER_INTERFACE_ATTRIBUTE
+int __asan_should_detect_stack_use_after_return() {
+ __asan_init();
+ return __asan_option_detect_stack_use_after_return;
+}
+
+// -------------------- A workaround for the abscence of weak symbols ----- {{{
+// We don't have a direct equivalent of weak symbols when using MSVC, but we can
+// use the /alternatename directive to tell the linker to default a specific
+// symbol to a specific value, which works nicely for allocator hooks and
+// __asan_default_options().
+void __sanitizer_default_malloc_hook(void *ptr, uptr size) { }
+void __sanitizer_default_free_hook(void *ptr) { }
+const char* __asan_default_default_options() { return ""; }
+const char* __asan_default_default_suppressions() { return ""; }
+void __asan_default_on_error() {}
+#pragma comment(linker, "/alternatename:___sanitizer_malloc_hook=___sanitizer_default_malloc_hook") // NOLINT
+#pragma comment(linker, "/alternatename:___sanitizer_free_hook=___sanitizer_default_free_hook") // NOLINT
+#pragma comment(linker, "/alternatename:___asan_default_options=___asan_default_default_options") // NOLINT
+#pragma comment(linker, "/alternatename:___asan_default_suppressions=___asan_default_default_suppressions") // NOLINT
+#pragma comment(linker, "/alternatename:___asan_on_error=___asan_default_on_error") // NOLINT
+// }}}
+} // extern "C"
+
+// ---------------------- Windows-specific inteceptors ---------------- {{{
+INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(RaiseException));
+ __asan_handle_no_return();
+ REAL(RaiseException)(a, b, c, d);
}
+INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(_except_handler3));
+ __asan_handle_no_return();
+ return REAL(_except_handler3)(a, b, c, d);
+}
+
+#if ASAN_DYNAMIC
+// This handler is named differently in -MT and -MD CRTs.
+#define _except_handler4 _except_handler4_common
+#endif
+INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(_except_handler4));
+ __asan_handle_no_return();
+ return REAL(_except_handler4)(a, b, c, d);
+}
+
+static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
+ AsanThread *t = (AsanThread*)arg;
+ SetCurrentThread(t);
+ return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
+}
+
+INTERCEPTOR_WINAPI(DWORD, CreateThread,
+ void* security, uptr stack_size,
+ DWORD (__stdcall *start_routine)(void*), void* arg,
+ DWORD thr_flags, void* tid) {
+ // Strict init-order checking is thread-hostile.
+ if (flags()->strict_init_order)
+ StopInitOrderChecking();
+ GET_STACK_TRACE_THREAD;
+ // FIXME: The CreateThread interceptor is not the same as a pthread_create
+ // one. This is a bandaid fix for PR22025.
+ bool detached = false; // FIXME: how can we determine it on Windows?
+ u32 current_tid = GetCurrentTidOrInvalid();
+ AsanThread *t =
+ AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
+ return REAL(CreateThread)(security, stack_size,
+ asan_thread_start, t, thr_flags, tid);
+}
+
+namespace {
+BlockingMutex mu_for_thread_tracking(LINKER_INITIALIZED);
+
+void EnsureWorkerThreadRegistered() {
+ // FIXME: GetCurrentThread relies on TSD, which might not play well with
+ // system thread pools. We might want to use something like reference
+ // counting to zero out GetCurrentThread() underlying storage when the last
+ // work item finishes? Or can we disable reclaiming of threads in the pool?
+ BlockingMutexLock l(&mu_for_thread_tracking);
+ if (__asan::GetCurrentThread())
+ return;
+
+ AsanThread *t = AsanThread::Create(
+ /* start_routine */ nullptr, /* arg */ nullptr,
+ /* parent_tid */ -1, /* stack */ nullptr, /* detached */ true);
+ t->Init();
+ asanThreadRegistry().StartThread(t->tid(), 0, 0);
+ SetCurrentThread(t);
+}
+} // namespace
+
+INTERCEPTOR_WINAPI(DWORD, NtWaitForWorkViaWorkerFactory, DWORD a, DWORD b) {
+ // NtWaitForWorkViaWorkerFactory is called from system worker pool threads to
+ // query work scheduled by BindIoCompletionCallback, QueueUserWorkItem, etc.
+ // System worker pool threads are created at arbitraty point in time and
+ // without using CreateThread, so we wrap NtWaitForWorkViaWorkerFactory
+ // instead and don't register a specific parent_tid/stack.
+ EnsureWorkerThreadRegistered();
+ return REAL(NtWaitForWorkViaWorkerFactory)(a, b);
+}
+
+// }}}
+
namespace __asan {
-// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1
-static BlockingMutex dbghelp_lock(LINKER_INITIALIZED);
-static bool dbghelp_initialized = false;
-#pragma comment(lib, "dbghelp.lib")
+void InitializePlatformInterceptors() {
+ ASAN_INTERCEPT_FUNC(CreateThread);
+ ASAN_INTERCEPT_FUNC(RaiseException);
+ ASAN_INTERCEPT_FUNC(_except_handler3);
+ ASAN_INTERCEPT_FUNC(_except_handler4);
+
+ // NtWaitForWorkViaWorkerFactory is always linked dynamically.
+ CHECK(::__interception::OverrideFunction(
+ "NtWaitForWorkViaWorkerFactory",
+ (uptr)WRAP(NtWaitForWorkViaWorkerFactory),
+ (uptr *)&REAL(NtWaitForWorkViaWorkerFactory)));
+}
-// ---------------------- TSD ---------------- {{{1
+// ---------------------- TSD ---------------- {{{
static bool tsd_key_inited = false;
static __declspec(thread) void *fake_tsd = 0;
void PlatformTSDDtor(void *tsd) {
AsanThread::TSDDtor(tsd);
}
-// ---------------------- Various stuff ---------------- {{{1
+// }}}
+
+// ---------------------- Various stuff ---------------- {{{
+void DisableReexec() {
+ // No need to re-exec on Windows.
+}
+
void MaybeReexec() {
// No need to re-exec on Windows.
}
return 0;
}
-void SetAlternateSignalStack() {
- // FIXME: Decide what to do on Windows.
-}
+void AsanCheckDynamicRTPrereqs() {}
-void UnsetAlternateSignalStack() {
- // FIXME: Decide what to do on Windows.
-}
+void AsanCheckIncompatibleRT() {}
-void InstallSignalHandlers() {
- // FIXME: Decide what to do on Windows.
-}
-
-void AsanPlatformThreadInit() {
- // Nothing here for now.
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ UNIMPLEMENTED();
}
-void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+void AsanOnDeadlySignal(int, void *siginfo, void *context) {
UNIMPLEMENTED();
}
-} // namespace __asan
+static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan; // NOLINT
+static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
+ EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
+ CONTEXT *context = info->ContextRecord;
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
-bool __asan_symbolize(const void *addr, char *out_buffer, int buffer_size) {
- BlockingMutexLock lock(&dbghelp_lock);
- if (!dbghelp_initialized) {
- SymSetOptions(SYMOPT_DEFERRED_LOADS |
- SYMOPT_UNDNAME |
- SYMOPT_LOAD_LINES);
- CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE));
- // FIXME: We don't call SymCleanup() on exit yet - should we?
- dbghelp_initialized = true;
+ if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
+ exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) {
+ const char *description =
+ (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ ? "access-violation"
+ : "in-page-error";
+ SignalContext sig = SignalContext::Create(exception_record, context);
+ ReportDeadlySignal(description, sig);
}
- // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
- char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
- PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
- symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
- symbol->MaxNameLen = MAX_SYM_NAME;
- DWORD64 offset = 0;
- BOOL got_objname = SymFromAddr(GetCurrentProcess(),
- (DWORD64)addr, &offset, symbol);
- if (!got_objname)
- return false;
-
- DWORD unused;
- IMAGEHLP_LINE64 info;
- info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
- BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(),
- (DWORD64)addr, &unused, &info);
- int written = 0;
- out_buffer[0] = '\0';
- // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too.
- if (got_fileline) {
- written += internal_snprintf(out_buffer + written, buffer_size - written,
- " %s %s:%d", symbol->Name,
- info.FileName, info.LineNumber);
- } else {
- written += internal_snprintf(out_buffer + written, buffer_size - written,
- " %s+0x%p", symbol->Name, offset);
- }
- return true;
+ // FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
+
+ return default_seh_handler(info);
+}
+
+// We want to install our own exception handler (EH) to print helpful reports
+// on access violations and whatnot. Unfortunately, the CRT initializers assume
+// they are run before any user code and drop any previously-installed EHs on
+// the floor, so we can't install our handler inside __asan_init.
+// (See crt0dat.c in the CRT sources for the details)
+//
+// Things get even more complicated with the dynamic runtime, as it finishes its
+// initialization before the .exe module CRT begins to initialize.
+//
+// For the static runtime (-MT), it's enough to put a callback to
+// __asan_set_seh_filter in the last section for C initializers.
+//
+// For the dynamic runtime (-MD), we want link the same
+// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter
+// will be called for each instrumented module. This ensures that at least one
+// __asan_set_seh_filter call happens after the .exe module CRT is initialized.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+int __asan_set_seh_filter() {
+ // We should only store the previous handler if it's not our own handler in
+ // order to avoid loops in the EH chain.
+ auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler);
+ if (prev_seh_handler != &SEHHandler)
+ default_seh_handler = prev_seh_handler;
+ return 0;
}
-} // extern "C"
+#if !ASAN_DYNAMIC
+// Put a pointer to __asan_set_seh_filter at the end of the global list
+// of C initializers, after the default EH is set by the CRT.
+#pragma section(".CRT$XIZ", long, read) // NOLINT
+__declspec(allocate(".CRT$XIZ"))
+ int (*__intercept_seh)() = __asan_set_seh_filter;
+#endif
+// }}}
+} // namespace __asan
#endif // _WIN32
--- /dev/null
+//===-- asan_win_dll_thunk.cc ---------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file defines a family of thunks that should be statically linked into
+// the DLLs that have ASan instrumentation in order to delegate the calls to the
+// shared runtime that lives in the main binary.
+// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the
+// details.
+//===----------------------------------------------------------------------===//
+
+// Only compile this code when buidling asan_dll_thunk.lib
+// Using #ifdef rather than relying on Makefiles etc.
+// simplifies the build procedure.
+#ifdef ASAN_DLL_THUNK
+#include "asan_init_version.h"
+#include "interception/interception.h"
+
+// ---------- Function interception helper functions and macros ----------- {{{1
+extern "C" {
+void *__stdcall GetModuleHandleA(const char *module_name);
+void *__stdcall GetProcAddress(void *module, const char *proc_name);
+void abort();
+}
+
+static uptr getRealProcAddressOrDie(const char *name) {
+ uptr ret =
+ __interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name);
+ if (!ret)
+ abort();
+ return ret;
+}
+
+// We need to intercept some functions (e.g. ASan interface, memory allocator --
+// let's call them "hooks") exported by the DLL thunk and forward the hooks to
+// the runtime in the main module.
+// However, we don't want to keep two lists of these hooks.
+// To avoid that, the list of hooks should be defined using the
+// INTERCEPT_WHEN_POSSIBLE macro. Then, all these hooks can be intercepted
+// at once by calling INTERCEPT_HOOKS().
+
+// Use macro+template magic to automatically generate the list of hooks.
+// Each hook at line LINE defines a template class with a static
+// FunctionInterceptor<LINE>::Execute() method intercepting the hook.
+// The default implementation of FunctionInterceptor<LINE> is to call
+// the Execute() method corresponding to the previous line.
+template<int LINE>
+struct FunctionInterceptor {
+ static void Execute() { FunctionInterceptor<LINE-1>::Execute(); }
+};
+
+// There shouldn't be any hooks with negative definition line number.
+template<>
+struct FunctionInterceptor<0> {
+ static void Execute() {}
+};
+
+#define INTERCEPT_WHEN_POSSIBLE(main_function, dll_function) \
+ template <> struct FunctionInterceptor<__LINE__> { \
+ static void Execute() { \
+ uptr wrapper = getRealProcAddressOrDie(main_function); \
+ if (!__interception::OverrideFunction((uptr)dll_function, wrapper, 0)) \
+ abort(); \
+ FunctionInterceptor<__LINE__ - 1>::Execute(); \
+ } \
+ };
+
+// Special case of hooks -- ASan own interface functions. Those are only called
+// after __asan_init, thus an empty implementation is sufficient.
+#define INTERFACE_FUNCTION(name) \
+ extern "C" __declspec(noinline) void name() { \
+ volatile int prevent_icf = (__LINE__ << 8); (void)prevent_icf; \
+ __debugbreak(); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name)
+
+// INTERCEPT_HOOKS must be used after the last INTERCEPT_WHEN_POSSIBLE.
+#define INTERCEPT_HOOKS FunctionInterceptor<__LINE__>::Execute
+
+// We can't define our own version of strlen etc. because that would lead to
+// link-time or even type mismatch errors. Instead, we can declare a function
+// just to be able to get its address. Me may miss the first few calls to the
+// functions since it can be called before __asan_init, but that would lead to
+// false negatives in the startup code before user's global initializers, which
+// isn't a big deal.
+#define INTERCEPT_LIBRARY_FUNCTION(name) \
+ extern "C" void name(); \
+ INTERCEPT_WHEN_POSSIBLE(WRAPPER_NAME(name), name)
+
+// Disable compiler warnings that show up if we declare our own version
+// of a compiler intrinsic (e.g. strlen).
+#pragma warning(disable: 4391)
+#pragma warning(disable: 4392)
+
+static void InterceptHooks();
+// }}}
+
+// ---------- Function wrapping helpers ----------------------------------- {{{1
+#define WRAP_V_V(name) \
+ extern "C" void name() { \
+ typedef void (*fntype)(); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ fn(); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+
+#define WRAP_V_W(name) \
+ extern "C" void name(void *arg) { \
+ typedef void (*fntype)(void *arg); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ fn(arg); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+
+#define WRAP_V_WW(name) \
+ extern "C" void name(void *arg1, void *arg2) { \
+ typedef void (*fntype)(void *, void *); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ fn(arg1, arg2); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+
+#define WRAP_V_WWW(name) \
+ extern "C" void name(void *arg1, void *arg2, void *arg3) { \
+ typedef void *(*fntype)(void *, void *, void *); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ fn(arg1, arg2, arg3); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+
+#define WRAP_W_V(name) \
+ extern "C" void *name() { \
+ typedef void *(*fntype)(); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ return fn(); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+
+#define WRAP_W_W(name) \
+ extern "C" void *name(void *arg) { \
+ typedef void *(*fntype)(void *arg); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ return fn(arg); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+
+#define WRAP_W_WW(name) \
+ extern "C" void *name(void *arg1, void *arg2) { \
+ typedef void *(*fntype)(void *, void *); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ return fn(arg1, arg2); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+
+#define WRAP_W_WWW(name) \
+ extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
+ typedef void *(*fntype)(void *, void *, void *); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ return fn(arg1, arg2, arg3); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+
+#define WRAP_W_WWWW(name) \
+ extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
+ typedef void *(*fntype)(void *, void *, void *, void *); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ return fn(arg1, arg2, arg3, arg4); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+
+#define WRAP_W_WWWWW(name) \
+ extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
+ void *arg5) { \
+ typedef void *(*fntype)(void *, void *, void *, void *, void *); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ return fn(arg1, arg2, arg3, arg4, arg5); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+
+#define WRAP_W_WWWWWW(name) \
+ extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
+ void *arg5, void *arg6) { \
+ typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \
+ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
+ return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
+ } \
+ INTERCEPT_WHEN_POSSIBLE(#name, name);
+// }}}
+
+// ----------------- ASan own interface functions --------------------
+// Don't use the INTERFACE_FUNCTION machinery for this function as we actually
+// want to call it in the __asan_init interceptor.
+WRAP_W_V(__asan_should_detect_stack_use_after_return)
+
+extern "C" {
+ int __asan_option_detect_stack_use_after_return;
+
+ // Manually wrap __asan_init as we need to initialize
+ // __asan_option_detect_stack_use_after_return afterwards.
+ void __asan_init() {
+ typedef void (*fntype)();
+ static fntype fn = 0;
+ // __asan_init is expected to be called by only one thread.
+ if (fn) return;
+
+ fn = (fntype)getRealProcAddressOrDie("__asan_init");
+ fn();
+ __asan_option_detect_stack_use_after_return =
+ (__asan_should_detect_stack_use_after_return() != 0);
+
+ InterceptHooks();
+ }
+}
+
+extern "C" void __asan_version_mismatch_check() {
+ // Do nothing.
+}
+
+INTERFACE_FUNCTION(__asan_handle_no_return)
+
+INTERFACE_FUNCTION(__asan_report_store1)
+INTERFACE_FUNCTION(__asan_report_store2)
+INTERFACE_FUNCTION(__asan_report_store4)
+INTERFACE_FUNCTION(__asan_report_store8)
+INTERFACE_FUNCTION(__asan_report_store16)
+INTERFACE_FUNCTION(__asan_report_store_n)
+
+INTERFACE_FUNCTION(__asan_report_load1)
+INTERFACE_FUNCTION(__asan_report_load2)
+INTERFACE_FUNCTION(__asan_report_load4)
+INTERFACE_FUNCTION(__asan_report_load8)
+INTERFACE_FUNCTION(__asan_report_load16)
+INTERFACE_FUNCTION(__asan_report_load_n)
+
+INTERFACE_FUNCTION(__asan_store1)
+INTERFACE_FUNCTION(__asan_store2)
+INTERFACE_FUNCTION(__asan_store4)
+INTERFACE_FUNCTION(__asan_store8)
+INTERFACE_FUNCTION(__asan_store16)
+INTERFACE_FUNCTION(__asan_storeN)
+
+INTERFACE_FUNCTION(__asan_load1)
+INTERFACE_FUNCTION(__asan_load2)
+INTERFACE_FUNCTION(__asan_load4)
+INTERFACE_FUNCTION(__asan_load8)
+INTERFACE_FUNCTION(__asan_load16)
+INTERFACE_FUNCTION(__asan_loadN)
+
+INTERFACE_FUNCTION(__asan_memcpy);
+INTERFACE_FUNCTION(__asan_memset);
+INTERFACE_FUNCTION(__asan_memmove);
+
+INTERFACE_FUNCTION(__asan_alloca_poison);
+INTERFACE_FUNCTION(__asan_allocas_unpoison);
+
+INTERFACE_FUNCTION(__asan_register_globals)
+INTERFACE_FUNCTION(__asan_unregister_globals)
+
+INTERFACE_FUNCTION(__asan_before_dynamic_init)
+INTERFACE_FUNCTION(__asan_after_dynamic_init)
+
+INTERFACE_FUNCTION(__asan_poison_stack_memory)
+INTERFACE_FUNCTION(__asan_unpoison_stack_memory)
+
+INTERFACE_FUNCTION(__asan_poison_memory_region)
+INTERFACE_FUNCTION(__asan_unpoison_memory_region)
+
+INTERFACE_FUNCTION(__asan_address_is_poisoned)
+INTERFACE_FUNCTION(__asan_region_is_poisoned)
+
+INTERFACE_FUNCTION(__asan_get_current_fake_stack)
+INTERFACE_FUNCTION(__asan_addr_is_in_fake_stack)
+
+INTERFACE_FUNCTION(__asan_stack_malloc_0)
+INTERFACE_FUNCTION(__asan_stack_malloc_1)
+INTERFACE_FUNCTION(__asan_stack_malloc_2)
+INTERFACE_FUNCTION(__asan_stack_malloc_3)
+INTERFACE_FUNCTION(__asan_stack_malloc_4)
+INTERFACE_FUNCTION(__asan_stack_malloc_5)
+INTERFACE_FUNCTION(__asan_stack_malloc_6)
+INTERFACE_FUNCTION(__asan_stack_malloc_7)
+INTERFACE_FUNCTION(__asan_stack_malloc_8)
+INTERFACE_FUNCTION(__asan_stack_malloc_9)
+INTERFACE_FUNCTION(__asan_stack_malloc_10)
+
+INTERFACE_FUNCTION(__asan_stack_free_0)
+INTERFACE_FUNCTION(__asan_stack_free_1)
+INTERFACE_FUNCTION(__asan_stack_free_2)
+INTERFACE_FUNCTION(__asan_stack_free_4)
+INTERFACE_FUNCTION(__asan_stack_free_5)
+INTERFACE_FUNCTION(__asan_stack_free_6)
+INTERFACE_FUNCTION(__asan_stack_free_7)
+INTERFACE_FUNCTION(__asan_stack_free_8)
+INTERFACE_FUNCTION(__asan_stack_free_9)
+INTERFACE_FUNCTION(__asan_stack_free_10)
+
+// FIXME: we might want to have a sanitizer_win_dll_thunk?
+INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
+INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
+INTERFACE_FUNCTION(__sanitizer_cov)
+INTERFACE_FUNCTION(__sanitizer_cov_dump)
+INTERFACE_FUNCTION(__sanitizer_cov_indir_call16)
+INTERFACE_FUNCTION(__sanitizer_cov_init)
+INTERFACE_FUNCTION(__sanitizer_cov_module_init)
+INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block)
+INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter)
+INTERFACE_FUNCTION(__sanitizer_cov_trace_cmp)
+INTERFACE_FUNCTION(__sanitizer_cov_trace_switch)
+INTERFACE_FUNCTION(__sanitizer_cov_with_check)
+INTERFACE_FUNCTION(__sanitizer_get_allocated_size)
+INTERFACE_FUNCTION(__sanitizer_get_coverage_guards)
+INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes)
+INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size)
+INTERFACE_FUNCTION(__sanitizer_get_free_bytes)
+INTERFACE_FUNCTION(__sanitizer_get_heap_size)
+INTERFACE_FUNCTION(__sanitizer_get_ownership)
+INTERFACE_FUNCTION(__sanitizer_get_total_unique_caller_callee_pairs)
+INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage)
+INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes)
+INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file)
+INTERFACE_FUNCTION(__sanitizer_print_stack_trace)
+INTERFACE_FUNCTION(__sanitizer_ptr_cmp)
+INTERFACE_FUNCTION(__sanitizer_ptr_sub)
+INTERFACE_FUNCTION(__sanitizer_report_error_summary)
+INTERFACE_FUNCTION(__sanitizer_reset_coverage)
+INTERFACE_FUNCTION(__sanitizer_get_number_of_counters)
+INTERFACE_FUNCTION(__sanitizer_update_counter_bitset_and_clear_counters)
+INTERFACE_FUNCTION(__sanitizer_sandbox_on_notify)
+INTERFACE_FUNCTION(__sanitizer_set_death_callback)
+INTERFACE_FUNCTION(__sanitizer_set_report_path)
+INTERFACE_FUNCTION(__sanitizer_unaligned_load16)
+INTERFACE_FUNCTION(__sanitizer_unaligned_load32)
+INTERFACE_FUNCTION(__sanitizer_unaligned_load64)
+INTERFACE_FUNCTION(__sanitizer_unaligned_store16)
+INTERFACE_FUNCTION(__sanitizer_unaligned_store32)
+INTERFACE_FUNCTION(__sanitizer_unaligned_store64)
+INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container)
+
+// TODO(timurrrr): Add more interface functions on the as-needed basis.
+
+// ----------------- Memory allocation functions ---------------------
+WRAP_V_W(free)
+WRAP_V_WW(_free_dbg)
+
+WRAP_W_W(malloc)
+WRAP_W_WWWW(_malloc_dbg)
+
+WRAP_W_WW(calloc)
+WRAP_W_WWWWW(_calloc_dbg)
+WRAP_W_WWW(_calloc_impl)
+
+WRAP_W_WW(realloc)
+WRAP_W_WWW(_realloc_dbg)
+WRAP_W_WWW(_recalloc)
+
+WRAP_W_W(_msize)
+WRAP_W_W(_expand)
+WRAP_W_W(_expand_dbg)
+
+// TODO(timurrrr): Might want to add support for _aligned_* allocation
+// functions to detect a bit more bugs. Those functions seem to wrap malloc().
+
+// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc).
+
+INTERCEPT_LIBRARY_FUNCTION(atoi);
+INTERCEPT_LIBRARY_FUNCTION(atol);
+INTERCEPT_LIBRARY_FUNCTION(_except_handler3);
+
+// _except_handler4 checks -GS cookie which is different for each module, so we
+// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4).
+INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
+ __asan_handle_no_return();
+ return REAL(_except_handler4)(a, b, c, d);
+}
+
+INTERCEPT_LIBRARY_FUNCTION(frexp);
+INTERCEPT_LIBRARY_FUNCTION(longjmp);
+INTERCEPT_LIBRARY_FUNCTION(memchr);
+INTERCEPT_LIBRARY_FUNCTION(memcmp);
+INTERCEPT_LIBRARY_FUNCTION(memcpy);
+INTERCEPT_LIBRARY_FUNCTION(memmove);
+INTERCEPT_LIBRARY_FUNCTION(memset);
+INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT
+INTERCEPT_LIBRARY_FUNCTION(strchr);
+INTERCEPT_LIBRARY_FUNCTION(strcmp);
+INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT
+INTERCEPT_LIBRARY_FUNCTION(strcspn);
+INTERCEPT_LIBRARY_FUNCTION(strlen);
+INTERCEPT_LIBRARY_FUNCTION(strncat);
+INTERCEPT_LIBRARY_FUNCTION(strncmp);
+INTERCEPT_LIBRARY_FUNCTION(strncpy);
+INTERCEPT_LIBRARY_FUNCTION(strnlen);
+INTERCEPT_LIBRARY_FUNCTION(strpbrk);
+INTERCEPT_LIBRARY_FUNCTION(strspn);
+INTERCEPT_LIBRARY_FUNCTION(strstr);
+INTERCEPT_LIBRARY_FUNCTION(strtol);
+INTERCEPT_LIBRARY_FUNCTION(wcslen);
+
+// Must be after all the interceptor declarations due to the way INTERCEPT_HOOKS
+// is defined.
+void InterceptHooks() {
+ INTERCEPT_HOOKS();
+ INTERCEPT_FUNCTION(_except_handler4);
+}
+
+// We want to call __asan_init before C/C++ initializers/constructors are
+// executed, otherwise functions like memset might be invoked.
+// For some strange reason, merely linking in asan_preinit.cc doesn't work
+// as the callback is never called... Is link.exe doing something too smart?
+
+// In DLLs, the callbacks are expected to return 0,
+// otherwise CRT initialization fails.
+static int call_asan_init() {
+ __asan_init();
+ return 0;
+}
+#pragma section(".CRT$XIB", long, read) // NOLINT
+__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = call_asan_init;
+
+#endif // ASAN_DLL_THUNK
--- /dev/null
+//===-- asan_win_uar_thunk.cc ---------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file defines things that need to be present in the application modules
+// to interact with the ASan DLL runtime correctly and can't be implemented
+// using the default "import library" generated when linking the DLL RTL.
+//
+// This includes:
+// - forwarding the detect_stack_use_after_return runtime option
+// - working around deficiencies of the MD runtime
+// - installing a custom SEH handlerx
+//
+//===----------------------------------------------------------------------===//
+
+// Only compile this code when buidling asan_dynamic_runtime_thunk.lib
+// Using #ifdef rather than relying on Makefiles etc.
+// simplifies the build procedure.
+#ifdef ASAN_DYNAMIC_RUNTIME_THUNK
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// First, declare CRT sections we'll be using in this file
+#pragma section(".CRT$XID", long, read) // NOLINT
+#pragma section(".CRT$XIZ", long, read) // NOLINT
+#pragma section(".CRT$XTW", long, read) // NOLINT
+#pragma section(".CRT$XTY", long, read) // NOLINT
+
+////////////////////////////////////////////////////////////////////////////////
+// Define a copy of __asan_option_detect_stack_use_after_return that should be
+// used when linking an MD runtime with a set of object files on Windows.
+//
+// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return',
+// so normally we would just dllimport it. Unfortunately, the dllimport
+// attribute adds __imp_ prefix to the symbol name of a variable.
+// Since in general we don't know if a given TU is going to be used
+// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
+// just to work around this issue, let's clone the a variable that is
+// constant after initialization anyways.
+extern "C" {
+__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
+int __asan_option_detect_stack_use_after_return =
+ __asan_should_detect_stack_use_after_return();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL
+// unload or on exit. ASan relies on LLVM global_dtors to call
+// __asan_unregister_globals on these events, which unfortunately doesn't work
+// with the MD runtime, see PR22545 for the details.
+// To work around this, for each DLL we schedule a call to UnregisterGlobals
+// using atexit() that calls a small subset of C terminators
+// where LLVM global_dtors is placed. Fingers crossed, no other C terminators
+// are there.
+extern "C" void __cdecl _initterm(void *a, void *b);
+
+namespace {
+__declspec(allocate(".CRT$XTW")) void* before_global_dtors = 0;
+__declspec(allocate(".CRT$XTY")) void* after_global_dtors = 0;
+
+void UnregisterGlobals() {
+ _initterm(&before_global_dtors, &after_global_dtors);
+}
+
+int ScheduleUnregisterGlobals() {
+ return atexit(UnregisterGlobals);
+}
+
+// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after
+// atexit() is initialized (.CRT$XIC). As this is executed before C++
+// initializers (think ctors for globals), UnregisterGlobals gets executed after
+// dtors for C++ globals.
+__declspec(allocate(".CRT$XID"))
+int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// ASan SEH handling.
+// We need to set the ASan-specific SEH handler at the end of CRT initialization
+// of each module (see also asan_win.cc).
+extern "C" {
+__declspec(dllimport) int __asan_set_seh_filter();
+static int SetSEHFilter() { return __asan_set_seh_filter(); }
+
+// Unfortunately, putting a pointer to __asan_set_seh_filter into
+// __asan_intercept_seh gets optimized out, so we have to use an extra function.
+__declspec(allocate(".CRT$XIZ")) int (*__asan_seh_interceptor)() = SetSEHFilter;
+}
+
+#endif // ASAN_DYNAMIC_RUNTIME_THUNK
# a separate file so that version updates don't involve re-running
# automake.
# CURRENT:REVISION:AGE
-1:0:0
+3:0:0
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
+/* Define to 1 if you have the <rpc/xdr.h> header file. */
+#undef HAVE_RPC_XDR_H
+
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
+/* Define to 1 if you have the <tirpc/rpc/xdr.h> header file. */
+#undef HAVE_TIRPC_RPC_XDR_H
+
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
+TSAN_TARGET_DEPENDENT_OBJECTS
LIBBACKTRACE_SUPPORTED_FALSE
LIBBACKTRACE_SUPPORTED_TRUE
+OBSTACK_DEFS
+RPC_DEFS
BACKTRACE_SUPPORTS_THREADS
BACKTRACE_USES_MALLOC
ALLOC_FILE
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 12023 "configure"
+#line 12031 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 12129 "configure"
+#line 12137 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
# Common libraries that we need to link against for all sanitizer libs.
link_sanitizer_common='-lpthread -ldl -lm'
+# At least for glibc, shm_open is in librt. But don't pull that
+# in if it still doesn't give us the function we want. This
+# test is copied from libgomp.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for shm_open in -lrt" >&5
+$as_echo_n "checking for shm_open in -lrt... " >&6; }
+if test "${ac_cv_lib_rt_shm_open+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrt $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shm_open ();
+int
+main ()
+{
+return shm_open ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_rt_shm_open=yes
+else
+ ac_cv_lib_rt_shm_open=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_shm_open" >&5
+$as_echo "$ac_cv_lib_rt_shm_open" >&6; }
+if test "x$ac_cv_lib_rt_shm_open" = x""yes; then :
+ link_sanitizer_common="-lrt $link_sanitizer_common"
+fi
+
+
# Set up the set of additional libraries that we need to link against for libasan.
link_libasan=$link_sanitizer_common
link_liblsan=$link_sanitizer_common
+
# At least for glibc, clock_gettime is in librt. But don't pull that
# in if it still doesn't give us the function we want. This
# test is copied from libgomp.
-if test $ac_cv_func_clock_gettime = no; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5
$as_echo_n "checking for clock_gettime in -lrt... " >&6; }
if test "${ac_cv_lib_rt_clock_gettime+set}" = set; then :
$as_echo_n "(cached) " >&6
fi
-fi
case "$host" in
*-*-darwin*) MAC_INTERPOSE=true ; enable_static=no ;;
fi
+# Check for rpc/xdr.h
+for ac_header in rpc/xdr.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "rpc/xdr.h" "ac_cv_header_rpc_xdr_h" "$ac_includes_default"
+if test "x$ac_cv_header_rpc_xdr_h" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_RPC_XDR_H 1
+_ACEOF
+
+fi
+
+done
+
+if test x"$ac_cv_header_rpc_xdr_h" = xyes; then
+ rpc_defs="$rpc_defs -DHAVE_RPC_XDR_H=1"
+else
+ rpc_defs="$rpc_defs -DHAVE_RPC_XDR_H=0"
+fi
+
+# Check for tirpc/rpc/xdr.h
+for ac_header in tirpc/rpc/xdr.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "tirpc/rpc/xdr.h" "ac_cv_header_tirpc_rpc_xdr_h" "$ac_includes_default"
+if test "x$ac_cv_header_tirpc_rpc_xdr_h" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_TIRPC_RPC_XDR_H 1
+_ACEOF
+
+fi
+
+done
+
+if test x"$ac_cv_header_tirpc_rpc_xdr_h" = xyes; then
+ rpc_defs="$rpc_defs -DHAVE_TIRPC_RPC_XDR_H=1"
+else
+ rpc_defs="$rpc_defs -DHAVE_TIRPC_RPC_XDR_H=0"
+fi
+
+RPC_DEFS=$rpc_defs
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking obstack params" >&5
+$as_echo_n "checking obstack params... " >&6; }
+if test "${libsanitizer_cv_sys_obstack+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ save_cppflags=$CPPFLAGS
+CPPFLAGS="-I${srcdir}/../include -o conftest.iii $CPPFLAGS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include "obstack.h"
+#ifdef _OBSTACK_SIZE_T
+_OBSTACK_SIZE_T
+#else
+int
+#endif
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ libsanitizer_cv_sys_obstack=`sed -e '/^#/d;/^[ ]*$/d' conftest.iii | sed -e '$!d;s/size_t/SIZE_T/'`
+else
+ libsanitizer_cv_sys_obstack=int
+fi
+rm -f conftest.err conftest.$ac_ext
+CPPFLAGS=$save_cppflags
+rm -f conftest.iii
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libsanitizer_cv_sys_obstack" >&5
+$as_echo "$libsanitizer_cv_sys_obstack" >&6; }
+OBSTACK_DEFS=-D_OBSTACK_SIZE_T=\"$libsanitizer_cv_sys_obstack\"
+
+
if test "x${BACKTRACE_SUPPORTED}x${BACKTRACE_USES_MALLOC}" = "x1x0"; then
LIBBACKTRACE_SUPPORTED_TRUE=
LIBBACKTRACE_SUPPORTED_FALSE='#'
fi
+case "${target}" in
+ x86_64-*-linux-*) TSAN_TARGET_DEPENDENT_OBJECTS='tsan_rtl_amd64.lo' ;;
+ *) TSAN_TARGET_DEPENDENT_OBJECTS='' ;;
+esac
+
+
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
# tests run on this system so they can be shared between configure
# Common libraries that we need to link against for all sanitizer libs.
link_sanitizer_common='-lpthread -ldl -lm'
+# At least for glibc, shm_open is in librt. But don't pull that
+# in if it still doesn't give us the function we want. This
+# test is copied from libgomp.
+AC_CHECK_LIB(rt, shm_open,
+ [link_sanitizer_common="-lrt $link_sanitizer_common"])
+
# Set up the set of additional libraries that we need to link against for libasan.
link_libasan=$link_sanitizer_common
AC_SUBST(link_libasan)
link_liblsan=$link_sanitizer_common
AC_SUBST(link_liblsan)
+
# At least for glibc, clock_gettime is in librt. But don't pull that
# in if it still doesn't give us the function we want. This
# test is copied from libgomp.
-if test $ac_cv_func_clock_gettime = no; then
- AC_CHECK_LIB(rt, clock_gettime,
- [link_libasan="-lrt $link_libasan"
+AC_CHECK_LIB(rt, clock_gettime,
+ [link_libasan="-lrt $link_libasan"
link_libtsan="-lrt $link_libtsan"
# Other sanitizers do not override clock_* API
])
-fi
case "$host" in
*-*-darwin*) MAC_INTERPOSE=true ; enable_static=no ;;
AC_DEFINE(HAVE_GETEXECNAME, 1, [Define if getexecname is available.])
fi
+# Check for rpc/xdr.h
+AC_CHECK_HEADERS(rpc/xdr.h)
+if test x"$ac_cv_header_rpc_xdr_h" = xyes; then
+ rpc_defs="$rpc_defs -DHAVE_RPC_XDR_H=1"
+else
+ rpc_defs="$rpc_defs -DHAVE_RPC_XDR_H=0"
+fi
+
+# Check for tirpc/rpc/xdr.h
+AC_CHECK_HEADERS(tirpc/rpc/xdr.h)
+if test x"$ac_cv_header_tirpc_rpc_xdr_h" = xyes; then
+ rpc_defs="$rpc_defs -DHAVE_TIRPC_RPC_XDR_H=1"
+else
+ rpc_defs="$rpc_defs -DHAVE_TIRPC_RPC_XDR_H=0"
+fi
+
+AC_SUBST([RPC_DEFS], [$rpc_defs])
+
+dnl If this file is processed by autoconf-2.67 or later then the CPPFLAGS
+dnl "-o conftest.iii" can disappear, conftest.iii be replaced with
+dnl conftest.i in the sed command line, and the rm deleted.
+dnl Not all cpp's accept -o, and gcc -E does not accept a second file
+dnl argument as the output file.
+AC_CACHE_CHECK([obstack params],
+[libsanitizer_cv_sys_obstack],
+[save_cppflags=$CPPFLAGS
+CPPFLAGS="-I${srcdir}/../include -o conftest.iii $CPPFLAGS"
+AC_PREPROC_IFELSE([AC_LANG_SOURCE([
+#include "obstack.h"
+#ifdef _OBSTACK_SIZE_T
+_OBSTACK_SIZE_T
+#else
+int
+#endif
+])],
+[libsanitizer_cv_sys_obstack=`sed -e '/^#/d;/^[ ]*$/d' conftest.iii | sed -e '$!d;s/size_t/SIZE_T/'`],
+[libsanitizer_cv_sys_obstack=int])
+CPPFLAGS=$save_cppflags
+rm -f conftest.iii
+])
+AC_SUBST([OBSTACK_DEFS], [-D_OBSTACK_SIZE_T=\"$libsanitizer_cv_sys_obstack\"])
+
AM_CONDITIONAL(LIBBACKTRACE_SUPPORTED,
[test "x${BACKTRACE_SUPPORTED}x${BACKTRACE_USES_MALLOC}" = "x1x0"])
])
fi
+case "${target}" in
+ x86_64-*-linux-*) TSAN_TARGET_DEPENDENT_OBJECTS='tsan_rtl_amd64.lo' ;;
+ *) TSAN_TARGET_DEPENDENT_OBJECTS='' ;;
+esac
+AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS])
+
AC_OUTPUT
;;
arm*-*-linux*)
;;
+ aarch64*-*-linux*)
+ if test x$ac_cv_sizeof_void_p = x8; then
+ TSAN_SUPPORTED=yes
+ LSAN_SUPPORTED=yes
+ TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
+ fi
+ ;;
x86_64-*-darwin[1]* | i?86-*-darwin[1]*)
TSAN_SUPPORTED=no
;;
--- /dev/null
+//===-- allocator_interface.h ---------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Public interface header for allocator used in sanitizers (ASan/TSan/MSan).
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_ALLOCATOR_INTERFACE_H
+#define SANITIZER_ALLOCATOR_INTERFACE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ /* Returns the estimated number of bytes that will be reserved by allocator
+ for request of "size" bytes. If allocator can't allocate that much
+ memory, returns the maximal possible allocation size, otherwise returns
+ "size". */
+ size_t __sanitizer_get_estimated_allocated_size(size_t size);
+
+ /* Returns true if p was returned by the allocator and
+ is not yet freed. */
+ int __sanitizer_get_ownership(const volatile void *p);
+
+ /* Returns the number of bytes reserved for the pointer p.
+ Requires (get_ownership(p) == true) or (p == 0). */
+ size_t __sanitizer_get_allocated_size(const volatile void *p);
+
+ /* Number of bytes, allocated and not yet freed by the application. */
+ size_t __sanitizer_get_current_allocated_bytes();
+
+ /* Number of bytes, mmaped by the allocator to fulfill allocation requests.
+ Generally, for request of X bytes, allocator can reserve and add to free
+ lists a large number of chunks of size X to use them for future requests.
+ All these chunks count toward the heap size. Currently, allocator never
+ releases memory to OS (instead, it just puts freed chunks to free
+ lists). */
+ size_t __sanitizer_get_heap_size();
+
+ /* Number of bytes, mmaped by the allocator, which can be used to fulfill
+ allocation requests. When a user program frees memory chunk, it can first
+ fall into quarantine and will count toward __sanitizer_get_free_bytes()
+ later. */
+ size_t __sanitizer_get_free_bytes();
+
+ /* Number of bytes in unmapped pages, that are released to OS. Currently,
+ always returns 0. */
+ size_t __sanitizer_get_unmapped_bytes();
+
+ /* Malloc hooks that may be optionally provided by user.
+ __sanitizer_malloc_hook(ptr, size) is called immediately after
+ allocation of "size" bytes, which returned "ptr".
+ __sanitizer_free_hook(ptr) is called immediately before
+ deallocation of "ptr". */
+ void __sanitizer_malloc_hook(const volatile void *ptr, size_t size);
+ void __sanitizer_free_hook(const volatile void *ptr);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
((void)(addr), (void)(size))
#endif
- // Returns true iff addr is poisoned (i.e. 1-byte read/write access to this
+ // Returns 1 if addr is poisoned (i.e. 1-byte read/write access to this
// address will result in error report from AddressSanitizer).
- bool __asan_address_is_poisoned(void const volatile *addr);
+ // Otherwise returns 0.
+ int __asan_address_is_poisoned(void const volatile *addr);
- // If at least on byte in [beg, beg+size) is poisoned, return the address
+ // If at least one byte in [beg, beg+size) is poisoned, return the address
// of the first such byte. Otherwise return 0.
void *__asan_region_is_poisoned(void *beg, size_t size);
// Print the description of addr (useful when debugging in gdb).
void __asan_describe_address(void *addr);
+ // Useful for calling from a debugger to get information about an ASan error.
+ // Returns 1 if an error has been (or is being) reported, otherwise returns 0.
+ int __asan_report_present();
+
+ // Useful for calling from a debugger to get information about an ASan error.
+ // If an error has been (or is being) reported, the following functions return
+ // the pc, bp, sp, address, access type (0 = read, 1 = write), access size and
+ // bug description (e.g. "heap-use-after-free"). Otherwise they return 0.
+ void *__asan_get_report_pc();
+ void *__asan_get_report_bp();
+ void *__asan_get_report_sp();
+ void *__asan_get_report_address();
+ int __asan_get_report_access_type();
+ size_t __asan_get_report_access_size();
+ const char *__asan_get_report_description();
+
+ // Useful for calling from the debugger to get information about a pointer.
+ // Returns the category of the given pointer as a constant string.
+ // Possible return values are "global", "stack", "stack-fake", "heap",
+ // "heap-invalid", "shadow-low", "shadow-gap", "shadow-high", "unknown".
+ // If global or stack, tries to also return the variable name, address and
+ // size. If heap, tries to return the chunk address and size. 'name' should
+ // point to an allocated buffer of size 'name_size'.
+ const char *__asan_locate_address(void *addr, char *name, size_t name_size,
+ void **region_address, size_t *region_size);
+
+ // Useful for calling from the debugger to get the allocation stack trace
+ // and thread ID for a heap address. Stores up to 'size' frames into 'trace',
+ // returns the number of stored frames or 0 on error.
+ size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size,
+ int *thread_id);
+
+ // Useful for calling from the debugger to get the free stack trace
+ // and thread ID for a heap address. Stores up to 'size' frames into 'trace',
+ // returns the number of stored frames or 0 on error.
+ size_t __asan_get_free_stack(void *addr, void **trace, size_t size,
+ int *thread_id);
+
+ // Useful for calling from the debugger to get the current shadow memory
+ // mapping.
+ void __asan_get_shadow_mapping(size_t *shadow_scale, size_t *shadow_offset);
+
// This is an internal function that is called to report an error.
// However it is still a part of the interface because users may want to
// set a breakpoint on this function in a debugger.
void __asan_report_error(void *pc, void *bp, void *sp,
- void *addr, bool is_write, size_t access_size);
-
- // Sets the exit code to use when reporting an error.
- // Returns the old value.
- int __asan_set_error_exit_code(int exit_code);
+ void *addr, int is_write, size_t access_size);
- // Sets the callback to be called right before death on error.
- // Passing 0 will unset the callback.
+ // Deprecated. Call __sanitizer_set_death_callback instead.
void __asan_set_death_callback(void (*callback)(void));
void __asan_set_error_report_callback(void (*callback)(const char*));
// the program crashes before ASan report is printed.
void __asan_on_error();
- // User may provide its own implementation for symbolization function.
- // It should print the description of instruction at address "pc" to
- // "out_buffer". Description should be at most "out_size" bytes long.
- // User-specified function should return true if symbolization was
- // successful.
- bool __asan_symbolize(const void *pc, char *out_buffer,
- int out_size);
-
- // Returns the estimated number of bytes that will be reserved by allocator
- // for request of "size" bytes. If ASan allocator can't allocate that much
- // memory, returns the maximal possible allocation size, otherwise returns
- // "size".
- size_t __asan_get_estimated_allocated_size(size_t size);
- // Returns true if p was returned by the ASan allocator and
- // is not yet freed.
- bool __asan_get_ownership(const void *p);
- // Returns the number of bytes reserved for the pointer p.
- // Requires (get_ownership(p) == true) or (p == 0).
- size_t __asan_get_allocated_size(const void *p);
- // Number of bytes, allocated and not yet freed by the application.
- size_t __asan_get_current_allocated_bytes();
- // Number of bytes, mmaped by asan allocator to fulfill allocation requests.
- // Generally, for request of X bytes, allocator can reserve and add to free
- // lists a large number of chunks of size X to use them for future requests.
- // All these chunks count toward the heap size. Currently, allocator never
- // releases memory to OS (instead, it just puts freed chunks to free lists).
- size_t __asan_get_heap_size();
- // Number of bytes, mmaped by asan allocator, which can be used to fulfill
- // allocation requests. When a user program frees memory chunk, it can first
- // fall into quarantine and will count toward __asan_get_free_bytes() later.
- size_t __asan_get_free_bytes();
- // Number of bytes in unmapped pages, that are released to OS. Currently,
- // always returns 0.
- size_t __asan_get_unmapped_bytes();
// Prints accumulated stats to stderr. Used for debugging.
void __asan_print_accumulated_stats();
// a string containing ASan runtime options. See asan_flags.h for details.
const char* __asan_default_options();
- // Malloc hooks that may be optionally provided by user.
- // __asan_malloc_hook(ptr, size) is called immediately after
- // allocation of "size" bytes, which returned "ptr".
- // __asan_free_hook(ptr) is called immediately before
- // deallocation of "ptr".
- void __asan_malloc_hook(void *ptr, size_t size);
- void __asan_free_hook(void *ptr);
+ // The following 2 functions facilitate garbage collection in presence of
+ // asan's fake stack.
+
+ // Returns an opaque handler to be used later in __asan_addr_is_in_fake_stack.
+ // Returns NULL if the current thread does not have a fake stack.
+ void *__asan_get_current_fake_stack();
+
+ // If fake_stack is non-NULL and addr belongs to a fake frame in
+ // fake_stack, returns the address on real stack that corresponds to
+ // the fake frame and sets beg/end to the boundaries of this fake frame.
+ // Otherwise returns NULL and does not touch beg/end.
+ // If beg/end are NULL, they are not touched.
+ // This function may be called from a thread other than the owner of
+ // fake_stack, but the owner thread need to be alive.
+ void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
+ void **end);
+
#ifdef __cplusplus
} // extern "C"
#endif
#ifdef __cplusplus
extern "C" {
#endif
+ // Arguments for __sanitizer_sandbox_on_notify() below.
+ typedef struct {
+ // Enable sandbox support in sanitizer coverage.
+ int coverage_sandboxed;
+ // File descriptor to write coverage data to. If -1 is passed, a file will
+ // be pre-opened by __sanitizer_sandobx_on_notify(). This field has no
+ // effect if coverage_sandboxed == 0.
+ intptr_t coverage_fd;
+ // If non-zero, split the coverage data into well-formed blocks. This is
+ // useful when coverage_fd is a socket descriptor. Each block will contain
+ // a header, allowing data from multiple processes to be sent over the same
+ // socket.
+ unsigned int coverage_max_block_size;
+ } __sanitizer_sandbox_arguments;
+
// Tell the tools to write their reports to "path.<pid>" instead of stderr.
void __sanitizer_set_report_path(const char *path);
// Notify the tools that the sandbox is going to be turned on. The reserved
// parameter will be used in the future to hold a structure with functions
// that the tools may call to bypass the sandbox.
- void __sanitizer_sandbox_on_notify(void *reserved);
+ void __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
// This function is called by the tool when it has just finished reporting
// an error. 'error_summary' is a one-line string that summarizes
void __sanitizer_unaligned_store32(void *p, uint32_t x);
void __sanitizer_unaligned_store64(void *p, uint64_t x);
- // Record and dump coverage info.
- void __sanitizer_cov_dump();
-
// Annotate the current state of a contiguous container, such as
// std::vector, std::string or similar.
// A contiguous container is a container that keeps all of its elements
// in a contiguous region of memory. The container owns the region of memory
// [beg, end); the memory [beg, mid) is used to store the current elements
// and the memory [mid, end) is reserved for future elements;
- // end <= mid <= end. For example, in "std::vector<> v"
+ // beg <= mid <= end. For example, in "std::vector<> v"
// beg = &v[0];
// end = beg + v.capacity() * sizeof(v[0]);
// mid = beg + v.size() * sizeof(v[0]);
const void *end,
const void *old_mid,
const void *new_mid);
+ // Returns true if the contiguous container [beg, end) is properly poisoned
+ // (e.g. with __sanitizer_annotate_contiguous_container), i.e. if
+ // - [beg, mid) is addressable,
+ // - [mid, end) is unaddressable.
+ // Full verification requires O(end-beg) time; this function tries to avoid
+ // such complexity by touching only parts of the container around beg/mid/end.
+ int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
+ const void *end);
+
+ // Similar to __sanitizer_verify_contiguous_container but returns the address
+ // of the first improperly poisoned byte otherwise. Returns null if the area
+ // is poisoned properly.
+ const void *__sanitizer_contiguous_container_find_bad_address(
+ const void *beg, const void *mid, const void *end);
// Print the stack trace leading to this call. Useful for debugging user code.
void __sanitizer_print_stack_trace();
+ // Sets the callback to be called right before death on error.
+ // Passing 0 will unset the callback.
+ void __sanitizer_set_death_callback(void (*callback)(void));
+
+ // Interceptor hooks.
+ // Whenever a libc function interceptor is called it checks if the
+ // corresponding weak hook is defined, and it so -- calls it.
+ // The primary use case is data-flow-guided fuzzing, where the fuzzer needs
+ // to know what is being passed to libc functions, e.g. memcmp.
+ // FIXME: implement more hooks.
+ void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1,
+ const void *s2, size_t n);
+ void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1,
+ const char *s2, size_t n);
#ifdef __cplusplus
} // extern "C"
#endif
--- /dev/null
+//===-- sanitizer/coverage_interface.h --------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Public interface for sanitizer coverage.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_COVERAG_INTERFACE_H
+#define SANITIZER_COVERAG_INTERFACE_H
+
+#include <sanitizer/common_interface_defs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ // Initialize coverage.
+ void __sanitizer_cov_init();
+ // Record and dump coverage info.
+ void __sanitizer_cov_dump();
+ // Open <name>.sancov.packed in the coverage directory and return the file
+ // descriptor. Returns -1 on failure, or if coverage dumping is disabled.
+ // This is intended for use by sandboxing code.
+ intptr_t __sanitizer_maybe_open_cov_file(const char *name);
+ // Get the number of unique covered blocks (or edges).
+ // This can be useful for coverage-directed in-process fuzzers.
+ uintptr_t __sanitizer_get_total_unique_coverage();
+ // Get the number of unique indirect caller-callee pairs.
+ uintptr_t __sanitizer_get_total_unique_caller_callee_pairs();
+
+ // Reset the basic-block (edge) coverage to the initial state.
+ // Useful for in-process fuzzing to start collecting coverage from scratch.
+ // Experimental, will likely not work for multi-threaded process.
+ void __sanitizer_reset_coverage();
+ // Set *data to the array of covered PCs and return the size of that array.
+ // Some of the entries in *data will be zero.
+ uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data);
+
+ // The coverage instrumentation may optionally provide imprecise counters.
+ // Rather than exposing the counter values to the user we instead map
+ // the counters to a bitset.
+ // Every counter is associated with 8 bits in the bitset.
+ // We define 8 value ranges: 1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+
+ // The i-th bit is set to 1 if the counter value is in the i-th range.
+ // This counter-based coverage implementation is *not* thread-safe.
+
+ // Returns the number of registered coverage counters.
+ uintptr_t __sanitizer_get_number_of_counters();
+ // Updates the counter 'bitset', clears the counters and returns the number of
+ // new bits in 'bitset'.
+ // If 'bitset' is nullptr, only clears the counters.
+ // Otherwise 'bitset' should be at least
+ // __sanitizer_get_number_of_counters bytes long and 8-aligned.
+ uintptr_t
+ __sanitizer_update_counter_bitset_and_clear_counters(uint8_t *bitset);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // SANITIZER_COVERAG_INTERFACE_H
void *userdata;
};
+/// Signature of the callback argument to dfsan_set_write_callback().
+typedef void (*dfsan_write_callback_t)(int fd, const void *buf, size_t count);
+
/// Computes the union of \c l1 and \c l2, possibly creating a union label in
/// the process.
dfsan_label dfsan_union(dfsan_label l1, dfsan_label l2);
/// that label, else returns 0.
dfsan_label dfsan_has_label_with_desc(dfsan_label label, const char *desc);
+/// Returns the number of labels allocated.
+size_t dfsan_get_label_count(void);
+
+/// Sets a callback to be invoked on calls to write(). The callback is invoked
+/// before the write is done. The write is not guaranteed to succeed when the
+/// callback executes. Pass in NULL to remove any callback.
+void dfsan_set_write_callback(dfsan_write_callback_t labeled_write_callback);
+
+/// Writes the labels currently used by the program to the given file
+/// descriptor. The lines of the output have the following format:
+///
+/// <label> <parent label 1> <parent label 2> <label description if any>
+void dfsan_dump_labels(int fd);
+
+/// Interceptor hooks.
+/// Whenever a dfsan's custom function is called the corresponding
+/// hook is called it non-zero. The hooks should be defined by the user.
+/// The primary use case is taint-guided fuzzing, where the fuzzer
+/// needs to see the parameters of the function and the labels.
+/// FIXME: implement more hooks.
+void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2,
+ size_t n, dfsan_label s1_label,
+ dfsan_label s2_label, dfsan_label n_label);
+void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2,
+ size_t n, dfsan_label s1_label,
+ dfsan_label s2_label, dfsan_label n_label);
#ifdef __cplusplus
} // extern "C"
// be treated as non-leaks. Disable/enable pairs may be nested.
void __lsan_disable();
void __lsan_enable();
+
// The heap object into which p points will be treated as a non-leak.
void __lsan_ignore_object(const void *p);
+
+ // Memory regions registered through this interface will be treated as sources
+ // of live pointers during leak checking. Useful if you store pointers in
+ // mapped memory.
+ // Points of note:
+ // - __lsan_unregister_root_region() must be called with the same pointer and
+ // size that have earlier been passed to __lsan_register_root_region()
+ // - LSan will skip any inaccessible memory when scanning a root region. E.g.,
+ // if you map memory within a larger region that you have mprotect'ed, you can
+ // register the entire large region.
+ // - the implementation is not optimized for performance. This interface is
+ // intended to be used for a small number of relatively static regions.
+ void __lsan_register_root_region(const void *p, size_t size);
+ void __lsan_unregister_root_region(const void *p, size_t size);
+
+ // Check for leaks now. This function behaves identically to the default
+ // end-of-process leak check. In particular, it will terminate the process if
+ // leaks are found and the exitcode runtime flag is non-zero.
+ // Subsequent calls to this function will have no effect and end-of-process
+ // leak check will not run. Effectively, end-of-process leak check is moved to
+ // the time of first invocation of this function.
+ // By calling this function early during process shutdown, you can instruct
+ // LSan to ignore shutdown-only leaks which happen later on.
+ void __lsan_do_leak_check();
+
+ // Check for leaks now. Returns zero if no leaks have been found or if leak
+ // detection is disabled, non-zero otherwise.
+ // This function may be called repeatedly, e.g. to periodically check a
+ // long-running process. It prints a leak report if appropriate, but does not
+ // terminate the process. It does not affect the behavior of
+ // __lsan_do_leak_check() or the end-of-process leak check, and is not
+ // affected by them.
+ int __lsan_do_recoverable_leak_check();
+
// The user may optionally provide this function to disallow leak checking
// for the program it is linked into (if the return value is non-zero). This
// function must be defined as returning a constant value; any behavior beyond
// that is unsupported.
int __lsan_is_turned_off();
- // Calling this function makes LSan enter the leak checking phase immediately.
- // Use this if normal end-of-process leak checking happens too late (e.g. if
- // you have intentional memory leaks in your shutdown code). Calling this
- // function overrides end-of-process leak checking; it must be called at
- // most once per process. This function will terminate the process if there
- // are memory leaks and the exit_code flag is non-zero.
- void __lsan_do_leak_check();
+
+ // This function may be optionally provided by the user and should return
+ // a string containing LSan suppressions.
+ const char *__lsan_default_suppressions();
#ifdef __cplusplus
} // extern "C"
#ifdef __cplusplus
extern "C" {
#endif
-
-#if __has_feature(memory_sanitizer)
- /* Returns a string describing a stack origin.
- Return NULL if the origin is invalid, or is not a stack origin. */
- const char *__msan_get_origin_descr_if_stack(uint32_t id);
-
-
/* Set raw origin for the memory range. */
void __msan_set_origin(const volatile void *a, size_t size, uint32_t origin);
/* Get raw origin for an address. */
uint32_t __msan_get_origin(const volatile void *a);
+ /* Test that this_id is a descendant of prev_id (or they are simply equal).
+ * "descendant" here means they are part of the same chain, created with
+ * __msan_chain_origin. */
+ int __msan_origin_is_descendant_or_same(uint32_t this_id, uint32_t prev_id);
+
/* Returns non-zero if tracking origins. */
int __msan_get_track_origins();
/* Make memory region fully initialized (without changing its contents). */
void __msan_unpoison(const volatile void *a, size_t size);
- /* Make memory region fully uninitialized (without changing its contents). */
+ /* Make a null-terminated string fully initialized (without changing its
+ contents). */
+ void __msan_unpoison_string(const volatile char *a);
+
+ /* Make memory region fully uninitialized (without changing its contents).
+ This is a legacy interface that does not update origin information. Use
+ __msan_allocated_memory() instead. */
void __msan_poison(const volatile void *a, size_t size);
/* Make memory region partially uninitialized (without changing its contents).
memory range, or -1 if the whole range is good. */
intptr_t __msan_test_shadow(const volatile void *x, size_t size);
- /* Set exit code when error(s) were detected.
- Value of 0 means don't change the program exit code. */
- void __msan_set_exit_code(int exit_code);
+ /* Checks that memory range is fully initialized, and reports an error if it
+ * is not. */
+ void __msan_check_mem_is_initialized(const volatile void *x, size_t size);
/* For testing:
__msan_set_expect_umr(1);
modules that were compiled without the corresponding compiler flag. */
void __msan_set_keep_going(int keep_going);
- /* Print shadow and origin for the memory range to stdout in a human-readable
+ /* Print shadow and origin for the memory range to stderr in a human-readable
format. */
void __msan_print_shadow(const volatile void *x, size_t size);
- /* Print current function arguments shadow and origin to stdout in a
+ /* Print shadow for the memory range to stderr in a minimalistic
human-readable format. */
- void __msan_print_param_shadow();
+ void __msan_dump_shadow(const volatile void *x, size_t size);
/* Returns true if running under a dynamic tool (DynamoRio-based). */
int __msan_has_dynamic_component();
Memory will be marked uninitialized, with origin at the call site. */
void __msan_allocated_memory(const volatile void* data, size_t size);
+ /* Tell MSan about newly destroyed memory. Mark memory as uninitialized. */
+ void __sanitizer_dtor_callback(const volatile void* data, size_t size);
+
/* This function may be optionally provided by user and should return
a string containing Msan runtime options. See msan_flags.h for details. */
const char* __msan_default_options();
+ /* Deprecated. Call __sanitizer_set_death_callback instead. */
+ void __msan_set_death_callback(void (*callback)(void));
- /***********************************/
- /* Allocator statistics interface. */
-
- /* Returns the estimated number of bytes that will be reserved by allocator
- for request of "size" bytes. If Msan allocator can't allocate that much
- memory, returns the maximal possible allocation size, otherwise returns
- "size". */
- size_t __msan_get_estimated_allocated_size(size_t size);
-
- /* Returns true if p was returned by the Msan allocator and
- is not yet freed. */
- int __msan_get_ownership(const volatile void *p);
-
- /* Returns the number of bytes reserved for the pointer p.
- Requires (get_ownership(p) == true) or (p == 0). */
- size_t __msan_get_allocated_size(const volatile void *p);
-
- /* Number of bytes, allocated and not yet freed by the application. */
- size_t __msan_get_current_allocated_bytes();
-
- /* Number of bytes, mmaped by msan allocator to fulfill allocation requests.
- Generally, for request of X bytes, allocator can reserve and add to free
- lists a large number of chunks of size X to use them for future requests.
- All these chunks count toward the heap size. Currently, allocator never
- releases memory to OS (instead, it just puts freed chunks to free
- lists). */
- size_t __msan_get_heap_size();
-
- /* Number of bytes, mmaped by msan allocator, which can be used to fulfill
- allocation requests. When a user program frees memory chunk, it can first
- fall into quarantine and will count toward __msan_get_free_bytes()
- later. */
- size_t __msan_get_free_bytes();
-
- /* Number of bytes in unmapped pages, that are released to OS. Currently,
- always returns 0. */
- size_t __msan_get_unmapped_bytes();
-
- /* Malloc hooks that may be optionally provided by user.
- __msan_malloc_hook(ptr, size) is called immediately after
- allocation of "size" bytes, which returned "ptr".
- __msan_free_hook(ptr) is called immediately before
- deallocation of "ptr". */
- void __msan_malloc_hook(const volatile void *ptr, size_t size);
- void __msan_free_hook(const volatile void *ptr);
-
-#else // __has_feature(memory_sanitizer)
-
-#define __msan_get_origin_descr_if_stack(id) ((const char*)0)
-#define __msan_set_origin(a, size, origin)
-#define __msan_get_origin(a) ((uint32_t)-1)
-#define __msan_get_track_origins() (0)
-#define __msan_get_umr_origin() ((uint32_t)-1)
-#define __msan_unpoison(a, size)
-#define __msan_poison(a, size)
-#define __msan_partial_poison(data, shadow, size)
-#define __msan_test_shadow(x, size) ((intptr_t)-1)
-#define __msan_set_exit_code(exit_code)
-#define __msan_set_expect_umr(expect_umr)
-#define __msan_print_shadow(x, size)
-#define __msan_print_param_shadow()
-#define __msan_has_dynamic_component() (0)
-#define __msan_allocated_memory(data, size)
-
-#endif // __has_feature(memory_sanitizer)
-
+ /* Update shadow for the application copy of size bytes from src to dst.
+ Src and dst are application addresses. This function does not copy the
+ actual application memory, it only updates shadow and origin for such
+ copy. Source and destination regions can overlap. */
+ void __msan_copy_shadow(const volatile void *dst, const volatile void *src,
+ size_t size);
#ifdef __cplusplus
} // extern "C"
#endif
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
+// Public interface header for TSan atomics.
//===----------------------------------------------------------------------===//
#ifndef TSAN_INTERFACE_ATOMIC_H
#define TSAN_INTERFACE_ATOMIC_H
-#ifndef INTERFACE_ATTRIBUTE
-# define INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
typedef short __tsan_atomic16; // NOLINT
typedef int __tsan_atomic32;
typedef long __tsan_atomic64; // NOLINT
-
#if defined(__SIZEOF_INT128__) \
|| (__clang_major__ * 100 + __clang_minor__ >= 302)
__extension__ typedef __int128 __tsan_atomic128;
-#define __TSAN_HAS_INT128 1
+# define __TSAN_HAS_INT128 1
#else
-typedef char __tsan_atomic128;
-#define __TSAN_HAS_INT128 0
+# define __TSAN_HAS_INT128 0
#endif
// Part of ABI, do not change.
} __tsan_memory_order;
__tsan_atomic8 __tsan_atomic8_load(const volatile __tsan_atomic8 *a,
- __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo);
__tsan_atomic16 __tsan_atomic16_load(const volatile __tsan_atomic16 *a,
- __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo);
__tsan_atomic32 __tsan_atomic32_load(const volatile __tsan_atomic32 *a,
- __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo);
__tsan_atomic64 __tsan_atomic64_load(const volatile __tsan_atomic64 *a,
- __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo);
+#if __TSAN_HAS_INT128
__tsan_atomic128 __tsan_atomic128_load(const volatile __tsan_atomic128 *a,
- __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo);
+#endif
void __tsan_atomic8_store(volatile __tsan_atomic8 *a, __tsan_atomic8 v,
- __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo);
void __tsan_atomic16_store(volatile __tsan_atomic16 *a, __tsan_atomic16 v,
- __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo);
void __tsan_atomic32_store(volatile __tsan_atomic32 *a, __tsan_atomic32 v,
- __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo);
void __tsan_atomic64_store(volatile __tsan_atomic64 *a, __tsan_atomic64 v,
- __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo);
+#if __TSAN_HAS_INT128
void __tsan_atomic128_store(volatile __tsan_atomic128 *a, __tsan_atomic128 v,
- __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo);
+#endif
__tsan_atomic8 __tsan_atomic8_exchange(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic8 v, __tsan_memory_order mo);
__tsan_atomic16 __tsan_atomic16_exchange(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic16 v, __tsan_memory_order mo);
__tsan_atomic32 __tsan_atomic32_exchange(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic32 v, __tsan_memory_order mo);
__tsan_atomic64 __tsan_atomic64_exchange(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic64 v, __tsan_memory_order mo);
+#if __TSAN_HAS_INT128
__tsan_atomic128 __tsan_atomic128_exchange(volatile __tsan_atomic128 *a,
- __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic128 v, __tsan_memory_order mo);
+#endif
__tsan_atomic8 __tsan_atomic8_fetch_add(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic8 v, __tsan_memory_order mo);
__tsan_atomic16 __tsan_atomic16_fetch_add(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic16 v, __tsan_memory_order mo);
__tsan_atomic32 __tsan_atomic32_fetch_add(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic32 v, __tsan_memory_order mo);
__tsan_atomic64 __tsan_atomic64_fetch_add(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic64 v, __tsan_memory_order mo);
+#if __TSAN_HAS_INT128
__tsan_atomic128 __tsan_atomic128_fetch_add(volatile __tsan_atomic128 *a,
- __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic128 v, __tsan_memory_order mo);
+#endif
__tsan_atomic8 __tsan_atomic8_fetch_sub(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic8 v, __tsan_memory_order mo);
__tsan_atomic16 __tsan_atomic16_fetch_sub(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic16 v, __tsan_memory_order mo);
__tsan_atomic32 __tsan_atomic32_fetch_sub(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic32 v, __tsan_memory_order mo);
__tsan_atomic64 __tsan_atomic64_fetch_sub(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic64 v, __tsan_memory_order mo);
+#if __TSAN_HAS_INT128
__tsan_atomic128 __tsan_atomic128_fetch_sub(volatile __tsan_atomic128 *a,
- __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic128 v, __tsan_memory_order mo);
+#endif
__tsan_atomic8 __tsan_atomic8_fetch_and(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic8 v, __tsan_memory_order mo);
__tsan_atomic16 __tsan_atomic16_fetch_and(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic16 v, __tsan_memory_order mo);
__tsan_atomic32 __tsan_atomic32_fetch_and(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic32 v, __tsan_memory_order mo);
__tsan_atomic64 __tsan_atomic64_fetch_and(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic64 v, __tsan_memory_order mo);
+#if __TSAN_HAS_INT128
__tsan_atomic128 __tsan_atomic128_fetch_and(volatile __tsan_atomic128 *a,
- __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic128 v, __tsan_memory_order mo);
+#endif
__tsan_atomic8 __tsan_atomic8_fetch_or(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic8 v, __tsan_memory_order mo);
__tsan_atomic16 __tsan_atomic16_fetch_or(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic16 v, __tsan_memory_order mo);
__tsan_atomic32 __tsan_atomic32_fetch_or(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic32 v, __tsan_memory_order mo);
__tsan_atomic64 __tsan_atomic64_fetch_or(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic64 v, __tsan_memory_order mo);
+#if __TSAN_HAS_INT128
__tsan_atomic128 __tsan_atomic128_fetch_or(volatile __tsan_atomic128 *a,
- __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic128 v, __tsan_memory_order mo);
+#endif
__tsan_atomic8 __tsan_atomic8_fetch_xor(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic8 v, __tsan_memory_order mo);
__tsan_atomic16 __tsan_atomic16_fetch_xor(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic16 v, __tsan_memory_order mo);
__tsan_atomic32 __tsan_atomic32_fetch_xor(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic32 v, __tsan_memory_order mo);
__tsan_atomic64 __tsan_atomic64_fetch_xor(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic64 v, __tsan_memory_order mo);
+#if __TSAN_HAS_INT128
__tsan_atomic128 __tsan_atomic128_fetch_xor(volatile __tsan_atomic128 *a,
- __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic128 v, __tsan_memory_order mo);
+#endif
__tsan_atomic8 __tsan_atomic8_fetch_nand(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic8 v, __tsan_memory_order mo);
__tsan_atomic16 __tsan_atomic16_fetch_nand(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic16 v, __tsan_memory_order mo);
__tsan_atomic32 __tsan_atomic32_fetch_nand(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic32 v, __tsan_memory_order mo);
__tsan_atomic64 __tsan_atomic64_fetch_nand(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic64 v, __tsan_memory_order mo);
+#if __TSAN_HAS_INT128
__tsan_atomic128 __tsan_atomic128_fetch_nand(volatile __tsan_atomic128 *a,
- __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+ __tsan_atomic128 v, __tsan_memory_order mo);
+#endif
int __tsan_atomic8_compare_exchange_weak(volatile __tsan_atomic8 *a,
__tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo,
- __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order fail_mo);
int __tsan_atomic16_compare_exchange_weak(volatile __tsan_atomic16 *a,
__tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo,
- __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order fail_mo);
int __tsan_atomic32_compare_exchange_weak(volatile __tsan_atomic32 *a,
__tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo,
- __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order fail_mo);
int __tsan_atomic64_compare_exchange_weak(volatile __tsan_atomic64 *a,
__tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo,
- __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order fail_mo);
+#if __TSAN_HAS_INT128
int __tsan_atomic128_compare_exchange_weak(volatile __tsan_atomic128 *a,
__tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo,
- __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order fail_mo);
+#endif
int __tsan_atomic8_compare_exchange_strong(volatile __tsan_atomic8 *a,
__tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo,
- __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order fail_mo);
int __tsan_atomic16_compare_exchange_strong(volatile __tsan_atomic16 *a,
__tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo,
- __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order fail_mo);
int __tsan_atomic32_compare_exchange_strong(volatile __tsan_atomic32 *a,
__tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo,
- __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order fail_mo);
int __tsan_atomic64_compare_exchange_strong(volatile __tsan_atomic64 *a,
__tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo,
- __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order fail_mo);
+#if __TSAN_HAS_INT128
int __tsan_atomic128_compare_exchange_strong(volatile __tsan_atomic128 *a,
__tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo,
- __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order fail_mo);
+#endif
__tsan_atomic8 __tsan_atomic8_compare_exchange_val(
volatile __tsan_atomic8 *a, __tsan_atomic8 c, __tsan_atomic8 v,
- __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
__tsan_atomic16 __tsan_atomic16_compare_exchange_val(
volatile __tsan_atomic16 *a, __tsan_atomic16 c, __tsan_atomic16 v,
- __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
__tsan_atomic32 __tsan_atomic32_compare_exchange_val(
volatile __tsan_atomic32 *a, __tsan_atomic32 c, __tsan_atomic32 v,
- __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
__tsan_atomic64 __tsan_atomic64_compare_exchange_val(
volatile __tsan_atomic64 *a, __tsan_atomic64 c, __tsan_atomic64 v,
- __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+#if __TSAN_HAS_INT128
__tsan_atomic128 __tsan_atomic128_compare_exchange_val(
volatile __tsan_atomic128 *a, __tsan_atomic128 c, __tsan_atomic128 v,
- __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+ __tsan_memory_order mo, __tsan_memory_order fail_mo);
+#endif
-void __tsan_atomic_thread_fence(__tsan_memory_order mo) INTERFACE_ATTRIBUTE;
-void __tsan_atomic_signal_fence(__tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+void __tsan_atomic_thread_fence(__tsan_memory_order mo);
+void __tsan_atomic_signal_fence(__tsan_memory_order mo);
#ifdef __cplusplus
} // extern "C"
#endif
-#undef INTERFACE_ATTRIBUTE
-
-#endif // #ifndef TSAN_INTERFACE_ATOMIC_H
+#endif // TSAN_INTERFACE_ATOMIC_H
--- /dev/null
+#include_next <asm/ptrace.h>
+/* ARM_VFPREGS_SIZE has been added in 3.0 */
+#if defined(__arm__) && !defined(ARM_VFPREGS_SIZE)
+/* The size of the user-visible VFP state as seen by PTRACE_GET/SETVFPREGS
+ and core dumps. */
+#define ARM_VFPREGS_SIZE ( 32 * 8 /*fpregs*/ + 4 /*fpscr*/ )
+#endif
DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++11
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libinterception.la
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
+OBSTACK_DEFS = @OBSTACK_DEFS@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
VERSION = @VERSION@
VIEW_FILE = @VIEW_FILE@
abs_builddir = @abs_builddir@
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
- -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+ -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) \
+ -std=gnu++11
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libinterception.la
interception_files = \
#ifndef INTERCEPTION_H
#define INTERCEPTION_H
-#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
+#if !defined(__linux__) && !defined(__FreeBSD__) && \
+ !defined(__APPLE__) && !defined(_WIN32)
# error "Interception doesn't work on this operating system."
#endif
# define DECLARE_WRAPPER(ret_type, func, ...)
#elif defined(_WIN32)
-# if defined(_DLL) // DLL CRT
-# define WRAP(x) x
-# define WRAPPER_NAME(x) #x
-# define INTERCEPTOR_ATTRIBUTE
-# else // Static CRT
-# define WRAP(x) wrap_##x
-# define WRAPPER_NAME(x) "wrap_"#x
-# define INTERCEPTOR_ATTRIBUTE
-# endif
+# define WRAP(x) __asan_wrap_##x
+# define WRAPPER_NAME(x) "__asan_wrap_"#x
+# define INTERCEPTOR_ATTRIBUTE __declspec(dllexport)
# define DECLARE_WRAPPER(ret_type, func, ...) \
extern "C" ret_type func(__VA_ARGS__);
# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \
extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__);
+#elif defined(__FreeBSD__)
+# define WRAP(x) __interceptor_ ## x
+# define WRAPPER_NAME(x) "__interceptor_" #x
+# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
+// FreeBSD's dynamic linker (incompliantly) gives non-weak symbols higher
+// priority than weak ones so weak aliases won't work for indirect calls
+// in position-independent (-fPIC / -fPIE) mode.
+# define DECLARE_WRAPPER(ret_type, func, ...) \
+ extern "C" ret_type func(__VA_ARGS__) \
+ __attribute__((alias("__interceptor_" #func), visibility("default")));
#else
# define WRAP(x) __interceptor_ ## x
# define WRAPPER_NAME(x) "__interceptor_" #x
namespace __interception { \
FUNC_TYPE(func) PTR_TO_REAL(func); \
} \
- DECLARE_WRAPPER_WINAPI(ret_type, func, __VA_ARGS__) \
extern "C" \
INTERCEPTOR_ATTRIBUTE \
ret_type __stdcall WRAP(func)(__VA_ARGS__)
#define INCLUDED_FROM_INTERCEPTION_LIB
-#if defined(__linux__)
+#if defined(__linux__) || defined(__FreeBSD__)
# include "interception_linux.h"
-# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX(func)
+# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func)
# define INTERCEPT_FUNCTION_VER(func, symver) \
- INTERCEPT_FUNCTION_VER_LINUX(func, symver)
+ INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver)
#elif defined(__APPLE__)
# include "interception_mac.h"
# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func)
// Linux-specific interception methods.
//===----------------------------------------------------------------------===//
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
#include "interception.h"
-#include <dlfcn.h> // for dlsym
+#include <dlfcn.h> // for dlsym() and dlvsym()
namespace __interception {
bool GetRealFunctionAddress(const char *func_name, uptr *func_addr,
} // namespace __interception
-#endif // __linux__
+#endif // __linux__ || __FreeBSD__
// Linux-specific interception methods.
//===----------------------------------------------------------------------===//
-#ifdef __linux__
+#if defined(__linux__) || defined(__FreeBSD__)
#if !defined(INCLUDED_FROM_INTERCEPTION_LIB)
# error "interception_linux.h should be included from interception library only"
void *GetFuncAddrVer(const char *func_name, const char *ver);
} // namespace __interception
-#define INTERCEPT_FUNCTION_LINUX(func) \
- ::__interception::GetRealFunctionAddress( \
- #func, (::__interception::uptr*)&REAL(func), \
- (::__interception::uptr)&(func), \
- (::__interception::uptr)&WRAP(func))
+#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \
+ ::__interception::GetRealFunctionAddress( \
+ #func, (::__interception::uptr *)&__interception::PTR_TO_REAL(func), \
+ (::__interception::uptr) & (func), \
+ (::__interception::uptr) & WRAP(func))
#if !defined(__ANDROID__) // android does not have dlvsym
-# define INTERCEPT_FUNCTION_VER_LINUX(func, symver) \
- ::__interception::real_##func = (func##_f)(unsigned long) \
- ::__interception::GetFuncAddrVer(#func, symver)
+#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
+ (::__interception::real_##func = (func##_f)( \
+ unsigned long)::__interception::GetFuncAddrVer(#func, symver))
#else
-# define INTERCEPT_FUNCTION_VER_LINUX(func, symver) \
- INTERCEPT_FUNCTION_LINUX(func)
+#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
+ INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func)
#endif // !defined(__ANDROID__)
#endif // INTERCEPTION_LINUX_H
-#endif // __linux__
+#endif // __linux__ || __FreeBSD__
#include <stddef.h>
#include <stdint.h>
-COMPILER_CHECK(sizeof(SIZE_T) == sizeof(size_t));
-COMPILER_CHECK(sizeof(SSIZE_T) == sizeof(ssize_t));
-COMPILER_CHECK(sizeof(PTRDIFF_T) == sizeof(ptrdiff_t));
-COMPILER_CHECK(sizeof(INTMAX_T) == sizeof(intmax_t));
+COMPILER_CHECK(sizeof(::SIZE_T) == sizeof(size_t));
+COMPILER_CHECK(sizeof(::SSIZE_T) == sizeof(ssize_t));
+COMPILER_CHECK(sizeof(::PTRDIFF_T) == sizeof(ptrdiff_t));
+COMPILER_CHECK(sizeof(::INTMAX_T) == sizeof(intmax_t));
#ifndef __APPLE__
-COMPILER_CHECK(sizeof(OFF64_T) == sizeof(off64_t));
+COMPILER_CHECK(sizeof(::OFF64_T) == sizeof(off64_t));
#endif
// The following are the cases when pread (and friends) is used instead of
// rest (they depend on _FILE_OFFSET_BITS setting when building an application).
# if defined(__ANDROID__) || !defined _FILE_OFFSET_BITS || \
_FILE_OFFSET_BITS != 64
-COMPILER_CHECK(sizeof(OFF_T) == sizeof(off_t));
+COMPILER_CHECK(sizeof(::OFF_T) == sizeof(off_t));
# endif
#endif
#ifdef _WIN32
#include "interception.h"
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
namespace __interception {
-bool GetRealFunctionAddress(const char *func_name, uptr *func_addr) {
- const char *DLLS[] = {
- "msvcr80.dll",
- "msvcr90.dll",
- "kernel32.dll",
- NULL
- };
- *func_addr = 0;
- for (size_t i = 0; *func_addr == 0 && DLLS[i]; ++i) {
- *func_addr = (uptr)GetProcAddress(GetModuleHandleA(DLLS[i]), func_name);
- }
- return (*func_addr != 0);
-}
-
// FIXME: internal_str* and internal_mem* functions should be moved from the
// ASan sources into interception/.
*(ptrdiff_t*)(jmp_from + 1) = offset;
}
-bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
-#ifdef _WIN64
-# error OverrideFunction was not tested on x64
-#endif
- // Basic idea:
- // We write 5 bytes (jmp-to-new_func) at the beginning of the 'old_func'
- // to override it. We want to be able to execute the original 'old_func' from
- // the wrapper, so we need to keep the leading 5+ bytes ('head') of the
- // original instructions somewhere with a "jmp old_func+head".
- // We call these 'head'+5 bytes of instructions a "trampoline".
-
+static char *GetMemoryForTrampoline(size_t size) {
// Trampolines are allocated from a common pool.
const int POOL_SIZE = 1024;
static char *pool = NULL;
static size_t pool_used = 0;
- if (pool == NULL) {
- pool = (char*)VirtualAlloc(NULL, POOL_SIZE,
- MEM_RESERVE | MEM_COMMIT,
- PAGE_EXECUTE_READWRITE);
- // FIXME: set PAGE_EXECUTE_READ access after setting all interceptors?
- if (pool == NULL)
- return false;
+ if (!pool) {
+ pool = (char *)VirtualAlloc(NULL, POOL_SIZE, MEM_RESERVE | MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE);
+ // FIXME: Might want to apply PAGE_EXECUTE_READ access after all the
+ // interceptors are in place.
+ if (!pool)
+ return NULL;
_memset(pool, 0xCC /* int 3 */, POOL_SIZE);
}
- char* old_bytes = (char*)old_func;
- char* trampoline = pool + pool_used;
+ if (pool_used + size > POOL_SIZE)
+ return NULL;
+
+ char *ret = pool + pool_used;
+ pool_used += size;
+ return ret;
+}
- // Find out the number of bytes of the instructions we need to copy to the
- // island and store it in 'head'.
- size_t head = 0;
- while (head < 5) {
- switch (old_bytes[head]) {
+// Returns 0 on error.
+static size_t RoundUpToInstrBoundary(size_t size, char *code) {
+ size_t cursor = 0;
+ while (cursor < size) {
+ switch (code[cursor]) {
+ case '\x51': // push ecx
+ case '\x52': // push edx
+ case '\x53': // push ebx
+ case '\x54': // push esp
case '\x55': // push ebp
case '\x56': // push esi
case '\x57': // push edi
- head++;
+ case '\x5D': // pop ebp
+ cursor++;
+ continue;
+ case '\x6A': // 6A XX = push XX
+ cursor += 2;
+ continue;
+ case '\xE9': // E9 XX YY ZZ WW = jmp WWZZYYXX
+ case '\xB8': // B8 XX YY ZZ WW = mov eax, WWZZYYXX
+ cursor += 5;
continue;
}
- switch (*(unsigned short*)(old_bytes + head)) { // NOLINT
+ switch (*(unsigned short*)(code + cursor)) { // NOLINT
case 0xFF8B: // 8B FF = mov edi, edi
case 0xEC8B: // 8B EC = mov ebp, esp
case 0xC033: // 33 C0 = xor eax, eax
- head += 2;
+ cursor += 2;
continue;
+ case 0x458B: // 8B 45 XX = mov eax, dword ptr [ebp+XXh]
+ case 0x5D8B: // 8B 5D XX = mov ebx, dword ptr [ebp+XXh]
case 0xEC83: // 83 EC XX = sub esp, XX
- head += 3;
+ case 0x75FF: // FF 75 XX = push dword ptr [ebp+XXh]
+ cursor += 3;
continue;
case 0xC1F7: // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX
- head += 6;
+ case 0x25FF: // FF 25 XX YY ZZ WW = jmp dword ptr ds:[WWZZYYXX]
+ cursor += 6;
+ continue;
+ case 0x3D83: // 83 3D XX YY ZZ WW TT = cmp TT, WWZZYYXX
+ cursor += 7;
continue;
}
- switch (0x00FFFFFF & *(unsigned int*)(old_bytes + head)) {
+ switch (0x00FFFFFF & *(unsigned int*)(code + cursor)) {
case 0x24448A: // 8A 44 24 XX = mov eal, dword ptr [esp+XXh]
+ case 0x24448B: // 8B 44 24 XX = mov eax, dword ptr [esp+XXh]
case 0x244C8B: // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh]
case 0x24548B: // 8B 54 24 XX = mov edx, dword ptr [esp+XXh]
+ case 0x24748B: // 8B 74 24 XX = mov esi, dword ptr [esp+XXh]
case 0x247C8B: // 8B 7C 24 XX = mov edi, dword ptr [esp+XXh]
- head += 4;
+ cursor += 4;
continue;
}
// Unknown instruction!
- return false;
+ // FIXME: Unknown instruction failures might happen when we add a new
+ // interceptor or a new compiler version. In either case, they should result
+ // in visible and readable error messages. However, merely calling abort()
+ // leads to an infinite recursion in CheckFailed.
+ // Do we have a good way to abort with an error message here?
+ __debugbreak();
+ return 0;
}
- if (pool_used + head + 5 > POOL_SIZE)
- return false;
+ return cursor;
+}
+
+bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
+#ifdef _WIN64
+#error OverrideFunction is not yet supported on x64
+#endif
+ // Function overriding works basically like this:
+ // We write "jmp <new_func>" (5 bytes) at the beginning of the 'old_func'
+ // to override it.
+ // We might want to be able to execute the original 'old_func' from the
+ // wrapper, in this case we need to keep the leading 5+ bytes ('head')
+ // of the original code somewhere with a "jmp <old_func+head>".
+ // We call these 'head'+5 bytes of instructions a "trampoline".
+ char *old_bytes = (char *)old_func;
+
+ // We'll need at least 5 bytes for a 'jmp'.
+ size_t head = 5;
+ if (orig_old_func) {
+ // Find out the number of bytes of the instructions we need to copy
+ // to the trampoline and store it in 'head'.
+ head = RoundUpToInstrBoundary(head, old_bytes);
+ if (!head)
+ return false;
- // Now put the "jump to trampoline" instruction into the original code.
+ // Put the needed instructions into the trampoline bytes.
+ char *trampoline = GetMemoryForTrampoline(head + 5);
+ if (!trampoline)
+ return false;
+ _memcpy(trampoline, old_bytes, head);
+ WriteJumpInstruction(trampoline + head, old_bytes + head);
+ *orig_old_func = (uptr)trampoline;
+ }
+
+ // Now put the "jmp <new_func>" instruction at the original code location.
+ // We should preserve the EXECUTE flag as some of our own code might be
+ // located in the same page (sic!). FIXME: might consider putting the
+ // __interception code into a separate section or something?
DWORD old_prot, unused_prot;
- if (!VirtualProtect((void*)old_func, head, PAGE_EXECUTE_READWRITE,
+ if (!VirtualProtect((void *)old_bytes, head, PAGE_EXECUTE_READWRITE,
&old_prot))
return false;
- // Put the needed instructions into the trampoline bytes.
- _memcpy(trampoline, old_bytes, head);
- WriteJumpInstruction(trampoline + head, old_bytes + head);
- *orig_old_func = (uptr)trampoline;
- pool_used += head + 5;
-
- // Intercept the 'old_func'.
- WriteJumpInstruction(old_bytes, (char*)new_func);
+ WriteJumpInstruction(old_bytes, (char *)new_func);
_memset(old_bytes + 5, 0xCC /* int 3 */, head - 5);
- if (!VirtualProtect((void*)old_func, head, old_prot, &unused_prot))
+ // Restore the original permissions.
+ if (!VirtualProtect((void *)old_bytes, head, old_prot, &unused_prot))
return false; // not clear if this failure bothers us.
return true;
}
+static void **InterestingDLLsAvailable() {
+ const char *InterestingDLLs[] = {
+ "kernel32.dll",
+ "msvcr110.dll", // VS2012
+ "msvcr120.dll", // VS2013
+ // NTDLL should go last as it exports some functions that we should override
+ // in the CRT [presumably only used internally].
+ "ntdll.dll", NULL
+ };
+ static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
+ if (!result[0]) {
+ for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
+ if (HMODULE h = GetModuleHandleA(InterestingDLLs[i]))
+ result[j++] = (void *)h;
+ }
+ }
+ return &result[0];
+}
+
+namespace {
+// Utility for reading loaded PE images.
+template <typename T> class RVAPtr {
+ public:
+ RVAPtr(void *module, uptr rva)
+ : ptr_(reinterpret_cast<T *>(reinterpret_cast<char *>(module) + rva)) {}
+ operator T *() { return ptr_; }
+ T *operator->() { return ptr_; }
+ T *operator++() { return ++ptr_; }
+
+ private:
+ T *ptr_;
+};
+} // namespace
+
+// Internal implementation of GetProcAddress. At least since Windows 8,
+// GetProcAddress appears to initialize DLLs before returning function pointers
+// into them. This is problematic for the sanitizers, because they typically
+// want to intercept malloc *before* MSVCRT initializes. Our internal
+// implementation walks the export list manually without doing initialization.
+uptr InternalGetProcAddress(void *module, const char *func_name) {
+ // Check that the module header is full and present.
+ RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0);
+ RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew);
+ if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ"
+ headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0"
+ headers->FileHeader.SizeOfOptionalHeader <
+ sizeof(IMAGE_OPTIONAL_HEADER)) {
+ return 0;
+ }
+
+ IMAGE_DATA_DIRECTORY *export_directory =
+ &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
+ RVAPtr<IMAGE_EXPORT_DIRECTORY> exports(module,
+ export_directory->VirtualAddress);
+ RVAPtr<DWORD> functions(module, exports->AddressOfFunctions);
+ RVAPtr<DWORD> names(module, exports->AddressOfNames);
+ RVAPtr<WORD> ordinals(module, exports->AddressOfNameOrdinals);
+
+ for (DWORD i = 0; i < exports->NumberOfNames; i++) {
+ RVAPtr<char> name(module, names[i]);
+ if (!strcmp(func_name, name)) {
+ DWORD index = ordinals[i];
+ RVAPtr<char> func(module, functions[index]);
+ return (uptr)(char *)func;
+ }
+ }
+
+ return 0;
+}
+
+static bool GetFunctionAddressInDLLs(const char *func_name, uptr *func_addr) {
+ *func_addr = 0;
+ void **DLLs = InterestingDLLsAvailable();
+ for (size_t i = 0; *func_addr == 0 && DLLs[i]; ++i)
+ *func_addr = InternalGetProcAddress(DLLs[i], func_name);
+ return (*func_addr != 0);
+}
+
+bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func) {
+ uptr orig_func;
+ if (!GetFunctionAddressInDLLs(name, &orig_func))
+ return false;
+ return OverrideFunction(orig_func, new_func, orig_old_func);
+}
+
} // namespace __interception
#endif // _WIN32
#define INTERCEPTION_WIN_H
namespace __interception {
-// returns true if a function with the given name was found.
-bool GetRealFunctionAddress(const char *func_name, uptr *func_addr);
+// All the functions in the OverrideFunction() family return true on success,
+// false on failure (including "couldn't find the function").
+
+// Overrides a function by its address.
+bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func = 0);
+
+// Overrides a function in a system DLL or DLL CRT by its exported name.
+bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func = 0);
+
+// Windows-only replacement for GetProcAddress. Useful for some sanitizers.
+uptr InternalGetProcAddress(void *module, const char *func_name);
-// returns true if the old function existed, false on failure.
-bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func);
} // namespace __interception
-#if defined(_DLL)
-# define INTERCEPT_FUNCTION_WIN(func) \
- ::__interception::GetRealFunctionAddress( \
- #func, (::__interception::uptr*)&REAL(func))
+#if defined(INTERCEPTION_DYNAMIC_CRT)
+#define INTERCEPT_FUNCTION_WIN(func) \
+ ::__interception::OverrideFunction(#func, \
+ (::__interception::uptr)WRAP(func), \
+ (::__interception::uptr *)&REAL(func))
#else
-# define INTERCEPT_FUNCTION_WIN(func) \
- ::__interception::OverrideFunction( \
- (::__interception::uptr)func, \
- (::__interception::uptr)WRAP(func), \
- (::__interception::uptr*)&REAL(func))
+#define INTERCEPT_FUNCTION_WIN(func) \
+ ::__interception::OverrideFunction((::__interception::uptr)func, \
+ (::__interception::uptr)WRAP(func), \
+ (::__interception::uptr *)&REAL(func))
#endif
-#define INTERCEPT_FUNCTION_VER_WIN(func, symver) \
- INTERCEPT_FUNCTION_WIN(func)
+#define INTERCEPT_FUNCTION_VER_WIN(func, symver) INTERCEPT_FUNCTION_WIN(func)
#endif // INTERCEPTION_WIN_H
#endif // _WIN32
CXX_WARN_FLAGS = $(WARN_FLAGS) -Wno-unused-parameter
AM_CFLAGS = $(C_WARN_FLAGS)
AM_CXXFLAGS = $(CXX_WARN_FLAGS) -fno-rtti -fno-exceptions
+AM_CXXFLAGS += -std=gnu++11
noinst_LTLIBRARIES = libsanitizer_libbacktrace.la
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
+OBSTACK_DEFS = @OBSTACK_DEFS@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
VERSION = @VERSION@
VIEW_FILE = @VIEW_FILE@
abs_builddir = @abs_builddir@
C_WARN_FLAGS = $(WARN_FLAGS) -Wstrict-prototypes -Wmissing-prototypes -Wold-style-definition
CXX_WARN_FLAGS = $(WARN_FLAGS) -Wno-unused-parameter
AM_CFLAGS = $(C_WARN_FLAGS)
-AM_CXXFLAGS = $(CXX_WARN_FLAGS) -fno-rtti -fno-exceptions
+AM_CXXFLAGS = $(CXX_WARN_FLAGS) -fno-rtti -fno-exceptions -std=gnu++11
noinst_LTLIBRARIES = libsanitizer_libbacktrace.la
libsanitizer_libbacktrace_la_SOURCES = \
../../libbacktrace/backtrace.h \
DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++11
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libsanitizer_lsan.la
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
+OBSTACK_DEFS = @OBSTACK_DEFS@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
VERSION = @VERSION@
VIEW_FILE = @VIEW_FILE@
abs_builddir = @abs_builddir@
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
- -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+ -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) \
+ -std=gnu++11
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libsanitizer_lsan.la
@LSAN_SUPPORTED_TRUE@toolexeclib_LTLIBRARIES = liblsan.la
#include "lsan.h"
#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "lsan_allocator.h"
#include "lsan_common.h"
namespace __lsan {
-static void InitializeCommonFlags() {
- CommonFlags *cf = common_flags();
- SetCommonFlagsDefaults(cf);
- cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
- cf->malloc_context_size = 30;
- cf->detect_leaks = true;
-
- ParseCommonFlagsFromString(cf, GetEnv("LSAN_OPTIONS"));
+///// Interface to the common LSan module. /////
+bool WordIsPoisoned(uptr addr) {
+ return false;
}
} // namespace __lsan
using namespace __lsan; // NOLINT
+static void InitializeFlags() {
+ // Set all the default values.
+ SetCommonFlagsDefaults();
+ {
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
+ cf.malloc_context_size = 30;
+ cf.detect_leaks = true;
+ cf.exitcode = 23;
+ OverrideCommonFlags(cf);
+ }
+
+ Flags *f = flags();
+ f->SetDefaults();
+
+ FlagParser parser;
+ RegisterLsanFlags(&parser, f);
+ RegisterCommonFlags(&parser);
+
+ parser.ParseString(GetEnv("LSAN_OPTIONS"));
+
+ SetVerbosity(common_flags()->verbosity);
+
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (common_flags()->help) parser.PrintFlagDescriptions();
+}
+
extern "C" void __lsan_init() {
CHECK(!lsan_init_is_running);
if (lsan_inited)
return;
lsan_init_is_running = true;
SanitizerToolName = "LeakSanitizer";
- InitializeCommonFlags();
+ CacheBinaryName();
+ InitializeFlags();
+ InitCommonLsan();
InitializeAllocator();
InitTlsSize();
InitializeInterceptors();
ThreadStart(tid, GetTid());
SetCurrentThread(tid);
- // Start symbolizer process if necessary.
- if (common_flags()->symbolize) {
- Symbolizer::Init(common_flags()->external_symbolizer_path);
- } else {
- Symbolizer::Disable();
- }
-
- InitCommonLsan();
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
Atexit(DoLeakCheck);
+
+ InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
+
lsan_inited = true;
lsan_init_is_running = false;
}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+ GET_STACK_TRACE_FATAL;
+ stack.Print();
+}
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
+#define GET_STACK_TRACE(max_size, fast) \
+ BufferedStackTrace stack; \
+ { \
+ uptr stack_top = 0, stack_bottom = 0; \
+ ThreadContext *t; \
+ if (fast && (t = CurrentThreadContext())) { \
+ stack_top = t->stack_end(); \
+ stack_bottom = t->stack_begin(); \
+ } \
+ stack.Unwind(max_size, StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \
+ /* context */ 0, stack_top, stack_bottom, fast); \
+ }
+
+#define GET_STACK_TRACE_FATAL \
+ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
+
+#define GET_STACK_TRACE_MALLOC \
+ GET_STACK_TRACE(__sanitizer::common_flags()->malloc_context_size, \
+ common_flags()->fast_unwind_on_malloc)
+
namespace __lsan {
void InitializeInterceptors();
#include "lsan_allocator.h"
#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
namespace __lsan {
-static const uptr kMaxAllowedMallocSize = 8UL << 30;
-static const uptr kAllocatorSpace = 0x600000000000ULL;
-static const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
-
struct ChunkMetadata {
- bool allocated : 8; // Must be first.
+ u8 allocated : 8; // Must be first.
ChunkTag tag : 2;
uptr requested_size : 54;
u32 stack_trace_id;
};
+#if defined(__mips64) || defined(__aarch64__)
+static const uptr kMaxAllowedMallocSize = 4UL << 30;
+static const uptr kRegionSizeLog = 20;
+static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
+typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
+typedef CompactSizeClassMap SizeClassMap;
+typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE,
+ sizeof(ChunkMetadata), SizeClassMap, kRegionSizeLog, ByteMap>
+ PrimaryAllocator;
+#else
+static const uptr kMaxAllowedMallocSize = 8UL << 30;
+static const uptr kAllocatorSpace = 0x600000000000ULL;
+static const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize,
sizeof(ChunkMetadata), DefaultSizeClassMap> PrimaryAllocator;
+#endif
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
typedef LargeMmapAllocator<> SecondaryAllocator;
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
static THREADLOCAL AllocatorCache cache;
void InitializeAllocator() {
- allocator.Init();
+ allocator.InitLinkerInitialized(common_flags()->allocator_may_return_null);
}
void AllocatorThreadFinish() {
allocator.SwallowCache(&cache);
}
-static ChunkMetadata *Metadata(void *p) {
+static ChunkMetadata *Metadata(const void *p) {
return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
}
ChunkMetadata *m = Metadata(p);
CHECK(m);
m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked;
- m->stack_trace_id = StackDepotPut(stack.trace, stack.size);
+ m->stack_trace_id = StackDepotPut(stack);
m->requested_size = size;
atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed);
}
size = 1;
if (size > kMaxAllowedMallocSize) {
Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size);
- return 0;
+ return nullptr;
}
void *p = allocator.Allocate(&cache, size, alignment, false);
// Do not rely on the allocator to clear the memory (it's slow).
if (cleared && allocator.FromPrimary(p))
memset(p, 0, size);
RegisterAllocation(stack, p, size);
+ if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size);
return p;
}
void Deallocate(void *p) {
+ if (&__sanitizer_free_hook) __sanitizer_free_hook(p);
RegisterDeallocation(p);
allocator.Deallocate(&cache, p);
}
if (new_size > kMaxAllowedMallocSize) {
Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size);
allocator.Deallocate(&cache, p);
- return 0;
+ return nullptr;
}
p = allocator.Reallocate(&cache, p, new_size, alignment);
RegisterAllocation(stack, p, new_size);
*end = *begin + sizeof(cache);
}
-uptr GetMallocUsableSize(void *p) {
+uptr GetMallocUsableSize(const void *p) {
ChunkMetadata *m = Metadata(p);
if (!m) return 0;
return m->requested_size;
if (addr < chunk) return 0;
ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk));
CHECK(m);
- if (m->allocated && addr < chunk + m->requested_size)
+ if (!m->allocated)
+ return 0;
+ if (addr < chunk + m->requested_size)
+ return chunk;
+ if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr))
return chunk;
return 0;
}
return kIgnoreObjectInvalid;
}
}
-} // namespace __lsan
+} // namespace __lsan
+
+using namespace __lsan;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_current_allocated_bytes() {
+ uptr stats[AllocatorStatCount];
+ allocator.GetStats(stats);
+ return stats[AllocatorStatAllocated];
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_heap_size() {
+ uptr stats[AllocatorStatCount];
+ allocator.GetStats(stats);
+ return stats[AllocatorStatMapped];
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_free_bytes() { return 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_unmapped_bytes() { return 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_get_ownership(const void *p) { return Metadata(p) != nullptr; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_allocated_size(const void *p) {
+ return GetMallocUsableSize(p);
+}
+} // extern "C"
void Deallocate(void *p);
void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
uptr alignment);
-uptr GetMallocUsableSize(void *p);
+uptr GetMallocUsableSize(const void *p);
template<typename Callable>
void ForEachChunk(const Callable &callback);
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_stoptheworld.h"
#include "sanitizer_common/sanitizer_suppressions.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#if CAN_SANITIZE_LEAKS
namespace __lsan {
-// This mutex is used to prevent races between DoLeakCheck and IgnoreObject.
+// This mutex is used to prevent races between DoLeakCheck and IgnoreObject, and
+// also to protect the global list of root regions.
BlockingMutex global_mutex(LINKER_INITIALIZED);
THREADLOCAL int disable_counter;
Flags lsan_flags;
-static void InitializeFlags() {
- Flags *f = flags();
- // Default values.
- f->report_objects = false;
- f->resolution = 0;
- f->max_leaks = 0;
- f->exitcode = 23;
- f->suppressions="";
- f->use_registers = true;
- f->use_globals = true;
- f->use_stacks = true;
- f->use_tls = true;
- f->use_unaligned = false;
- f->verbosity = 0;
- f->log_pointers = false;
- f->log_threads = false;
-
- const char *options = GetEnv("LSAN_OPTIONS");
- if (options) {
- ParseFlag(options, &f->use_registers, "use_registers");
- ParseFlag(options, &f->use_globals, "use_globals");
- ParseFlag(options, &f->use_stacks, "use_stacks");
- ParseFlag(options, &f->use_tls, "use_tls");
- ParseFlag(options, &f->use_unaligned, "use_unaligned");
- ParseFlag(options, &f->report_objects, "report_objects");
- ParseFlag(options, &f->resolution, "resolution");
- CHECK_GE(&f->resolution, 0);
- ParseFlag(options, &f->max_leaks, "max_leaks");
- CHECK_GE(&f->max_leaks, 0);
- ParseFlag(options, &f->verbosity, "verbosity");
- ParseFlag(options, &f->log_pointers, "log_pointers");
- ParseFlag(options, &f->log_threads, "log_threads");
- ParseFlag(options, &f->exitcode, "exitcode");
- ParseFlag(options, &f->suppressions, "suppressions");
- }
+void Flags::SetDefaults() {
+#define LSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "lsan_flags.inc"
+#undef LSAN_FLAG
+}
+
+void RegisterLsanFlags(FlagParser *parser, Flags *f) {
+#define LSAN_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &f->Name);
+#include "lsan_flags.inc"
+#undef LSAN_FLAG
}
-SuppressionContext *suppression_ctx;
+#define LOG_POINTERS(...) \
+ do { \
+ if (flags()->log_pointers) Report(__VA_ARGS__); \
+ } while (0);
+
+#define LOG_THREADS(...) \
+ do { \
+ if (flags()->log_threads) Report(__VA_ARGS__); \
+ } while (0);
+
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char kSuppressionLeak[] = "leak";
+static const char *kSuppressionTypes[] = { kSuppressionLeak };
void InitializeSuppressions() {
- CHECK(!suppression_ctx);
- ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)];
- suppression_ctx = new(placeholder_) SuppressionContext;
- char *suppressions_from_file;
- uptr buffer_size;
- if (ReadFileToBuffer(flags()->suppressions, &suppressions_from_file,
- &buffer_size, 1 << 26 /* max_len */))
- suppression_ctx->Parse(suppressions_from_file);
- if (flags()->suppressions[0] && !buffer_size) {
- Printf("LeakSanitizer: failed to read suppressions file '%s'\n",
- flags()->suppressions);
- Die();
- }
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
if (&__lsan_default_suppressions)
suppression_ctx->Parse(__lsan_default_suppressions());
}
+static SuppressionContext *GetSuppressionContext() {
+ CHECK(suppression_ctx);
+ return suppression_ctx;
+}
+
+struct RootRegion {
+ const void *begin;
+ uptr size;
+};
+
+InternalMmapVector<RootRegion> *root_regions;
+
+void InitializeRootRegions() {
+ CHECK(!root_regions);
+ ALIGNED(64) static char placeholder[sizeof(InternalMmapVector<RootRegion>)];
+ root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
+}
+
void InitCommonLsan() {
- InitializeFlags();
+ InitializeRootRegions();
if (common_flags()->detect_leaks) {
// Initialization which can fail or print warnings should only be done if
// LSan is actually enabled.
}
}
-class Decorator: private __sanitizer::AnsiColorDecorator {
+class Decorator: public __sanitizer::SanitizerCommonDecorator {
public:
- Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
+ Decorator() : SanitizerCommonDecorator() { }
const char *Error() { return Red(); }
const char *Leak() { return Blue(); }
const char *End() { return Default(); }
// bound on heap addresses.
const uptr kMinAddress = 4 * 4096;
if (p < kMinAddress) return false;
-#ifdef __x86_64__
+#if defined(__x86_64__)
// Accept only canonical form user-space addresses.
return ((p >> 47) == 0);
+#elif defined(__mips64)
+ return ((p >> 40) == 0);
+#elif defined(__aarch64__)
+ unsigned runtimeVMA =
+ (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
+ return ((p >> runtimeVMA) == 0);
#else
return true;
#endif
// Scans the memory range, looking for byte patterns that point into allocator
// chunks. Marks those chunks with |tag| and adds them to |frontier|.
-// There are two usage modes for this function: finding reachable or ignored
-// chunks (|tag| = kReachable or kIgnored) and finding indirectly leaked chunks
+// There are two usage modes for this function: finding reachable chunks
+// (|tag| = kReachable) and finding indirectly leaked chunks
// (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill,
// so |frontier| = 0.
void ScanRangeForPointers(uptr begin, uptr end,
Frontier *frontier,
const char *region_type, ChunkTag tag) {
+ CHECK(tag == kReachable || tag == kIndirectlyLeaked);
const uptr alignment = flags()->pointer_alignment();
- if (flags()->log_pointers)
- Report("Scanning %s range %p-%p.\n", region_type, begin, end);
+ LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end);
uptr pp = begin;
if (pp % alignment)
pp = pp + alignment - pp % alignment;
// Pointers to self don't count. This matters when tag == kIndirectlyLeaked.
if (chunk == begin) continue;
LsanMetadata m(chunk);
- // Reachable beats ignored beats leaked.
- if (m.tag() == kReachable) continue;
- if (m.tag() == kIgnored && tag != kReachable) continue;
+ if (m.tag() == kReachable || m.tag() == kIgnored) continue;
+
+ // Do this check relatively late so we can log only the interesting cases.
+ if (!flags()->use_poisoned && WordIsPoisoned(pp)) {
+ LOG_POINTERS(
+ "%p is poisoned: ignoring %p pointing into chunk %p-%p of size "
+ "%zu.\n",
+ pp, p, chunk, chunk + m.requested_size(), m.requested_size());
+ continue;
+ }
+
m.set_tag(tag);
- if (flags()->log_pointers)
- Report("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p,
- chunk, chunk + m.requested_size(), m.requested_size());
+ LOG_POINTERS("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p,
+ chunk, chunk + m.requested_size(), m.requested_size());
if (frontier)
frontier->push_back(chunk);
}
uptr registers_end = registers_begin + registers.size();
for (uptr i = 0; i < suspended_threads.thread_count(); i++) {
uptr os_id = static_cast<uptr>(suspended_threads.GetThreadID(i));
- if (flags()->log_threads) Report("Processing thread %d.\n", os_id);
+ LOG_THREADS("Processing thread %d.\n", os_id);
uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end,
&tls_begin, &tls_end,
if (!thread_found) {
// If a thread can't be found in the thread registry, it's probably in the
// process of destruction. Log this event and move on.
- if (flags()->log_threads)
- Report("Thread %d not found in registry.\n", os_id);
+ LOG_THREADS("Thread %d not found in registry.\n", os_id);
continue;
}
uptr sp;
"REGISTERS", kReachable);
if (flags()->use_stacks) {
- if (flags()->log_threads)
- Report("Stack at %p-%p, SP = %p.\n", stack_begin, stack_end, sp);
+ LOG_THREADS("Stack at %p-%p (SP = %p).\n", stack_begin, stack_end, sp);
if (sp < stack_begin || sp >= stack_end) {
// SP is outside the recorded stack range (e.g. the thread is running a
// signal handler on alternate stack). Again, consider the entire stack
// range to be reachable.
- if (flags()->log_threads)
- Report("WARNING: stack pointer not in stack range.\n");
+ LOG_THREADS("WARNING: stack pointer not in stack range.\n");
} else {
// Shrink the stack range to ignore out-of-scope values.
stack_begin = sp;
}
if (flags()->use_tls) {
- if (flags()->log_threads) Report("TLS at %p-%p.\n", tls_begin, tls_end);
+ LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end);
if (cache_begin == cache_end) {
ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable);
} else {
}
}
+static void ProcessRootRegion(Frontier *frontier, uptr root_begin,
+ uptr root_end) {
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ uptr begin, end, prot;
+ while (proc_maps.Next(&begin, &end,
+ /*offset*/ nullptr, /*filename*/ nullptr,
+ /*filename_size*/ 0, &prot)) {
+ uptr intersection_begin = Max(root_begin, begin);
+ uptr intersection_end = Min(end, root_end);
+ if (intersection_begin >= intersection_end) continue;
+ bool is_readable = prot & MemoryMappingLayout::kProtectionRead;
+ LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n",
+ root_begin, root_end, begin, end,
+ is_readable ? "readable" : "unreadable");
+ if (is_readable)
+ ScanRangeForPointers(intersection_begin, intersection_end, frontier,
+ "ROOT", kReachable);
+ }
+}
+
+// Scans root regions for heap pointers.
+static void ProcessRootRegions(Frontier *frontier) {
+ if (!flags()->use_root_regions) return;
+ CHECK(root_regions);
+ for (uptr i = 0; i < root_regions->size(); i++) {
+ RootRegion region = (*root_regions)[i];
+ uptr begin_addr = reinterpret_cast<uptr>(region.begin);
+ ProcessRootRegion(frontier, begin_addr, begin_addr + region.size);
+ }
+}
+
static void FloodFillTag(Frontier *frontier, ChunkTag tag) {
while (frontier->size()) {
uptr next_chunk = frontier->back();
LsanMetadata m(chunk);
if (m.allocated() && m.tag() != kReachable) {
ScanRangeForPointers(chunk, chunk + m.requested_size(),
- /* frontier */ 0, "HEAP", kIndirectlyLeaked);
+ /* frontier */ nullptr, "HEAP", kIndirectlyLeaked);
}
}
CHECK(arg);
chunk = GetUserBegin(chunk);
LsanMetadata m(chunk);
- if (m.allocated() && m.tag() == kIgnored)
+ if (m.allocated() && m.tag() == kIgnored) {
+ LOG_POINTERS("Ignored: chunk %p-%p of size %zu.\n",
+ chunk, chunk + m.requested_size(), m.requested_size());
reinterpret_cast<Frontier *>(arg)->push_back(chunk);
+ }
}
// Sets the appropriate tag on each chunk.
static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
// Holds the flood fill frontier.
- Frontier frontier(GetPageSizeCached());
+ Frontier frontier(1);
- if (flags()->use_globals)
- ProcessGlobalRegions(&frontier);
+ ForEachChunk(CollectIgnoredCb, &frontier);
+ ProcessGlobalRegions(&frontier);
ProcessThreads(suspended_threads, &frontier);
+ ProcessRootRegions(&frontier);
FloodFillTag(&frontier, kReachable);
+
// The check here is relatively expensive, so we do this in a separate flood
// fill. That way we can skip the check for chunks that are reachable
// otherwise.
- if (flags()->log_pointers)
- Report("Processing platform-specific allocations.\n");
+ LOG_POINTERS("Processing platform-specific allocations.\n");
+ CHECK_EQ(0, frontier.size());
ProcessPlatformSpecificAllocations(&frontier);
FloodFillTag(&frontier, kReachable);
- if (flags()->log_pointers)
- Report("Scanning ignored chunks.\n");
- CHECK_EQ(0, frontier.size());
- ForEachChunk(CollectIgnoredCb, &frontier);
- FloodFillTag(&frontier, kIgnored);
-
// Iterate over leaked chunks and mark those that are reachable from other
// leaked chunks.
- if (flags()->log_pointers)
- Report("Scanning leaked chunks.\n");
- ForEachChunk(MarkIndirectlyLeakedCb, 0 /* arg */);
+ LOG_POINTERS("Scanning leaked chunks.\n");
+ ForEachChunk(MarkIndirectlyLeakedCb, nullptr);
+}
+
+// ForEachChunk callback. Resets the tags to pre-leak-check state.
+static void ResetTagsCb(uptr chunk, void *arg) {
+ (void)arg;
+ chunk = GetUserBegin(chunk);
+ LsanMetadata m(chunk);
+ if (m.allocated() && m.tag() != kIgnored)
+ m.set_tag(kDirectlyLeaked);
}
static void PrintStackTraceById(u32 stack_trace_id) {
CHECK(stack_trace_id);
- uptr size = 0;
- const uptr *trace = StackDepotGet(stack_trace_id, &size);
- StackTrace::PrintStack(trace, size);
+ StackDepotGet(stack_trace_id).Print();
}
-// ForEachChunk callback. Aggregates unreachable chunks into a LeakReport.
+// ForEachChunk callback. Aggregates information about unreachable chunks into
+// a LeakReport.
static void CollectLeaksCb(uptr chunk, void *arg) {
CHECK(arg);
LeakReport *leak_report = reinterpret_cast<LeakReport *>(arg);
LsanMetadata m(chunk);
if (!m.allocated()) return;
if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) {
- uptr resolution = flags()->resolution;
+ u32 resolution = flags()->resolution;
+ u32 stack_trace_id = 0;
if (resolution > 0) {
- uptr size = 0;
- const uptr *trace = StackDepotGet(m.stack_trace_id(), &size);
- size = Min(size, resolution);
- leak_report->Add(StackDepotPut(trace, size), m.requested_size(), m.tag());
+ StackTrace stack = StackDepotGet(m.stack_trace_id());
+ stack.size = Min(stack.size, resolution);
+ stack_trace_id = StackDepotPut(stack);
} else {
- leak_report->Add(m.stack_trace_id(), m.requested_size(), m.tag());
+ stack_trace_id = m.stack_trace_id();
}
- }
-}
-
-// ForEachChunkCallback. Prints addresses of unreachable chunks.
-static void PrintLeakedCb(uptr chunk, void *arg) {
- chunk = GetUserBegin(chunk);
- LsanMetadata m(chunk);
- if (!m.allocated()) return;
- if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) {
- Printf("%s leaked %zu byte object at %p.\n",
- m.tag() == kDirectlyLeaked ? "Directly" : "Indirectly",
- m.requested_size(), chunk);
+ leak_report->AddLeakedChunk(chunk, stack_trace_id, m.requested_size(),
+ m.tag());
}
}
static void PrintMatchedSuppressions() {
InternalMmapVector<Suppression *> matched(1);
- suppression_ctx->GetMatched(&matched);
+ GetSuppressionContext()->GetMatched(&matched);
if (!matched.size())
return;
const char *line = "-----------------------------------------------------";
Printf("Suppressions used:\n");
Printf(" count bytes template\n");
for (uptr i = 0; i < matched.size(); i++)
- Printf("%7zu %10zu %s\n", static_cast<uptr>(matched[i]->hit_count),
- matched[i]->weight, matched[i]->templ);
+ Printf("%7zu %10zu %s\n", static_cast<uptr>(atomic_load_relaxed(
+ &matched[i]->hit_count)), matched[i]->weight, matched[i]->templ);
Printf("%s\n\n", line);
}
-static void PrintLeaked() {
- Printf("\n");
- Printf("Reporting individual objects:\n");
- ForEachChunk(PrintLeakedCb, 0 /* arg */);
-}
-
-struct DoLeakCheckParam {
+struct CheckForLeaksParam {
bool success;
LeakReport leak_report;
};
-static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads,
- void *arg) {
- DoLeakCheckParam *param = reinterpret_cast<DoLeakCheckParam *>(arg);
+static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
+ void *arg) {
+ CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg);
CHECK(param);
CHECK(!param->success);
- CHECK(param->leak_report.IsEmpty());
ClassifyAllChunks(suspended_threads);
ForEachChunk(CollectLeaksCb, ¶m->leak_report);
- if (!param->leak_report.IsEmpty() && flags()->report_objects)
- PrintLeaked();
+ // Clean up for subsequent leak checks. This assumes we did not overwrite any
+ // kIgnored tags.
+ ForEachChunk(ResetTagsCb, nullptr);
param->success = true;
}
-void DoLeakCheck() {
- EnsureMainThreadIDIsCorrect();
- BlockingMutexLock l(&global_mutex);
- static bool already_done;
- if (already_done) return;
- already_done = true;
+static bool CheckForLeaks() {
if (&__lsan_is_turned_off && __lsan_is_turned_off())
- return;
-
- DoLeakCheckParam param;
+ return false;
+ EnsureMainThreadIDIsCorrect();
+ CheckForLeaksParam param;
param.success = false;
LockThreadRegistry();
LockAllocator();
- StopTheWorld(DoLeakCheckCallback, ¶m);
+ DoStopTheWorld(CheckForLeaksCallback, ¶m);
UnlockAllocator();
UnlockThreadRegistry();
Report("LeakSanitizer has encountered a fatal error.\n");
Die();
}
- uptr have_unsuppressed = param.leak_report.ApplySuppressions();
- if (have_unsuppressed) {
+ param.leak_report.ApplySuppressions();
+ uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount();
+ if (unsuppressed_count > 0) {
Decorator d;
Printf("\n"
"================================================================="
Printf("%s", d.Error());
Report("ERROR: LeakSanitizer: detected memory leaks\n");
Printf("%s", d.End());
- param.leak_report.PrintLargest(flags()->max_leaks);
+ param.leak_report.ReportTopLeaks(flags()->max_leaks);
}
- if (have_unsuppressed || (flags()->verbosity >= 1)) {
+ if (common_flags()->print_suppressions)
PrintMatchedSuppressions();
+ if (unsuppressed_count > 0) {
param.leak_report.PrintSummary();
+ return true;
}
- if (have_unsuppressed && flags()->exitcode)
- internal__exit(flags()->exitcode);
+ return false;
+}
+
+void DoLeakCheck() {
+ BlockingMutexLock l(&global_mutex);
+ static bool already_done;
+ if (already_done) return;
+ already_done = true;
+ bool have_leaks = CheckForLeaks();
+ if (!have_leaks) {
+ return;
+ }
+ if (common_flags()->exitcode) {
+ Die();
+ }
+}
+
+static int DoRecoverableLeakCheck() {
+ BlockingMutexLock l(&global_mutex);
+ bool have_leaks = CheckForLeaks();
+ return have_leaks ? 1 : 0;
}
static Suppression *GetSuppressionForAddr(uptr addr) {
- static const uptr kMaxAddrFrames = 16;
- InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
- for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo();
- uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode(
- addr, addr_frames.data(), kMaxAddrFrames);
- for (uptr i = 0; i < addr_frames_num; i++) {
- Suppression* s;
- if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) ||
- suppression_ctx->Match(addr_frames[i].file, SuppressionLeak, &s) ||
- suppression_ctx->Match(addr_frames[i].module, SuppressionLeak, &s))
+ Suppression *s = nullptr;
+
+ // Suppress by module name.
+ SuppressionContext *suppressions = GetSuppressionContext();
+ if (const char *module_name =
+ Symbolizer::GetOrInit()->GetModuleNameForPc(addr))
+ if (suppressions->Match(module_name, kSuppressionLeak, &s))
return s;
+
+ // Suppress by file or function name.
+ SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
+ for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ if (suppressions->Match(cur->info.function, kSuppressionLeak, &s) ||
+ suppressions->Match(cur->info.file, kSuppressionLeak, &s)) {
+ break;
+ }
}
- return 0;
+ frames->ClearAll();
+ return s;
}
static Suppression *GetSuppressionForStack(u32 stack_trace_id) {
- uptr size = 0;
- const uptr *trace = StackDepotGet(stack_trace_id, &size);
- for (uptr i = 0; i < size; i++) {
- Suppression *s =
- GetSuppressionForAddr(StackTrace::GetPreviousInstructionPc(trace[i]));
+ StackTrace stack = StackDepotGet(stack_trace_id);
+ for (uptr i = 0; i < stack.size; i++) {
+ Suppression *s = GetSuppressionForAddr(
+ StackTrace::GetPreviousInstructionPc(stack.trace[i]));
if (s) return s;
}
- return 0;
+ return nullptr;
}
///// LeakReport implementation. /////
// A hard limit on the number of distinct leaks, to avoid quadratic complexity
-// in LeakReport::Add(). We don't expect to ever see this many leaks in
-// real-world applications.
+// in LeakReport::AddLeakedChunk(). We don't expect to ever see this many leaks
+// in real-world applications.
// FIXME: Get rid of this limit by changing the implementation of LeakReport to
// use a hash table.
const uptr kMaxLeaksConsidered = 5000;
-void LeakReport::Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag) {
+void LeakReport::AddLeakedChunk(uptr chunk, u32 stack_trace_id,
+ uptr leaked_size, ChunkTag tag) {
CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked);
bool is_directly_leaked = (tag == kDirectlyLeaked);
- for (uptr i = 0; i < leaks_.size(); i++)
+ uptr i;
+ for (i = 0; i < leaks_.size(); i++) {
if (leaks_[i].stack_trace_id == stack_trace_id &&
leaks_[i].is_directly_leaked == is_directly_leaked) {
leaks_[i].hit_count++;
leaks_[i].total_size += leaked_size;
- return;
+ break;
}
- if (leaks_.size() == kMaxLeaksConsidered) return;
- Leak leak = { /* hit_count */ 1, leaked_size, stack_trace_id,
- is_directly_leaked, /* is_suppressed */ false };
- leaks_.push_back(leak);
+ }
+ if (i == leaks_.size()) {
+ if (leaks_.size() == kMaxLeaksConsidered) return;
+ Leak leak = { next_id_++, /* hit_count */ 1, leaked_size, stack_trace_id,
+ is_directly_leaked, /* is_suppressed */ false };
+ leaks_.push_back(leak);
+ }
+ if (flags()->report_objects) {
+ LeakedObject obj = {leaks_[i].id, chunk, leaked_size};
+ leaked_objects_.push_back(obj);
+ }
}
static bool LeakComparator(const Leak &leak1, const Leak &leak2) {
return leak1.is_directly_leaked;
}
-void LeakReport::PrintLargest(uptr num_leaks_to_print) {
+void LeakReport::ReportTopLeaks(uptr num_leaks_to_report) {
CHECK(leaks_.size() <= kMaxLeaksConsidered);
Printf("\n");
if (leaks_.size() == kMaxLeaksConsidered)
"reported.\n",
kMaxLeaksConsidered);
- uptr unsuppressed_count = 0;
- for (uptr i = 0; i < leaks_.size(); i++)
- if (!leaks_[i].is_suppressed) unsuppressed_count++;
- if (num_leaks_to_print > 0 && num_leaks_to_print < unsuppressed_count)
- Printf("The %zu largest leak(s):\n", num_leaks_to_print);
+ uptr unsuppressed_count = UnsuppressedLeakCount();
+ if (num_leaks_to_report > 0 && num_leaks_to_report < unsuppressed_count)
+ Printf("The %zu top leak(s):\n", num_leaks_to_report);
InternalSort(&leaks_, leaks_.size(), LeakComparator);
- uptr leaks_printed = 0;
- Decorator d;
+ uptr leaks_reported = 0;
for (uptr i = 0; i < leaks_.size(); i++) {
if (leaks_[i].is_suppressed) continue;
- Printf("%s", d.Leak());
- Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n",
- leaks_[i].is_directly_leaked ? "Direct" : "Indirect",
- leaks_[i].total_size, leaks_[i].hit_count);
- Printf("%s", d.End());
- PrintStackTraceById(leaks_[i].stack_trace_id);
- leaks_printed++;
- if (leaks_printed == num_leaks_to_print) break;
+ PrintReportForLeak(i);
+ leaks_reported++;
+ if (leaks_reported == num_leaks_to_report) break;
}
- if (leaks_printed < unsuppressed_count) {
- uptr remaining = unsuppressed_count - leaks_printed;
+ if (leaks_reported < unsuppressed_count) {
+ uptr remaining = unsuppressed_count - leaks_reported;
Printf("Omitting %zu more leak(s).\n", remaining);
}
}
+void LeakReport::PrintReportForLeak(uptr index) {
+ Decorator d;
+ Printf("%s", d.Leak());
+ Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n",
+ leaks_[index].is_directly_leaked ? "Direct" : "Indirect",
+ leaks_[index].total_size, leaks_[index].hit_count);
+ Printf("%s", d.End());
+
+ PrintStackTraceById(leaks_[index].stack_trace_id);
+
+ if (flags()->report_objects) {
+ Printf("Objects leaked above:\n");
+ PrintLeakedObjectsForLeak(index);
+ Printf("\n");
+ }
+}
+
+void LeakReport::PrintLeakedObjectsForLeak(uptr index) {
+ u32 leak_id = leaks_[index].id;
+ for (uptr j = 0; j < leaked_objects_.size(); j++) {
+ if (leaked_objects_[j].leak_id == leak_id)
+ Printf("%p (%zu bytes)\n", leaked_objects_[j].addr,
+ leaked_objects_[j].size);
+ }
+}
+
void LeakReport::PrintSummary() {
CHECK(leaks_.size() <= kMaxLeaksConsidered);
uptr bytes = 0, allocations = 0;
bytes += leaks_[i].total_size;
allocations += leaks_[i].hit_count;
}
- InternalScopedBuffer<char> summary(kMaxSummaryLength);
- internal_snprintf(summary.data(), summary.size(),
- "%zu byte(s) leaked in %zu allocation(s).", bytes,
- allocations);
+ InternalScopedString summary(kMaxSummaryLength);
+ summary.append("%zu byte(s) leaked in %zu allocation(s).", bytes,
+ allocations);
ReportErrorSummary(summary.data());
}
-uptr LeakReport::ApplySuppressions() {
- uptr unsuppressed_count = 0;
+void LeakReport::ApplySuppressions() {
for (uptr i = 0; i < leaks_.size(); i++) {
Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id);
if (s) {
s->weight += leaks_[i].total_size;
- s->hit_count += leaks_[i].hit_count;
+ atomic_store_relaxed(&s->hit_count, atomic_load_relaxed(&s->hit_count) +
+ leaks_[i].hit_count);
leaks_[i].is_suppressed = true;
- } else {
- unsuppressed_count++;
}
}
- return unsuppressed_count;
}
-} // namespace __lsan
-#endif // CAN_SANITIZE_LEAKS
+
+uptr LeakReport::UnsuppressedLeakCount() {
+ uptr result = 0;
+ for (uptr i = 0; i < leaks_.size(); i++)
+ if (!leaks_[i].is_suppressed) result++;
+ return result;
+}
+
+} // namespace __lsan
+#endif // CAN_SANITIZE_LEAKS
using namespace __lsan; // NOLINT
// locked.
BlockingMutexLock l(&global_mutex);
IgnoreObjectResult res = IgnoreObjectLocked(p);
- if (res == kIgnoreObjectInvalid && flags()->verbosity >= 2)
- Report("__lsan_ignore_object(): no heap object found at %p", p);
- if (res == kIgnoreObjectAlreadyIgnored && flags()->verbosity >= 2)
- Report("__lsan_ignore_object(): "
+ if (res == kIgnoreObjectInvalid)
+ VReport(1, "__lsan_ignore_object(): no heap object found at %p", p);
+ if (res == kIgnoreObjectAlreadyIgnored)
+ VReport(1, "__lsan_ignore_object(): "
"heap object at %p is already being ignored\n", p);
- if (res == kIgnoreObjectSuccess && flags()->verbosity >= 3)
- Report("__lsan_ignore_object(): ignoring heap object at %p\n", p);
-#endif // CAN_SANITIZE_LEAKS
+ if (res == kIgnoreObjectSuccess)
+ VReport(1, "__lsan_ignore_object(): ignoring heap object at %p\n", p);
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_register_root_region(const void *begin, uptr size) {
+#if CAN_SANITIZE_LEAKS
+ BlockingMutexLock l(&global_mutex);
+ CHECK(root_regions);
+ RootRegion region = {begin, size};
+ root_regions->push_back(region);
+ VReport(1, "Registered root region at %p of size %llu\n", begin, size);
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_unregister_root_region(const void *begin, uptr size) {
+#if CAN_SANITIZE_LEAKS
+ BlockingMutexLock l(&global_mutex);
+ CHECK(root_regions);
+ bool removed = false;
+ for (uptr i = 0; i < root_regions->size(); i++) {
+ RootRegion region = (*root_regions)[i];
+ if (region.begin == begin && region.size == size) {
+ removed = true;
+ uptr last_index = root_regions->size() - 1;
+ (*root_regions)[i] = (*root_regions)[last_index];
+ root_regions->pop_back();
+ VReport(1, "Unregistered root region at %p of size %llu\n", begin, size);
+ break;
+ }
+ }
+ if (!removed) {
+ Report(
+ "__lsan_unregister_root_region(): region at %p of size %llu has not "
+ "been registered.\n",
+ begin, size);
+ Die();
+ }
+#endif // CAN_SANITIZE_LEAKS
}
SANITIZER_INTERFACE_ATTRIBUTE
#if CAN_SANITIZE_LEAKS
if (common_flags()->detect_leaks)
__lsan::DoLeakCheck();
-#endif // CAN_SANITIZE_LEAKS
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __lsan_do_recoverable_leak_check() {
+#if CAN_SANITIZE_LEAKS
+ if (common_flags()->detect_leaks)
+ return __lsan::DoRecoverableLeakCheck();
+#endif // CAN_SANITIZE_LEAKS
+ return 0;
}
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
return 0;
}
#endif
-} // extern "C"
+} // extern "C"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_platform.h"
+#include "sanitizer_common/sanitizer_stoptheworld.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
-#if SANITIZER_LINUX && defined(__x86_64__)
+#if (SANITIZER_LINUX && !SANITIZER_ANDROID) && (SANITIZER_WORDSIZE == 64) \
+ && (defined(__x86_64__) || defined(__mips64) || defined(__aarch64__))
#define CAN_SANITIZE_LEAKS 1
#else
#define CAN_SANITIZE_LEAKS 0
#endif
+namespace __sanitizer {
+class FlagParser;
+}
+
namespace __lsan {
// Chunk tags.
};
struct Flags {
+#define LSAN_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "lsan_flags.inc"
+#undef LSAN_FLAG
+
+ void SetDefaults();
uptr pointer_alignment() const {
return use_unaligned ? 1 : sizeof(uptr);
}
-
- // Print addresses of leaked objects after main leak report.
- bool report_objects;
- // Aggregate two objects into one leak if this many stack frames match. If
- // zero, the entire stack trace must match.
- int resolution;
- // The number of leaks reported.
- int max_leaks;
- // If nonzero kill the process with this exit code upon finding leaks.
- int exitcode;
- // Suppressions file name.
- const char* suppressions;
-
- // Flags controlling the root set of reachable memory.
- // Global variables (.data and .bss).
- bool use_globals;
- // Thread stacks.
- bool use_stacks;
- // Thread registers.
- bool use_registers;
- // TLS and thread-specific storage.
- bool use_tls;
-
- // Consider unaligned pointers valid.
- bool use_unaligned;
-
- // User-visible verbosity.
- int verbosity;
-
- // Debug logging.
- bool log_pointers;
- bool log_threads;
};
extern Flags lsan_flags;
inline Flags *flags() { return &lsan_flags; }
+void RegisterLsanFlags(FlagParser *parser, Flags *f);
struct Leak {
+ u32 id;
uptr hit_count;
uptr total_size;
u32 stack_trace_id;
bool is_suppressed;
};
+struct LeakedObject {
+ u32 leak_id;
+ uptr addr;
+ uptr size;
+};
+
// Aggregates leaks by stack trace prefix.
class LeakReport {
public:
- LeakReport() : leaks_(1) {}
- void Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag);
- void PrintLargest(uptr max_leaks);
+ LeakReport() : next_id_(0), leaks_(1), leaked_objects_(1) {}
+ void AddLeakedChunk(uptr chunk, u32 stack_trace_id, uptr leaked_size,
+ ChunkTag tag);
+ void ReportTopLeaks(uptr max_leaks);
void PrintSummary();
- bool IsEmpty() { return leaks_.size() == 0; }
- uptr ApplySuppressions();
+ void ApplySuppressions();
+ uptr UnsuppressedLeakCount();
+
+
private:
+ void PrintReportForLeak(uptr index);
+ void PrintLeakedObjectsForLeak(uptr index);
+
+ u32 next_id_;
InternalMmapVector<Leak> leaks_;
+ InternalMmapVector<LeakedObject> leaked_objects_;
};
typedef InternalMmapVector<uptr> Frontier;
void InitializePlatformSpecificModules();
void ProcessGlobalRegions(Frontier *frontier);
void ProcessPlatformSpecificAllocations(Frontier *frontier);
+// Run stoptheworld while holding any platform-specific locks.
+void DoStopTheWorld(StopTheWorldCallback callback, void* argument);
void ScanRangeForPointers(uptr begin, uptr end,
Frontier *frontier,
void DoLeakCheck();
bool DisabledInThisThread();
+// Special case for "new T[0]" where T is a type with DTOR.
+// new T[0] will allocate one word for the array size (0) and store a pointer
+// to the end of allocated chunk.
+inline bool IsSpecialCaseOfOperatorNew0(uptr chunk_beg, uptr chunk_size,
+ uptr addr) {
+ return chunk_size == sizeof(uptr) && chunk_beg + chunk_size == addr &&
+ *reinterpret_cast<uptr *>(chunk_beg) == 0;
+}
+
// The following must be implemented in the parent tool.
void ForEachChunk(ForEachChunkCallback callback, void *arg);
// Wrappers for allocator's ForceLock()/ForceUnlock().
void LockAllocator();
void UnlockAllocator();
+// Returns true if [addr, addr + sizeof(void *)) is poisoned.
+bool WordIsPoisoned(uptr addr);
// Wrappers for ThreadRegistry access.
void LockThreadRegistry();
void UnlockThreadRegistry();
#include <link.h>
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_linux.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
// We request 2 modules matching "ld", so we can print a warning if there's more
// than one match. But only the first one is actually used.
static char linker_placeholder[2 * sizeof(LoadedModule)] ALIGNED(64);
-static LoadedModule *linker = 0;
+static LoadedModule *linker = nullptr;
static bool IsLinker(const char* full_name) {
return LibraryNameIs(full_name, kLinkerName);
return;
}
if (num_matches == 0)
- Report("LeakSanitizer: Dynamic linker not found. "
- "TLS will not be handled correctly.\n");
+ VReport(1, "LeakSanitizer: Dynamic linker not found. "
+ "TLS will not be handled correctly.\n");
else if (num_matches > 1)
- Report("LeakSanitizer: Multiple modules match \"%s\". "
- "TLS will not be handled correctly.\n", kLinkerName);
- linker = 0;
+ VReport(1, "LeakSanitizer: Multiple modules match \"%s\". "
+ "TLS will not be handled correctly.\n", kLinkerName);
+ linker = nullptr;
}
static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
// Scans global variables for heap pointers.
void ProcessGlobalRegions(Frontier *frontier) {
- // FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of
- // deadlocking by running this under StopTheWorld. However, the lock is
- // reentrant, so we should be able to fix this by acquiring the lock before
- // suspending threads.
+ if (!flags()->use_globals) return;
dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
}
static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) {
CHECK(stack_id);
- uptr size = 0;
- const uptr *trace = map->Get(stack_id, &size);
+ StackTrace stack = map->Get(stack_id);
// The top frame is our malloc/calloc/etc. The next frame is the caller.
- if (size >= 2)
- return trace[1];
+ if (stack.size >= 2)
+ return stack.trace[1];
return 0;
}
reinterpret_cast<ProcessPlatformAllocParam *>(arg);
chunk = GetUserBegin(chunk);
LsanMetadata m(chunk);
- if (m.allocated() && m.tag() != kReachable) {
+ if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) {
u32 stack_id = m.stack_trace_id();
uptr caller_pc = 0;
if (stack_id > 0)
// Handles dynamically allocated TLS blocks by treating all chunks allocated
// from ld-linux.so as reachable.
+// Dynamic TLS blocks contain the TLS variables of dynamically loaded modules.
+// They are allocated with a __libc_memalign() call in allocate_and_init()
+// (elf/dl-tls.c). Glibc won't tell us the address ranges occupied by those
+// blocks, but we can make sure they come from our own allocator by intercepting
+// __libc_memalign(). On top of that, there is no easy way to reach them. Their
+// addresses are stored in a dynamically allocated array (the DTV) which is
+// referenced from the static TLS. Unfortunately, we can't just rely on the DTV
+// being reachable from the static TLS, and the dynamic TLS being reachable from
+// the DTV. This is because the initial DTV is allocated before our interception
+// mechanism kicks in, and thus we don't recognize it as allocated memory. We
+// can't special-case it either, since we don't know its size.
+// Our solution is to include in the root set all allocations made from
+// ld-linux.so (which is where allocate_and_init() is implemented). This is
+// guaranteed to include all dynamic TLS blocks (and possibly other allocations
+// which we don't care about).
void ProcessPlatformSpecificAllocations(Frontier *frontier) {
if (!flags()->use_tls) return;
if (!linker) return;
ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg);
}
-} // namespace __lsan
-#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX
+struct DoStopTheWorldParam {
+ StopTheWorldCallback callback;
+ void *argument;
+};
+
+static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size,
+ void *data) {
+ DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);
+ StopTheWorld(param->callback, param->argument);
+ return 1;
+}
+
+// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one
+// of the threads is frozen while holding the libdl lock, the tracer will hang
+// in dl_iterate_phdr() forever.
+// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the
+// tracer task and the thread that spawned it. Thus, if we run the tracer task
+// while holding the libdl lock in the parent thread, we can safely reenter it
+// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()
+// callback in the parent thread.
+void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
+ DoStopTheWorldParam param = {callback, argument};
+ dl_iterate_phdr(DoStopTheWorldCallback, ¶m);
+}
+
+} // namespace __lsan
+
+#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX
--- /dev/null
+//===-- lsan_flags.inc ------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// LSan runtime flags.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LSAN_FLAG
+# error "Define LSAN_FLAG prior to including this file!"
+#endif
+
+// LSAN_FLAG(Type, Name, DefaultValue, Description)
+// See COMMON_FLAG in sanitizer_flags.inc for more details.
+
+LSAN_FLAG(bool, report_objects, false,
+ "Print addresses of leaked objects after main leak report.")
+LSAN_FLAG(
+ int, resolution, 0,
+ "Aggregate two objects into one leak if this many stack frames match. If "
+ "zero, the entire stack trace must match.")
+LSAN_FLAG(int, max_leaks, 0, "The number of leaks reported.")
+
+// Flags controlling the root set of reachable memory.
+LSAN_FLAG(bool, use_globals, true,
+ "Root set: include global variables (.data and .bss)")
+LSAN_FLAG(bool, use_stacks, true, "Root set: include thread stacks")
+LSAN_FLAG(bool, use_registers, true, "Root set: include thread registers")
+LSAN_FLAG(bool, use_tls, true,
+ "Root set: include TLS and thread-specific storage")
+LSAN_FLAG(bool, use_root_regions, true,
+ "Root set: include regions added via __lsan_register_root_region().")
+
+LSAN_FLAG(bool, use_unaligned, false, "Consider unaligned pointers valid.")
+LSAN_FLAG(bool, use_poisoned, false,
+ "Consider pointers found in poisoned memory to be valid.")
+LSAN_FLAG(bool, log_pointers, false, "Debug logging")
+LSAN_FLAG(bool, log_threads, false, "Debug logging")
+LSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
int pthread_setspecific(unsigned key, const void *v);
}
-#define GET_STACK_TRACE \
- StackTrace stack; \
- { \
- uptr stack_top = 0, stack_bottom = 0; \
- ThreadContext *t; \
- bool fast = common_flags()->fast_unwind_on_malloc; \
- if (fast && (t = CurrentThreadContext())) { \
- stack_top = t->stack_end(); \
- stack_bottom = t->stack_begin(); \
- } \
- stack.Unwind(__sanitizer::common_flags()->malloc_context_size, \
- StackTrace::GetCurrentPc(), \
- GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \
- }
-
#define ENSURE_LSAN_INITED do { \
CHECK(!lsan_init_is_running); \
if (!lsan_inited) \
INTERCEPTOR(void*, malloc, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
return Allocate(stack, size, 1, kAlwaysClearMemory);
}
CHECK(allocated < kCallocPoolSize);
return mem;
}
- if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0;
+ if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return nullptr;
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
size *= nmemb;
return Allocate(stack, size, 1, true);
}
INTERCEPTOR(void*, realloc, void *q, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
return Reallocate(stack, q, size, 1);
}
INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
+ return Allocate(stack, size, alignment, kAlwaysClearMemory);
+}
+
+INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
return Allocate(stack, size, alignment, kAlwaysClearMemory);
}
INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
*memptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
// FIXME: Return ENOMEM if user requested more than max alloc size.
return 0;
INTERCEPTOR(void*, valloc, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
if (size == 0)
size = GetPageSizeCached();
return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory);
INTERCEPTOR(void*, pvalloc, uptr size) {
ENSURE_LSAN_INITED;
- GET_STACK_TRACE;
+ GET_STACK_TRACE_MALLOC;
uptr PageSize = GetPageSizeCached();
size = RoundUpTo(size, PageSize);
if (size == 0) {
return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory);
}
-INTERCEPTOR(void, cfree, void *p) ALIAS("free");
+INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free));
#define OPERATOR_NEW_BODY \
ENSURE_LSAN_INITED; \
- GET_STACK_TRACE; \
+ GET_STACK_TRACE_MALLOC; \
return Allocate(stack, size, 1, kAlwaysClearMemory);
INTERCEPTOR_ATTRIBUTE
Deallocate(ptr);
INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr) { OPERATOR_DELETE_BODY; }
+void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; }
+void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
INTERCEPTOR_ATTRIBUTE
void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; }
INTERCEPTOR_ATTRIBUTE
// We need this to intercept the __libc_memalign calls that are used to
// allocate dynamic TLS space in ld-linux.so.
-INTERCEPTOR(void *, __libc_memalign, uptr align, uptr s) ALIAS("memalign");
+INTERCEPTOR(void *, __libc_memalign, uptr align, uptr s)
+ ALIAS(WRAPPER_NAME(memalign));
///// Thread initialization and finalization. /////
// Wait until the last iteration to maximize the chance that we are the last
// destructor to run.
if (pthread_setspecific(g_thread_finalize_key,
- (void*)kPthreadDestructorIterations)) {
+ (void*)GetPthreadDestructorIterations())) {
Report("LeakSanitizer: failed to set thread key.\n");
Die();
}
int tid = 0;
while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
internal_sched_yield();
- atomic_store(&p->tid, 0, memory_order_release);
SetCurrentThread(tid);
ThreadStart(tid, GetTid());
+ atomic_store(&p->tid, 0, memory_order_release);
return callback(param);
}
ENSURE_LSAN_INITED;
EnsureMainThreadIDIsCorrect();
__sanitizer_pthread_attr_t myattr;
- if (attr == 0) {
+ if (!attr) {
pthread_attr_init(&myattr);
attr = &myattr;
}
- AdjustStackSizeLinux(attr);
+ AdjustStackSize(attr);
int detached = 0;
pthread_attr_getdetachstate(attr, &detached);
ThreadParam p;
}
}
-} // namespace __lsan
+} // namespace __lsan
#include "lsan.h"
-#ifndef LSAN_USE_PREINIT_ARRAY
-#define LSAN_USE_PREINIT_ARRAY 1
-#endif
-
-#if LSAN_USE_PREINIT_ARRAY && !defined(PIC)
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
// We force __lsan_init to be called before anyone else by placing it into
// .preinit_array section.
__attribute__((section(".preinit_array"), used))
u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) {
return thread_registry->CreateThread(user_id, detached, parent_tid,
- /* arg */ 0);
+ /* arg */ nullptr);
}
void ThreadStart(u32 tid, uptr os_id) {
}
ThreadContext *CurrentThreadContext() {
- if (!thread_registry) return 0;
+ if (!thread_registry) return nullptr;
if (GetCurrentThread() == kInvalidTid)
- return 0;
+ return nullptr;
// No lock needed when getting current thread.
return (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread());
}
void ThreadJoin(u32 tid) {
CHECK_NE(tid, kInvalidTid);
- thread_registry->JoinThread(tid, /* arg */0);
+ thread_registry->JoinThread(tid, /* arg */nullptr);
}
void EnsureMainThreadIDIsCorrect() {
thread_registry->Unlock();
}
-} // namespace __lsan
+} // namespace __lsan
class ThreadContext : public ThreadContextBase {
public:
explicit ThreadContext(int tid);
- void OnStarted(void *arg);
- void OnFinished();
+ void OnStarted(void *arg) override;
+ void OnFinished() override;
uptr stack_begin() { return stack_begin_; }
uptr stack_end() { return stack_end_; }
uptr tls_begin() { return tls_begin_; }
# May be used by toolexeclibdir.
gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
-DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS @RPC_DEFS@
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++11
if LIBBACKTRACE_SUPPORTED
AM_CXXFLAGS += -DSANITIZER_LIBBACKTRACE -DSANITIZER_CP_DEMANGLE \
-I $(top_srcdir)/../libbacktrace \
sanitizer_allocator.cc \
sanitizer_common.cc \
sanitizer_common_libcdep.cc \
- sanitizer_coverage.cc \
+ sanitizer_coverage_libcdep.cc \
+ sanitizer_coverage_mapping_libcdep.cc \
+ sanitizer_deadlock_detector1.cc \
+ sanitizer_deadlock_detector2.cc \
sanitizer_flags.cc \
+ sanitizer_flag_parser.cc \
sanitizer_libc.cc \
sanitizer_libignore.cc \
sanitizer_linux.cc \
sanitizer_linux_libcdep.cc \
sanitizer_mac.cc \
+ sanitizer_persistent_allocator.cc \
sanitizer_platform_limits_linux.cc \
sanitizer_platform_limits_posix.cc \
- sanitizer_posix_libcdep.cc \
sanitizer_posix.cc \
+ sanitizer_posix_libcdep.cc \
sanitizer_printf.cc \
+ sanitizer_procmaps_common.cc \
+ sanitizer_procmaps_freebsd.cc \
+ sanitizer_procmaps_linux.cc \
+ sanitizer_procmaps_mac.cc \
sanitizer_stackdepot.cc \
sanitizer_stacktrace.cc \
sanitizer_stacktrace_libcdep.cc \
+ sanitizer_symbolizer_mac.cc \
+ sanitizer_stacktrace_printer.cc \
sanitizer_stoptheworld_linux_libcdep.cc \
sanitizer_suppressions.cc \
- sanitizer_symbolizer_posix_libcdep.cc \
- sanitizer_symbolizer_win.cc \
sanitizer_symbolizer.cc \
sanitizer_symbolizer_libbacktrace.cc \
sanitizer_symbolizer_libcdep.cc \
+ sanitizer_symbolizer_posix_libcdep.cc \
+ sanitizer_symbolizer_win.cc \
sanitizer_thread_registry.cc \
+ sanitizer_tls_get_addr.cc \
+ sanitizer_unwind_linux_libcdep.cc \
sanitizer_win.cc
+
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
# Work around what appears to be a GNU make bug handling MAKEFLAGS
LTLIBRARIES = $(noinst_LTLIBRARIES)
libsanitizer_common_la_LIBADD =
am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
- sanitizer_common_libcdep.lo sanitizer_coverage.lo \
- sanitizer_flags.lo sanitizer_libc.lo sanitizer_libignore.lo \
- sanitizer_linux.lo sanitizer_linux_libcdep.lo sanitizer_mac.lo \
+ sanitizer_common_libcdep.lo sanitizer_coverage_libcdep.lo \
+ sanitizer_coverage_mapping_libcdep.lo \
+ sanitizer_deadlock_detector1.lo \
+ sanitizer_deadlock_detector2.lo sanitizer_flags.lo \
+ sanitizer_flag_parser.lo sanitizer_libc.lo \
+ sanitizer_libignore.lo sanitizer_linux.lo \
+ sanitizer_linux_libcdep.lo sanitizer_mac.lo \
+ sanitizer_persistent_allocator.lo \
sanitizer_platform_limits_linux.lo \
- sanitizer_platform_limits_posix.lo sanitizer_posix_libcdep.lo \
- sanitizer_posix.lo sanitizer_printf.lo sanitizer_stackdepot.lo \
- sanitizer_stacktrace.lo sanitizer_stacktrace_libcdep.lo \
+ sanitizer_platform_limits_posix.lo sanitizer_posix.lo \
+ sanitizer_posix_libcdep.lo sanitizer_printf.lo \
+ sanitizer_procmaps_common.lo sanitizer_procmaps_freebsd.lo \
+ sanitizer_procmaps_linux.lo sanitizer_procmaps_mac.lo \
+ sanitizer_stackdepot.lo sanitizer_stacktrace.lo \
+ sanitizer_stacktrace_libcdep.lo sanitizer_symbolizer_mac.lo \
+ sanitizer_stacktrace_printer.lo \
sanitizer_stoptheworld_linux_libcdep.lo \
- sanitizer_suppressions.lo \
- sanitizer_symbolizer_posix_libcdep.lo \
- sanitizer_symbolizer_win.lo sanitizer_symbolizer.lo \
+ sanitizer_suppressions.lo sanitizer_symbolizer.lo \
sanitizer_symbolizer_libbacktrace.lo \
- sanitizer_symbolizer_libcdep.lo sanitizer_thread_registry.lo \
+ sanitizer_symbolizer_libcdep.lo \
+ sanitizer_symbolizer_posix_libcdep.lo \
+ sanitizer_symbolizer_win.lo sanitizer_thread_registry.lo \
+ sanitizer_tls_get_addr.lo sanitizer_unwind_linux_libcdep.lo \
sanitizer_win.lo
am_libsanitizer_common_la_OBJECTS = $(am__objects_1)
libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS)
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
-DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS @RPC_DEFS@
DEPDIR = @DEPDIR@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
+OBSTACK_DEFS = @OBSTACK_DEFS@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
VERSION = @VERSION@
VIEW_FILE = @VIEW_FILE@
abs_builddir = @abs_builddir@
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) \
- $(am__append_1)
+ -std=gnu++11 $(am__append_1)
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libsanitizer_common.la
sanitizer_common_files = \
sanitizer_allocator.cc \
sanitizer_common.cc \
sanitizer_common_libcdep.cc \
- sanitizer_coverage.cc \
+ sanitizer_coverage_libcdep.cc \
+ sanitizer_coverage_mapping_libcdep.cc \
+ sanitizer_deadlock_detector1.cc \
+ sanitizer_deadlock_detector2.cc \
sanitizer_flags.cc \
+ sanitizer_flag_parser.cc \
sanitizer_libc.cc \
sanitizer_libignore.cc \
sanitizer_linux.cc \
sanitizer_linux_libcdep.cc \
sanitizer_mac.cc \
+ sanitizer_persistent_allocator.cc \
sanitizer_platform_limits_linux.cc \
sanitizer_platform_limits_posix.cc \
- sanitizer_posix_libcdep.cc \
sanitizer_posix.cc \
+ sanitizer_posix_libcdep.cc \
sanitizer_printf.cc \
+ sanitizer_procmaps_common.cc \
+ sanitizer_procmaps_freebsd.cc \
+ sanitizer_procmaps_linux.cc \
+ sanitizer_procmaps_mac.cc \
sanitizer_stackdepot.cc \
sanitizer_stacktrace.cc \
sanitizer_stacktrace_libcdep.cc \
+ sanitizer_symbolizer_mac.cc \
+ sanitizer_stacktrace_printer.cc \
sanitizer_stoptheworld_linux_libcdep.cc \
sanitizer_suppressions.cc \
- sanitizer_symbolizer_posix_libcdep.cc \
- sanitizer_symbolizer_win.cc \
sanitizer_symbolizer.cc \
sanitizer_symbolizer_libbacktrace.cc \
sanitizer_symbolizer_libcdep.cc \
+ sanitizer_symbolizer_posix_libcdep.cc \
+ sanitizer_symbolizer_win.cc \
sanitizer_thread_registry.cc \
+ sanitizer_tls_get_addr.cc \
+ sanitizer_unwind_linux_libcdep.cc \
sanitizer_win.cc
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common_libcdep.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage_libcdep.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage_mapping_libcdep.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector1.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flag_parser.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flags.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libignore.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_persistent_allocator.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_posix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_printf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_common.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_freebsd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_linux.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace_libcdep.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace_printer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stoptheworld_linux_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_suppressions.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libbacktrace.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libcdep.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_posix_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_registry.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_tls_get_addr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_linux_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@
.cc.o:
--- /dev/null
+//===-- sanitizer_addrhashmap.h ---------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Concurrent uptr->T hashmap.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ADDRHASHMAP_H
+#define SANITIZER_ADDRHASHMAP_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_allocator_internal.h"
+
+namespace __sanitizer {
+
+// Concurrent uptr->T hashmap.
+// T must be a POD type, kSize is preferably a prime but can be any number.
+// Usage example:
+//
+// typedef AddrHashMap<uptr, 11> Map;
+// Map m;
+// {
+// Map::Handle h(&m, addr);
+// use h.operator->() to access the data
+// if h.created() then the element was just created, and the current thread
+// has exclusive access to it
+// otherwise the current thread has only read access to the data
+// }
+// {
+// Map::Handle h(&m, addr, true);
+// this will remove the data from the map in Handle dtor
+// the current thread has exclusive access to the data
+// if !h.exists() then the element never existed
+// }
+template<typename T, uptr kSize>
+class AddrHashMap {
+ private:
+ struct Cell {
+ atomic_uintptr_t addr;
+ T val;
+ };
+
+ struct AddBucket {
+ uptr cap;
+ uptr size;
+ Cell cells[1]; // variable len
+ };
+
+ static const uptr kBucketSize = 3;
+
+ struct Bucket {
+ RWMutex mtx;
+ atomic_uintptr_t add;
+ Cell cells[kBucketSize];
+ };
+
+ public:
+ AddrHashMap();
+
+ class Handle {
+ public:
+ Handle(AddrHashMap<T, kSize> *map, uptr addr);
+ Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove);
+ Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove, bool create);
+
+ ~Handle();
+ T *operator->();
+ bool created() const;
+ bool exists() const;
+
+ private:
+ friend AddrHashMap<T, kSize>;
+ AddrHashMap<T, kSize> *map_;
+ Bucket *bucket_;
+ Cell *cell_;
+ uptr addr_;
+ uptr addidx_;
+ bool created_;
+ bool remove_;
+ bool create_;
+ };
+
+ private:
+ friend class Handle;
+ Bucket *table_;
+
+ void acquire(Handle *h);
+ void release(Handle *h);
+ uptr calcHash(uptr addr);
+};
+
+template<typename T, uptr kSize>
+AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr) {
+ map_ = map;
+ addr_ = addr;
+ remove_ = false;
+ create_ = true;
+ map_->acquire(this);
+}
+
+template<typename T, uptr kSize>
+AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,
+ bool remove) {
+ map_ = map;
+ addr_ = addr;
+ remove_ = remove;
+ create_ = true;
+ map_->acquire(this);
+}
+
+template<typename T, uptr kSize>
+AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,
+ bool remove, bool create) {
+ map_ = map;
+ addr_ = addr;
+ remove_ = remove;
+ create_ = create;
+ map_->acquire(this);
+}
+
+template<typename T, uptr kSize>
+AddrHashMap<T, kSize>::Handle::~Handle() {
+ map_->release(this);
+}
+
+template <typename T, uptr kSize>
+T *AddrHashMap<T, kSize>::Handle::operator->() {
+ return &cell_->val;
+}
+
+template<typename T, uptr kSize>
+bool AddrHashMap<T, kSize>::Handle::created() const {
+ return created_;
+}
+
+template<typename T, uptr kSize>
+bool AddrHashMap<T, kSize>::Handle::exists() const {
+ return cell_ != nullptr;
+}
+
+template<typename T, uptr kSize>
+AddrHashMap<T, kSize>::AddrHashMap() {
+ table_ = (Bucket*)MmapOrDie(kSize * sizeof(table_[0]), "AddrHashMap");
+}
+
+template<typename T, uptr kSize>
+void AddrHashMap<T, kSize>::acquire(Handle *h) {
+ uptr addr = h->addr_;
+ uptr hash = calcHash(addr);
+ Bucket *b = &table_[hash];
+
+ h->created_ = false;
+ h->addidx_ = -1U;
+ h->bucket_ = b;
+ h->cell_ = nullptr;
+
+ // If we want to remove the element, we need exclusive access to the bucket,
+ // so skip the lock-free phase.
+ if (h->remove_)
+ goto locked;
+
+ retry:
+ // First try to find an existing element w/o read mutex.
+ CHECK(!h->remove_);
+ // Check the embed cells.
+ for (uptr i = 0; i < kBucketSize; i++) {
+ Cell *c = &b->cells[i];
+ uptr addr1 = atomic_load(&c->addr, memory_order_acquire);
+ if (addr1 == addr) {
+ h->cell_ = c;
+ return;
+ }
+ }
+
+ // Check the add cells with read lock.
+ if (atomic_load(&b->add, memory_order_relaxed)) {
+ b->mtx.ReadLock();
+ AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
+ for (uptr i = 0; i < add->size; i++) {
+ Cell *c = &add->cells[i];
+ uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
+ if (addr1 == addr) {
+ h->addidx_ = i;
+ h->cell_ = c;
+ return;
+ }
+ }
+ b->mtx.ReadUnlock();
+ }
+
+ locked:
+ // Re-check existence under write lock.
+ // Embed cells.
+ b->mtx.Lock();
+ for (uptr i = 0; i < kBucketSize; i++) {
+ Cell *c = &b->cells[i];
+ uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
+ if (addr1 == addr) {
+ if (h->remove_) {
+ h->cell_ = c;
+ return;
+ }
+ b->mtx.Unlock();
+ goto retry;
+ }
+ }
+
+ // Add cells.
+ AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
+ if (add) {
+ for (uptr i = 0; i < add->size; i++) {
+ Cell *c = &add->cells[i];
+ uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
+ if (addr1 == addr) {
+ if (h->remove_) {
+ h->addidx_ = i;
+ h->cell_ = c;
+ return;
+ }
+ b->mtx.Unlock();
+ goto retry;
+ }
+ }
+ }
+
+ // The element does not exist, no need to create it if we want to remove.
+ if (h->remove_ || !h->create_) {
+ b->mtx.Unlock();
+ return;
+ }
+
+ // Now try to create it under the mutex.
+ h->created_ = true;
+ // See if we have a free embed cell.
+ for (uptr i = 0; i < kBucketSize; i++) {
+ Cell *c = &b->cells[i];
+ uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
+ if (addr1 == 0) {
+ h->cell_ = c;
+ return;
+ }
+ }
+
+ // Store in the add cells.
+ if (!add) {
+ // Allocate a new add array.
+ const uptr kInitSize = 64;
+ add = (AddBucket*)InternalAlloc(kInitSize);
+ internal_memset(add, 0, kInitSize);
+ add->cap = (kInitSize - sizeof(*add)) / sizeof(add->cells[0]) + 1;
+ add->size = 0;
+ atomic_store(&b->add, (uptr)add, memory_order_relaxed);
+ }
+ if (add->size == add->cap) {
+ // Grow existing add array.
+ uptr oldsize = sizeof(*add) + (add->cap - 1) * sizeof(add->cells[0]);
+ uptr newsize = oldsize * 2;
+ AddBucket *add1 = (AddBucket*)InternalAlloc(newsize);
+ internal_memset(add1, 0, newsize);
+ add1->cap = (newsize - sizeof(*add)) / sizeof(add->cells[0]) + 1;
+ add1->size = add->size;
+ internal_memcpy(add1->cells, add->cells, add->size * sizeof(add->cells[0]));
+ InternalFree(add);
+ atomic_store(&b->add, (uptr)add1, memory_order_relaxed);
+ add = add1;
+ }
+ // Store.
+ uptr i = add->size++;
+ Cell *c = &add->cells[i];
+ CHECK_EQ(atomic_load(&c->addr, memory_order_relaxed), 0);
+ h->addidx_ = i;
+ h->cell_ = c;
+}
+
+template<typename T, uptr kSize>
+void AddrHashMap<T, kSize>::release(Handle *h) {
+ if (!h->cell_)
+ return;
+ Bucket *b = h->bucket_;
+ Cell *c = h->cell_;
+ uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
+ if (h->created_) {
+ // Denote completion of insertion.
+ CHECK_EQ(addr1, 0);
+ // After the following store, the element becomes available
+ // for lock-free reads.
+ atomic_store(&c->addr, h->addr_, memory_order_release);
+ b->mtx.Unlock();
+ } else if (h->remove_) {
+ // Denote that the cell is empty now.
+ CHECK_EQ(addr1, h->addr_);
+ atomic_store(&c->addr, 0, memory_order_release);
+ // See if we need to compact the bucket.
+ AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
+ if (h->addidx_ == -1U) {
+ // Removed from embed array, move an add element into the freed cell.
+ if (add && add->size != 0) {
+ uptr last = --add->size;
+ Cell *c1 = &add->cells[last];
+ c->val = c1->val;
+ uptr addr1 = atomic_load(&c1->addr, memory_order_relaxed);
+ atomic_store(&c->addr, addr1, memory_order_release);
+ atomic_store(&c1->addr, 0, memory_order_release);
+ }
+ } else {
+ // Removed from add array, compact it.
+ uptr last = --add->size;
+ Cell *c1 = &add->cells[last];
+ if (c != c1) {
+ *c = *c1;
+ atomic_store(&c1->addr, 0, memory_order_relaxed);
+ }
+ }
+ if (add && add->size == 0) {
+ // FIXME(dvyukov): free add?
+ }
+ b->mtx.Unlock();
+ } else {
+ CHECK_EQ(addr1, h->addr_);
+ if (h->addidx_ != -1U)
+ b->mtx.ReadUnlock();
+ }
+}
+
+template<typename T, uptr kSize>
+uptr AddrHashMap<T, kSize>::calcHash(uptr addr) {
+ addr += addr << 10;
+ addr ^= addr >> 6;
+ return addr % kSize;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_ADDRHASHMAP_H
// run-time libraries.
// This allocator is used inside run-times.
//===----------------------------------------------------------------------===//
+
#include "sanitizer_allocator.h"
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
namespace __sanitizer {
// ThreadSanitizer for Go uses libc malloc/free.
-#if defined(SANITIZER_GO)
+#if defined(SANITIZER_GO) || defined(SANITIZER_USE_MALLOC)
# if SANITIZER_LINUX && !SANITIZER_ANDROID
extern "C" void *__libc_malloc(uptr size);
extern "C" void __libc_free(void *ptr);
return 0;
}
-#else // SANITIZER_GO
+#else // SANITIZER_GO
static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)];
static atomic_uint8_t internal_allocator_initialized;
SpinMutexLock l(&internal_alloc_init_mu);
if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) ==
0) {
- internal_allocator_instance->Init();
+ internal_allocator_instance->Init(/* may_return_null*/ false);
atomic_store(&internal_allocator_initialized, 1, memory_order_release);
}
}
}
static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
- if (cache == 0) {
+ if (!cache) {
SpinMutexLock l(&internal_allocator_cache_mu);
return internal_allocator()->Deallocate(&internal_allocator_cache, ptr);
}
internal_allocator()->Deallocate(cache, ptr);
}
-#endif // SANITIZER_GO
+#endif // SANITIZER_GO
const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull;
void *InternalAlloc(uptr size, InternalAllocatorCache *cache) {
if (size + sizeof(u64) < size)
- return 0;
+ return nullptr;
void *p = RawInternalAlloc(size + sizeof(u64), cache);
- if (p == 0)
- return 0;
+ if (!p)
+ return nullptr;
((u64*)p)[0] = kBlockMagic;
return (char*)p + sizeof(u64);
}
void InternalFree(void *addr, InternalAllocatorCache *cache) {
- if (addr == 0)
+ if (!addr)
return;
addr = (char*)addr - sizeof(u64);
CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
if (allocated_end_ - allocated_current_ < (sptr)size) {
uptr size_to_allocate = Max(size, GetPageSizeCached());
allocated_current_ =
- (char*)MmapOrDie(size_to_allocate, __FUNCTION__);
+ (char*)MmapOrDie(size_to_allocate, __func__);
allocated_end_ = allocated_current_ + size_to_allocate;
if (low_level_alloc_callback) {
low_level_alloc_callback((uptr)allocated_current_,
return (max / size) < n;
}
-void *AllocatorReturnNull() {
- if (common_flags()->allocator_may_return_null)
- return 0;
+void NORETURN ReportAllocatorCannotReturnNull() {
Report("%s's allocator is terminating the process instead of returning 0\n",
SanitizerToolName);
Report("If you don't like this behavior set allocator_may_return_null=1\n");
CHECK(0);
- return 0;
+ Die();
}
-} // namespace __sanitizer
+} // namespace __sanitizer
namespace __sanitizer {
-// Depending on allocator_may_return_null either return 0 or crash.
-void *AllocatorReturnNull();
+// Prints error message and kills the program.
+void NORETURN ReportAllocatorCannotReturnNull();
// SizeClassMap maps allocation sizes into size classes and back.
// Class 0 corresponds to size 0.
// Memory allocator statistics
enum AllocatorStat {
- AllocatorStatMalloced,
- AllocatorStatFreed,
- AllocatorStatMmapped,
- AllocatorStatUnmapped,
+ AllocatorStatAllocated,
+ AllocatorStatMapped,
AllocatorStatCount
};
-typedef u64 AllocatorStatCounters[AllocatorStatCount];
+typedef uptr AllocatorStatCounters[AllocatorStatCount];
// Per-thread stats, live in per-thread cache.
class AllocatorStats {
void Init() {
internal_memset(this, 0, sizeof(*this));
}
+ void InitLinkerInitialized() {}
- void Add(AllocatorStat i, u64 v) {
+ void Add(AllocatorStat i, uptr v) {
v += atomic_load(&stats_[i], memory_order_relaxed);
atomic_store(&stats_[i], v, memory_order_relaxed);
}
- void Set(AllocatorStat i, u64 v) {
+ void Sub(AllocatorStat i, uptr v) {
+ v = atomic_load(&stats_[i], memory_order_relaxed) - v;
atomic_store(&stats_[i], v, memory_order_relaxed);
}
- u64 Get(AllocatorStat i) const {
+ void Set(AllocatorStat i, uptr v) {
+ atomic_store(&stats_[i], v, memory_order_relaxed);
+ }
+
+ uptr Get(AllocatorStat i) const {
return atomic_load(&stats_[i], memory_order_relaxed);
}
friend class AllocatorGlobalStats;
AllocatorStats *next_;
AllocatorStats *prev_;
- atomic_uint64_t stats_[AllocatorStatCount];
+ atomic_uintptr_t stats_[AllocatorStatCount];
};
// Global stats, used for aggregation and querying.
class AllocatorGlobalStats : public AllocatorStats {
public:
- void Init() {
- internal_memset(this, 0, sizeof(*this));
+ void InitLinkerInitialized() {
next_ = this;
prev_ = this;
}
+ void Init() {
+ internal_memset(this, 0, sizeof(*this));
+ InitLinkerInitialized();
+ }
void Register(AllocatorStats *s) {
SpinMutexLock l(&mu_);
}
void Get(AllocatorStatCounters s) const {
- internal_memset(s, 0, AllocatorStatCount * sizeof(u64));
+ internal_memset(s, 0, AllocatorStatCount * sizeof(uptr));
SpinMutexLock l(&mu_);
const AllocatorStats *stats = this;
for (;;) {
if (stats == this)
break;
}
+ // All stats must be non-negative.
+ for (int i = 0; i < AllocatorStatCount; i++)
+ s[i] = ((sptr)s[i]) >= 0 ? s[i] : 0;
}
private:
void Init() {
CHECK_EQ(kSpaceBeg,
- reinterpret_cast<uptr>(Mprotect(kSpaceBeg, kSpaceSize)));
+ reinterpret_cast<uptr>(MmapNoAccess(kSpaceBeg, kSpaceSize)));
MapWithCallback(kSpaceEnd, AdditionalSize());
}
CHECK_LT(class_id, kNumClasses);
RegionInfo *region = GetRegionInfo(class_id);
Batch *b = region->free_list.Pop();
- if (b == 0)
+ if (!b)
b = PopulateFreeList(stat, c, class_id, region);
region->n_allocated += b->count;
return b;
void *GetBlockBegin(const void *p) {
uptr class_id = GetSizeClass(p);
uptr size = SizeClassMap::Size(class_id);
- if (!size) return 0;
+ if (!size) return nullptr;
uptr chunk_idx = GetChunkIdx((uptr)p, size);
uptr reg_beg = (uptr)p & ~(kRegionSize - 1);
uptr beg = chunk_idx * size;
uptr next_beg = beg + size;
- if (class_id >= kNumClasses) return 0;
+ if (class_id >= kNumClasses) return nullptr;
RegionInfo *region = GetRegionInfo(class_id);
if (region->mapped_user >= next_beg)
return reinterpret_cast<void*>(reg_beg + beg);
- return 0;
+ return nullptr;
}
static uptr GetActuallyAllocatedSize(void *p) {
}
}
+ static uptr AdditionalSize() {
+ return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded,
+ GetPageSizeCached());
+ }
+
typedef SizeClassMap SizeClassMapT;
static const uptr kNumClasses = SizeClassMap::kNumClasses;
static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded;
};
COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize);
- static uptr AdditionalSize() {
- return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded,
- GetPageSizeCached());
- }
-
RegionInfo *GetRegionInfo(uptr class_id) {
CHECK_LT(class_id, kNumClasses);
RegionInfo *regions = reinterpret_cast<RegionInfo*>(kSpaceBeg + kSpaceSize);
map_size += kUserMapSize;
CHECK_GE(region->mapped_user + map_size, end_idx);
MapWithCallback(region_beg + region->mapped_user, map_size);
- stat->Add(AllocatorStatMmapped, map_size);
+ stat->Add(AllocatorStatMapped, map_size);
region->mapped_user += map_size;
}
uptr total_count = (region->mapped_user - beg_idx - size)
internal_memset(map1_, 0, sizeof(map1_));
mu_.Init();
}
+
void TestOnlyUnmap() {
for (uptr i = 0; i < kSize1; i++) {
u8 *p = Get(i);
void PrintStats() {
}
+ static uptr AdditionalSize() {
+ return 0;
+ }
+
typedef SizeClassMap SizeClassMapT;
static const uptr kNumClasses = SizeClassMap::kNumClasses;
uptr res = reinterpret_cast<uptr>(MmapAlignedOrDie(kRegionSize, kRegionSize,
"SizeClassAllocator32"));
MapUnmapCallback().OnMap(res, kRegionSize);
- stat->Add(AllocatorStatMmapped, kRegionSize);
+ stat->Add(AllocatorStatMapped, kRegionSize);
CHECK_EQ(0U, (res & (kRegionSize - 1)));
possible_regions.set(ComputeRegionId(res), static_cast<u8>(class_id));
return res;
uptr reg = AllocateRegion(stat, class_id);
uptr n_chunks = kRegionSize / (size + kMetadataSize);
uptr max_count = SizeClassMap::MaxCached(class_id);
- Batch *b = 0;
+ Batch *b = nullptr;
for (uptr i = reg; i < reg + n_chunks * size; i += size) {
- if (b == 0) {
+ if (!b) {
if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id))
b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch)));
else
if (b->count == max_count) {
CHECK_GT(b->count, 0);
sci->free_list.push_back(b);
- b = 0;
+ b = nullptr;
}
}
if (b) {
void *Allocate(SizeClassAllocator *allocator, uptr class_id) {
CHECK_NE(class_id, 0UL);
CHECK_LT(class_id, kNumClasses);
- stats_.Add(AllocatorStatMalloced, SizeClassMap::Size(class_id));
+ stats_.Add(AllocatorStatAllocated, SizeClassMap::Size(class_id));
PerClass *c = &per_class_[class_id];
if (UNLIKELY(c->count == 0))
Refill(allocator, class_id);
// If the first allocator call on a new thread is a deallocation, then
// max_count will be zero, leading to check failure.
InitCache();
- stats_.Add(AllocatorStatFreed, SizeClassMap::Size(class_id));
+ stats_.Sub(AllocatorStatAllocated, SizeClassMap::Size(class_id));
PerClass *c = &per_class_[class_id];
CHECK_NE(c->max_count, 0UL);
if (UNLIKELY(c->count == c->max_count))
template <class MapUnmapCallback = NoOpMapUnmapCallback>
class LargeMmapAllocator {
public:
- void Init() {
- internal_memset(this, 0, sizeof(*this));
+ void InitLinkerInitialized(bool may_return_null) {
page_size_ = GetPageSizeCached();
+ atomic_store(&may_return_null_, may_return_null, memory_order_relaxed);
+ }
+
+ void Init(bool may_return_null) {
+ internal_memset(this, 0, sizeof(*this));
+ InitLinkerInitialized(may_return_null);
}
void *Allocate(AllocatorStats *stat, uptr size, uptr alignment) {
uptr map_size = RoundUpMapSize(size);
if (alignment > page_size_)
map_size += alignment;
- if (map_size < size) return AllocatorReturnNull(); // Overflow.
+ // Overflow.
+ if (map_size < size)
+ return ReturnNullOrDie();
uptr map_beg = reinterpret_cast<uptr>(
MmapOrDie(map_size, "LargeMmapAllocator"));
+ CHECK(IsAligned(map_beg, page_size_));
MapUnmapCallback().OnMap(map_beg, map_size);
uptr map_end = map_beg + map_size;
uptr res = map_beg + page_size_;
if (res & (alignment - 1)) // Align.
res += alignment - (res & (alignment - 1));
- CHECK_EQ(0, res & (alignment - 1));
+ CHECK(IsAligned(res, alignment));
+ CHECK(IsAligned(res, page_size_));
+ CHECK_GE(res + size, map_beg);
CHECK_LE(res + size, map_end);
Header *h = GetHeader(res);
h->size = size;
stats.currently_allocated += map_size;
stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated);
stats.by_size_log[size_log]++;
- stat->Add(AllocatorStatMalloced, map_size);
- stat->Add(AllocatorStatMmapped, map_size);
+ stat->Add(AllocatorStatAllocated, map_size);
+ stat->Add(AllocatorStatMapped, map_size);
}
return reinterpret_cast<void*>(res);
}
+ void *ReturnNullOrDie() {
+ if (atomic_load(&may_return_null_, memory_order_acquire))
+ return nullptr;
+ ReportAllocatorCannotReturnNull();
+ }
+
+ void SetMayReturnNull(bool may_return_null) {
+ atomic_store(&may_return_null_, may_return_null, memory_order_release);
+ }
+
void Deallocate(AllocatorStats *stat, void *p) {
Header *h = GetHeader(p);
{
chunks_sorted_ = false;
stats.n_frees++;
stats.currently_allocated -= h->map_size;
- stat->Add(AllocatorStatFreed, h->map_size);
- stat->Add(AllocatorStatUnmapped, h->map_size);
+ stat->Sub(AllocatorStatAllocated, h->map_size);
+ stat->Sub(AllocatorStatMapped, h->map_size);
}
MapUnmapCallback().OnUnmap(h->map_beg, h->map_size);
UnmapOrDie(reinterpret_cast<void*>(h->map_beg), h->map_size);
}
bool PointerIsMine(const void *p) {
- return GetBlockBegin(p) != 0;
+ return GetBlockBegin(p) != nullptr;
}
uptr GetActuallyAllocatedSize(void *p) {
nearest_chunk = ch;
}
if (!nearest_chunk)
- return 0;
+ return nullptr;
Header *h = reinterpret_cast<Header *>(nearest_chunk);
CHECK_GE(nearest_chunk, h->map_beg);
CHECK_LT(nearest_chunk, h->map_beg + h->map_size);
CHECK_LE(nearest_chunk, p);
if (h->map_beg + h->map_size <= p)
- return 0;
+ return nullptr;
return GetUser(h);
}
mutex_.CheckLocked();
uptr p = reinterpret_cast<uptr>(ptr);
uptr n = n_chunks_;
- if (!n) return 0;
+ if (!n) return nullptr;
if (!chunks_sorted_) {
// Do one-time sort. chunks_sorted_ is reset in Allocate/Deallocate.
SortArray(reinterpret_cast<uptr*>(chunks_), n);
chunks_[n - 1]->map_size;
}
if (p < min_mmap_ || p >= max_mmap_)
- return 0;
+ return nullptr;
uptr beg = 0, end = n - 1;
// This loop is a log(n) lower_bound. It does not check for the exact match
// to avoid expensive cache-thrashing loads.
Header *h = chunks_[beg];
if (h->map_beg + h->map_size <= p || p < h->map_beg)
- return 0;
+ return nullptr;
return GetUser(h);
}
struct Stats {
uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64];
} stats;
+ atomic_uint8_t may_return_null_;
SpinMutex mutex_;
};
class SecondaryAllocator> // NOLINT
class CombinedAllocator {
public:
- void Init() {
+ void InitCommon(bool may_return_null) {
primary_.Init();
- secondary_.Init();
+ atomic_store(&may_return_null_, may_return_null, memory_order_relaxed);
+ }
+
+ void InitLinkerInitialized(bool may_return_null) {
+ secondary_.InitLinkerInitialized(may_return_null);
+ stats_.InitLinkerInitialized();
+ InitCommon(may_return_null);
+ }
+
+ void Init(bool may_return_null) {
+ secondary_.Init(may_return_null);
stats_.Init();
+ InitCommon(may_return_null);
}
void *Allocate(AllocatorCache *cache, uptr size, uptr alignment,
- bool cleared = false) {
+ bool cleared = false, bool check_rss_limit = false) {
// Returning 0 on malloc(0) may break a lot of code.
if (size == 0)
size = 1;
if (size + alignment < size)
- return AllocatorReturnNull();
+ return ReturnNullOrDie();
+ if (check_rss_limit && RssLimitIsExceeded())
+ return ReturnNullOrDie();
if (alignment > 8)
size = RoundUpTo(size, alignment);
void *res;
return res;
}
+ bool MayReturnNull() const {
+ return atomic_load(&may_return_null_, memory_order_acquire);
+ }
+
+ void *ReturnNullOrDie() {
+ if (MayReturnNull())
+ return nullptr;
+ ReportAllocatorCannotReturnNull();
+ }
+
+ void SetMayReturnNull(bool may_return_null) {
+ secondary_.SetMayReturnNull(may_return_null);
+ atomic_store(&may_return_null_, may_return_null, memory_order_release);
+ }
+
+ bool RssLimitIsExceeded() {
+ return atomic_load(&rss_limit_is_exceeded_, memory_order_acquire);
+ }
+
+ void SetRssLimitIsExceeded(bool rss_limit_is_exceeded) {
+ atomic_store(&rss_limit_is_exceeded_, rss_limit_is_exceeded,
+ memory_order_release);
+ }
+
void Deallocate(AllocatorCache *cache, void *p) {
if (!p) return;
if (primary_.PointerIsMine(p))
return Allocate(cache, new_size, alignment);
if (!new_size) {
Deallocate(cache, p);
- return 0;
+ return nullptr;
}
CHECK(PointerIsMine(p));
uptr old_size = GetActuallyAllocatedSize(p);
PrimaryAllocator primary_;
SecondaryAllocator secondary_;
AllocatorGlobalStats stats_;
+ atomic_uint8_t may_return_null_;
+ atomic_uint8_t rss_limit_is_exceeded_;
};
// Returns true if calloc(size, n) should return 0 due to overflow in size*n.
bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n);
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_ALLOCATOR_H
+#endif // SANITIZER_ALLOCATOR_H
--- /dev/null
+//===-- sanitizer_allocator_interface.h ------------------------- C++ -----===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Re-declaration of functions from public sanitizer allocator interface.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ALLOCATOR_INTERFACE_H
+#define SANITIZER_ALLOCATOR_INTERFACE_H
+
+#include "sanitizer_internal_defs.h"
+
+using __sanitizer::uptr;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_estimated_allocated_size(uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_get_ownership(const void *p);
+SANITIZER_INTERFACE_ATTRIBUTE uptr
+__sanitizer_get_allocated_size(const void *p);
+SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_current_allocated_bytes();
+SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_heap_size();
+SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_free_bytes();
+SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_unmapped_bytes();
+
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+ /* OPTIONAL */ void __sanitizer_malloc_hook(void *ptr, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+ /* OPTIONAL */ void __sanitizer_free_hook(void *ptr);
+} // extern "C"
+
+#endif // SANITIZER_ALLOCATOR_INTERFACE_H
-//===-- sanitizer_allocator_internal.h -------------------------- C++ -----===//
+//===-- sanitizer_allocator_internal.h --------------------------*- C++ -*-===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
typedef CompactSizeClassMap InternalSizeClassMap;
static const uptr kInternalAllocatorSpace = 0;
-#if SANITIZER_WORDSIZE == 32
-static const u64 kInternalAllocatorSize = (1ULL << 32);
+static const u64 kInternalAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
static const uptr kInternalAllocatorRegionSizeLog = 20;
+#if SANITIZER_WORDSIZE == 32
static const uptr kInternalAllocatorNumRegions =
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
typedef FlatByteMap<kInternalAllocatorNumRegions> ByteMap;
#else
-static const u64 kInternalAllocatorSize = (1ULL << 47);
-static const uptr kInternalAllocatorRegionSizeLog = 24;
static const uptr kInternalAllocatorNumRegions =
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap;
#endif
typedef SizeClassAllocator32<
- kInternalAllocatorSpace, kInternalAllocatorSize, 16, InternalSizeClassMap,
+ kInternalAllocatorSpace, kInternalAllocatorSize, 0, InternalSizeClassMap,
kInternalAllocatorRegionSizeLog, ByteMap> PrimaryInternalAllocator;
typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator>
InternalAllocatorCache;
-// We don't want our internal allocator to do any map/unmap operations from
-// LargeMmapAllocator.
-struct CrashOnMapUnmap {
- void OnMap(uptr p, uptr size) const {
- RAW_CHECK_MSG(0, "Unexpected mmap in InternalAllocator!");
- }
- void OnUnmap(uptr p, uptr size) const {
- RAW_CHECK_MSG(0, "Unexpected munmap in InternalAllocator!");
- }
-};
-
typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache,
- LargeMmapAllocator<CrashOnMapUnmap> >
- InternalAllocator;
+ LargeMmapAllocator<> > InternalAllocator;
-void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0);
-void InternalFree(void *p, InternalAllocatorCache *cache = 0);
+void *InternalAlloc(uptr size, InternalAllocatorCache *cache = nullptr);
+void InternalFree(void *p, InternalAllocatorCache *cache = nullptr);
InternalAllocator *internal_allocator();
-} // namespace __sanitizer
+enum InternalAllocEnum {
+ INTERNAL_ALLOC
+};
+
+} // namespace __sanitizer
+
+inline void *operator new(__sanitizer::operator_new_size_type size,
+ InternalAllocEnum) {
+ return InternalAlloc(size);
+}
-#endif // SANITIZER_ALLOCATOR_INTERNAL_H
+#endif // SANITIZER_ALLOCATOR_INTERNAL_H
# define CFI_STARTPROC .cfi_startproc
# define CFI_ENDPROC .cfi_endproc
# define CFI_ADJUST_CFA_OFFSET(n) .cfi_adjust_cfa_offset n
+# define CFI_DEF_CFA_OFFSET(n) .cfi_def_cfa_offset n
# define CFI_REL_OFFSET(reg, n) .cfi_rel_offset reg, n
+# define CFI_OFFSET(reg, n) .cfi_offset reg, n
# define CFI_DEF_CFA_REGISTER(reg) .cfi_def_cfa_register reg
+# define CFI_DEF_CFA(reg, n) .cfi_def_cfa reg, n
# define CFI_RESTORE(reg) .cfi_restore reg
#else // No CFI
# define CFI_STARTPROC
# define CFI_ENDPROC
# define CFI_ADJUST_CFA_OFFSET(n)
+# define CFI_DEF_CFA_OFFSET(n)
# define CFI_REL_OFFSET(reg, n)
+# define CFI_OFFSET(reg, n)
# define CFI_DEF_CFA_REGISTER(reg)
+# define CFI_DEF_CFA(reg, n)
# define CFI_RESTORE(reg)
#endif
struct atomic_uint64_t {
typedef u64 Type;
- volatile Type val_dont_use;
+ // On 32-bit platforms u64 is not necessary aligned on 8 bytes.
+ volatile ALIGNED(8) Type val_dont_use;
};
struct atomic_uintptr_t {
} // namespace __sanitizer
-#if defined(__GNUC__)
+#if defined(__clang__) || defined(__GNUC__)
# include "sanitizer_atomic_clang.h"
#elif defined(_MSC_VER)
# include "sanitizer_atomic_msvc.h"
# error "Unsupported compiler"
#endif
+namespace __sanitizer {
+
+// Clutter-reducing helpers.
+
+template<typename T>
+INLINE typename T::Type atomic_load_relaxed(const volatile T *a) {
+ return atomic_load(a, memory_order_relaxed);
+}
+
+template<typename T>
+INLINE void atomic_store_relaxed(volatile T *a, typename T::Type v) {
+ atomic_store(a, v, memory_order_relaxed);
+}
+
+} // namespace __sanitizer
+
#endif // SANITIZER_ATOMIC_H
#ifndef SANITIZER_ATOMIC_CLANG_H
#define SANITIZER_ATOMIC_CLANG_H
+#if defined(__i386__) || defined(__x86_64__)
+# include "sanitizer_atomic_clang_x86.h"
+#else
+# include "sanitizer_atomic_clang_other.h"
+#endif
+
namespace __sanitizer {
+// We would like to just use compiler builtin atomic operations
+// for loads and stores, but they are mostly broken in clang:
+// - they lead to vastly inefficient code generation
+// (http://llvm.org/bugs/show_bug.cgi?id=17281)
+// - 64-bit atomic operations are not implemented on x86_32
+// (http://llvm.org/bugs/show_bug.cgi?id=15034)
+// - they are not implemented on ARM
+// error: undefined reference to '__atomic_load_4'
+
+// See http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
+// for mappings of the memory model to different processors.
+
INLINE void atomic_signal_fence(memory_order) {
__asm__ __volatile__("" ::: "memory");
}
__sync_synchronize();
}
-INLINE void proc_yield(int cnt) {
- __asm__ __volatile__("" ::: "memory");
-#if defined(__i386__) || defined(__x86_64__)
- for (int i = 0; i < cnt; i++)
- __asm__ __volatile__("pause");
-#endif
- __asm__ __volatile__("" ::: "memory");
-}
-
-template<typename T>
-INLINE typename T::Type atomic_load(
- const volatile T *a, memory_order mo) {
- DCHECK(mo & (memory_order_relaxed | memory_order_consume
- | memory_order_acquire | memory_order_seq_cst));
- DCHECK(!((uptr)a % sizeof(*a)));
- typename T::Type v;
- // FIXME:
- // 64-bit atomic operations are not atomic on 32-bit platforms.
- // The implementation lacks necessary memory fences on ARM/PPC.
- // We would like to use compiler builtin atomic operations,
- // but they are mostly broken:
- // - they lead to vastly inefficient code generation
- // (http://llvm.org/bugs/show_bug.cgi?id=17281)
- // - 64-bit atomic operations are not implemented on x86_32
- // (http://llvm.org/bugs/show_bug.cgi?id=15034)
- // - they are not implemented on ARM
- // error: undefined reference to '__atomic_load_4'
- if (mo == memory_order_relaxed) {
- v = a->val_dont_use;
- } else {
- atomic_signal_fence(memory_order_seq_cst);
- v = a->val_dont_use;
- atomic_signal_fence(memory_order_seq_cst);
- }
- return v;
-}
-
-template<typename T>
-INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
- DCHECK(mo & (memory_order_relaxed | memory_order_release
- | memory_order_seq_cst));
- DCHECK(!((uptr)a % sizeof(*a)));
- if (mo == memory_order_relaxed) {
- a->val_dont_use = v;
- } else {
- atomic_signal_fence(memory_order_seq_cst);
- a->val_dont_use = v;
- atomic_signal_fence(memory_order_seq_cst);
- }
- if (mo == memory_order_seq_cst)
- atomic_thread_fence(memory_order_seq_cst);
-}
-
template<typename T>
INLINE typename T::Type atomic_fetch_add(volatile T *a,
typename T::Type v, memory_order mo) {
--- /dev/null
+//===-- sanitizer_atomic_clang_other.h --------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+// Not intended for direct inclusion. Include sanitizer_atomic.h.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
+#define SANITIZER_ATOMIC_CLANG_OTHER_H
+
+namespace __sanitizer {
+
+INLINE void proc_yield(int cnt) {
+ __asm__ __volatile__("" ::: "memory");
+}
+
+template<typename T>
+INLINE typename T::Type atomic_load(
+ const volatile T *a, memory_order mo) {
+ DCHECK(mo & (memory_order_relaxed | memory_order_consume
+ | memory_order_acquire | memory_order_seq_cst));
+ DCHECK(!((uptr)a % sizeof(*a)));
+ typename T::Type v;
+
+ if (sizeof(*a) < 8 || sizeof(void*) == 8) {
+ // Assume that aligned loads are atomic.
+ if (mo == memory_order_relaxed) {
+ v = a->val_dont_use;
+ } else if (mo == memory_order_consume) {
+ // Assume that processor respects data dependencies
+ // (and that compiler won't break them).
+ __asm__ __volatile__("" ::: "memory");
+ v = a->val_dont_use;
+ __asm__ __volatile__("" ::: "memory");
+ } else if (mo == memory_order_acquire) {
+ __asm__ __volatile__("" ::: "memory");
+ v = a->val_dont_use;
+ __sync_synchronize();
+ } else { // seq_cst
+ // E.g. on POWER we need a hw fence even before the store.
+ __sync_synchronize();
+ v = a->val_dont_use;
+ __sync_synchronize();
+ }
+ } else {
+ // 64-bit load on 32-bit platform.
+ // Gross, but simple and reliable.
+ // Assume that it is not in read-only memory.
+ v = __sync_fetch_and_add(
+ const_cast<typename T::Type volatile *>(&a->val_dont_use), 0);
+ }
+ return v;
+}
+
+template<typename T>
+INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
+ DCHECK(mo & (memory_order_relaxed | memory_order_release
+ | memory_order_seq_cst));
+ DCHECK(!((uptr)a % sizeof(*a)));
+
+ if (sizeof(*a) < 8 || sizeof(void*) == 8) {
+ // Assume that aligned loads are atomic.
+ if (mo == memory_order_relaxed) {
+ a->val_dont_use = v;
+ } else if (mo == memory_order_release) {
+ __sync_synchronize();
+ a->val_dont_use = v;
+ __asm__ __volatile__("" ::: "memory");
+ } else { // seq_cst
+ __sync_synchronize();
+ a->val_dont_use = v;
+ __sync_synchronize();
+ }
+ } else {
+ // 64-bit store on 32-bit platform.
+ // Gross, but simple and reliable.
+ typename T::Type cmp = a->val_dont_use;
+ typename T::Type cur;
+ for (;;) {
+ cur = __sync_val_compare_and_swap(&a->val_dont_use, cmp, v);
+ if (cmp == v)
+ break;
+ cmp = cur;
+ }
+ }
+}
+
+} // namespace __sanitizer
+
+#endif // #ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
--- /dev/null
+//===-- sanitizer_atomic_clang_x86.h ----------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+// Not intended for direct inclusion. Include sanitizer_atomic.h.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ATOMIC_CLANG_X86_H
+#define SANITIZER_ATOMIC_CLANG_X86_H
+
+namespace __sanitizer {
+
+INLINE void proc_yield(int cnt) {
+ __asm__ __volatile__("" ::: "memory");
+ for (int i = 0; i < cnt; i++)
+ __asm__ __volatile__("pause");
+ __asm__ __volatile__("" ::: "memory");
+}
+
+template<typename T>
+INLINE typename T::Type atomic_load(
+ const volatile T *a, memory_order mo) {
+ DCHECK(mo & (memory_order_relaxed | memory_order_consume
+ | memory_order_acquire | memory_order_seq_cst));
+ DCHECK(!((uptr)a % sizeof(*a)));
+ typename T::Type v;
+
+ if (sizeof(*a) < 8 || sizeof(void*) == 8) {
+ // Assume that aligned loads are atomic.
+ if (mo == memory_order_relaxed) {
+ v = a->val_dont_use;
+ } else if (mo == memory_order_consume) {
+ // Assume that processor respects data dependencies
+ // (and that compiler won't break them).
+ __asm__ __volatile__("" ::: "memory");
+ v = a->val_dont_use;
+ __asm__ __volatile__("" ::: "memory");
+ } else if (mo == memory_order_acquire) {
+ __asm__ __volatile__("" ::: "memory");
+ v = a->val_dont_use;
+ // On x86 loads are implicitly acquire.
+ __asm__ __volatile__("" ::: "memory");
+ } else { // seq_cst
+ // On x86 plain MOV is enough for seq_cst store.
+ __asm__ __volatile__("" ::: "memory");
+ v = a->val_dont_use;
+ __asm__ __volatile__("" ::: "memory");
+ }
+ } else {
+ // 64-bit load on 32-bit platform.
+ __asm__ __volatile__(
+ "movq %1, %%mm0;" // Use mmx reg for 64-bit atomic moves
+ "movq %%mm0, %0;" // (ptr could be read-only)
+ "emms;" // Empty mmx state/Reset FP regs
+ : "=m" (v)
+ : "m" (a->val_dont_use)
+ : // mark the FP stack and mmx registers as clobbered
+ "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)",
+#ifdef __MMX__
+ "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+#endif // #ifdef __MMX__
+ "memory");
+ }
+ return v;
+}
+
+template<typename T>
+INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
+ DCHECK(mo & (memory_order_relaxed | memory_order_release
+ | memory_order_seq_cst));
+ DCHECK(!((uptr)a % sizeof(*a)));
+
+ if (sizeof(*a) < 8 || sizeof(void*) == 8) {
+ // Assume that aligned loads are atomic.
+ if (mo == memory_order_relaxed) {
+ a->val_dont_use = v;
+ } else if (mo == memory_order_release) {
+ // On x86 stores are implicitly release.
+ __asm__ __volatile__("" ::: "memory");
+ a->val_dont_use = v;
+ __asm__ __volatile__("" ::: "memory");
+ } else { // seq_cst
+ // On x86 stores are implicitly release.
+ __asm__ __volatile__("" ::: "memory");
+ a->val_dont_use = v;
+ __sync_synchronize();
+ }
+ } else {
+ // 64-bit store on 32-bit platform.
+ __asm__ __volatile__(
+ "movq %1, %%mm0;" // Use mmx reg for 64-bit atomic moves
+ "movq %%mm0, %0;"
+ "emms;" // Empty mmx state/Reset FP regs
+ : "=m" (a->val_dont_use)
+ : "m" (v)
+ : // mark the FP stack and mmx registers as clobbered
+ "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)",
+#ifdef __MMX__
+ "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+#endif // #ifdef __MMX__
+ "memory");
+ if (mo == memory_order_seq_cst)
+ __sync_synchronize();
+ }
+}
+
+} // namespace __sanitizer
+
+#endif // #ifndef SANITIZER_ATOMIC_CLANG_X86_H
#pragma intrinsic(_mm_mfence)
extern "C" void _mm_pause();
#pragma intrinsic(_mm_pause)
+extern "C" char _InterlockedExchange8( // NOLINT
+ char volatile *Addend, char Value); // NOLINT
+#pragma intrinsic(_InterlockedExchange8)
+extern "C" short _InterlockedExchange16( // NOLINT
+ short volatile *Addend, short Value); // NOLINT
+#pragma intrinsic(_InterlockedExchange16)
+extern "C" long _InterlockedExchange( // NOLINT
+ long volatile *Addend, long Value); // NOLINT
+#pragma intrinsic(_InterlockedExchange)
extern "C" long _InterlockedExchangeAdd( // NOLINT
long volatile * Addend, long Value); // NOLINT
#pragma intrinsic(_InterlockedExchangeAdd)
-
-#ifdef _WIN64
+extern "C" short _InterlockedCompareExchange16( // NOLINT
+ short volatile *Destination, // NOLINT
+ short Exchange, short Comparand); // NOLINT
+#pragma intrinsic(_InterlockedCompareExchange16)
+extern "C"
+long long _InterlockedCompareExchange64( // NOLINT
+ long long volatile *Destination, // NOLINT
+ long long Exchange, long long Comparand); // NOLINT
+#pragma intrinsic(_InterlockedCompareExchange64)
extern "C" void *_InterlockedCompareExchangePointer(
void *volatile *Destination,
void *Exchange, void *Comparand);
#pragma intrinsic(_InterlockedCompareExchangePointer)
-#else
-// There's no _InterlockedCompareExchangePointer intrinsic on x86,
-// so call _InterlockedCompareExchange instead.
extern "C"
long __cdecl _InterlockedCompareExchange( // NOLINT
long volatile *Destination, // NOLINT
long Exchange, long Comparand); // NOLINT
#pragma intrinsic(_InterlockedCompareExchange)
-inline static void *_InterlockedCompareExchangePointer(
- void *volatile *Destination,
- void *Exchange, void *Comparand) {
- return reinterpret_cast<void*>(
- _InterlockedCompareExchange(
- reinterpret_cast<long volatile*>(Destination), // NOLINT
- reinterpret_cast<long>(Exchange), // NOLINT
- reinterpret_cast<long>(Comparand))); // NOLINT
-}
+#ifdef _WIN64
+extern "C" long long _InterlockedExchangeAdd64( // NOLINT
+ long long volatile * Addend, long long Value); // NOLINT
+#pragma intrinsic(_InterlockedExchangeAdd64)
#endif
namespace __sanitizer {
(volatile long*)&a->val_dont_use, (long)v); // NOLINT
}
+INLINE uptr atomic_fetch_add(volatile atomic_uintptr_t *a,
+ uptr v, memory_order mo) {
+ (void)mo;
+ DCHECK(!((uptr)a % sizeof(*a)));
+#ifdef _WIN64
+ return (uptr)_InterlockedExchangeAdd64(
+ (volatile long long*)&a->val_dont_use, (long long)v); // NOLINT
+#else
+ return (uptr)_InterlockedExchangeAdd(
+ (volatile long*)&a->val_dont_use, (long)v); // NOLINT
+#endif
+}
+
+INLINE u32 atomic_fetch_sub(volatile atomic_uint32_t *a,
+ u32 v, memory_order mo) {
+ (void)mo;
+ DCHECK(!((uptr)a % sizeof(*a)));
+ return (u32)_InterlockedExchangeAdd(
+ (volatile long*)&a->val_dont_use, -(long)v); // NOLINT
+}
+
+INLINE uptr atomic_fetch_sub(volatile atomic_uintptr_t *a,
+ uptr v, memory_order mo) {
+ (void)mo;
+ DCHECK(!((uptr)a % sizeof(*a)));
+#ifdef _WIN64
+ return (uptr)_InterlockedExchangeAdd64(
+ (volatile long long*)&a->val_dont_use, -(long long)v); // NOLINT
+#else
+ return (uptr)_InterlockedExchangeAdd(
+ (volatile long*)&a->val_dont_use, -(long)v); // NOLINT
+#endif
+}
+
INLINE u8 atomic_exchange(volatile atomic_uint8_t *a,
u8 v, memory_order mo) {
(void)mo;
DCHECK(!((uptr)a % sizeof(*a)));
- __asm {
- mov eax, a
- mov cl, v
- xchg [eax], cl // NOLINT
- mov v, cl
- }
- return v;
+ return (u8)_InterlockedExchange8((volatile char*)&a->val_dont_use, v);
}
INLINE u16 atomic_exchange(volatile atomic_uint16_t *a,
u16 v, memory_order mo) {
(void)mo;
DCHECK(!((uptr)a % sizeof(*a)));
- __asm {
- mov eax, a
- mov cx, v
- xchg [eax], cx // NOLINT
- mov v, cx
- }
- return v;
+ return (u16)_InterlockedExchange16((volatile short*)&a->val_dont_use, v);
+}
+
+INLINE u32 atomic_exchange(volatile atomic_uint32_t *a,
+ u32 v, memory_order mo) {
+ (void)mo;
+ DCHECK(!((uptr)a % sizeof(*a)));
+ return (u32)_InterlockedExchange((volatile long*)&a->val_dont_use, v);
}
+#ifndef _WIN64
+
INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a,
u8 *cmp,
u8 xchgv,
return false;
}
+#endif
+
INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a,
uptr *cmp,
uptr xchg,
return false;
}
+INLINE bool atomic_compare_exchange_strong(volatile atomic_uint16_t *a,
+ u16 *cmp,
+ u16 xchg,
+ memory_order mo) {
+ u16 cmpv = *cmp;
+ u16 prev = (u16)_InterlockedCompareExchange16(
+ (volatile short*)&a->val_dont_use, (short)xchg, (short)cmpv);
+ if (prev == cmpv)
+ return true;
+ *cmp = prev;
+ return false;
+}
+
+INLINE bool atomic_compare_exchange_strong(volatile atomic_uint32_t *a,
+ u32 *cmp,
+ u32 xchg,
+ memory_order mo) {
+ u32 cmpv = *cmp;
+ u32 prev = (u32)_InterlockedCompareExchange(
+ (volatile long*)&a->val_dont_use, (long)xchg, (long)cmpv);
+ if (prev == cmpv)
+ return true;
+ *cmp = prev;
+ return false;
+}
+
+INLINE bool atomic_compare_exchange_strong(volatile atomic_uint64_t *a,
+ u64 *cmp,
+ u64 xchg,
+ memory_order mo) {
+ u64 cmpv = *cmp;
+ u64 prev = (u64)_InterlockedCompareExchange64(
+ (volatile long long*)&a->val_dont_use, (long long)xchg, (long long)cmpv);
+ if (prev == cmpv)
+ return true;
+ *cmp = prev;
+ return false;
+}
+
template<typename T>
INLINE bool atomic_compare_exchange_weak(volatile T *a,
typename T::Type *cmp,
--- /dev/null
+//===-- sanitizer_bitvector.h -----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Specializer BitVector implementation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_BITVECTOR_H
+#define SANITIZER_BITVECTOR_H
+
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+// Fixed size bit vector based on a single basic integer.
+template <class basic_int_t = uptr>
+class BasicBitVector {
+ public:
+ enum SizeEnum { kSize = sizeof(basic_int_t) * 8 };
+
+ uptr size() const { return kSize; }
+ // No CTOR.
+ void clear() { bits_ = 0; }
+ void setAll() { bits_ = ~(basic_int_t)0; }
+ bool empty() const { return bits_ == 0; }
+
+ // Returns true if the bit has changed from 0 to 1.
+ bool setBit(uptr idx) {
+ basic_int_t old = bits_;
+ bits_ |= mask(idx);
+ return bits_ != old;
+ }
+
+ // Returns true if the bit has changed from 1 to 0.
+ bool clearBit(uptr idx) {
+ basic_int_t old = bits_;
+ bits_ &= ~mask(idx);
+ return bits_ != old;
+ }
+
+ bool getBit(uptr idx) const { return (bits_ & mask(idx)) != 0; }
+
+ uptr getAndClearFirstOne() {
+ CHECK(!empty());
+ uptr idx = LeastSignificantSetBitIndex(bits_);
+ clearBit(idx);
+ return idx;
+ }
+
+ // Do "this |= v" and return whether new bits have been added.
+ bool setUnion(const BasicBitVector &v) {
+ basic_int_t old = bits_;
+ bits_ |= v.bits_;
+ return bits_ != old;
+ }
+
+ // Do "this &= v" and return whether any bits have been removed.
+ bool setIntersection(const BasicBitVector &v) {
+ basic_int_t old = bits_;
+ bits_ &= v.bits_;
+ return bits_ != old;
+ }
+
+ // Do "this &= ~v" and return whether any bits have been removed.
+ bool setDifference(const BasicBitVector &v) {
+ basic_int_t old = bits_;
+ bits_ &= ~v.bits_;
+ return bits_ != old;
+ }
+
+ void copyFrom(const BasicBitVector &v) { bits_ = v.bits_; }
+
+ // Returns true if 'this' intersects with 'v'.
+ bool intersectsWith(const BasicBitVector &v) const {
+ return (bits_ & v.bits_) != 0;
+ }
+
+ // for (BasicBitVector<>::Iterator it(bv); it.hasNext();) {
+ // uptr idx = it.next();
+ // use(idx);
+ // }
+ class Iterator {
+ public:
+ Iterator() { }
+ explicit Iterator(const BasicBitVector &bv) : bv_(bv) {}
+ bool hasNext() const { return !bv_.empty(); }
+ uptr next() { return bv_.getAndClearFirstOne(); }
+ void clear() { bv_.clear(); }
+ private:
+ BasicBitVector bv_;
+ };
+
+ private:
+ basic_int_t mask(uptr idx) const {
+ CHECK_LT(idx, size());
+ return (basic_int_t)1UL << idx;
+ }
+ basic_int_t bits_;
+};
+
+// Fixed size bit vector of (kLevel1Size*BV::kSize**2) bits.
+// The implementation is optimized for better performance on
+// sparse bit vectors, i.e. the those with few set bits.
+template <uptr kLevel1Size = 1, class BV = BasicBitVector<> >
+class TwoLevelBitVector {
+ // This is essentially a 2-level bit vector.
+ // Set bit in the first level BV indicates that there are set bits
+ // in the corresponding BV of the second level.
+ // This structure allows O(kLevel1Size) time for clear() and empty(),
+ // as well fast handling of sparse BVs.
+ public:
+ enum SizeEnum { kSize = BV::kSize * BV::kSize * kLevel1Size };
+ // No CTOR.
+
+ uptr size() const { return kSize; }
+
+ void clear() {
+ for (uptr i = 0; i < kLevel1Size; i++)
+ l1_[i].clear();
+ }
+
+ void setAll() {
+ for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+ l1_[i0].setAll();
+ for (uptr i1 = 0; i1 < BV::kSize; i1++)
+ l2_[i0][i1].setAll();
+ }
+ }
+
+ bool empty() const {
+ for (uptr i = 0; i < kLevel1Size; i++)
+ if (!l1_[i].empty())
+ return false;
+ return true;
+ }
+
+ // Returns true if the bit has changed from 0 to 1.
+ bool setBit(uptr idx) {
+ check(idx);
+ uptr i0 = idx0(idx);
+ uptr i1 = idx1(idx);
+ uptr i2 = idx2(idx);
+ if (!l1_[i0].getBit(i1)) {
+ l1_[i0].setBit(i1);
+ l2_[i0][i1].clear();
+ }
+ bool res = l2_[i0][i1].setBit(i2);
+ // Printf("%s: %zd => %zd %zd %zd; %d\n", __func__,
+ // idx, i0, i1, i2, res);
+ return res;
+ }
+
+ bool clearBit(uptr idx) {
+ check(idx);
+ uptr i0 = idx0(idx);
+ uptr i1 = idx1(idx);
+ uptr i2 = idx2(idx);
+ bool res = false;
+ if (l1_[i0].getBit(i1)) {
+ res = l2_[i0][i1].clearBit(i2);
+ if (l2_[i0][i1].empty())
+ l1_[i0].clearBit(i1);
+ }
+ return res;
+ }
+
+ bool getBit(uptr idx) const {
+ check(idx);
+ uptr i0 = idx0(idx);
+ uptr i1 = idx1(idx);
+ uptr i2 = idx2(idx);
+ // Printf("%s: %zd => %zd %zd %zd\n", __func__, idx, i0, i1, i2);
+ return l1_[i0].getBit(i1) && l2_[i0][i1].getBit(i2);
+ }
+
+ uptr getAndClearFirstOne() {
+ for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+ if (l1_[i0].empty()) continue;
+ uptr i1 = l1_[i0].getAndClearFirstOne();
+ uptr i2 = l2_[i0][i1].getAndClearFirstOne();
+ if (!l2_[i0][i1].empty())
+ l1_[i0].setBit(i1);
+ uptr res = i0 * BV::kSize * BV::kSize + i1 * BV::kSize + i2;
+ // Printf("getAndClearFirstOne: %zd %zd %zd => %zd\n", i0, i1, i2, res);
+ return res;
+ }
+ CHECK(0);
+ return 0;
+ }
+
+ // Do "this |= v" and return whether new bits have been added.
+ bool setUnion(const TwoLevelBitVector &v) {
+ bool res = false;
+ for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+ BV t = v.l1_[i0];
+ while (!t.empty()) {
+ uptr i1 = t.getAndClearFirstOne();
+ if (l1_[i0].setBit(i1))
+ l2_[i0][i1].clear();
+ if (l2_[i0][i1].setUnion(v.l2_[i0][i1]))
+ res = true;
+ }
+ }
+ return res;
+ }
+
+ // Do "this &= v" and return whether any bits have been removed.
+ bool setIntersection(const TwoLevelBitVector &v) {
+ bool res = false;
+ for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+ if (l1_[i0].setIntersection(v.l1_[i0]))
+ res = true;
+ if (!l1_[i0].empty()) {
+ BV t = l1_[i0];
+ while (!t.empty()) {
+ uptr i1 = t.getAndClearFirstOne();
+ if (l2_[i0][i1].setIntersection(v.l2_[i0][i1]))
+ res = true;
+ if (l2_[i0][i1].empty())
+ l1_[i0].clearBit(i1);
+ }
+ }
+ }
+ return res;
+ }
+
+ // Do "this &= ~v" and return whether any bits have been removed.
+ bool setDifference(const TwoLevelBitVector &v) {
+ bool res = false;
+ for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+ BV t = l1_[i0];
+ t.setIntersection(v.l1_[i0]);
+ while (!t.empty()) {
+ uptr i1 = t.getAndClearFirstOne();
+ if (l2_[i0][i1].setDifference(v.l2_[i0][i1]))
+ res = true;
+ if (l2_[i0][i1].empty())
+ l1_[i0].clearBit(i1);
+ }
+ }
+ return res;
+ }
+
+ void copyFrom(const TwoLevelBitVector &v) {
+ clear();
+ setUnion(v);
+ }
+
+ // Returns true if 'this' intersects with 'v'.
+ bool intersectsWith(const TwoLevelBitVector &v) const {
+ for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
+ BV t = l1_[i0];
+ t.setIntersection(v.l1_[i0]);
+ while (!t.empty()) {
+ uptr i1 = t.getAndClearFirstOne();
+ if (!v.l1_[i0].getBit(i1)) continue;
+ if (l2_[i0][i1].intersectsWith(v.l2_[i0][i1]))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // for (TwoLevelBitVector<>::Iterator it(bv); it.hasNext();) {
+ // uptr idx = it.next();
+ // use(idx);
+ // }
+ class Iterator {
+ public:
+ Iterator() { }
+ explicit Iterator(const TwoLevelBitVector &bv) : bv_(bv), i0_(0), i1_(0) {
+ it1_.clear();
+ it2_.clear();
+ }
+
+ bool hasNext() const {
+ if (it1_.hasNext()) return true;
+ for (uptr i = i0_; i < kLevel1Size; i++)
+ if (!bv_.l1_[i].empty()) return true;
+ return false;
+ }
+
+ uptr next() {
+ // Printf("++++: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
+ // it2_.hasNext(), kSize);
+ if (!it1_.hasNext() && !it2_.hasNext()) {
+ for (; i0_ < kLevel1Size; i0_++) {
+ if (bv_.l1_[i0_].empty()) continue;
+ it1_ = typename BV::Iterator(bv_.l1_[i0_]);
+ // Printf("+i0: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
+ // it2_.hasNext(), kSize);
+ break;
+ }
+ }
+ if (!it2_.hasNext()) {
+ CHECK(it1_.hasNext());
+ i1_ = it1_.next();
+ it2_ = typename BV::Iterator(bv_.l2_[i0_][i1_]);
+ // Printf("++i1: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
+ // it2_.hasNext(), kSize);
+ }
+ CHECK(it2_.hasNext());
+ uptr i2 = it2_.next();
+ uptr res = i0_ * BV::kSize * BV::kSize + i1_ * BV::kSize + i2;
+ // Printf("+ret: %zd %zd; %d %d; size %zd; res: %zd\n", i0_, i1_,
+ // it1_.hasNext(), it2_.hasNext(), kSize, res);
+ if (!it1_.hasNext() && !it2_.hasNext())
+ i0_++;
+ return res;
+ }
+
+ private:
+ const TwoLevelBitVector &bv_;
+ uptr i0_, i1_;
+ typename BV::Iterator it1_, it2_;
+ };
+
+ private:
+ void check(uptr idx) const { CHECK_LE(idx, size()); }
+
+ uptr idx0(uptr idx) const {
+ uptr res = idx / (BV::kSize * BV::kSize);
+ CHECK_LE(res, kLevel1Size);
+ return res;
+ }
+
+ uptr idx1(uptr idx) const {
+ uptr res = (idx / BV::kSize) % BV::kSize;
+ CHECK_LE(res, BV::kSize);
+ return res;
+ }
+
+ uptr idx2(uptr idx) const {
+ uptr res = idx % BV::kSize;
+ CHECK_LE(res, BV::kSize);
+ return res;
+ }
+
+ BV l1_[kLevel1Size];
+ BV l2_[kLevel1Size][BV::kSize];
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_BITVECTOR_H
--- /dev/null
+//===-- sanitizer_bvgraph.h -------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer runtime.
+// BVGraph -- a directed graph.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_BVGRAPH_H
+#define SANITIZER_BVGRAPH_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_bitvector.h"
+
+namespace __sanitizer {
+
+// Directed graph of fixed size implemented as an array of bit vectors.
+// Not thread-safe, all accesses should be protected by an external lock.
+template<class BV>
+class BVGraph {
+ public:
+ enum SizeEnum { kSize = BV::kSize };
+ uptr size() const { return kSize; }
+ // No CTOR.
+ void clear() {
+ for (uptr i = 0; i < size(); i++)
+ v[i].clear();
+ }
+
+ bool empty() const {
+ for (uptr i = 0; i < size(); i++)
+ if (!v[i].empty())
+ return false;
+ return true;
+ }
+
+ // Returns true if a new edge was added.
+ bool addEdge(uptr from, uptr to) {
+ check(from, to);
+ return v[from].setBit(to);
+ }
+
+ // Returns true if at least one new edge was added.
+ uptr addEdges(const BV &from, uptr to, uptr added_edges[],
+ uptr max_added_edges) {
+ uptr res = 0;
+ t1.copyFrom(from);
+ while (!t1.empty()) {
+ uptr node = t1.getAndClearFirstOne();
+ if (v[node].setBit(to))
+ if (res < max_added_edges)
+ added_edges[res++] = node;
+ }
+ return res;
+ }
+
+ // *EXPERIMENTAL*
+ // Returns true if an edge from=>to exist.
+ // This function does not use any global state except for 'this' itself,
+ // and thus can be called from different threads w/o locking.
+ // This would be racy.
+ // FIXME: investigate how much we can prove about this race being "benign".
+ bool hasEdge(uptr from, uptr to) { return v[from].getBit(to); }
+
+ // Returns true if the edge from=>to was removed.
+ bool removeEdge(uptr from, uptr to) {
+ return v[from].clearBit(to);
+ }
+
+ // Returns true if at least one edge *=>to was removed.
+ bool removeEdgesTo(const BV &to) {
+ bool res = 0;
+ for (uptr from = 0; from < size(); from++) {
+ if (v[from].setDifference(to))
+ res = true;
+ }
+ return res;
+ }
+
+ // Returns true if at least one edge from=>* was removed.
+ bool removeEdgesFrom(const BV &from) {
+ bool res = false;
+ t1.copyFrom(from);
+ while (!t1.empty()) {
+ uptr idx = t1.getAndClearFirstOne();
+ if (!v[idx].empty()) {
+ v[idx].clear();
+ res = true;
+ }
+ }
+ return res;
+ }
+
+ void removeEdgesFrom(uptr from) {
+ return v[from].clear();
+ }
+
+ bool hasEdge(uptr from, uptr to) const {
+ check(from, to);
+ return v[from].getBit(to);
+ }
+
+ // Returns true if there is a path from the node 'from'
+ // to any of the nodes in 'targets'.
+ bool isReachable(uptr from, const BV &targets) {
+ BV &to_visit = t1,
+ &visited = t2;
+ to_visit.copyFrom(v[from]);
+ visited.clear();
+ visited.setBit(from);
+ while (!to_visit.empty()) {
+ uptr idx = to_visit.getAndClearFirstOne();
+ if (visited.setBit(idx))
+ to_visit.setUnion(v[idx]);
+ }
+ return targets.intersectsWith(visited);
+ }
+
+ // Finds a path from 'from' to one of the nodes in 'target',
+ // stores up to 'path_size' items of the path into 'path',
+ // returns the path length, or 0 if there is no path of size 'path_size'.
+ uptr findPath(uptr from, const BV &targets, uptr *path, uptr path_size) {
+ if (path_size == 0)
+ return 0;
+ path[0] = from;
+ if (targets.getBit(from))
+ return 1;
+ // The function is recursive, so we don't want to create BV on stack.
+ // Instead of a getAndClearFirstOne loop we use the slower iterator.
+ for (typename BV::Iterator it(v[from]); it.hasNext(); ) {
+ uptr idx = it.next();
+ if (uptr res = findPath(idx, targets, path + 1, path_size - 1))
+ return res + 1;
+ }
+ return 0;
+ }
+
+ // Same as findPath, but finds a shortest path.
+ uptr findShortestPath(uptr from, const BV &targets, uptr *path,
+ uptr path_size) {
+ for (uptr p = 1; p <= path_size; p++)
+ if (findPath(from, targets, path, p) == p)
+ return p;
+ return 0;
+ }
+
+ private:
+ void check(uptr idx1, uptr idx2) const {
+ CHECK_LT(idx1, size());
+ CHECK_LT(idx2, size());
+ }
+ BV v[kSize];
+ // Keep temporary vectors here since we can not create large objects on stack.
+ BV t1, t2;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_BVGRAPH_H
//===----------------------------------------------------------------------===//
#include "sanitizer_common.h"
+#include "sanitizer_allocator_internal.h"
#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
-#include "sanitizer_stacktrace.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_stacktrace_printer.h"
#include "sanitizer_symbolizer.h"
namespace __sanitizer {
const char *SanitizerToolName = "SanitizerTool";
+atomic_uint32_t current_verbosity;
+
uptr GetPageSizeCached() {
static uptr PageSize;
if (!PageSize)
return PageSize;
}
+StaticSpinMutex report_file_mu;
+ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
+
+void RawWrite(const char *buffer) {
+ report_file.Write(buffer, internal_strlen(buffer));
+}
-// By default, dump to stderr. If |log_to_file| is true and |report_fd_pid|
-// isn't equal to the current PID, try to obtain file descriptor by opening
-// file "report_path_prefix.<PID>".
-fd_t report_fd = kStderrFd;
+void ReportFile::ReopenIfNecessary() {
+ mu->CheckLocked();
+ if (fd == kStdoutFd || fd == kStderrFd) return;
+
+ uptr pid = internal_getpid();
+ // If in tracer, use the parent's file.
+ if (pid == stoptheworld_tracer_pid)
+ pid = stoptheworld_tracer_ppid;
+ if (fd != kInvalidFd) {
+ // If the report file is already opened by the current process,
+ // do nothing. Otherwise the report file was opened by the parent
+ // process, close it now.
+ if (fd_pid == pid)
+ return;
+ else
+ CloseFile(fd);
+ }
-// Set via __sanitizer_set_report_path.
-bool log_to_file = false;
-char report_path_prefix[sizeof(report_path_prefix)];
+ const char *exe_name = GetProcessName();
+ if (common_flags()->log_exe_name && exe_name) {
+ internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
+ exe_name, pid);
+ } else {
+ internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
+ }
+ fd = OpenFile(full_path, WrOnly);
+ if (fd == kInvalidFd) {
+ const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
+ WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
+ WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
+ Die();
+ }
+ fd_pid = pid;
+}
-// PID of process that opened |report_fd|. If a fork() occurs, the PID of the
-// child thread will be different from |report_fd_pid|.
-uptr report_fd_pid = 0;
+void ReportFile::SetReportPath(const char *path) {
+ if (!path)
+ return;
+ uptr len = internal_strlen(path);
+ if (len > sizeof(path_prefix) - 100) {
+ Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
+ path[0], path[1], path[2], path[3],
+ path[4], path[5], path[6], path[7]);
+ Die();
+ }
+
+ SpinMutexLock l(mu);
+ if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
+ CloseFile(fd);
+ fd = kInvalidFd;
+ if (internal_strcmp(path, "stdout") == 0) {
+ fd = kStdoutFd;
+ } else if (internal_strcmp(path, "stderr") == 0) {
+ fd = kStderrFd;
+ } else {
+ internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
+ }
+}
// PID of the tracer task in StopTheWorld. It shares the address space with the
// main process, but has a different PID and thus requires special handling.
// writing to the same log file.
uptr stoptheworld_tracer_ppid = 0;
-static DieCallbackType DieCallback;
-void SetDieCallback(DieCallbackType callback) {
- DieCallback = callback;
+static const int kMaxNumOfInternalDieCallbacks = 5;
+static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks];
+
+bool AddDieCallback(DieCallbackType callback) {
+ for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
+ if (InternalDieCallbacks[i] == nullptr) {
+ InternalDieCallbacks[i] = callback;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool RemoveDieCallback(DieCallbackType callback) {
+ for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
+ if (InternalDieCallbacks[i] == callback) {
+ internal_memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1],
+ sizeof(InternalDieCallbacks[0]) *
+ (kMaxNumOfInternalDieCallbacks - i - 1));
+ InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr;
+ return true;
+ }
+ }
+ return false;
}
-DieCallbackType GetDieCallback() {
- return DieCallback;
+static DieCallbackType UserDieCallback;
+void SetUserDieCallback(DieCallbackType callback) {
+ UserDieCallback = callback;
}
void NORETURN Die() {
- if (DieCallback) {
- DieCallback();
+ if (UserDieCallback)
+ UserDieCallback();
+ for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) {
+ if (InternalDieCallbacks[i])
+ InternalDieCallbacks[i]();
}
- internal__exit(1);
+ if (common_flags()->abort_on_error)
+ Abort();
+ internal__exit(common_flags()->exitcode);
}
static CheckFailedCallbackType CheckFailedCallback;
Die();
}
-uptr ReadFileToBuffer(const char *file_name, char **buff,
- uptr *buff_size, uptr max_len) {
+void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
+ const char *mmap_type, error_t err) {
+ static int recursion_count;
+ if (recursion_count) {
+ // The Report() and CHECK calls below may call mmap recursively and fail.
+ // If we went into recursion, just die.
+ RawWrite("ERROR: Failed to mmap\n");
+ Die();
+ }
+ recursion_count++;
+ Report("ERROR: %s failed to "
+ "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
+ SanitizerToolName, mmap_type, size, size, mem_type, err);
+#ifndef SANITIZER_GO
+ DumpProcessMap();
+#endif
+ UNREACHABLE("unable to mmap");
+}
+
+bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
+ uptr *read_len, uptr max_len, error_t *errno_p) {
uptr PageSize = GetPageSizeCached();
uptr kMinFileLen = PageSize;
- uptr read_len = 0;
- *buff = 0;
+ *buff = nullptr;
*buff_size = 0;
+ *read_len = 0;
// The files we usually open are not seekable, so try different buffer sizes.
for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
- uptr openrv = OpenFile(file_name, /*write*/ false);
- if (internal_iserror(openrv)) return 0;
- fd_t fd = openrv;
+ fd_t fd = OpenFile(file_name, RdOnly, errno_p);
+ if (fd == kInvalidFd) return false;
UnmapOrDie(*buff, *buff_size);
- *buff = (char*)MmapOrDie(size, __FUNCTION__);
+ *buff = (char*)MmapOrDie(size, __func__);
*buff_size = size;
+ *read_len = 0;
// Read up to one page at a time.
- read_len = 0;
bool reached_eof = false;
- while (read_len + PageSize <= size) {
- uptr just_read = internal_read(fd, *buff + read_len, PageSize);
+ while (*read_len + PageSize <= size) {
+ uptr just_read;
+ if (!ReadFromFile(fd, *buff + *read_len, PageSize, &just_read, errno_p)) {
+ UnmapOrDie(*buff, *buff_size);
+ return false;
+ }
if (just_read == 0) {
reached_eof = true;
break;
}
- read_len += just_read;
+ *read_len += just_read;
}
- internal_close(fd);
+ CloseFile(fd);
if (reached_eof) // We've read the whole file.
break;
}
- return read_len;
+ return true;
}
typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
const char *StripPathPrefix(const char *filepath,
const char *strip_path_prefix) {
- if (filepath == 0) return 0;
- if (strip_path_prefix == 0) return filepath;
- const char *pos = internal_strstr(filepath, strip_path_prefix);
- if (pos == 0) return filepath;
- pos += internal_strlen(strip_path_prefix);
- if (pos[0] == '.' && pos[1] == '/')
- pos += 2;
- return pos;
-}
-
-void PrintSourceLocation(InternalScopedString *buffer, const char *file,
- int line, int column) {
- CHECK(file);
- buffer->append("%s",
- StripPathPrefix(file, common_flags()->strip_path_prefix));
- if (line > 0) {
- buffer->append(":%d", line);
- if (column > 0)
- buffer->append(":%d", column);
- }
+ if (!filepath) return nullptr;
+ if (!strip_path_prefix) return filepath;
+ const char *res = filepath;
+ if (const char *pos = internal_strstr(filepath, strip_path_prefix))
+ res = pos + internal_strlen(strip_path_prefix);
+ if (res[0] == '.' && res[1] == '/')
+ res += 2;
+ return res;
}
-void PrintModuleAndOffset(InternalScopedString *buffer, const char *module,
- uptr offset) {
- buffer->append("(%s+0x%zx)",
- StripPathPrefix(module, common_flags()->strip_path_prefix),
- offset);
+const char *StripModuleName(const char *module) {
+ if (!module)
+ return nullptr;
+ if (SANITIZER_WINDOWS) {
+ // On Windows, both slash and backslash are possible.
+ // Pick the one that goes last.
+ if (const char *bslash_pos = internal_strrchr(module, '\\'))
+ return StripModuleName(bslash_pos + 1);
+ }
+ if (const char *slash_pos = internal_strrchr(module, '/')) {
+ return slash_pos + 1;
+ }
+ return module;
}
void ReportErrorSummary(const char *error_message) {
if (!common_flags()->print_summary)
return;
- InternalScopedBuffer<char> buff(kMaxSummaryLength);
- internal_snprintf(buff.data(), buff.size(),
- "SUMMARY: %s: %s", SanitizerToolName, error_message);
+ InternalScopedString buff(kMaxSummaryLength);
+ buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
__sanitizer_report_error_summary(buff.data());
}
-void ReportErrorSummary(const char *error_type, const char *file,
- int line, const char *function) {
+#ifndef SANITIZER_GO
+void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
if (!common_flags()->print_summary)
return;
- InternalScopedBuffer<char> buff(kMaxSummaryLength);
- internal_snprintf(
- buff.data(), buff.size(), "%s %s:%d %s", error_type,
- file ? StripPathPrefix(file, common_flags()->strip_path_prefix) : "??",
- line, function ? function : "??");
+ InternalScopedString buff(kMaxSummaryLength);
+ buff.append("%s ", error_type);
+ RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
ReportErrorSummary(buff.data());
}
-
-void ReportErrorSummary(const char *error_type, StackTrace *stack) {
- if (!common_flags()->print_summary)
- return;
- AddressInfo ai;
-#if !SANITIZER_GO
- if (stack->size > 0 && Symbolizer::Get()->IsAvailable()) {
- // Currently, we include the first stack frame into the report summary.
- // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
- uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
- Symbolizer::Get()->SymbolizeCode(pc, &ai, 1);
- }
#endif
- ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
-}
-LoadedModule::LoadedModule(const char *module_name, uptr base_address) {
+void LoadedModule::set(const char *module_name, uptr base_address) {
+ clear();
full_name_ = internal_strdup(module_name);
base_address_ = base_address;
- n_ranges_ = 0;
}
-void LoadedModule::addAddressRange(uptr beg, uptr end) {
- CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges);
- ranges_[n_ranges_].beg = beg;
- ranges_[n_ranges_].end = end;
- n_ranges_++;
+void LoadedModule::clear() {
+ InternalFree(full_name_);
+ full_name_ = nullptr;
+ while (!ranges_.empty()) {
+ AddressRange *r = ranges_.front();
+ ranges_.pop_front();
+ InternalFree(r);
+ }
+}
+
+void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
+ void *mem = InternalAlloc(sizeof(AddressRange));
+ AddressRange *r = new(mem) AddressRange(beg, end, executable);
+ ranges_.push_back(r);
}
bool LoadedModule::containsAddress(uptr address) const {
- for (uptr i = 0; i < n_ranges_; i++) {
- if (ranges_[i].beg <= address && address < ranges_[i].end)
+ for (Iterator iter = ranges(); iter.hasNext();) {
+ const AddressRange *r = iter.next();
+ if (r->beg <= address && address < r->end)
return true;
}
return false;
}
-char *StripModuleName(const char *module) {
- if (module == 0)
- return 0;
- const char *short_module_name = internal_strrchr(module, '/');
- if (short_module_name)
- short_module_name += 1;
- else
- short_module_name = module;
- return internal_strdup(short_module_name);
+static atomic_uintptr_t g_total_mmaped;
+
+void IncreaseTotalMmap(uptr size) {
+ if (!common_flags()->mmap_limit_mb) return;
+ uptr total_mmaped =
+ atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
+ // Since for now mmap_limit_mb is not a user-facing flag, just kill
+ // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
+ RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
}
-} // namespace __sanitizer
+void DecreaseTotalMmap(uptr size) {
+ if (!common_flags()->mmap_limit_mb) return;
+ atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
+}
-using namespace __sanitizer; // NOLINT
+bool TemplateMatch(const char *templ, const char *str) {
+ if ((!str) || str[0] == 0)
+ return false;
+ bool start = false;
+ if (templ && templ[0] == '^') {
+ start = true;
+ templ++;
+ }
+ bool asterisk = false;
+ while (templ && templ[0]) {
+ if (templ[0] == '*') {
+ templ++;
+ start = false;
+ asterisk = true;
+ continue;
+ }
+ if (templ[0] == '$')
+ return str[0] == 0 || asterisk;
+ if (str[0] == 0)
+ return false;
+ char *tpos = (char*)internal_strchr(templ, '*');
+ char *tpos1 = (char*)internal_strchr(templ, '$');
+ if ((!tpos) || (tpos1 && tpos1 < tpos))
+ tpos = tpos1;
+ if (tpos)
+ tpos[0] = 0;
+ const char *str0 = str;
+ const char *spos = internal_strstr(str, templ);
+ str = spos + internal_strlen(templ);
+ templ = tpos;
+ if (tpos)
+ tpos[0] = tpos == tpos1 ? '$' : '*';
+ if (!spos)
+ return false;
+ if (start && spos != str0)
+ return false;
+ start = false;
+ asterisk = false;
+ }
+ return true;
+}
-extern "C" {
-void __sanitizer_set_report_path(const char *path) {
+static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
+
+char *FindPathToBinary(const char *name) {
+ const char *path = GetEnv("PATH");
if (!path)
- return;
- uptr len = internal_strlen(path);
- if (len > sizeof(report_path_prefix) - 100) {
- Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
- path[0], path[1], path[2], path[3],
- path[4], path[5], path[6], path[7]);
- Die();
+ return nullptr;
+ uptr name_len = internal_strlen(name);
+ InternalScopedBuffer<char> buffer(kMaxPathLength);
+ const char *beg = path;
+ while (true) {
+ const char *end = internal_strchrnul(beg, kPathSeparator);
+ uptr prefix_len = end - beg;
+ if (prefix_len + name_len + 2 <= kMaxPathLength) {
+ internal_memcpy(buffer.data(), beg, prefix_len);
+ buffer[prefix_len] = '/';
+ internal_memcpy(&buffer[prefix_len + 1], name, name_len);
+ buffer[prefix_len + 1 + name_len] = '\0';
+ if (FileExists(buffer.data()))
+ return internal_strdup(buffer.data());
+ }
+ if (*end == '\0') break;
+ beg = end + 1;
}
- if (report_fd != kStdoutFd &&
- report_fd != kStderrFd &&
- report_fd != kInvalidFd)
- internal_close(report_fd);
- report_fd = kInvalidFd;
- log_to_file = false;
- if (internal_strcmp(path, "stdout") == 0) {
- report_fd = kStdoutFd;
- } else if (internal_strcmp(path, "stderr") == 0) {
- report_fd = kStderrFd;
- } else {
- internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix));
- report_path_prefix[len] = '\0';
- log_to_file = true;
+ return nullptr;
+}
+
+static char binary_name_cache_str[kMaxPathLength];
+static char process_name_cache_str[kMaxPathLength];
+
+const char *GetProcessName() {
+ return process_name_cache_str;
+}
+
+static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
+ ReadLongProcessName(buf, buf_len);
+ char *s = const_cast<char *>(StripModuleName(buf));
+ uptr len = internal_strlen(s);
+ if (s != buf) {
+ internal_memmove(buf, s, len);
+ buf[len] = '\0';
}
+ return len;
+}
+
+void UpdateProcessName() {
+ ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
}
-void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) {
- (void)reserved;
- PrepareForSandboxing();
+// Call once to make sure that binary_name_cache_str is initialized
+void CacheBinaryName() {
+ if (binary_name_cache_str[0] != '\0')
+ return;
+ ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
+ ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
+}
+
+uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
+ CacheBinaryName();
+ uptr name_len = internal_strlen(binary_name_cache_str);
+ name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
+ if (buf_len == 0)
+ return 0;
+ internal_memcpy(buf, binary_name_cache_str, name_len);
+ buf[name_len] = '\0';
+ return name_len;
+}
+
+} // namespace __sanitizer
+
+using namespace __sanitizer; // NOLINT
+
+extern "C" {
+void __sanitizer_set_report_path(const char *path) {
+ report_file.SetReportPath(path);
}
void __sanitizer_report_error_summary(const char *error_summary) {
Printf("%s\n", error_summary);
}
-} // extern "C"
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_set_death_callback(void (*callback)(void)) {
+ SetUserDieCallback(callback);
+}
+} // extern "C"
//
//===----------------------------------------------------------------------===//
//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
+// This file is shared between run-time libraries of sanitizers.
+//
// It declares common functions and classes that are used in both runtimes.
// Implementation of some functions are provided in sanitizer_common, while
// others must be defined by run-time library itself.
#ifndef SANITIZER_COMMON_H
#define SANITIZER_COMMON_H
+#include "sanitizer_flags.h"
+#include "sanitizer_interface_internal.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_libc.h"
+#include "sanitizer_list.h"
#include "sanitizer_mutex.h"
+#ifdef _MSC_VER
+extern "C" void _ReadWriteBarrier();
+#pragma intrinsic(_ReadWriteBarrier)
+#endif
+
namespace __sanitizer {
struct StackTrace;
+struct AddressInfo;
// Constants.
const uptr kWordSize = SANITIZER_WORDSIZE / 8;
const uptr kWordSizeInBits = 8 * kWordSize;
#if defined(__powerpc__) || defined(__powerpc64__)
-const uptr kCacheLineSize = 128;
+ const uptr kCacheLineSize = 128;
#else
-const uptr kCacheLineSize = 64;
+ const uptr kCacheLineSize = 64;
#endif
-const uptr kMaxPathLength = 512;
+const uptr kMaxPathLength = 4096;
+
+// 16K loaded modules should be enough for everyone.
+static const uptr kMaxNumberOfModules = 1 << 14;
+
+const uptr kMaxThreadStackSize = 1 << 30; // 1Gb
+
+// Denotes fake PC values that come from JIT/JAVA/etc.
+// For such PC values __tsan_symbolize_external() will be called.
+const u64 kExternalPCBit = 1ULL << 60;
extern const char *SanitizerToolName; // Can be changed by the tool.
+extern atomic_uint32_t current_verbosity;
+INLINE void SetVerbosity(int verbosity) {
+ atomic_store(¤t_verbosity, verbosity, memory_order_relaxed);
+}
+INLINE int Verbosity() {
+ return atomic_load(¤t_verbosity, memory_order_relaxed);
+}
+
uptr GetPageSize();
uptr GetPageSizeCached();
uptr GetMmapGranularity();
// Memory management
void *MmapOrDie(uptr size, const char *mem_type);
void UnmapOrDie(void *addr, uptr size);
-void *MmapFixedNoReserve(uptr fixed_addr, uptr size);
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size,
+ const char *name = nullptr);
+void *MmapNoReserveOrDie(uptr size, const char *mem_type);
void *MmapFixedOrDie(uptr fixed_addr, uptr size);
-void *Mprotect(uptr fixed_addr, uptr size);
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr);
// Map aligned chunk of address space; size and alignment are powers of two.
void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type);
+// Disallow access to a memory range. Use MmapNoAccess to allocate an
+// unaccessible memory.
+bool MprotectNoAccess(uptr addr, uptr size);
+
// Used to check if we can map shadow memory to a fixed location.
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end);
void FlushUnneededShadowMemory(uptr addr, uptr size);
+void IncreaseTotalMmap(uptr size);
+void DecreaseTotalMmap(uptr size);
+uptr GetRSS();
+void NoHugePagesInRegion(uptr addr, uptr length);
+void DontDumpShadowMemory(uptr addr, uptr length);
+// Check if the built VMA size matches the runtime one.
+void CheckVMASize();
// InternalScopedBuffer can be used instead of large stack arrays to
// keep frame size low.
// IO
void RawWrite(const char *buffer);
-bool PrintsToTty();
-// Caching version of PrintsToTty(). Not thread-safe.
-bool PrintsToTtyCached();
+bool ColorizeReports();
void Printf(const char *format, ...);
void Report(const char *format, ...);
void SetPrintfAndReportCallback(void (*callback)(const char *));
+#define VReport(level, ...) \
+ do { \
+ if ((uptr)Verbosity() >= (level)) Report(__VA_ARGS__); \
+ } while (0)
+#define VPrintf(level, ...) \
+ do { \
+ if ((uptr)Verbosity() >= (level)) Printf(__VA_ARGS__); \
+ } while (0)
// Can be used to prevent mixing error reports from different sanitizers.
extern StaticSpinMutex CommonSanitizerReportMutex;
-void MaybeOpenReportFile();
-extern fd_t report_fd;
-extern bool log_to_file;
-extern char report_path_prefix[4096];
-extern uptr report_fd_pid;
+
+struct ReportFile {
+ void Write(const char *buffer, uptr length);
+ bool SupportsColors();
+ void SetReportPath(const char *path);
+
+ // Don't use fields directly. They are only declared public to allow
+ // aggregate initialization.
+
+ // Protects fields below.
+ StaticSpinMutex *mu;
+ // Opened file descriptor. Defaults to stderr. It may be equal to
+ // kInvalidFd, in which case new file will be opened when necessary.
+ fd_t fd;
+ // Path prefix of report file, set via __sanitizer_set_report_path.
+ char path_prefix[kMaxPathLength];
+ // Full path to report, obtained as <path_prefix>.PID
+ char full_path[kMaxPathLength];
+ // PID of the process that opened fd. If a fork() occurs,
+ // the PID of child will be different from fd_pid.
+ uptr fd_pid;
+
+ private:
+ void ReopenIfNecessary();
+};
+extern ReportFile report_file;
+
extern uptr stoptheworld_tracer_pid;
extern uptr stoptheworld_tracer_ppid;
-uptr OpenFile(const char *filename, bool write);
+enum FileAccessMode {
+ RdOnly,
+ WrOnly,
+ RdWr
+};
+
+// Returns kInvalidFd on error.
+fd_t OpenFile(const char *filename, FileAccessMode mode,
+ error_t *errno_p = nullptr);
+void CloseFile(fd_t);
+
+// Return true on success, false on error.
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size,
+ uptr *bytes_read = nullptr, error_t *error_p = nullptr);
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size,
+ uptr *bytes_written = nullptr, error_t *error_p = nullptr);
+
+bool RenameFile(const char *oldpath, const char *newpath,
+ error_t *error_p = nullptr);
+
+// Scoped file handle closer.
+struct FileCloser {
+ explicit FileCloser(fd_t fd) : fd(fd) {}
+ ~FileCloser() { CloseFile(fd); }
+ fd_t fd;
+};
+
+bool SupportsColoredOutput(fd_t fd);
+
// Opens the file 'file_name" and reads up to 'max_len' bytes.
// The resulting buffer is mmaped and stored in '*buff'.
-// The size of the mmaped region is stored in '*buff_size',
-// Returns the number of read bytes or 0 if file can not be opened.
-uptr ReadFileToBuffer(const char *file_name, char **buff,
- uptr *buff_size, uptr max_len);
+// The size of the mmaped region is stored in '*buff_size'.
+// The total number of read bytes is stored in '*read_len'.
+// Returns true if file was successfully opened and read.
+bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
+ uptr *read_len, uptr max_len = 1 << 26,
+ error_t *errno_p = nullptr);
// Maps given file to virtual memory, and returns pointer to it
-// (or NULL if the mapping failes). Stores the size of mmaped region
+// (or NULL if mapping fails). Stores the size of mmaped region
// in '*buff_size'.
void *MapFileToMemory(const char *file_name, uptr *buff_size);
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset);
+
+bool IsAccessibleMemoryRange(uptr beg, uptr size);
// Error report formatting.
const char *StripPathPrefix(const char *filepath,
const char *strip_file_prefix);
-void PrintSourceLocation(InternalScopedString *buffer, const char *file,
- int line, int column);
-void PrintModuleAndOffset(InternalScopedString *buffer,
- const char *module, uptr offset);
+// Strip the directories from the module name.
+const char *StripModuleName(const char *module);
// OS
-void DisableCoreDumper();
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len);
+uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len);
+uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len);
+const char *GetProcessName();
+void UpdateProcessName();
+void CacheBinaryName();
+void DisableCoreDumperIfNecessary();
void DumpProcessMap();
bool FileExists(const char *filename);
const char *GetEnv(const char *name);
bool SetEnv(const char *name, const char *value);
const char *GetPwd();
char *FindPathToBinary(const char *name);
+bool IsPathSeparator(const char c);
+bool IsAbsolutePath(const char *path);
+
u32 GetUid();
void ReExec();
bool StackSizeIsUnlimited();
void SetStackSizeLimitInBytes(uptr limit);
-void PrepareForSandboxing();
+bool AddressSpaceIsUnlimited();
+void SetAddressSpaceUnlimited();
+void AdjustStackSize(void *attr);
+void PrepareForSandboxing(__sanitizer_sandbox_arguments *args);
+void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args);
+void SetSandboxingCallback(void (*f)());
+
+void CoverageUpdateMapping();
+void CovBeforeFork();
+void CovAfterFork(int child_pid);
+
+void InitializeCoverage(bool enabled, const char *coverage_dir);
+void ReInitializeCoverage(bool enabled, const char *coverage_dir);
void InitTlsSize();
uptr GetTlsSize();
u64 NanoTime();
int Atexit(void (*function)(void));
void SortArray(uptr *array, uptr size);
-// Strip the directories from the module name, return a new string allocated
-// with internal_strdup.
-char *StripModuleName(const char *module);
+bool TemplateMatch(const char *templ, const char *str);
// Exit
void NORETURN Abort();
void NORETURN Die();
void NORETURN
CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2);
+void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
+ const char *mmap_type, error_t err);
// Set the name of the current thread to 'name', return true on succees.
// The name may be truncated to a system-dependent limit.
// Specific tools may override behavior of "Die" and "CheckFailed" functions
// to do tool-specific job.
typedef void (*DieCallbackType)(void);
-void SetDieCallback(DieCallbackType);
-DieCallbackType GetDieCallback();
+
+// It's possible to add several callbacks that would be run when "Die" is
+// called. The callbacks will be run in the opposite order. The tools are
+// strongly recommended to setup all callbacks during initialization, when there
+// is only a single thread.
+bool AddDieCallback(DieCallbackType callback);
+bool RemoveDieCallback(DieCallbackType callback);
+
+void SetUserDieCallback(DieCallbackType callback);
+
typedef void (*CheckFailedCallbackType)(const char *, int, const char *,
u64, u64);
void SetCheckFailedCallback(CheckFailedCallbackType callback);
+// Callback will be called if soft_rss_limit_mb is given and the limit is
+// exceeded (exceeded==true) or if rss went down below the limit
+// (exceeded==false).
+// The callback should be registered once at the tool init time.
+void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded));
+
+// Functions related to signal handling.
+typedef void (*SignalHandlerType)(int, void *, void *);
+bool IsDeadlySignal(int signum);
+void InstallDeadlySignalHandlers(SignalHandlerType handler);
+// Alternative signal stack (POSIX-only).
+void SetAlternateSignalStack();
+void UnsetAlternateSignalStack();
+
// We don't want a summary too long.
const int kMaxSummaryLength = 1024;
// Construct a one-line string:
// and pass it to __sanitizer_report_error_summary.
void ReportErrorSummary(const char *error_message);
// Same as above, but construct error_message as:
-// error_type: file:line function
-void ReportErrorSummary(const char *error_type, const char *file,
- int line, const char *function);
+// error_type file:line[:column][ function]
+void ReportErrorSummary(const char *error_type, const AddressInfo &info);
+// Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
void ReportErrorSummary(const char *error_type, StackTrace *trace);
// Math
CHECK_NE(x, 0U);
unsigned long up; // NOLINT
#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
+# ifdef _WIN64
+ up = SANITIZER_WORDSIZE - 1 - __builtin_clzll(x);
+# else
up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x);
+# endif
#elif defined(_WIN64)
_BitScanReverse64(&up, x);
#else
return up;
}
+INLINE uptr LeastSignificantSetBitIndex(uptr x) {
+ CHECK_NE(x, 0U);
+ unsigned long up; // NOLINT
+#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
+# ifdef _WIN64
+ up = __builtin_ctzll(x);
+# else
+ up = __builtin_ctzl(x);
+# endif
+#elif defined(_WIN64)
+ _BitScanForward64(&up, x);
+#else
+ _BitScanForward(&up, x);
+#endif
+ return up;
+}
+
INLINE bool IsPowerOfTwo(uptr x) {
return (x & (x - 1)) == 0;
}
uptr up = MostSignificantSetBitIndex(size);
CHECK(size < (1ULL << (up + 1)));
CHECK(size > (1ULL << up));
- return 1UL << (up + 1);
+ return 1ULL << (up + 1);
}
INLINE uptr RoundUpTo(uptr size, uptr boundary) {
INLINE uptr Log2(uptr x) {
CHECK(IsPowerOfTwo(x));
-#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
- return __builtin_ctzl(x);
-#elif defined(_WIN64)
- unsigned long ret; // NOLINT
- _BitScanForward64(&ret, x);
- return ret;
-#else
- unsigned long ret; // NOLINT
- _BitScanForward(&ret, x);
- return ret;
-#endif
+ return LeastSignificantSetBitIndex(x);
}
// Don't use std::min, std::max or std::swap, to minimize dependency
return (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c;
}
-#if SANITIZER_WORDSIZE == 64
-# define FIRST_32_SECOND_64(a, b) (b)
-#else
-# define FIRST_32_SECOND_64(a, b) (a)
-#endif
-
// A low-level vector based on mmap. May incur a significant memory overhead for
// small vectors.
// WARNING: The current implementation supports only POD types.
template<typename T>
-class InternalMmapVector {
+class InternalMmapVectorNoCtor {
public:
- explicit InternalMmapVector(uptr initial_capacity) {
+ void Initialize(uptr initial_capacity) {
capacity_ = Max(initial_capacity, (uptr)1);
size_ = 0;
- data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVector");
+ data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVectorNoCtor");
}
- ~InternalMmapVector() {
+ void Destroy() {
UnmapOrDie(data_, capacity_ * sizeof(T));
}
T &operator[](uptr i) {
const T *data() const {
return data_;
}
+ T *data() {
+ return data_;
+ }
uptr capacity() const {
return capacity_;
}
void clear() { size_ = 0; }
+ bool empty() const { return size() == 0; }
private:
void Resize(uptr new_capacity) {
UnmapOrDie(old_data, capacity_ * sizeof(T));
capacity_ = new_capacity;
}
- // Disallow evil constructors.
- InternalMmapVector(const InternalMmapVector&);
- void operator=(const InternalMmapVector&);
T *data_;
uptr capacity_;
uptr size_;
};
+template<typename T>
+class InternalMmapVector : public InternalMmapVectorNoCtor<T> {
+ public:
+ explicit InternalMmapVector(uptr initial_capacity) {
+ InternalMmapVectorNoCtor<T>::Initialize(initial_capacity);
+ }
+ ~InternalMmapVector() { InternalMmapVectorNoCtor<T>::Destroy(); }
+ // Disallow evil constructors.
+ InternalMmapVector(const InternalMmapVector&);
+ void operator=(const InternalMmapVector&);
+};
+
// HeapSort for arrays and InternalMmapVector.
template<class Container, class Compare>
void InternalSort(Container *v, uptr size, Compare comp) {
// executable or a shared object).
class LoadedModule {
public:
- LoadedModule(const char *module_name, uptr base_address);
- void addAddressRange(uptr beg, uptr end);
+ LoadedModule() : full_name_(nullptr), base_address_(0) { ranges_.clear(); }
+ void set(const char *module_name, uptr base_address);
+ void clear();
+ void addAddressRange(uptr beg, uptr end, bool executable);
bool containsAddress(uptr address) const;
const char *full_name() const { return full_name_; }
uptr base_address() const { return base_address_; }
- private:
struct AddressRange {
+ AddressRange *next;
uptr beg;
uptr end;
+ bool executable;
+
+ AddressRange(uptr beg, uptr end, bool executable)
+ : next(nullptr), beg(beg), end(end), executable(executable) {}
};
- char *full_name_;
+
+ typedef IntrusiveList<AddressRange>::ConstIterator Iterator;
+ Iterator ranges() const { return Iterator(&ranges_); }
+
+ private:
+ char *full_name_; // Owned.
uptr base_address_;
- static const uptr kMaxNumberOfAddressRanges = 6;
- AddressRange ranges_[kMaxNumberOfAddressRanges];
- uptr n_ranges_;
+ IntrusiveList<AddressRange> ranges_;
};
// OS-dependent function that fills array with descriptions of at most
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter);
-#if SANITIZER_POSIX
-const uptr kPthreadDestructorIterations = 4;
+// Callback type for iterating over a set of memory ranges.
+typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg);
+
+enum AndroidApiLevel {
+ ANDROID_NOT_ANDROID = 0,
+ ANDROID_KITKAT = 19,
+ ANDROID_LOLLIPOP_MR1 = 22,
+ ANDROID_POST_LOLLIPOP = 23
+};
+
+#if SANITIZER_LINUX
+// Initialize Android logging. Any writes before this are silently lost.
+void AndroidLogInit();
+void WriteToSyslog(const char *buffer);
+#else
+INLINE void AndroidLogInit() {}
+INLINE void WriteToSyslog(const char *buffer) {}
+#endif
+
+#if SANITIZER_ANDROID
+void GetExtraActivationFlags(char *buf, uptr size);
+void SanitizerInitializeUnwinder();
+AndroidApiLevel AndroidGetApiLevel();
+#else
+INLINE void AndroidLogWrite(const char *buffer_unused) {}
+INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
+INLINE void SanitizerInitializeUnwinder() {}
+INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; }
+#endif
+
+INLINE uptr GetPthreadDestructorIterations() {
+#if SANITIZER_ANDROID
+ return (AndroidGetApiLevel() == ANDROID_LOLLIPOP_MR1) ? 8 : 4;
+#elif SANITIZER_POSIX
+ return 4;
#else
// Unused on Windows.
-const uptr kPthreadDestructorIterations = 0;
+ return 0;
#endif
+}
+
+void *internal_start_thread(void(*func)(void*), void *arg);
+void internal_join_thread(void *th);
+void MaybeStartBackgroudThread();
+
+// Make the compiler think that something is going on there.
+// Use this inside a loop that looks like memset/memcpy/etc to prevent the
+// compiler from recognising it and turning it into an actual call to
+// memset/memcpy/etc.
+static inline void SanitizerBreakOptimization(void *arg) {
+#if _MSC_VER && !defined(__clang__)
+ _ReadWriteBarrier();
+#else
+ __asm__ __volatile__("" : : "r" (arg) : "memory");
+#endif
+}
+
+struct SignalContext {
+ void *context;
+ uptr addr;
+ uptr pc;
+ uptr sp;
+ uptr bp;
+
+ SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) :
+ context(context), addr(addr), pc(pc), sp(sp), bp(bp) {
+ }
+
+ // Creates signal context in a platform-specific manner.
+ static SignalContext Create(void *siginfo, void *context);
+};
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
-// Callback type for iterating over a set of memory ranges.
-typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg);
} // namespace __sanitizer
inline void *operator new(__sanitizer::operator_new_size_type size,
return alloc.Allocate(size);
}
+struct StackDepotStats {
+ uptr n_uniq_ids;
+ uptr allocated;
+};
+
#endif // SANITIZER_COMMON_H
// This file should be included into the tool's interceptor file,
// which has to define it's own macros:
// COMMON_INTERCEPTOR_ENTER
+// COMMON_INTERCEPTOR_ENTER_NOIGNORE
// COMMON_INTERCEPTOR_READ_RANGE
// COMMON_INTERCEPTOR_WRITE_RANGE
// COMMON_INTERCEPTOR_INITIALIZE_RANGE
+// COMMON_INTERCEPTOR_DIR_ACQUIRE
// COMMON_INTERCEPTOR_FD_ACQUIRE
// COMMON_INTERCEPTOR_FD_RELEASE
// COMMON_INTERCEPTOR_FD_ACCESS
// COMMON_INTERCEPTOR_SET_THREAD_NAME
+// COMMON_INTERCEPTOR_ON_DLOPEN
// COMMON_INTERCEPTOR_ON_EXIT
// COMMON_INTERCEPTOR_MUTEX_LOCK
// COMMON_INTERCEPTOR_MUTEX_UNLOCK
// COMMON_INTERCEPTOR_MUTEX_REPAIR
// COMMON_INTERCEPTOR_SET_PTHREAD_NAME
// COMMON_INTERCEPTOR_HANDLE_RECVMSG
+// COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED
//===----------------------------------------------------------------------===//
+
#include "interception/interception.h"
+#include "sanitizer_addrhashmap.h"
+#include "sanitizer_placement_new.h"
#include "sanitizer_platform_interceptors.h"
+#include "sanitizer_tls_get_addr.h"
#include <stdarg.h>
+#if SANITIZER_INTERCEPTOR_HOOKS
+#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) \
+ do { \
+ if (f) \
+ f(__VA_ARGS__); \
+ } while (false);
+#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) \
+ extern "C" { \
+ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void f(__VA_ARGS__); \
+ } // extern "C"
+#else
+#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...)
+#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...)
+
+#endif // SANITIZER_INTERCEPTOR_HOOKS
+
#if SANITIZER_WINDOWS && !defined(va_copy)
#define va_copy(dst, src) ((dst) = (src))
#endif // _WIN32
+#if SANITIZER_FREEBSD
+#define pthread_setname_np pthread_set_name_np
+#define inet_aton __inet_aton
+#define inet_pton __inet_pton
+#define iconv __bsd_iconv
+#endif
+
#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE
-#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, p, size) {}
+#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(p, size) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_UNPOISON_PARAM
+#define COMMON_INTERCEPTOR_UNPOISON_PARAM(count) {}
#endif
#ifndef COMMON_INTERCEPTOR_FD_ACCESS
#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) ((void)(msg))
#endif
+#ifndef COMMON_INTERCEPTOR_FILE_OPEN
+#define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_FILE_CLOSE
+#define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_LIBRARY_LOADED
+#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_LIBRARY_UNLOADED
+#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_ENTER_NOIGNORE
+#define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, ...) \
+ COMMON_INTERCEPTOR_ENTER(ctx, __VA_ARGS__)
+#endif
+
+#ifndef COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED
+#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0)
+#endif
+
+#define COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n) \
+ COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \
+ common_flags()->strict_string_checks ? (len) + 1 : (n) )
+
+#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \
+ COMMON_INTERCEPTOR_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
+
+#ifndef COMMON_INTERCEPTOR_ON_DLOPEN
+#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_GET_TLS_RANGE
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) *begin = *end = 0;
+#endif
+
+#ifndef COMMON_INTERCEPTOR_ACQUIRE
+#define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_RELEASE
+#define COMMON_INTERCEPTOR_RELEASE(ctx, u) {}
+#endif
+
+struct FileMetadata {
+ // For open_memstream().
+ char **addr;
+ SIZE_T *size;
+};
+
+struct CommonInterceptorMetadata {
+ enum {
+ CIMT_INVALID = 0,
+ CIMT_FILE
+ } type;
+ union {
+ FileMetadata file;
+ };
+};
+
+typedef AddrHashMap<CommonInterceptorMetadata, 31051> MetadataHashMap;
+
+static MetadataHashMap *interceptor_metadata_map;
+
+#if SI_NOT_WINDOWS
+UNUSED static void SetInterceptorMetadata(__sanitizer_FILE *addr,
+ const FileMetadata &file) {
+ MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr);
+ CHECK(h.created());
+ h->type = CommonInterceptorMetadata::CIMT_FILE;
+ h->file = file;
+}
+
+UNUSED static const FileMetadata *GetInterceptorMetadata(
+ __sanitizer_FILE *addr) {
+ MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr,
+ /* remove */ false,
+ /* create */ false);
+ if (h.exists()) {
+ CHECK(!h.created());
+ CHECK(h->type == CommonInterceptorMetadata::CIMT_FILE);
+ return &h->file;
+ } else {
+ return 0;
+ }
+}
+
+UNUSED static void DeleteInterceptorMetadata(void *addr) {
+ MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr, true);
+ CHECK(h.exists());
+}
+#endif // SI_NOT_WINDOWS
+
#if SANITIZER_INTERCEPT_TEXTDOMAIN
INTERCEPTOR(char*, textdomain, const char *domainname) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname);
- char* domain = REAL(textdomain)(domainname);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, domainname, 0);
+ char *domain = REAL(textdomain)(domainname);
if (domain) {
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, domain,
- REAL(strlen)(domain) + 1);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1);
}
return domain;
}
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
}
+DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, uptr called_pc,
+ const char *s1, const char *s2)
+
INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2);
+ CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, GET_CALLER_PC(), s1,
+ s2);
unsigned char c1, c2;
uptr i;
for (i = 0;; i++) {
c2 = (unsigned char)s2[i];
if (c1 != c2 || c1 == '\0') break;
}
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
return CharCmpX(c1, c2);
}
+DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, uptr called_pc,
+ const char *s1, const char *s2, uptr n)
+
INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_strncmp(s1, s2, size);
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size);
+ CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, GET_CALLER_PC(), s1,
+ s2, size);
unsigned char c1 = 0, c2 = 0;
uptr i;
for (i = 0; i < size; i++) {
c2 = (unsigned char)s2[i];
if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
}
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
return CharCaseCmp(c1, c2);
}
#define INIT_STRNCASECMP
#endif
+#if SANITIZER_INTERCEPT_STRSTR || SANITIZER_INTERCEPT_STRCASESTR
+static inline void StrstrCheck(void *ctx, char *r, const char *s1,
+ const char *s2) {
+ uptr len1 = REAL(strlen)(s1);
+ uptr len2 = REAL(strlen)(s2);
+ COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s1, len1,
+ r ? r - s1 + len2 : len1 + 1);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2 + 1);
+}
+#endif
+
+#if SANITIZER_INTERCEPT_STRSTR
+INTERCEPTOR(char*, strstr, const char *s1, const char *s2) {
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_strstr(s1, s2);
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strstr, s1, s2);
+ char *r = REAL(strstr)(s1, s2);
+ if (common_flags()->intercept_strstr)
+ StrstrCheck(ctx, r, s1, s2);
+ return r;
+}
+
+#define INIT_STRSTR COMMON_INTERCEPT_FUNCTION(strstr);
+#else
+#define INIT_STRSTR
+#endif
+
+#if SANITIZER_INTERCEPT_STRCASESTR
+INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strcasestr, s1, s2);
+ char *r = REAL(strcasestr)(s1, s2);
+ if (common_flags()->intercept_strstr)
+ StrstrCheck(ctx, r, s1, s2);
+ return r;
+}
+
+#define INIT_STRCASESTR COMMON_INTERCEPT_FUNCTION(strcasestr);
+#else
+#define INIT_STRCASESTR
+#endif
+
+#if SANITIZER_INTERCEPT_STRSPN
+INTERCEPTOR(SIZE_T, strspn, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strspn, s1, s2);
+ SIZE_T r = REAL(strspn)(s1, s2);
+ if (common_flags()->intercept_strspn) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1);
+ }
+ return r;
+}
+
+INTERCEPTOR(SIZE_T, strcspn, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strcspn, s1, s2);
+ SIZE_T r = REAL(strcspn)(s1, s2);
+ if (common_flags()->intercept_strspn) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1);
+ }
+ return r;
+}
+
+#define INIT_STRSPN \
+ COMMON_INTERCEPT_FUNCTION(strspn); \
+ COMMON_INTERCEPT_FUNCTION(strcspn);
+#else
+#define INIT_STRSPN
+#endif
+
+#if SANITIZER_INTERCEPT_STRPBRK
+INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strpbrk, s1, s2);
+ char *r = REAL(strpbrk)(s1, s2);
+ if (common_flags()->intercept_strpbrk) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1,
+ r ? r - s1 + 1 : REAL(strlen)(s1) + 1);
+ }
+ return r;
+}
+
+#define INIT_STRPBRK COMMON_INTERCEPT_FUNCTION(strpbrk);
+#else
+#define INIT_STRPBRK
+#endif
+
+#if SANITIZER_INTERCEPT_MEMCMP
+
+DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, uptr called_pc,
+ const void *s1, const void *s2, uptr n)
+
+INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, memcmp, a1, a2, size);
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_memcmp(a1, a2, size);
+ CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1,
+ a2, size);
+ if (common_flags()->intercept_memcmp) {
+ if (common_flags()->strict_memcmp) {
+ // Check the entire regions even if the first bytes of the buffers are
+ // different.
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, a1, size);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, a2, size);
+ // Fallthrough to REAL(memcmp) below.
+ } else {
+ unsigned char c1 = 0, c2 = 0;
+ const unsigned char *s1 = (const unsigned char*)a1;
+ const unsigned char *s2 = (const unsigned char*)a2;
+ uptr i;
+ for (i = 0; i < size; i++) {
+ c1 = s1[i];
+ c2 = s2[i];
+ if (c1 != c2) break;
+ }
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size));
+ return CharCmpX(c1, c2);
+ }
+ }
+ return REAL(memcmp(a1, a2, size));
+}
+
+#define INIT_MEMCMP COMMON_INTERCEPT_FUNCTION(memcmp)
+#else
+#define INIT_MEMCMP
+#endif
+
+#if SANITIZER_INTERCEPT_MEMCHR
+INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) {
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_memchr(s, c, n);
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, memchr, s, c, n);
+ void *res = REAL(memchr)(s, c, n);
+ uptr len = res ? (char *)res - (const char *)s + 1 : n;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s, len);
+ return res;
+}
+
+#define INIT_MEMCHR COMMON_INTERCEPT_FUNCTION(memchr)
+#else
+#define INIT_MEMCHR
+#endif
+
+#if SANITIZER_INTERCEPT_MEMRCHR
+INTERCEPTOR(void*, memrchr, const void *s, int c, SIZE_T n) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, memrchr, s, c, n);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s, n);
+ return REAL(memrchr)(s, c, n);
+}
+
+#define INIT_MEMRCHR COMMON_INTERCEPT_FUNCTION(memrchr)
+#else
+#define INIT_MEMRCHR
+#endif
+
#if SANITIZER_INTERCEPT_FREXP
INTERCEPTOR(double, frexp, double x, int *exp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, frexp, x, exp);
- double res = REAL(frexp)(x, exp);
+ // Assuming frexp() always writes to |exp|.
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
+ double res = REAL(frexp)(x, exp);
return res;
}
INTERCEPTOR(float, frexpf, float x, int *exp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, frexpf, x, exp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
float res = REAL(frexpf)(x, exp);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
return res;
INTERCEPTOR(long double, frexpl, long double x, int *exp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, frexpl, x, exp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
long double res = REAL(frexpl)(x, exp);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
return res;
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SSIZE_T res = REAL(read)(fd, ptr, count);
if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SSIZE_T res = REAL(pread)(fd, ptr, count, offset);
if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SSIZE_T res = REAL(pread64)(fd, ptr, count, offset);
if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
INTERCEPTOR(unsigned long, time, unsigned long *t) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, time, t);
- unsigned long res = REAL(time)(t);
+ unsigned long local_t;
+ unsigned long res = REAL(time)(&local_t);
if (t && res != (unsigned long)-1) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, t, sizeof(*t));
+ *t = local_t;
}
return res;
}
if (tm->tm_zone) {
// Can not use COMMON_INTERCEPTOR_WRITE_RANGE here, because tm->tm_zone
// can point to shared memory and tsan would report a data race.
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, tm->tm_zone,
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(tm->tm_zone,
REAL(strlen(tm->tm_zone)) + 1);
}
}
INTERCEPTOR(char *, ctime, unsigned long *timep) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, ctime, timep);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(ctime)(timep);
if (res) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, ctime_r, timep, result);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(ctime_r)(timep, result);
if (res) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(asctime)(tm);
if (res) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm));
INTERCEPTOR(char *, asctime_r, __sanitizer_tm *tm, char *result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, asctime_r, tm, result);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(asctime_r)(tm, result);
if (res) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm));
}
return res;
}
+INTERCEPTOR(long, mktime, __sanitizer_tm *tm) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, mktime, tm);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_sec, sizeof(tm->tm_sec));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_min, sizeof(tm->tm_min));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_hour, sizeof(tm->tm_hour));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_mday, sizeof(tm->tm_mday));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_mon, sizeof(tm->tm_mon));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_year, sizeof(tm->tm_year));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_isdst, sizeof(tm->tm_isdst));
+ long res = REAL(mktime)(tm);
+ if (res != -1) unpoison_tm(ctx, tm);
+ return res;
+}
#define INIT_LOCALTIME_AND_FRIENDS \
COMMON_INTERCEPT_FUNCTION(localtime); \
COMMON_INTERCEPT_FUNCTION(localtime_r); \
COMMON_INTERCEPT_FUNCTION(ctime); \
COMMON_INTERCEPT_FUNCTION(ctime_r); \
COMMON_INTERCEPT_FUNCTION(asctime); \
- COMMON_INTERCEPT_FUNCTION(asctime_r);
+ COMMON_INTERCEPT_FUNCTION(asctime_r); \
+ COMMON_INTERCEPT_FUNCTION(mktime);
#else
#define INIT_LOCALTIME_AND_FRIENDS
#endif // SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS
COMMON_INTERCEPTOR_ENTER(ctx, strptime, s, format, tm);
if (format)
COMMON_INTERCEPTOR_READ_RANGE(ctx, format, REAL(strlen)(format) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(strptime)(s, format, tm);
- if (res) {
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0);
+ if (res && tm) {
// Do not call unpoison_tm here, because strptime does not, in fact,
// initialize the entire struct tm. For example, tm_zone pointer is left
// uninitialized.
- if (tm) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
}
return res;
}
#define INIT_STRPTIME
#endif
-#if SANITIZER_INTERCEPT_SCANF
+#if SANITIZER_INTERCEPT_SCANF || SANITIZER_INTERCEPT_PRINTF
+#include "sanitizer_common_interceptors_format.inc"
+
+#define FORMAT_INTERCEPTOR_IMPL(name, vname, ...) \
+ { \
+ void *ctx; \
+ va_list ap; \
+ va_start(ap, format); \
+ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap); \
+ int res = WRAP(vname)(__VA_ARGS__, ap); \
+ va_end(ap); \
+ return res; \
+ }
-#include "sanitizer_common_interceptors_scanf.inc"
+#endif
+
+#if SANITIZER_INTERCEPT_SCANF
#define VSCANF_INTERCEPTOR_IMPL(vname, allowGnuMalloc, ...) \
{ \
VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap)
#endif // SANITIZER_INTERCEPT_ISOC99_SCANF
-#define SCANF_INTERCEPTOR_IMPL(name, vname, ...) \
- { \
- void *ctx; \
- va_list ap; \
- va_start(ap, format); \
- COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap); \
- int res = vname(__VA_ARGS__, ap); \
- va_end(ap); \
- return res; \
- }
-
INTERCEPTOR(int, scanf, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(scanf, vscanf, format)
+FORMAT_INTERCEPTOR_IMPL(scanf, vscanf, format)
INTERCEPTOR(int, fscanf, void *stream, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format)
+FORMAT_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format)
INTERCEPTOR(int, sscanf, const char *str, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format)
+FORMAT_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format)
#if SANITIZER_INTERCEPT_ISOC99_SCANF
INTERCEPTOR(int, __isoc99_scanf, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format)
INTERCEPTOR(int, __isoc99_fscanf, void *stream, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format)
INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...)
-SCANF_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format)
#endif
#endif
#define INIT_ISOC99_SCANF
#endif
+#if SANITIZER_INTERCEPT_PRINTF
+
+#define VPRINTF_INTERCEPTOR_ENTER(vname, ...) \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \
+ va_list aq; \
+ va_copy(aq, ap);
+
+#define VPRINTF_INTERCEPTOR_RETURN() \
+ va_end(aq);
+
+#define VPRINTF_INTERCEPTOR_IMPL(vname, ...) \
+ { \
+ VPRINTF_INTERCEPTOR_ENTER(vname, __VA_ARGS__); \
+ if (common_flags()->check_printf) \
+ printf_common(ctx, format, aq); \
+ int res = REAL(vname)(__VA_ARGS__); \
+ VPRINTF_INTERCEPTOR_RETURN(); \
+ return res; \
+ }
+
+// FIXME: under ASan the REAL() call below may write to freed memory and
+// corrupt its metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define VSPRINTF_INTERCEPTOR_IMPL(vname, str, ...) \
+ { \
+ VPRINTF_INTERCEPTOR_ENTER(vname, str, __VA_ARGS__) \
+ if (common_flags()->check_printf) { \
+ printf_common(ctx, format, aq); \
+ } \
+ int res = REAL(vname)(str, __VA_ARGS__); \
+ if (res >= 0) { \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, res + 1); \
+ } \
+ VPRINTF_INTERCEPTOR_RETURN(); \
+ return res; \
+ }
+
+// FIXME: under ASan the REAL() call below may write to freed memory and
+// corrupt its metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define VSNPRINTF_INTERCEPTOR_IMPL(vname, str, size, ...) \
+ { \
+ VPRINTF_INTERCEPTOR_ENTER(vname, str, size, __VA_ARGS__) \
+ if (common_flags()->check_printf) { \
+ printf_common(ctx, format, aq); \
+ } \
+ int res = REAL(vname)(str, size, __VA_ARGS__); \
+ if (res >= 0) { \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, Min(size, (SIZE_T)(res + 1))); \
+ } \
+ VPRINTF_INTERCEPTOR_RETURN(); \
+ return res; \
+ }
+
+// FIXME: under ASan the REAL() call below may write to freed memory and
+// corrupt its metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define VASPRINTF_INTERCEPTOR_IMPL(vname, strp, ...) \
+ { \
+ VPRINTF_INTERCEPTOR_ENTER(vname, strp, __VA_ARGS__) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, strp, sizeof(char *)); \
+ if (common_flags()->check_printf) { \
+ printf_common(ctx, format, aq); \
+ } \
+ int res = REAL(vname)(strp, __VA_ARGS__); \
+ if (res >= 0) { \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *strp, res + 1); \
+ } \
+ VPRINTF_INTERCEPTOR_RETURN(); \
+ return res; \
+ }
+
+INTERCEPTOR(int, vprintf, const char *format, va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(vprintf, format, ap)
+
+INTERCEPTOR(int, vfprintf, __sanitizer_FILE *stream, const char *format,
+ va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(vfprintf, stream, format, ap)
+
+INTERCEPTOR(int, vsnprintf, char *str, SIZE_T size, const char *format,
+ va_list ap)
+VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf, str, size, format, ap)
+
+#if SANITIZER_INTERCEPT_PRINTF_L
+INTERCEPTOR(int, vsnprintf_l, char *str, SIZE_T size, void *loc,
+ const char *format, va_list ap)
+VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf_l, str, size, loc, format, ap)
+
+INTERCEPTOR(int, snprintf_l, char *str, SIZE_T size, void *loc,
+ const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(snprintf_l, vsnprintf_l, str, size, loc, format)
+#endif // SANITIZER_INTERCEPT_PRINTF_L
+
+INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap)
+VSPRINTF_INTERCEPTOR_IMPL(vsprintf, str, format, ap)
+
+INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap)
+VASPRINTF_INTERCEPTOR_IMPL(vasprintf, strp, format, ap)
+
+#if SANITIZER_INTERCEPT_ISOC99_PRINTF
+INTERCEPTOR(int, __isoc99_vprintf, const char *format, va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(__isoc99_vprintf, format, ap)
+
+INTERCEPTOR(int, __isoc99_vfprintf, __sanitizer_FILE *stream,
+ const char *format, va_list ap)
+VPRINTF_INTERCEPTOR_IMPL(__isoc99_vfprintf, stream, format, ap)
+
+INTERCEPTOR(int, __isoc99_vsnprintf, char *str, SIZE_T size, const char *format,
+ va_list ap)
+VSNPRINTF_INTERCEPTOR_IMPL(__isoc99_vsnprintf, str, size, format, ap)
+
+INTERCEPTOR(int, __isoc99_vsprintf, char *str, const char *format,
+ va_list ap)
+VSPRINTF_INTERCEPTOR_IMPL(__isoc99_vsprintf, str, format,
+ ap)
+
+#endif // SANITIZER_INTERCEPT_ISOC99_PRINTF
+
+INTERCEPTOR(int, printf, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(printf, vprintf, format)
+
+INTERCEPTOR(int, fprintf, __sanitizer_FILE *stream, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(fprintf, vfprintf, stream, format)
+
+INTERCEPTOR(int, sprintf, char *str, const char *format, ...) // NOLINT
+FORMAT_INTERCEPTOR_IMPL(sprintf, vsprintf, str, format) // NOLINT
+
+INTERCEPTOR(int, snprintf, char *str, SIZE_T size, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(snprintf, vsnprintf, str, size, format)
+
+INTERCEPTOR(int, asprintf, char **strp, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(asprintf, vasprintf, strp, format)
+
+#if SANITIZER_INTERCEPT_ISOC99_PRINTF
+INTERCEPTOR(int, __isoc99_printf, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_printf, __isoc99_vprintf, format)
+
+INTERCEPTOR(int, __isoc99_fprintf, __sanitizer_FILE *stream, const char *format,
+ ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_fprintf, __isoc99_vfprintf, stream, format)
+
+INTERCEPTOR(int, __isoc99_sprintf, char *str, const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_sprintf, __isoc99_vsprintf, str, format)
+
+INTERCEPTOR(int, __isoc99_snprintf, char *str, SIZE_T size,
+ const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size,
+ format)
+
+#endif // SANITIZER_INTERCEPT_ISOC99_PRINTF
+
+#endif // SANITIZER_INTERCEPT_PRINTF
+
+#if SANITIZER_INTERCEPT_PRINTF
+#define INIT_PRINTF \
+ COMMON_INTERCEPT_FUNCTION(printf); \
+ COMMON_INTERCEPT_FUNCTION(sprintf); \
+ COMMON_INTERCEPT_FUNCTION(snprintf); \
+ COMMON_INTERCEPT_FUNCTION(asprintf); \
+ COMMON_INTERCEPT_FUNCTION(fprintf); \
+ COMMON_INTERCEPT_FUNCTION(vprintf); \
+ COMMON_INTERCEPT_FUNCTION(vsprintf); \
+ COMMON_INTERCEPT_FUNCTION(vsnprintf); \
+ COMMON_INTERCEPT_FUNCTION(vasprintf); \
+ COMMON_INTERCEPT_FUNCTION(vfprintf);
+#else
+#define INIT_PRINTF
+#endif
+
+#if SANITIZER_INTERCEPT_PRINTF_L
+#define INIT_PRINTF_L \
+ COMMON_INTERCEPT_FUNCTION(snprintf_l); \
+ COMMON_INTERCEPT_FUNCTION(vsnprintf_l);
+#else
+#define INIT_PRINTF_L
+#endif
+
+#if SANITIZER_INTERCEPT_ISOC99_PRINTF
+#define INIT_ISOC99_PRINTF \
+ COMMON_INTERCEPT_FUNCTION(__isoc99_printf); \
+ COMMON_INTERCEPT_FUNCTION(__isoc99_sprintf); \
+ COMMON_INTERCEPT_FUNCTION(__isoc99_snprintf); \
+ COMMON_INTERCEPT_FUNCTION(__isoc99_fprintf); \
+ COMMON_INTERCEPT_FUNCTION(__isoc99_vprintf); \
+ COMMON_INTERCEPT_FUNCTION(__isoc99_vsprintf); \
+ COMMON_INTERCEPT_FUNCTION(__isoc99_vsnprintf); \
+ COMMON_INTERCEPT_FUNCTION(__isoc99_vfprintf);
+#else
+#define INIT_ISOC99_PRINTF
+#endif
+
#if SANITIZER_INTERCEPT_IOCTL
#include "sanitizer_common_interceptors_ioctl.inc"
-INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) {
- void *ctx;
+INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) {
+ // We need a frame pointer, because we call into ioctl_common_[pre|post] which
+ // can trigger a report and we need to be able to unwind through this
+ // function. On Mac in debug mode we might not have a frame pointer, because
+ // ioctl_common_[pre|post] doesn't get inlined here.
+ ENABLE_FRAME_POINTER;
+
+ void *ctx;
+ va_list ap;
+ va_start(ap, request);
+ void *arg = va_arg(ap, void *);
+ va_end(ap);
COMMON_INTERCEPTOR_ENTER(ctx, ioctl, d, request, arg);
CHECK(ioctl_initialized);
// This effectively disables ioctl handling in TSan.
if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg);
+ // Although request is unsigned long, the rest of the interceptor uses it
+ // as just "unsigned" to save space, because we know that all values fit in
+ // "unsigned" - they are compile-time constants.
+
const ioctl_desc *desc = ioctl_lookup(request);
- if (!desc) Printf("WARNING: unknown ioctl %x\n", request);
+ ioctl_desc decoded_desc;
+ if (!desc) {
+ VPrintf(2, "Decoding unknown ioctl 0x%x\n", request);
+ if (!ioctl_decode(request, &decoded_desc))
+ Printf("WARNING: failed decoding unknown ioctl 0x%x\n", request);
+ else
+ desc = &decoded_desc;
+ }
if (desc) ioctl_common_pre(ctx, desc, d, request, arg);
int res = REAL(ioctl)(d, request, arg);
#define INIT_IOCTL
#endif
+#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || \
+ SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || \
+ SANITIZER_INTERCEPT_GETPWENT_R || SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
+static void unpoison_passwd(void *ctx, __sanitizer_passwd *pwd) {
+ if (pwd) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, sizeof(*pwd));
+ if (pwd->pw_name)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_name,
+ REAL(strlen)(pwd->pw_name) + 1);
+ if (pwd->pw_passwd)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_passwd,
+ REAL(strlen)(pwd->pw_passwd) + 1);
+#if !SANITIZER_ANDROID
+ if (pwd->pw_gecos)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_gecos,
+ REAL(strlen)(pwd->pw_gecos) + 1);
+#endif
+#if SANITIZER_MAC
+ if (pwd->pw_class)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_class,
+ REAL(strlen)(pwd->pw_class) + 1);
+#endif
+ if (pwd->pw_dir)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_dir,
+ REAL(strlen)(pwd->pw_dir) + 1);
+ if (pwd->pw_shell)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_shell,
+ REAL(strlen)(pwd->pw_shell) + 1);
+ }
+}
+
+static void unpoison_group(void *ctx, __sanitizer_group *grp) {
+ if (grp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, sizeof(*grp));
+ if (grp->gr_name)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_name,
+ REAL(strlen)(grp->gr_name) + 1);
+ if (grp->gr_passwd)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_passwd,
+ REAL(strlen)(grp->gr_passwd) + 1);
+ char **p = grp->gr_mem;
+ for (; *p; ++p) {
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*p, REAL(strlen)(*p) + 1);
+ }
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_mem,
+ (p - grp->gr_mem + 1) * sizeof(*p));
+ }
+}
+#endif // SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS ||
+ // SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT ||
+ // SANITIZER_INTERCEPT_GETPWENT_R ||
+ // SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
+
#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
-INTERCEPTOR(void *, getpwnam, const char *name) {
+INTERCEPTOR(__sanitizer_passwd *, getpwnam, const char *name) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getpwnam, name);
COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
- void *res = REAL(getpwnam)(name);
- if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz);
+ __sanitizer_passwd *res = REAL(getpwnam)(name);
+ if (res) unpoison_passwd(ctx, res);
return res;
}
-INTERCEPTOR(void *, getpwuid, u32 uid) {
+INTERCEPTOR(__sanitizer_passwd *, getpwuid, u32 uid) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid);
- void *res = REAL(getpwuid)(uid);
- if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz);
+ __sanitizer_passwd *res = REAL(getpwuid)(uid);
+ if (res) unpoison_passwd(ctx, res);
return res;
}
-INTERCEPTOR(void *, getgrnam, const char *name) {
+INTERCEPTOR(__sanitizer_group *, getgrnam, const char *name) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name);
COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
- void *res = REAL(getgrnam)(name);
- if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz);
+ __sanitizer_group *res = REAL(getgrnam)(name);
+ if (res) unpoison_group(ctx, res);
return res;
}
-INTERCEPTOR(void *, getgrgid, u32 gid) {
+INTERCEPTOR(__sanitizer_group *, getgrgid, u32 gid) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid);
- void *res = REAL(getgrgid)(gid);
- if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz);
+ __sanitizer_group *res = REAL(getgrgid)(gid);
+ if (res) unpoison_group(ctx, res);
return res;
}
#define INIT_GETPWNAM_AND_FRIENDS \
#endif
#if SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
-INTERCEPTOR(int, getpwnam_r, const char *name, void *pwd, char *buf,
- SIZE_T buflen, void **result) {
+INTERCEPTOR(int, getpwnam_r, const char *name, __sanitizer_passwd *pwd,
+ char *buf, SIZE_T buflen, __sanitizer_passwd **result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getpwnam_r, name, pwd, buf, buflen, result);
COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(getpwnam_r)(name, pwd, buf, buflen, result);
if (!res) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, struct_passwd_sz);
+ if (result && *result) unpoison_passwd(ctx, *result);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
}
+ if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
return res;
}
-INTERCEPTOR(int, getpwuid_r, u32 uid, void *pwd, char *buf, SIZE_T buflen,
- void **result) {
+INTERCEPTOR(int, getpwuid_r, u32 uid, __sanitizer_passwd *pwd, char *buf,
+ SIZE_T buflen, __sanitizer_passwd **result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result);
if (!res) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, struct_passwd_sz);
+ if (result && *result) unpoison_passwd(ctx, *result);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
}
+ if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
return res;
}
-INTERCEPTOR(int, getgrnam_r, const char *name, void *grp, char *buf,
- SIZE_T buflen, void **result) {
+INTERCEPTOR(int, getgrnam_r, const char *name, __sanitizer_group *grp,
+ char *buf, SIZE_T buflen, __sanitizer_group **result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getgrnam_r, name, grp, buf, buflen, result);
COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(getgrnam_r)(name, grp, buf, buflen, result);
if (!res) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, struct_group_sz);
+ if (result && *result) unpoison_group(ctx, *result);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
}
+ if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
return res;
}
-INTERCEPTOR(int, getgrgid_r, u32 gid, void *grp, char *buf, SIZE_T buflen,
- void **result) {
+INTERCEPTOR(int, getgrgid_r, u32 gid, __sanitizer_group *grp, char *buf,
+ SIZE_T buflen, __sanitizer_group **result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result);
if (!res) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, struct_group_sz);
+ if (result && *result) unpoison_group(ctx, *result);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
}
+ if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
return res;
}
#define INIT_GETPWNAM_R_AND_FRIENDS \
#define INIT_GETPWNAM_R_AND_FRIENDS
#endif
+#if SANITIZER_INTERCEPT_GETPWENT
+INTERCEPTOR(__sanitizer_passwd *, getpwent, int dummy) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getpwent, dummy);
+ __sanitizer_passwd *res = REAL(getpwent)(dummy);
+ if (res) unpoison_passwd(ctx, res);
+ return res;
+}
+INTERCEPTOR(__sanitizer_group *, getgrent, int dummy) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getgrent, dummy);
+ __sanitizer_group *res = REAL(getgrent)(dummy);
+ if (res) unpoison_group(ctx, res);;
+ return res;
+}
+#define INIT_GETPWENT \
+ COMMON_INTERCEPT_FUNCTION(getpwent); \
+ COMMON_INTERCEPT_FUNCTION(getgrent);
+#else
+#define INIT_GETPWENT
+#endif
+
+#if SANITIZER_INTERCEPT_FGETPWENT
+INTERCEPTOR(__sanitizer_passwd *, fgetpwent, void *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent, fp);
+ __sanitizer_passwd *res = REAL(fgetpwent)(fp);
+ if (res) unpoison_passwd(ctx, res);
+ return res;
+}
+INTERCEPTOR(__sanitizer_group *, fgetgrent, void *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent, fp);
+ __sanitizer_group *res = REAL(fgetgrent)(fp);
+ if (res) unpoison_group(ctx, res);
+ return res;
+}
+#define INIT_FGETPWENT \
+ COMMON_INTERCEPT_FUNCTION(fgetpwent); \
+ COMMON_INTERCEPT_FUNCTION(fgetgrent);
+#else
+#define INIT_FGETPWENT
+#endif
+
+#if SANITIZER_INTERCEPT_GETPWENT_R
+INTERCEPTOR(int, getpwent_r, __sanitizer_passwd *pwbuf, char *buf,
+ SIZE_T buflen, __sanitizer_passwd **pwbufp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getpwent_r, pwbuf, buf, buflen, pwbufp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(getpwent_r)(pwbuf, buf, buflen, pwbufp);
+ if (!res) {
+ if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+ }
+ if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
+ return res;
+}
+INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf,
+ SIZE_T buflen, __sanitizer_passwd **pwbufp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp);
+ if (!res) {
+ if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+ }
+ if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
+ return res;
+}
+INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen,
+ __sanitizer_group **pwbufp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp);
+ if (!res) {
+ if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+ }
+ if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
+ return res;
+}
+INTERCEPTOR(int, fgetgrent_r, void *fp, __sanitizer_group *pwbuf, char *buf,
+ SIZE_T buflen, __sanitizer_group **pwbufp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent_r, fp, pwbuf, buf, buflen, pwbufp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(fgetgrent_r)(fp, pwbuf, buf, buflen, pwbufp);
+ if (!res) {
+ if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
+ }
+ if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
+ return res;
+}
+#define INIT_GETPWENT_R \
+ COMMON_INTERCEPT_FUNCTION(getpwent_r); \
+ COMMON_INTERCEPT_FUNCTION(fgetpwent_r); \
+ COMMON_INTERCEPT_FUNCTION(getgrent_r); \
+ COMMON_INTERCEPT_FUNCTION(fgetgrent_r);
+#else
+#define INIT_GETPWENT_R
+#endif
+
+#if SANITIZER_INTERCEPT_SETPWENT
+// The only thing these interceptors do is disable any nested interceptors.
+// These functions may open nss modules and call uninstrumented functions from
+// them, and we don't want things like strlen() to trigger.
+INTERCEPTOR(void, setpwent, int dummy) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, setpwent, dummy);
+ REAL(setpwent)(dummy);
+}
+INTERCEPTOR(void, endpwent, int dummy) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, endpwent, dummy);
+ REAL(endpwent)(dummy);
+}
+INTERCEPTOR(void, setgrent, int dummy) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, setgrent, dummy);
+ REAL(setgrent)(dummy);
+}
+INTERCEPTOR(void, endgrent, int dummy) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, endgrent, dummy);
+ REAL(endgrent)(dummy);
+}
+#define INIT_SETPWENT \
+ COMMON_INTERCEPT_FUNCTION(setpwent); \
+ COMMON_INTERCEPT_FUNCTION(endpwent); \
+ COMMON_INTERCEPT_FUNCTION(setgrent); \
+ COMMON_INTERCEPT_FUNCTION(endgrent);
+#else
+#define INIT_SETPWENT
+#endif
+
#if SANITIZER_INTERCEPT_CLOCK_GETTIME
INTERCEPTOR(int, clock_getres, u32 clk_id, void *tp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, clock_getres, clk_id, tp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(clock_getres)(clk_id, tp);
if (!res && tp) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz);
INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, clock_gettime, clk_id, tp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(clock_gettime)(clk_id, tp);
if (!res) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz);
INTERCEPTOR(int, getitimer, int which, void *curr_value) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getitimer, which, curr_value);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(getitimer)(which, curr_value);
if (!res && curr_value) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerval_sz);
COMMON_INTERCEPTOR_ENTER(ctx, setitimer, which, new_value, old_value);
if (new_value)
COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(setitimer)(which, new_value, old_value);
if (!res && old_value) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerval_sz);
}
static THREADLOCAL __sanitizer_glob_t *pglob_copy;
-static THREADLOCAL void *glob_ctx;
static void wrapped_gl_closedir(void *dir) {
- COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 1);
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
pglob_copy->gl_closedir(dir);
}
static void *wrapped_gl_readdir(void *dir) {
- COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 1);
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
return pglob_copy->gl_readdir(dir);
}
static void *wrapped_gl_opendir(const char *s) {
- COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 1);
- COMMON_INTERCEPTOR_WRITE_RANGE(glob_ctx, s, REAL(strlen)(s) + 1);
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
return pglob_copy->gl_opendir(s);
}
static int wrapped_gl_lstat(const char *s, void *st) {
- COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 2);
- COMMON_INTERCEPTOR_WRITE_RANGE(glob_ctx, s, REAL(strlen)(s) + 1);
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
return pglob_copy->gl_lstat(s, st);
}
static int wrapped_gl_stat(const char *s, void *st) {
- COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 2);
- COMMON_INTERCEPTOR_WRITE_RANGE(glob_ctx, s, REAL(strlen)(s) + 1);
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
return pglob_copy->gl_stat(s, st);
}
__sanitizer_glob_t *pglob) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
__sanitizer_glob_t glob_copy = {
0, 0, 0,
0, wrapped_gl_closedir, wrapped_gl_readdir,
Swap(pglob->gl_lstat, glob_copy.gl_lstat);
Swap(pglob->gl_stat, glob_copy.gl_stat);
pglob_copy = &glob_copy;
- glob_ctx = ctx;
}
int res = REAL(glob)(pattern, flags, errfunc, pglob);
if (flags & glob_altdirfunc) {
Swap(pglob->gl_stat, glob_copy.gl_stat);
}
pglob_copy = 0;
- glob_ctx = 0;
if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob);
return res;
}
__sanitizer_glob_t *pglob) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
__sanitizer_glob_t glob_copy = {
0, 0, 0,
0, wrapped_gl_closedir, wrapped_gl_readdir,
Swap(pglob->gl_lstat, glob_copy.gl_lstat);
Swap(pglob->gl_stat, glob_copy.gl_stat);
pglob_copy = &glob_copy;
- glob_ctx = ctx;
}
int res = REAL(glob64)(pattern, flags, errfunc, pglob);
if (flags & glob_altdirfunc) {
Swap(pglob->gl_stat, glob_copy.gl_stat);
}
pglob_copy = 0;
- glob_ctx = 0;
if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob);
return res;
}
INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, wait, status);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(wait)(status);
if (res != -1 && status)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
return res;
}
+// On FreeBSD id_t is always 64-bit wide.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, long long id, void *infop,
+ int options) {
+#else
INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop,
int options) {
+#endif
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(waitid)(idtype, id, infop, options);
if (res != -1 && infop)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz);
INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, waitpid, pid, status, options);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(waitpid)(pid, status, options);
if (res != -1 && status)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
INTERCEPTOR(int, wait3, int *status, int options, void *rusage) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(wait3)(status, options, rusage);
if (res != -1) {
if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
}
return res;
}
+#if SANITIZER_ANDROID
+INTERCEPTOR(int, __wait4, int pid, int *status, int options, void *rusage) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, __wait4, pid, status, options, rusage);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(__wait4)(pid, status, options, rusage);
+ if (res != -1) {
+ if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
+ if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz);
+ }
+ return res;
+}
+#define INIT_WAIT4 COMMON_INTERCEPT_FUNCTION(__wait4);
+#else
INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(wait4)(pid, status, options, rusage);
if (res != -1) {
if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
}
return res;
}
+#define INIT_WAIT4 COMMON_INTERCEPT_FUNCTION(wait4);
+#endif // SANITIZER_ANDROID
#define INIT_WAIT \
COMMON_INTERCEPT_FUNCTION(wait); \
COMMON_INTERCEPT_FUNCTION(waitid); \
COMMON_INTERCEPT_FUNCTION(waitpid); \
- COMMON_INTERCEPT_FUNCTION(wait3); \
- COMMON_INTERCEPT_FUNCTION(wait4);
+ COMMON_INTERCEPT_FUNCTION(wait3);
#else
#define INIT_WAIT
+#define INIT_WAIT4
#endif
#if SANITIZER_INTERCEPT_INET
uptr sz = __sanitizer_in_addr_sz(af);
if (sz) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sz);
// FIXME: figure out read size based on the address family.
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(inet_ntop)(af, src, dst, size);
if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
return res;
INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, src, 0);
// FIXME: figure out read size based on the address family.
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(inet_pton)(af, src, dst);
if (res == 1) {
uptr sz = __sanitizer_in_addr_sz(af);
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, inet_aton, cp, dst);
if (cp) COMMON_INTERCEPTOR_READ_RANGE(ctx, cp, REAL(strlen)(cp) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(inet_aton)(cp, dst);
if (res != 0) {
uptr sz = __sanitizer_in_addr_sz(af_inet);
INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, pthread_getschedparam, thread, policy, param);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(pthread_getschedparam)(thread, policy, param);
if (res == 0) {
if (policy) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, policy, sizeof(*policy));
COMMON_INTERCEPTOR_READ_RANGE(ctx, service, REAL(strlen)(service) + 1);
if (hints)
COMMON_INTERCEPTOR_READ_RANGE(ctx, hints, sizeof(__sanitizer_addrinfo));
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(getaddrinfo)(node, service, hints, out);
if (res == 0 && out) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, out, sizeof(*out));
serv, servlen, flags);
// FIXME: consider adding READ_RANGE(sockaddr, salen)
// There is padding in in_addr that may make this too noisy
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res =
REAL(getnameinfo)(sockaddr, salen, host, hostlen, serv, servlen, flags);
if (res == 0) {
COMMON_INTERCEPTOR_ENTER(ctx, getsockname, sock_fd, addr, addrlen);
COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen));
int addrlen_in = *addrlen;
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(getsockname)(sock_fd, addr, addrlen);
if (res == 0) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addrlen_in, *addrlen));
#endif
#if SANITIZER_INTERCEPT_GETHOSTBYNAME_R
+INTERCEPTOR(int, gethostbyname_r, char *name, struct __sanitizer_hostent *ret,
+ char *buf, SIZE_T buflen, __sanitizer_hostent **result,
+ int *h_errnop) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname_r, name, ret, buf, buflen, result,
+ h_errnop);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(gethostbyname_r)(name, ret, buf, buflen, result, h_errnop);
+ if (result) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+ if (res == 0 && *result) write_hostent(ctx, *result);
+ }
+ if (h_errnop)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
+ return res;
+}
+#define INIT_GETHOSTBYNAME_R COMMON_INTERCEPT_FUNCTION(gethostbyname_r);
+#else
+#define INIT_GETHOSTBYNAME_R
+#endif
+
+#if SANITIZER_INTERCEPT_GETHOSTENT_R
INTERCEPTOR(int, gethostent_r, struct __sanitizer_hostent *ret, char *buf,
SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, gethostent_r, ret, buf, buflen, result,
h_errnop);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(gethostent_r)(ret, buf, buflen, result, h_errnop);
- if (res == 0) {
- if (result) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
- if (*result) write_hostent(ctx, *result);
- }
- if (h_errnop)
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
+ if (result) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+ if (res == 0 && *result) write_hostent(ctx, *result);
}
+ if (h_errnop)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
return res;
}
+#define INIT_GETHOSTENT_R \
+ COMMON_INTERCEPT_FUNCTION(gethostent_r);
+#else
+#define INIT_GETHOSTENT_R
+#endif
+#if SANITIZER_INTERCEPT_GETHOSTBYADDR_R
INTERCEPTOR(int, gethostbyaddr_r, void *addr, int len, int type,
struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen,
__sanitizer_hostent **result, int *h_errnop) {
COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr_r, addr, len, type, ret, buf,
buflen, result, h_errnop);
COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(gethostbyaddr_r)(addr, len, type, ret, buf, buflen, result,
h_errnop);
- if (res == 0) {
- if (result) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
- if (*result) write_hostent(ctx, *result);
- }
- if (h_errnop)
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
- }
- return res;
-}
-
-INTERCEPTOR(int, gethostbyname_r, char *name, struct __sanitizer_hostent *ret,
- char *buf, SIZE_T buflen, __sanitizer_hostent **result,
- int *h_errnop) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname_r, name, ret, buf, buflen, result,
- h_errnop);
- int res = REAL(gethostbyname_r)(name, ret, buf, buflen, result, h_errnop);
- if (res == 0) {
- if (result) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
- if (*result) write_hostent(ctx, *result);
- }
- if (h_errnop)
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
+ if (result) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+ if (res == 0 && *result) write_hostent(ctx, *result);
}
+ if (h_errnop)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
return res;
}
+#define INIT_GETHOSTBYADDR_R \
+ COMMON_INTERCEPT_FUNCTION(gethostbyaddr_r);
+#else
+#define INIT_GETHOSTBYADDR_R
+#endif
+#if SANITIZER_INTERCEPT_GETHOSTBYNAME2_R
INTERCEPTOR(int, gethostbyname2_r, char *name, int af,
struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen,
__sanitizer_hostent **result, int *h_errnop) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2_r, name, af, ret, buf, buflen,
result, h_errnop);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res =
REAL(gethostbyname2_r)(name, af, ret, buf, buflen, result, h_errnop);
- if (res == 0) {
- if (result) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
- if (*result) write_hostent(ctx, *result);
- }
- if (h_errnop)
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
+ if (result) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
+ if (res == 0 && *result) write_hostent(ctx, *result);
}
+ if (h_errnop)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop));
return res;
}
-#define INIT_GETHOSTBYNAME_R \
- COMMON_INTERCEPT_FUNCTION(gethostent_r); \
- COMMON_INTERCEPT_FUNCTION(gethostbyaddr_r); \
- COMMON_INTERCEPT_FUNCTION(gethostbyname_r); \
+#define INIT_GETHOSTBYNAME2_R \
COMMON_INTERCEPT_FUNCTION(gethostbyname2_r);
#else
-#define INIT_GETHOSTBYNAME_R
+#define INIT_GETHOSTBYNAME2_R
#endif
#if SANITIZER_INTERCEPT_GETSOCKOPT
COMMON_INTERCEPTOR_ENTER(ctx, getsockopt, sockfd, level, optname, optval,
optlen);
if (optlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, optlen, sizeof(*optlen));
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(getsockopt)(sockfd, level, optname, optval, optlen);
if (res == 0)
if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen);
INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, accept, fd, addr, addrlen);
- unsigned addrlen0;
+ unsigned addrlen0 = 0;
if (addrlen) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen));
addrlen0 = *addrlen;
INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, accept4, fd, addr, addrlen, f);
- unsigned addrlen0;
+ unsigned addrlen0 = 0;
if (addrlen) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen));
addrlen0 = *addrlen;
}
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int fd2 = REAL(accept4)(fd, addr, addrlen, f);
if (fd2 >= 0) {
if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2);
INTERCEPTOR(double, modf, double x, double *iptr) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, modf, x, iptr);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
double res = REAL(modf)(x, iptr);
if (iptr) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr));
INTERCEPTOR(float, modff, float x, float *iptr) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, modff, x, iptr);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
float res = REAL(modff)(x, iptr);
if (iptr) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr));
INTERCEPTOR(long double, modfl, long double x, long double *iptr) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, modfl, x, iptr);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
long double res = REAL(modfl)(x, iptr);
if (iptr) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr));
int flags) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, recvmsg, fd, msg, flags);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SSIZE_T res = REAL(recvmsg)(fd, msg, flags);
if (res >= 0) {
if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
COMMON_INTERCEPTOR_ENTER(ctx, getpeername, sockfd, addr, addrlen);
unsigned addr_sz;
if (addrlen) addr_sz = *addrlen;
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(getpeername)(sockfd, addr, addrlen);
if (!res && addr && addrlen)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen));
#if SANITIZER_INTERCEPT_SYSINFO
INTERCEPTOR(int, sysinfo, void *info) {
void *ctx;
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
COMMON_INTERCEPTOR_ENTER(ctx, sysinfo, info);
int res = REAL(sysinfo)(info);
if (!res && info)
#endif
#if SANITIZER_INTERCEPT_READDIR
+INTERCEPTOR(__sanitizer_dirent *, opendir, const char *path) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, opendir, path);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ __sanitizer_dirent *res = REAL(opendir)(path);
+ if (res)
+ COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path);
+ return res;
+}
+
INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
__sanitizer_dirent *res = REAL(readdir)(dirp);
if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen);
return res;
__sanitizer_dirent **result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, readdir_r, dirp, entry, result);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(readdir_r)(dirp, entry, result);
if (!res) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
}
#define INIT_READDIR \
+ COMMON_INTERCEPT_FUNCTION(opendir); \
COMMON_INTERCEPT_FUNCTION(readdir); \
COMMON_INTERCEPT_FUNCTION(readdir_r);
#else
INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
__sanitizer_dirent64 *res = REAL(readdir64)(dirp);
if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen);
return res;
__sanitizer_dirent64 **result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, readdir64_r, dirp, entry, result);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(readdir64_r)(dirp, entry, result);
if (!res) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data);
+ __sanitizer_iovec local_iovec;
if (data) {
if (request == ptrace_setregs)
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz);
else if (request == ptrace_setfpxregs)
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz);
+ else if (request == ptrace_setvfpregs)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_vfpregs_struct_sz);
else if (request == ptrace_setsiginfo)
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz);
- else if (request == ptrace_setregset) {
- __sanitizer_iovec *iov = (__sanitizer_iovec *)data;
- COMMON_INTERCEPTOR_READ_RANGE(ctx, iov->iov_base, iov->iov_len);
+ // Some kernel might zero the iovec::iov_base in case of invalid
+ // write access. In this case copy the invalid address for further
+ // inspection.
+ else if (request == ptrace_setregset || request == ptrace_getregset) {
+ __sanitizer_iovec *iovec = (__sanitizer_iovec*)data;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec));
+ local_iovec = *iovec;
+ if (request == ptrace_setregset)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec->iov_base, iovec->iov_len);
}
}
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
uptr res = REAL(ptrace)(request, pid, addr, data);
if (!res && data) {
- // Note that PEEK* requests assing different meaning to the return value.
+ // Note that PEEK* requests assign different meaning to the return value.
// This function does not handle them (nor does it need to).
if (request == ptrace_getregs)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_regs_struct_sz);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz);
else if (request == ptrace_getfpxregs)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz);
+ else if (request == ptrace_getvfpregs)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_vfpregs_struct_sz);
else if (request == ptrace_getsiginfo)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz);
+ else if (request == ptrace_geteventmsg)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long));
else if (request == ptrace_getregset) {
- __sanitizer_iovec *iov = (__sanitizer_iovec *)data;
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iov->iov_base, iov->iov_len);
+ __sanitizer_iovec *iovec = (__sanitizer_iovec*)data;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec, sizeof(*iovec));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, local_iovec.iov_base,
+ local_iovec.iov_len);
}
}
return res;
INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(getcwd)(buf, size);
if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
return res;
INTERCEPTOR(char *, get_current_dir_name, int fake) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name, fake);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(get_current_dir_name)(fake);
if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
return res;
#define INIT_GET_CURRENT_DIR_NAME
#endif
+UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
+ CHECK(endptr);
+ if (nptr == *endptr) {
+ // No digits were found at strtol call, we need to find out the last
+ // symbol accessed by strtoll on our own.
+ // We get this symbol by skipping leading blanks and optional +/- sign.
+ while (IsSpace(*nptr)) nptr++;
+ if (*nptr == '+' || *nptr == '-') nptr++;
+ *endptr = const_cast<char *>(nptr);
+ }
+ CHECK(*endptr >= nptr);
+}
+
+UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr,
+ char **endptr, char *real_endptr, int base) {
+ if (endptr) {
+ *endptr = real_endptr;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
+ }
+ // If base has unsupported value, strtol can exit with EINVAL
+ // without reading any characters. So do additional checks only
+ // if base is valid.
+ bool is_valid_base = (base == 0) || (2 <= base && base <= 36);
+ if (is_valid_base) {
+ FixRealStrtolEndptr(nptr, &real_endptr);
+ }
+ COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, is_valid_base ?
+ (real_endptr - nptr) + 1 : 0);
+}
+
+
#if SANITIZER_INTERCEPT_STRTOIMAX
INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, base);
- INTMAX_T res = REAL(strtoimax)(nptr, endptr, base);
- if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ char *real_endptr;
+ INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base);
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return res;
}
INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, base);
- INTMAX_T res = REAL(strtoumax)(nptr, endptr, base);
- if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ char *real_endptr;
+ INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base);
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return res;
}
INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, mbstowcs, dest, src, len);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SIZE_T res = REAL(mbstowcs)(dest, src, len);
if (res != (SIZE_T) - 1 && dest) {
SIZE_T write_cnt = res + (res < len);
COMMON_INTERCEPTOR_ENTER(ctx, mbsrtowcs, dest, src, len, ps);
if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src));
if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SIZE_T res = REAL(mbsrtowcs)(dest, src, len, ps);
if (res != (SIZE_T)(-1) && dest && src) {
// This function, and several others, may or may not write the terminating
if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms);
}
if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps);
if (res != (SIZE_T)(-1) && dest && src) {
SIZE_T write_cnt = res + !*src;
INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, wcstombs, dest, src, len);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SIZE_T res = REAL(wcstombs)(dest, src, len);
if (res != (SIZE_T) - 1 && dest) {
SIZE_T write_cnt = res + (res < len);
COMMON_INTERCEPTOR_ENTER(ctx, wcsrtombs, dest, src, len, ps);
if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src));
if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps);
if (res != (SIZE_T) - 1 && dest && src) {
SIZE_T write_cnt = res + !*src;
if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms);
}
if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps);
- if (res != (SIZE_T) - 1 && dest && src) {
+ if (res != ((SIZE_T)-1) && dest && src) {
SIZE_T write_cnt = res + !*src;
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt);
}
#define INIT_WCSNRTOMBS
#endif
+
+#if SANITIZER_INTERCEPT_WCRTOMB
+INTERCEPTOR(SIZE_T, wcrtomb, char *dest, wchar_t src, void *ps) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, wcrtomb, dest, src, ps);
+ if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ SIZE_T res = REAL(wcrtomb)(dest, src, ps);
+ if (res != ((SIZE_T)-1) && dest) {
+ SIZE_T write_cnt = res;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt);
+ }
+ return res;
+}
+
+#define INIT_WCRTOMB COMMON_INTERCEPT_FUNCTION(wcrtomb);
+#else
+#define INIT_WCRTOMB
+#endif
+
#if SANITIZER_INTERCEPT_TCGETATTR
INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, tcgetattr, fd, termios_p);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(tcgetattr)(fd, termios_p);
if (!res && termios_p)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, termios_p, struct_termios_sz);
// version of a versioned symbol. For realpath(), this gives us something
// (called __old_realpath) that does not handle NULL in the second argument.
// Handle it as part of the interceptor.
- char *allocated_path = 0;
+ char *allocated_path = nullptr;
if (!resolved_path)
allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1);
INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, confstr, name, buf, len);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SIZE_T res = REAL(confstr)(name, buf, len);
if (buf && res)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len);
INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(sched_getaffinity)(pid, cpusetsize, mask);
if (mask && !res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize);
return res;
#define INIT_SCHED_GETAFFINITY
#endif
+#if SANITIZER_INTERCEPT_SCHED_GETPARAM
+INTERCEPTOR(int, sched_getparam, int pid, void *param) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sched_getparam, pid, param);
+ int res = REAL(sched_getparam)(pid, param);
+ if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, struct_sched_param_sz);
+ return res;
+}
+#define INIT_SCHED_GETPARAM COMMON_INTERCEPT_FUNCTION(sched_getparam);
+#else
+#define INIT_SCHED_GETPARAM
+#endif
+
#if SANITIZER_INTERCEPT_STRERROR
INTERCEPTOR(char *, strerror, int errnum) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, strerror, errnum);
char *res = REAL(strerror)(errnum);
- if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+ if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
return res;
}
#define INIT_STRERROR COMMON_INTERCEPT_FUNCTION(strerror);
INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(strerror_r)(errnum, buf, buflen);
// There are 2 versions of strerror_r:
// * POSIX version returns 0 on success, negative error code on failure,
INTERCEPTOR(int, __xpg_strerror_r, int errnum, char *buf, SIZE_T buflen) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, __xpg_strerror_r, errnum, buf, buflen);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(__xpg_strerror_r)(errnum, buf, buflen);
// This version always returns a null-terminated string.
if (buf && buflen)
typedef int (*scandir_compar_f)(const struct __sanitizer_dirent **,
const struct __sanitizer_dirent **);
-static THREADLOCAL void *scandir_ctx;
static THREADLOCAL scandir_filter_f scandir_filter;
static THREADLOCAL scandir_compar_f scandir_compar;
static int wrapped_scandir_filter(const struct __sanitizer_dirent *dir) {
- COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir_ctx, 1);
- COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, dir, dir->d_reclen);
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen);
return scandir_filter(dir);
}
static int wrapped_scandir_compar(const struct __sanitizer_dirent **a,
const struct __sanitizer_dirent **b) {
- COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir_ctx, 2);
- COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, a, sizeof(*a));
- COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, *a, (*a)->d_reclen);
- COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, b, sizeof(*b));
- COMMON_INTERCEPTOR_WRITE_RANGE(scandir_ctx, *b, (*b)->d_reclen);
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a));
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b));
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen);
return scandir_compar(a, b);
}
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, scandir, dirp, namelist, filter, compar);
if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1);
- CHECK_EQ(0, scandir_ctx);
- scandir_ctx = ctx;
scandir_filter = filter;
scandir_compar = compar;
- int res = REAL(scandir)(dirp, namelist, filter ? wrapped_scandir_filter : 0,
- compar ? wrapped_scandir_compar : 0);
- scandir_ctx = 0;
- scandir_filter = 0;
- scandir_compar = 0;
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(scandir)(dirp, namelist,
+ filter ? wrapped_scandir_filter : nullptr,
+ compar ? wrapped_scandir_compar : nullptr);
+ scandir_filter = nullptr;
+ scandir_compar = nullptr;
if (namelist && res > 0) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist));
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res);
typedef int (*scandir64_compar_f)(const struct __sanitizer_dirent64 **,
const struct __sanitizer_dirent64 **);
-static THREADLOCAL void *scandir64_ctx;
static THREADLOCAL scandir64_filter_f scandir64_filter;
static THREADLOCAL scandir64_compar_f scandir64_compar;
static int wrapped_scandir64_filter(const struct __sanitizer_dirent64 *dir) {
- COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir64_ctx, 1);
- COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, dir, dir->d_reclen);
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen);
return scandir64_filter(dir);
}
static int wrapped_scandir64_compar(const struct __sanitizer_dirent64 **a,
const struct __sanitizer_dirent64 **b) {
- COMMON_INTERCEPTOR_UNPOISON_PARAM(scandir64_ctx, 2);
- COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, a, sizeof(*a));
- COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, *a, (*a)->d_reclen);
- COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, b, sizeof(*b));
- COMMON_INTERCEPTOR_WRITE_RANGE(scandir64_ctx, *b, (*b)->d_reclen);
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a));
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b));
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen);
return scandir64_compar(a, b);
}
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, scandir64, dirp, namelist, filter, compar);
if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1);
- CHECK_EQ(0, scandir64_ctx);
- scandir64_ctx = ctx;
scandir64_filter = filter;
scandir64_compar = compar;
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res =
- REAL(scandir64)(dirp, namelist, filter ? wrapped_scandir64_filter : 0,
- compar ? wrapped_scandir64_compar : 0);
- scandir64_ctx = 0;
- scandir64_filter = 0;
- scandir64_compar = 0;
+ REAL(scandir64)(dirp, namelist,
+ filter ? wrapped_scandir64_filter : nullptr,
+ compar ? wrapped_scandir64_compar : nullptr);
+ scandir64_filter = nullptr;
+ scandir64_compar = nullptr;
if (namelist && res > 0) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist));
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res);
INTERCEPTOR(int, getgroups, int size, u32 *lst) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(getgroups)(size, lst);
if (res && lst) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst));
return res;
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, wordexp, s, p, flags);
if (s) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(wordexp)(s, p, flags);
if (!res && p) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p));
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, sigwait, set, sig);
// FIXME: read sigset_t when all of sigemptyset, etc are intercepted
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(sigwait)(set, sig);
if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig));
return res;
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, sigwaitinfo, set, info);
// FIXME: read sigset_t when all of sigemptyset, etc are intercepted
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(sigwaitinfo)(set, info);
if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz);
return res;
COMMON_INTERCEPTOR_ENTER(ctx, sigtimedwait, set, info, timeout);
if (timeout) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz);
// FIXME: read sigset_t when all of sigemptyset, etc are intercepted
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(sigtimedwait)(set, info, timeout);
if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz);
return res;
INTERCEPTOR(int, sigemptyset, __sanitizer_sigset_t *set) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, sigemptyset, set);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(sigemptyset)(set);
if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set));
return res;
INTERCEPTOR(int, sigfillset, __sanitizer_sigset_t *set) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, sigfillset, set);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(sigfillset)(set);
if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set));
return res;
INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, sigpending, set);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(sigpending)(set);
if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set));
return res;
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset);
// FIXME: read sigset_t when all of sigemptyset, etc are intercepted
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(sigprocmask)(how, set, oldset);
if (!res && oldset)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
INTERCEPTOR(int, backtrace, void **buffer, int size) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(backtrace)(buffer, size);
if (res && buffer)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer));
COMMON_INTERCEPTOR_ENTER(ctx, backtrace_symbols, buffer, size);
if (buffer && size)
COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer));
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char **res = REAL(backtrace_symbols)(buffer, size);
if (res && size) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res));
#define INIT_PTHREAD_MUTEX_UNLOCK
#endif
-#if SANITIZER_INTERCEPT_PTHREAD_COND
-INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait, c, m);
- COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz);
- int res = REAL(pthread_cond_wait)(c, m);
- COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m);
- return res;
-}
-
-INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init, c, a);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, pthread_cond_t_sz);
- return REAL(pthread_cond_init)(c, a);
-}
-
-INTERCEPTOR(int, pthread_cond_signal, void *c) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal, c);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz);
- return REAL(pthread_cond_signal)(c);
-}
-
-INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast, c);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz);
- return REAL(pthread_cond_broadcast)(c);
-}
-
-#define INIT_PTHREAD_COND_WAIT \
- INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2")
-#define INIT_PTHREAD_COND_INIT \
- INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2")
-#define INIT_PTHREAD_COND_SIGNAL \
- INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2")
-#define INIT_PTHREAD_COND_BROADCAST \
- INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2")
-#else
-#define INIT_PTHREAD_COND_WAIT
-#define INIT_PTHREAD_COND_INIT
-#define INIT_PTHREAD_COND_SIGNAL
-#define INIT_PTHREAD_COND_BROADCAST
-#endif
-
#if SANITIZER_INTERCEPT_GETMNTENT || SANITIZER_INTERCEPT_GETMNTENT_R
static void write_mntent(void *ctx, __sanitizer_mntent *mnt) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt, sizeof(*mnt));
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, statfs, path, buf);
if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(statfs)(path, buf);
if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz);
return res;
INTERCEPTOR(int, fstatfs, int fd, void *buf) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fstatfs, fd, buf);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(fstatfs)(fd, buf);
if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz);
return res;
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, statfs64, path, buf);
if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(statfs64)(path, buf);
if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz);
return res;
INTERCEPTOR(int, fstatfs64, int fd, void *buf) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fstatfs64, fd, buf);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(fstatfs64)(fd, buf);
if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz);
return res;
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, statvfs, path, buf);
if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(statvfs)(path, buf);
if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
return res;
INTERCEPTOR(int, fstatvfs, int fd, void *buf) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(fstatvfs)(fd, buf);
if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
return res;
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, statvfs64, path, buf);
if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(statvfs64)(path, buf);
if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz);
return res;
INTERCEPTOR(int, fstatvfs64, int fd, void *buf) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs64, fd, buf);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(fstatvfs64)(fd, buf);
if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz);
return res;
#define INIT_INITGROUPS
#endif
-#if SANITIZER_INTERCEPT_ETHER
+#if SANITIZER_INTERCEPT_ETHER_NTOA_ATON
INTERCEPTOR(char *, ether_ntoa, __sanitizer_ether_addr *addr) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa, addr);
if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr));
char *res = REAL(ether_ntoa)(addr);
- if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+ if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
return res;
}
INTERCEPTOR(__sanitizer_ether_addr *, ether_aton, char *buf) {
COMMON_INTERCEPTOR_ENTER(ctx, ether_aton, buf);
if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1);
__sanitizer_ether_addr *res = REAL(ether_aton)(buf);
- if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, sizeof(*res));
+ if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, sizeof(*res));
return res;
}
+#define INIT_ETHER_NTOA_ATON \
+ COMMON_INTERCEPT_FUNCTION(ether_ntoa); \
+ COMMON_INTERCEPT_FUNCTION(ether_aton);
+#else
+#define INIT_ETHER_NTOA_ATON
+#endif
+
+#if SANITIZER_INTERCEPT_ETHER_HOST
INTERCEPTOR(int, ether_ntohost, char *hostname, __sanitizer_ether_addr *addr) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, ether_ntohost, hostname, addr);
if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr));
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(ether_ntohost)(hostname, addr);
if (!res && hostname)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1);
COMMON_INTERCEPTOR_ENTER(ctx, ether_hostton, hostname, addr);
if (hostname)
COMMON_INTERCEPTOR_READ_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(ether_hostton)(hostname, addr);
if (!res && addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr));
return res;
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, ether_line, line, addr, hostname);
if (line) COMMON_INTERCEPTOR_READ_RANGE(ctx, line, REAL(strlen)(line) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(ether_line)(line, addr, hostname);
if (!res) {
if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr));
}
return res;
}
-#define INIT_ETHER \
- COMMON_INTERCEPT_FUNCTION(ether_ntoa); \
- COMMON_INTERCEPT_FUNCTION(ether_aton); \
+#define INIT_ETHER_HOST \
COMMON_INTERCEPT_FUNCTION(ether_ntohost); \
COMMON_INTERCEPT_FUNCTION(ether_hostton); \
COMMON_INTERCEPT_FUNCTION(ether_line);
#else
-#define INIT_ETHER
+#define INIT_ETHER_HOST
#endif
#if SANITIZER_INTERCEPT_ETHER_R
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa_r, addr, buf);
if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr));
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(ether_ntoa_r)(addr, buf);
if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
return res;
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, ether_aton_r, buf, addr);
if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
__sanitizer_ether_addr *res = REAL(ether_aton_r)(buf, addr);
if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(*res));
return res;
INTERCEPTOR(int, shmctl, int shmid, int cmd, void *buf) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, shmctl, shmid, cmd, buf);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(shmctl)(shmid, cmd, buf);
if (res >= 0) {
unsigned sz = 0;
INTERCEPTOR(int, random_r, void *buf, u32 *result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, random_r, buf, result);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(random_r)(buf, result);
if (!res && result)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
#define INIT_RANDOM_R
#endif
-#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET || \
- SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED
-#define INTERCEPTOR_PTHREAD_ATTR_GET(what, sz) \
- INTERCEPTOR(int, pthread_attr_get##what, void *attr, void *r) { \
- void *ctx; \
- COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_get##what, attr, r); \
- int res = REAL(pthread_attr_get##what)(attr, r); \
- if (!res && r) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, r, sz); \
- return res; \
+// FIXME: under ASan the REAL() call below may write to freed memory and corrupt
+// its metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET || \
+ SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED || \
+ SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GET || \
+ SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GET || \
+ SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GET || \
+ SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GET
+#define INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(fn, sz) \
+ INTERCEPTOR(int, fn, void *attr, void *r) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, fn, attr, r); \
+ int res = REAL(fn)(attr, r); \
+ if (!res && r) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, r, sz); \
+ return res; \
}
+#define INTERCEPTOR_PTHREAD_ATTR_GET(what, sz) \
+ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_attr_get##what, sz)
+#define INTERCEPTOR_PTHREAD_MUTEXATTR_GET(what, sz) \
+ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_mutexattr_get##what, sz)
+#define INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(what, sz) \
+ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_rwlockattr_get##what, sz)
+#define INTERCEPTOR_PTHREAD_CONDATTR_GET(what, sz) \
+ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_condattr_get##what, sz)
+#define INTERCEPTOR_PTHREAD_BARRIERATTR_GET(what, sz) \
+ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_barrierattr_get##what, sz)
#endif
#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET
INTERCEPTOR(int, pthread_attr_getstack, void *attr, void **addr, SIZE_T *size) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getstack, attr, addr, size);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(pthread_attr_getstack)(attr, addr, size);
if (!res) {
if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr));
// We may need to call the real pthread_attr_getstack from the run-time
// in sanitizer_common, but we don't want to include the interception headers
// there. So, just define this function here.
-int __sanitizer_pthread_attr_getstack(void *attr, void **addr, SIZE_T *size) {
+namespace __sanitizer {
+extern "C" {
+int real_pthread_attr_getstack(void *attr, void **addr, SIZE_T *size) {
return REAL(pthread_attr_getstack)(attr, addr, size);
}
+} // extern "C"
+} // namespace __sanitizer
#define INIT_PTHREAD_ATTR_GET \
COMMON_INTERCEPT_FUNCTION(pthread_attr_getdetachstate); \
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getaffinity_np, attr, cpusetsize,
cpuset);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(pthread_attr_getaffinity_np)(attr, cpusetsize, cpuset);
if (!res && cpusetsize && cpuset)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cpuset, cpusetsize);
#define INIT_PTHREAD_ATTR_GETAFFINITY_NP
#endif
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(pshared, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETPSHARED \
+ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getpshared);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETPSHARED
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(type, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETTYPE \
+ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_gettype);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETTYPE
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(protocol, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETPROTOCOL \
+ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getprotocol);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETPROTOCOL
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(prioceiling, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING \
+ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getprioceiling);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(robust, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETROBUST \
+ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getrobust);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETROBUST
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP
+INTERCEPTOR_PTHREAD_MUTEXATTR_GET(robust_np, sizeof(int))
+#define INIT_PTHREAD_MUTEXATTR_GETROBUST_NP \
+ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getrobust_np);
+#else
+#define INIT_PTHREAD_MUTEXATTR_GETROBUST_NP
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED
+INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(pshared, sizeof(int))
+#define INIT_PTHREAD_RWLOCKATTR_GETPSHARED \
+ COMMON_INTERCEPT_FUNCTION(pthread_rwlockattr_getpshared);
+#else
+#define INIT_PTHREAD_RWLOCKATTR_GETPSHARED
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP
+INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(kind_np, sizeof(int))
+#define INIT_PTHREAD_RWLOCKATTR_GETKIND_NP \
+ COMMON_INTERCEPT_FUNCTION(pthread_rwlockattr_getkind_np);
+#else
+#define INIT_PTHREAD_RWLOCKATTR_GETKIND_NP
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED
+INTERCEPTOR_PTHREAD_CONDATTR_GET(pshared, sizeof(int))
+#define INIT_PTHREAD_CONDATTR_GETPSHARED \
+ COMMON_INTERCEPT_FUNCTION(pthread_condattr_getpshared);
+#else
+#define INIT_PTHREAD_CONDATTR_GETPSHARED
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK
+INTERCEPTOR_PTHREAD_CONDATTR_GET(clock, sizeof(int))
+#define INIT_PTHREAD_CONDATTR_GETCLOCK \
+ COMMON_INTERCEPT_FUNCTION(pthread_condattr_getclock);
+#else
+#define INIT_PTHREAD_CONDATTR_GETCLOCK
+#endif
+
+#if SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED
+INTERCEPTOR_PTHREAD_BARRIERATTR_GET(pshared, sizeof(int)) // !mac !android
+#define INIT_PTHREAD_BARRIERATTR_GETPSHARED \
+ COMMON_INTERCEPT_FUNCTION(pthread_barrierattr_getpshared);
+#else
+#define INIT_PTHREAD_BARRIERATTR_GETPSHARED
+#endif
+
#if SANITIZER_INTERCEPT_TMPNAM
INTERCEPTOR(char *, tmpnam, char *s) {
void *ctx;
char *res = REAL(tmpnam)(s);
if (res) {
if (s)
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1);
else
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
}
return res;
}
INTERCEPTOR(char *, tmpnam_r, char *s) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, tmpnam_r, s);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(tmpnam_r)(s);
if (res && s) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1);
return res;
if (dir) COMMON_INTERCEPTOR_READ_RANGE(ctx, dir, REAL(strlen)(dir) + 1);
if (pfx) COMMON_INTERCEPTOR_READ_RANGE(ctx, pfx, REAL(strlen)(pfx) + 1);
char *res = REAL(tempnam)(dir, pfx);
- if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+ if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
return res;
}
#define INIT_TEMPNAM COMMON_INTERCEPT_FUNCTION(tempnam);
INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name);
return REAL(pthread_setname_np)(thread, name);
}
INTERCEPTOR(void, sincos, double x, double *sin, double *cos) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, sincos, x, sin, cos);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
REAL(sincos)(x, sin, cos);
if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin));
if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos));
INTERCEPTOR(void, sincosf, float x, float *sin, float *cos) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, sincosf, x, sin, cos);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
REAL(sincosf)(x, sin, cos);
if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin));
if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos));
INTERCEPTOR(void, sincosl, long double x, long double *sin, long double *cos) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, sincosl, x, sin, cos);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
REAL(sincosl)(x, sin, cos);
if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin));
if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos));
INTERCEPTOR(double, remquo, double x, double y, int *quo) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, remquo, x, y, quo);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
double res = REAL(remquo)(x, y, quo);
if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
return res;
INTERCEPTOR(float, remquof, float x, float y, int *quo) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, remquof, x, y, quo);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
float res = REAL(remquof)(x, y, quo);
if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
return res;
INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
long double res = REAL(remquol)(x, y, quo);
if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
return res;
INTERCEPTOR(double, lgamma_r, double x, int *signp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, lgamma_r, x, signp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
double res = REAL(lgamma_r)(x, signp);
if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp));
return res;
INTERCEPTOR(float, lgammaf_r, float x, int *signp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, lgammaf_r, x, signp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
float res = REAL(lgammaf_r)(x, signp);
if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp));
return res;
}
+#define INIT_LGAMMA_R \
+ COMMON_INTERCEPT_FUNCTION(lgamma_r); \
+ COMMON_INTERCEPT_FUNCTION(lgammaf_r);
+#else
+#define INIT_LGAMMA_R
+#endif
+
+#if SANITIZER_INTERCEPT_LGAMMAL_R
INTERCEPTOR(long double, lgammal_r, long double x, int *signp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, lgammal_r, x, signp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
long double res = REAL(lgammal_r)(x, signp);
if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp));
return res;
}
-#define INIT_LGAMMA_R \
- COMMON_INTERCEPT_FUNCTION(lgamma_r); \
- COMMON_INTERCEPT_FUNCTION(lgammaf_r); \
- COMMON_INTERCEPT_FUNCTION(lgammal_r);
+#define INIT_LGAMMAL_R COMMON_INTERCEPT_FUNCTION(lgammal_r);
#else
-#define INIT_LGAMMA_R
+#define INIT_LGAMMAL_R
#endif
#if SANITIZER_INTERCEPT_DRAND48_R
INTERCEPTOR(int, drand48_r, void *buffer, double *result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, drand48_r, buffer, result);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(drand48_r)(buffer, result);
if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
return res;
INTERCEPTOR(int, lrand48_r, void *buffer, long *result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, lrand48_r, buffer, result);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res = REAL(lrand48_r)(buffer, result);
if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
return res;
#define INIT_DRAND48_R
#endif
+#if SANITIZER_INTERCEPT_RAND_R
+INTERCEPTOR(int, rand_r, unsigned *seedp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, rand_r, seedp);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, seedp, sizeof(*seedp));
+ return REAL(rand_r)(seedp);
+}
+#define INIT_RAND_R COMMON_INTERCEPT_FUNCTION(rand_r);
+#else
+#define INIT_RAND_R
+#endif
+
#if SANITIZER_INTERCEPT_GETLINE
INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getline, lineptr, n, stream);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SSIZE_T res = REAL(getline)(lineptr, n, stream);
if (res > 0) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr));
}
return res;
}
-INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim,
- void *stream) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, getdelim, lineptr, n, delim, stream);
- SSIZE_T res = REAL(getdelim)(lineptr, n, delim, stream);
- if (res > 0) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr));
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n));
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1);
+
+// FIXME: under ASan the call below may write to freed memory and corrupt its
+// metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define GETDELIM_INTERCEPTOR_IMPL(vname) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, vname, lineptr, n, delim, stream); \
+ SSIZE_T res = REAL(vname)(lineptr, n, delim, stream); \
+ if (res > 0) { \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); \
+ } \
+ return res; \
}
- return res;
-}
-#define INIT_GETLINE \
- COMMON_INTERCEPT_FUNCTION(getline); \
+
+INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim,
+ void *stream)
+GETDELIM_INTERCEPTOR_IMPL(__getdelim)
+
+// There's no __getdelim() on FreeBSD so we supply the getdelim() interceptor
+// with its own body.
+INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim,
+ void *stream)
+GETDELIM_INTERCEPTOR_IMPL(getdelim)
+
+#define INIT_GETLINE \
+ COMMON_INTERCEPT_FUNCTION(getline); \
+ COMMON_INTERCEPT_FUNCTION(__getdelim); \
COMMON_INTERCEPT_FUNCTION(getdelim);
#else
#define INIT_GETLINE
COMMON_INTERCEPTOR_READ_RANGE(ctx, *inbuf, *inbytesleft);
if (outbytesleft)
COMMON_INTERCEPTOR_READ_RANGE(ctx, outbytesleft, sizeof(*outbytesleft));
- void *outbuf_orig = outbuf ? *outbuf : 0;
+ void *outbuf_orig = outbuf ? *outbuf : nullptr;
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SIZE_T res = REAL(iconv)(cd, inbuf, inbytesleft, outbuf, outbytesleft);
if (res != (SIZE_T) - 1 && outbuf && *outbuf > outbuf_orig) {
SIZE_T sz = (char *)*outbuf - (char *)outbuf_orig;
INTERCEPTOR(__sanitizer_clock_t, times, void *tms) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, times, tms);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
__sanitizer_clock_t res = REAL(times)(tms);
if (res != (__sanitizer_clock_t)-1 && tms)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tms, struct_tms_sz);
#define INIT_TIMES
#endif
-#define SANITIZER_COMMON_INTERCEPTORS_INIT \
- INIT_TEXTDOMAIN; \
- INIT_STRCMP; \
- INIT_STRNCMP; \
- INIT_STRCASECMP; \
- INIT_STRNCASECMP; \
- INIT_READ; \
- INIT_PREAD; \
- INIT_PREAD64; \
- INIT_READV; \
- INIT_PREADV; \
- INIT_PREADV64; \
- INIT_WRITE; \
- INIT_PWRITE; \
- INIT_PWRITE64; \
- INIT_WRITEV; \
- INIT_PWRITEV; \
- INIT_PWRITEV64; \
- INIT_PRCTL; \
- INIT_LOCALTIME_AND_FRIENDS; \
- INIT_STRPTIME; \
- INIT_SCANF; \
- INIT_ISOC99_SCANF; \
- INIT_FREXP; \
- INIT_FREXPF_FREXPL; \
- INIT_GETPWNAM_AND_FRIENDS; \
- INIT_GETPWNAM_R_AND_FRIENDS; \
- INIT_CLOCK_GETTIME; \
- INIT_GETITIMER; \
- INIT_TIME; \
- INIT_GLOB; \
- INIT_WAIT; \
- INIT_INET; \
- INIT_PTHREAD_GETSCHEDPARAM; \
- INIT_GETADDRINFO; \
- INIT_GETNAMEINFO; \
- INIT_GETSOCKNAME; \
- INIT_GETHOSTBYNAME; \
- INIT_GETHOSTBYNAME_R; \
- INIT_GETSOCKOPT; \
- INIT_ACCEPT; \
- INIT_ACCEPT4; \
- INIT_MODF; \
- INIT_RECVMSG; \
- INIT_GETPEERNAME; \
- INIT_IOCTL; \
- INIT_INET_ATON; \
- INIT_SYSINFO; \
- INIT_READDIR; \
- INIT_READDIR64; \
- INIT_PTRACE; \
- INIT_SETLOCALE; \
- INIT_GETCWD; \
- INIT_GET_CURRENT_DIR_NAME; \
- INIT_STRTOIMAX; \
- INIT_MBSTOWCS; \
- INIT_MBSNRTOWCS; \
- INIT_WCSTOMBS; \
- INIT_WCSNRTOMBS; \
- INIT_TCGETATTR; \
- INIT_REALPATH; \
- INIT_CANONICALIZE_FILE_NAME; \
- INIT_CONFSTR; \
- INIT_SCHED_GETAFFINITY; \
- INIT_STRERROR; \
- INIT_STRERROR_R; \
- INIT_XPG_STRERROR_R; \
- INIT_SCANDIR; \
- INIT_SCANDIR64; \
- INIT_GETGROUPS; \
- INIT_POLL; \
- INIT_PPOLL; \
- INIT_WORDEXP; \
- INIT_SIGWAIT; \
- INIT_SIGWAITINFO; \
- INIT_SIGTIMEDWAIT; \
- INIT_SIGSETOPS; \
- INIT_SIGPENDING; \
- INIT_SIGPROCMASK; \
- INIT_BACKTRACE; \
- INIT__EXIT; \
- INIT_PTHREAD_MUTEX_LOCK; \
- INIT_PTHREAD_MUTEX_UNLOCK; \
- INIT_PTHREAD_COND_WAIT; \
- INIT_PTHREAD_COND_INIT; \
- INIT_PTHREAD_COND_SIGNAL; \
- INIT_PTHREAD_COND_BROADCAST; \
- INIT_GETMNTENT; \
- INIT_GETMNTENT_R; \
- INIT_STATFS; \
- INIT_STATFS64; \
- INIT_STATVFS; \
- INIT_STATVFS64; \
- INIT_INITGROUPS; \
- INIT_ETHER; \
- INIT_ETHER_R; \
- INIT_SHMCTL; \
- INIT_RANDOM_R; \
- INIT_PTHREAD_ATTR_GET; \
- INIT_PTHREAD_ATTR_GETINHERITSCHED; \
- INIT_PTHREAD_ATTR_GETAFFINITY_NP; \
- INIT_TMPNAM; \
- INIT_TMPNAM_R; \
- INIT_TEMPNAM; \
- INIT_PTHREAD_SETNAME_NP; \
- INIT_SINCOS; \
- INIT_REMQUO; \
- INIT_LGAMMA; \
- INIT_LGAMMA_R; \
- INIT_DRAND48_R; \
- INIT_GETLINE; \
- INIT_ICONV; \
- INIT_TIMES; \
-/**/
+#if SANITIZER_INTERCEPT_TLS_GET_ADDR
+#define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_addr)
+// If you see any crashes around this functions, there are 2 known issues with
+// it: 1. __tls_get_addr can be called with mis-aligned stack due to:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066
+// 2. It can be called recursively if sanitizer code uses __tls_get_addr
+// to access thread local variables (it should not happen normally,
+// because sanitizers use initial-exec tls model).
+INTERCEPTOR(void *, __tls_get_addr, void *arg) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg);
+ void *res = REAL(__tls_get_addr)(arg);
+ uptr tls_begin, tls_end;
+ COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end);
+ DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end);
+ if (dtv) {
+ // New DTLS block has been allocated.
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size);
+ }
+ return res;
+}
+#else
+#define INIT_TLS_GET_ADDR
+#endif
+
+#if SANITIZER_INTERCEPT_LISTXATTR
+INTERCEPTOR(SSIZE_T, listxattr, const char *path, char *list, SIZE_T size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, listxattr, path, list, size);
+ if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ SSIZE_T res = REAL(listxattr)(path, list, size);
+ // Here and below, size == 0 is a special case where nothing is written to the
+ // buffer, and res contains the desired buffer size.
+ if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res);
+ return res;
+}
+INTERCEPTOR(SSIZE_T, llistxattr, const char *path, char *list, SIZE_T size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, llistxattr, path, list, size);
+ if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ SSIZE_T res = REAL(llistxattr)(path, list, size);
+ if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res);
+ return res;
+}
+INTERCEPTOR(SSIZE_T, flistxattr, int fd, char *list, SIZE_T size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, flistxattr, fd, list, size);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ SSIZE_T res = REAL(flistxattr)(fd, list, size);
+ if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res);
+ return res;
+}
+#define INIT_LISTXATTR \
+ COMMON_INTERCEPT_FUNCTION(listxattr); \
+ COMMON_INTERCEPT_FUNCTION(llistxattr); \
+ COMMON_INTERCEPT_FUNCTION(flistxattr);
+#else
+#define INIT_LISTXATTR
+#endif
+
+#if SANITIZER_INTERCEPT_GETXATTR
+INTERCEPTOR(SSIZE_T, getxattr, const char *path, const char *name, char *value,
+ SIZE_T size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getxattr, path, name, value, size);
+ if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ SSIZE_T res = REAL(getxattr)(path, name, value, size);
+ if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res);
+ return res;
+}
+INTERCEPTOR(SSIZE_T, lgetxattr, const char *path, const char *name, char *value,
+ SIZE_T size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, lgetxattr, path, name, value, size);
+ if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ SSIZE_T res = REAL(lgetxattr)(path, name, value, size);
+ if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res);
+ return res;
+}
+INTERCEPTOR(SSIZE_T, fgetxattr, int fd, const char *name, char *value,
+ SIZE_T size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fgetxattr, fd, name, value, size);
+ if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ SSIZE_T res = REAL(fgetxattr)(fd, name, value, size);
+ if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res);
+ return res;
+}
+#define INIT_GETXATTR \
+ COMMON_INTERCEPT_FUNCTION(getxattr); \
+ COMMON_INTERCEPT_FUNCTION(lgetxattr); \
+ COMMON_INTERCEPT_FUNCTION(fgetxattr);
+#else
+#define INIT_GETXATTR
+#endif
+
+#if SANITIZER_INTERCEPT_GETRESID
+INTERCEPTOR(int, getresuid, void *ruid, void *euid, void *suid) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getresuid, ruid, euid, suid);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(getresuid)(ruid, euid, suid);
+ if (res >= 0) {
+ if (ruid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ruid, uid_t_sz);
+ if (euid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, euid, uid_t_sz);
+ if (suid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, suid, uid_t_sz);
+ }
+ return res;
+}
+INTERCEPTOR(int, getresgid, void *rgid, void *egid, void *sgid) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getresgid, rgid, egid, sgid);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(getresgid)(rgid, egid, sgid);
+ if (res >= 0) {
+ if (rgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rgid, gid_t_sz);
+ if (egid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, egid, gid_t_sz);
+ if (sgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sgid, gid_t_sz);
+ }
+ return res;
+}
+#define INIT_GETRESID \
+ COMMON_INTERCEPT_FUNCTION(getresuid); \
+ COMMON_INTERCEPT_FUNCTION(getresgid);
+#else
+#define INIT_GETRESID
+#endif
+
+#if SANITIZER_INTERCEPT_GETIFADDRS
+// As long as getifaddrs()/freeifaddrs() use calloc()/free(), we don't need to
+// intercept freeifaddrs(). If that ceases to be the case, we might need to
+// intercept it to poison the memory again.
+INTERCEPTOR(int, getifaddrs, __sanitizer_ifaddrs **ifap) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getifaddrs, ifap);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(getifaddrs)(ifap);
+ if (res == 0 && ifap) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifap, sizeof(void *));
+ __sanitizer_ifaddrs *p = *ifap;
+ while (p) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(__sanitizer_ifaddrs));
+ if (p->ifa_name)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_name,
+ REAL(strlen)(p->ifa_name) + 1);
+ if (p->ifa_addr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_addr, struct_sockaddr_sz);
+ if (p->ifa_netmask)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_netmask, struct_sockaddr_sz);
+ // On Linux this is a union, but the other member also points to a
+ // struct sockaddr, so the following is sufficient.
+ if (p->ifa_dstaddr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_dstaddr, struct_sockaddr_sz);
+ // FIXME(smatveev): Unpoison p->ifa_data as well.
+ p = p->ifa_next;
+ }
+ }
+ return res;
+}
+#define INIT_GETIFADDRS \
+ COMMON_INTERCEPT_FUNCTION(getifaddrs);
+#else
+#define INIT_GETIFADDRS
+#endif
+
+#if SANITIZER_INTERCEPT_IF_INDEXTONAME
+INTERCEPTOR(char *, if_indextoname, unsigned int ifindex, char* ifname) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, if_indextoname, ifindex, ifname);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ char *res = REAL(if_indextoname)(ifindex, ifname);
+ if (res && ifname)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1);
+ return res;
+}
+INTERCEPTOR(unsigned int, if_nametoindex, const char* ifname) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, if_nametoindex, ifname);
+ if (ifname)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1);
+ return REAL(if_nametoindex)(ifname);
+}
+#define INIT_IF_INDEXTONAME \
+ COMMON_INTERCEPT_FUNCTION(if_indextoname); \
+ COMMON_INTERCEPT_FUNCTION(if_nametoindex);
+#else
+#define INIT_IF_INDEXTONAME
+#endif
+
+#if SANITIZER_INTERCEPT_CAPGET
+INTERCEPTOR(int, capget, void *hdrp, void *datap) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, capget, hdrp, datap);
+ if (hdrp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(capget)(hdrp, datap);
+ if (res == 0 && datap)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datap, __user_cap_data_struct_sz);
+ // We can also return -1 and write to hdrp->version if the version passed in
+ // hdrp->version is unsupported. But that's not a trivial condition to check,
+ // and anyway COMMON_INTERCEPTOR_READ_RANGE protects us to some extent.
+ return res;
+}
+INTERCEPTOR(int, capset, void *hdrp, const void *datap) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, capset, hdrp, datap);
+ if (hdrp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz);
+ if (datap)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, datap, __user_cap_data_struct_sz);
+ return REAL(capset)(hdrp, datap);
+}
+#define INIT_CAPGET \
+ COMMON_INTERCEPT_FUNCTION(capget); \
+ COMMON_INTERCEPT_FUNCTION(capset);
+#else
+#define INIT_CAPGET
+#endif
+
+#if SANITIZER_INTERCEPT_AEABI_MEM
+DECLARE_REAL_AND_INTERCEPTOR(void *, memmove, void *, const void *, uptr)
+DECLARE_REAL_AND_INTERCEPTOR(void *, memcpy, void *, const void *, uptr)
+DECLARE_REAL_AND_INTERCEPTOR(void *, memset, void *, int, uptr)
+
+INTERCEPTOR(void *, __aeabi_memmove, void *to, const void *from, uptr size) {
+ return WRAP(memmove)(to, from, size);
+}
+INTERCEPTOR(void *, __aeabi_memmove4, void *to, const void *from, uptr size) {
+ return WRAP(memmove)(to, from, size);
+}
+INTERCEPTOR(void *, __aeabi_memmove8, void *to, const void *from, uptr size) {
+ return WRAP(memmove)(to, from, size);
+}
+INTERCEPTOR(void *, __aeabi_memcpy, void *to, const void *from, uptr size) {
+ return WRAP(memcpy)(to, from, size);
+}
+INTERCEPTOR(void *, __aeabi_memcpy4, void *to, const void *from, uptr size) {
+ return WRAP(memcpy)(to, from, size);
+}
+INTERCEPTOR(void *, __aeabi_memcpy8, void *to, const void *from, uptr size) {
+ return WRAP(memcpy)(to, from, size);
+}
+// Note the argument order.
+INTERCEPTOR(void *, __aeabi_memset, void *block, uptr size, int c) {
+ return WRAP(memset)(block, c, size);
+}
+INTERCEPTOR(void *, __aeabi_memset4, void *block, uptr size, int c) {
+ return WRAP(memset)(block, c, size);
+}
+INTERCEPTOR(void *, __aeabi_memset8, void *block, uptr size, int c) {
+ return WRAP(memset)(block, c, size);
+}
+INTERCEPTOR(void *, __aeabi_memclr, void *block, uptr size) {
+ return WRAP(memset)(block, 0, size);
+}
+INTERCEPTOR(void *, __aeabi_memclr4, void *block, uptr size) {
+ return WRAP(memset)(block, 0, size);
+}
+INTERCEPTOR(void *, __aeabi_memclr8, void *block, uptr size) {
+ return WRAP(memset)(block, 0, size);
+}
+#define INIT_AEABI_MEM \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove4); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove8); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy4); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy8); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memset); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memset4); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memset8); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr4); \
+ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr8);
+#else
+#define INIT_AEABI_MEM
+#endif // SANITIZER_INTERCEPT_AEABI_MEM
+
+#if SANITIZER_INTERCEPT___BZERO
+DECLARE_REAL_AND_INTERCEPTOR(void *, memset, void *, int, uptr);
+
+INTERCEPTOR(void *, __bzero, void *block, uptr size) {
+ return WRAP(memset)(block, 0, size);
+}
+#define INIT___BZERO COMMON_INTERCEPT_FUNCTION(__bzero);
+#else
+#define INIT___BZERO
+#endif // SANITIZER_INTERCEPT___BZERO
+
+#if SANITIZER_INTERCEPT_FTIME
+INTERCEPTOR(int, ftime, __sanitizer_timeb *tp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, ftime, tp);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(ftime)(tp);
+ if (tp)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, sizeof(*tp));
+ return res;
+}
+#define INIT_FTIME COMMON_INTERCEPT_FUNCTION(ftime);
+#else
+#define INIT_FTIME
+#endif // SANITIZER_INTERCEPT_FTIME
+
+#if SANITIZER_INTERCEPT_XDR
+INTERCEPTOR(void, xdrmem_create, __sanitizer_XDR *xdrs, uptr addr,
+ unsigned size, int op) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, xdrmem_create, xdrs, addr, size, op);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ REAL(xdrmem_create)(xdrs, addr, size, op);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs));
+ if (op == __sanitizer_XDR_ENCODE) {
+ // It's not obvious how much data individual xdr_ routines write.
+ // Simply unpoison the entire target buffer in advance.
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (void *)addr, size);
+ }
+}
+
+INTERCEPTOR(void, xdrstdio_create, __sanitizer_XDR *xdrs, void *file, int op) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, xdrstdio_create, xdrs, file, op);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ REAL(xdrstdio_create)(xdrs, file, op);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs));
+}
+
+// FIXME: under ASan the call below may write to freed memory and corrupt
+// its metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define XDR_INTERCEPTOR(F, T) \
+ INTERCEPTOR(int, F, __sanitizer_XDR *xdrs, T *p) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, F, xdrs, p); \
+ if (p && xdrs->x_op == __sanitizer_XDR_ENCODE) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p)); \
+ int res = REAL(F)(xdrs, p); \
+ if (res && p && xdrs->x_op == __sanitizer_XDR_DECODE) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); \
+ return res; \
+ }
+
+XDR_INTERCEPTOR(xdr_short, short)
+XDR_INTERCEPTOR(xdr_u_short, unsigned short)
+XDR_INTERCEPTOR(xdr_int, int)
+XDR_INTERCEPTOR(xdr_u_int, unsigned)
+XDR_INTERCEPTOR(xdr_long, long)
+XDR_INTERCEPTOR(xdr_u_long, unsigned long)
+XDR_INTERCEPTOR(xdr_hyper, long long)
+XDR_INTERCEPTOR(xdr_u_hyper, unsigned long long)
+XDR_INTERCEPTOR(xdr_longlong_t, long long)
+XDR_INTERCEPTOR(xdr_u_longlong_t, unsigned long long)
+XDR_INTERCEPTOR(xdr_int8_t, u8)
+XDR_INTERCEPTOR(xdr_uint8_t, u8)
+XDR_INTERCEPTOR(xdr_int16_t, u16)
+XDR_INTERCEPTOR(xdr_uint16_t, u16)
+XDR_INTERCEPTOR(xdr_int32_t, u32)
+XDR_INTERCEPTOR(xdr_uint32_t, u32)
+XDR_INTERCEPTOR(xdr_int64_t, u64)
+XDR_INTERCEPTOR(xdr_uint64_t, u64)
+XDR_INTERCEPTOR(xdr_quad_t, long long)
+XDR_INTERCEPTOR(xdr_u_quad_t, unsigned long long)
+XDR_INTERCEPTOR(xdr_bool, bool)
+XDR_INTERCEPTOR(xdr_enum, int)
+XDR_INTERCEPTOR(xdr_char, char)
+XDR_INTERCEPTOR(xdr_u_char, unsigned char)
+XDR_INTERCEPTOR(xdr_float, float)
+XDR_INTERCEPTOR(xdr_double, double)
+
+// FIXME: intercept xdr_array, opaque, union, vector, reference, pointer,
+// wrapstring, sizeof
+
+INTERCEPTOR(int, xdr_bytes, __sanitizer_XDR *xdrs, char **p, unsigned *sizep,
+ unsigned maxsize) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, xdr_bytes, xdrs, p, sizep, maxsize);
+ if (p && sizep && xdrs->x_op == __sanitizer_XDR_ENCODE) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sizep, sizeof(*sizep));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, *p, *sizep);
+ }
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(xdr_bytes)(xdrs, p, sizep, maxsize);
+ if (p && sizep && xdrs->x_op == __sanitizer_XDR_DECODE) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizep, sizeof(*sizep));
+ if (res && *p && *sizep) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, *sizep);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, xdr_string, __sanitizer_XDR *xdrs, char **p,
+ unsigned maxsize) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, xdr_string, xdrs, p, maxsize);
+ if (p && xdrs->x_op == __sanitizer_XDR_ENCODE) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, *p, REAL(strlen)(*p) + 1);
+ }
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ int res = REAL(xdr_string)(xdrs, p, maxsize);
+ if (p && xdrs->x_op == __sanitizer_XDR_DECODE) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p));
+ if (res && *p)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1);
+ }
+ return res;
+}
+
+#define INIT_XDR \
+ COMMON_INTERCEPT_FUNCTION(xdrmem_create); \
+ COMMON_INTERCEPT_FUNCTION(xdrstdio_create); \
+ COMMON_INTERCEPT_FUNCTION(xdr_short); \
+ COMMON_INTERCEPT_FUNCTION(xdr_u_short); \
+ COMMON_INTERCEPT_FUNCTION(xdr_int); \
+ COMMON_INTERCEPT_FUNCTION(xdr_u_int); \
+ COMMON_INTERCEPT_FUNCTION(xdr_long); \
+ COMMON_INTERCEPT_FUNCTION(xdr_u_long); \
+ COMMON_INTERCEPT_FUNCTION(xdr_hyper); \
+ COMMON_INTERCEPT_FUNCTION(xdr_u_hyper); \
+ COMMON_INTERCEPT_FUNCTION(xdr_longlong_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_u_longlong_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_int8_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_uint8_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_int16_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_uint16_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_int32_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_uint32_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_int64_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_uint64_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_quad_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_u_quad_t); \
+ COMMON_INTERCEPT_FUNCTION(xdr_bool); \
+ COMMON_INTERCEPT_FUNCTION(xdr_enum); \
+ COMMON_INTERCEPT_FUNCTION(xdr_char); \
+ COMMON_INTERCEPT_FUNCTION(xdr_u_char); \
+ COMMON_INTERCEPT_FUNCTION(xdr_float); \
+ COMMON_INTERCEPT_FUNCTION(xdr_double); \
+ COMMON_INTERCEPT_FUNCTION(xdr_bytes); \
+ COMMON_INTERCEPT_FUNCTION(xdr_string);
+#else
+#define INIT_XDR
+#endif // SANITIZER_INTERCEPT_XDR
+
+#if SANITIZER_INTERCEPT_TSEARCH
+INTERCEPTOR(void *, tsearch, void *key, void **rootp,
+ int (*compar)(const void *, const void *)) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, tsearch, key, rootp, compar);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ void *res = REAL(tsearch)(key, rootp, compar);
+ if (res && *(void **)res == key)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(void *));
+ return res;
+}
+#define INIT_TSEARCH COMMON_INTERCEPT_FUNCTION(tsearch);
+#else
+#define INIT_TSEARCH
+#endif
+
+#if SANITIZER_INTERCEPT_LIBIO_INTERNALS || SANITIZER_INTERCEPT_FOPEN || \
+ SANITIZER_INTERCEPT_OPEN_MEMSTREAM
+void unpoison_file(__sanitizer_FILE *fp) {
+#if SANITIZER_HAS_STRUCT_FILE
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp, sizeof(*fp));
+ if (fp->_IO_read_base && fp->_IO_read_base < fp->_IO_read_end)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_IO_read_base,
+ fp->_IO_read_end - fp->_IO_read_base);
+#endif // SANITIZER_HAS_STRUCT_FILE
+}
+#endif
+
+#if SANITIZER_INTERCEPT_LIBIO_INTERNALS
+// These guys are called when a .c source is built with -O2.
+INTERCEPTOR(int, __uflow, __sanitizer_FILE *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, __uflow, fp);
+ int res = REAL(__uflow)(fp);
+ unpoison_file(fp);
+ return res;
+}
+INTERCEPTOR(int, __underflow, __sanitizer_FILE *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, __underflow, fp);
+ int res = REAL(__underflow)(fp);
+ unpoison_file(fp);
+ return res;
+}
+INTERCEPTOR(int, __overflow, __sanitizer_FILE *fp, int ch) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, __overflow, fp, ch);
+ int res = REAL(__overflow)(fp, ch);
+ unpoison_file(fp);
+ return res;
+}
+INTERCEPTOR(int, __wuflow, __sanitizer_FILE *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, __wuflow, fp);
+ int res = REAL(__wuflow)(fp);
+ unpoison_file(fp);
+ return res;
+}
+INTERCEPTOR(int, __wunderflow, __sanitizer_FILE *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, __wunderflow, fp);
+ int res = REAL(__wunderflow)(fp);
+ unpoison_file(fp);
+ return res;
+}
+INTERCEPTOR(int, __woverflow, __sanitizer_FILE *fp, int ch) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, __woverflow, fp, ch);
+ int res = REAL(__woverflow)(fp, ch);
+ unpoison_file(fp);
+ return res;
+}
+#define INIT_LIBIO_INTERNALS \
+ COMMON_INTERCEPT_FUNCTION(__uflow); \
+ COMMON_INTERCEPT_FUNCTION(__underflow); \
+ COMMON_INTERCEPT_FUNCTION(__overflow); \
+ COMMON_INTERCEPT_FUNCTION(__wuflow); \
+ COMMON_INTERCEPT_FUNCTION(__wunderflow); \
+ COMMON_INTERCEPT_FUNCTION(__woverflow);
+#else
+#define INIT_LIBIO_INTERNALS
+#endif
+
+#if SANITIZER_INTERCEPT_FOPEN
+INTERCEPTOR(__sanitizer_FILE *, fopen, const char *path, const char *mode) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fopen, path, mode);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
+ __sanitizer_FILE *res = REAL(fopen)(path, mode);
+ COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path);
+ if (res) unpoison_file(res);
+ return res;
+}
+INTERCEPTOR(__sanitizer_FILE *, fdopen, int fd, const char *mode) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fdopen, fd, mode);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
+ __sanitizer_FILE *res = REAL(fdopen)(fd, mode);
+ if (res) unpoison_file(res);
+ return res;
+}
+INTERCEPTOR(__sanitizer_FILE *, freopen, const char *path, const char *mode,
+ __sanitizer_FILE *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, freopen, path, mode, fp);
+ if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
+ COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
+ __sanitizer_FILE *res = REAL(freopen)(path, mode, fp);
+ COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path);
+ if (res) unpoison_file(res);
+ return res;
+}
+#define INIT_FOPEN \
+ COMMON_INTERCEPT_FUNCTION(fopen); \
+ COMMON_INTERCEPT_FUNCTION(fdopen); \
+ COMMON_INTERCEPT_FUNCTION(freopen);
+#else
+#define INIT_FOPEN
+#endif
+
+#if SANITIZER_INTERCEPT_FOPEN64
+INTERCEPTOR(__sanitizer_FILE *, fopen64, const char *path, const char *mode) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fopen64, path, mode);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
+ __sanitizer_FILE *res = REAL(fopen64)(path, mode);
+ COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path);
+ if (res) unpoison_file(res);
+ return res;
+}
+INTERCEPTOR(__sanitizer_FILE *, freopen64, const char *path, const char *mode,
+ __sanitizer_FILE *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, freopen64, path, mode, fp);
+ if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
+ COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
+ __sanitizer_FILE *res = REAL(freopen64)(path, mode, fp);
+ COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path);
+ if (res) unpoison_file(res);
+ return res;
+}
+#define INIT_FOPEN64 \
+ COMMON_INTERCEPT_FUNCTION(fopen64); \
+ COMMON_INTERCEPT_FUNCTION(freopen64);
+#else
+#define INIT_FOPEN64
+#endif
+
+#if SANITIZER_INTERCEPT_OPEN_MEMSTREAM
+INTERCEPTOR(__sanitizer_FILE *, open_memstream, char **ptr, SIZE_T *sizeloc) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, open_memstream, ptr, sizeloc);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ __sanitizer_FILE *res = REAL(open_memstream)(ptr, sizeloc);
+ if (res) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizeloc, sizeof(*sizeloc));
+ unpoison_file(res);
+ FileMetadata file = {ptr, sizeloc};
+ SetInterceptorMetadata(res, file);
+ }
+ return res;
+}
+INTERCEPTOR(__sanitizer_FILE *, open_wmemstream, wchar_t **ptr,
+ SIZE_T *sizeloc) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, open_wmemstream, ptr, sizeloc);
+ __sanitizer_FILE *res = REAL(open_wmemstream)(ptr, sizeloc);
+ if (res) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizeloc, sizeof(*sizeloc));
+ unpoison_file(res);
+ FileMetadata file = {(char **)ptr, sizeloc};
+ SetInterceptorMetadata(res, file);
+ }
+ return res;
+}
+INTERCEPTOR(__sanitizer_FILE *, fmemopen, void *buf, SIZE_T size,
+ const char *mode) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fmemopen, buf, size, mode);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ __sanitizer_FILE *res = REAL(fmemopen)(buf, size, mode);
+ if (res) unpoison_file(res);
+ return res;
+}
+#define INIT_OPEN_MEMSTREAM \
+ COMMON_INTERCEPT_FUNCTION(open_memstream); \
+ COMMON_INTERCEPT_FUNCTION(open_wmemstream); \
+ COMMON_INTERCEPT_FUNCTION(fmemopen);
+#else
+#define INIT_OPEN_MEMSTREAM
+#endif
+
+#if SANITIZER_INTERCEPT_OBSTACK
+static void initialize_obstack(__sanitizer_obstack *obstack) {
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(obstack, sizeof(*obstack));
+ if (obstack->chunk)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(obstack->chunk,
+ sizeof(*obstack->chunk));
+}
+
+INTERCEPTOR(int, _obstack_begin_1, __sanitizer_obstack *obstack, int sz,
+ int align, void *(*alloc_fn)(uptr arg, uptr sz),
+ void (*free_fn)(uptr arg, void *p)) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin_1, obstack, sz, align, alloc_fn,
+ free_fn);
+ int res = REAL(_obstack_begin_1)(obstack, sz, align, alloc_fn, free_fn);
+ if (res) initialize_obstack(obstack);
+ return res;
+}
+INTERCEPTOR(int, _obstack_begin, __sanitizer_obstack *obstack, int sz,
+ int align, void *(*alloc_fn)(uptr sz), void (*free_fn)(void *p)) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin, obstack, sz, align, alloc_fn,
+ free_fn);
+ int res = REAL(_obstack_begin)(obstack, sz, align, alloc_fn, free_fn);
+ if (res) initialize_obstack(obstack);
+ return res;
+}
+INTERCEPTOR(void, _obstack_newchunk, __sanitizer_obstack *obstack, int length) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, _obstack_newchunk, obstack, length);
+ REAL(_obstack_newchunk)(obstack, length);
+ if (obstack->chunk)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(
+ obstack->chunk, obstack->next_free - (char *)obstack->chunk);
+}
+#define INIT_OBSTACK \
+ COMMON_INTERCEPT_FUNCTION(_obstack_begin_1); \
+ COMMON_INTERCEPT_FUNCTION(_obstack_begin); \
+ COMMON_INTERCEPT_FUNCTION(_obstack_newchunk);
+#else
+#define INIT_OBSTACK
+#endif
+
+#if SANITIZER_INTERCEPT_FFLUSH
+INTERCEPTOR(int, fflush, __sanitizer_FILE *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fflush, fp);
+ int res = REAL(fflush)(fp);
+ // FIXME: handle fp == NULL
+ if (fp) {
+ const FileMetadata *m = GetInterceptorMetadata(fp);
+ if (m) COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size);
+ }
+ return res;
+}
+#define INIT_FFLUSH COMMON_INTERCEPT_FUNCTION(fflush);
+#else
+#define INIT_FFLUSH
+#endif
+
+#if SANITIZER_INTERCEPT_FCLOSE
+INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fclose, fp);
+ COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
+ const FileMetadata *m = GetInterceptorMetadata(fp);
+ int res = REAL(fclose)(fp);
+ if (m) {
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size);
+ DeleteInterceptorMetadata(fp);
+ }
+ return res;
+}
+#define INIT_FCLOSE COMMON_INTERCEPT_FUNCTION(fclose);
+#else
+#define INIT_FCLOSE
+#endif
+
+#if SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
+INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
+ if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
+ COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag);
+ void *res = REAL(dlopen)(filename, flag);
+ COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res);
+ return res;
+}
+
+INTERCEPTOR(int, dlclose, void *handle) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlclose, handle);
+ int res = REAL(dlclose)(handle);
+ COMMON_INTERCEPTOR_LIBRARY_UNLOADED();
+ return res;
+}
+#define INIT_DLOPEN_DLCLOSE \
+ COMMON_INTERCEPT_FUNCTION(dlopen); \
+ COMMON_INTERCEPT_FUNCTION(dlclose);
+#else
+#define INIT_DLOPEN_DLCLOSE
+#endif
+
+#if SANITIZER_INTERCEPT_GETPASS
+INTERCEPTOR(char *, getpass, const char *prompt) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getpass, prompt);
+ if (prompt)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, prompt, REAL(strlen)(prompt)+1);
+ char *res = REAL(getpass)(prompt);
+ if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res)+1);
+ return res;
+}
+
+#define INIT_GETPASS COMMON_INTERCEPT_FUNCTION(getpass);
+#else
+#define INIT_GETPASS
+#endif
+
+#if SANITIZER_INTERCEPT_TIMERFD
+INTERCEPTOR(int, timerfd_settime, int fd, int flags, void *new_value,
+ void *old_value) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, timerfd_settime, fd, flags, new_value,
+ old_value);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerspec_sz);
+ int res = REAL(timerfd_settime)(fd, flags, new_value, old_value);
+ if (res != -1 && old_value)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerspec_sz);
+ return res;
+}
+
+INTERCEPTOR(int, timerfd_gettime, int fd, void *curr_value) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, timerfd_gettime, fd, curr_value);
+ int res = REAL(timerfd_gettime)(fd, curr_value);
+ if (res != -1 && curr_value)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerspec_sz);
+ return res;
+}
+#define INIT_TIMERFD \
+ COMMON_INTERCEPT_FUNCTION(timerfd_settime); \
+ COMMON_INTERCEPT_FUNCTION(timerfd_gettime);
+#else
+#define INIT_TIMERFD
+#endif
+
+#if SANITIZER_INTERCEPT_MLOCKX
+// Linux kernel has a bug that leads to kernel deadlock if a process
+// maps TBs of memory and then calls mlock().
+static void MlockIsUnsupported() {
+ static atomic_uint8_t printed;
+ if (atomic_exchange(&printed, 1, memory_order_relaxed))
+ return;
+ VPrintf(1, "%s ignores mlock/mlockall/munlock/munlockall\n",
+ SanitizerToolName);
+}
+
+INTERCEPTOR(int, mlock, const void *addr, uptr len) {
+ MlockIsUnsupported();
+ return 0;
+}
+
+INTERCEPTOR(int, munlock, const void *addr, uptr len) {
+ MlockIsUnsupported();
+ return 0;
+}
+
+INTERCEPTOR(int, mlockall, int flags) {
+ MlockIsUnsupported();
+ return 0;
+}
+
+INTERCEPTOR(int, munlockall, void) {
+ MlockIsUnsupported();
+ return 0;
+}
+
+#define INIT_MLOCKX \
+ COMMON_INTERCEPT_FUNCTION(mlock); \
+ COMMON_INTERCEPT_FUNCTION(munlock); \
+ COMMON_INTERCEPT_FUNCTION(mlockall); \
+ COMMON_INTERCEPT_FUNCTION(munlockall);
+
+#else
+#define INIT_MLOCKX
+#endif // SANITIZER_INTERCEPT_MLOCKX
+
+#if SANITIZER_INTERCEPT_FOPENCOOKIE
+struct WrappedCookie {
+ void *real_cookie;
+ __sanitizer_cookie_io_functions_t real_io_funcs;
+};
+
+static uptr wrapped_read(void *cookie, char *buf, uptr size) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_read real_read = wrapped_cookie->real_io_funcs.read;
+ return real_read ? real_read(wrapped_cookie->real_cookie, buf, size) : 0;
+}
+
+static uptr wrapped_write(void *cookie, const char *buf, uptr size) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_write real_write = wrapped_cookie->real_io_funcs.write;
+ return real_write ? real_write(wrapped_cookie->real_cookie, buf, size) : size;
+}
+
+static int wrapped_seek(void *cookie, u64 *offset, int whence) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(offset, sizeof(*offset));
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_seek real_seek = wrapped_cookie->real_io_funcs.seek;
+ return real_seek ? real_seek(wrapped_cookie->real_cookie, offset, whence)
+ : -1;
+}
+
+static int wrapped_close(void *cookie) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_close real_close = wrapped_cookie->real_io_funcs.close;
+ int res = real_close ? real_close(wrapped_cookie->real_cookie) : 0;
+ InternalFree(wrapped_cookie);
+ return res;
+}
+
+INTERCEPTOR(__sanitizer_FILE *, fopencookie, void *cookie, const char *mode,
+ __sanitizer_cookie_io_functions_t io_funcs) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fopencookie, cookie, mode, io_funcs);
+ WrappedCookie *wrapped_cookie =
+ (WrappedCookie *)InternalAlloc(sizeof(WrappedCookie));
+ wrapped_cookie->real_cookie = cookie;
+ wrapped_cookie->real_io_funcs = io_funcs;
+ __sanitizer_FILE *res =
+ REAL(fopencookie)(wrapped_cookie, mode, {wrapped_read, wrapped_write,
+ wrapped_seek, wrapped_close});
+ return res;
+}
+
+#define INIT_FOPENCOOKIE COMMON_INTERCEPT_FUNCTION(fopencookie);
+#else
+#define INIT_FOPENCOOKIE
+#endif // SANITIZER_INTERCEPT_FOPENCOOKIE
+
+#if SANITIZER_INTERCEPT_SEM
+INTERCEPTOR(int, sem_init, __sanitizer_sem_t *s, int pshared, unsigned value) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_init, s, pshared, value);
+ // Workaround a bug in glibc's "old" semaphore implementation by
+ // zero-initializing the sem_t contents. This has to be done here because
+ // interceptors bind to the lowest symbols version by default, hitting the
+ // buggy code path while the non-sanitized build of the same code works fine.
+ REAL(memset)(s, 0, sizeof(*s));
+ int res = REAL(sem_init)(s, pshared, value);
+ return res;
+}
+
+INTERCEPTOR(int, sem_destroy, __sanitizer_sem_t *s) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_destroy, s);
+ int res = REAL(sem_destroy)(s);
+ return res;
+}
+
+INTERCEPTOR(int, sem_wait, __sanitizer_sem_t *s) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_wait, s);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_wait)(s);
+ if (res == 0) {
+ COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sem_trywait, __sanitizer_sem_t *s) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_trywait, s);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_trywait)(s);
+ if (res == 0) {
+ COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sem_timedwait, __sanitizer_sem_t *s, void *abstime) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_timedwait, s, abstime);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, abstime, struct_timespec_sz);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_timedwait)(s, abstime);
+ if (res == 0) {
+ COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sem_post, __sanitizer_sem_t *s) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_post, s);
+ COMMON_INTERCEPTOR_RELEASE(ctx, (uptr)s);
+ int res = REAL(sem_post)(s);
+ return res;
+}
+
+INTERCEPTOR(int, sem_getvalue, __sanitizer_sem_t *s, int *sval) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_getvalue, s, sval);
+ int res = REAL(sem_getvalue)(s, sval);
+ if (res == 0) {
+ COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sval, sizeof(*sval));
+ }
+ return res;
+}
+#define INIT_SEM \
+ COMMON_INTERCEPT_FUNCTION(sem_init); \
+ COMMON_INTERCEPT_FUNCTION(sem_destroy); \
+ COMMON_INTERCEPT_FUNCTION(sem_wait); \
+ COMMON_INTERCEPT_FUNCTION(sem_trywait); \
+ COMMON_INTERCEPT_FUNCTION(sem_timedwait); \
+ COMMON_INTERCEPT_FUNCTION(sem_post); \
+ COMMON_INTERCEPT_FUNCTION(sem_getvalue);
+#else
+#define INIT_SEM
+#endif // SANITIZER_INTERCEPT_SEM
+
+#if SANITIZER_INTERCEPT_PTHREAD_SETCANCEL
+INTERCEPTOR(int, pthread_setcancelstate, int state, int *oldstate) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcancelstate, state, oldstate);
+ int res = REAL(pthread_setcancelstate)(state, oldstate);
+ if (res == 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldstate, sizeof(*oldstate));
+ return res;
+}
+
+INTERCEPTOR(int, pthread_setcanceltype, int type, int *oldtype) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcanceltype, type, oldtype);
+ int res = REAL(pthread_setcanceltype)(type, oldtype);
+ if (res == 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldtype, sizeof(*oldtype));
+ return res;
+}
+#define INIT_PTHREAD_SETCANCEL \
+ COMMON_INTERCEPT_FUNCTION(pthread_setcancelstate); \
+ COMMON_INTERCEPT_FUNCTION(pthread_setcanceltype);
+#else
+#define INIT_PTHREAD_SETCANCEL
+#endif
+
+#if SANITIZER_INTERCEPT_MINCORE
+INTERCEPTOR(int, mincore, void *addr, uptr length, unsigned char *vec) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, mincore, addr, length, vec);
+ int res = REAL(mincore)(addr, length, vec);
+ if (res == 0) {
+ uptr page_size = GetPageSizeCached();
+ uptr vec_size = ((length + page_size - 1) & (~(page_size - 1))) / page_size;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, vec, vec_size);
+ }
+ return res;
+}
+#define INIT_MINCORE COMMON_INTERCEPT_FUNCTION(mincore);
+#else
+#define INIT_MINCORE
+#endif
+
+#if SANITIZER_INTERCEPT_PROCESS_VM_READV
+INTERCEPTOR(SSIZE_T, process_vm_readv, int pid, __sanitizer_iovec *local_iov,
+ uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt,
+ uptr flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, process_vm_readv, pid, local_iov, liovcnt,
+ remote_iov, riovcnt, flags);
+ SSIZE_T res = REAL(process_vm_readv)(pid, local_iov, liovcnt, remote_iov,
+ riovcnt, flags);
+ if (res > 0)
+ write_iovec(ctx, local_iov, liovcnt, res);
+ return res;
+}
+
+INTERCEPTOR(SSIZE_T, process_vm_writev, int pid, __sanitizer_iovec *local_iov,
+ uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt,
+ uptr flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, process_vm_writev, pid, local_iov, liovcnt,
+ remote_iov, riovcnt, flags);
+ SSIZE_T res = REAL(process_vm_writev)(pid, local_iov, liovcnt, remote_iov,
+ riovcnt, flags);
+ if (res > 0)
+ read_iovec(ctx, local_iov, liovcnt, res);
+ return res;
+}
+#define INIT_PROCESS_VM_READV \
+ COMMON_INTERCEPT_FUNCTION(process_vm_readv); \
+ COMMON_INTERCEPT_FUNCTION(process_vm_writev);
+#else
+#define INIT_PROCESS_VM_READV
+#endif
+
+static void InitializeCommonInterceptors() {
+ static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
+ interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap();
+
+ INIT_TEXTDOMAIN;
+ INIT_STRCMP;
+ INIT_STRNCMP;
+ INIT_STRCASECMP;
+ INIT_STRNCASECMP;
+ INIT_STRSTR;
+ INIT_STRCASESTR;
+ INIT_STRSPN;
+ INIT_STRPBRK;
+ INIT_MEMCHR;
+ INIT_MEMCMP;
+ INIT_MEMRCHR;
+ INIT_READ;
+ INIT_PREAD;
+ INIT_PREAD64;
+ INIT_READV;
+ INIT_PREADV;
+ INIT_PREADV64;
+ INIT_WRITE;
+ INIT_PWRITE;
+ INIT_PWRITE64;
+ INIT_WRITEV;
+ INIT_PWRITEV;
+ INIT_PWRITEV64;
+ INIT_PRCTL;
+ INIT_LOCALTIME_AND_FRIENDS;
+ INIT_STRPTIME;
+ INIT_SCANF;
+ INIT_ISOC99_SCANF;
+ INIT_PRINTF;
+ INIT_PRINTF_L;
+ INIT_ISOC99_PRINTF;
+ INIT_FREXP;
+ INIT_FREXPF_FREXPL;
+ INIT_GETPWNAM_AND_FRIENDS;
+ INIT_GETPWNAM_R_AND_FRIENDS;
+ INIT_GETPWENT;
+ INIT_FGETPWENT;
+ INIT_GETPWENT_R;
+ INIT_SETPWENT;
+ INIT_CLOCK_GETTIME;
+ INIT_GETITIMER;
+ INIT_TIME;
+ INIT_GLOB;
+ INIT_WAIT;
+ INIT_WAIT4;
+ INIT_INET;
+ INIT_PTHREAD_GETSCHEDPARAM;
+ INIT_GETADDRINFO;
+ INIT_GETNAMEINFO;
+ INIT_GETSOCKNAME;
+ INIT_GETHOSTBYNAME;
+ INIT_GETHOSTBYNAME_R;
+ INIT_GETHOSTBYNAME2_R;
+ INIT_GETHOSTBYADDR_R;
+ INIT_GETHOSTENT_R;
+ INIT_GETSOCKOPT;
+ INIT_ACCEPT;
+ INIT_ACCEPT4;
+ INIT_MODF;
+ INIT_RECVMSG;
+ INIT_GETPEERNAME;
+ INIT_IOCTL;
+ INIT_INET_ATON;
+ INIT_SYSINFO;
+ INIT_READDIR;
+ INIT_READDIR64;
+ INIT_PTRACE;
+ INIT_SETLOCALE;
+ INIT_GETCWD;
+ INIT_GET_CURRENT_DIR_NAME;
+ INIT_STRTOIMAX;
+ INIT_MBSTOWCS;
+ INIT_MBSNRTOWCS;
+ INIT_WCSTOMBS;
+ INIT_WCSNRTOMBS;
+ INIT_WCRTOMB;
+ INIT_TCGETATTR;
+ INIT_REALPATH;
+ INIT_CANONICALIZE_FILE_NAME;
+ INIT_CONFSTR;
+ INIT_SCHED_GETAFFINITY;
+ INIT_SCHED_GETPARAM;
+ INIT_STRERROR;
+ INIT_STRERROR_R;
+ INIT_XPG_STRERROR_R;
+ INIT_SCANDIR;
+ INIT_SCANDIR64;
+ INIT_GETGROUPS;
+ INIT_POLL;
+ INIT_PPOLL;
+ INIT_WORDEXP;
+ INIT_SIGWAIT;
+ INIT_SIGWAITINFO;
+ INIT_SIGTIMEDWAIT;
+ INIT_SIGSETOPS;
+ INIT_SIGPENDING;
+ INIT_SIGPROCMASK;
+ INIT_BACKTRACE;
+ INIT__EXIT;
+ INIT_PTHREAD_MUTEX_LOCK;
+ INIT_PTHREAD_MUTEX_UNLOCK;
+ INIT_GETMNTENT;
+ INIT_GETMNTENT_R;
+ INIT_STATFS;
+ INIT_STATFS64;
+ INIT_STATVFS;
+ INIT_STATVFS64;
+ INIT_INITGROUPS;
+ INIT_ETHER_NTOA_ATON;
+ INIT_ETHER_HOST;
+ INIT_ETHER_R;
+ INIT_SHMCTL;
+ INIT_RANDOM_R;
+ INIT_PTHREAD_ATTR_GET;
+ INIT_PTHREAD_ATTR_GETINHERITSCHED;
+ INIT_PTHREAD_ATTR_GETAFFINITY_NP;
+ INIT_PTHREAD_MUTEXATTR_GETPSHARED;
+ INIT_PTHREAD_MUTEXATTR_GETTYPE;
+ INIT_PTHREAD_MUTEXATTR_GETPROTOCOL;
+ INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING;
+ INIT_PTHREAD_MUTEXATTR_GETROBUST;
+ INIT_PTHREAD_MUTEXATTR_GETROBUST_NP;
+ INIT_PTHREAD_RWLOCKATTR_GETPSHARED;
+ INIT_PTHREAD_RWLOCKATTR_GETKIND_NP;
+ INIT_PTHREAD_CONDATTR_GETPSHARED;
+ INIT_PTHREAD_CONDATTR_GETCLOCK;
+ INIT_PTHREAD_BARRIERATTR_GETPSHARED;
+ INIT_TMPNAM;
+ INIT_TMPNAM_R;
+ INIT_TEMPNAM;
+ INIT_PTHREAD_SETNAME_NP;
+ INIT_SINCOS;
+ INIT_REMQUO;
+ INIT_LGAMMA;
+ INIT_LGAMMA_R;
+ INIT_LGAMMAL_R;
+ INIT_DRAND48_R;
+ INIT_RAND_R;
+ INIT_GETLINE;
+ INIT_ICONV;
+ INIT_TIMES;
+ INIT_TLS_GET_ADDR;
+ INIT_LISTXATTR;
+ INIT_GETXATTR;
+ INIT_GETRESID;
+ INIT_GETIFADDRS;
+ INIT_IF_INDEXTONAME;
+ INIT_CAPGET;
+ INIT_AEABI_MEM;
+ INIT___BZERO;
+ INIT_FTIME;
+ INIT_XDR;
+ INIT_TSEARCH;
+ INIT_LIBIO_INTERNALS;
+ INIT_FOPEN;
+ INIT_FOPEN64;
+ INIT_OPEN_MEMSTREAM;
+ INIT_OBSTACK;
+ INIT_FFLUSH;
+ INIT_FCLOSE;
+ INIT_DLOPEN_DLCLOSE;
+ INIT_GETPASS;
+ INIT_TIMERFD;
+ INIT_MLOCKX;
+ INIT_FOPENCOOKIE;
+ INIT_SEM;
+ INIT_PTHREAD_SETCANCEL;
+ INIT_MINCORE;
+ INIT_PROCESS_VM_READV;
+}
--- /dev/null
+//===-- sanitizer_common_interceptors_format.inc ----------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Scanf/printf implementation for use in *Sanitizer interceptors.
+// Follows http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html
+// and http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html
+// with a few common GNU extensions.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdarg.h>
+
+static const char *parse_number(const char *p, int *out) {
+ *out = internal_atoll(p);
+ while (*p >= '0' && *p <= '9')
+ ++p;
+ return p;
+}
+
+static const char *maybe_parse_param_index(const char *p, int *out) {
+ // n$
+ if (*p >= '0' && *p <= '9') {
+ int number;
+ const char *q = parse_number(p, &number);
+ CHECK(q);
+ if (*q == '$') {
+ *out = number;
+ p = q + 1;
+ }
+ }
+
+ // Otherwise, do not change p. This will be re-parsed later as the field
+ // width.
+ return p;
+}
+
+static bool char_is_one_of(char c, const char *s) {
+ return !!internal_strchr(s, c);
+}
+
+static const char *maybe_parse_length_modifier(const char *p, char ll[2]) {
+ if (char_is_one_of(*p, "jztLq")) {
+ ll[0] = *p;
+ ++p;
+ } else if (*p == 'h') {
+ ll[0] = 'h';
+ ++p;
+ if (*p == 'h') {
+ ll[1] = 'h';
+ ++p;
+ }
+ } else if (*p == 'l') {
+ ll[0] = 'l';
+ ++p;
+ if (*p == 'l') {
+ ll[1] = 'l';
+ ++p;
+ }
+ }
+ return p;
+}
+
+// Returns true if the character is an integer conversion specifier.
+static bool format_is_integer_conv(char c) {
+ return char_is_one_of(c, "diouxXn");
+}
+
+// Returns true if the character is an floating point conversion specifier.
+static bool format_is_float_conv(char c) {
+ return char_is_one_of(c, "aAeEfFgG");
+}
+
+// Returns string output character size for string-like conversions,
+// or 0 if the conversion is invalid.
+static int format_get_char_size(char convSpecifier,
+ const char lengthModifier[2]) {
+ if (char_is_one_of(convSpecifier, "CS")) {
+ return sizeof(wchar_t);
+ }
+
+ if (char_is_one_of(convSpecifier, "cs[")) {
+ if (lengthModifier[0] == 'l' && lengthModifier[1] == '\0')
+ return sizeof(wchar_t);
+ else if (lengthModifier[0] == '\0')
+ return sizeof(char);
+ }
+
+ return 0;
+}
+
+enum FormatStoreSize {
+ // Store size not known in advance; can be calculated as wcslen() of the
+ // destination buffer.
+ FSS_WCSLEN = -2,
+ // Store size not known in advance; can be calculated as strlen() of the
+ // destination buffer.
+ FSS_STRLEN = -1,
+ // Invalid conversion specifier.
+ FSS_INVALID = 0
+};
+
+// Returns the memory size of a format directive (if >0), or a value of
+// FormatStoreSize.
+static int format_get_value_size(char convSpecifier,
+ const char lengthModifier[2],
+ bool promote_float) {
+ if (format_is_integer_conv(convSpecifier)) {
+ switch (lengthModifier[0]) {
+ case 'h':
+ return lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short);
+ case 'l':
+ return lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long);
+ case 'q':
+ return sizeof(long long);
+ case 'L':
+ return sizeof(long long);
+ case 'j':
+ return sizeof(INTMAX_T);
+ case 'z':
+ return sizeof(SIZE_T);
+ case 't':
+ return sizeof(PTRDIFF_T);
+ case 0:
+ return sizeof(int);
+ default:
+ return FSS_INVALID;
+ }
+ }
+
+ if (format_is_float_conv(convSpecifier)) {
+ switch (lengthModifier[0]) {
+ case 'L':
+ case 'q':
+ return sizeof(long double);
+ case 'l':
+ return lengthModifier[1] == 'l' ? sizeof(long double)
+ : sizeof(double);
+ case 0:
+ // Printf promotes floats to doubles but scanf does not
+ return promote_float ? sizeof(double) : sizeof(float);
+ default:
+ return FSS_INVALID;
+ }
+ }
+
+ if (convSpecifier == 'p') {
+ if (lengthModifier[0] != 0)
+ return FSS_INVALID;
+ return sizeof(void *);
+ }
+
+ return FSS_INVALID;
+}
+
+struct ScanfDirective {
+ int argIdx; // argument index, or -1 if not specified ("%n$")
+ int fieldWidth;
+ const char *begin;
+ const char *end;
+ bool suppressed; // suppress assignment ("*")
+ bool allocate; // allocate space ("m")
+ char lengthModifier[2];
+ char convSpecifier;
+ bool maybeGnuMalloc;
+};
+
+// Parse scanf format string. If a valid directive in encountered, it is
+// returned in dir. This function returns the pointer to the first
+// unprocessed character, or 0 in case of error.
+// In case of the end-of-string, a pointer to the closing \0 is returned.
+static const char *scanf_parse_next(const char *p, bool allowGnuMalloc,
+ ScanfDirective *dir) {
+ internal_memset(dir, 0, sizeof(*dir));
+ dir->argIdx = -1;
+
+ while (*p) {
+ if (*p != '%') {
+ ++p;
+ continue;
+ }
+ dir->begin = p;
+ ++p;
+ // %%
+ if (*p == '%') {
+ ++p;
+ continue;
+ }
+ if (*p == '\0') {
+ return nullptr;
+ }
+ // %n$
+ p = maybe_parse_param_index(p, &dir->argIdx);
+ CHECK(p);
+ // *
+ if (*p == '*') {
+ dir->suppressed = true;
+ ++p;
+ }
+ // Field width
+ if (*p >= '0' && *p <= '9') {
+ p = parse_number(p, &dir->fieldWidth);
+ CHECK(p);
+ if (dir->fieldWidth <= 0) // Width if at all must be non-zero
+ return nullptr;
+ }
+ // m
+ if (*p == 'm') {
+ dir->allocate = true;
+ ++p;
+ }
+ // Length modifier.
+ p = maybe_parse_length_modifier(p, dir->lengthModifier);
+ // Conversion specifier.
+ dir->convSpecifier = *p++;
+ // Consume %[...] expression.
+ if (dir->convSpecifier == '[') {
+ if (*p == '^')
+ ++p;
+ if (*p == ']')
+ ++p;
+ while (*p && *p != ']')
+ ++p;
+ if (*p == 0)
+ return nullptr; // unexpected end of string
+ // Consume the closing ']'.
+ ++p;
+ }
+ // This is unfortunately ambiguous between old GNU extension
+ // of %as, %aS and %a[...] and newer POSIX %a followed by
+ // letters s, S or [.
+ if (allowGnuMalloc && dir->convSpecifier == 'a' &&
+ !dir->lengthModifier[0]) {
+ if (*p == 's' || *p == 'S') {
+ dir->maybeGnuMalloc = true;
+ ++p;
+ } else if (*p == '[') {
+ // Watch for %a[h-j%d], if % appears in the
+ // [...] range, then we need to give up, we don't know
+ // if scanf will parse it as POSIX %a [h-j %d ] or
+ // GNU allocation of string with range dh-j plus %.
+ const char *q = p + 1;
+ if (*q == '^')
+ ++q;
+ if (*q == ']')
+ ++q;
+ while (*q && *q != ']' && *q != '%')
+ ++q;
+ if (*q == 0 || *q == '%')
+ return nullptr;
+ p = q + 1; // Consume the closing ']'.
+ dir->maybeGnuMalloc = true;
+ }
+ }
+ dir->end = p;
+ break;
+ }
+ return p;
+}
+
+static int scanf_get_value_size(ScanfDirective *dir) {
+ if (dir->allocate) {
+ if (!char_is_one_of(dir->convSpecifier, "cCsS["))
+ return FSS_INVALID;
+ return sizeof(char *);
+ }
+
+ if (dir->maybeGnuMalloc) {
+ if (dir->convSpecifier != 'a' || dir->lengthModifier[0])
+ return FSS_INVALID;
+ // This is ambiguous, so check the smaller size of char * (if it is
+ // a GNU extension of %as, %aS or %a[...]) and float (if it is
+ // POSIX %a followed by s, S or [ letters).
+ return sizeof(char *) < sizeof(float) ? sizeof(char *) : sizeof(float);
+ }
+
+ if (char_is_one_of(dir->convSpecifier, "cCsS[")) {
+ bool needsTerminator = char_is_one_of(dir->convSpecifier, "sS[");
+ unsigned charSize =
+ format_get_char_size(dir->convSpecifier, dir->lengthModifier);
+ if (charSize == 0)
+ return FSS_INVALID;
+ if (dir->fieldWidth == 0) {
+ if (!needsTerminator)
+ return charSize;
+ return (charSize == sizeof(char)) ? FSS_STRLEN : FSS_WCSLEN;
+ }
+ return (dir->fieldWidth + needsTerminator) * charSize;
+ }
+
+ return format_get_value_size(dir->convSpecifier, dir->lengthModifier, false);
+}
+
+// Common part of *scanf interceptors.
+// Process format string and va_list, and report all store ranges.
+// Stops when "consuming" n_inputs input items.
+static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc,
+ const char *format, va_list aq) {
+ CHECK_GT(n_inputs, 0);
+ const char *p = format;
+
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1);
+
+ while (*p) {
+ ScanfDirective dir;
+ p = scanf_parse_next(p, allowGnuMalloc, &dir);
+ if (!p)
+ break;
+ if (dir.convSpecifier == 0) {
+ // This can only happen at the end of the format string.
+ CHECK_EQ(*p, 0);
+ break;
+ }
+ // Here the directive is valid. Do what it says.
+ if (dir.argIdx != -1) {
+ // Unsupported.
+ break;
+ }
+ if (dir.suppressed)
+ continue;
+ int size = scanf_get_value_size(&dir);
+ if (size == FSS_INVALID) {
+ Report("WARNING: unexpected format specifier in scanf interceptor: "
+ "%.*s\n", dir.end - dir.begin, dir.begin);
+ break;
+ }
+ void *argp = va_arg(aq, void *);
+ if (dir.convSpecifier != 'n')
+ --n_inputs;
+ if (n_inputs < 0)
+ break;
+ if (size == FSS_STRLEN) {
+ size = internal_strlen((const char *)argp) + 1;
+ } else if (size == FSS_WCSLEN) {
+ // FIXME: actually use wcslen() to calculate it.
+ size = 0;
+ }
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size);
+ }
+}
+
+#if SANITIZER_INTERCEPT_PRINTF
+
+struct PrintfDirective {
+ int fieldWidth;
+ int fieldPrecision;
+ int argIdx; // width argument index, or -1 if not specified ("%*n$")
+ int precisionIdx; // precision argument index, or -1 if not specified (".*n$")
+ const char *begin;
+ const char *end;
+ bool starredWidth;
+ bool starredPrecision;
+ char lengthModifier[2];
+ char convSpecifier;
+};
+
+static const char *maybe_parse_number(const char *p, int *out) {
+ if (*p >= '0' && *p <= '9')
+ p = parse_number(p, out);
+ return p;
+}
+
+static const char *maybe_parse_number_or_star(const char *p, int *out,
+ bool *star) {
+ if (*p == '*') {
+ *star = true;
+ ++p;
+ } else {
+ *star = false;
+ p = maybe_parse_number(p, out);
+ }
+ return p;
+}
+
+// Parse printf format string. Same as scanf_parse_next.
+static const char *printf_parse_next(const char *p, PrintfDirective *dir) {
+ internal_memset(dir, 0, sizeof(*dir));
+ dir->argIdx = -1;
+ dir->precisionIdx = -1;
+
+ while (*p) {
+ if (*p != '%') {
+ ++p;
+ continue;
+ }
+ dir->begin = p;
+ ++p;
+ // %%
+ if (*p == '%') {
+ ++p;
+ continue;
+ }
+ if (*p == '\0') {
+ return nullptr;
+ }
+ // %n$
+ p = maybe_parse_param_index(p, &dir->precisionIdx);
+ CHECK(p);
+ // Flags
+ while (char_is_one_of(*p, "'-+ #0")) {
+ ++p;
+ }
+ // Field width
+ p = maybe_parse_number_or_star(p, &dir->fieldWidth,
+ &dir->starredWidth);
+ if (!p)
+ return nullptr;
+ // Precision
+ if (*p == '.') {
+ ++p;
+ // Actual precision is optional (surprise!)
+ p = maybe_parse_number_or_star(p, &dir->fieldPrecision,
+ &dir->starredPrecision);
+ if (!p)
+ return nullptr;
+ // m$
+ if (dir->starredPrecision) {
+ p = maybe_parse_param_index(p, &dir->precisionIdx);
+ CHECK(p);
+ }
+ }
+ // Length modifier.
+ p = maybe_parse_length_modifier(p, dir->lengthModifier);
+ // Conversion specifier.
+ dir->convSpecifier = *p++;
+ dir->end = p;
+ break;
+ }
+ return p;
+}
+
+static int printf_get_value_size(PrintfDirective *dir) {
+ if (dir->convSpecifier == 'm') {
+ return sizeof(char *);
+ }
+
+ if (char_is_one_of(dir->convSpecifier, "cCsS")) {
+ unsigned charSize =
+ format_get_char_size(dir->convSpecifier, dir->lengthModifier);
+ if (charSize == 0)
+ return FSS_INVALID;
+ if (char_is_one_of(dir->convSpecifier, "sS")) {
+ return (charSize == sizeof(char)) ? FSS_STRLEN : FSS_WCSLEN;
+ }
+ return charSize;
+ }
+
+ return format_get_value_size(dir->convSpecifier, dir->lengthModifier, true);
+}
+
+#define SKIP_SCALAR_ARG(aq, convSpecifier, size) \
+ do { \
+ if (format_is_float_conv(convSpecifier)) { \
+ switch (size) { \
+ case 8: \
+ va_arg(*aq, double); \
+ break; \
+ case 12: \
+ va_arg(*aq, long double); \
+ break; \
+ case 16: \
+ va_arg(*aq, long double); \
+ break; \
+ default: \
+ Report("WARNING: unexpected floating-point arg size" \
+ " in printf interceptor: %d\n", size); \
+ return; \
+ } \
+ } else { \
+ switch (size) { \
+ case 1: \
+ case 2: \
+ case 4: \
+ va_arg(*aq, u32); \
+ break; \
+ case 8: \
+ va_arg(*aq, u64); \
+ break; \
+ default: \
+ Report("WARNING: unexpected arg size" \
+ " in printf interceptor: %d\n", size); \
+ return; \
+ } \
+ } \
+ } while (0)
+
+// Common part of *printf interceptors.
+// Process format string and va_list, and report all load ranges.
+static void printf_common(void *ctx, const char *format, va_list aq) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1);
+
+ const char *p = format;
+
+ while (*p) {
+ PrintfDirective dir;
+ p = printf_parse_next(p, &dir);
+ if (!p)
+ break;
+ if (dir.convSpecifier == 0) {
+ // This can only happen at the end of the format string.
+ CHECK_EQ(*p, 0);
+ break;
+ }
+ // Here the directive is valid. Do what it says.
+ if (dir.argIdx != -1 || dir.precisionIdx != -1) {
+ // Unsupported.
+ break;
+ }
+ if (dir.starredWidth) {
+ // Dynamic width
+ SKIP_SCALAR_ARG(&aq, 'd', sizeof(int));
+ }
+ if (dir.starredPrecision) {
+ // Dynamic precision
+ SKIP_SCALAR_ARG(&aq, 'd', sizeof(int));
+ }
+ int size = printf_get_value_size(&dir);
+ if (size == FSS_INVALID) {
+ Report("WARNING: unexpected format specifier in printf "
+ "interceptor: %.*s\n", dir.end - dir.begin, dir.begin);
+ break;
+ }
+ if (dir.convSpecifier == 'n') {
+ void *argp = va_arg(aq, void *);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size);
+ continue;
+ } else if (size == FSS_STRLEN) {
+ if (void *argp = va_arg(aq, void *)) {
+ if (dir.starredPrecision) {
+ // FIXME: properly support starred precision for strings.
+ size = 0;
+ } else if (dir.fieldPrecision > 0) {
+ // Won't read more than "precision" symbols.
+ size = internal_strnlen((const char *)argp, dir.fieldPrecision);
+ if (size < dir.fieldPrecision) size++;
+ } else {
+ // Whole string will be accessed.
+ size = internal_strlen((const char *)argp) + 1;
+ }
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size);
+ }
+ } else if (size == FSS_WCSLEN) {
+ if (void *argp = va_arg(aq, void *)) {
+ // FIXME: Properly support wide-character strings (via wcsrtombs).
+ size = 0;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size);
+ }
+ } else {
+ // Skip non-pointer args
+ SKIP_SCALAR_ARG(&aq, dir.convSpecifier, size);
+ }
+ }
+}
+
+#endif // SANITIZER_INTERCEPT_PRINTF
struct ioctl_desc {
unsigned req;
- // FIXME: support read+write arguments. Those are currently marked as WRITE.
+ // FIXME: support read+write arguments. Currently READWRITE and WRITE do the
+ // same thing.
+ // XXX: The declarations below may use WRITE instead of READWRITE, unless
+ // explicitly noted.
enum {
NONE,
READ,
WRITE,
+ READWRITE,
CUSTOM
- } type : 2;
- unsigned size : 30;
+ } type : 3;
+ unsigned size : 29;
const char* name;
};
// Handle the most evil ioctls that encode argument value as part of request id.
static unsigned ioctl_request_fixup(unsigned req) {
#if SANITIZER_LINUX
- if ((req & ~0x3fff001fU) == IOCTL_EVIOCGBIT)
+ // Strip size and event number.
+ const unsigned kEviocgbitMask =
+ (IOC_SIZEMASK << IOC_SIZESHIFT) | EVIOC_EV_MAX;
+ if ((req & ~kEviocgbitMask) == IOCTL_EVIOCGBIT)
return IOCTL_EVIOCGBIT;
- if ((req & ~0x3fU) == IOCTL_EVIOCGABS)
+ // Strip absolute axis number.
+ if ((req & ~EVIOC_ABS_MAX) == IOCTL_EVIOCGABS)
return IOCTL_EVIOCGABS;
- if ((req & ~0x3fU) == IOCTL_EVIOCSABS)
+ if ((req & ~EVIOC_ABS_MAX) == IOCTL_EVIOCSABS)
return IOCTL_EVIOCSABS;
#endif
return req;
if (left == right && ioctl_table[left].req == req)
return ioctl_table + left;
else
- return 0;
+ return nullptr;
+}
+
+static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
+ CHECK(desc);
+ desc->req = req;
+ desc->name = "<DECODED_IOCTL>";
+ desc->size = IOC_SIZE(req);
+ // Sanity check.
+ if (desc->size > 0xFFFF) return false;
+ unsigned dir = IOC_DIR(req);
+ switch (dir) {
+ case IOC_NONE:
+ desc->type = ioctl_desc::NONE;
+ break;
+ case IOC_READ | IOC_WRITE:
+ desc->type = ioctl_desc::READWRITE;
+ break;
+ case IOC_READ:
+ desc->type = ioctl_desc::WRITE;
+ break;
+ case IOC_WRITE:
+ desc->type = ioctl_desc::READ;
+ break;
+ default:
+ return false;
+ }
+ // Size can be 0 iff type is NONE.
+ if ((desc->type == IOC_NONE) != (desc->size == 0)) return false;
+ // Sanity check.
+ if (IOC_TYPE(req) == 0) return false;
+ return true;
}
static const ioctl_desc *ioctl_lookup(unsigned req) {
if (desc) return desc;
// Try stripping access size from the request id.
- desc = ioctl_table_lookup(req & ~0x3fff0000U);
+ desc = ioctl_table_lookup(req & ~(IOC_SIZEMASK << IOC_SIZESHIFT));
// Sanity check: requests that encode access size are either read or write and
// have size of 0 in the table.
if (desc && desc->size == 0 &&
- (desc->type == ioctl_desc::WRITE || desc->type == ioctl_desc::READ))
+ (desc->type == ioctl_desc::READWRITE || desc->type == ioctl_desc::WRITE ||
+ desc->type == ioctl_desc::READ))
return desc;
- return 0;
+ return nullptr;
}
static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d,
unsigned request, void *arg) {
- if (desc->type == ioctl_desc::READ) {
+ if (desc->type == ioctl_desc::READ || desc->type == ioctl_desc::READWRITE) {
unsigned size = desc->size ? desc->size : IOC_SIZE(request);
COMMON_INTERCEPTOR_READ_RANGE(ctx, arg, size);
}
if (desc->type != ioctl_desc::CUSTOM)
return;
- switch (request) {
- case 0x00008912: { // SIOCGIFCONF
- struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
- COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len));
- break;
- }
+ if (request == IOCTL_SIOCGIFCONF) {
+ struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len));
}
- return;
}
static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d,
unsigned request, void *arg) {
- if (desc->type == ioctl_desc::WRITE) {
+ if (desc->type == ioctl_desc::WRITE || desc->type == ioctl_desc::READWRITE) {
// FIXME: add verbose output
unsigned size = desc->size ? desc->size : IOC_SIZE(request);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, arg, size);
}
if (desc->type != ioctl_desc::CUSTOM)
return;
- switch (request) {
- case 0x00008912: { // SIOCGIFCONF
- struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len);
- break;
- }
+ if (request == IOCTL_SIOCGIFCONF) {
+ struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len);
}
- return;
}
+++ /dev/null
-//===-- sanitizer_common_interceptors_scanf.inc -----------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Scanf implementation for use in *Sanitizer interceptors.
-// Follows http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html
-// with a few common GNU extensions.
-//
-//===----------------------------------------------------------------------===//
-#include <stdarg.h>
-
-struct ScanfDirective {
- int argIdx; // argument index, or -1 of not specified ("%n$")
- int fieldWidth;
- bool suppressed; // suppress assignment ("*")
- bool allocate; // allocate space ("m")
- char lengthModifier[2];
- char convSpecifier;
- bool maybeGnuMalloc;
-};
-
-static const char *parse_number(const char *p, int *out) {
- *out = internal_atoll(p);
- while (*p >= '0' && *p <= '9')
- ++p;
- return p;
-}
-
-static bool char_is_one_of(char c, const char *s) {
- return !!internal_strchr(s, c);
-}
-
-// Parse scanf format string. If a valid directive in encountered, it is
-// returned in dir. This function returns the pointer to the first
-// unprocessed character, or 0 in case of error.
-// In case of the end-of-string, a pointer to the closing \0 is returned.
-static const char *scanf_parse_next(const char *p, bool allowGnuMalloc,
- ScanfDirective *dir) {
- internal_memset(dir, 0, sizeof(*dir));
- dir->argIdx = -1;
-
- while (*p) {
- if (*p != '%') {
- ++p;
- continue;
- }
- ++p;
- // %%
- if (*p == '%') {
- ++p;
- continue;
- }
- if (*p == '\0') {
- return 0;
- }
- // %n$
- if (*p >= '0' && *p <= '9') {
- int number;
- const char *q = parse_number(p, &number);
- if (*q == '$') {
- dir->argIdx = number;
- p = q + 1;
- }
- // Otherwise, do not change p. This will be re-parsed later as the field
- // width.
- }
- // *
- if (*p == '*') {
- dir->suppressed = true;
- ++p;
- }
- // Field width.
- if (*p >= '0' && *p <= '9') {
- p = parse_number(p, &dir->fieldWidth);
- if (dir->fieldWidth <= 0)
- return 0;
- }
- // m
- if (*p == 'm') {
- dir->allocate = true;
- ++p;
- }
- // Length modifier.
- if (char_is_one_of(*p, "jztLq")) {
- dir->lengthModifier[0] = *p;
- ++p;
- } else if (*p == 'h') {
- dir->lengthModifier[0] = 'h';
- ++p;
- if (*p == 'h') {
- dir->lengthModifier[1] = 'h';
- ++p;
- }
- } else if (*p == 'l') {
- dir->lengthModifier[0] = 'l';
- ++p;
- if (*p == 'l') {
- dir->lengthModifier[1] = 'l';
- ++p;
- }
- }
- // Conversion specifier.
- dir->convSpecifier = *p++;
- // Consume %[...] expression.
- if (dir->convSpecifier == '[') {
- if (*p == '^')
- ++p;
- if (*p == ']')
- ++p;
- while (*p && *p != ']')
- ++p;
- if (*p == 0)
- return 0; // unexpected end of string
- // Consume the closing ']'.
- ++p;
- }
- // This is unfortunately ambiguous between old GNU extension
- // of %as, %aS and %a[...] and newer POSIX %a followed by
- // letters s, S or [.
- if (allowGnuMalloc && dir->convSpecifier == 'a' &&
- !dir->lengthModifier[0]) {
- if (*p == 's' || *p == 'S') {
- dir->maybeGnuMalloc = true;
- ++p;
- } else if (*p == '[') {
- // Watch for %a[h-j%d], if % appears in the
- // [...] range, then we need to give up, we don't know
- // if scanf will parse it as POSIX %a [h-j %d ] or
- // GNU allocation of string with range dh-j plus %.
- const char *q = p + 1;
- if (*q == '^')
- ++q;
- if (*q == ']')
- ++q;
- while (*q && *q != ']' && *q != '%')
- ++q;
- if (*q == 0 || *q == '%')
- return 0;
- p = q + 1; // Consume the closing ']'.
- dir->maybeGnuMalloc = true;
- }
- }
- break;
- }
- return p;
-}
-
-// Returns true if the character is an integer conversion specifier.
-static bool scanf_is_integer_conv(char c) {
- return char_is_one_of(c, "diouxXn");
-}
-
-// Returns true if the character is an floating point conversion specifier.
-static bool scanf_is_float_conv(char c) {
- return char_is_one_of(c, "aAeEfFgG");
-}
-
-// Returns string output character size for string-like conversions,
-// or 0 if the conversion is invalid.
-static int scanf_get_char_size(ScanfDirective *dir) {
- if (char_is_one_of(dir->convSpecifier, "CS")) {
- // wchar_t
- return 0;
- }
-
- if (char_is_one_of(dir->convSpecifier, "cs[")) {
- if (dir->lengthModifier[0] == 'l')
- // wchar_t
- return 0;
- else if (dir->lengthModifier[0] == 0)
- return sizeof(char);
- else
- return 0;
- }
-
- return 0;
-}
-
-enum ScanfStoreSize {
- // Store size not known in advance; can be calculated as strlen() of the
- // destination buffer.
- SSS_STRLEN = -1,
- // Invalid conversion specifier.
- SSS_INVALID = 0
-};
-
-// Returns the store size of a scanf directive (if >0), or a value of
-// ScanfStoreSize.
-static int scanf_get_store_size(ScanfDirective *dir) {
- if (dir->allocate) {
- if (!char_is_one_of(dir->convSpecifier, "cCsS["))
- return SSS_INVALID;
- return sizeof(char *);
- }
-
- if (dir->maybeGnuMalloc) {
- if (dir->convSpecifier != 'a' || dir->lengthModifier[0])
- return SSS_INVALID;
- // This is ambiguous, so check the smaller size of char * (if it is
- // a GNU extension of %as, %aS or %a[...]) and float (if it is
- // POSIX %a followed by s, S or [ letters).
- return sizeof(char *) < sizeof(float) ? sizeof(char *) : sizeof(float);
- }
-
- if (scanf_is_integer_conv(dir->convSpecifier)) {
- switch (dir->lengthModifier[0]) {
- case 'h':
- return dir->lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short);
- case 'l':
- return dir->lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long);
- case 'L':
- return sizeof(long long);
- case 'j':
- return sizeof(INTMAX_T);
- case 'z':
- return sizeof(SIZE_T);
- case 't':
- return sizeof(PTRDIFF_T);
- case 0:
- return sizeof(int);
- default:
- return SSS_INVALID;
- }
- }
-
- if (scanf_is_float_conv(dir->convSpecifier)) {
- switch (dir->lengthModifier[0]) {
- case 'L':
- case 'q':
- return sizeof(long double);
- case 'l':
- return dir->lengthModifier[1] == 'l' ? sizeof(long double)
- : sizeof(double);
- case 0:
- return sizeof(float);
- default:
- return SSS_INVALID;
- }
- }
-
- if (char_is_one_of(dir->convSpecifier, "sS[")) {
- unsigned charSize = scanf_get_char_size(dir);
- if (charSize == 0)
- return SSS_INVALID;
- if (dir->fieldWidth == 0)
- return SSS_STRLEN;
- return (dir->fieldWidth + 1) * charSize;
- }
-
- if (char_is_one_of(dir->convSpecifier, "cC")) {
- unsigned charSize = scanf_get_char_size(dir);
- if (charSize == 0)
- return SSS_INVALID;
- if (dir->fieldWidth == 0)
- return charSize;
- return dir->fieldWidth * charSize;
- }
-
- if (dir->convSpecifier == 'p') {
- if (dir->lengthModifier[1] != 0)
- return SSS_INVALID;
- return sizeof(void *);
- }
-
- return SSS_INVALID;
-}
-
-// Common part of *scanf interceptors.
-// Process format string and va_list, and report all store ranges.
-// Stops when "consuming" n_inputs input items.
-static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc,
- const char *format, va_list aq) {
- CHECK_GT(n_inputs, 0);
- const char *p = format;
-
- while (*p) {
- ScanfDirective dir;
- p = scanf_parse_next(p, allowGnuMalloc, &dir);
- if (!p)
- break;
- if (dir.convSpecifier == 0) {
- // This can only happen at the end of the format string.
- CHECK_EQ(*p, 0);
- break;
- }
- // Here the directive is valid. Do what it says.
- if (dir.argIdx != -1) {
- // Unsupported.
- break;
- }
- if (dir.suppressed)
- continue;
- int size = scanf_get_store_size(&dir);
- if (size == SSS_INVALID)
- break;
- void *argp = va_arg(aq, void *);
- if (dir.convSpecifier != 'n')
- --n_inputs;
- if (n_inputs < 0)
- break;
- if (size == SSS_STRLEN) {
- size = internal_strlen((const char *)argp) + 1;
- }
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size);
- }
-}
//===----------------------------------------------------------------------===//
#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_stackdepot.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
+
+#if SANITIZER_POSIX
+#include "sanitizer_posix.h"
+#endif
namespace __sanitizer {
-bool PrintsToTty() {
- MaybeOpenReportFile();
- return internal_isatty(report_fd) != 0;
+bool ReportFile::SupportsColors() {
+ SpinMutexLock l(mu);
+ ReopenIfNecessary();
+ return SupportsColoredOutput(fd);
}
-bool PrintsToTtyCached() {
+bool ColorizeReports() {
// FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color
// printing on Windows.
if (SANITIZER_WINDOWS)
- return 0;
+ return false;
+
+ const char *flag = common_flags()->color;
+ return internal_strcmp(flag, "always") == 0 ||
+ (internal_strcmp(flag, "auto") == 0 && report_file.SupportsColors());
+}
+
+static void (*sandboxing_callback)();
+void SetSandboxingCallback(void (*f)()) {
+ sandboxing_callback = f;
+}
- static int cached = 0;
- static bool prints_to_tty;
- if (!cached) { // Not thread-safe.
- prints_to_tty = PrintsToTty();
- cached = 1;
+void ReportErrorSummary(const char *error_type, StackTrace *stack) {
+ if (!common_flags()->print_summary)
+ return;
+ if (stack->size == 0) {
+ ReportErrorSummary(error_type);
+ return;
}
- return prints_to_tty;
+ // Currently, we include the first stack frame into the report summary.
+ // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
+ uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
+ SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
+ ReportErrorSummary(error_type, frame->info);
+ frame->ClearAll();
+}
+
+static void (*SoftRssLimitExceededCallback)(bool exceeded);
+void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) {
+ CHECK_EQ(SoftRssLimitExceededCallback, nullptr);
+ SoftRssLimitExceededCallback = Callback;
}
+
+void BackgroundThread(void *arg) {
+ uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb;
+ uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb;
+ uptr prev_reported_rss = 0;
+ uptr prev_reported_stack_depot_size = 0;
+ bool reached_soft_rss_limit = false;
+ while (true) {
+ SleepForMillis(100);
+ uptr current_rss_mb = GetRSS() >> 20;
+ if (Verbosity()) {
+ // If RSS has grown 10% since last time, print some information.
+ if (prev_reported_rss * 11 / 10 < current_rss_mb) {
+ Printf("%s: RSS: %zdMb\n", SanitizerToolName, current_rss_mb);
+ prev_reported_rss = current_rss_mb;
+ }
+ // If stack depot has grown 10% since last time, print it too.
+ StackDepotStats *stack_depot_stats = StackDepotGetStats();
+ if (prev_reported_stack_depot_size * 11 / 10 <
+ stack_depot_stats->allocated) {
+ Printf("%s: StackDepot: %zd ids; %zdM allocated\n",
+ SanitizerToolName,
+ stack_depot_stats->n_uniq_ids,
+ stack_depot_stats->allocated >> 20);
+ prev_reported_stack_depot_size = stack_depot_stats->allocated;
+ }
+ }
+ // Check RSS against the limit.
+ if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) {
+ Report("%s: hard rss limit exhausted (%zdMb vs %zdMb)\n",
+ SanitizerToolName, hard_rss_limit_mb, current_rss_mb);
+ DumpProcessMap();
+ Die();
+ }
+ if (soft_rss_limit_mb) {
+ if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) {
+ reached_soft_rss_limit = true;
+ Report("%s: soft rss limit exhausted (%zdMb vs %zdMb)\n",
+ SanitizerToolName, soft_rss_limit_mb, current_rss_mb);
+ if (SoftRssLimitExceededCallback)
+ SoftRssLimitExceededCallback(true);
+ } else if (soft_rss_limit_mb >= current_rss_mb &&
+ reached_soft_rss_limit) {
+ reached_soft_rss_limit = false;
+ if (SoftRssLimitExceededCallback)
+ SoftRssLimitExceededCallback(false);
+ }
+ }
+ }
+}
+
+void MaybeStartBackgroudThread() {
+#if SANITIZER_LINUX // Need to implement/test on other platforms.
+ // Start the background thread if one of the rss limits is given.
+ if (!common_flags()->hard_rss_limit_mb &&
+ !common_flags()->soft_rss_limit_mb) return;
+ if (!&real_pthread_create) return; // Can't spawn the thread anyway.
+ internal_start_thread(BackgroundThread, nullptr);
+#endif
+}
+
} // namespace __sanitizer
+
+void NOINLINE
+__sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args) {
+ PrepareForSandboxing(args);
+ if (sandboxing_callback)
+ sandboxing_callback();
+}
}
}
+#if !SANITIZER_ANDROID
PRE_SYSCALL(adjtimex)(void *txc_p) {}
POST_SYSCALL(adjtimex)(long res, void *txc_p) {
if (txc_p) POST_WRITE(txc_p, struct_timex_sz);
}
}
+#endif
PRE_SYSCALL(times)(void *tbuf) {}
POST_SYSCALL(acct)(long res, const void *name) {}
-PRE_SYSCALL(capget)(void *header, void *dataptr) {}
+PRE_SYSCALL(capget)(void *header, void *dataptr) {
+ if (header) PRE_READ(header, __user_cap_header_struct_sz);
+}
POST_SYSCALL(capget)(long res, void *header, void *dataptr) {
- if (res >= 0) {
- if (header) POST_WRITE(header, __user_cap_header_struct_sz);
+ if (res >= 0)
if (dataptr) POST_WRITE(dataptr, __user_cap_data_struct_sz);
- }
}
PRE_SYSCALL(capset)(void *header, const void *data) {
+ if (header) PRE_READ(header, __user_cap_header_struct_sz);
if (data) PRE_READ(data, __user_cap_data_struct_sz);
}
-POST_SYSCALL(capset)(long res, void *header, const void *data) {
- if (res >= 0) {
- if (header) POST_WRITE(header, __user_cap_header_struct_sz);
- }
-}
+POST_SYSCALL(capset)(long res, void *header, const void *data) {}
PRE_SYSCALL(personality)(long personality) {}
}
}
+#if !SANITIZER_ANDROID
PRE_SYSCALL(clock_adjtime)(long which_clock, void *tx) {}
POST_SYSCALL(clock_adjtime)(long res, long which_clock, void *tx) {
if (tx) POST_WRITE(tx, struct_timex_sz);
}
}
+#endif
PRE_SYSCALL(clock_getres)(long which_clock, void *tp) {}
}
}
+#if !SANITIZER_ANDROID
PRE_SYSCALL(statfs)(const void *path, void *buf) {
if (path)
PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
if (buf) POST_WRITE(buf, struct_statfs64_sz);
}
}
+#endif // !SANITIZER_ANDROID
PRE_SYSCALL(lstat)(const void *filename, void *statbuf) {
if (filename)
}
}
+#if !SANITIZER_ANDROID
PRE_SYSCALL(ustat)(long dev, void *ubuf) {}
POST_SYSCALL(ustat)(long res, long dev, void *ubuf) {
if (ubuf) POST_WRITE(ubuf, struct_ustat_sz);
}
}
+#endif // !SANITIZER_ANDROID
PRE_SYSCALL(stat64)(const void *filename, void *statbuf) {
if (filename)
POST_SYSCALL(getxattr)(long res, const void *path, const void *name,
void *value, long size) {
- if (res >= 0) {
- if (value) POST_WRITE(value, size);
+ if (size && res > 0) {
+ if (value) POST_WRITE(value, res);
}
}
POST_SYSCALL(lgetxattr)(long res, const void *path, const void *name,
void *value, long size) {
- if (res >= 0) {
- if (value) POST_WRITE(value, size);
+ if (size && res > 0) {
+ if (value) POST_WRITE(value, res);
}
}
POST_SYSCALL(fgetxattr)(long res, long fd, const void *name, void *value,
long size) {
- if (res >= 0) {
- if (value) POST_WRITE(value, size);
+ if (size && res > 0) {
+ if (value) POST_WRITE(value, res);
}
}
}
POST_SYSCALL(listxattr)(long res, const void *path, void *list, long size) {
- if (res >= 0) {
- if (list) POST_WRITE(list, size);
+ if (size && res > 0) {
+ if (list) POST_WRITE(list, res);
}
}
}
POST_SYSCALL(llistxattr)(long res, const void *path, void *list, long size) {
- if (res >= 0) {
- if (list) POST_WRITE(list, size);
+ if (size && res > 0) {
+ if (list) POST_WRITE(list, res);
}
}
PRE_SYSCALL(flistxattr)(long fd, void *list, long size) {}
POST_SYSCALL(flistxattr)(long res, long fd, void *list, long size) {
- if (res >= 0) {
- if (list) POST_WRITE(list, size);
+ if (size && res > 0) {
+ if (list) POST_WRITE(list, res);
}
}
} else if (op == iocb_cmd_pread && buf && len) {
POST_WRITE(buf, len);
} else if (op == iocb_cmd_pwritev) {
- __sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf;
+ __sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
for (uptr v = 0; v < len; v++)
- PRE_READ(iovec[i].iov_base, iovec[i].iov_len);
+ PRE_READ(iovec[v].iov_base, iovec[v].iov_len);
} else if (op == iocb_cmd_preadv) {
- __sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf;
+ __sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
for (uptr v = 0; v < len; v++)
- POST_WRITE(iovec[i].iov_base, iovec[i].iov_len);
+ POST_WRITE(iovec[v].iov_base, iovec[v].iov_len);
}
// See comment in io_getevents.
COMMON_SYSCALL_RELEASE(data);
}
}
+#if !SANITIZER_ANDROID
PRE_SYSCALL(msgctl)(long msqid, long cmd, void *buf) {}
POST_SYSCALL(msgctl)(long res, long msqid, long cmd, void *buf) {
if (buf) POST_WRITE(buf, struct_msqid_ds_sz);
}
}
+#endif
PRE_SYSCALL(semget)(long key, long nsems, long semflg) {}
POST_SYSCALL(ni_syscall)(long res) {}
PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
-#if defined(__i386) || defined (__x86_64)
+#if !SANITIZER_ANDROID && \
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__))
if (data) {
if (request == ptrace_setregs) {
PRE_READ((void *)data, struct_user_regs_struct_sz);
}
POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {
-#if defined(__i386) || defined (__x86_64)
+#if !SANITIZER_ANDROID && \
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__))
if (res >= 0 && data) {
// Note that this is different from the interceptor in
// sanitizer_common_interceptors.inc.
+++ /dev/null
-//===-- sanitizer_coverage.cc ---------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Sanitizer Coverage.
-// This file implements run-time support for a poor man's coverage tool.
-//
-// Compiler instrumentation:
-// For every function F the compiler injects the following code:
-// if (*Guard) {
-// __sanitizer_cov(&F);
-// *Guard = 1;
-// }
-// It's fine to call __sanitizer_cov more than once for a given function.
-//
-// Run-time:
-// - __sanitizer_cov(pc): record that we've executed a given PC.
-// - __sanitizer_cov_dump: dump the coverage data to disk.
-// For every module of the current process that has coverage data
-// this will create a file module_name.PID.sancov. The file format is simple:
-// it's just a sorted sequence of 4-byte offsets in the module.
-//
-// Eventually, this coverage implementation should be obsoleted by a more
-// powerful general purpose Clang/LLVM coverage instrumentation.
-// Consider this implementation as prototype.
-//
-// FIXME: support (or at least test with) dlclose.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_common.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_mutex.h"
-#include "sanitizer_procmaps.h"
-#include "sanitizer_flags.h"
-
-struct CovData {
- BlockingMutex mu;
- InternalMmapVector<uptr> v;
-};
-
-static uptr cov_data_placeholder[sizeof(CovData) / sizeof(uptr)];
-COMPILER_CHECK(sizeof(cov_data_placeholder) >= sizeof(CovData));
-static CovData *cov_data = reinterpret_cast<CovData*>(cov_data_placeholder);
-
-namespace __sanitizer {
-
-// Simply add the pc into the vector under lock. If the function is called more
-// than once for a given PC it will be inserted multiple times, which is fine.
-static void CovAdd(uptr pc) {
- BlockingMutexLock lock(&cov_data->mu);
- cov_data->v.push_back(pc);
-}
-
-static inline bool CompareLess(const uptr &a, const uptr &b) {
- return a < b;
-}
-
-// Dump the coverage on disk.
-void CovDump() {
-#if !SANITIZER_WINDOWS
- BlockingMutexLock lock(&cov_data->mu);
- InternalMmapVector<uptr> &v = cov_data->v;
- InternalSort(&v, v.size(), CompareLess);
- InternalMmapVector<u32> offsets(v.size());
- const uptr *vb = v.data();
- const uptr *ve = vb + v.size();
- MemoryMappingLayout proc_maps(/*cache_enabled*/false);
- uptr mb, me, off, prot;
- InternalScopedBuffer<char> module(4096);
- InternalScopedBuffer<char> path(4096 * 2);
- for (int i = 0;
- proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot);
- i++) {
- if ((prot & MemoryMappingLayout::kProtectionExecute) == 0)
- continue;
- if (vb >= ve) break;
- if (mb <= *vb && *vb < me) {
- offsets.clear();
- const uptr *old_vb = vb;
- CHECK_LE(off, *vb);
- for (; vb < ve && *vb < me; vb++) {
- uptr diff = *vb - (i ? mb : 0) + off;
- CHECK_LE(diff, 0xffffffffU);
- offsets.push_back(static_cast<u32>(diff));
- }
- char *module_name = StripModuleName(module.data());
- internal_snprintf((char *)path.data(), path.size(), "%s.%zd.sancov",
- module_name, internal_getpid());
- InternalFree(module_name);
- uptr fd = OpenFile(path.data(), true);
- internal_write(fd, offsets.data(), offsets.size() * sizeof(u32));
- internal_close(fd);
- if (common_flags()->verbosity)
- Report(" CovDump: %s: %zd PCs written\n", path.data(), vb - old_vb);
- }
- }
-#endif // !SANITIZER_WINDOWS
-}
-
-} // namespace __sanitizer
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(void *pc) {
- CovAdd(reinterpret_cast<uptr>(pc));
-}
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
-} // extern "C"
--- /dev/null
+//===-- sanitizer_coverage.cc ---------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Sanitizer Coverage.
+// This file implements run-time support for a poor man's coverage tool.
+//
+// Compiler instrumentation:
+// For every interesting basic block the compiler injects the following code:
+// if (Guard < 0) {
+// __sanitizer_cov(&Guard);
+// }
+// At the module start up time __sanitizer_cov_module_init sets the guards
+// to consecutive negative numbers (-1, -2, -3, ...).
+// It's fine to call __sanitizer_cov more than once for a given block.
+//
+// Run-time:
+// - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC).
+// and atomically set Guard to -Guard.
+// - __sanitizer_cov_dump: dump the coverage data to disk.
+// For every module of the current process that has coverage data
+// this will create a file module_name.PID.sancov.
+//
+// The file format is simple: the first 8 bytes is the magic,
+// one of 0xC0BFFFFFFFFFFF64 and 0xC0BFFFFFFFFFFF32. The last byte of the
+// magic defines the size of the following offsets.
+// The rest of the data is the offsets in the module.
+//
+// Eventually, this coverage implementation should be obsoleted by a more
+// powerful general purpose Clang/LLVM coverage instrumentation.
+// Consider this implementation as prototype.
+//
+// FIXME: support (or at least test with) dlclose.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
+#include "sanitizer_flags.h"
+
+static const u64 kMagic64 = 0xC0BFFFFFFFFFFF64ULL;
+static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL;
+
+static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
+
+static atomic_uintptr_t coverage_counter;
+static atomic_uintptr_t caller_callee_counter;
+
+static void ResetGlobalCounters() {
+ return atomic_store(&coverage_counter, 0, memory_order_relaxed);
+ return atomic_store(&caller_callee_counter, 0, memory_order_relaxed);
+}
+
+// pc_array is the array containing the covered PCs.
+// To make the pc_array thread- and async-signal-safe it has to be large enough.
+// 128M counters "ought to be enough for anybody" (4M on 32-bit).
+
+// With coverage_direct=1 in ASAN_OPTIONS, pc_array memory is mapped to a file.
+// In this mode, __sanitizer_cov_dump does nothing, and CovUpdateMapping()
+// dump current memory layout to another file.
+
+static bool cov_sandboxed = false;
+static fd_t cov_fd = kInvalidFd;
+static unsigned int cov_max_block_size = 0;
+static bool coverage_enabled = false;
+static const char *coverage_dir;
+
+namespace __sanitizer {
+
+class CoverageData {
+ public:
+ void Init();
+ void Enable();
+ void Disable();
+ void ReInit();
+ void BeforeFork();
+ void AfterFork(int child_pid);
+ void Extend(uptr npcs);
+ void Add(uptr pc, u32 *guard);
+ void IndirCall(uptr caller, uptr callee, uptr callee_cache[],
+ uptr cache_size);
+ void DumpCallerCalleePairs();
+ void DumpTrace();
+ void DumpAsBitSet();
+ void DumpCounters();
+ void DumpOffsets();
+ void DumpAll();
+
+ ALWAYS_INLINE
+ void TraceBasicBlock(s32 *id);
+
+ void InitializeGuardArray(s32 *guards);
+ void InitializeGuards(s32 *guards, uptr n, const char *module_name,
+ uptr caller_pc);
+ void InitializeCounters(u8 *counters, uptr n);
+ void ReinitializeGuards();
+ uptr GetNumberOf8bitCounters();
+ uptr Update8bitCounterBitsetAndClearCounters(u8 *bitset);
+
+ uptr *data();
+ uptr size();
+
+ private:
+ void DirectOpen();
+ void UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end);
+
+ // Maximal size pc array may ever grow.
+ // We MmapNoReserve this space to ensure that the array is contiguous.
+ static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(
+ 1 << (SANITIZER_ANDROID ? 24 : (SANITIZER_WINDOWS ? 27 : 26)),
+ 1 << 27);
+ // The amount file mapping for the pc array is grown by.
+ static const uptr kPcArrayMmapSize = 64 * 1024;
+
+ // pc_array is allocated with MmapNoReserveOrDie and so it uses only as
+ // much RAM as it really needs.
+ uptr *pc_array;
+ // Index of the first available pc_array slot.
+ atomic_uintptr_t pc_array_index;
+ // Array size.
+ atomic_uintptr_t pc_array_size;
+ // Current file mapped size of the pc array.
+ uptr pc_array_mapped_size;
+ // Descriptor of the file mapped pc array.
+ fd_t pc_fd;
+
+ // Vector of coverage guard arrays, protected by mu.
+ InternalMmapVectorNoCtor<s32*> guard_array_vec;
+
+ struct NamedPcRange {
+ const char *copied_module_name;
+ uptr beg, end; // elements [beg,end) in pc_array.
+ };
+
+ // Vector of module and compilation unit pc ranges.
+ InternalMmapVectorNoCtor<NamedPcRange> comp_unit_name_vec;
+ InternalMmapVectorNoCtor<NamedPcRange> module_name_vec;
+
+ struct CounterAndSize {
+ u8 *counters;
+ uptr n;
+ };
+
+ InternalMmapVectorNoCtor<CounterAndSize> counters_vec;
+ uptr num_8bit_counters;
+
+ // Caller-Callee (cc) array, size and current index.
+ static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24);
+ uptr **cc_array;
+ atomic_uintptr_t cc_array_index;
+ atomic_uintptr_t cc_array_size;
+
+ // Tracing event array, size and current pointer.
+ // We record all events (basic block entries) in a global buffer of u32
+ // values. Each such value is the index in pc_array.
+ // So far the tracing is highly experimental:
+ // - not thread-safe;
+ // - does not support long traces;
+ // - not tuned for performance.
+ static const uptr kTrEventArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 30);
+ u32 *tr_event_array;
+ uptr tr_event_array_size;
+ u32 *tr_event_pointer;
+ static const uptr kTrPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27);
+
+ StaticSpinMutex mu;
+};
+
+static CoverageData coverage_data;
+
+void CovUpdateMapping(const char *path, uptr caller_pc = 0);
+
+void CoverageData::DirectOpen() {
+ InternalScopedString path(kMaxPathLength);
+ internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw",
+ coverage_dir, internal_getpid());
+ pc_fd = OpenFile(path.data(), RdWr);
+ if (pc_fd == kInvalidFd) {
+ Report("Coverage: failed to open %s for reading/writing\n", path.data());
+ Die();
+ }
+
+ pc_array_mapped_size = 0;
+ CovUpdateMapping(coverage_dir);
+}
+
+void CoverageData::Init() {
+ pc_fd = kInvalidFd;
+}
+
+void CoverageData::Enable() {
+ if (pc_array)
+ return;
+ pc_array = reinterpret_cast<uptr *>(
+ MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit"));
+ atomic_store(&pc_array_index, 0, memory_order_relaxed);
+ if (common_flags()->coverage_direct) {
+ atomic_store(&pc_array_size, 0, memory_order_relaxed);
+ } else {
+ atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed);
+ }
+
+ cc_array = reinterpret_cast<uptr **>(MmapNoReserveOrDie(
+ sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array"));
+ atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed);
+ atomic_store(&cc_array_index, 0, memory_order_relaxed);
+
+ // Allocate tr_event_array with a guard page at the end.
+ tr_event_array = reinterpret_cast<u32 *>(MmapNoReserveOrDie(
+ sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(),
+ "CovInit::tr_event_array"));
+ MprotectNoAccess(
+ reinterpret_cast<uptr>(&tr_event_array[kTrEventArrayMaxSize]),
+ GetMmapGranularity());
+ tr_event_array_size = kTrEventArrayMaxSize;
+ tr_event_pointer = tr_event_array;
+
+ num_8bit_counters = 0;
+}
+
+void CoverageData::InitializeGuardArray(s32 *guards) {
+ Enable(); // Make sure coverage is enabled at this point.
+ s32 n = guards[0];
+ for (s32 j = 1; j <= n; j++) {
+ uptr idx = atomic_load_relaxed(&pc_array_index);
+ atomic_store_relaxed(&pc_array_index, idx + 1);
+ guards[j] = -static_cast<s32>(idx + 1);
+ }
+}
+
+void CoverageData::Disable() {
+ if (pc_array) {
+ UnmapOrDie(pc_array, sizeof(uptr) * kPcArrayMaxSize);
+ pc_array = nullptr;
+ }
+ if (cc_array) {
+ UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize);
+ cc_array = nullptr;
+ }
+ if (tr_event_array) {
+ UnmapOrDie(tr_event_array,
+ sizeof(tr_event_array[0]) * kTrEventArrayMaxSize +
+ GetMmapGranularity());
+ tr_event_array = nullptr;
+ tr_event_pointer = nullptr;
+ }
+ if (pc_fd != kInvalidFd) {
+ CloseFile(pc_fd);
+ pc_fd = kInvalidFd;
+ }
+}
+
+void CoverageData::ReinitializeGuards() {
+ // Assuming single thread.
+ atomic_store(&pc_array_index, 0, memory_order_relaxed);
+ for (uptr i = 0; i < guard_array_vec.size(); i++)
+ InitializeGuardArray(guard_array_vec[i]);
+}
+
+void CoverageData::ReInit() {
+ Disable();
+ if (coverage_enabled) {
+ if (common_flags()->coverage_direct) {
+ // In memory-mapped mode we must extend the new file to the known array
+ // size.
+ uptr size = atomic_load(&pc_array_size, memory_order_relaxed);
+ uptr npcs = size / sizeof(uptr);
+ Enable();
+ if (size) Extend(npcs);
+ if (coverage_enabled) CovUpdateMapping(coverage_dir);
+ } else {
+ Enable();
+ }
+ }
+ // Re-initialize the guards.
+ // We are single-threaded now, no need to grab any lock.
+ CHECK_EQ(atomic_load(&pc_array_index, memory_order_relaxed), 0);
+ ReinitializeGuards();
+}
+
+void CoverageData::BeforeFork() {
+ mu.Lock();
+}
+
+void CoverageData::AfterFork(int child_pid) {
+ // We are single-threaded so it's OK to release the lock early.
+ mu.Unlock();
+ if (child_pid == 0) ReInit();
+}
+
+// Extend coverage PC array to fit additional npcs elements.
+void CoverageData::Extend(uptr npcs) {
+ if (!common_flags()->coverage_direct) return;
+ SpinMutexLock l(&mu);
+
+ uptr size = atomic_load(&pc_array_size, memory_order_relaxed);
+ size += npcs * sizeof(uptr);
+
+ if (coverage_enabled && size > pc_array_mapped_size) {
+ if (pc_fd == kInvalidFd) DirectOpen();
+ CHECK_NE(pc_fd, kInvalidFd);
+
+ uptr new_mapped_size = pc_array_mapped_size;
+ while (size > new_mapped_size) new_mapped_size += kPcArrayMmapSize;
+ CHECK_LE(new_mapped_size, sizeof(uptr) * kPcArrayMaxSize);
+
+ // Extend the file and map the new space at the end of pc_array.
+ uptr res = internal_ftruncate(pc_fd, new_mapped_size);
+ int err;
+ if (internal_iserror(res, &err)) {
+ Printf("failed to extend raw coverage file: %d\n", err);
+ Die();
+ }
+
+ uptr next_map_base = ((uptr)pc_array) + pc_array_mapped_size;
+ void *p = MapWritableFileToMemory((void *)next_map_base,
+ new_mapped_size - pc_array_mapped_size,
+ pc_fd, pc_array_mapped_size);
+ CHECK_EQ((uptr)p, next_map_base);
+ pc_array_mapped_size = new_mapped_size;
+ }
+
+ atomic_store(&pc_array_size, size, memory_order_release);
+}
+
+void CoverageData::InitializeCounters(u8 *counters, uptr n) {
+ if (!counters) return;
+ CHECK_EQ(reinterpret_cast<uptr>(counters) % 16, 0);
+ n = RoundUpTo(n, 16); // The compiler must ensure that counters is 16-aligned.
+ SpinMutexLock l(&mu);
+ counters_vec.push_back({counters, n});
+ num_8bit_counters += n;
+}
+
+void CoverageData::UpdateModuleNameVec(uptr caller_pc, uptr range_beg,
+ uptr range_end) {
+ auto sym = Symbolizer::GetOrInit();
+ if (!sym)
+ return;
+ const char *module_name = sym->GetModuleNameForPc(caller_pc);
+ if (!module_name) return;
+ if (module_name_vec.empty() ||
+ module_name_vec.back().copied_module_name != module_name)
+ module_name_vec.push_back({module_name, range_beg, range_end});
+ else
+ module_name_vec.back().end = range_end;
+}
+
+void CoverageData::InitializeGuards(s32 *guards, uptr n,
+ const char *comp_unit_name,
+ uptr caller_pc) {
+ // The array 'guards' has n+1 elements, we use the element zero
+ // to store 'n'.
+ CHECK_LT(n, 1 << 30);
+ guards[0] = static_cast<s32>(n);
+ InitializeGuardArray(guards);
+ SpinMutexLock l(&mu);
+ uptr range_end = atomic_load(&pc_array_index, memory_order_relaxed);
+ uptr range_beg = range_end - n;
+ comp_unit_name_vec.push_back({comp_unit_name, range_beg, range_end});
+ guard_array_vec.push_back(guards);
+ UpdateModuleNameVec(caller_pc, range_beg, range_end);
+}
+
+static const uptr kBundleCounterBits = 16;
+
+// When coverage_order_pcs==true and SANITIZER_WORDSIZE==64
+// we insert the global counter into the first 16 bits of the PC.
+uptr BundlePcAndCounter(uptr pc, uptr counter) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return pc;
+ static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1;
+ if (counter > kMaxCounter)
+ counter = kMaxCounter;
+ CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits));
+ return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits));
+}
+
+uptr UnbundlePc(uptr bundle) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return bundle;
+ return (bundle << kBundleCounterBits) >> kBundleCounterBits;
+}
+
+uptr UnbundleCounter(uptr bundle) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return 0;
+ return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits);
+}
+
+// If guard is negative, atomically set it to -guard and store the PC in
+// pc_array.
+void CoverageData::Add(uptr pc, u32 *guard) {
+ atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard);
+ s32 guard_value = atomic_load(atomic_guard, memory_order_relaxed);
+ if (guard_value >= 0) return;
+
+ atomic_store(atomic_guard, -guard_value, memory_order_relaxed);
+ if (!pc_array) return;
+
+ uptr idx = -guard_value - 1;
+ if (idx >= atomic_load(&pc_array_index, memory_order_acquire))
+ return; // May happen after fork when pc_array_index becomes 0.
+ CHECK_LT(idx * sizeof(uptr),
+ atomic_load(&pc_array_size, memory_order_acquire));
+ uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+ pc_array[idx] = BundlePcAndCounter(pc, counter);
+}
+
+// Registers a pair caller=>callee.
+// When a given caller is seen for the first time, the callee_cache is added
+// to the global array cc_array, callee_cache[0] is set to caller and
+// callee_cache[1] is set to cache_size.
+// Then we are trying to add callee to callee_cache [2,cache_size) if it is
+// not there yet.
+// If the cache is full we drop the callee (may want to fix this later).
+void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[],
+ uptr cache_size) {
+ if (!cc_array) return;
+ atomic_uintptr_t *atomic_callee_cache =
+ reinterpret_cast<atomic_uintptr_t *>(callee_cache);
+ uptr zero = 0;
+ if (atomic_compare_exchange_strong(&atomic_callee_cache[0], &zero, caller,
+ memory_order_seq_cst)) {
+ uptr idx = atomic_fetch_add(&cc_array_index, 1, memory_order_relaxed);
+ CHECK_LT(idx * sizeof(uptr),
+ atomic_load(&cc_array_size, memory_order_acquire));
+ callee_cache[1] = cache_size;
+ cc_array[idx] = callee_cache;
+ }
+ CHECK_EQ(atomic_load(&atomic_callee_cache[0], memory_order_relaxed), caller);
+ for (uptr i = 2; i < cache_size; i++) {
+ uptr was = 0;
+ if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee,
+ memory_order_seq_cst)) {
+ atomic_fetch_add(&caller_callee_counter, 1, memory_order_relaxed);
+ return;
+ }
+ if (was == callee) // Already have this callee.
+ return;
+ }
+}
+
+uptr CoverageData::GetNumberOf8bitCounters() {
+ return num_8bit_counters;
+}
+
+// Map every 8bit counter to a 8-bit bitset and clear the counter.
+uptr CoverageData::Update8bitCounterBitsetAndClearCounters(u8 *bitset) {
+ uptr num_new_bits = 0;
+ uptr cur = 0;
+ // For better speed we map 8 counters to 8 bytes of bitset at once.
+ static const uptr kBatchSize = 8;
+ CHECK_EQ(reinterpret_cast<uptr>(bitset) % kBatchSize, 0);
+ for (uptr i = 0, len = counters_vec.size(); i < len; i++) {
+ u8 *c = counters_vec[i].counters;
+ uptr n = counters_vec[i].n;
+ CHECK_EQ(n % 16, 0);
+ CHECK_EQ(cur % kBatchSize, 0);
+ CHECK_EQ(reinterpret_cast<uptr>(c) % kBatchSize, 0);
+ if (!bitset) {
+ internal_bzero_aligned16(c, n);
+ cur += n;
+ continue;
+ }
+ for (uptr j = 0; j < n; j += kBatchSize, cur += kBatchSize) {
+ CHECK_LT(cur, num_8bit_counters);
+ u64 *pc64 = reinterpret_cast<u64*>(c + j);
+ u64 *pb64 = reinterpret_cast<u64*>(bitset + cur);
+ u64 c64 = *pc64;
+ u64 old_bits_64 = *pb64;
+ u64 new_bits_64 = old_bits_64;
+ if (c64) {
+ *pc64 = 0;
+ for (uptr k = 0; k < kBatchSize; k++) {
+ u64 x = (c64 >> (8 * k)) & 0xff;
+ if (x) {
+ u64 bit = 0;
+ /**/ if (x >= 128) bit = 128;
+ else if (x >= 32) bit = 64;
+ else if (x >= 16) bit = 32;
+ else if (x >= 8) bit = 16;
+ else if (x >= 4) bit = 8;
+ else if (x >= 3) bit = 4;
+ else if (x >= 2) bit = 2;
+ else if (x >= 1) bit = 1;
+ u64 mask = bit << (8 * k);
+ if (!(new_bits_64 & mask)) {
+ num_new_bits++;
+ new_bits_64 |= mask;
+ }
+ }
+ }
+ *pb64 = new_bits_64;
+ }
+ }
+ }
+ CHECK_EQ(cur, num_8bit_counters);
+ return num_new_bits;
+}
+
+uptr *CoverageData::data() {
+ return pc_array;
+}
+
+uptr CoverageData::size() {
+ return atomic_load(&pc_array_index, memory_order_relaxed);
+}
+
+// Block layout for packed file format: header, followed by module name (no
+// trailing zero), followed by data blob.
+struct CovHeader {
+ int pid;
+ unsigned int module_name_length;
+ unsigned int data_length;
+};
+
+static void CovWritePacked(int pid, const char *module, const void *blob,
+ unsigned int blob_size) {
+ if (cov_fd == kInvalidFd) return;
+ unsigned module_name_length = internal_strlen(module);
+ CovHeader header = {pid, module_name_length, blob_size};
+
+ if (cov_max_block_size == 0) {
+ // Writing to a file. Just go ahead.
+ WriteToFile(cov_fd, &header, sizeof(header));
+ WriteToFile(cov_fd, module, module_name_length);
+ WriteToFile(cov_fd, blob, blob_size);
+ } else {
+ // Writing to a socket. We want to split the data into appropriately sized
+ // blocks.
+ InternalScopedBuffer<char> block(cov_max_block_size);
+ CHECK_EQ((uptr)block.data(), (uptr)(CovHeader *)block.data());
+ uptr header_size_with_module = sizeof(header) + module_name_length;
+ CHECK_LT(header_size_with_module, cov_max_block_size);
+ unsigned int max_payload_size =
+ cov_max_block_size - header_size_with_module;
+ char *block_pos = block.data();
+ internal_memcpy(block_pos, &header, sizeof(header));
+ block_pos += sizeof(header);
+ internal_memcpy(block_pos, module, module_name_length);
+ block_pos += module_name_length;
+ char *block_data_begin = block_pos;
+ const char *blob_pos = (const char *)blob;
+ while (blob_size > 0) {
+ unsigned int payload_size = Min(blob_size, max_payload_size);
+ blob_size -= payload_size;
+ internal_memcpy(block_data_begin, blob_pos, payload_size);
+ blob_pos += payload_size;
+ ((CovHeader *)block.data())->data_length = payload_size;
+ WriteToFile(cov_fd, block.data(), header_size_with_module + payload_size);
+ }
+ }
+}
+
+// If packed = false: <name>.<pid>.<sancov> (name = module name).
+// If packed = true and name == 0: <pid>.<sancov>.<packed>.
+// If packed = true and name != 0: <name>.<sancov>.<packed> (name is
+// user-supplied).
+static fd_t CovOpenFile(InternalScopedString *path, bool packed,
+ const char *name, const char *extension = "sancov") {
+ path->clear();
+ if (!packed) {
+ CHECK(name);
+ path->append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(),
+ extension);
+ } else {
+ if (!name)
+ path->append("%s/%zd.%s.packed", coverage_dir, internal_getpid(),
+ extension);
+ else
+ path->append("%s/%s.%s.packed", coverage_dir, name, extension);
+ }
+ error_t err;
+ fd_t fd = OpenFile(path->data(), WrOnly, &err);
+ if (fd == kInvalidFd)
+ Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n",
+ path->data(), err);
+ return fd;
+}
+
+// Dump trace PCs and trace events into two separate files.
+void CoverageData::DumpTrace() {
+ uptr max_idx = tr_event_pointer - tr_event_array;
+ if (!max_idx) return;
+ auto sym = Symbolizer::GetOrInit();
+ if (!sym)
+ return;
+ InternalScopedString out(32 << 20);
+ for (uptr i = 0, n = size(); i < n; i++) {
+ const char *module_name = "<unknown>";
+ uptr module_address = 0;
+ sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name,
+ &module_address);
+ out.append("%s 0x%zx\n", module_name, module_address);
+ }
+ InternalScopedString path(kMaxPathLength);
+ fd_t fd = CovOpenFile(&path, false, "trace-points");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, out.data(), out.length());
+ CloseFile(fd);
+
+ fd = CovOpenFile(&path, false, "trace-compunits");
+ if (fd == kInvalidFd) return;
+ out.clear();
+ for (uptr i = 0; i < comp_unit_name_vec.size(); i++)
+ out.append("%s\n", comp_unit_name_vec[i].copied_module_name);
+ WriteToFile(fd, out.data(), out.length());
+ CloseFile(fd);
+
+ fd = CovOpenFile(&path, false, "trace-events");
+ if (fd == kInvalidFd) return;
+ uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]);
+ u8 *event_bytes = reinterpret_cast<u8*>(tr_event_array);
+ // The trace file could be huge, and may not be written with a single syscall.
+ while (bytes_to_write) {
+ uptr actually_written;
+ if (WriteToFile(fd, event_bytes, bytes_to_write, &actually_written) &&
+ actually_written <= bytes_to_write) {
+ bytes_to_write -= actually_written;
+ event_bytes += actually_written;
+ } else {
+ break;
+ }
+ }
+ CloseFile(fd);
+ VReport(1, " CovDump: Trace: %zd PCs written\n", size());
+ VReport(1, " CovDump: Trace: %zd Events written\n", max_idx);
+}
+
+// This function dumps the caller=>callee pairs into a file as a sequence of
+// lines like "module_name offset".
+void CoverageData::DumpCallerCalleePairs() {
+ uptr max_idx = atomic_load(&cc_array_index, memory_order_relaxed);
+ if (!max_idx) return;
+ auto sym = Symbolizer::GetOrInit();
+ if (!sym)
+ return;
+ InternalScopedString out(32 << 20);
+ uptr total = 0;
+ for (uptr i = 0; i < max_idx; i++) {
+ uptr *cc_cache = cc_array[i];
+ CHECK(cc_cache);
+ uptr caller = cc_cache[0];
+ uptr n_callees = cc_cache[1];
+ const char *caller_module_name = "<unknown>";
+ uptr caller_module_address = 0;
+ sym->GetModuleNameAndOffsetForPC(caller, &caller_module_name,
+ &caller_module_address);
+ for (uptr j = 2; j < n_callees; j++) {
+ uptr callee = cc_cache[j];
+ if (!callee) break;
+ total++;
+ const char *callee_module_name = "<unknown>";
+ uptr callee_module_address = 0;
+ sym->GetModuleNameAndOffsetForPC(callee, &callee_module_name,
+ &callee_module_address);
+ out.append("%s 0x%zx\n%s 0x%zx\n", caller_module_name,
+ caller_module_address, callee_module_name,
+ callee_module_address);
+ }
+ }
+ InternalScopedString path(kMaxPathLength);
+ fd_t fd = CovOpenFile(&path, false, "caller-callee");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, out.data(), out.length());
+ CloseFile(fd);
+ VReport(1, " CovDump: %zd caller-callee pairs written\n", total);
+}
+
+// Record the current PC into the event buffer.
+// Every event is a u32 value (index in tr_pc_array_index) so we compute
+// it once and then cache in the provided 'cache' storage.
+//
+// This function will eventually be inlined by the compiler.
+void CoverageData::TraceBasicBlock(s32 *id) {
+ // Will trap here if
+ // 1. coverage is not enabled at run-time.
+ // 2. The array tr_event_array is full.
+ *tr_event_pointer = static_cast<u32>(*id - 1);
+ tr_event_pointer++;
+}
+
+void CoverageData::DumpCounters() {
+ if (!common_flags()->coverage_counters) return;
+ uptr n = coverage_data.GetNumberOf8bitCounters();
+ if (!n) return;
+ InternalScopedBuffer<u8> bitset(n);
+ coverage_data.Update8bitCounterBitsetAndClearCounters(bitset.data());
+ InternalScopedString path(kMaxPathLength);
+
+ for (uptr m = 0; m < module_name_vec.size(); m++) {
+ auto r = module_name_vec[m];
+ CHECK(r.copied_module_name);
+ CHECK_LE(r.beg, r.end);
+ CHECK_LE(r.end, size());
+ const char *base_name = StripModuleName(r.copied_module_name);
+ fd_t fd =
+ CovOpenFile(&path, /* packed */ false, base_name, "counters-sancov");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, bitset.data() + r.beg, r.end - r.beg);
+ CloseFile(fd);
+ VReport(1, " CovDump: %zd counters written for '%s'\n", r.end - r.beg,
+ base_name);
+ }
+}
+
+void CoverageData::DumpAsBitSet() {
+ if (!common_flags()->coverage_bitset) return;
+ if (!size()) return;
+ InternalScopedBuffer<char> out(size());
+ InternalScopedString path(kMaxPathLength);
+ for (uptr m = 0; m < module_name_vec.size(); m++) {
+ uptr n_set_bits = 0;
+ auto r = module_name_vec[m];
+ CHECK(r.copied_module_name);
+ CHECK_LE(r.beg, r.end);
+ CHECK_LE(r.end, size());
+ for (uptr i = r.beg; i < r.end; i++) {
+ uptr pc = UnbundlePc(pc_array[i]);
+ out[i] = pc ? '1' : '0';
+ if (pc)
+ n_set_bits++;
+ }
+ const char *base_name = StripModuleName(r.copied_module_name);
+ fd_t fd = CovOpenFile(&path, /* packed */false, base_name, "bitset-sancov");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, out.data() + r.beg, r.end - r.beg);
+ CloseFile(fd);
+ VReport(1,
+ " CovDump: bitset of %zd bits written for '%s', %zd bits are set\n",
+ r.end - r.beg, base_name, n_set_bits);
+ }
+}
+
+void CoverageData::DumpOffsets() {
+ auto sym = Symbolizer::GetOrInit();
+ if (!common_flags()->coverage_pcs) return;
+ CHECK_NE(sym, nullptr);
+ InternalMmapVector<uptr> offsets(0);
+ InternalScopedString path(kMaxPathLength);
+ for (uptr m = 0; m < module_name_vec.size(); m++) {
+ offsets.clear();
+ uptr num_words_for_magic = SANITIZER_WORDSIZE == 64 ? 1 : 2;
+ for (uptr i = 0; i < num_words_for_magic; i++)
+ offsets.push_back(0);
+ auto r = module_name_vec[m];
+ CHECK(r.copied_module_name);
+ CHECK_LE(r.beg, r.end);
+ CHECK_LE(r.end, size());
+ for (uptr i = r.beg; i < r.end; i++) {
+ uptr pc = UnbundlePc(pc_array[i]);
+ uptr counter = UnbundleCounter(pc_array[i]);
+ if (!pc) continue; // Not visited.
+ uptr offset = 0;
+ sym->GetModuleNameAndOffsetForPC(pc, nullptr, &offset);
+ offsets.push_back(BundlePcAndCounter(offset, counter));
+ }
+
+ CHECK_GE(offsets.size(), num_words_for_magic);
+ SortArray(offsets.data(), offsets.size());
+ for (uptr i = 0; i < offsets.size(); i++)
+ offsets[i] = UnbundlePc(offsets[i]);
+
+ uptr num_offsets = offsets.size() - num_words_for_magic;
+ u64 *magic_p = reinterpret_cast<u64*>(offsets.data());
+ CHECK_EQ(*magic_p, 0ULL);
+ // FIXME: we may want to write 32-bit offsets even in 64-mode
+ // if all the offsets are small enough.
+ *magic_p = SANITIZER_WORDSIZE == 64 ? kMagic64 : kMagic32;
+
+ const char *module_name = StripModuleName(r.copied_module_name);
+ if (cov_sandboxed) {
+ if (cov_fd != kInvalidFd) {
+ CovWritePacked(internal_getpid(), module_name, offsets.data(),
+ offsets.size() * sizeof(offsets[0]));
+ VReport(1, " CovDump: %zd PCs written to packed file\n", num_offsets);
+ }
+ } else {
+ // One file per module per process.
+ fd_t fd = CovOpenFile(&path, false /* packed */, module_name);
+ if (fd == kInvalidFd) continue;
+ WriteToFile(fd, offsets.data(), offsets.size() * sizeof(offsets[0]));
+ CloseFile(fd);
+ VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), num_offsets);
+ }
+ }
+ if (cov_fd != kInvalidFd)
+ CloseFile(cov_fd);
+}
+
+void CoverageData::DumpAll() {
+ if (!coverage_enabled || common_flags()->coverage_direct) return;
+ if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
+ return;
+ DumpAsBitSet();
+ DumpCounters();
+ DumpTrace();
+ DumpOffsets();
+ DumpCallerCalleePairs();
+}
+
+void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
+ if (!args) return;
+ if (!coverage_enabled) return;
+ cov_sandboxed = args->coverage_sandboxed;
+ if (!cov_sandboxed) return;
+ cov_max_block_size = args->coverage_max_block_size;
+ if (args->coverage_fd >= 0) {
+ cov_fd = (fd_t)args->coverage_fd;
+ } else {
+ InternalScopedString path(kMaxPathLength);
+ // Pre-open the file now. The sandbox won't allow us to do it later.
+ cov_fd = CovOpenFile(&path, true /* packed */, nullptr);
+ }
+}
+
+fd_t MaybeOpenCovFile(const char *name) {
+ CHECK(name);
+ if (!coverage_enabled) return kInvalidFd;
+ InternalScopedString path(kMaxPathLength);
+ return CovOpenFile(&path, true /* packed */, name);
+}
+
+void CovBeforeFork() {
+ coverage_data.BeforeFork();
+}
+
+void CovAfterFork(int child_pid) {
+ coverage_data.AfterFork(child_pid);
+}
+
+static void MaybeDumpCoverage() {
+ if (common_flags()->coverage)
+ __sanitizer_cov_dump();
+}
+
+void InitializeCoverage(bool enabled, const char *dir) {
+ if (coverage_enabled)
+ return; // May happen if two sanitizer enable coverage in the same process.
+ coverage_enabled = enabled;
+ coverage_dir = dir;
+ coverage_data.Init();
+ if (enabled) coverage_data.Enable();
+ if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump);
+ AddDieCallback(MaybeDumpCoverage);
+}
+
+void ReInitializeCoverage(bool enabled, const char *dir) {
+ coverage_enabled = enabled;
+ coverage_dir = dir;
+ coverage_data.ReInit();
+}
+
+void CoverageUpdateMapping() {
+ if (coverage_enabled)
+ CovUpdateMapping(coverage_dir);
+}
+
+} // namespace __sanitizer
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) {
+ coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
+ guard);
+}
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) {
+ atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard);
+ if (static_cast<s32>(
+ __sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) < 0)
+ __sanitizer_cov(guard);
+}
+SANITIZER_INTERFACE_ATTRIBUTE void
+__sanitizer_cov_indir_call16(uptr callee, uptr callee_cache16[]) {
+ coverage_data.IndirCall(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
+ callee, callee_cache16, 16);
+}
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
+ coverage_enabled = true;
+ coverage_dir = common_flags()->coverage_dir;
+ coverage_data.Init();
+}
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
+ coverage_data.DumpAll();
+}
+SANITIZER_INTERFACE_ATTRIBUTE void
+__sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters,
+ const char *comp_unit_name) {
+ coverage_data.InitializeGuards(guards, npcs, comp_unit_name, GET_CALLER_PC());
+ coverage_data.InitializeCounters(counters, npcs);
+ if (!common_flags()->coverage_direct) return;
+ if (SANITIZER_ANDROID && coverage_enabled) {
+ // dlopen/dlclose interceptors do not work on Android, so we rely on
+ // Extend() calls to update .sancov.map.
+ CovUpdateMapping(coverage_dir, GET_CALLER_PC());
+ }
+ coverage_data.Extend(npcs);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+sptr __sanitizer_maybe_open_cov_file(const char *name) {
+ return (sptr)MaybeOpenCovFile(name);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_total_unique_coverage() {
+ return atomic_load(&coverage_counter, memory_order_relaxed);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_total_unique_caller_callee_pairs() {
+ return atomic_load(&caller_callee_counter, memory_order_relaxed);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov_trace_func_enter(s32 *id) {
+ coverage_data.TraceBasicBlock(id);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov_trace_basic_block(s32 *id) {
+ coverage_data.TraceBasicBlock(id);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_reset_coverage() {
+ ResetGlobalCounters();
+ coverage_data.ReinitializeGuards();
+ internal_bzero_aligned16(
+ coverage_data.data(),
+ RoundUpTo(coverage_data.size() * sizeof(coverage_data.data()[0]), 16));
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_coverage_guards(uptr **data) {
+ *data = coverage_data.data();
+ return coverage_data.size();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_number_of_counters() {
+ return coverage_data.GetNumberOf8bitCounters();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_update_counter_bitset_and_clear_counters(u8 *bitset) {
+ return coverage_data.Update8bitCounterBitsetAndClearCounters(bitset);
+}
+// Default empty implementations (weak). Users should redefine them.
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_cov_trace_cmp() {}
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_cov_trace_switch() {}
+} // extern "C"
--- /dev/null
+//===-- sanitizer_coverage_mapping.cc -------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Mmap-based implementation of sanitizer coverage.
+//
+// This is part of the implementation of code coverage that does not require
+// __sanitizer_cov_dump() call. Data is stored in 2 files per process.
+//
+// $pid.sancov.map describes process memory layout in the following text-based
+// format:
+// <pointer size in bits> // 1 line, 32 or 64
+// <mapping start> <mapping end> <base address> <dso name> // repeated
+// ...
+// Mapping lines are NOT sorted. This file is updated every time memory layout
+// is changed (i.e. in dlopen() and dlclose() interceptors).
+//
+// $pid.sancov.raw is a binary dump of PC values, sizeof(uptr) each. Again, not
+// sorted. This file is extended by 64Kb at a time and mapped into memory. It
+// contains one or more 0 words at the end, up to the next 64Kb aligned offset.
+//
+// To convert these 2 files to the usual .sancov format, run sancov.py rawunpack
+// $pid.sancov.raw.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+static const uptr kMaxTextSize = 64 * 1024;
+
+struct CachedMapping {
+ public:
+ bool NeedsUpdate(uptr pc) {
+ int new_pid = internal_getpid();
+ if (last_pid == new_pid && pc && pc >= last_range_start &&
+ pc < last_range_end)
+ return false;
+ last_pid = new_pid;
+ return true;
+ }
+
+ void SetModuleRange(uptr start, uptr end) {
+ last_range_start = start;
+ last_range_end = end;
+ }
+
+ private:
+ uptr last_range_start, last_range_end;
+ int last_pid;
+};
+
+static CachedMapping cached_mapping;
+static StaticSpinMutex mapping_mu;
+
+void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) {
+ if (!common_flags()->coverage_direct) return;
+
+ SpinMutexLock l(&mapping_mu);
+
+ if (!cached_mapping.NeedsUpdate(caller_pc))
+ return;
+
+ InternalScopedString text(kMaxTextSize);
+
+ {
+ InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules);
+ CHECK(modules.data());
+ int n_modules = GetListOfModules(modules.data(), kMaxNumberOfModules,
+ /* filter */ nullptr);
+
+ text.append("%d\n", sizeof(uptr) * 8);
+ for (int i = 0; i < n_modules; ++i) {
+ const char *module_name = StripModuleName(modules[i].full_name());
+ uptr base = modules[i].base_address();
+ for (auto iter = modules[i].ranges(); iter.hasNext();) {
+ const auto *range = iter.next();
+ if (range->executable) {
+ uptr start = range->beg;
+ uptr end = range->end;
+ text.append("%zx %zx %zx %s\n", start, end, base, module_name);
+ if (caller_pc && caller_pc >= start && caller_pc < end)
+ cached_mapping.SetModuleRange(start, end);
+ }
+ }
+ modules[i].clear();
+ }
+ }
+
+ error_t err;
+ InternalScopedString tmp_path(64 + internal_strlen(coverage_dir));
+ uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(),
+ "%s/%zd.sancov.map.tmp", coverage_dir,
+ internal_getpid());
+ CHECK_LE(res, tmp_path.size());
+ fd_t map_fd = OpenFile(tmp_path.data(), WrOnly, &err);
+ if (map_fd == kInvalidFd) {
+ Report("Coverage: failed to open %s for writing: %d\n", tmp_path.data(),
+ err);
+ Die();
+ }
+
+ if (!WriteToFile(map_fd, text.data(), text.length(), nullptr, &err)) {
+ Printf("sancov.map write failed: %d\n", err);
+ Die();
+ }
+ CloseFile(map_fd);
+
+ InternalScopedString path(64 + internal_strlen(coverage_dir));
+ res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map",
+ coverage_dir, internal_getpid());
+ CHECK_LE(res, path.size());
+ if (!RenameFile(tmp_path.data(), path.data(), &err)) {
+ Printf("sancov.map rename failed: %d\n", err);
+ Die();
+ }
+}
+
+} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_deadlock_detector.h ---------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer runtime.
+// The deadlock detector maintains a directed graph of lock acquisitions.
+// When a lock event happens, the detector checks if the locks already held by
+// the current thread are reachable from the newly acquired lock.
+//
+// The detector can handle only a fixed amount of simultaneously live locks
+// (a lock is alive if it has been locked at least once and has not been
+// destroyed). When the maximal number of locks is reached the entire graph
+// is flushed and the new lock epoch is started. The node ids from the old
+// epochs can not be used with any of the detector methods except for
+// nodeBelongsToCurrentEpoch().
+//
+// FIXME: this is work in progress, nothing really works yet.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_DEADLOCK_DETECTOR_H
+#define SANITIZER_DEADLOCK_DETECTOR_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_bvgraph.h"
+
+namespace __sanitizer {
+
+// Thread-local state for DeadlockDetector.
+// It contains the locks currently held by the owning thread.
+template <class BV>
+class DeadlockDetectorTLS {
+ public:
+ // No CTOR.
+ void clear() {
+ bv_.clear();
+ epoch_ = 0;
+ n_recursive_locks = 0;
+ n_all_locks_ = 0;
+ }
+
+ bool empty() const { return bv_.empty(); }
+
+ void ensureCurrentEpoch(uptr current_epoch) {
+ if (epoch_ == current_epoch) return;
+ bv_.clear();
+ epoch_ = current_epoch;
+ }
+
+ uptr getEpoch() const { return epoch_; }
+
+ // Returns true if this is the first (non-recursive) acquisition of this lock.
+ bool addLock(uptr lock_id, uptr current_epoch, u32 stk) {
+ // Printf("addLock: %zx %zx stk %u\n", lock_id, current_epoch, stk);
+ CHECK_EQ(epoch_, current_epoch);
+ if (!bv_.setBit(lock_id)) {
+ // The lock is already held by this thread, it must be recursive.
+ CHECK_LT(n_recursive_locks, ARRAY_SIZE(recursive_locks));
+ recursive_locks[n_recursive_locks++] = lock_id;
+ return false;
+ }
+ CHECK_LT(n_all_locks_, ARRAY_SIZE(all_locks_with_contexts_));
+ // lock_id < BV::kSize, can cast to a smaller int.
+ u32 lock_id_short = static_cast<u32>(lock_id);
+ LockWithContext l = {lock_id_short, stk};
+ all_locks_with_contexts_[n_all_locks_++] = l;
+ return true;
+ }
+
+ void removeLock(uptr lock_id) {
+ if (n_recursive_locks) {
+ for (sptr i = n_recursive_locks - 1; i >= 0; i--) {
+ if (recursive_locks[i] == lock_id) {
+ n_recursive_locks--;
+ Swap(recursive_locks[i], recursive_locks[n_recursive_locks]);
+ return;
+ }
+ }
+ }
+ // Printf("remLock: %zx %zx\n", lock_id, epoch_);
+ CHECK(bv_.clearBit(lock_id));
+ if (n_all_locks_) {
+ for (sptr i = n_all_locks_ - 1; i >= 0; i--) {
+ if (all_locks_with_contexts_[i].lock == static_cast<u32>(lock_id)) {
+ Swap(all_locks_with_contexts_[i],
+ all_locks_with_contexts_[n_all_locks_ - 1]);
+ n_all_locks_--;
+ break;
+ }
+ }
+ }
+ }
+
+ u32 findLockContext(uptr lock_id) {
+ for (uptr i = 0; i < n_all_locks_; i++)
+ if (all_locks_with_contexts_[i].lock == static_cast<u32>(lock_id))
+ return all_locks_with_contexts_[i].stk;
+ return 0;
+ }
+
+ const BV &getLocks(uptr current_epoch) const {
+ CHECK_EQ(epoch_, current_epoch);
+ return bv_;
+ }
+
+ uptr getNumLocks() const { return n_all_locks_; }
+ uptr getLock(uptr idx) const { return all_locks_with_contexts_[idx].lock; }
+
+ private:
+ BV bv_;
+ uptr epoch_;
+ uptr recursive_locks[64];
+ uptr n_recursive_locks;
+ struct LockWithContext {
+ u32 lock;
+ u32 stk;
+ };
+ LockWithContext all_locks_with_contexts_[64];
+ uptr n_all_locks_;
+};
+
+// DeadlockDetector.
+// For deadlock detection to work we need one global DeadlockDetector object
+// and one DeadlockDetectorTLS object per evey thread.
+// This class is not thread safe, all concurrent accesses should be guarded
+// by an external lock.
+// Most of the methods of this class are not thread-safe (i.e. should
+// be protected by an external lock) unless explicitly told otherwise.
+template <class BV>
+class DeadlockDetector {
+ public:
+ typedef BV BitVector;
+
+ uptr size() const { return g_.size(); }
+
+ // No CTOR.
+ void clear() {
+ current_epoch_ = 0;
+ available_nodes_.clear();
+ recycled_nodes_.clear();
+ g_.clear();
+ n_edges_ = 0;
+ }
+
+ // Allocate new deadlock detector node.
+ // If we are out of available nodes first try to recycle some.
+ // If there is nothing to recycle, flush the graph and increment the epoch.
+ // Associate 'data' (opaque user's object) with the new node.
+ uptr newNode(uptr data) {
+ if (!available_nodes_.empty())
+ return getAvailableNode(data);
+ if (!recycled_nodes_.empty()) {
+ // Printf("recycling: n_edges_ %zd\n", n_edges_);
+ for (sptr i = n_edges_ - 1; i >= 0; i--) {
+ if (recycled_nodes_.getBit(edges_[i].from) ||
+ recycled_nodes_.getBit(edges_[i].to)) {
+ Swap(edges_[i], edges_[n_edges_ - 1]);
+ n_edges_--;
+ }
+ }
+ CHECK(available_nodes_.empty());
+ // removeEdgesFrom was called in removeNode.
+ g_.removeEdgesTo(recycled_nodes_);
+ available_nodes_.setUnion(recycled_nodes_);
+ recycled_nodes_.clear();
+ return getAvailableNode(data);
+ }
+ // We are out of vacant nodes. Flush and increment the current_epoch_.
+ current_epoch_ += size();
+ recycled_nodes_.clear();
+ available_nodes_.setAll();
+ g_.clear();
+ return getAvailableNode(data);
+ }
+
+ // Get data associated with the node created by newNode().
+ uptr getData(uptr node) const { return data_[nodeToIndex(node)]; }
+
+ bool nodeBelongsToCurrentEpoch(uptr node) {
+ return node && (node / size() * size()) == current_epoch_;
+ }
+
+ void removeNode(uptr node) {
+ uptr idx = nodeToIndex(node);
+ CHECK(!available_nodes_.getBit(idx));
+ CHECK(recycled_nodes_.setBit(idx));
+ g_.removeEdgesFrom(idx);
+ }
+
+ void ensureCurrentEpoch(DeadlockDetectorTLS<BV> *dtls) {
+ dtls->ensureCurrentEpoch(current_epoch_);
+ }
+
+ // Returns true if there is a cycle in the graph after this lock event.
+ // Ideally should be called before the lock is acquired so that we can
+ // report a deadlock before a real deadlock happens.
+ bool onLockBefore(DeadlockDetectorTLS<BV> *dtls, uptr cur_node) {
+ ensureCurrentEpoch(dtls);
+ uptr cur_idx = nodeToIndex(cur_node);
+ return g_.isReachable(cur_idx, dtls->getLocks(current_epoch_));
+ }
+
+ u32 findLockContext(DeadlockDetectorTLS<BV> *dtls, uptr node) {
+ return dtls->findLockContext(nodeToIndex(node));
+ }
+
+ // Add cur_node to the set of locks held currently by dtls.
+ void onLockAfter(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
+ ensureCurrentEpoch(dtls);
+ uptr cur_idx = nodeToIndex(cur_node);
+ dtls->addLock(cur_idx, current_epoch_, stk);
+ }
+
+ // Experimental *racy* fast path function.
+ // Returns true if all edges from the currently held locks to cur_node exist.
+ bool hasAllEdges(DeadlockDetectorTLS<BV> *dtls, uptr cur_node) {
+ uptr local_epoch = dtls->getEpoch();
+ // Read from current_epoch_ is racy.
+ if (cur_node && local_epoch == current_epoch_ &&
+ local_epoch == nodeToEpoch(cur_node)) {
+ uptr cur_idx = nodeToIndexUnchecked(cur_node);
+ for (uptr i = 0, n = dtls->getNumLocks(); i < n; i++) {
+ if (!g_.hasEdge(dtls->getLock(i), cur_idx))
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ // Adds edges from currently held locks to cur_node,
+ // returns the number of added edges, and puts the sources of added edges
+ // into added_edges[].
+ // Should be called before onLockAfter.
+ uptr addEdges(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk,
+ int unique_tid) {
+ ensureCurrentEpoch(dtls);
+ uptr cur_idx = nodeToIndex(cur_node);
+ uptr added_edges[40];
+ uptr n_added_edges = g_.addEdges(dtls->getLocks(current_epoch_), cur_idx,
+ added_edges, ARRAY_SIZE(added_edges));
+ for (uptr i = 0; i < n_added_edges; i++) {
+ if (n_edges_ < ARRAY_SIZE(edges_)) {
+ Edge e = {(u16)added_edges[i], (u16)cur_idx,
+ dtls->findLockContext(added_edges[i]), stk,
+ unique_tid};
+ edges_[n_edges_++] = e;
+ }
+ // Printf("Edge%zd: %u %zd=>%zd in T%d\n",
+ // n_edges_, stk, added_edges[i], cur_idx, unique_tid);
+ }
+ return n_added_edges;
+ }
+
+ bool findEdge(uptr from_node, uptr to_node, u32 *stk_from, u32 *stk_to,
+ int *unique_tid) {
+ uptr from_idx = nodeToIndex(from_node);
+ uptr to_idx = nodeToIndex(to_node);
+ for (uptr i = 0; i < n_edges_; i++) {
+ if (edges_[i].from == from_idx && edges_[i].to == to_idx) {
+ *stk_from = edges_[i].stk_from;
+ *stk_to = edges_[i].stk_to;
+ *unique_tid = edges_[i].unique_tid;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Test-only function. Handles the before/after lock events,
+ // returns true if there is a cycle.
+ bool onLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
+ ensureCurrentEpoch(dtls);
+ bool is_reachable = !isHeld(dtls, cur_node) && onLockBefore(dtls, cur_node);
+ addEdges(dtls, cur_node, stk, 0);
+ onLockAfter(dtls, cur_node, stk);
+ return is_reachable;
+ }
+
+ // Handles the try_lock event, returns false.
+ // When a try_lock event happens (i.e. a try_lock call succeeds) we need
+ // to add this lock to the currently held locks, but we should not try to
+ // change the lock graph or to detect a cycle. We may want to investigate
+ // whether a more aggressive strategy is possible for try_lock.
+ bool onTryLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
+ ensureCurrentEpoch(dtls);
+ uptr cur_idx = nodeToIndex(cur_node);
+ dtls->addLock(cur_idx, current_epoch_, stk);
+ return false;
+ }
+
+ // Returns true iff dtls is empty (no locks are currently held) and we can
+ // add the node to the currently held locks w/o chanding the global state.
+ // This operation is thread-safe as it only touches the dtls.
+ bool onFirstLock(DeadlockDetectorTLS<BV> *dtls, uptr node, u32 stk = 0) {
+ if (!dtls->empty()) return false;
+ if (dtls->getEpoch() && dtls->getEpoch() == nodeToEpoch(node)) {
+ dtls->addLock(nodeToIndexUnchecked(node), nodeToEpoch(node), stk);
+ return true;
+ }
+ return false;
+ }
+
+ // Finds a path between the lock 'cur_node' (currently not held in dtls)
+ // and some currently held lock, returns the length of the path
+ // or 0 on failure.
+ uptr findPathToLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, uptr *path,
+ uptr path_size) {
+ tmp_bv_.copyFrom(dtls->getLocks(current_epoch_));
+ uptr idx = nodeToIndex(cur_node);
+ CHECK(!tmp_bv_.getBit(idx));
+ uptr res = g_.findShortestPath(idx, tmp_bv_, path, path_size);
+ for (uptr i = 0; i < res; i++)
+ path[i] = indexToNode(path[i]);
+ if (res)
+ CHECK_EQ(path[0], cur_node);
+ return res;
+ }
+
+ // Handle the unlock event.
+ // This operation is thread-safe as it only touches the dtls.
+ void onUnlock(DeadlockDetectorTLS<BV> *dtls, uptr node) {
+ if (dtls->getEpoch() == nodeToEpoch(node))
+ dtls->removeLock(nodeToIndexUnchecked(node));
+ }
+
+ // Tries to handle the lock event w/o writing to global state.
+ // Returns true on success.
+ // This operation is thread-safe as it only touches the dtls
+ // (modulo racy nature of hasAllEdges).
+ bool onLockFast(DeadlockDetectorTLS<BV> *dtls, uptr node, u32 stk = 0) {
+ if (hasAllEdges(dtls, node)) {
+ dtls->addLock(nodeToIndexUnchecked(node), nodeToEpoch(node), stk);
+ return true;
+ }
+ return false;
+ }
+
+ bool isHeld(DeadlockDetectorTLS<BV> *dtls, uptr node) const {
+ return dtls->getLocks(current_epoch_).getBit(nodeToIndex(node));
+ }
+
+ uptr testOnlyGetEpoch() const { return current_epoch_; }
+ bool testOnlyHasEdge(uptr l1, uptr l2) {
+ return g_.hasEdge(nodeToIndex(l1), nodeToIndex(l2));
+ }
+ // idx1 and idx2 are raw indices to g_, not lock IDs.
+ bool testOnlyHasEdgeRaw(uptr idx1, uptr idx2) {
+ return g_.hasEdge(idx1, idx2);
+ }
+
+ void Print() {
+ for (uptr from = 0; from < size(); from++)
+ for (uptr to = 0; to < size(); to++)
+ if (g_.hasEdge(from, to))
+ Printf(" %zx => %zx\n", from, to);
+ }
+
+ private:
+ void check_idx(uptr idx) const { CHECK_LT(idx, size()); }
+
+ void check_node(uptr node) const {
+ CHECK_GE(node, size());
+ CHECK_EQ(current_epoch_, nodeToEpoch(node));
+ }
+
+ uptr indexToNode(uptr idx) const {
+ check_idx(idx);
+ return idx + current_epoch_;
+ }
+
+ uptr nodeToIndexUnchecked(uptr node) const { return node % size(); }
+
+ uptr nodeToIndex(uptr node) const {
+ check_node(node);
+ return nodeToIndexUnchecked(node);
+ }
+
+ uptr nodeToEpoch(uptr node) const { return node / size() * size(); }
+
+ uptr getAvailableNode(uptr data) {
+ uptr idx = available_nodes_.getAndClearFirstOne();
+ data_[idx] = data;
+ return indexToNode(idx);
+ }
+
+ struct Edge {
+ u16 from;
+ u16 to;
+ u32 stk_from;
+ u32 stk_to;
+ int unique_tid;
+ };
+
+ uptr current_epoch_;
+ BV available_nodes_;
+ BV recycled_nodes_;
+ BV tmp_bv_;
+ BVGraph<BV> g_;
+ uptr data_[BV::kSize];
+ Edge edges_[BV::kSize * 32];
+ uptr n_edges_;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_DEADLOCK_DETECTOR_H
--- /dev/null
+//===-- sanitizer_deadlock_detector1.cc -----------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Deadlock detector implementation based on NxN adjacency bit matrix.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_deadlock_detector_interface.h"
+#include "sanitizer_deadlock_detector.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_mutex.h"
+
+#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
+
+namespace __sanitizer {
+
+typedef TwoLevelBitVector<> DDBV; // DeadlockDetector's bit vector.
+
+struct DDPhysicalThread {
+};
+
+struct DDLogicalThread {
+ u64 ctx;
+ DeadlockDetectorTLS<DDBV> dd;
+ DDReport rep;
+ bool report_pending;
+};
+
+struct DD : public DDetector {
+ SpinMutex mtx;
+ DeadlockDetector<DDBV> dd;
+ DDFlags flags;
+
+ explicit DD(const DDFlags *flags);
+
+ DDPhysicalThread *CreatePhysicalThread() override;
+ void DestroyPhysicalThread(DDPhysicalThread *pt) override;
+
+ DDLogicalThread *CreateLogicalThread(u64 ctx) override;
+ void DestroyLogicalThread(DDLogicalThread *lt) override;
+
+ void MutexInit(DDCallback *cb, DDMutex *m) override;
+ void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override;
+ void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+ bool trylock) override;
+ void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override;
+ void MutexDestroy(DDCallback *cb, DDMutex *m) override;
+
+ DDReport *GetReport(DDCallback *cb) override;
+
+ void MutexEnsureID(DDLogicalThread *lt, DDMutex *m);
+ void ReportDeadlock(DDCallback *cb, DDMutex *m);
+};
+
+DDetector *DDetector::Create(const DDFlags *flags) {
+ (void)flags;
+ void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
+ return new(mem) DD(flags);
+}
+
+DD::DD(const DDFlags *flags)
+ : flags(*flags) {
+ dd.clear();
+}
+
+DDPhysicalThread* DD::CreatePhysicalThread() {
+ return nullptr;
+}
+
+void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
+}
+
+DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
+ DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(sizeof(*lt));
+ lt->ctx = ctx;
+ lt->dd.clear();
+ lt->report_pending = false;
+ return lt;
+}
+
+void DD::DestroyLogicalThread(DDLogicalThread *lt) {
+ lt->~DDLogicalThread();
+ InternalFree(lt);
+}
+
+void DD::MutexInit(DDCallback *cb, DDMutex *m) {
+ m->id = 0;
+ m->stk = cb->Unwind();
+}
+
+void DD::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) {
+ if (!dd.nodeBelongsToCurrentEpoch(m->id))
+ m->id = dd.newNode(reinterpret_cast<uptr>(m));
+ dd.ensureCurrentEpoch(<->dd);
+}
+
+void DD::MutexBeforeLock(DDCallback *cb,
+ DDMutex *m, bool wlock) {
+ DDLogicalThread *lt = cb->lt;
+ if (lt->dd.empty()) return; // This will be the first lock held by lt.
+ if (dd.hasAllEdges(<->dd, m->id)) return; // We already have all edges.
+ SpinMutexLock lk(&mtx);
+ MutexEnsureID(lt, m);
+ if (dd.isHeld(<->dd, m->id))
+ return; // FIXME: allow this only for recursive locks.
+ if (dd.onLockBefore(<->dd, m->id)) {
+ // Actually add this edge now so that we have all the stack traces.
+ dd.addEdges(<->dd, m->id, cb->Unwind(), cb->UniqueTid());
+ ReportDeadlock(cb, m);
+ }
+}
+
+void DD::ReportDeadlock(DDCallback *cb, DDMutex *m) {
+ DDLogicalThread *lt = cb->lt;
+ uptr path[10];
+ uptr len = dd.findPathToLock(<->dd, m->id, path, ARRAY_SIZE(path));
+ CHECK_GT(len, 0U); // Hm.. cycle of 10 locks? I'd like to see that.
+ CHECK_EQ(m->id, path[0]);
+ lt->report_pending = true;
+ DDReport *rep = <->rep;
+ rep->n = len;
+ for (uptr i = 0; i < len; i++) {
+ uptr from = path[i];
+ uptr to = path[(i + 1) % len];
+ DDMutex *m0 = (DDMutex*)dd.getData(from);
+ DDMutex *m1 = (DDMutex*)dd.getData(to);
+
+ u32 stk_from = -1U, stk_to = -1U;
+ int unique_tid = 0;
+ dd.findEdge(from, to, &stk_from, &stk_to, &unique_tid);
+ // Printf("Edge: %zd=>%zd: %u/%u T%d\n", from, to, stk_from, stk_to,
+ // unique_tid);
+ rep->loop[i].thr_ctx = unique_tid;
+ rep->loop[i].mtx_ctx0 = m0->ctx;
+ rep->loop[i].mtx_ctx1 = m1->ctx;
+ rep->loop[i].stk[0] = stk_to;
+ rep->loop[i].stk[1] = stk_from;
+ }
+}
+
+void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock) {
+ DDLogicalThread *lt = cb->lt;
+ u32 stk = 0;
+ if (flags.second_deadlock_stack)
+ stk = cb->Unwind();
+ // Printf("T%p MutexLock: %zx stk %u\n", lt, m->id, stk);
+ if (dd.onFirstLock(<->dd, m->id, stk))
+ return;
+ if (dd.onLockFast(<->dd, m->id, stk))
+ return;
+
+ SpinMutexLock lk(&mtx);
+ MutexEnsureID(lt, m);
+ if (wlock) // Only a recursive rlock may be held.
+ CHECK(!dd.isHeld(<->dd, m->id));
+ if (!trylock)
+ dd.addEdges(<->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid());
+ dd.onLockAfter(<->dd, m->id, stk);
+}
+
+void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
+ // Printf("T%p MutexUnLock: %zx\n", cb->lt, m->id);
+ dd.onUnlock(&cb->lt->dd, m->id);
+}
+
+void DD::MutexDestroy(DDCallback *cb,
+ DDMutex *m) {
+ if (!m->id) return;
+ SpinMutexLock lk(&mtx);
+ if (dd.nodeBelongsToCurrentEpoch(m->id))
+ dd.removeNode(m->id);
+ m->id = 0;
+}
+
+DDReport *DD::GetReport(DDCallback *cb) {
+ if (!cb->lt->report_pending)
+ return nullptr;
+ cb->lt->report_pending = false;
+ return &cb->lt->rep;
+}
+
+} // namespace __sanitizer
+#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
--- /dev/null
+//===-- sanitizer_deadlock_detector2.cc -----------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Deadlock detector implementation based on adjacency lists.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_deadlock_detector_interface.h"
+#include "sanitizer_common.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_mutex.h"
+
+#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
+
+namespace __sanitizer {
+
+const int kMaxNesting = 64;
+const u32 kNoId = -1;
+const u32 kEndId = -2;
+const int kMaxLink = 8;
+const int kL1Size = 1024;
+const int kL2Size = 1024;
+const int kMaxMutex = kL1Size * kL2Size;
+
+struct Id {
+ u32 id;
+ u32 seq;
+
+ explicit Id(u32 id = 0, u32 seq = 0)
+ : id(id)
+ , seq(seq) {
+ }
+};
+
+struct Link {
+ u32 id;
+ u32 seq;
+ u32 tid;
+ u32 stk0;
+ u32 stk1;
+
+ explicit Link(u32 id = 0, u32 seq = 0, u32 tid = 0, u32 s0 = 0, u32 s1 = 0)
+ : id(id)
+ , seq(seq)
+ , tid(tid)
+ , stk0(s0)
+ , stk1(s1) {
+ }
+};
+
+struct DDPhysicalThread {
+ DDReport rep;
+ bool report_pending;
+ bool visited[kMaxMutex];
+ Link pending[kMaxMutex];
+ Link path[kMaxMutex];
+};
+
+struct ThreadMutex {
+ u32 id;
+ u32 stk;
+};
+
+struct DDLogicalThread {
+ u64 ctx;
+ ThreadMutex locked[kMaxNesting];
+ int nlocked;
+};
+
+struct Mutex {
+ StaticSpinMutex mtx;
+ u32 seq;
+ int nlink;
+ Link link[kMaxLink];
+};
+
+struct DD : public DDetector {
+ explicit DD(const DDFlags *flags);
+
+ DDPhysicalThread* CreatePhysicalThread();
+ void DestroyPhysicalThread(DDPhysicalThread *pt);
+
+ DDLogicalThread* CreateLogicalThread(u64 ctx);
+ void DestroyLogicalThread(DDLogicalThread *lt);
+
+ void MutexInit(DDCallback *cb, DDMutex *m);
+ void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock);
+ void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+ bool trylock);
+ void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock);
+ void MutexDestroy(DDCallback *cb, DDMutex *m);
+
+ DDReport *GetReport(DDCallback *cb);
+
+ void CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt, DDMutex *mtx);
+ void Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath);
+ u32 allocateId(DDCallback *cb);
+ Mutex *getMutex(u32 id);
+ u32 getMutexId(Mutex *m);
+
+ DDFlags flags;
+
+ Mutex* mutex[kL1Size];
+
+ SpinMutex mtx;
+ InternalMmapVector<u32> free_id;
+ int id_gen;
+};
+
+DDetector *DDetector::Create(const DDFlags *flags) {
+ (void)flags;
+ void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
+ return new(mem) DD(flags);
+}
+
+DD::DD(const DDFlags *flags)
+ : flags(*flags)
+ , free_id(1024) {
+ id_gen = 0;
+}
+
+DDPhysicalThread* DD::CreatePhysicalThread() {
+ DDPhysicalThread *pt = (DDPhysicalThread*)MmapOrDie(sizeof(DDPhysicalThread),
+ "deadlock detector (physical thread)");
+ return pt;
+}
+
+void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
+ pt->~DDPhysicalThread();
+ UnmapOrDie(pt, sizeof(DDPhysicalThread));
+}
+
+DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
+ DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(
+ sizeof(DDLogicalThread));
+ lt->ctx = ctx;
+ lt->nlocked = 0;
+ return lt;
+}
+
+void DD::DestroyLogicalThread(DDLogicalThread *lt) {
+ lt->~DDLogicalThread();
+ InternalFree(lt);
+}
+
+void DD::MutexInit(DDCallback *cb, DDMutex *m) {
+ VPrintf(2, "#%llu: DD::MutexInit(%p)\n", cb->lt->ctx, m);
+ m->id = kNoId;
+ m->recursion = 0;
+ atomic_store(&m->owner, 0, memory_order_relaxed);
+}
+
+Mutex *DD::getMutex(u32 id) {
+ return &mutex[id / kL2Size][id % kL2Size];
+}
+
+u32 DD::getMutexId(Mutex *m) {
+ for (int i = 0; i < kL1Size; i++) {
+ Mutex *tab = mutex[i];
+ if (tab == 0)
+ break;
+ if (m >= tab && m < tab + kL2Size)
+ return i * kL2Size + (m - tab);
+ }
+ return -1;
+}
+
+u32 DD::allocateId(DDCallback *cb) {
+ u32 id = -1;
+ SpinMutexLock l(&mtx);
+ if (free_id.size() > 0) {
+ id = free_id.back();
+ free_id.pop_back();
+ } else {
+ CHECK_LT(id_gen, kMaxMutex);
+ if ((id_gen % kL2Size) == 0) {
+ mutex[id_gen / kL2Size] = (Mutex*)MmapOrDie(kL2Size * sizeof(Mutex),
+ "deadlock detector (mutex table)");
+ }
+ id = id_gen++;
+ }
+ CHECK_LE(id, kMaxMutex);
+ VPrintf(3, "#%llu: DD::allocateId assign id %d\n", cb->lt->ctx, id);
+ return id;
+}
+
+void DD::MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {
+ VPrintf(2, "#%llu: DD::MutexBeforeLock(%p, wlock=%d) nlocked=%d\n",
+ cb->lt->ctx, m, wlock, cb->lt->nlocked);
+ DDPhysicalThread *pt = cb->pt;
+ DDLogicalThread *lt = cb->lt;
+
+ uptr owner = atomic_load(&m->owner, memory_order_relaxed);
+ if (owner == (uptr)cb->lt) {
+ VPrintf(3, "#%llu: DD::MutexBeforeLock recursive\n",
+ cb->lt->ctx);
+ return;
+ }
+
+ CHECK_LE(lt->nlocked, kMaxNesting);
+
+ // FIXME(dvyukov): don't allocate id if lt->nlocked == 0?
+ if (m->id == kNoId)
+ m->id = allocateId(cb);
+
+ ThreadMutex *tm = <->locked[lt->nlocked++];
+ tm->id = m->id;
+ if (flags.second_deadlock_stack)
+ tm->stk = cb->Unwind();
+ if (lt->nlocked == 1) {
+ VPrintf(3, "#%llu: DD::MutexBeforeLock first mutex\n",
+ cb->lt->ctx);
+ return;
+ }
+
+ bool added = false;
+ Mutex *mtx = getMutex(m->id);
+ for (int i = 0; i < lt->nlocked - 1; i++) {
+ u32 id1 = lt->locked[i].id;
+ u32 stk1 = lt->locked[i].stk;
+ Mutex *mtx1 = getMutex(id1);
+ SpinMutexLock l(&mtx1->mtx);
+ if (mtx1->nlink == kMaxLink) {
+ // FIXME(dvyukov): check stale links
+ continue;
+ }
+ int li = 0;
+ for (; li < mtx1->nlink; li++) {
+ Link *link = &mtx1->link[li];
+ if (link->id == m->id) {
+ if (link->seq != mtx->seq) {
+ link->seq = mtx->seq;
+ link->tid = lt->ctx;
+ link->stk0 = stk1;
+ link->stk1 = cb->Unwind();
+ added = true;
+ VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
+ cb->lt->ctx, getMutexId(mtx1), m->id);
+ }
+ break;
+ }
+ }
+ if (li == mtx1->nlink) {
+ // FIXME(dvyukov): check stale links
+ Link *link = &mtx1->link[mtx1->nlink++];
+ link->id = m->id;
+ link->seq = mtx->seq;
+ link->tid = lt->ctx;
+ link->stk0 = stk1;
+ link->stk1 = cb->Unwind();
+ added = true;
+ VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
+ cb->lt->ctx, getMutexId(mtx1), m->id);
+ }
+ }
+
+ if (!added || mtx->nlink == 0) {
+ VPrintf(3, "#%llu: DD::MutexBeforeLock don't check\n",
+ cb->lt->ctx);
+ return;
+ }
+
+ CycleCheck(pt, lt, m);
+}
+
+void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+ bool trylock) {
+ VPrintf(2, "#%llu: DD::MutexAfterLock(%p, wlock=%d, try=%d) nlocked=%d\n",
+ cb->lt->ctx, m, wlock, trylock, cb->lt->nlocked);
+ DDLogicalThread *lt = cb->lt;
+
+ uptr owner = atomic_load(&m->owner, memory_order_relaxed);
+ if (owner == (uptr)cb->lt) {
+ VPrintf(3, "#%llu: DD::MutexAfterLock recursive\n", cb->lt->ctx);
+ CHECK(wlock);
+ m->recursion++;
+ return;
+ }
+ CHECK_EQ(owner, 0);
+ if (wlock) {
+ VPrintf(3, "#%llu: DD::MutexAfterLock set owner\n", cb->lt->ctx);
+ CHECK_EQ(m->recursion, 0);
+ m->recursion = 1;
+ atomic_store(&m->owner, (uptr)cb->lt, memory_order_relaxed);
+ }
+
+ if (!trylock)
+ return;
+
+ CHECK_LE(lt->nlocked, kMaxNesting);
+ if (m->id == kNoId)
+ m->id = allocateId(cb);
+ ThreadMutex *tm = <->locked[lt->nlocked++];
+ tm->id = m->id;
+ if (flags.second_deadlock_stack)
+ tm->stk = cb->Unwind();
+}
+
+void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
+ VPrintf(2, "#%llu: DD::MutexBeforeUnlock(%p, wlock=%d) nlocked=%d\n",
+ cb->lt->ctx, m, wlock, cb->lt->nlocked);
+ DDLogicalThread *lt = cb->lt;
+
+ uptr owner = atomic_load(&m->owner, memory_order_relaxed);
+ if (owner == (uptr)cb->lt) {
+ VPrintf(3, "#%llu: DD::MutexBeforeUnlock recursive\n", cb->lt->ctx);
+ if (--m->recursion > 0)
+ return;
+ VPrintf(3, "#%llu: DD::MutexBeforeUnlock reset owner\n", cb->lt->ctx);
+ atomic_store(&m->owner, 0, memory_order_relaxed);
+ }
+ CHECK_NE(m->id, kNoId);
+ int last = lt->nlocked - 1;
+ for (int i = last; i >= 0; i--) {
+ if (cb->lt->locked[i].id == m->id) {
+ lt->locked[i] = lt->locked[last];
+ lt->nlocked--;
+ break;
+ }
+ }
+}
+
+void DD::MutexDestroy(DDCallback *cb, DDMutex *m) {
+ VPrintf(2, "#%llu: DD::MutexDestroy(%p)\n",
+ cb->lt->ctx, m);
+ DDLogicalThread *lt = cb->lt;
+
+ if (m->id == kNoId)
+ return;
+
+ // Remove the mutex from lt->locked if there.
+ int last = lt->nlocked - 1;
+ for (int i = last; i >= 0; i--) {
+ if (lt->locked[i].id == m->id) {
+ lt->locked[i] = lt->locked[last];
+ lt->nlocked--;
+ break;
+ }
+ }
+
+ // Clear and invalidate the mutex descriptor.
+ {
+ Mutex *mtx = getMutex(m->id);
+ SpinMutexLock l(&mtx->mtx);
+ mtx->seq++;
+ mtx->nlink = 0;
+ }
+
+ // Return id to cache.
+ {
+ SpinMutexLock l(&mtx);
+ free_id.push_back(m->id);
+ }
+}
+
+void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt,
+ DDMutex *m) {
+ internal_memset(pt->visited, 0, sizeof(pt->visited));
+ int npath = 0;
+ int npending = 0;
+ {
+ Mutex *mtx = getMutex(m->id);
+ SpinMutexLock l(&mtx->mtx);
+ for (int li = 0; li < mtx->nlink; li++)
+ pt->pending[npending++] = mtx->link[li];
+ }
+ while (npending > 0) {
+ Link link = pt->pending[--npending];
+ if (link.id == kEndId) {
+ npath--;
+ continue;
+ }
+ if (pt->visited[link.id])
+ continue;
+ Mutex *mtx1 = getMutex(link.id);
+ SpinMutexLock l(&mtx1->mtx);
+ if (mtx1->seq != link.seq)
+ continue;
+ pt->visited[link.id] = true;
+ if (mtx1->nlink == 0)
+ continue;
+ pt->path[npath++] = link;
+ pt->pending[npending++] = Link(kEndId);
+ if (link.id == m->id)
+ return Report(pt, lt, npath); // Bingo!
+ for (int li = 0; li < mtx1->nlink; li++) {
+ Link *link1 = &mtx1->link[li];
+ // Mutex *mtx2 = getMutex(link->id);
+ // FIXME(dvyukov): fast seq check
+ // FIXME(dvyukov): fast nlink != 0 check
+ // FIXME(dvyukov): fast pending check?
+ // FIXME(dvyukov): npending can be larger than kMaxMutex
+ pt->pending[npending++] = *link1;
+ }
+ }
+}
+
+void DD::Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath) {
+ DDReport *rep = &pt->rep;
+ rep->n = npath;
+ for (int i = 0; i < npath; i++) {
+ Link *link = &pt->path[i];
+ Link *link0 = &pt->path[i ? i - 1 : npath - 1];
+ rep->loop[i].thr_ctx = link->tid;
+ rep->loop[i].mtx_ctx0 = link0->id;
+ rep->loop[i].mtx_ctx1 = link->id;
+ rep->loop[i].stk[0] = flags.second_deadlock_stack ? link->stk0 : 0;
+ rep->loop[i].stk[1] = link->stk1;
+ }
+ pt->report_pending = true;
+}
+
+DDReport *DD::GetReport(DDCallback *cb) {
+ if (!cb->pt->report_pending)
+ return 0;
+ cb->pt->report_pending = false;
+ return &cb->pt->rep;
+}
+
+} // namespace __sanitizer
+#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
--- /dev/null
+//===-- sanitizer_deadlock_detector_interface.h -----------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer runtime.
+// Abstract deadlock detector interface.
+// FIXME: this is work in progress, nothing really works yet.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H
+#define SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H
+
+#ifndef SANITIZER_DEADLOCK_DETECTOR_VERSION
+# define SANITIZER_DEADLOCK_DETECTOR_VERSION 1
+#endif
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_atomic.h"
+
+namespace __sanitizer {
+
+// dd - deadlock detector.
+// lt - logical (user) thread.
+// pt - physical (OS) thread.
+
+struct DDPhysicalThread;
+struct DDLogicalThread;
+
+struct DDMutex {
+#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
+ uptr id;
+ u32 stk; // creation stack
+#elif SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
+ u32 id;
+ u32 recursion;
+ atomic_uintptr_t owner;
+#else
+# error "BAD SANITIZER_DEADLOCK_DETECTOR_VERSION"
+#endif
+ u64 ctx;
+};
+
+struct DDFlags {
+ bool second_deadlock_stack;
+};
+
+struct DDReport {
+ enum { kMaxLoopSize = 8 };
+ int n; // number of entries in loop
+ struct {
+ u64 thr_ctx; // user thread context
+ u64 mtx_ctx0; // user mutex context, start of the edge
+ u64 mtx_ctx1; // user mutex context, end of the edge
+ u32 stk[2]; // stack ids for the edge
+ } loop[kMaxLoopSize];
+};
+
+struct DDCallback {
+ DDPhysicalThread *pt;
+ DDLogicalThread *lt;
+
+ virtual u32 Unwind() { return 0; }
+ virtual int UniqueTid() { return 0; }
+};
+
+struct DDetector {
+ static DDetector *Create(const DDFlags *flags);
+
+ virtual DDPhysicalThread* CreatePhysicalThread() { return nullptr; }
+ virtual void DestroyPhysicalThread(DDPhysicalThread *pt) {}
+
+ virtual DDLogicalThread* CreateLogicalThread(u64 ctx) { return nullptr; }
+ virtual void DestroyLogicalThread(DDLogicalThread *lt) {}
+
+ virtual void MutexInit(DDCallback *cb, DDMutex *m) {}
+ virtual void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {}
+ virtual void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+ bool trylock) {}
+ virtual void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {}
+ virtual void MutexDestroy(DDCallback *cb, DDMutex *m) {}
+
+ virtual DDReport *GetReport(DDCallback *cb) { return nullptr; }
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H
--- /dev/null
+//===-- sanitizer_flag_parser.cc ------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_flag_parser.h"
+
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_flag_parser.h"
+
+namespace __sanitizer {
+
+LowLevelAllocator FlagParser::Alloc;
+
+class UnknownFlags {
+ static const int kMaxUnknownFlags = 20;
+ const char *unknown_flags_[kMaxUnknownFlags];
+ int n_unknown_flags_;
+
+ public:
+ void Add(const char *name) {
+ CHECK_LT(n_unknown_flags_, kMaxUnknownFlags);
+ unknown_flags_[n_unknown_flags_++] = name;
+ }
+
+ void Report() {
+ if (!n_unknown_flags_) return;
+ Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_);
+ for (int i = 0; i < n_unknown_flags_; ++i)
+ Printf(" %s\n", unknown_flags_[i]);
+ n_unknown_flags_ = 0;
+ }
+};
+
+UnknownFlags unknown_flags;
+
+void ReportUnrecognizedFlags() {
+ unknown_flags.Report();
+}
+
+char *FlagParser::ll_strndup(const char *s, uptr n) {
+ uptr len = internal_strnlen(s, n);
+ char *s2 = (char*)Alloc.Allocate(len + 1);
+ internal_memcpy(s2, s, len);
+ s2[len] = 0;
+ return s2;
+}
+
+void FlagParser::PrintFlagDescriptions() {
+ Printf("Available flags for %s:\n", SanitizerToolName);
+ for (int i = 0; i < n_flags_; ++i)
+ Printf("\t%s\n\t\t- %s\n", flags_[i].name, flags_[i].desc);
+}
+
+void FlagParser::fatal_error(const char *err) {
+ Printf("ERROR: %s\n", err);
+ Die();
+}
+
+bool FlagParser::is_space(char c) {
+ return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
+ c == '\r';
+}
+
+void FlagParser::skip_whitespace() {
+ while (is_space(buf_[pos_])) ++pos_;
+}
+
+void FlagParser::parse_flag() {
+ uptr name_start = pos_;
+ while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_;
+ if (buf_[pos_] != '=') fatal_error("expected '='");
+ char *name = ll_strndup(buf_ + name_start, pos_ - name_start);
+
+ uptr value_start = ++pos_;
+ char *value;
+ if (buf_[pos_] == '\'' || buf_[pos_] == '"') {
+ char quote = buf_[pos_++];
+ while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_;
+ if (buf_[pos_] == 0) fatal_error("unterminated string");
+ value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1);
+ ++pos_; // consume the closing quote
+ } else {
+ while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_;
+ if (buf_[pos_] != 0 && !is_space(buf_[pos_]))
+ fatal_error("expected separator or eol");
+ value = ll_strndup(buf_ + value_start, pos_ - value_start);
+ }
+
+ bool res = run_handler(name, value);
+ if (!res) fatal_error("Flag parsing failed.");
+}
+
+void FlagParser::parse_flags() {
+ while (true) {
+ skip_whitespace();
+ if (buf_[pos_] == 0) break;
+ parse_flag();
+ }
+
+ // Do a sanity check for certain flags.
+ if (common_flags_dont_use.malloc_context_size < 1)
+ common_flags_dont_use.malloc_context_size = 1;
+}
+
+void FlagParser::ParseString(const char *s) {
+ if (!s) return;
+ // Backup current parser state to allow nested ParseString() calls.
+ const char *old_buf_ = buf_;
+ uptr old_pos_ = pos_;
+ buf_ = s;
+ pos_ = 0;
+
+ parse_flags();
+
+ buf_ = old_buf_;
+ pos_ = old_pos_;
+}
+
+bool FlagParser::ParseFile(const char *path, bool ignore_missing) {
+ static const uptr kMaxIncludeSize = 1 << 15;
+ char *data;
+ uptr data_mapped_size;
+ error_t err;
+ uptr len;
+ if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len,
+ Max(kMaxIncludeSize, GetPageSizeCached()), &err)) {
+ if (ignore_missing)
+ return true;
+ Printf("Failed to read options from '%s': error %d\n", path, err);
+ return false;
+ }
+ ParseString(data);
+ UnmapOrDie(data, data_mapped_size);
+ return true;
+}
+
+bool FlagParser::run_handler(const char *name, const char *value) {
+ for (int i = 0; i < n_flags_; ++i) {
+ if (internal_strcmp(name, flags_[i].name) == 0)
+ return flags_[i].handler->Parse(value);
+ }
+ // Unrecognized flag. This is not a fatal error, we may print a warning later.
+ unknown_flags.Add(name);
+ return true;
+}
+
+void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
+ const char *desc) {
+ CHECK_LT(n_flags_, kMaxFlags);
+ flags_[n_flags_].name = name;
+ flags_[n_flags_].desc = desc;
+ flags_[n_flags_].handler = handler;
+ ++n_flags_;
+}
+
+FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
+ flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags);
+}
+
+} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_flag_parser.h ---------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_FLAG_REGISTRY_H
+#define SANITIZER_FLAG_REGISTRY_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+class FlagHandlerBase {
+ public:
+ virtual bool Parse(const char *value) { return false; }
+};
+
+template <typename T>
+class FlagHandler : public FlagHandlerBase {
+ T *t_;
+
+ public:
+ explicit FlagHandler(T *t) : t_(t) {}
+ bool Parse(const char *value) final;
+};
+
+template <>
+inline bool FlagHandler<bool>::Parse(const char *value) {
+ if (internal_strcmp(value, "0") == 0 ||
+ internal_strcmp(value, "no") == 0 ||
+ internal_strcmp(value, "false") == 0) {
+ *t_ = false;
+ return true;
+ }
+ if (internal_strcmp(value, "1") == 0 ||
+ internal_strcmp(value, "yes") == 0 ||
+ internal_strcmp(value, "true") == 0) {
+ *t_ = true;
+ return true;
+ }
+ Printf("ERROR: Invalid value for bool option: '%s'\n", value);
+ return false;
+}
+
+template <>
+inline bool FlagHandler<const char *>::Parse(const char *value) {
+ *t_ = internal_strdup(value);
+ return true;
+}
+
+template <>
+inline bool FlagHandler<int>::Parse(const char *value) {
+ char *value_end;
+ *t_ = internal_simple_strtoll(value, &value_end, 10);
+ bool ok = *value_end == 0;
+ if (!ok) Printf("ERROR: Invalid value for int option: '%s'\n", value);
+ return ok;
+}
+
+template <>
+inline bool FlagHandler<uptr>::Parse(const char *value) {
+ char *value_end;
+ *t_ = internal_simple_strtoll(value, &value_end, 10);
+ bool ok = *value_end == 0;
+ if (!ok) Printf("ERROR: Invalid value for uptr option: '%s'\n", value);
+ return ok;
+}
+
+class FlagParser {
+ static const int kMaxFlags = 200;
+ struct Flag {
+ const char *name;
+ const char *desc;
+ FlagHandlerBase *handler;
+ } *flags_;
+ int n_flags_;
+
+ const char *buf_;
+ uptr pos_;
+
+ public:
+ FlagParser();
+ void RegisterHandler(const char *name, FlagHandlerBase *handler,
+ const char *desc);
+ void ParseString(const char *s);
+ bool ParseFile(const char *path, bool ignore_missing);
+ void PrintFlagDescriptions();
+
+ static LowLevelAllocator Alloc;
+
+ private:
+ void fatal_error(const char *err);
+ bool is_space(char c);
+ void skip_whitespace();
+ void parse_flags();
+ void parse_flag();
+ bool run_handler(const char *name, const char *value);
+ char *ll_strndup(const char *s, uptr n);
+};
+
+template <typename T>
+static void RegisterFlag(FlagParser *parser, const char *name, const char *desc,
+ T *var) {
+ FlagHandler<T> *fh = new (FlagParser::Alloc) FlagHandler<T>(var); // NOLINT
+ parser->RegisterHandler(name, fh, desc);
+}
+
+void ReportUnrecognizedFlags();
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FLAG_REGISTRY_H
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
+#include "sanitizer_list.h"
+#include "sanitizer_flag_parser.h"
namespace __sanitizer {
-void SetCommonFlagsDefaults(CommonFlags *f) {
- f->symbolize = true;
- f->external_symbolizer_path = 0;
- f->strip_path_prefix = "";
- f->fast_unwind_on_fatal = false;
- f->fast_unwind_on_malloc = true;
- f->handle_ioctl = false;
- f->malloc_context_size = 1;
- f->log_path = "stderr";
- f->verbosity = 0;
- f->detect_leaks = false;
- f->leak_check_at_exit = true;
- f->allocator_may_return_null = false;
- f->print_summary = true;
+CommonFlags common_flags_dont_use;
+
+struct FlagDescription {
+ const char *name;
+ const char *description;
+ FlagDescription *next;
+};
+
+IntrusiveList<FlagDescription> flag_descriptions;
+
+// If set, the tool will install its own SEGV signal handler by default.
+#ifndef SANITIZER_NEEDS_SEGV
+# define SANITIZER_NEEDS_SEGV 1
+#endif
+
+void CommonFlags::SetDefaults() {
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
}
-void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
- ParseFlag(str, &f->symbolize, "symbolize");
- ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path");
- ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix");
- ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal");
- ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc");
- ParseFlag(str, &f->handle_ioctl, "handle_ioctl");
- ParseFlag(str, &f->malloc_context_size, "malloc_context_size");
- ParseFlag(str, &f->log_path, "log_path");
- ParseFlag(str, &f->verbosity, "verbosity");
- ParseFlag(str, &f->detect_leaks, "detect_leaks");
- ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit");
- ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null");
- ParseFlag(str, &f->print_summary, "print_summary");
-
- // Do a sanity check for certain flags.
- if (f->malloc_context_size < 1)
- f->malloc_context_size = 1;
+void CommonFlags::CopyFrom(const CommonFlags &other) {
+ internal_memcpy(this, &other, sizeof(*this));
}
-static bool GetFlagValue(const char *env, const char *name,
- const char **value, int *value_length) {
- if (env == 0)
- return false;
- const char *pos = 0;
- for (;;) {
- pos = internal_strstr(env, name);
- if (pos == 0)
- return false;
- if (pos != env && ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) {
- // Seems to be middle of another flag name or value.
- env = pos + 1;
- continue;
- }
- break;
- }
- pos += internal_strlen(name);
- const char *end;
- if (pos[0] != '=') {
- end = pos;
- } else {
- pos += 1;
- if (pos[0] == '"') {
- pos += 1;
- end = internal_strchr(pos, '"');
- } else if (pos[0] == '\'') {
- pos += 1;
- end = internal_strchr(pos, '\'');
- } else {
- // Read until the next space or colon.
- end = pos + internal_strcspn(pos, " :");
- }
- if (end == 0)
- end = pos + internal_strlen(pos);
+// Copy the string from "s" to "out", replacing "%b" with the binary basename.
+static void SubstituteBinaryName(const char *s, char *out, uptr out_size) {
+ char *out_end = out + out_size;
+ while (*s && out < out_end - 1) {
+ if (s[0] != '%' || s[1] != 'b') { *out++ = *s++; continue; }
+ const char *base = GetProcessName();
+ CHECK(base);
+ while (*base && out < out_end - 1)
+ *out++ = *base++;
+ s += 2; // skip "%b"
}
- *value = pos;
- *value_length = end - pos;
- return true;
+ *out = '\0';
}
-static bool StartsWith(const char *flag, int flag_length, const char *value) {
- if (!flag || !value)
- return false;
- int value_length = internal_strlen(value);
- return (flag_length >= value_length) &&
- (0 == internal_strncmp(flag, value, value_length));
-}
+class FlagHandlerInclude : public FlagHandlerBase {
+ FlagParser *parser_;
+ bool ignore_missing_;
-void ParseFlag(const char *env, bool *flag, const char *name) {
- const char *value;
- int value_length;
- if (!GetFlagValue(env, name, &value, &value_length))
- return;
- if (StartsWith(value, value_length, "0") ||
- StartsWith(value, value_length, "no") ||
- StartsWith(value, value_length, "false"))
- *flag = false;
- if (StartsWith(value, value_length, "1") ||
- StartsWith(value, value_length, "yes") ||
- StartsWith(value, value_length, "true"))
- *flag = true;
-}
+ public:
+ explicit FlagHandlerInclude(FlagParser *parser, bool ignore_missing)
+ : parser_(parser), ignore_missing_(ignore_missing) {}
+ bool Parse(const char *value) final {
+ if (internal_strchr(value, '%')) {
+ char *buf = (char *)MmapOrDie(kMaxPathLength, "FlagHandlerInclude");
+ SubstituteBinaryName(value, buf, kMaxPathLength);
+ bool res = parser_->ParseFile(buf, ignore_missing_);
+ UnmapOrDie(buf, kMaxPathLength);
+ return res;
+ }
+ return parser_->ParseFile(value, ignore_missing_);
+ }
+};
-void ParseFlag(const char *env, int *flag, const char *name) {
- const char *value;
- int value_length;
- if (!GetFlagValue(env, name, &value, &value_length))
- return;
- *flag = static_cast<int>(internal_atoll(value));
+void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf) {
+ FlagHandlerInclude *fh_include = new (FlagParser::Alloc) // NOLINT
+ FlagHandlerInclude(parser, /*ignore_missing*/ false);
+ parser->RegisterHandler("include", fh_include,
+ "read more options from the given file");
+ FlagHandlerInclude *fh_include_if_exists = new (FlagParser::Alloc) // NOLINT
+ FlagHandlerInclude(parser, /*ignore_missing*/ true);
+ parser->RegisterHandler(
+ "include_if_exists", fh_include_if_exists,
+ "read more options from the given file (if it exists)");
}
-static LowLevelAllocator allocator_for_flags;
-
-void ParseFlag(const char *env, const char **flag, const char *name) {
- const char *value;
- int value_length;
- if (!GetFlagValue(env, name, &value, &value_length))
- return;
- // Copy the flag value. Don't use locks here, as flags are parsed at
- // tool startup.
- char *value_copy = (char*)(allocator_for_flags.Allocate(value_length + 1));
- internal_memcpy(value_copy, value, value_length);
- value_copy[value_length] = '\0';
- *flag = value_copy;
+void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) {
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &cf->Name);
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
+
+ RegisterIncludeFlags(parser, cf);
}
} // namespace __sanitizer
namespace __sanitizer {
-void ParseFlag(const char *env, bool *flag, const char *name);
-void ParseFlag(const char *env, int *flag, const char *name);
-void ParseFlag(const char *env, const char **flag, const char *name);
-
struct CommonFlags {
- // If set, use the online symbolizer from common sanitizer runtime.
- bool symbolize;
- // Path to external symbolizer. If it is NULL, symbolizer will be looked for
- // in PATH. If it is empty, external symbolizer will not be started.
- const char *external_symbolizer_path;
- // Strips this prefix from file paths in error reports.
- const char *strip_path_prefix;
- // Use fast (frame-pointer-based) unwinder on fatal errors (if available).
- bool fast_unwind_on_fatal;
- // Use fast (frame-pointer-based) unwinder on malloc/free (if available).
- bool fast_unwind_on_malloc;
- // Intercept and handle ioctl requests.
- bool handle_ioctl;
- // Max number of stack frames kept for each allocation/deallocation.
- int malloc_context_size;
- // Write logs to "log_path.pid".
- // The special values are "stdout" and "stderr".
- // The default is "stderr".
- const char *log_path;
- // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).
- int verbosity;
- // Enable memory leak detection.
- bool detect_leaks;
- // Invoke leak checking in an atexit handler. Has no effect if
- // detect_leaks=false, or if __lsan_do_leak_check() is called before the
- // handler has a chance to run.
- bool leak_check_at_exit;
- // If false, the allocator will crash instead of returning 0 on out-of-memory.
- bool allocator_may_return_null;
- // If false, disable printing error summaries in addition to error reports.
- bool print_summary;
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
+
+ void SetDefaults();
+ void CopyFrom(const CommonFlags &other);
};
-inline CommonFlags *common_flags() {
- static CommonFlags f;
- return &f;
+// Functions to get/set global CommonFlags shared by all sanitizer runtimes:
+extern CommonFlags common_flags_dont_use;
+inline const CommonFlags *common_flags() {
+ return &common_flags_dont_use;
}
-void SetCommonFlagsDefaults(CommonFlags *f);
-void ParseCommonFlagsFromString(CommonFlags *f, const char *str);
+inline void SetCommonFlagsDefaults() {
+ common_flags_dont_use.SetDefaults();
+}
+
+// This function can only be used to setup tool-specific overrides for
+// CommonFlags defaults. Generally, it should only be used right after
+// SetCommonFlagsDefaults(), but before ParseCommonFlagsFromString(), and
+// only during the flags initialization (i.e. before they are used for
+// the first time).
+inline void OverrideCommonFlags(const CommonFlags &cf) {
+ common_flags_dont_use.CopyFrom(cf);
+}
+class FlagParser;
+void RegisterCommonFlags(FlagParser *parser,
+ CommonFlags *cf = &common_flags_dont_use);
+void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf);
} // namespace __sanitizer
#endif // SANITIZER_FLAGS_H
--- /dev/null
+//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file describes common flags available in all sanitizers.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef COMMON_FLAG
+#error "Define COMMON_FLAG prior to including this file!"
+#endif
+
+// COMMON_FLAG(Type, Name, DefaultValue, Description)
+// Supported types: bool, const char *, int, uptr.
+// Default value must be a compile-time constant.
+// Description must be a string literal.
+
+COMMON_FLAG(
+ bool, symbolize, true,
+ "If set, use the online symbolizer from common sanitizer runtime to turn "
+ "virtual addresses to file/line locations.")
+COMMON_FLAG(
+ const char *, external_symbolizer_path, nullptr,
+ "Path to external symbolizer. If empty, the tool will search $PATH for "
+ "the symbolizer.")
+COMMON_FLAG(
+ bool, allow_addr2line, false,
+ "If set, allows online symbolizer to run addr2line binary to symbolize "
+ "stack traces (addr2line will only be used if llvm-symbolizer binary is "
+ "unavailable.")
+COMMON_FLAG(const char *, strip_path_prefix, "",
+ "Strips this prefix from file paths in error reports.")
+COMMON_FLAG(bool, fast_unwind_on_check, false,
+ "If available, use the fast frame-pointer-based unwinder on "
+ "internal CHECK failures.")
+COMMON_FLAG(bool, fast_unwind_on_fatal, false,
+ "If available, use the fast frame-pointer-based unwinder on fatal "
+ "errors.")
+COMMON_FLAG(bool, fast_unwind_on_malloc, true,
+ "If available, use the fast frame-pointer-based unwinder on "
+ "malloc/free.")
+COMMON_FLAG(bool, handle_ioctl, false, "Intercept and handle ioctl requests.")
+COMMON_FLAG(int, malloc_context_size, 1,
+ "Max number of stack frames kept for each allocation/deallocation.")
+COMMON_FLAG(
+ const char *, log_path, "stderr",
+ "Write logs to \"log_path.pid\". The special values are \"stdout\" and "
+ "\"stderr\". The default is \"stderr\".")
+COMMON_FLAG(
+ bool, log_exe_name, false,
+ "Mention name of executable when reporting error and "
+ "append executable name to logs (as in \"log_path.exe_name.pid\").")
+COMMON_FLAG(
+ bool, log_to_syslog, SANITIZER_ANDROID,
+ "Write all sanitizer output to syslog in addition to other means of "
+ "logging.")
+COMMON_FLAG(
+ int, verbosity, 0,
+ "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).")
+COMMON_FLAG(bool, detect_leaks, true, "Enable memory leak detection.")
+COMMON_FLAG(
+ bool, leak_check_at_exit, true,
+ "Invoke leak checking in an atexit handler. Has no effect if "
+ "detect_leaks=false, or if __lsan_do_leak_check() is called before the "
+ "handler has a chance to run.")
+COMMON_FLAG(bool, allocator_may_return_null, false,
+ "If false, the allocator will crash instead of returning 0 on "
+ "out-of-memory.")
+COMMON_FLAG(bool, print_summary, true,
+ "If false, disable printing error summaries in addition to error "
+ "reports.")
+COMMON_FLAG(bool, check_printf, true, "Check printf arguments.")
+COMMON_FLAG(bool, handle_segv, SANITIZER_NEEDS_SEGV,
+ "If set, registers the tool's custom SIGSEGV/SIGBUS handler.")
+COMMON_FLAG(bool, handle_abort, false,
+ "If set, registers the tool's custom SIGABRT handler.")
+COMMON_FLAG(bool, handle_sigfpe, true,
+ "If set, registers the tool's custom SIGFPE handler.")
+COMMON_FLAG(bool, allow_user_segv_handler, false,
+ "If set, allows user to register a SEGV handler even if the tool "
+ "registers one.")
+COMMON_FLAG(bool, use_sigaltstack, true,
+ "If set, uses alternate stack for signal handling.")
+COMMON_FLAG(bool, detect_deadlocks, false,
+ "If set, deadlock detection is enabled.")
+COMMON_FLAG(
+ uptr, clear_shadow_mmap_threshold, 64 * 1024,
+ "Large shadow regions are zero-filled using mmap(NORESERVE) instead of "
+ "memset(). This is the threshold size in bytes.")
+COMMON_FLAG(const char *, color, "auto",
+ "Colorize reports: (always|never|auto).")
+COMMON_FLAG(
+ bool, legacy_pthread_cond, false,
+ "Enables support for dynamic libraries linked with libpthread 2.2.5.")
+COMMON_FLAG(bool, intercept_tls_get_addr, false, "Intercept __tls_get_addr.")
+COMMON_FLAG(bool, help, false, "Print the flag descriptions.")
+COMMON_FLAG(uptr, mmap_limit_mb, 0,
+ "Limit the amount of mmap-ed memory (excluding shadow) in Mb; "
+ "not a user-facing flag, used mosly for testing the tools")
+COMMON_FLAG(uptr, hard_rss_limit_mb, 0,
+ "Hard RSS limit in Mb."
+ " If non-zero, a background thread is spawned at startup"
+ " which periodically reads RSS and aborts the process if the"
+ " limit is reached")
+COMMON_FLAG(uptr, soft_rss_limit_mb, 0,
+ "Soft RSS limit in Mb."
+ " If non-zero, a background thread is spawned at startup"
+ " which periodically reads RSS. If the limit is reached"
+ " all subsequent malloc/new calls will fail or return NULL"
+ " (depending on the value of allocator_may_return_null)"
+ " until the RSS goes below the soft limit."
+ " This limit does not affect memory allocations other than"
+ " malloc/new.")
+COMMON_FLAG(bool, can_use_proc_maps_statm, true,
+ "If false, do not attempt to read /proc/maps/statm."
+ " Mostly useful for testing sanitizers.")
+COMMON_FLAG(
+ bool, coverage, false,
+ "If set, coverage information will be dumped at program shutdown (if the "
+ "coverage instrumentation was enabled at compile time).")
+COMMON_FLAG(bool, coverage_pcs, true,
+ "If set (and if 'coverage' is set too), the coverage information "
+ "will be dumped as a set of PC offsets for every module.")
+COMMON_FLAG(bool, coverage_order_pcs, false,
+ "If true, the PCs will be dumped in the order they've"
+ " appeared during the execution.")
+COMMON_FLAG(bool, coverage_bitset, false,
+ "If set (and if 'coverage' is set too), the coverage information "
+ "will also be dumped as a bitset to a separate file.")
+COMMON_FLAG(bool, coverage_counters, false,
+ "If set (and if 'coverage' is set too), the bitmap that corresponds"
+ " to coverage counters will be dumped.")
+COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID,
+ "If set, coverage information will be dumped directly to a memory "
+ "mapped file. This way data is not lost even if the process is "
+ "suddenly killed.")
+COMMON_FLAG(const char *, coverage_dir, ".",
+ "Target directory for coverage dumps. Defaults to the current "
+ "directory.")
+COMMON_FLAG(bool, full_address_space, false,
+ "Sanitize complete address space; "
+ "by default kernel area on 32-bit platforms will not be sanitized")
+COMMON_FLAG(bool, print_suppressions, true,
+ "Print matched suppressions at exit.")
+COMMON_FLAG(
+ bool, disable_coredump, (SANITIZER_WORDSIZE == 64),
+ "Disable core dumping. By default, disable_core=1 on 64-bit to avoid "
+ "dumping a 16T+ core file. Ignored on OSes that don't dump core by"
+ "default and for sanitizers that don't reserve lots of virtual memory.")
+COMMON_FLAG(bool, use_madv_dontdump, true,
+ "If set, instructs kernel to not store the (huge) shadow "
+ "in core file.")
+COMMON_FLAG(bool, symbolize_inline_frames, true,
+ "Print inlined frames in stacktraces. Defaults to true.")
+COMMON_FLAG(bool, symbolize_vs_style, false,
+ "Print file locations in Visual Studio style (e.g: "
+ " file(10,42): ...")
+COMMON_FLAG(const char *, stack_trace_format, "DEFAULT",
+ "Format string used to render stack frames. "
+ "See sanitizer_stacktrace_printer.h for the format description. "
+ "Use DEFAULT to get default format.")
+COMMON_FLAG(bool, no_huge_pages_for_shadow, true,
+ "If true, the shadow is not allowed to use huge pages. ")
+COMMON_FLAG(bool, strict_string_checks, false,
+ "If set check that string arguments are properly null-terminated")
+COMMON_FLAG(bool, intercept_strstr, true,
+ "If set, uses custom wrappers for strstr and strcasestr functions "
+ "to find more errors.")
+COMMON_FLAG(bool, intercept_strspn, true,
+ "If set, uses custom wrappers for strspn and strcspn function "
+ "to find more errors.")
+COMMON_FLAG(bool, intercept_strpbrk, true,
+ "If set, uses custom wrappers for strpbrk function "
+ "to find more errors.")
+COMMON_FLAG(bool, intercept_memcmp, true,
+ "If set, uses custom wrappers for memcmp function "
+ "to find more errors.")
+COMMON_FLAG(bool, strict_memcmp, true,
+ "If true, assume that memcmp(p1, p2, n) always reads n bytes before "
+ "comparing p1 and p2.")
+COMMON_FLAG(bool, decorate_proc_maps, false, "If set, decorate sanitizer "
+ "mappings in /proc/self/maps with "
+ "user-readable names")
+COMMON_FLAG(int, exitcode, 1, "Override the program exit status if the tool "
+ "found an error")
+COMMON_FLAG(
+ bool, abort_on_error, SANITIZER_MAC,
+ "If set, the tool calls abort() instead of _exit() after printing the "
+ "error report.")
--- /dev/null
+//===-- sanitizer_freebsd.h -------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer runtime. It contains FreeBSD-specific
+// definitions.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_FREEBSD_H
+#define SANITIZER_FREEBSD_H
+
+#include "sanitizer_internal_defs.h"
+
+// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
+// 32-bit mode.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+# include <osreldate.h>
+# if __FreeBSD_version <= 902001 // v9.2
+# include <link.h>
+# include <sys/param.h>
+# include <ucontext.h>
+
+namespace __sanitizer {
+
+typedef unsigned long long __xuint64_t;
+
+typedef __int32_t __xregister_t;
+
+typedef struct __xmcontext {
+ __xregister_t mc_onstack;
+ __xregister_t mc_gs;
+ __xregister_t mc_fs;
+ __xregister_t mc_es;
+ __xregister_t mc_ds;
+ __xregister_t mc_edi;
+ __xregister_t mc_esi;
+ __xregister_t mc_ebp;
+ __xregister_t mc_isp;
+ __xregister_t mc_ebx;
+ __xregister_t mc_edx;
+ __xregister_t mc_ecx;
+ __xregister_t mc_eax;
+ __xregister_t mc_trapno;
+ __xregister_t mc_err;
+ __xregister_t mc_eip;
+ __xregister_t mc_cs;
+ __xregister_t mc_eflags;
+ __xregister_t mc_esp;
+ __xregister_t mc_ss;
+
+ int mc_len;
+ int mc_fpformat;
+ int mc_ownedfp;
+ __xregister_t mc_flags;
+
+ int mc_fpstate[128] __aligned(16);
+ __xregister_t mc_fsbase;
+ __xregister_t mc_gsbase;
+ __xregister_t mc_xfpustate;
+ __xregister_t mc_xfpustate_len;
+
+ int mc_spare2[4];
+} xmcontext_t;
+
+typedef struct __xucontext {
+ sigset_t uc_sigmask;
+ xmcontext_t uc_mcontext;
+
+ struct __ucontext *uc_link;
+ stack_t uc_stack;
+ int uc_flags;
+ int __spare__[4];
+} xucontext_t;
+
+struct xkinfo_vmentry {
+ int kve_structsize;
+ int kve_type;
+ __xuint64_t kve_start;
+ __xuint64_t kve_end;
+ __xuint64_t kve_offset;
+ __xuint64_t kve_vn_fileid;
+ __uint32_t kve_vn_fsid;
+ int kve_flags;
+ int kve_resident;
+ int kve_private_resident;
+ int kve_protection;
+ int kve_ref_count;
+ int kve_shadow_count;
+ int kve_vn_type;
+ __xuint64_t kve_vn_size;
+ __uint32_t kve_vn_rdev;
+ __uint16_t kve_vn_mode;
+ __uint16_t kve_status;
+ int _kve_ispare[12];
+ char kve_path[PATH_MAX];
+};
+
+typedef struct {
+ __uint32_t p_type;
+ __uint32_t p_offset;
+ __uint32_t p_vaddr;
+ __uint32_t p_paddr;
+ __uint32_t p_filesz;
+ __uint32_t p_memsz;
+ __uint32_t p_flags;
+ __uint32_t p_align;
+} XElf32_Phdr;
+
+struct xdl_phdr_info {
+ Elf_Addr dlpi_addr;
+ const char *dlpi_name;
+ const XElf32_Phdr *dlpi_phdr;
+ Elf_Half dlpi_phnum;
+ unsigned long long int dlpi_adds;
+ unsigned long long int dlpi_subs;
+ size_t dlpi_tls_modid;
+ void *dlpi_tls_data;
+};
+
+typedef int (*__xdl_iterate_hdr_callback)(struct xdl_phdr_info*, size_t, void*);
+typedef int xdl_iterate_phdr_t(__xdl_iterate_hdr_callback, void*);
+
+#define xdl_iterate_phdr(callback, param) \
+ (((xdl_iterate_phdr_t*) dl_iterate_phdr)((callback), (param)))
+
+} // namespace __sanitizer
+
+# endif // __FreeBSD_version <= 902001
+#endif // SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+
+#endif // SANITIZER_FREEBSD_H
--- /dev/null
+//===-- sanitizer_interface_internal.h --------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between run-time libraries of sanitizers.
+//
+// This header declares the sanitizer runtime interface functions.
+// The runtime library has to define these functions so the instrumented program
+// could call them.
+//
+// See also include/sanitizer/common_interface_defs.h
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_INTERFACE_INTERNAL_H
+#define SANITIZER_INTERFACE_INTERNAL_H
+
+#include "sanitizer_internal_defs.h"
+
+extern "C" {
+ // Tell the tools to write their reports to "path.<pid>" instead of stderr.
+ // The special values are "stdout" and "stderr".
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __sanitizer_set_report_path(const char *path);
+
+ typedef struct {
+ int coverage_sandboxed;
+ __sanitizer::sptr coverage_fd;
+ unsigned int coverage_max_block_size;
+ } __sanitizer_sandbox_arguments;
+
+ // Notify the tools that the sandbox is going to be turned on.
+ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
+ __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
+
+ // This function is called by the tool when it has just finished reporting
+ // an error. 'error_summary' is a one-line string that summarizes
+ // the error message. This function can be overridden by the client.
+ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+ void __sanitizer_report_error_summary(const char *error_summary);
+
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init();
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __sanitizer_annotate_contiguous_container(const void *beg,
+ const void *end,
+ const void *old_mid,
+ const void *new_mid);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
+ const void *end);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ const void *__sanitizer_contiguous_container_find_bad_address(
+ const void *beg, const void *mid, const void *end);
+ } // extern "C"
+
+#endif // SANITIZER_INTERFACE_INTERNAL_H
#include "sanitizer_platform.h"
+#ifndef SANITIZER_DEBUG
+# define SANITIZER_DEBUG 0
+#endif
+
// Only use SANITIZER_*ATTRIBUTE* before the function return type!
#if SANITIZER_WINDOWS
# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport)
# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
#endif
-#if SANITIZER_LINUX && !defined(SANITIZER_GO)
+#if (SANITIZER_LINUX || SANITIZER_WINDOWS) && !defined(SANITIZER_GO)
# define SANITIZER_SUPPORTS_WEAK_HOOKS 1
#else
# define SANITIZER_SUPPORTS_WEAK_HOOKS 0
#endif
-#if __LP64__ || defined(_WIN64)
-# define SANITIZER_WORDSIZE 64
+// We can use .preinit_array section on Linux to call sanitizer initialization
+// functions very early in the process startup (unless PIC macro is defined).
+// FIXME: do we have anything like this on Mac?
+#if SANITIZER_LINUX && !SANITIZER_ANDROID && !defined(PIC)
+# define SANITIZER_CAN_USE_PREINIT_ARRAY 1
#else
-# define SANITIZER_WORDSIZE 32
+# define SANITIZER_CAN_USE_PREINIT_ARRAY 0
#endif
// GCC does not understand __has_feature
typedef signed long sptr; // NOLINT
#endif // defined(_WIN64)
#if defined(__x86_64__)
-// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use
+// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use
// 64-bit pointer to unwind stack frame.
typedef unsigned long long uhwptr; // NOLINT
#else
typedef signed short s16; // NOLINT
typedef signed int s32;
typedef signed long long s64; // NOLINT
+#if SANITIZER_WINDOWS
+// On Windows, files are HANDLE, which is a synonim of void*.
+// Use void* to avoid including <windows.h> everywhere.
+typedef void* fd_t;
+typedef unsigned error_t;
+#else
typedef int fd_t;
+typedef int error_t;
+#endif
// WARNING: OFF_T may be different from OS type off_t, depending on the value of
// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls
// like pread and mmap, as opposed to pread64 and mmap64.
-// Mac and Linux/x86-64 are special.
-#if SANITIZER_MAC || (SANITIZER_LINUX && defined(__x86_64__))
+// FreeBSD, Mac and Linux/x86-64 are special.
+#if SANITIZER_FREEBSD || SANITIZER_MAC || \
+ (SANITIZER_LINUX && defined(__x86_64__))
typedef u64 OFF_T;
#else
typedef uptr OFF_T;
#endif
} // namespace __sanitizer
-extern "C" {
- // Tell the tools to write their reports to "path.<pid>" instead of stderr.
- // The special values are "stdout" and "stderr".
- SANITIZER_INTERFACE_ATTRIBUTE
- void __sanitizer_set_report_path(const char *path);
-
- // Notify the tools that the sandbox is going to be turned on. The reserved
- // parameter will be used in the future to hold a structure with functions
- // that the tools may call to bypass the sandbox.
- SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
- void __sanitizer_sandbox_on_notify(void *reserved);
-
- // This function is called by the tool when it has just finished reporting
- // an error. 'error_summary' is a one-line string that summarizes
- // the error message. This function can be overridden by the client.
- SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
- void __sanitizer_report_error_summary(const char *error_summary);
-
- SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
- SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(void *pc);
- SANITIZER_INTERFACE_ATTRIBUTE
- void __sanitizer_annotate_contiguous_container(const void *beg,
- const void *end,
- const void *old_mid,
- const void *new_mid);
-} // extern "C"
-
using namespace __sanitizer; // NOLINT
// ----------- ATTENTION -------------
# define NOINLINE __declspec(noinline)
# define NORETURN __declspec(noreturn)
# define THREADLOCAL __declspec(thread)
-# define NOTHROW
# define LIKELY(x) (x)
# define UNLIKELY(x) (x)
-# define UNUSED
-# define USED
# define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */
#else // _MSC_VER
# define ALWAYS_INLINE inline __attribute__((always_inline))
# define NOINLINE __attribute__((noinline))
# define NORETURN __attribute__((noreturn))
# define THREADLOCAL __thread
-# define NOTHROW throw()
# define LIKELY(x) __builtin_expect(!!(x), 1)
# define UNLIKELY(x) __builtin_expect(!!(x), 0)
-# define UNUSED __attribute__((unused))
-# define USED __attribute__((used))
# if defined(__i386__) || defined(__x86_64__)
// __builtin_prefetch(x) generates prefetchnt0 on x86
# define PREFETCH(x) __asm__("prefetchnta (%0)" : : "r" (x))
# endif
#endif // _MSC_VER
+#if !defined(_MSC_VER) || defined(__clang__)
+# define UNUSED __attribute__((unused))
+# define USED __attribute__((used))
+#else
+# define UNUSED
+# define USED
+#endif
+
+#if !defined(_MSC_VER) || defined(__clang__) || MSC_PREREQ(1900)
+# define NOEXCEPT noexcept
+#else
+# define NOEXCEPT throw()
+#endif
+
// Unaligned versions of basic types.
typedef ALIGNED(1) u16 uu16;
typedef ALIGNED(1) u32 uu32;
// Check macro
#define RAW_CHECK_MSG(expr, msg) do { \
- if (!(expr)) { \
+ if (UNLIKELY(!(expr))) { \
RawWrite(msg); \
Die(); \
} \
do { \
__sanitizer::u64 v1 = (u64)(c1); \
__sanitizer::u64 v2 = (u64)(c2); \
- if (!(v1 op v2)) \
+ if (UNLIKELY(!(v1 op v2))) \
__sanitizer::CheckFailed(__FILE__, __LINE__, \
"(" #c1 ") " #op " (" #c2 ")", v1, v2); \
} while (false) \
#define CHECK_GT(a, b) CHECK_IMPL((a), >, (b))
#define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b))
-#if TSAN_DEBUG
+#if SANITIZER_DEBUG
#define DCHECK(a) CHECK(a)
#define DCHECK_EQ(a, b) CHECK_EQ(a, b)
#define DCHECK_NE(a, b) CHECK_NE(a, b)
} while (internal_iserror(res, &rverrno) && rverrno == EINTR); \
}
+// Forces the compiler to generate a frame pointer in the function.
+#define ENABLE_FRAME_POINTER \
+ do { \
+ volatile uptr enable_fp; \
+ enable_fp = GET_CURRENT_FRAME(); \
+ (void)enable_fp; \
+ } while (0)
+
#endif // SANITIZER_DEFS_H
u64 cmp = atomic_load(&head_, memory_order_acquire);
for (;;) {
T *cur = (T*)(uptr)(cmp & kPtrMask);
- if (cur == 0)
- return 0;
+ if (!cur)
+ return nullptr;
T *nxt = cur->next;
u64 cnt = (cmp & kCounterMask);
u64 xch = (u64)(uptr)nxt | cnt;
atomic_uint64_t head_;
};
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // #ifndef SANITIZER_LFSTACK_H
+#endif // SANITIZER_LFSTACK_H
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries. See sanitizer_libc.h for details.
//===----------------------------------------------------------------------===//
+
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
namespace __sanitizer {
-// Make the compiler think that something is going on there.
-static inline void break_optimization(void *arg) {
-#if SANITIZER_WINDOWS
- // FIXME: make sure this is actually enough.
- __asm;
-#else
- __asm__ __volatile__("" : : "r" (arg) : "memory");
-#endif
-}
-
s64 internal_atoll(const char *nptr) {
- return internal_simple_strtoll(nptr, (char**)0, 10);
+ return internal_simple_strtoll(nptr, nullptr, 10);
}
void *internal_memchr(const void *s, int c, uptr n) {
- const char* t = (char*)s;
+ const char *t = (const char *)s;
for (uptr i = 0; i < n; ++i, ++t)
if (*t == c)
- return (void*)t;
- return 0;
+ return reinterpret_cast<void *>(const_cast<char *>(t));
+ return nullptr;
+}
+
+void *internal_memrchr(const void *s, int c, uptr n) {
+ const char *t = (const char *)s;
+ void *res = nullptr;
+ for (uptr i = 0; i < n; ++i, ++t) {
+ if (*t == c) res = reinterpret_cast<void *>(const_cast<char *>(t));
+ }
+ return res;
}
int internal_memcmp(const void* s1, const void* s2, uptr n) {
- const char* t1 = (char*)s1;
- const char* t2 = (char*)s2;
+ const char *t1 = (const char *)s1;
+ const char *t2 = (const char *)s2;
for (uptr i = 0; i < n; ++i, ++t1, ++t2)
if (*t1 != *t2)
return *t1 < *t2 ? -1 : 1;
void *internal_memcpy(void *dest, const void *src, uptr n) {
char *d = (char*)dest;
- char *s = (char*)src;
+ const char *s = (const char *)src;
for (uptr i = 0; i < n; ++i)
d[i] = s[i];
return dest;
void *internal_memmove(void *dest, const void *src, uptr n) {
char *d = (char*)dest;
- char *s = (char*)src;
+ const char *s = (const char *)src;
sptr i, signed_n = (sptr)n;
CHECK_GE(signed_n, 0);
if (d < s) {
CHECK_EQ((reinterpret_cast<uptr>(s) | n) & 15, 0);
for (S16 *p = reinterpret_cast<S16*>(s), *end = p + n / 16; p < end; p++) {
p->a = p->b = 0;
- break_optimization(0); // Make sure this does not become memset.
+ // Make sure this does not become memset.
+ SanitizerBreakOptimization(nullptr);
}
}
uptr internal_strcspn(const char *s, const char *reject) {
uptr i;
for (i = 0; s[i]; i++) {
- if (internal_strchr(reject, s[i]) != 0)
+ if (internal_strchr(reject, s[i]))
return i;
}
return i;
return s2;
}
+char* internal_strndup(const char *s, uptr n) {
+ uptr len = internal_strnlen(s, n);
+ char *s2 = (char*)InternalAlloc(len + 1);
+ internal_memcpy(s2, s, len);
+ s2[len] = 0;
+ return s2;
+}
+
int internal_strcmp(const char *s1, const char *s2) {
while (true) {
unsigned c1 = *s1;
char* internal_strchr(const char *s, int c) {
while (true) {
if (*s == (char)c)
- return (char*)s;
+ return const_cast<char *>(s);
if (*s == 0)
- return 0;
+ return nullptr;
s++;
}
}
char *internal_strchrnul(const char *s, int c) {
char *res = internal_strchr(s, c);
if (!res)
- res = (char*)s + internal_strlen(s);
+ res = const_cast<char *>(s) + internal_strlen(s);
return res;
}
char *internal_strrchr(const char *s, int c) {
- const char *res = 0;
+ const char *res = nullptr;
for (uptr i = 0; s[i]; i++) {
if (s[i] == c) res = s + i;
}
- return (char*)res;
+ return const_cast<char *>(res);
}
uptr internal_strlen(const char *s) {
// This is O(N^2), but we are not using it in hot places.
uptr len1 = internal_strlen(haystack);
uptr len2 = internal_strlen(needle);
- if (len1 < len2) return 0;
+ if (len1 < len2) return nullptr;
for (uptr pos = 0; pos <= len1 - len2; pos++) {
if (internal_memcmp(haystack + pos, needle, len2) == 0)
- return (char*)haystack + pos;
+ return const_cast<char *>(haystack) + pos;
}
- return 0;
+ return nullptr;
}
s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) {
int sgn = 1;
u64 res = 0;
bool have_digits = false;
- char *old_nptr = (char*)nptr;
+ char *old_nptr = const_cast<char *>(nptr);
if (*nptr == '+') {
sgn = 1;
nptr++;
have_digits = true;
nptr++;
}
- if (endptr != 0) {
- *endptr = (have_digits) ? (char*)nptr : old_nptr;
+ if (endptr) {
+ *endptr = (have_digits) ? const_cast<char *>(nptr) : old_nptr;
}
if (sgn > 0) {
return (s64)(Min((u64)INT64_MAX, res));
return all == 0;
}
-} // namespace __sanitizer
+} // namespace __sanitizer
// run-time libraries.
// These tools can not use some of the libc functions directly because those
// functions are intercepted. Instead, we implement a tiny subset of libc here.
+// FIXME: Some of functions declared in this file are in fact POSIX, not libc.
//===----------------------------------------------------------------------===//
+
#ifndef SANITIZER_LIBC_H
#define SANITIZER_LIBC_H
// String functions
s64 internal_atoll(const char *nptr);
void *internal_memchr(const void *s, int c, uptr n);
+void *internal_memrchr(const void *s, int c, uptr n);
int internal_memcmp(const void* s1, const void* s2, uptr n);
void *internal_memcpy(void *dest, const void *src, uptr n);
void *internal_memmove(void *dest, const void *src, uptr n);
int internal_strcmp(const char *s1, const char *s2);
uptr internal_strcspn(const char *s, const char *reject);
char *internal_strdup(const char *s);
+char *internal_strndup(const char *s, uptr n);
uptr internal_strlen(const char *s);
char *internal_strncat(char *dst, const char *src, uptr n);
int internal_strncmp(const char *s1, const char *s2, uptr n);
// Optimized for the case when the result is true.
bool mem_is_zero(const char *mem, uptr size);
-
-// Memory
-uptr internal_mmap(void *addr, uptr length, int prot, int flags,
- int fd, u64 offset);
-uptr internal_munmap(void *addr, uptr length);
-
// I/O
-const fd_t kInvalidFd = -1;
+const fd_t kInvalidFd = (fd_t)-1;
const fd_t kStdinFd = 0;
-const fd_t kStdoutFd = 1;
-const fd_t kStderrFd = 2;
-uptr internal_close(fd_t fd);
-int internal_isatty(fd_t fd);
-
-// Use __sanitizer::OpenFile() instead.
-uptr internal_open(const char *filename, int flags);
-uptr internal_open(const char *filename, int flags, u32 mode);
+const fd_t kStdoutFd = (fd_t)1;
+const fd_t kStderrFd = (fd_t)2;
-uptr internal_read(fd_t fd, void *buf, uptr count);
-uptr internal_write(fd_t fd, const void *buf, uptr count);
+uptr internal_ftruncate(fd_t fd, uptr size);
// OS
-uptr internal_filesize(fd_t fd); // -1 on error.
-uptr internal_stat(const char *path, void *buf);
-uptr internal_lstat(const char *path, void *buf);
-uptr internal_fstat(fd_t fd, void *buf);
-uptr internal_dup2(int oldfd, int newfd);
-uptr internal_readlink(const char *path, char *buf, uptr bufsize);
-uptr internal_unlink(const char *path);
void NORETURN internal__exit(int exitcode);
-uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
-uptr internal_ptrace(int request, int pid, void *addr, void *data);
-uptr internal_waitpid(int pid, int *status, int options);
uptr internal_getpid();
uptr internal_getppid();
uptr internal_sched_yield();
// Error handling
-bool internal_iserror(uptr retval, int *rverrno = 0);
+bool internal_iserror(uptr retval, int *rverrno = nullptr);
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_LIBC_H
+#endif // SANITIZER_LIBC_H
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC
#include "sanitizer_libignore.h"
#include "sanitizer_flags.h"
+#include "sanitizer_posix.h"
#include "sanitizer_procmaps.h"
namespace __sanitizer {
LibIgnore::LibIgnore(LinkerInitialized) {
}
-void LibIgnore::Init(const SuppressionContext &supp) {
+void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
BlockingMutexLock lock(&mutex_);
- CHECK_EQ(count_, 0);
- const uptr n = supp.SuppressionCount();
- for (uptr i = 0; i < n; i++) {
- const Suppression *s = supp.SuppressionAt(i);
- if (s->type != SuppressionLib)
- continue;
- if (count_ >= kMaxLibs) {
- Report("%s: too many called_from_lib suppressions (max: %d)\n",
- SanitizerToolName, kMaxLibs);
- Die();
- }
- Lib *lib = &libs_[count_++];
- lib->templ = internal_strdup(s->templ);
- lib->name = 0;
- lib->loaded = false;
+ if (count_ >= kMaxLibs) {
+ Report("%s: too many ignored libraries (max: %d)\n", SanitizerToolName,
+ kMaxLibs);
+ Die();
}
+ Lib *lib = &libs_[count_++];
+ lib->templ = internal_strdup(name_templ);
+ lib->name = nullptr;
+ lib->real_name = nullptr;
+ lib->loaded = false;
}
void LibIgnore::OnLibraryLoaded(const char *name) {
BlockingMutexLock lock(&mutex_);
// Try to match suppressions with symlink target.
- InternalScopedBuffer<char> buf(4096);
- if (name != 0 && internal_readlink(name, buf.data(), buf.size() - 1) > 0 &&
- buf.data()[0]) {
+ InternalScopedString buf(kMaxPathLength);
+ if (name && internal_readlink(name, buf.data(), buf.size() - 1) > 0 &&
+ buf[0]) {
for (uptr i = 0; i < count_; i++) {
Lib *lib = &libs_[i];
- if (!lib->loaded && lib->real_name == 0 &&
+ if (!lib->loaded && (!lib->real_name) &&
TemplateMatch(lib->templ, name))
lib->real_name = internal_strdup(buf.data());
}
// Scan suppressions list and find newly loaded and unloaded libraries.
MemoryMappingLayout proc_maps(/*cache_enabled*/false);
- InternalScopedBuffer<char> module(4096);
+ InternalScopedString module(kMaxPathLength);
for (uptr i = 0; i < count_; i++) {
Lib *lib = &libs_[i];
bool loaded = false;
if ((prot & MemoryMappingLayout::kProtectionExecute) == 0)
continue;
if (TemplateMatch(lib->templ, module.data()) ||
- (lib->real_name != 0 &&
+ (lib->real_name &&
internal_strcmp(lib->real_name, module.data()) == 0)) {
if (loaded) {
Report("%s: called_from_lib suppression '%s' is matched against"
loaded = true;
if (lib->loaded)
continue;
- if (common_flags()->verbosity)
- Report("Matched called_from_lib suppression '%s' against library"
- " '%s'\n", lib->templ, module.data());
+ VReport(1,
+ "Matched called_from_lib suppression '%s' against library"
+ " '%s'\n",
+ lib->templ, module.data());
lib->loaded = true;
lib->name = internal_strdup(module.data());
const uptr idx = atomic_load(&loaded_count_, memory_order_relaxed);
}
void LibIgnore::OnLibraryUnloaded() {
- OnLibraryLoaded(0);
+ OnLibraryLoaded(nullptr);
}
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // #if SANITIZER_LINUX
+#endif // #if SANITIZER_FREEBSD || SANITIZER_LINUX
//===----------------------------------------------------------------------===//
//
// LibIgnore allows to ignore all interceptors called from a particular set
-// of dynamic libraries. LibIgnore remembers all "called_from_lib" suppressions
-// from the provided SuppressionContext; finds code ranges for the libraries;
+// of dynamic libraries. LibIgnore can be initialized with several templates
+// of names of libraries to be ignored. It finds code ranges for the libraries;
// and checks whether the provided PC value belongs to the code ranges.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_internal_defs.h"
#include "sanitizer_common.h"
-#include "sanitizer_suppressions.h"
#include "sanitizer_atomic.h"
#include "sanitizer_mutex.h"
public:
explicit LibIgnore(LinkerInitialized);
- // Fetches all "called_from_lib" suppressions from the SuppressionContext.
- void Init(const SuppressionContext &supp);
+ // Must be called during initialization.
+ void AddIgnoredLibrary(const char *name_templ);
// Must be called after a new dynamic library is loaded.
void OnLibraryLoaded(const char *name);
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_libc.h"
#include "sanitizer_linux.h"
#include "sanitizer_stacktrace.h"
#include "sanitizer_symbolizer.h"
+#if !SANITIZER_FREEBSD
#include <asm/param.h>
+#endif
+
+// For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat'
+// format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To
+// access stat from asm/stat.h, without conflicting with definition in
+// sys/stat.h, we use this trick.
+#if defined(__mips64)
+#include <asm/unistd.h>
+#include <sys/types.h>
+#define stat kernel_stat
+#include <asm/stat.h>
+#undef stat
+#endif
+
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
-#if !SANITIZER_ANDROID
#include <link.h>
-#endif
#include <pthread.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <ucontext.h>
#include <unistd.h>
-#include <unwind.h>
+
+#if SANITIZER_FREEBSD
+#include <sys/sysctl.h>
+#include <machine/atomic.h>
+extern "C" {
+// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
+// FreeBSD 9.2 and 10.0.
+#include <sys/umtx.h>
+}
+extern char **environ; // provided by crt1
+#endif // SANITIZER_FREEBSD
#if !SANITIZER_ANDROID
#include <sys/signal.h>
#endif
+#if SANITIZER_LINUX
// <linux/time.h>
struct kernel_timeval {
long tv_sec;
// <linux/futex.h> is broken on some linux distributions.
const int FUTEX_WAIT = 0;
const int FUTEX_WAKE = 1;
+#endif // SANITIZER_LINUX
-// Are we using 32-bit or 64-bit syscalls?
+// Are we using 32-bit or 64-bit Linux syscalls?
// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
// but it still needs to use 64-bit syscalls.
-#if defined(__x86_64__) || SANITIZER_WORDSIZE == 64
+#if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_WORDSIZE == 64)
# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1
#else
# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
namespace __sanitizer {
-#ifdef __x86_64__
+#if SANITIZER_LINUX && defined(__x86_64__)
#include "sanitizer_syscall_linux_x86_64.inc"
+#elif SANITIZER_LINUX && defined(__aarch64__)
+#include "sanitizer_syscall_linux_aarch64.inc"
#else
#include "sanitizer_syscall_generic.inc"
#endif
// --------------- sanitizer_libc.h
-uptr internal_mmap(void *addr, uptr length, int prot, int flags,
- int fd, u64 offset) {
-#if SANITIZER_LINUX_USES_64BIT_SYSCALLS
- return internal_syscall(__NR_mmap, (uptr)addr, length, prot, flags, fd,
+uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
+ OFF_T offset) {
+#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
+ return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd,
offset);
#else
- return internal_syscall(__NR_mmap2, addr, length, prot, flags, fd, offset);
+ // mmap2 specifies file offset in 4096-byte units.
+ CHECK(IsAligned(offset, 4096));
+ return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd,
+ offset / 4096);
#endif
}
uptr internal_munmap(void *addr, uptr length) {
- return internal_syscall(__NR_munmap, (uptr)addr, length);
+ return internal_syscall(SYSCALL(munmap), (uptr)addr, length);
+}
+
+int internal_mprotect(void *addr, uptr length, int prot) {
+ return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot);
}
uptr internal_close(fd_t fd) {
- return internal_syscall(__NR_close, fd);
+ return internal_syscall(SYSCALL(close), fd);
}
uptr internal_open(const char *filename, int flags) {
- return internal_syscall(__NR_open, (uptr)filename, flags);
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags);
+#else
+ return internal_syscall(SYSCALL(open), (uptr)filename, flags);
+#endif
}
uptr internal_open(const char *filename, int flags, u32 mode) {
- return internal_syscall(__NR_open, (uptr)filename, flags, mode);
-}
-
-uptr OpenFile(const char *filename, bool write) {
- return internal_open(filename,
- write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660);
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags,
+ mode);
+#else
+ return internal_syscall(SYSCALL(open), (uptr)filename, flags, mode);
+#endif
}
uptr internal_read(fd_t fd, void *buf, uptr count) {
sptr res;
- HANDLE_EINTR(res, (sptr)internal_syscall(__NR_read, fd, (uptr)buf, count));
+ HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf,
+ count));
return res;
}
uptr internal_write(fd_t fd, const void *buf, uptr count) {
sptr res;
- HANDLE_EINTR(res, (sptr)internal_syscall(__NR_write, fd, (uptr)buf, count));
+ HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(write), fd, (uptr)buf,
+ count));
return res;
}
-#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS
+uptr internal_ftruncate(fd_t fd, uptr size) {
+ sptr res;
+ HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd,
+ (OFF_T)size));
+ return res;
+}
+
+#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && !SANITIZER_FREEBSD
static void stat64_to_stat(struct stat64 *in, struct stat *out) {
internal_memset(out, 0, sizeof(*out));
out->st_dev = in->st_dev;
}
#endif
+#if defined(__mips64)
+static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
+ internal_memset(out, 0, sizeof(*out));
+ out->st_dev = in->st_dev;
+ out->st_ino = in->st_ino;
+ out->st_mode = in->st_mode;
+ out->st_nlink = in->st_nlink;
+ out->st_uid = in->st_uid;
+ out->st_gid = in->st_gid;
+ out->st_rdev = in->st_rdev;
+ out->st_size = in->st_size;
+ out->st_blksize = in->st_blksize;
+ out->st_blocks = in->st_blocks;
+ out->st_atime = in->st_atime_nsec;
+ out->st_mtime = in->st_mtime_nsec;
+ out->st_ctime = in->st_ctime_nsec;
+ out->st_ino = in->st_ino;
+}
+#endif
+
uptr internal_stat(const char *path, void *buf) {
-#if SANITIZER_LINUX_USES_64BIT_SYSCALLS
- return internal_syscall(__NR_stat, (uptr)path, (uptr)buf);
+#if SANITIZER_FREEBSD
+ return internal_syscall(SYSCALL(stat), path, buf);
+#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path,
+ (uptr)buf, 0);
+#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
+# if defined(__mips64)
+ // For mips64, stat syscall fills buffer in the format of kernel_stat
+ struct kernel_stat kbuf;
+ int res = internal_syscall(SYSCALL(stat), path, &kbuf);
+ kernel_stat_to_stat(&kbuf, (struct stat *)buf);
+ return res;
+# else
+ return internal_syscall(SYSCALL(stat), (uptr)path, (uptr)buf);
+# endif
#else
struct stat64 buf64;
- int res = internal_syscall(__NR_stat64, path, &buf64);
+ int res = internal_syscall(SYSCALL(stat64), path, &buf64);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
#endif
}
uptr internal_lstat(const char *path, void *buf) {
-#if SANITIZER_LINUX_USES_64BIT_SYSCALLS
- return internal_syscall(__NR_lstat, (uptr)path, (uptr)buf);
+#if SANITIZER_FREEBSD
+ return internal_syscall(SYSCALL(lstat), path, buf);
+#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path,
+ (uptr)buf, AT_SYMLINK_NOFOLLOW);
+#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
+ return internal_syscall(SYSCALL(lstat), (uptr)path, (uptr)buf);
#else
struct stat64 buf64;
- int res = internal_syscall(__NR_lstat64, path, &buf64);
+ int res = internal_syscall(SYSCALL(lstat64), path, &buf64);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
#endif
}
uptr internal_fstat(fd_t fd, void *buf) {
-#if SANITIZER_LINUX_USES_64BIT_SYSCALLS
- return internal_syscall(__NR_fstat, fd, (uptr)buf);
+#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
+ return internal_syscall(SYSCALL(fstat), fd, (uptr)buf);
#else
struct stat64 buf64;
- int res = internal_syscall(__NR_fstat64, fd, &buf64);
+ int res = internal_syscall(SYSCALL(fstat64), fd, &buf64);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
#endif
}
uptr internal_dup2(int oldfd, int newfd) {
- return internal_syscall(__NR_dup2, oldfd, newfd);
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(dup3), oldfd, newfd, 0);
+#else
+ return internal_syscall(SYSCALL(dup2), oldfd, newfd);
+#endif
}
uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
- return internal_syscall(__NR_readlink, (uptr)path, (uptr)buf, bufsize);
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(readlinkat), AT_FDCWD,
+ (uptr)path, (uptr)buf, bufsize);
+#else
+ return internal_syscall(SYSCALL(readlink), (uptr)path, (uptr)buf, bufsize);
+#endif
}
uptr internal_unlink(const char *path) {
- return internal_syscall(__NR_unlink, (uptr)path);
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0);
+#else
+ return internal_syscall(SYSCALL(unlink), (uptr)path);
+#endif
+}
+
+uptr internal_rename(const char *oldpath, const char *newpath) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
+ (uptr)newpath);
+#else
+ return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
+#endif
}
uptr internal_sched_yield() {
- return internal_syscall(__NR_sched_yield);
+ return internal_syscall(SYSCALL(sched_yield));
}
void internal__exit(int exitcode) {
- internal_syscall(__NR_exit_group, exitcode);
+#if SANITIZER_FREEBSD
+ internal_syscall(SYSCALL(exit), exitcode);
+#else
+ internal_syscall(SYSCALL(exit_group), exitcode);
+#endif
Die(); // Unreachable.
}
uptr internal_execve(const char *filename, char *const argv[],
char *const envp[]) {
- return internal_syscall(__NR_execve, (uptr)filename, (uptr)argv, (uptr)envp);
+ return internal_syscall(SYSCALL(execve), (uptr)filename, (uptr)argv,
+ (uptr)envp);
}
// ----------------- sanitizer_common.h
bool FileExists(const char *filename) {
struct stat st;
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ if (internal_syscall(SYSCALL(newfstatat), AT_FDCWD, filename, &st, 0))
+#else
if (internal_stat(filename, &st))
+#endif
return false;
// Sanity check: filename is a regular file.
return S_ISREG(st.st_mode);
}
uptr GetTid() {
- return internal_syscall(__NR_gettid);
+#if SANITIZER_FREEBSD
+ return (uptr)pthread_self();
+#else
+ return internal_syscall(SYSCALL(gettid));
+#endif
}
u64 NanoTime() {
+#if SANITIZER_FREEBSD
+ timeval tv;
+#else
kernel_timeval tv;
+#endif
internal_memset(&tv, 0, sizeof(tv));
- internal_syscall(__NR_gettimeofday, (uptr)&tv, 0);
+ internal_syscall(SYSCALL(gettimeofday), (uptr)&tv, 0);
return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
}
-// Like getenv, but reads env directly from /proc and does not use libc.
-// This function should be called first inside __asan_init.
+// Like getenv, but reads env directly from /proc (on Linux) or parses the
+// 'environ' array (on FreeBSD) and does not use libc. This function should be
+// called first inside __asan_init.
const char *GetEnv(const char *name) {
+#if SANITIZER_FREEBSD
+ if (::environ != 0) {
+ uptr NameLen = internal_strlen(name);
+ for (char **Env = ::environ; *Env != 0; Env++) {
+ if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=')
+ return (*Env) + NameLen + 1;
+ }
+ }
+ return 0; // Not found.
+#elif SANITIZER_LINUX
static char *environ;
static uptr len;
static bool inited;
if (!inited) {
inited = true;
uptr environ_size;
- len = ReadFileToBuffer("/proc/self/environ",
- &environ, &environ_size, 1 << 26);
+ if (!ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, &len))
+ environ = nullptr;
}
- if (!environ || len == 0) return 0;
+ if (!environ || len == 0) return nullptr;
uptr namelen = internal_strlen(name);
const char *p = environ;
while (*p != '\0') { // will happen at the \0\0 that terminates the buffer
// proc file has the format NAME=value\0NAME=value\0NAME=value\0...
const char* endp =
(char*)internal_memchr(p, '\0', len - (p - environ));
- if (endp == 0) // this entry isn't NUL terminated
- return 0;
+ if (!endp) // this entry isn't NUL terminated
+ return nullptr;
else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match.
return p + namelen + 1; // point after =
p = endp + 1;
}
- return 0; // Not found.
+ return nullptr; // Not found.
+#else
+#error "Unsupported platform"
+#endif
}
extern "C" {
static void ReadNullSepFileToArray(const char *path, char ***arr,
int arr_size) {
char *buff;
- uptr buff_size = 0;
+ uptr buff_size;
+ uptr buff_len;
*arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray");
- ReadFileToBuffer(path, &buff, &buff_size, 1024 * 1024);
+ if (!ReadFileToBuffer(path, &buff, &buff_size, &buff_len, 1024 * 1024)) {
+ (*arr)[0] = nullptr;
+ return;
+ }
(*arr)[0] = buff;
int count, i;
for (count = 1, i = 1; ; i++) {
count++;
}
}
- (*arr)[count] = 0;
+ (*arr)[count] = nullptr;
}
#endif
Die();
}
-void PrepareForSandboxing() {
- // Some kinds of sandboxes may forbid filesystem access, so we won't be able
- // to read the file mappings from /proc/self/maps. Luckily, neither the
- // process will be able to load additional libraries, so it's fine to use the
- // cached mappings.
- MemoryMappingLayout::CacheMemoryMappings();
- // Same for /proc/self/exe in the symbolizer.
-#if !SANITIZER_GO
- if (Symbolizer *sym = Symbolizer::GetOrNull())
- sym->PrepareForSandboxing();
-#endif
-}
-
-// ----------------- sanitizer_procmaps.h
-// Linker initialized.
-ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
-StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
-
-MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
- proc_self_maps_.len =
- ReadFileToBuffer("/proc/self/maps", &proc_self_maps_.data,
- &proc_self_maps_.mmaped_size, 1 << 26);
- if (cache_enabled) {
- if (proc_self_maps_.mmaped_size == 0) {
- LoadFromCache();
- CHECK_GT(proc_self_maps_.len, 0);
- }
- } else {
- CHECK_GT(proc_self_maps_.mmaped_size, 0);
- }
- Reset();
- // FIXME: in the future we may want to cache the mappings on demand only.
- if (cache_enabled)
- CacheMemoryMappings();
-}
-
-MemoryMappingLayout::~MemoryMappingLayout() {
- // Only unmap the buffer if it is different from the cached one. Otherwise
- // it will be unmapped when the cache is refreshed.
- if (proc_self_maps_.data != cached_proc_self_maps_.data) {
- UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
- }
-}
-
-void MemoryMappingLayout::Reset() {
- current_ = proc_self_maps_.data;
-}
-
-// static
-void MemoryMappingLayout::CacheMemoryMappings() {
- SpinMutexLock l(&cache_lock_);
- // Don't invalidate the cache if the mappings are unavailable.
- ProcSelfMapsBuff old_proc_self_maps;
- old_proc_self_maps = cached_proc_self_maps_;
- cached_proc_self_maps_.len =
- ReadFileToBuffer("/proc/self/maps", &cached_proc_self_maps_.data,
- &cached_proc_self_maps_.mmaped_size, 1 << 26);
- if (cached_proc_self_maps_.mmaped_size == 0) {
- cached_proc_self_maps_ = old_proc_self_maps;
- } else {
- if (old_proc_self_maps.mmaped_size) {
- UnmapOrDie(old_proc_self_maps.data,
- old_proc_self_maps.mmaped_size);
- }
- }
-}
-
-void MemoryMappingLayout::LoadFromCache() {
- SpinMutexLock l(&cache_lock_);
- if (cached_proc_self_maps_.data) {
- proc_self_maps_ = cached_proc_self_maps_;
- }
-}
-
-// Parse a hex value in str and update str.
-static uptr ParseHex(char **str) {
- uptr x = 0;
- char *s;
- for (s = *str; ; s++) {
- char c = *s;
- uptr v = 0;
- if (c >= '0' && c <= '9')
- v = c - '0';
- else if (c >= 'a' && c <= 'f')
- v = c - 'a' + 10;
- else if (c >= 'A' && c <= 'F')
- v = c - 'A' + 10;
- else
- break;
- x = x * 16 + v;
- }
- *str = s;
- return x;
-}
-
-static bool IsOneOf(char c, char c1, char c2) {
- return c == c1 || c == c2;
-}
-
-static bool IsDecimal(char c) {
- return c >= '0' && c <= '9';
-}
-
-static bool IsHex(char c) {
- return (c >= '0' && c <= '9')
- || (c >= 'a' && c <= 'f');
-}
-
-static uptr ReadHex(const char *p) {
- uptr v = 0;
- for (; IsHex(p[0]); p++) {
- if (p[0] >= '0' && p[0] <= '9')
- v = v * 16 + p[0] - '0';
- else
- v = v * 16 + p[0] - 'a' + 10;
- }
- return v;
-}
-
-static uptr ReadDecimal(const char *p) {
- uptr v = 0;
- for (; IsDecimal(p[0]); p++)
- v = v * 10 + p[0] - '0';
- return v;
-}
-
-
-bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
- char filename[], uptr filename_size,
- uptr *protection) {
- char *last = proc_self_maps_.data + proc_self_maps_.len;
- if (current_ >= last) return false;
- uptr dummy;
- if (!start) start = &dummy;
- if (!end) end = &dummy;
- if (!offset) offset = &dummy;
- char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
- if (next_line == 0)
- next_line = last;
- // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar
- *start = ParseHex(¤t_);
- CHECK_EQ(*current_++, '-');
- *end = ParseHex(¤t_);
- CHECK_EQ(*current_++, ' ');
- uptr local_protection = 0;
- CHECK(IsOneOf(*current_, '-', 'r'));
- if (*current_++ == 'r')
- local_protection |= kProtectionRead;
- CHECK(IsOneOf(*current_, '-', 'w'));
- if (*current_++ == 'w')
- local_protection |= kProtectionWrite;
- CHECK(IsOneOf(*current_, '-', 'x'));
- if (*current_++ == 'x')
- local_protection |= kProtectionExecute;
- CHECK(IsOneOf(*current_, 's', 'p'));
- if (*current_++ == 's')
- local_protection |= kProtectionShared;
- if (protection) {
- *protection = local_protection;
- }
- CHECK_EQ(*current_++, ' ');
- *offset = ParseHex(¤t_);
- CHECK_EQ(*current_++, ' ');
- ParseHex(¤t_);
- CHECK_EQ(*current_++, ':');
- ParseHex(¤t_);
- CHECK_EQ(*current_++, ' ');
- while (IsDecimal(*current_))
- current_++;
- // Qemu may lack the trailing space.
- // http://code.google.com/p/address-sanitizer/issues/detail?id=160
- // CHECK_EQ(*current_++, ' ');
- // Skip spaces.
- while (current_ < next_line && *current_ == ' ')
- current_++;
- // Fill in the filename.
- uptr i = 0;
- while (current_ < next_line) {
- if (filename && i < filename_size - 1)
- filename[i++] = *current_;
- current_++;
- }
- if (filename && i < filename_size)
- filename[i] = 0;
- current_ = next_line + 1;
- return true;
-}
-
-// Gets the object name and the offset by walking MemoryMappingLayout.
-bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
- char filename[],
- uptr filename_size,
- uptr *protection) {
- return IterateForObjectNameAndOffset(addr, offset, filename, filename_size,
- protection);
-}
-
-void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
- char *smaps = 0;
- uptr smaps_cap = 0;
- uptr smaps_len = ReadFileToBuffer("/proc/self/smaps",
- &smaps, &smaps_cap, 64<<20);
- uptr start = 0;
- bool file = false;
- const char *pos = smaps;
- while (pos < smaps + smaps_len) {
- if (IsHex(pos[0])) {
- start = ReadHex(pos);
- for (; *pos != '/' && *pos > '\n'; pos++) {}
- file = *pos == '/';
- } else if (internal_strncmp(pos, "Rss:", 4) == 0) {
- for (; *pos < '0' || *pos > '9'; pos++) {}
- uptr rss = ReadDecimal(pos) * 1024;
- cb(start, rss, file, stats, stats_size);
- }
- while (*pos++ != '\n') {}
- }
- UnmapOrDie(smaps, smaps_cap);
-}
-
enum MutexState {
MtxUnlocked = 0,
MtxLocked = 1,
MtxSleeping = 2
};
-BlockingMutex::BlockingMutex(LinkerInitialized) {
- CHECK_EQ(owner_, 0);
-}
-
BlockingMutex::BlockingMutex() {
internal_memset(this, 0, sizeof(*this));
}
void BlockingMutex::Lock() {
+ CHECK_EQ(owner_, 0);
atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
return;
- while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked)
- internal_syscall(__NR_futex, (uptr)m, FUTEX_WAIT, MtxSleeping, 0, 0, 0);
+ while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
+#if SANITIZER_FREEBSD
+ _umtx_op(m, UMTX_OP_WAIT_UINT, MtxSleeping, 0, 0);
+#else
+ internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAIT, MtxSleeping, 0, 0, 0);
+#endif
+ }
}
void BlockingMutex::Unlock() {
atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed);
CHECK_NE(v, MtxUnlocked);
- if (v == MtxSleeping)
- internal_syscall(__NR_futex, (uptr)m, FUTEX_WAKE, 1, 0, 0, 0);
+ if (v == MtxSleeping) {
+#if SANITIZER_FREEBSD
+ _umtx_op(m, UMTX_OP_WAKE, 1, 0, 0);
+#else
+ internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAKE, 1, 0, 0, 0);
+#endif
+ }
}
void BlockingMutex::CheckLocked() {
// Note that getdents64 uses a different structure format. We only provide the
// 32-bit syscall here.
struct linux_dirent {
+#if SANITIZER_X32 || defined(__aarch64__)
+ u64 d_ino;
+ u64 d_off;
+#else
unsigned long d_ino;
unsigned long d_off;
+#endif
unsigned short d_reclen;
+#ifdef __aarch64__
+ unsigned char d_type;
+#endif
char d_name[256];
};
// Syscall wrappers.
uptr internal_ptrace(int request, int pid, void *addr, void *data) {
- return internal_syscall(__NR_ptrace, request, pid, (uptr)addr, (uptr)data);
+ return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr,
+ (uptr)data);
}
uptr internal_waitpid(int pid, int *status, int options) {
- return internal_syscall(__NR_wait4, pid, (uptr)status, options,
+ return internal_syscall(SYSCALL(wait4), pid, (uptr)status, options,
0 /* rusage */);
}
uptr internal_getpid() {
- return internal_syscall(__NR_getpid);
+ return internal_syscall(SYSCALL(getpid));
}
uptr internal_getppid() {
- return internal_syscall(__NR_getppid);
+ return internal_syscall(SYSCALL(getppid));
}
uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) {
- return internal_syscall(__NR_getdents, fd, (uptr)dirp, count);
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(getdents64), fd, (uptr)dirp, count);
+#else
+ return internal_syscall(SYSCALL(getdents), fd, (uptr)dirp, count);
+#endif
}
uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
- return internal_syscall(__NR_lseek, fd, offset, whence);
+ return internal_syscall(SYSCALL(lseek), fd, offset, whence);
}
+#if SANITIZER_LINUX
uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
- return internal_syscall(__NR_prctl, option, arg2, arg3, arg4, arg5);
+ return internal_syscall(SYSCALL(prctl), option, arg2, arg3, arg4, arg5);
}
+#endif
uptr internal_sigaltstack(const struct sigaltstack *ss,
struct sigaltstack *oss) {
- return internal_syscall(__NR_sigaltstack, (uptr)ss, (uptr)oss);
+ return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss);
+}
+
+int internal_fork() {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(clone), SIGCHLD, 0);
+#else
+ return internal_syscall(SYSCALL(fork));
+#endif
}
-uptr internal_sigaction(int signum, const __sanitizer_kernel_sigaction_t *act,
- __sanitizer_kernel_sigaction_t *oldact) {
- return internal_syscall(__NR_rt_sigaction, signum, act, oldact,
- sizeof(__sanitizer_kernel_sigset_t));
+#if SANITIZER_LINUX
+#define SA_RESTORER 0x04000000
+// Doesn't set sa_restorer, use with caution (see below).
+int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
+ __sanitizer_kernel_sigaction_t k_act, k_oldact;
+ internal_memset(&k_act, 0, sizeof(__sanitizer_kernel_sigaction_t));
+ internal_memset(&k_oldact, 0, sizeof(__sanitizer_kernel_sigaction_t));
+ const __sanitizer_sigaction *u_act = (const __sanitizer_sigaction *)act;
+ __sanitizer_sigaction *u_oldact = (__sanitizer_sigaction *)oldact;
+ if (u_act) {
+ k_act.handler = u_act->handler;
+ k_act.sigaction = u_act->sigaction;
+ internal_memcpy(&k_act.sa_mask, &u_act->sa_mask,
+ sizeof(__sanitizer_kernel_sigset_t));
+ // Without SA_RESTORER kernel ignores the calls (probably returns EINVAL).
+ k_act.sa_flags = u_act->sa_flags | SA_RESTORER;
+ // FIXME: most often sa_restorer is unset, however the kernel requires it
+ // to point to a valid signal restorer that calls the rt_sigreturn syscall.
+ // If sa_restorer passed to the kernel is NULL, the program may crash upon
+ // signal delivery or fail to unwind the stack in the signal handler.
+ // libc implementation of sigaction() passes its own restorer to
+ // rt_sigaction, so we need to do the same (we'll need to reimplement the
+ // restorers; for x86_64 the restorer address can be obtained from
+ // oldact->sa_restorer upon a call to sigaction(xxx, NULL, oldact).
+ k_act.sa_restorer = u_act->sa_restorer;
+ }
+
+ uptr result = internal_syscall(SYSCALL(rt_sigaction), (uptr)signum,
+ (uptr)(u_act ? &k_act : nullptr),
+ (uptr)(u_oldact ? &k_oldact : nullptr),
+ (uptr)sizeof(__sanitizer_kernel_sigset_t));
+
+ if ((result == 0) && u_oldact) {
+ u_oldact->handler = k_oldact.handler;
+ u_oldact->sigaction = k_oldact.sigaction;
+ internal_memcpy(&u_oldact->sa_mask, &k_oldact.sa_mask,
+ sizeof(__sanitizer_kernel_sigset_t));
+ u_oldact->sa_flags = k_oldact.sa_flags;
+ u_oldact->sa_restorer = k_oldact.sa_restorer;
+ }
+ return result;
}
+#endif // SANITIZER_LINUX
-uptr internal_sigprocmask(int how, __sanitizer_kernel_sigset_t *set,
- __sanitizer_kernel_sigset_t *oldset) {
- return internal_syscall(__NR_rt_sigprocmask, (uptr)how, &set->sig[0],
- &oldset->sig[0], sizeof(__sanitizer_kernel_sigset_t));
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+#if SANITIZER_FREEBSD
+ return internal_syscall(SYSCALL(sigprocmask), how, set, oldset);
+#else
+ __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
+ __sanitizer_kernel_sigset_t *k_oldset = (__sanitizer_kernel_sigset_t *)oldset;
+ return internal_syscall(SYSCALL(rt_sigprocmask), (uptr)how,
+ (uptr)&k_set->sig[0], (uptr)&k_oldset->sig[0],
+ sizeof(__sanitizer_kernel_sigset_t));
+#endif
}
-void internal_sigfillset(__sanitizer_kernel_sigset_t *set) {
+void internal_sigfillset(__sanitizer_sigset_t *set) {
internal_memset(set, 0xff, sizeof(*set));
}
-void internal_sigdelset(__sanitizer_kernel_sigset_t *set, int signum) {
+#if SANITIZER_LINUX
+void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
signum -= 1;
CHECK_GE(signum, 0);
CHECK_LT(signum, sizeof(*set) * 8);
- const uptr idx = signum / (sizeof(set->sig[0]) * 8);
- const uptr bit = signum % (sizeof(set->sig[0]) * 8);
- set->sig[idx] &= ~(1 << bit);
+ __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
+ const uptr idx = signum / (sizeof(k_set->sig[0]) * 8);
+ const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
+ k_set->sig[idx] &= ~(1 << bit);
}
+#endif // SANITIZER_LINUX
// ThreadLister implementation.
ThreadLister::ThreadLister(int pid)
}
uptr GetPageSize() {
-#if defined(__x86_64__) || defined(__i386__)
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__))
return EXEC_PAGESIZE;
#else
return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy.
#endif
}
-static char proc_self_exe_cache_str[kMaxPathLength];
-static uptr proc_self_exe_cache_len = 0;
-
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+#if SANITIZER_FREEBSD
+ const int Mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+ const char *default_module_name = "kern.proc.pathname";
+ size_t Size = buf_len;
+ bool IsErr = (sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0);
+ int readlink_error = IsErr ? errno : 0;
+ uptr module_name_len = Size;
+#else
+ const char *default_module_name = "/proc/self/exe";
uptr module_name_len = internal_readlink(
- "/proc/self/exe", buf, buf_len);
+ default_module_name, buf, buf_len);
int readlink_error;
- if (internal_iserror(module_name_len, &readlink_error)) {
- if (proc_self_exe_cache_len) {
- // If available, use the cached module name.
- CHECK_LE(proc_self_exe_cache_len, buf_len);
- internal_strncpy(buf, proc_self_exe_cache_str, buf_len);
- module_name_len = internal_strlen(proc_self_exe_cache_str);
- } else {
- // We can't read /proc/self/exe for some reason, assume the name of the
- // binary is unknown.
- Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, "
- "some stack frames may not be symbolized\n", readlink_error);
- module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe");
- }
+ bool IsErr = internal_iserror(module_name_len, &readlink_error);
+#endif
+ if (IsErr) {
+ // We can't read binary name for some reason, assume it's unknown.
+ Report("WARNING: reading executable name failed with errno %d, "
+ "some stack frames may not be symbolized\n", readlink_error);
+ module_name_len = internal_snprintf(buf, buf_len, "%s",
+ default_module_name);
CHECK_LT(module_name_len, buf_len);
- buf[module_name_len] = '\0';
}
return module_name_len;
}
-void CacheBinaryName() {
- if (!proc_self_exe_cache_len) {
- proc_self_exe_cache_len =
- ReadBinaryName(proc_self_exe_cache_str, kMaxPathLength);
+uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
+#if SANITIZER_LINUX
+ char *tmpbuf;
+ uptr tmpsize;
+ uptr tmplen;
+ if (ReadFileToBuffer("/proc/self/cmdline", &tmpbuf, &tmpsize, &tmplen,
+ 1024 * 1024)) {
+ internal_strncpy(buf, tmpbuf, buf_len);
+ UnmapOrDie(tmpbuf, tmpsize);
+ return internal_strlen(buf);
}
+#endif
+ return ReadBinaryName(buf, buf_len);
}
// Match full names of the form /path/to/base_name{-,.}*
#if !SANITIZER_ANDROID
// Call cb for each region mapped by map.
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) {
+ CHECK_NE(map, nullptr);
+#if !SANITIZER_FREEBSD
typedef ElfW(Phdr) Elf_Phdr;
typedef ElfW(Ehdr) Elf_Ehdr;
+#endif // !SANITIZER_FREEBSD
char *base = (char *)map->l_addr;
Elf_Ehdr *ehdr = (Elf_Ehdr *)base;
char *phdrs = base + ehdr->e_phoff;
}
#endif
-#if defined(__x86_64__)
+#if defined(__x86_64__) && SANITIZER_LINUX
// We cannot use glibc's clone wrapper, because it messes with the child
// task's TLS. It writes the PID and TID of the child task to its thread
// descriptor, but in our case the child task shares the thread descriptor with
register void *r8 __asm__("r8") = newtls;
register int *r10 __asm__("r10") = child_tidptr;
__asm__ __volatile__(
- /* %rax = syscall(%rax = __NR_clone,
+ /* %rax = syscall(%rax = SYSCALL(clone),
* %rdi = flags,
* %rsi = child_stack,
* %rdx = parent_tidptr,
/* Return to parent. */
"1:\n"
: "=a" (res)
- : "a"(__NR_clone), "i"(__NR_exit),
+ : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
"S"(child_stack),
"D"(flags),
"d"(parent_tidptr),
: "rsp", "memory", "r11", "rcx");
return res;
}
-#endif // defined(__x86_64__)
-} // namespace __sanitizer
+#elif defined(__mips__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ long long res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+ ((unsigned long long *)child_stack)[0] = (uptr)fn;
+ ((unsigned long long *)child_stack)[1] = (uptr)arg;
+ register void *a3 __asm__("$7") = newtls;
+ register int *a4 __asm__("$8") = child_tidptr;
+ // We don't have proper CFI directives here because it requires alot of code
+ // for very marginal benefits.
+ __asm__ __volatile__(
+ /* $v0 = syscall($v0 = __NR_clone,
+ * $a0 = flags,
+ * $a1 = child_stack,
+ * $a2 = parent_tidptr,
+ * $a3 = new_tls,
+ * $a4 = child_tidptr)
+ */
+ ".cprestore 16;\n"
+ "move $4,%1;\n"
+ "move $5,%2;\n"
+ "move $6,%3;\n"
+ "move $7,%4;\n"
+ /* Store the fifth argument on stack
+ * if we are using 32-bit abi.
+ */
+#if SANITIZER_WORDSIZE == 32
+ "lw %5,16($29);\n"
+#else
+ "move $8,%5;\n"
+#endif
+ "li $2,%6;\n"
+ "syscall;\n"
-#endif // SANITIZER_LINUX
+ /* if ($v0 != 0)
+ * return;
+ */
+ "bnez $2,1f;\n"
+
+ /* Call "fn(arg)". */
+ "ld $25,0($29);\n"
+ "ld $4,8($29);\n"
+ "jal $25;\n"
+
+ /* Call _exit($v0). */
+ "move $4,$2;\n"
+ "li $2,%7;\n"
+ "syscall;\n"
+
+ /* Return to parent. */
+ "1:\n"
+ : "=r" (res)
+ : "r"(flags),
+ "r"(child_stack),
+ "r"(parent_tidptr),
+ "r"(a3),
+ "r"(a4),
+ "i"(__NR_clone),
+ "i"(__NR_exit)
+ : "memory", "$29" );
+ return res;
+}
+#elif defined(__aarch64__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ long long res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+ ((unsigned long long *)child_stack)[0] = (uptr)fn;
+ ((unsigned long long *)child_stack)[1] = (uptr)arg;
+
+ register int (*__fn)(void *) __asm__("x0") = fn;
+ register void *__stack __asm__("x1") = child_stack;
+ register int __flags __asm__("x2") = flags;
+ register void *__arg __asm__("x3") = arg;
+ register int *__ptid __asm__("x4") = parent_tidptr;
+ register void *__tls __asm__("x5") = newtls;
+ register int *__ctid __asm__("x6") = child_tidptr;
+
+ __asm__ __volatile__(
+ "mov x0,x2\n" /* flags */
+ "mov x2,x4\n" /* ptid */
+ "mov x3,x5\n" /* tls */
+ "mov x4,x6\n" /* ctid */
+ "mov x8,%9\n" /* clone */
+
+ "svc 0x0\n"
+
+ /* if (%r0 != 0)
+ * return %r0;
+ */
+ "cmp x0, #0\n"
+ "bne 1f\n"
+
+ /* In the child, now. Call "fn(arg)". */
+ "ldp x1, x0, [sp], #16\n"
+ "blr x1\n"
+
+ /* Call _exit(%r0). */
+ "mov x8, %10\n"
+ "svc 0x0\n"
+ "1:\n"
+
+ : "=r" (res)
+ : "i"(-EINVAL),
+ "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg),
+ "r"(__ptid), "r"(__tls), "r"(__ctid),
+ "i"(__NR_clone), "i"(__NR_exit)
+ : "x30", "memory");
+ return res;
+}
+#endif // defined(__x86_64__) && SANITIZER_LINUX
+
+#if SANITIZER_ANDROID
+#define PROP_VALUE_MAX 92
+extern "C" SANITIZER_WEAK_ATTRIBUTE int __system_property_get(const char *name,
+ char *value);
+void GetExtraActivationFlags(char *buf, uptr size) {
+ CHECK(size > PROP_VALUE_MAX);
+ CHECK(&__system_property_get);
+ __system_property_get("asan.options", buf);
+}
+
+#if __ANDROID_API__ < 21
+extern "C" __attribute__((weak)) int dl_iterate_phdr(
+ int (*)(struct dl_phdr_info *, size_t, void *), void *);
+#endif
+
+static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
+ void *data) {
+ // Any name starting with "lib" indicates a bug in L where library base names
+ // are returned instead of paths.
+ if (info->dlpi_name && info->dlpi_name[0] == 'l' &&
+ info->dlpi_name[1] == 'i' && info->dlpi_name[2] == 'b') {
+ *(bool *)data = true;
+ return 1;
+ }
+ return 0;
+}
+
+static atomic_uint32_t android_api_level;
+
+static AndroidApiLevel AndroidDetectApiLevel() {
+ if (!&dl_iterate_phdr)
+ return ANDROID_KITKAT; // K or lower
+ bool base_name_seen = false;
+ dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen);
+ if (base_name_seen)
+ return ANDROID_LOLLIPOP_MR1; // L MR1
+ return ANDROID_POST_LOLLIPOP; // post-L
+ // Plain L (API level 21) is completely broken wrt ASan and not very
+ // interesting to detect.
+}
+
+AndroidApiLevel AndroidGetApiLevel() {
+ AndroidApiLevel level =
+ (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed);
+ if (level) return level;
+ level = AndroidDetectApiLevel();
+ atomic_store(&android_api_level, level, memory_order_relaxed);
+ return level;
+}
+
+#endif
+
+bool IsDeadlySignal(int signum) {
+ if (common_flags()->handle_abort && signum == SIGABRT)
+ return true;
+ if (common_flags()->handle_sigfpe && signum == SIGFPE)
+ return true;
+ return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv;
+}
+
+#ifndef SANITIZER_GO
+void *internal_start_thread(void(*func)(void *arg), void *arg) {
+ // Start the thread with signals blocked, otherwise it can steal user signals.
+ __sanitizer_sigset_t set, old;
+ internal_sigfillset(&set);
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked
+ // on any thread, setuid call hangs (see test/tsan/setuid.c).
+ internal_sigdelset(&set, 33);
+#endif
+ internal_sigprocmask(SIG_SETMASK, &set, &old);
+ void *th;
+ real_pthread_create(&th, nullptr, (void*(*)(void *arg))func, arg);
+ internal_sigprocmask(SIG_SETMASK, &old, nullptr);
+ return th;
+}
+
+void internal_join_thread(void *th) {
+ real_pthread_join(th, nullptr);
+}
+#else
+void *internal_start_thread(void (*func)(void *), void *arg) { return 0; }
+
+void internal_join_thread(void *th) {}
+#endif
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+#if defined(__arm__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.arm_pc;
+ *bp = ucontext->uc_mcontext.arm_fp;
+ *sp = ucontext->uc_mcontext.arm_sp;
+#elif defined(__aarch64__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.pc;
+ *bp = ucontext->uc_mcontext.regs[29];
+ *sp = ucontext->uc_mcontext.sp;
+#elif defined(__hppa__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.sc_iaoq[0];
+ /* GCC uses %r3 whenever a frame pointer is needed. */
+ *bp = ucontext->uc_mcontext.sc_gr[3];
+ *sp = ucontext->uc_mcontext.sc_gr[30];
+#elif defined(__x86_64__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.mc_rip;
+ *bp = ucontext->uc_mcontext.mc_rbp;
+ *sp = ucontext->uc_mcontext.mc_rsp;
+# else
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.gregs[REG_RIP];
+ *bp = ucontext->uc_mcontext.gregs[REG_RBP];
+ *sp = ucontext->uc_mcontext.gregs[REG_RSP];
+# endif
+#elif defined(__i386__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.mc_eip;
+ *bp = ucontext->uc_mcontext.mc_ebp;
+ *sp = ucontext->uc_mcontext.mc_esp;
+# else
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.gregs[REG_EIP];
+ *bp = ucontext->uc_mcontext.gregs[REG_EBP];
+ *sp = ucontext->uc_mcontext.gregs[REG_ESP];
+# endif
+#elif defined(__powerpc__) || defined(__powerpc64__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.regs->nip;
+ *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
+ // The powerpc{,64}-linux ABIs do not specify r31 as the frame
+ // pointer, but GCC always uses r31 when we need a frame pointer.
+ *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
+#elif defined(__sparc__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ uptr *stk_ptr;
+# if defined (__arch64__)
+ *pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
+ *sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
+ stk_ptr = (uptr *) (*sp + 2047);
+ *bp = stk_ptr[15];
+# else
+ *pc = ucontext->uc_mcontext.gregs[REG_PC];
+ *sp = ucontext->uc_mcontext.gregs[REG_O6];
+ stk_ptr = (uptr *) *sp;
+ *bp = stk_ptr[15];
+# endif
+#elif defined(__mips__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.pc;
+ *bp = ucontext->uc_mcontext.gregs[30];
+ *sp = ucontext->uc_mcontext.gregs[29];
+#else
+# error "Unsupported arch"
+#endif
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
#define SANITIZER_LINUX_H
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
+#include "sanitizer_posix.h"
#include "sanitizer_platform_limits_posix.h"
struct link_map; // Opaque type returned by dlopen().
// Syscall wrappers.
uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count);
-uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
uptr internal_sigaltstack(const struct sigaltstack* ss,
struct sigaltstack* oss);
-uptr internal_sigaction(int signum, const __sanitizer_kernel_sigaction_t *act,
- __sanitizer_kernel_sigaction_t *oldact);
-uptr internal_sigprocmask(int how, __sanitizer_kernel_sigset_t *set,
- __sanitizer_kernel_sigset_t *oldset);
-void internal_sigfillset(__sanitizer_kernel_sigset_t *set);
-void internal_sigdelset(__sanitizer_kernel_sigset_t *set, int signum);
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset);
+void internal_sigfillset(__sanitizer_sigset_t *set);
-#ifdef __x86_64__
+// Linux-only syscalls.
+#if SANITIZER_LINUX
+uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
+// Used only by sanitizer_stoptheworld. Signal handlers that are actually used
+// (like the process-wide error reporting SEGV handler) must use
+// internal_sigaction instead.
+int internal_sigaction_norestorer(int signum, const void *act, void *oldact);
+void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
+#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr);
#endif
+#endif // SANITIZER_LINUX
// This class reads thread IDs from /proc/<pid>/task using only syscalls.
class ThreadLister {
int bytes_read_;
};
-void AdjustStackSizeLinux(void *attr);
-
// Exposed for testing.
uptr ThreadDescriptorSize();
uptr ThreadSelf();
// information).
bool LibraryNameIs(const char *full_name, const char *base_name);
-// Read the name of the current binary from /proc/self/exe.
-uptr ReadBinaryName(/*out*/char *buf, uptr buf_len);
-// Cache the value of /proc/self/exe.
-void CacheBinaryName();
-
// Call cb for each region mapped by map.
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr));
} // namespace __sanitizer
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
#endif // SANITIZER_LINUX_H
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_atomic.h"
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
+#include "sanitizer_freebsd.h"
#include "sanitizer_linux.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
-#include "sanitizer_atomic.h"
-#include <dlfcn.h>
+#if SANITIZER_ANDROID || SANITIZER_FREEBSD
+#include <dlfcn.h> // for dlsym()
+#endif
+
+#include <link.h>
#include <pthread.h>
-#include <sys/prctl.h>
+#include <signal.h>
#include <sys/resource.h>
-#include <unwind.h>
+
+#if SANITIZER_FREEBSD
+#include <pthread_np.h>
+#include <osreldate.h>
+#define pthread_getattr_np pthread_attr_get_np
+#endif
+
+#if SANITIZER_LINUX
+#include <sys/prctl.h>
+#endif
+
+#if SANITIZER_ANDROID
+#include <android/api-level.h>
+#endif
+
+#if SANITIZER_ANDROID && __ANDROID_API__ < 21
+#include <android/log.h>
+#else
+#include <syslog.h>
+#endif
#if !SANITIZER_ANDROID
#include <elf.h>
-#include <link.h>
#include <unistd.h>
#endif
-// This function is defined elsewhere if we intercepted pthread_attr_getstack.
-SANITIZER_WEAK_ATTRIBUTE
-int __sanitizer_pthread_attr_getstack(void *attr, void **addr, size_t *size) {
- return pthread_attr_getstack((pthread_attr_t*)attr, addr, size);
-}
-
namespace __sanitizer {
+SANITIZER_WEAK_ATTRIBUTE int
+real_sigaction(int signum, const void *act, void *oldact);
+
+int internal_sigaction(int signum, const void *act, void *oldact) {
+#if !SANITIZER_GO
+ if (&real_sigaction)
+ return real_sigaction(signum, act, oldact);
+#endif
+ return sigaction(signum, (const struct sigaction *)act,
+ (struct sigaction *)oldact);
+}
+
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
uptr *stack_bottom) {
- static const uptr kMaxThreadStackSize = 1 << 30; // 1Gb
CHECK(stack_top);
CHECK(stack_bottom);
if (at_initialization) {
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
uptr start, end, offset;
uptr prev_end = 0;
- while (proc_maps.Next(&start, &end, &offset, 0, 0, /* protection */0)) {
+ while (proc_maps.Next(&start, &end, &offset, nullptr, 0,
+ /* protection */nullptr)) {
if ((uptr)&rl < end)
break;
prev_end = end;
return;
}
pthread_attr_t attr;
+ pthread_attr_init(&attr);
CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
uptr stacksize = 0;
- void *stackaddr = 0;
- __sanitizer_pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize);
+ void *stackaddr = nullptr;
+ my_pthread_attr_getstack(&attr, &stackaddr, &stacksize);
pthread_attr_destroy(&attr);
CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check.
*stack_bottom = (uptr)stackaddr;
}
-// Does not compile for Go because dlsym() requires -ldl
-#ifndef SANITIZER_GO
+#if !SANITIZER_GO
bool SetEnv(const char *name, const char *value) {
void *f = dlsym(RTLD_NEXT, "setenv");
- if (f == 0)
+ if (!f)
return false;
typedef int(*setenv_ft)(const char *name, const char *value, int overwrite);
setenv_ft setenv_f;
#endif
}
-#ifndef SANITIZER_GO
-//------------------------- SlowUnwindStack -----------------------------------
-#ifdef __arm__
-#define UNWIND_STOP _URC_END_OF_STACK
-#define UNWIND_CONTINUE _URC_NO_REASON
-#else
-#define UNWIND_STOP _URC_NORMAL_STOP
-#define UNWIND_CONTINUE _URC_NO_REASON
-#endif
-
-uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
-#ifdef __arm__
- uptr val;
- _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
- 15 /* r15 = PC */, _UVRSD_UINT32, &val);
- CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
- // Clear the Thumb bit.
- return val & ~(uptr)1;
-#else
- return _Unwind_GetIP(ctx);
-#endif
-}
-
-struct UnwindTraceArg {
- StackTrace *stack;
- uptr max_depth;
-};
-
-_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
- UnwindTraceArg *arg = (UnwindTraceArg*)param;
- CHECK_LT(arg->stack->size, arg->max_depth);
- uptr pc = Unwind_GetIP(ctx);
- arg->stack->trace[arg->stack->size++] = pc;
- if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
- return UNWIND_CONTINUE;
-}
-
-void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
- size = 0;
- if (max_depth == 0)
- return;
- UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
- _Unwind_Backtrace(Unwind_Trace, &arg);
- // We need to pop a few frames so that pc is on top.
- uptr to_pop = LocatePcInTrace(pc);
- // trace[0] belongs to the current function so we always pop it.
- if (to_pop == 0)
- to_pop = 1;
- PopStackFrames(to_pop);
- trace[0] = pc;
-}
-
-#endif // !SANITIZER_GO
-
+#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO
static uptr g_tls_size;
+#endif
#ifdef __i386__
# define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))
# define DL_INTERNAL_FUNCTION
#endif
+#if defined(__mips__) || defined(__powerpc64__)
+// TlsPreTcbSize includes size of struct pthread_descr and size of tcb
+// head structure. It lies before the static tls blocks.
+static uptr TlsPreTcbSize() {
+# if defined(__mips__)
+ const uptr kTcbHead = 16; // sizeof (tcbhead_t)
+# elif defined(__powerpc64__)
+ const uptr kTcbHead = 88; // sizeof (tcbhead_t)
+# endif
+ const uptr kTlsAlign = 16;
+ const uptr kTlsPreTcbSize =
+ (ThreadDescriptorSize() + kTcbHead + kTlsAlign - 1) & ~(kTlsAlign - 1);
+ InitTlsSize();
+ g_tls_size = (g_tls_size + kTlsPreTcbSize + kTlsAlign -1) & ~(kTlsAlign - 1);
+ return kTlsPreTcbSize;
+}
+#endif
+
void InitTlsSize() {
-#if !defined(SANITIZER_GO) && !SANITIZER_ANDROID
+#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO
+// all current supported platforms have 16 bytes stack alignment
+ const size_t kStackAlign = 16;
typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION;
get_tls_func get_tls;
void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
size_t tls_size = 0;
size_t tls_align = 0;
get_tls(&tls_size, &tls_align);
- g_tls_size = tls_size;
-#endif
-}
-
-uptr GetTlsSize() {
- return g_tls_size;
+ if (tls_align < kStackAlign)
+ tls_align = kStackAlign;
+ g_tls_size = RoundUpTo(tls_size, tls_align);
+#endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO
}
-#if defined(__x86_64__) || defined(__i386__)
-// sizeof(struct thread) from glibc.
+#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) \
+ || defined(__aarch64__) || defined(__powerpc64__)) \
+ && SANITIZER_LINUX && !SANITIZER_ANDROID
+// sizeof(struct pthread) from glibc.
static atomic_uintptr_t kThreadDescriptorSize;
uptr ThreadDescriptorSize() {
- char buf[64];
uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed);
if (val)
return val;
+#if defined(__x86_64__) || defined(__i386__)
#ifdef _CS_GNU_LIBC_VERSION
+ char buf[64];
uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) {
char *end;
int minor = internal_simple_strtoll(buf + 8, &end, 10);
if (end != buf + 8 && (*end == '\0' || *end == '.')) {
- /* sizeof(struct thread) values from various glibc versions. */
- if (minor <= 3)
+ /* sizeof(struct pthread) values from various glibc versions. */
+ if (SANITIZER_X32)
+ val = 1728; // Assume only one particular version for x32.
+ else if (minor <= 3)
val = FIRST_32_SECOND_64(1104, 1696);
else if (minor == 4)
val = FIRST_32_SECOND_64(1120, 1728);
val = FIRST_32_SECOND_64(1168, 1776);
else if (minor <= 12)
val = FIRST_32_SECOND_64(1168, 2288);
+ else if (minor == 13)
+ val = FIRST_32_SECOND_64(1168, 2304);
else
val = FIRST_32_SECOND_64(1216, 2304);
}
return val;
}
#endif
+#elif defined(__mips__)
+ // TODO(sagarthakur): add more values as per different glibc versions.
+ val = FIRST_32_SECOND_64(1152, 1776);
+ if (val)
+ atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+ return val;
+#elif defined(__aarch64__)
+ // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
+ val = 1776;
+ atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+ return val;
+#elif defined(__powerpc64__)
+ val = 1776; // from glibc.ppc64le 2.20-8.fc21
+ atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+ return val;
+#endif
return 0;
}
uptr ThreadSelf() {
uptr descr_addr;
-#ifdef __i386__
+# if defined(__i386__)
asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
-#else
+# elif defined(__x86_64__)
asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
-#endif
+# elif defined(__mips__)
+ // MIPS uses TLS variant I. The thread pointer (in hardware register $29)
+ // points to the end of the TCB + 0x7000. The pthread_descr structure is
+ // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
+ // TCB and the size of pthread_descr.
+ const uptr kTlsTcbOffset = 0x7000;
+ uptr thread_pointer;
+ asm volatile(".set push;\
+ .set mips64r2;\
+ rdhwr %0,$29;\
+ .set pop" : "=r" (thread_pointer));
+ descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize();
+# elif defined(__aarch64__)
+ descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer());
+# elif defined(__powerpc64__)
+ // PPC64LE uses TLS variant I. The thread pointer (in GPR 13)
+ // points to the end of the TCB + 0x7000. The pthread_descr structure is
+ // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
+ // TCB and the size of pthread_descr.
+ const uptr kTlsTcbOffset = 0x7000;
+ uptr thread_pointer;
+ asm("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset));
+ descr_addr = thread_pointer - TlsPreTcbSize();
+# else
+# error "unsupported CPU arch"
+# endif
return descr_addr;
}
-#endif // defined(__x86_64__) || defined(__i386__)
+#endif // (x86_64 || i386 || MIPS) && SANITIZER_LINUX
+
+#if SANITIZER_FREEBSD
+static void **ThreadSelfSegbase() {
+ void **segbase = 0;
+# if defined(__i386__)
+ // sysarch(I386_GET_GSBASE, segbase);
+ __asm __volatile("mov %%gs:0, %0" : "=r" (segbase));
+# elif defined(__x86_64__)
+ // sysarch(AMD64_GET_FSBASE, segbase);
+ __asm __volatile("movq %%fs:0, %0" : "=r" (segbase));
+# else
+# error "unsupported CPU arch for FreeBSD platform"
+# endif
+ return segbase;
+}
+
+uptr ThreadSelf() {
+ return (uptr)ThreadSelfSegbase()[2];
+}
+#endif // SANITIZER_FREEBSD
+
+#if !SANITIZER_GO
+static void GetTls(uptr *addr, uptr *size) {
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+# if defined(__x86_64__) || defined(__i386__)
+ *addr = ThreadSelf();
+ *size = GetTlsSize();
+ *addr -= *size;
+ *addr += ThreadDescriptorSize();
+# elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__)
+ *addr = ThreadSelf();
+ *size = GetTlsSize();
+# else
+ *addr = 0;
+ *size = 0;
+# endif
+#elif SANITIZER_FREEBSD
+ void** segbase = ThreadSelfSegbase();
+ *addr = 0;
+ *size = 0;
+ if (segbase != 0) {
+ // tcbalign = 16
+ // tls_size = round(tls_static_space, tcbalign);
+ // dtv = segbase[1];
+ // dtv[2] = segbase - tls_static_space;
+ void **dtv = (void**) segbase[1];
+ *addr = (uptr) dtv[2];
+ *size = (*addr == 0) ? 0 : ((uptr) segbase[0] - (uptr) dtv[2]);
+ }
+#elif SANITIZER_ANDROID
+ *addr = 0;
+ *size = 0;
+#else
+# error "Unknown OS"
+#endif
+}
+#endif
+
+#if !SANITIZER_GO
+uptr GetTlsSize() {
+#if SANITIZER_FREEBSD || SANITIZER_ANDROID
+ uptr addr, size;
+ GetTls(&addr, &size);
+ return size;
+#else
+ return g_tls_size;
+#endif
+}
+#endif
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
uptr *tls_addr, uptr *tls_size) {
-#ifndef SANITIZER_GO
-#if defined(__x86_64__) || defined(__i386__)
- *tls_addr = ThreadSelf();
- *tls_size = GetTlsSize();
- *tls_addr -= *tls_size;
- *tls_addr += ThreadDescriptorSize();
+#if SANITIZER_GO
+ // Stub implementation for Go.
+ *stk_addr = *stk_size = *tls_addr = *tls_size = 0;
#else
- *tls_addr = 0;
- *tls_size = 0;
-#endif
+ GetTls(tls_addr, tls_size);
uptr stack_top, stack_bottom;
GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
*tls_addr = *stk_addr + *stk_size;
}
}
-#else // SANITIZER_GO
- *stk_addr = 0;
- *stk_size = 0;
- *tls_addr = 0;
- *tls_size = 0;
-#endif // SANITIZER_GO
-}
-
-void AdjustStackSizeLinux(void *attr_) {
- pthread_attr_t *attr = (pthread_attr_t *)attr_;
- uptr stackaddr = 0;
- size_t stacksize = 0;
- __sanitizer_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize);
- // GLibC will return (0 - stacksize) as the stack address in the case when
- // stacksize is set, but stackaddr is not.
- bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0);
- // We place a lot of tool data into TLS, account for that.
- const uptr minstacksize = GetTlsSize() + 128*1024;
- if (stacksize < minstacksize) {
- if (!stack_set) {
- if (common_flags()->verbosity && stacksize != 0)
- Printf("Sanitizer: increasing stacksize %zu->%zu\n", stacksize,
- minstacksize);
- pthread_attr_setstacksize(attr, minstacksize);
- } else {
- Printf("Sanitizer: pre-allocated stack size is insufficient: "
- "%zu < %zu\n", stacksize, minstacksize);
- Printf("Sanitizer: pthread_create is likely to fail.\n");
- }
- }
+#endif
}
-#if SANITIZER_ANDROID
-uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
- string_predicate_t filter) {
- return 0;
-}
-#else // SANITIZER_ANDROID
+# if !SANITIZER_FREEBSD
typedef ElfW(Phdr) Elf_Phdr;
+# elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2
+# define Elf_Phdr XElf32_Phdr
+# define dl_phdr_info xdl_phdr_info
+# define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b))
+# endif
struct DlIteratePhdrData {
LoadedModule *modules;
DlIteratePhdrData *data = (DlIteratePhdrData*)arg;
if (data->current_n == data->max_n)
return 0;
- InternalScopedBuffer<char> module_name(kMaxPathLength);
- module_name.data()[0] = '\0';
+ InternalScopedString module_name(kMaxPathLength);
if (data->first) {
data->first = false;
// First module is the binary itself.
- ReadBinaryName(module_name.data(), module_name.size());
+ ReadBinaryNameCached(module_name.data(), module_name.size());
} else if (info->dlpi_name) {
- internal_strncpy(module_name.data(), info->dlpi_name, module_name.size());
+ module_name.append("%s", info->dlpi_name);
}
- if (module_name.data()[0] == '\0')
+ if (module_name[0] == '\0')
return 0;
if (data->filter && !data->filter(module_name.data()))
return 0;
- void *mem = &data->modules[data->current_n];
- LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(),
- info->dlpi_addr);
+ LoadedModule *cur_module = &data->modules[data->current_n];
+ cur_module->set(module_name.data(), info->dlpi_addr);
data->current_n++;
for (int i = 0; i < info->dlpi_phnum; i++) {
const Elf_Phdr *phdr = &info->dlpi_phdr[i];
if (phdr->p_type == PT_LOAD) {
uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
uptr cur_end = cur_beg + phdr->p_memsz;
- cur_module->addAddressRange(cur_beg, cur_end);
+ bool executable = phdr->p_flags & PF_X;
+ cur_module->addAddressRange(cur_beg, cur_end, executable);
}
}
return 0;
}
+#if SANITIZER_ANDROID && __ANDROID_API__ < 21
+extern "C" __attribute__((weak)) int dl_iterate_phdr(
+ int (*)(struct dl_phdr_info *, size_t, void *), void *);
+#endif
+
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter) {
+#if SANITIZER_ANDROID && __ANDROID_API__ <= 22
+ u32 api_level = AndroidGetApiLevel();
+ // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken.
+ // The runtime check allows the same library to work with
+ // both K and L (and future) Android releases.
+ if (api_level <= ANDROID_LOLLIPOP_MR1) { // L or earlier
+ MemoryMappingLayout memory_mapping(false);
+ return memory_mapping.DumpListOfModules(modules, max_modules, filter);
+ }
+#endif
CHECK(modules);
DlIteratePhdrData data = {modules, 0, true, max_modules, filter};
dl_iterate_phdr(dl_iterate_phdr_cb, &data);
return data.current_n;
}
+
+// getrusage does not give us the current RSS, only the max RSS.
+// Still, this is better than nothing if /proc/self/statm is not available
+// for some reason, e.g. due to a sandbox.
+static uptr GetRSSFromGetrusage() {
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage)) // Failed, probably due to a sandbox.
+ return 0;
+ return usage.ru_maxrss << 10; // ru_maxrss is in Kb.
+}
+
+uptr GetRSS() {
+ if (!common_flags()->can_use_proc_maps_statm)
+ return GetRSSFromGetrusage();
+ fd_t fd = OpenFile("/proc/self/statm", RdOnly);
+ if (fd == kInvalidFd)
+ return GetRSSFromGetrusage();
+ char buf[64];
+ uptr len = internal_read(fd, buf, sizeof(buf) - 1);
+ internal_close(fd);
+ if ((sptr)len <= 0)
+ return 0;
+ buf[len] = 0;
+ // The format of the file is:
+ // 1084 89 69 11 0 79 0
+ // We need the second number which is RSS in pages.
+ char *pos = buf;
+ // Skip the first number.
+ while (*pos >= '0' && *pos <= '9')
+ pos++;
+ // Skip whitespaces.
+ while (!(*pos >= '0' && *pos <= '9') && *pos != 0)
+ pos++;
+ // Read the number.
+ uptr rss = 0;
+ while (*pos >= '0' && *pos <= '9')
+ rss = rss * 10 + *pos++ - '0';
+ return rss * GetPageSizeCached();
+}
+
+// 64-bit Android targets don't provide the deprecated __android_log_write.
+// Starting with the L release, syslog() works and is preferable to
+// __android_log_write.
+#if SANITIZER_LINUX
+
+#if SANITIZER_ANDROID
+static atomic_uint8_t android_log_initialized;
+
+void AndroidLogInit() {
+ atomic_store(&android_log_initialized, 1, memory_order_release);
+}
+
+static bool IsSyslogAvailable() {
+ return atomic_load(&android_log_initialized, memory_order_acquire);
+}
+#else
+void AndroidLogInit() {}
+
+static bool IsSyslogAvailable() { return true; }
#endif // SANITIZER_ANDROID
-} // namespace __sanitizer
+static void WriteOneLineToSyslog(const char *s) {
+#if SANITIZER_ANDROID &&__ANDROID_API__ < 21
+ __android_log_write(ANDROID_LOG_INFO, NULL, s);
+#else
+ syslog(LOG_INFO, "%s", s);
+#endif
+}
+
+void WriteToSyslog(const char *buffer) {
+ if (!IsSyslogAvailable())
+ return;
+ char *copy = internal_strdup(buffer);
+ char *p = copy;
+ char *q;
+ // syslog, at least on Android, has an implicit message length limit.
+ // Print one line at a time.
+ do {
+ q = internal_strchr(p, '\n');
+ if (q)
+ *q = '\0';
+ WriteOneLineToSyslog(p);
+ if (q)
+ p = q + 1;
+ } while (q);
+ InternalFree(copy);
+}
+#endif // SANITIZER_LINUX
+
+} // namespace __sanitizer
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
// ThreadSanitizer, etc run-times.
//
//===----------------------------------------------------------------------===//
+
#ifndef SANITIZER_LIST_H
#define SANITIZER_LIST_H
// non-zero-initialized objects before using.
template<class Item>
struct IntrusiveList {
+ friend class Iterator;
+
void clear() {
- first_ = last_ = 0;
+ first_ = last_ = nullptr;
size_ = 0;
}
void push_back(Item *x) {
if (empty()) {
- x->next = 0;
+ x->next = nullptr;
first_ = last_ = x;
size_ = 1;
} else {
- x->next = 0;
+ x->next = nullptr;
last_->next = x;
last_ = x;
size_++;
void push_front(Item *x) {
if (empty()) {
- x->next = 0;
+ x->next = nullptr;
first_ = last_ = x;
size_ = 1;
} else {
void pop_front() {
CHECK(!empty());
first_ = first_->next;
- if (first_ == 0)
- last_ = 0;
+ if (!first_)
+ last_ = nullptr;
size_--;
}
}
}
+ template<class ListTy, class ItemTy>
+ class IteratorBase {
+ public:
+ explicit IteratorBase(ListTy *list)
+ : list_(list), current_(list->first_) { }
+ ItemTy *next() {
+ ItemTy *ret = current_;
+ if (current_) current_ = current_->next;
+ return ret;
+ }
+ bool hasNext() const { return current_ != nullptr; }
+ private:
+ ListTy *list_;
+ ItemTy *current_;
+ };
+
+ typedef IteratorBase<IntrusiveList<Item>, Item> Iterator;
+ typedef IteratorBase<const IntrusiveList<Item>, const Item> ConstIterator;
+
// private, don't use directly.
uptr size_;
Item *first_;
Item *last_;
};
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_LIST_H
+#endif // SANITIZER_LIST_H
//
//===----------------------------------------------------------------------===//
//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries and implements mac-specific functions from
-// sanitizer_libc.h.
+// This file is shared between various sanitizers' runtime libraries and
+// implements OSX-specific functions.
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
#include <stdio.h>
#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_libc.h"
+#include "sanitizer_mac.h"
#include "sanitizer_placement_new.h"
+#include "sanitizer_platform_limits_posix.h"
#include "sanitizer_procmaps.h"
+#if !SANITIZER_IOS
#include <crt_externs.h> // for _NSGetEnviron
+#else
+extern char **environ;
+#endif
+
+#include <errno.h>
#include <fcntl.h>
+#include <libkern/OSAtomic.h>
#include <mach-o/dyld.h>
-#include <mach-o/loader.h>
+#include <mach/mach.h>
+#include <mach/vm_statistics.h>
#include <pthread.h>
#include <sched.h>
+#include <signal.h>
+#include <stdlib.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/stat.h>
+#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
-#include <libkern/OSAtomic.h>
-#include <errno.h>
namespace __sanitizer {
// ---------------------- sanitizer_libc.h
uptr internal_mmap(void *addr, size_t length, int prot, int flags,
int fd, u64 offset) {
+ if (fd == -1) fd = VM_MAKE_TAG(VM_MEMORY_ANALYSIS_TOOL);
return (uptr)mmap(addr, length, prot, flags, fd, offset);
}
return munmap(addr, length);
}
+int internal_mprotect(void *addr, uptr length, int prot) {
+ return mprotect(addr, length, prot);
+}
+
uptr internal_close(fd_t fd) {
return close(fd);
}
return open(filename, flags, mode);
}
-uptr OpenFile(const char *filename, bool write) {
- return internal_open(filename,
- write ? O_WRONLY | O_CREAT : O_RDONLY, 0660);
-}
-
uptr internal_read(fd_t fd, void *buf, uptr count) {
return read(fd, buf, count);
}
return readlink(path, buf, bufsize);
}
+uptr internal_unlink(const char *path) {
+ return unlink(path);
+}
+
uptr internal_sched_yield() {
return sched_yield();
}
return getpid();
}
+int internal_sigaction(int signum, const void *act, void *oldact) {
+ return sigaction(signum,
+ (struct sigaction *)act, (struct sigaction *)oldact);
+}
+
+void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); }
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+ return sigprocmask(how, set, oldset);
+}
+
+int internal_fork() {
+ // TODO(glider): this may call user's pthread_atfork() handlers which is bad.
+ return fork();
+}
+
+uptr internal_rename(const char *oldpath, const char *newpath) {
+ return rename(oldpath, newpath);
+}
+
+uptr internal_ftruncate(fd_t fd, uptr size) {
+ return ftruncate(fd, size);
+}
+
// ----------------- sanitizer_common.h
bool FileExists(const char *filename) {
struct stat st;
CHECK(stack_top);
CHECK(stack_bottom);
uptr stacksize = pthread_get_stacksize_np(pthread_self());
+ // pthread_get_stacksize_np() returns an incorrect stack size for the main
+ // thread on Mavericks. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=261
+ if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization &&
+ stacksize == (1 << 19)) {
+ struct rlimit rl;
+ CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
+ // Most often rl.rlim_cur will be the desired 8M.
+ if (rl.rlim_cur < kMaxThreadStackSize) {
+ stacksize = rl.rlim_cur;
+ } else {
+ stacksize = kMaxThreadStackSize;
+ }
+ }
void *stackaddr = pthread_get_stackaddr_np(pthread_self());
*stack_top = (uptr)stackaddr;
*stack_bottom = *stack_top - stacksize;
}
-const char *GetEnv(const char *name) {
+char **GetEnviron() {
+#if !SANITIZER_IOS
char ***env_ptr = _NSGetEnviron();
if (!env_ptr) {
Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is "
CHECK(env_ptr);
}
char **environ = *env_ptr;
+#endif
CHECK(environ);
+ return environ;
+}
+
+const char *GetEnv(const char *name) {
+ char **env = GetEnviron();
uptr name_len = internal_strlen(name);
- while (*environ != 0) {
- uptr len = internal_strlen(*environ);
+ while (*env != 0) {
+ uptr len = internal_strlen(*env);
if (len > name_len) {
- const char *p = *environ;
+ const char *p = *env;
if (!internal_memcmp(p, name, name_len) &&
p[name_len] == '=') { // Match.
- return *environ + name_len + 1; // String starting after =.
+ return *env + name_len + 1; // String starting after =.
}
}
- environ++;
+ env++;
}
return 0;
}
-void ReExec() {
- UNIMPLEMENTED();
-}
-
-void PrepareForSandboxing() {
- // Nothing here for now.
-}
-
-uptr GetPageSize() {
- return sysconf(_SC_PAGESIZE);
-}
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+ CHECK_LE(kMaxPathLength, buf_len);
-// ----------------- sanitizer_procmaps.h
-
-MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
- Reset();
-}
-
-MemoryMappingLayout::~MemoryMappingLayout() {
-}
-
-// More information about Mach-O headers can be found in mach-o/loader.h
-// Each Mach-O image has a header (mach_header or mach_header_64) starting with
-// a magic number, and a list of linker load commands directly following the
-// header.
-// A load command is at least two 32-bit words: the command type and the
-// command size in bytes. We're interested only in segment load commands
-// (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped
-// into the task's address space.
-// The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or
-// segment_command_64 correspond to the memory address, memory size and the
-// file offset of the current memory segment.
-// Because these fields are taken from the images as is, one needs to add
-// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime.
-
-void MemoryMappingLayout::Reset() {
- // Count down from the top.
- // TODO(glider): as per man 3 dyld, iterating over the headers with
- // _dyld_image_count is thread-unsafe. We need to register callbacks for
- // adding and removing images which will invalidate the MemoryMappingLayout
- // state.
- current_image_ = _dyld_image_count();
- current_load_cmd_count_ = -1;
- current_load_cmd_addr_ = 0;
- current_magic_ = 0;
- current_filetype_ = 0;
-}
-
-// static
-void MemoryMappingLayout::CacheMemoryMappings() {
- // No-op on Mac for now.
-}
-
-void MemoryMappingLayout::LoadFromCache() {
- // No-op on Mac for now.
-}
-
-// Next and NextSegmentLoad were inspired by base/sysinfo.cc in
-// Google Perftools, http://code.google.com/p/google-perftools.
-
-// NextSegmentLoad scans the current image for the next segment load command
-// and returns the start and end addresses and file offset of the corresponding
-// segment.
-// Note that the segment addresses are not necessarily sorted.
-template<u32 kLCSegment, typename SegmentCommand>
-bool MemoryMappingLayout::NextSegmentLoad(
- uptr *start, uptr *end, uptr *offset,
- char filename[], uptr filename_size, uptr *protection) {
- if (protection)
- UNIMPLEMENTED();
- const char* lc = current_load_cmd_addr_;
- current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize;
- if (((const load_command *)lc)->cmd == kLCSegment) {
- const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_);
- const SegmentCommand* sc = (const SegmentCommand *)lc;
- if (start) *start = sc->vmaddr + dlloff;
- if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
- if (offset) {
- if (current_filetype_ == /*MH_EXECUTE*/ 0x2) {
- *offset = sc->vmaddr;
- } else {
- *offset = sc->fileoff;
- }
- }
- if (filename) {
- internal_strncpy(filename, _dyld_get_image_name(current_image_),
- filename_size);
- }
- return true;
+ // On OS X the executable path is saved to the stack by dyld. Reading it
+ // from there is much faster than calling dladdr, especially for large
+ // binaries with symbols.
+ InternalScopedString exe_path(kMaxPathLength);
+ uint32_t size = exe_path.size();
+ if (_NSGetExecutablePath(exe_path.data(), &size) == 0 &&
+ realpath(exe_path.data(), buf) != 0) {
+ return internal_strlen(buf);
}
- return false;
-}
-
-bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
- char filename[], uptr filename_size,
- uptr *protection) {
- for (; current_image_ >= 0; current_image_--) {
- const mach_header* hdr = _dyld_get_image_header(current_image_);
- if (!hdr) continue;
- if (current_load_cmd_count_ < 0) {
- // Set up for this image;
- current_load_cmd_count_ = hdr->ncmds;
- current_magic_ = hdr->magic;
- current_filetype_ = hdr->filetype;
- switch (current_magic_) {
-#ifdef MH_MAGIC_64
- case MH_MAGIC_64: {
- current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header_64);
- break;
- }
-#endif
- case MH_MAGIC: {
- current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header);
- break;
- }
- default: {
- continue;
- }
- }
- }
+ return 0;
+}
- for (; current_load_cmd_count_ >= 0; current_load_cmd_count_--) {
- switch (current_magic_) {
- // current_magic_ may be only one of MH_MAGIC, MH_MAGIC_64.
-#ifdef MH_MAGIC_64
- case MH_MAGIC_64: {
- if (NextSegmentLoad<LC_SEGMENT_64, struct segment_command_64>(
- start, end, offset, filename, filename_size, protection))
- return true;
- break;
- }
-#endif
- case MH_MAGIC: {
- if (NextSegmentLoad<LC_SEGMENT, struct segment_command>(
- start, end, offset, filename, filename_size, protection))
- return true;
- break;
- }
- }
- }
- // If we get here, no more load_cmd's in this image talk about
- // segments. Go on to the next image.
- }
- return false;
+uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
+ return ReadBinaryName(buf, buf_len);
}
-bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
- char filename[],
- uptr filename_size,
- uptr *protection) {
- return IterateForObjectNameAndOffset(addr, offset, filename, filename_size,
- protection);
+void ReExec() {
+ UNIMPLEMENTED();
}
-BlockingMutex::BlockingMutex(LinkerInitialized) {
- // We assume that OS_SPINLOCK_INIT is zero
+uptr GetPageSize() {
+ return sysconf(_SC_PAGESIZE);
}
BlockingMutex::BlockingMutex() {
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter) {
MemoryMappingLayout memory_mapping(false);
- memory_mapping.Reset();
- uptr cur_beg, cur_end, cur_offset;
- InternalScopedBuffer<char> module_name(kMaxPathLength);
- uptr n_modules = 0;
- for (uptr i = 0;
- n_modules < max_modules &&
- memory_mapping.Next(&cur_beg, &cur_end, &cur_offset,
- module_name.data(), module_name.size(), 0);
- i++) {
- const char *cur_name = module_name.data();
- if (cur_name[0] == '\0')
- continue;
- if (filter && !filter(cur_name))
- continue;
- LoadedModule *cur_module = 0;
- if (n_modules > 0 &&
- 0 == internal_strcmp(cur_name, modules[n_modules - 1].full_name())) {
- cur_module = &modules[n_modules - 1];
- } else {
- void *mem = &modules[n_modules];
- cur_module = new(mem) LoadedModule(cur_name, cur_beg);
- n_modules++;
+ return memory_mapping.DumpListOfModules(modules, max_modules, filter);
+}
+
+bool IsDeadlySignal(int signum) {
+ return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv;
+}
+
+MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED;
+
+MacosVersion GetMacosVersionInternal() {
+ int mib[2] = { CTL_KERN, KERN_OSRELEASE };
+ char version[100];
+ uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
+ for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
+ // Get the version length.
+ CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1);
+ CHECK_LT(len, maxlen);
+ CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1);
+ switch (version[0]) {
+ case '9': return MACOS_VERSION_LEOPARD;
+ case '1': {
+ switch (version[1]) {
+ case '0': return MACOS_VERSION_SNOW_LEOPARD;
+ case '1': return MACOS_VERSION_LION;
+ case '2': return MACOS_VERSION_MOUNTAIN_LION;
+ case '3': return MACOS_VERSION_MAVERICKS;
+ case '4': return MACOS_VERSION_YOSEMITE;
+ default:
+ if (IsDigit(version[1]))
+ return MACOS_VERSION_UNKNOWN_NEWER;
+ else
+ return MACOS_VERSION_UNKNOWN;
+ }
}
- cur_module->addAddressRange(cur_beg, cur_end);
+ default: return MACOS_VERSION_UNKNOWN;
+ }
+}
+
+MacosVersion GetMacosVersion() {
+ atomic_uint32_t *cache =
+ reinterpret_cast<atomic_uint32_t*>(&cached_macos_version);
+ MacosVersion result =
+ static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire));
+ if (result == MACOS_VERSION_UNINITIALIZED) {
+ result = GetMacosVersionInternal();
+ atomic_store(cache, result, memory_order_release);
+ }
+ return result;
+}
+
+uptr GetRSS() {
+ struct task_basic_info info;
+ unsigned count = TASK_BASIC_INFO_COUNT;
+ kern_return_t result =
+ task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count);
+ if (UNLIKELY(result != KERN_SUCCESS)) {
+ Report("Cannot get task info. Error: %d\n", result);
+ Die();
}
- return n_modules;
+ return info.resident_size;
+}
+
+void *internal_start_thread(void(*func)(void *arg), void *arg) {
+ // Start the thread with signals blocked, otherwise it can steal user signals.
+ __sanitizer_sigset_t set, old;
+ internal_sigfillset(&set);
+ internal_sigprocmask(SIG_SETMASK, &set, &old);
+ pthread_t th;
+ pthread_create(&th, 0, (void*(*)(void *arg))func, arg);
+ internal_sigprocmask(SIG_SETMASK, &old, 0);
+ return th;
+}
+
+void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); }
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+ ucontext_t *ucontext = (ucontext_t*)context;
+# if defined(__aarch64__)
+ *pc = ucontext->uc_mcontext->__ss.__pc;
+# if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
+ *bp = ucontext->uc_mcontext->__ss.__fp;
+# else
+ *bp = ucontext->uc_mcontext->__ss.__lr;
+# endif
+ *sp = ucontext->uc_mcontext->__ss.__sp;
+# elif defined(__x86_64__)
+ *pc = ucontext->uc_mcontext->__ss.__rip;
+ *bp = ucontext->uc_mcontext->__ss.__rbp;
+ *sp = ucontext->uc_mcontext->__ss.__rsp;
+# elif defined(__arm__)
+ *pc = ucontext->uc_mcontext->__ss.__pc;
+ *bp = ucontext->uc_mcontext->__ss.__r[7];
+ *sp = ucontext->uc_mcontext->__ss.__sp;
+# elif defined(__i386__)
+ *pc = ucontext->uc_mcontext->__ss.__eip;
+ *bp = ucontext->uc_mcontext->__ss.__ebp;
+ *sp = ucontext->uc_mcontext->__ss.__esp;
+# else
+# error "Unknown architecture"
+# endif
}
} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_mac.h -----------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries and
+// provides definitions for OSX-specific functions.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_MAC_H
+#define SANITIZER_MAC_H
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+#include "sanitizer_posix.h"
+
+namespace __sanitizer {
+
+enum MacosVersion {
+ MACOS_VERSION_UNINITIALIZED = 0,
+ MACOS_VERSION_UNKNOWN,
+ MACOS_VERSION_LEOPARD,
+ MACOS_VERSION_SNOW_LEOPARD,
+ MACOS_VERSION_LION,
+ MACOS_VERSION_MOUNTAIN_LION,
+ MACOS_VERSION_MAVERICKS,
+ MACOS_VERSION_YOSEMITE,
+ MACOS_VERSION_UNKNOWN_NEWER
+};
+
+MacosVersion GetMacosVersion();
+
+char **GetEnviron();
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
+#endif // SANITIZER_MAC_H
--- /dev/null
+//===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains Mac-specific malloc interceptors and a custom zone
+// implementation, which together replace the system allocator.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if !SANITIZER_MAC
+#error "This file should only be compiled on Darwin."
+#endif
+
+#include <AvailabilityMacros.h>
+#include <CoreFoundation/CFBase.h>
+#include <dlfcn.h>
+#include <malloc/malloc.h>
+#include <sys/mman.h>
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_mac.h"
+
+// Similar code is used in Google Perftools,
+// http://code.google.com/p/google-perftools.
+
+static malloc_zone_t sanitizer_zone;
+
+INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
+ vm_size_t start_size, unsigned zone_flags) {
+ COMMON_MALLOC_ENTER();
+ uptr page_size = GetPageSizeCached();
+ uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
+ COMMON_MALLOC_MEMALIGN(page_size, allocated_size);
+ malloc_zone_t *new_zone = (malloc_zone_t *)p;
+ internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));
+ new_zone->zone_name = NULL; // The name will be changed anyway.
+ if (GetMacosVersion() >= MACOS_VERSION_LION) {
+ // Prevent the client app from overwriting the zone contents.
+ // Library functions that need to modify the zone will set PROT_WRITE on it.
+ // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
+ mprotect(new_zone, allocated_size, PROT_READ);
+ }
+ return new_zone;
+}
+
+INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
+ COMMON_MALLOC_ENTER();
+ return &sanitizer_zone;
+}
+
+INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
+ // FIXME: ASan should support purgeable allocations.
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=139
+ COMMON_MALLOC_ENTER();
+ return &sanitizer_zone;
+}
+
+INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
+ // FIXME: ASan should support purgeable allocations. Ignoring them is fine
+ // for now.
+ COMMON_MALLOC_ENTER();
+}
+
+INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
+ // FIXME: ASan should support purgeable allocations. Ignoring them is fine
+ // for now.
+ COMMON_MALLOC_ENTER();
+ // Must return 0 if the contents were not purged since the last call to
+ // malloc_make_purgeable().
+ return 0;
+}
+
+INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
+ COMMON_MALLOC_ENTER();
+ // Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)|
+ // bytes.
+ size_t buflen =
+ sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0);
+ InternalScopedString new_name(buflen);
+ if (name && zone->introspect == sanitizer_zone.introspect) {
+ new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
+ name = new_name.data();
+ }
+
+ // Call the system malloc's implementation for both external and our zones,
+ // since that appropriately changes VM region protections on the zone.
+ REAL(malloc_set_zone_name)(zone, name);
+}
+
+INTERCEPTOR(void *, malloc, size_t size) {
+ COMMON_MALLOC_ENTER();
+ COMMON_MALLOC_MALLOC(size);
+ return p;
+}
+
+INTERCEPTOR(void, free, void *ptr) {
+ COMMON_MALLOC_ENTER();
+ if (!ptr) return;
+ COMMON_MALLOC_FREE(ptr);
+}
+
+INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
+ COMMON_MALLOC_ENTER();
+ COMMON_MALLOC_REALLOC(ptr, size);
+ return p;
+}
+
+INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
+ COMMON_MALLOC_ENTER();
+ COMMON_MALLOC_CALLOC(nmemb, size);
+ return p;
+}
+
+INTERCEPTOR(void *, valloc, size_t size) {
+ COMMON_MALLOC_ENTER();
+ COMMON_MALLOC_VALLOC(size);
+ return p;
+}
+
+INTERCEPTOR(size_t, malloc_good_size, size_t size) {
+ COMMON_MALLOC_ENTER();
+ return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
+}
+
+INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
+ COMMON_MALLOC_ENTER();
+ CHECK(memptr);
+ COMMON_MALLOC_MEMALIGN(alignment, size);
+ if (p) {
+ *memptr = p;
+ return 0;
+ }
+ return -1;
+}
+
+namespace {
+
+// TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
+// wrappers, as they are basically copied from there.
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
+ COMMON_MALLOC_SIZE(ptr);
+ return size;
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
+ COMMON_MALLOC_ENTER();
+ COMMON_MALLOC_MALLOC(size);
+ return p;
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
+ if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
+ // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
+ const size_t kCallocPoolSize = 1024;
+ static uptr calloc_memory_for_dlsym[kCallocPoolSize];
+ static size_t allocated;
+ size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
+ void *mem = (void*)&calloc_memory_for_dlsym[allocated];
+ allocated += size_in_words;
+ CHECK(allocated < kCallocPoolSize);
+ return mem;
+ }
+ COMMON_MALLOC_CALLOC(nmemb, size);
+ return p;
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
+ COMMON_MALLOC_ENTER();
+ COMMON_MALLOC_VALLOC(size);
+ return p;
+}
+
+#define GET_ZONE_FOR_PTR(ptr) \
+ malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
+ const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
+
+void ALWAYS_INLINE free_common(void *context, void *ptr) {
+ if (!ptr) return;
+ // FIXME: need to retire this flag.
+ if (!COMMON_MALLOC_IGNORE_INVALID_FREE) {
+ COMMON_MALLOC_FREE(ptr);
+ } else {
+ GET_ZONE_FOR_PTR(ptr);
+ COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name);
+ }
+}
+
+// TODO(glider): the allocation callbacks need to be refactored.
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
+ free_common(zone, ptr);
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
+ if (!ptr) {
+ COMMON_MALLOC_MALLOC(new_size);
+ return p;
+ } else {
+ COMMON_MALLOC_SIZE(ptr);
+ if (size) {
+ COMMON_MALLOC_REALLOC(ptr, new_size);
+ return p;
+ } else {
+ // We can't recover from reallocating an unknown address, because
+ // this would require reading at most |new_size| bytes from
+ // potentially unaccessible memory.
+ GET_ZONE_FOR_PTR(ptr);
+ COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
+ return nullptr;
+ }
+ }
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_mz_destroy(malloc_zone_t* zone) {
+ // A no-op -- we will not be destroyed!
+ Report("__sanitizer_mz_destroy() called -- ignoring\n");
+}
+
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
+ COMMON_MALLOC_ENTER();
+ COMMON_MALLOC_MEMALIGN(align, size);
+ return p;
+}
+
+// This function is currently unused, and we build with -Werror.
+#if 0
+void __sanitizer_mz_free_definite_size(
+ malloc_zone_t* zone, void *ptr, size_t size) {
+ // TODO(glider): check that |size| is valid.
+ UNIMPLEMENTED();
+}
+#endif
+
+kern_return_t mi_enumerator(task_t task, void *,
+ unsigned type_mask, vm_address_t zone_address,
+ memory_reader_t reader,
+ vm_range_recorder_t recorder) {
+ // Should enumerate all the pointers we have. Seems like a lot of work.
+ return KERN_FAILURE;
+}
+
+size_t mi_good_size(malloc_zone_t *zone, size_t size) {
+ // I think it's always safe to return size, but we maybe could do better.
+ return size;
+}
+
+boolean_t mi_check(malloc_zone_t *zone) {
+ UNIMPLEMENTED();
+}
+
+void mi_print(malloc_zone_t *zone, boolean_t verbose) {
+ UNIMPLEMENTED();
+}
+
+void mi_log(malloc_zone_t *zone, void *address) {
+ // I don't think we support anything like this
+}
+
+void mi_force_lock(malloc_zone_t *zone) {
+ COMMON_MALLOC_FORCE_LOCK();
+}
+
+void mi_force_unlock(malloc_zone_t *zone) {
+ COMMON_MALLOC_FORCE_UNLOCK();
+}
+
+void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
+ COMMON_MALLOC_FILL_STATS(zone, stats);
+}
+
+boolean_t mi_zone_locked(malloc_zone_t *zone) {
+ // UNIMPLEMENTED();
+ return false;
+}
+
+} // unnamed namespace
+
+namespace COMMON_MALLOC_NAMESPACE {
+
+void ReplaceSystemMalloc() {
+ static malloc_introspection_t sanitizer_zone_introspection;
+ // Ok to use internal_memset, these places are not performance-critical.
+ internal_memset(&sanitizer_zone_introspection, 0,
+ sizeof(sanitizer_zone_introspection));
+
+ sanitizer_zone_introspection.enumerator = &mi_enumerator;
+ sanitizer_zone_introspection.good_size = &mi_good_size;
+ sanitizer_zone_introspection.check = &mi_check;
+ sanitizer_zone_introspection.print = &mi_print;
+ sanitizer_zone_introspection.log = &mi_log;
+ sanitizer_zone_introspection.force_lock = &mi_force_lock;
+ sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
+ sanitizer_zone_introspection.statistics = &mi_statistics;
+ sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
+
+ internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
+
+ // Use version 6 for OSX >= 10.6.
+ sanitizer_zone.version = 6;
+ sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
+ sanitizer_zone.size = &__sanitizer_mz_size;
+ sanitizer_zone.malloc = &__sanitizer_mz_malloc;
+ sanitizer_zone.calloc = &__sanitizer_mz_calloc;
+ sanitizer_zone.valloc = &__sanitizer_mz_valloc;
+ sanitizer_zone.free = &__sanitizer_mz_free;
+ sanitizer_zone.realloc = &__sanitizer_mz_realloc;
+ sanitizer_zone.destroy = &__sanitizer_mz_destroy;
+ sanitizer_zone.batch_malloc = 0;
+ sanitizer_zone.batch_free = 0;
+ sanitizer_zone.free_definite_size = 0;
+ sanitizer_zone.memalign = &__sanitizer_mz_memalign;
+ sanitizer_zone.introspect = &sanitizer_zone_introspection;
+
+ // Register the zone.
+ malloc_zone_register(&sanitizer_zone);
+}
+
+} // namespace COMMON_MALLOC_NAMESPACE
class BlockingMutex {
public:
+#if SANITIZER_WINDOWS
+ // Windows does not currently support LinkerInitialized
explicit BlockingMutex(LinkerInitialized);
+#else
+ explicit constexpr BlockingMutex(LinkerInitialized)
+ : opaque_storage_ {0, }, owner_(0) {}
+#endif
BlockingMutex();
void Lock();
void Unlock();
uptr owner_; // for debugging
};
+// Reader-writer spin mutex.
+class RWMutex {
+ public:
+ RWMutex() {
+ atomic_store(&state_, kUnlocked, memory_order_relaxed);
+ }
+
+ ~RWMutex() {
+ CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
+ }
+
+ void Lock() {
+ u32 cmp = kUnlocked;
+ if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
+ memory_order_acquire))
+ return;
+ LockSlow();
+ }
+
+ void Unlock() {
+ u32 prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
+ DCHECK_NE(prev & kWriteLock, 0);
+ (void)prev;
+ }
+
+ void ReadLock() {
+ u32 prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
+ if ((prev & kWriteLock) == 0)
+ return;
+ ReadLockSlow();
+ }
+
+ void ReadUnlock() {
+ u32 prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
+ DCHECK_EQ(prev & kWriteLock, 0);
+ DCHECK_GT(prev & ~kWriteLock, 0);
+ (void)prev;
+ }
+
+ void CheckLocked() {
+ CHECK_NE(atomic_load(&state_, memory_order_relaxed), kUnlocked);
+ }
+
+ private:
+ atomic_uint32_t state_;
+
+ enum {
+ kUnlocked = 0,
+ kWriteLock = 1,
+ kReadLock = 2
+ };
+
+ void NOINLINE LockSlow() {
+ for (int i = 0;; i++) {
+ if (i < 10)
+ proc_yield(10);
+ else
+ internal_sched_yield();
+ u32 cmp = atomic_load(&state_, memory_order_relaxed);
+ if (cmp == kUnlocked &&
+ atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
+ memory_order_acquire))
+ return;
+ }
+ }
+
+ void NOINLINE ReadLockSlow() {
+ for (int i = 0;; i++) {
+ if (i < 10)
+ proc_yield(10);
+ else
+ internal_sched_yield();
+ u32 prev = atomic_load(&state_, memory_order_acquire);
+ if ((prev & kWriteLock) == 0)
+ return;
+ }
+ }
+
+ RWMutex(const RWMutex&);
+ void operator = (const RWMutex&);
+};
+
template<typename MutexType>
class GenericScopedLock {
public:
typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
typedef GenericScopedLock<BlockingMutex> BlockingMutexLock;
+typedef GenericScopedLock<RWMutex> RWMutexLock;
+typedef GenericScopedReadLock<RWMutex> RWMutexReadLock;
} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_persistent_allocator.cc -----------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+#include "sanitizer_persistent_allocator.h"
+
+namespace __sanitizer {
+
+PersistentAllocator thePersistentAllocator;
+
+} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_persistent_allocator.h ------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A fast memory allocator that does not support free() nor realloc().
+// All allocations are forever.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_PERSISTENT_ALLOCATOR_H
+#define SANITIZER_PERSISTENT_ALLOCATOR_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+class PersistentAllocator {
+ public:
+ void *alloc(uptr size);
+
+ private:
+ void *tryAlloc(uptr size);
+ StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator.
+ atomic_uintptr_t region_pos; // Region allocator for Node's.
+ atomic_uintptr_t region_end;
+};
+
+inline void *PersistentAllocator::tryAlloc(uptr size) {
+ // Optimisic lock-free allocation, essentially try to bump the region ptr.
+ for (;;) {
+ uptr cmp = atomic_load(®ion_pos, memory_order_acquire);
+ uptr end = atomic_load(®ion_end, memory_order_acquire);
+ if (cmp == 0 || cmp + size > end) return nullptr;
+ if (atomic_compare_exchange_weak(®ion_pos, &cmp, cmp + size,
+ memory_order_acquire))
+ return (void *)cmp;
+ }
+}
+
+inline void *PersistentAllocator::alloc(uptr size) {
+ // First, try to allocate optimisitically.
+ void *s = tryAlloc(size);
+ if (s) return s;
+ // If failed, lock, retry and alloc new superblock.
+ SpinMutexLock l(&mtx);
+ for (;;) {
+ s = tryAlloc(size);
+ if (s) return s;
+ atomic_store(®ion_pos, 0, memory_order_relaxed);
+ uptr allocsz = 64 * 1024;
+ if (allocsz < size) allocsz = size;
+ uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
+ atomic_store(®ion_end, mem + allocsz, memory_order_release);
+ atomic_store(®ion_pos, mem, memory_order_release);
+ }
+}
+
+extern PersistentAllocator thePersistentAllocator;
+inline void *PersistentAlloc(uptr sz) {
+ return thePersistentAllocator.alloc(sz);
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_PERSISTENT_ALLOCATOR_H
#ifndef SANITIZER_PLATFORM_H
#define SANITIZER_PLATFORM_H
-#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
+#if !defined(__linux__) && !defined(__FreeBSD__) && \
+ !defined(__APPLE__) && !defined(_WIN32)
# error "This operating system is not supported"
#endif
# define SANITIZER_LINUX 0
#endif
+#if defined(__FreeBSD__)
+# define SANITIZER_FREEBSD 1
+#else
+# define SANITIZER_FREEBSD 0
+#endif
+
#if defined(__APPLE__)
# define SANITIZER_MAC 1
# include <TargetConditionals.h>
# else
# define SANITIZER_IOS 0
# endif
+# if TARGET_IPHONE_SIMULATOR
+# define SANITIZER_IOSSIM 1
+# else
+# define SANITIZER_IOSSIM 0
+# endif
#else
# define SANITIZER_MAC 0
# define SANITIZER_IOS 0
+# define SANITIZER_IOSSIM 0
#endif
#if defined(_WIN32)
# define SANITIZER_WINDOWS 0
#endif
-#if defined(__ANDROID__) || defined(ANDROID)
+#if defined(__ANDROID__)
# define SANITIZER_ANDROID 1
#else
# define SANITIZER_ANDROID 0
#endif
-#define SANITIZER_POSIX (SANITIZER_LINUX || SANITIZER_MAC)
+#define SANITIZER_POSIX (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC)
+
+#if __LP64__ || defined(_WIN64)
+# define SANITIZER_WORDSIZE 64
+#else
+# define SANITIZER_WORDSIZE 32
+#endif
+
+#if SANITIZER_WORDSIZE == 64
+# define FIRST_32_SECOND_64(a, b) (b)
+#else
+# define FIRST_32_SECOND_64(a, b) (a)
+#endif
+
+#if defined(__x86_64__) && !defined(_LP64)
+# define SANITIZER_X32 1
+#else
+# define SANITIZER_X32 0
+#endif
+
+// VMA size definition for architecture that support multiple sizes.
+// AArch64 has 3 VMA sizes: 39, 42 and 48.
+#if !defined(SANITIZER_AARCH64_VMA)
+# define SANITIZER_AARCH64_VMA 39
+#else
+# if SANITIZER_AARCH64_VMA != 39 && SANITIZER_AARCH64_VMA != 42
+# error "invalid SANITIZER_AARCH64_VMA size"
+# endif
+#endif
+
+// By default we allow to use SizeClassAllocator64 on 64-bit platform.
+// But in some cases (e.g. AArch64's 39-bit address space) SizeClassAllocator64
+// does not work well and we need to fallback to SizeClassAllocator32.
+// For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or
+// change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here.
+#ifndef SANITIZER_CAN_USE_ALLOCATOR64
+# if defined(__mips64) || defined(__aarch64__)
+# define SANITIZER_CAN_USE_ALLOCATOR64 0
+# else
+# define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64)
+# endif
+#endif
+
+// The range of addresses which can be returned my mmap.
+// FIXME: this value should be different on different platforms. Larger values
+// will still work but will consume more memory for TwoLevelByteMap.
+#if defined(__mips__)
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40)
+#else
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
+#endif
+
+// The AArch64 linux port uses the canonical syscall set as mandated by
+// the upstream linux community for all new ports. Other ports may still
+// use legacy syscalls.
+#ifndef SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+# if defined(__aarch64__) && SANITIZER_LINUX
+# define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 1
+# else
+# define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 0
+# endif
+#endif
+
+// udi16 syscalls can only be used when the following conditions are
+// met:
+// * target is one of arm32, x86-32, sparc32, sh or m68k
+// * libc version is libc5, glibc-2.0, glibc-2.1 or glibc-2.2 to 2.15
+// built against > linux-2.2 kernel headers
+// Since we don't want to include libc headers here, we check the
+// target only.
+#if defined(__arm__) || SANITIZER_X32 || defined(__sparc__)
+#define SANITIZER_USES_UID16_SYSCALLS 1
+#else
+#define SANITIZER_USES_UID16_SYSCALLS 0
+#endif
+
+#if defined(__mips__) || (defined(__aarch64__) && SANITIZER_AARCH64_VMA == 39)
+# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10)
+#elif defined(__aarch64__) && SANITIZER_AARCH64_VMA == 42
+# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 11)
+#else
+# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12)
+#endif
+
+// Assume obsolete RPC headers are available by default
+#if !defined(HAVE_RPC_XDR_H) && !defined(HAVE_TIRPC_RPC_XDR_H)
+# define HAVE_RPC_XDR_H (SANITIZER_LINUX && !SANITIZER_ANDROID)
+# define HAVE_TIRPC_RPC_XDR_H 0
+#endif
+
+/// \macro MSC_PREREQ
+/// \brief Is the compiler MSVC of at least the specified version?
+/// The common \param version values to check for are:
+/// * 1800: Microsoft Visual Studio 2013 / 12.0
+/// * 1900: Microsoft Visual Studio 2015 / 14.0
+#ifdef _MSC_VER
+# define MSC_PREREQ(version) (_MSC_VER >= (version))
+#else
+# define MSC_PREREQ(version) 0
+#endif
#endif // SANITIZER_PLATFORM_H
# define SI_LINUX_NOT_ANDROID 0
#endif
+#if SANITIZER_FREEBSD
+# define SI_FREEBSD 1
+#else
+# define SI_FREEBSD 0
+#endif
+
#if SANITIZER_LINUX
# define SI_LINUX 1
#else
# define SI_IOS 0
#endif
-# define SANITIZER_INTERCEPT_STRCMP 1
-# define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_STRCMP 1
+#define SANITIZER_INTERCEPT_STRSTR 1
+#define SANITIZER_INTERCEPT_STRCASESTR SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_STRSPN 1
+#define SANITIZER_INTERCEPT_STRPBRK 1
+#define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_MEMCMP 1
+#define SANITIZER_INTERCEPT_MEMCHR 1
+#define SANITIZER_INTERCEPT_MEMRCHR SI_FREEBSD || SI_LINUX
-# define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_PREAD SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_WRITE SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_PWRITE SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PREAD SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_WRITE SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PWRITE SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_READV SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_WRITEV SI_NOT_WINDOWS
-#define SANITIZER_INTERCEPT_PREADV SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PREADV SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_PRCTL SI_LINUX
-
-# define SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS
-
-# define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX
-
-# define SANITIZER_INTERCEPT_FREXP 1
-# define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_NOT_WINDOWS
-
-# define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \
- SI_MAC || SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_LINUX
-# define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_GLOB SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_WAIT SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_INET SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_GETADDRINFO SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_GETNAMEINFO SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_LINUX
-# define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX
-# define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_IOCTL SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_INET_ATON SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_SYSINFO SI_LINUX
-# define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \
- (defined(__i386) || defined (__x86_64)) // NOLINT
-# define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX
-# define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX
-# define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_CONFSTR SI_MAC || SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_SCANDIR SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_WORDEXP SI_MAC || SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_SIGSETOPS SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
-# define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_STATFS SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_STATFS64 \
- (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_ETHER SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_ETHER_R SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_SHMCTL \
- (SI_LINUX_NOT_ANDROID && SANITIZER_WORDSIZE == 64)
-# define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \
+#define SANITIZER_INTERCEPT_PRCTL SI_LINUX
+
+#define SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS
+
+#define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX_NOT_ANDROID
+
+#ifndef SANITIZER_INTERCEPT_PRINTF
+# define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_PRINTF_L SI_FREEBSD
+# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID
+#endif
+
+#define SANITIZER_INTERCEPT_FREXP 1
+#define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_NOT_WINDOWS
+
+#define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETPWENT \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETPWENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SETPWENT SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_FREEBSD || SI_LINUX
+#define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GLOB SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WAIT SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_INET SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETADDRINFO SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETNAMEINFO SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_FREEBSD || SI_LINUX
+#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETHOSTENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_IOCTL SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_INET_ATON SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_SYSINFO SI_LINUX
+#define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__))
+#define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_WCSNRTOMBS \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WCRTOMB \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_CONFSTR \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCHED_GETPARAM SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCANDIR \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WORDEXP \
+ SI_FREEBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SIGSETOPS \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_BACKTRACE SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
+#define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STATFS SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STATFS64 \
+ (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STATVFS SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_ETHER_NTOA_ATON SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_ETHER_HOST \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_ETHER_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SHMCTL \
+ ((SI_FREEBSD || SI_LINUX_NOT_ANDROID) && SANITIZER_WORDSIZE == 64)
+#define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL \
SI_MAC || SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_SINCOS SI_LINUX
-# define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX
-# define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT_TIMES SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING \
+ SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_SINCOS SI_LINUX
+#define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_LGAMMA_R SI_FREEBSD || SI_LINUX
+#define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_RAND_R \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_ICONV SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TIMES SI_NOT_WINDOWS
// FIXME: getline seems to be available on OSX 10.7
-# define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETLINE SI_FREEBSD || SI_LINUX_NOT_ANDROID
+
+#define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD || SI_MAC
+
+#define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID
+
+#define SANITIZER_INTERCEPT_TLS_GET_ADDR \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID
+
+#define SANITIZER_INTERCEPT_LISTXATTR SI_LINUX
+#define SANITIZER_INTERCEPT_GETXATTR SI_LINUX
+#define SANITIZER_INTERCEPT_GETRESID SI_LINUX
+#define SANITIZER_INTERCEPT_GETIFADDRS \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_IF_INDEXTONAME \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_CAPGET SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_AEABI_MEM SI_LINUX && defined(__arm__)
+#define SANITIZER_INTERCEPT___BZERO SI_MAC
+#define SANITIZER_INTERCEPT_FTIME !SI_FREEBSD && SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TSEARCH SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_FOPEN SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_FOPEN64 SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_OPEN_MEMSTREAM SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_GETPASS SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID
-# define SANITIZER_INTERCEPT__EXIT SI_LINUX
+#define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_FOPENCOOKIE SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SEM SI_LINUX || SI_FREEBSD
+#define SANITIZER_INTERCEPT_PTHREAD_SETCANCEL SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_MINCORE SI_LINUX
+#define SANITIZER_INTERCEPT_PROCESS_VM_READV SI_LINUX
-# define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_PTHREAD_COND SI_NOT_WINDOWS
-# define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPTOR_HOOKS SI_LINUX
#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
// are not defined anywhere in userspace headers. Fake them. This seems to work
// fine with newer headers, too.
#include <asm/posix_types.h>
-#if defined(__x86_64__)
+#if defined(__x86_64__) || defined(__mips__)
#include <sys/stat.h>
#else
#define ino_t __kernel_ino_t
#include <linux/aio_abi.h>
-#if SANITIZER_ANDROID
-#include <asm/statfs.h>
-#else
-#include <sys/statfs.h>
-#endif
-
#if !SANITIZER_ANDROID
+#include <sys/statfs.h>
#include <linux/perf_event.h>
#endif
namespace __sanitizer {
+#if !SANITIZER_ANDROID
unsigned struct_statfs64_sz = sizeof(struct statfs64);
+#endif
} // namespace __sanitizer
-#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__sparc__)
+#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__)\
+ && !defined(__mips__) && !defined(__sparc__)
COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
#endif
// Sizes and layouts of platform-specific POSIX data structures.
//===----------------------------------------------------------------------===//
-
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX || SANITIZER_MAC
-
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_platform_limits_posix.h"
+#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
+// Tests in this file assume that off_t-dependent data structures match the
+// libc ABI. For example, struct dirent here is what readdir() function (as
+// exported from libc) returns, and not the user-facing "dirent", which
+// depends on _FILE_OFFSET_BITS setting.
+// To get this "true" dirent definition, we undefine _FILE_OFFSET_BITS below.
+#ifdef _FILE_OFFSET_BITS
+#undef _FILE_OFFSET_BITS
+#endif
+#if SANITIZER_FREEBSD
+#define _WANT_RTENTRY
+#include <sys/param.h>
+#include <sys/socketvar.h>
+#endif
#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#include <grp.h>
#include <limits.h>
#include <net/if.h>
-#include <net/if_arp.h>
-#include <net/route.h>
#include <netdb.h>
#include <poll.h>
#include <pthread.h>
#include <pwd.h>
#include <signal.h>
#include <stddef.h>
+#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <time.h>
#include <wchar.h>
+#if !SANITIZER_IOS
+#include <net/route.h>
+#endif
+
+#if !SANITIZER_ANDROID
+#include <sys/mount.h>
+#include <sys/timeb.h>
+#endif
+
#if SANITIZER_LINUX
+#include <malloc.h>
#include <mntent.h>
#include <netinet/ether.h>
-#include <utime.h>
-#include <sys/mount.h>
-#include <sys/ptrace.h>
#include <sys/sysinfo.h>
#include <sys/vt.h>
#include <linux/cdrom.h>
#include <linux/sysctl.h>
#include <linux/utsname.h>
#include <linux/posix_types.h>
+#include <net/if_arp.h>
+#endif
+
+#if SANITIZER_FREEBSD
+# include <sys/mount.h>
+# include <sys/sockio.h>
+# include <sys/socket.h>
+# include <sys/filio.h>
+# include <sys/signal.h>
+# include <sys/timespec.h>
+# include <sys/timex.h>
+# include <sys/mqueue.h>
+# include <sys/msg.h>
+# include <sys/ipc.h>
+# include <sys/msg.h>
+# include <sys/statvfs.h>
+# include <sys/soundcard.h>
+# include <sys/mtio.h>
+# include <sys/consio.h>
+# include <sys/kbio.h>
+# include <sys/link_elf.h>
+# include <netinet/ip_mroute.h>
+# include <netinet/in.h>
+# include <net/ethernet.h>
+# include <net/ppp_defs.h>
+# include <glob.h>
+# include <term.h>
+
+#define _KERNEL // to declare 'shminfo' structure
+# include <sys/shm.h>
+#undef _KERNEL
+
+#undef INLINE // to avoid clashes with sanitizers' definitions
+#endif
+
+#if SANITIZER_FREEBSD || SANITIZER_IOS
+#undef IOC_DIRMASK
+#endif
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+# include <utime.h>
+# include <sys/ptrace.h>
+# if defined(__mips64) || defined(__aarch64__) || defined(__arm__)
+# include <asm/ptrace.h>
+# ifdef __arm__
+typedef struct user_fpregs elf_fpregset_t;
+# endif
+# endif
+# include <semaphore.h>
#endif
#if !SANITIZER_ANDROID
+#include <ifaddrs.h>
#include <sys/ucontext.h>
#include <wordexp.h>
#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID
#include <glob.h>
+#include <obstack.h>
#include <mqueue.h>
#include <net/if_ppp.h>
#include <netax25/ax25.h>
#include <netipx/ipx.h>
#include <netrom/netrom.h>
+#if HAVE_RPC_XDR_H
+# include <rpc/xdr.h>
+#elif HAVE_TIRPC_RPC_XDR_H
+# include <tirpc/rpc/xdr.h>
+#endif
#include <scsi/scsi.h>
#include <sys/mtio.h>
#include <sys/kd.h>
#include <sys/shm.h>
#include <sys/statvfs.h>
#include <sys/timex.h>
+#if defined(__mips64)
+# include <sys/procfs.h>
+#endif
#include <sys/user.h>
#include <sys/ustat.h>
#include <linux/cyclades.h>
#include <linux/serial.h>
#include <sys/msg.h>
#include <sys/ipc.h>
-#include <sys/shm.h>
#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
#if SANITIZER_ANDROID
#if SANITIZER_MAC
#include <net/ethernet.h>
#include <sys/filio.h>
-#include <sys/mount.h>
#include <sys/sockio.h>
#endif
+// Include these after system headers to avoid name clashes and ambiguities.
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_posix.h"
+
namespace __sanitizer {
unsigned struct_utsname_sz = sizeof(struct utsname);
unsigned struct_stat_sz = sizeof(struct stat);
-#if !SANITIZER_IOS
+#if !SANITIZER_IOS && !SANITIZER_FREEBSD
unsigned struct_stat64_sz = sizeof(struct stat64);
-#endif // !SANITIZER_IOS
+#endif // !SANITIZER_IOS && !SANITIZER_FREEBSD
unsigned struct_rusage_sz = sizeof(struct rusage);
unsigned struct_tm_sz = sizeof(struct tm);
unsigned struct_passwd_sz = sizeof(struct passwd);
unsigned pid_t_sz = sizeof(pid_t);
unsigned timeval_sz = sizeof(timeval);
unsigned uid_t_sz = sizeof(uid_t);
+ unsigned gid_t_sz = sizeof(gid_t);
unsigned mbstate_t_sz = sizeof(mbstate_t);
unsigned sigset_t_sz = sizeof(sigset_t);
unsigned struct_timezone_sz = sizeof(struct timezone);
unsigned struct_tms_sz = sizeof(struct tms);
unsigned struct_sigevent_sz = sizeof(struct sigevent);
unsigned struct_sched_param_sz = sizeof(struct sched_param);
- unsigned struct_statfs_sz = sizeof(struct statfs);
+
#if SANITIZER_MAC && !SANITIZER_IOS
unsigned struct_statfs64_sz = sizeof(struct statfs64);
#endif // SANITIZER_MAC && !SANITIZER_IOS
#if !SANITIZER_ANDROID
+ unsigned struct_statfs_sz = sizeof(struct statfs);
+ unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
unsigned ucontext_t_sz = sizeof(ucontext_t);
#endif // !SANITIZER_ANDROID
#if SANITIZER_LINUX
- unsigned struct_rlimit_sz = sizeof(struct rlimit);
unsigned struct_epoll_event_sz = sizeof(struct epoll_event);
unsigned struct_sysinfo_sz = sizeof(struct sysinfo);
- unsigned struct_timespec_sz = sizeof(struct timespec);
unsigned __user_cap_header_struct_sz =
sizeof(struct __user_cap_header_struct);
unsigned __user_cap_data_struct_sz = sizeof(struct __user_cap_data_struct);
- unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
unsigned struct_new_utsname_sz = sizeof(struct new_utsname);
unsigned struct_old_utsname_sz = sizeof(struct old_utsname);
unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname);
- unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
- unsigned struct_ustat_sz = sizeof(struct ustat);
#endif // SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+ unsigned struct_rlimit_sz = sizeof(struct rlimit);
+ unsigned struct_timespec_sz = sizeof(struct timespec);
+ unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
+ unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ unsigned struct_ustat_sz = sizeof(struct ustat);
unsigned struct_rlimit64_sz = sizeof(struct rlimit64);
+ unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
unsigned struct_timex_sz = sizeof(struct timex);
unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
unsigned struct_statvfs_sz = sizeof(struct statvfs);
- unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
uptr sig_ign = (uptr)SIG_IGN;
uptr sig_dfl = (uptr)SIG_DFL;
#endif
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
unsigned struct_shminfo_sz = sizeof(struct shminfo);
unsigned struct_shm_info_sz = sizeof(struct shm_info);
int shmctl_ipc_stat = (int)IPC_STAT;
int shmctl_ipc_info = (int)IPC_INFO;
int shmctl_shm_info = (int)SHM_INFO;
- int shmctl_shm_stat = (int)SHM_INFO;
+ int shmctl_shm_stat = (int)SHM_STAT;
#endif
+ int map_fixed = MAP_FIXED;
+
int af_inet = (int)AF_INET;
int af_inet6 = (int)AF_INET6;
return 0;
}
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#if SANITIZER_LINUX
+unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr));
+#elif SANITIZER_FREEBSD
+unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
+#endif
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
int glob_nomatch = GLOB_NOMATCH;
int glob_altdirfunc = GLOB_ALTDIRFUNC;
#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined (__x86_64)) // NOLINT
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__))
+#if defined(__mips64) || defined(__powerpc64__) || defined(__arm__)
+ unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs);
+ unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t);
+#elif defined(__aarch64__)
+ unsigned struct_user_regs_struct_sz = sizeof(struct user_pt_regs);
+ unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpsimd_state);
+#else
unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct);
unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct);
-#ifdef __x86_64
+#endif // __mips64 || __powerpc64__ || __aarch64__
+#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \
+ defined(__aarch64__) || defined(__arm__)
unsigned struct_user_fpxregs_struct_sz = 0;
#else
unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct);
+#endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__ || __arm__
+#ifdef __arm__
+ unsigned struct_user_vfpregs_struct_sz = ARM_VFPREGS_SIZE;
+#else
+ unsigned struct_user_vfpregs_struct_sz = 0;
#endif
int ptrace_peektext = PTRACE_PEEKTEXT;
int ptrace_peekdata = PTRACE_PEEKDATA;
int ptrace_peekuser = PTRACE_PEEKUSER;
+#if (defined(PTRACE_GETREGS) && defined(PTRACE_SETREGS)) || \
+ (defined(PT_GETREGS) && defined(PT_SETREGS))
int ptrace_getregs = PTRACE_GETREGS;
int ptrace_setregs = PTRACE_SETREGS;
+#else
+ int ptrace_getregs = -1;
+ int ptrace_setregs = -1;
+#endif
+#if (defined(PTRACE_GETFPREGS) && defined(PTRACE_SETFPREGS)) || \
+ (defined(PT_GETFPREGS) && defined(PT_SETFPREGS))
int ptrace_getfpregs = PTRACE_GETFPREGS;
int ptrace_setfpregs = PTRACE_SETFPREGS;
+#else
+ int ptrace_getfpregs = -1;
+ int ptrace_setfpregs = -1;
+#endif
+#if (defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS)) || \
+ (defined(PT_GETFPXREGS) && defined(PT_SETFPXREGS))
int ptrace_getfpxregs = PTRACE_GETFPXREGS;
int ptrace_setfpxregs = PTRACE_SETFPXREGS;
+#else
+ int ptrace_getfpxregs = -1;
+ int ptrace_setfpxregs = -1;
+#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS
+#if defined(PTRACE_GETVFPREGS) && defined(PTRACE_SETVFPREGS)
+ int ptrace_getvfpregs = PTRACE_GETVFPREGS;
+ int ptrace_setvfpregs = PTRACE_SETVFPREGS;
+#else
+ int ptrace_getvfpregs = -1;
+ int ptrace_setvfpregs = -1;
+#endif
+ int ptrace_geteventmsg = PTRACE_GETEVENTMSG;
+#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \
+ (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))
int ptrace_getsiginfo = PTRACE_GETSIGINFO;
int ptrace_setsiginfo = PTRACE_SETSIGINFO;
+#else
+ int ptrace_getsiginfo = -1;
+ int ptrace_setsiginfo = -1;
+#endif // PTRACE_GETSIGINFO/PTRACE_SETSIGINFO
#if defined(PTRACE_GETREGSET) && defined(PTRACE_SETREGSET)
int ptrace_getregset = PTRACE_GETREGSET;
int ptrace_setregset = PTRACE_SETREGSET;
#else
int ptrace_getregset = -1;
int ptrace_setregset = -1;
-#endif
+#endif // PTRACE_GETREGSET/PTRACE_SETREGSET
#endif
unsigned path_max = PATH_MAX;
// ioctl arguments
- unsigned struct_arpreq_sz = sizeof(struct arpreq);
unsigned struct_ifreq_sz = sizeof(struct ifreq);
unsigned struct_termios_sz = sizeof(struct termios);
unsigned struct_winsize_sz = sizeof(struct winsize);
#if SANITIZER_LINUX
+ unsigned struct_arpreq_sz = sizeof(struct arpreq);
unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf);
unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession);
unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio);
unsigned struct_cdrom_tocentry_sz = sizeof(struct cdrom_tocentry);
unsigned struct_cdrom_tochdr_sz = sizeof(struct cdrom_tochdr);
unsigned struct_cdrom_volctrl_sz = sizeof(struct cdrom_volctrl);
-#if SOUND_VERSION >= 0x040000
- unsigned struct_copr_buffer_sz = 0;
- unsigned struct_copr_debug_buf_sz = 0;
- unsigned struct_copr_msg_sz = 0;
-#else
- unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer);
- unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf);
- unsigned struct_copr_msg_sz = sizeof(struct copr_msg);
-#endif
unsigned struct_ff_effect_sz = sizeof(struct ff_effect);
unsigned struct_floppy_drive_params_sz = sizeof(struct floppy_drive_params);
unsigned struct_floppy_drive_struct_sz = sizeof(struct floppy_drive_struct);
unsigned struct_hd_geometry_sz = sizeof(struct hd_geometry);
unsigned struct_input_absinfo_sz = sizeof(struct input_absinfo);
unsigned struct_input_id_sz = sizeof(struct input_id);
+ unsigned struct_mtpos_sz = sizeof(struct mtpos);
+ unsigned struct_termio_sz = sizeof(struct termio);
+ unsigned struct_vt_consize_sz = sizeof(struct vt_consize);
+ unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes);
+ unsigned struct_vt_stat_sz = sizeof(struct vt_stat);
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SOUND_VERSION >= 0x040000
+ unsigned struct_copr_buffer_sz = 0;
+ unsigned struct_copr_debug_buf_sz = 0;
+ unsigned struct_copr_msg_sz = 0;
+#else
+ unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer);
+ unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf);
+ unsigned struct_copr_msg_sz = sizeof(struct copr_msg);
+#endif
unsigned struct_midi_info_sz = sizeof(struct midi_info);
unsigned struct_mtget_sz = sizeof(struct mtget);
unsigned struct_mtop_sz = sizeof(struct mtop);
- unsigned struct_mtpos_sz = sizeof(struct mtpos);
unsigned struct_rtentry_sz = sizeof(struct rtentry);
unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument);
unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec);
unsigned struct_synth_info_sz = sizeof(struct synth_info);
- unsigned struct_termio_sz = sizeof(struct termio);
- unsigned struct_vt_consize_sz = sizeof(struct vt_consize);
unsigned struct_vt_mode_sz = sizeof(struct vt_mode);
- unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes);
- unsigned struct_vt_stat_sz = sizeof(struct vt_stat);
-#endif
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
#if SANITIZER_LINUX && !SANITIZER_ANDROID
- unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct);
unsigned struct_cyclades_monitor_sz = sizeof(struct cyclades_monitor);
#if EV_VERSION > (0x010000)
unsigned struct_kbsentry_sz = sizeof(struct kbsentry);
unsigned struct_mtconfiginfo_sz = sizeof(struct mtconfiginfo);
unsigned struct_nr_parms_struct_sz = sizeof(struct nr_parms_struct);
- unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
unsigned struct_scc_modem_sz = sizeof(struct scc_modem);
unsigned struct_scc_stat_sz = sizeof(struct scc_stat);
unsigned struct_serial_multiport_struct_sz
unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25);
unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc);
unsigned struct_unimapinit_sz = sizeof(struct unimapinit);
-#endif
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+ unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
+ unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
#if !SANITIZER_ANDROID && !SANITIZER_MAC
unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
#endif
- unsigned IOCTL_NOT_PRESENT = 0;
+ const unsigned IOCTL_NOT_PRESENT = 0;
unsigned IOCTL_FIOASYNC = FIOASYNC;
unsigned IOCTL_FIOCLEX = FIOCLEX;
unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
unsigned IOCTL_TIOCSTI = TIOCSTI;
unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
-#if (SANITIZER_LINUX && !SANITIZER_ANDROID)
+#if ((SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID)
unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
#endif
+
#if SANITIZER_LINUX
unsigned IOCTL_EVIOCGABS = EVIOCGABS(0);
unsigned IOCTL_EVIOCGBIT = EVIOCGBIT(0, 0);
unsigned IOCTL_HDIO_SET_MULTCOUNT = HDIO_SET_MULTCOUNT;
unsigned IOCTL_HDIO_SET_NOWERR = HDIO_SET_NOWERR;
unsigned IOCTL_HDIO_SET_UNMASKINTR = HDIO_SET_UNMASKINTR;
- unsigned IOCTL_MTIOCGET = MTIOCGET;
unsigned IOCTL_MTIOCPOS = MTIOCPOS;
- unsigned IOCTL_MTIOCTOP = MTIOCTOP;
unsigned IOCTL_PPPIOCGASYNCMAP = PPPIOCGASYNCMAP;
unsigned IOCTL_PPPIOCGDEBUG = PPPIOCGDEBUG;
unsigned IOCTL_PPPIOCGFLAGS = PPPIOCGFLAGS;
unsigned IOCTL_SIOCSIFMEM = SIOCSIFMEM;
unsigned IOCTL_SIOCSIFSLAVE = SIOCSIFSLAVE;
unsigned IOCTL_SIOCSRARP = SIOCSRARP;
-#if SOUND_VERSION >= 0x040000
+# if SOUND_VERSION >= 0x040000
unsigned IOCTL_SNDCTL_COPR_HALT = IOCTL_NOT_PRESENT;
unsigned IOCTL_SNDCTL_COPR_LOAD = IOCTL_NOT_PRESENT;
unsigned IOCTL_SNDCTL_COPR_RCODE = IOCTL_NOT_PRESENT;
unsigned IOCTL_SOUND_PCM_READ_RATE = IOCTL_NOT_PRESENT;
unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = IOCTL_NOT_PRESENT;
unsigned IOCTL_SOUND_PCM_WRITE_FILTER = IOCTL_NOT_PRESENT;
-#else
+# else // SOUND_VERSION
unsigned IOCTL_SNDCTL_COPR_HALT = SNDCTL_COPR_HALT;
unsigned IOCTL_SNDCTL_COPR_LOAD = SNDCTL_COPR_LOAD;
unsigned IOCTL_SNDCTL_COPR_RCODE = SNDCTL_COPR_RCODE;
unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE;
unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = SOUND_PCM_WRITE_CHANNELS;
unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER;
-#endif
+#endif // SOUND_VERSION
+ unsigned IOCTL_TCFLSH = TCFLSH;
+ unsigned IOCTL_TCGETA = TCGETA;
+ unsigned IOCTL_TCGETS = TCGETS;
+ unsigned IOCTL_TCSBRK = TCSBRK;
+ unsigned IOCTL_TCSBRKP = TCSBRKP;
+ unsigned IOCTL_TCSETA = TCSETA;
+ unsigned IOCTL_TCSETAF = TCSETAF;
+ unsigned IOCTL_TCSETAW = TCSETAW;
+ unsigned IOCTL_TCSETS = TCSETS;
+ unsigned IOCTL_TCSETSF = TCSETSF;
+ unsigned IOCTL_TCSETSW = TCSETSW;
+ unsigned IOCTL_TCXONC = TCXONC;
+ unsigned IOCTL_TIOCGLCKTRMIOS = TIOCGLCKTRMIOS;
+ unsigned IOCTL_TIOCGSOFTCAR = TIOCGSOFTCAR;
+ unsigned IOCTL_TIOCINQ = TIOCINQ;
+ unsigned IOCTL_TIOCLINUX = TIOCLINUX;
+ unsigned IOCTL_TIOCSERCONFIG = TIOCSERCONFIG;
+ unsigned IOCTL_TIOCSERGETLSR = TIOCSERGETLSR;
+ unsigned IOCTL_TIOCSERGWILD = TIOCSERGWILD;
+ unsigned IOCTL_TIOCSERSWILD = TIOCSERSWILD;
+ unsigned IOCTL_TIOCSLCKTRMIOS = TIOCSLCKTRMIOS;
+ unsigned IOCTL_TIOCSSOFTCAR = TIOCSSOFTCAR;
+ unsigned IOCTL_VT_DISALLOCATE = VT_DISALLOCATE;
+ unsigned IOCTL_VT_GETSTATE = VT_GETSTATE;
+ unsigned IOCTL_VT_RESIZE = VT_RESIZE;
+ unsigned IOCTL_VT_RESIZEX = VT_RESIZEX;
+ unsigned IOCTL_VT_SENDSIG = VT_SENDSIG;
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+ unsigned IOCTL_MTIOCGET = MTIOCGET;
+ unsigned IOCTL_MTIOCTOP = MTIOCTOP;
unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS;
unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK;
unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH;
unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE;
unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME;
- unsigned IOCTL_TCFLSH = TCFLSH;
- unsigned IOCTL_TCGETA = TCGETA;
- unsigned IOCTL_TCGETS = TCGETS;
- unsigned IOCTL_TCSBRK = TCSBRK;
- unsigned IOCTL_TCSBRKP = TCSBRKP;
- unsigned IOCTL_TCSETA = TCSETA;
- unsigned IOCTL_TCSETAF = TCSETAF;
- unsigned IOCTL_TCSETAW = TCSETAW;
- unsigned IOCTL_TCSETS = TCSETS;
- unsigned IOCTL_TCSETSF = TCSETSF;
- unsigned IOCTL_TCSETSW = TCSETSW;
- unsigned IOCTL_TCXONC = TCXONC;
- unsigned IOCTL_TIOCGLCKTRMIOS = TIOCGLCKTRMIOS;
- unsigned IOCTL_TIOCGSOFTCAR = TIOCGSOFTCAR;
- unsigned IOCTL_TIOCINQ = TIOCINQ;
- unsigned IOCTL_TIOCLINUX = TIOCLINUX;
- unsigned IOCTL_TIOCSERCONFIG = TIOCSERCONFIG;
- unsigned IOCTL_TIOCSERGETLSR = TIOCSERGETLSR;
- unsigned IOCTL_TIOCSERGWILD = TIOCSERGWILD;
- unsigned IOCTL_TIOCSERSWILD = TIOCSERSWILD;
- unsigned IOCTL_TIOCSLCKTRMIOS = TIOCSLCKTRMIOS;
- unsigned IOCTL_TIOCSSOFTCAR = TIOCSSOFTCAR;
unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE;
- unsigned IOCTL_VT_DISALLOCATE = VT_DISALLOCATE;
unsigned IOCTL_VT_GETMODE = VT_GETMODE;
- unsigned IOCTL_VT_GETSTATE = VT_GETSTATE;
unsigned IOCTL_VT_OPENQRY = VT_OPENQRY;
unsigned IOCTL_VT_RELDISP = VT_RELDISP;
- unsigned IOCTL_VT_RESIZE = VT_RESIZE;
- unsigned IOCTL_VT_RESIZEX = VT_RESIZEX;
- unsigned IOCTL_VT_SENDSIG = VT_SENDSIG;
unsigned IOCTL_VT_SETMODE = VT_SETMODE;
unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
-#endif
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH;
unsigned IOCTL_CYGETDEFTIMEOUT = CYGETDEFTIMEOUT;
unsigned IOCTL_FS_IOC_SETVERSION = FS_IOC_SETVERSION;
unsigned IOCTL_GIO_CMAP = GIO_CMAP;
unsigned IOCTL_GIO_FONT = GIO_FONT;
- unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP;
unsigned IOCTL_GIO_UNIMAP = GIO_UNIMAP;
unsigned IOCTL_GIO_UNISCRNMAP = GIO_UNISCRNMAP;
unsigned IOCTL_KDADDIO = KDADDIO;
unsigned IOCTL_KDDELIO = KDDELIO;
- unsigned IOCTL_KDDISABIO = KDDISABIO;
- unsigned IOCTL_KDENABIO = KDENABIO;
unsigned IOCTL_KDGETKEYCODE = KDGETKEYCODE;
- unsigned IOCTL_KDGETLED = KDGETLED;
- unsigned IOCTL_KDGETMODE = KDGETMODE;
unsigned IOCTL_KDGKBDIACR = KDGKBDIACR;
unsigned IOCTL_KDGKBENT = KDGKBENT;
unsigned IOCTL_KDGKBLED = KDGKBLED;
unsigned IOCTL_KDGKBMETA = KDGKBMETA;
- unsigned IOCTL_KDGKBMODE = KDGKBMODE;
unsigned IOCTL_KDGKBSENT = KDGKBSENT;
- unsigned IOCTL_KDGKBTYPE = KDGKBTYPE;
unsigned IOCTL_KDMAPDISP = KDMAPDISP;
- unsigned IOCTL_KDMKTONE = KDMKTONE;
unsigned IOCTL_KDSETKEYCODE = KDSETKEYCODE;
- unsigned IOCTL_KDSETLED = KDSETLED;
- unsigned IOCTL_KDSETMODE = KDSETMODE;
unsigned IOCTL_KDSIGACCEPT = KDSIGACCEPT;
unsigned IOCTL_KDSKBDIACR = KDSKBDIACR;
unsigned IOCTL_KDSKBENT = KDSKBENT;
unsigned IOCTL_KDSKBLED = KDSKBLED;
unsigned IOCTL_KDSKBMETA = KDSKBMETA;
- unsigned IOCTL_KDSKBMODE = KDSKBMODE;
unsigned IOCTL_KDSKBSENT = KDSKBSENT;
unsigned IOCTL_KDUNMAPDISP = KDUNMAPDISP;
- unsigned IOCTL_KIOCSOUND = KIOCSOUND;
unsigned IOCTL_LPABORT = LPABORT;
unsigned IOCTL_LPABORTOPEN = LPABORTOPEN;
unsigned IOCTL_LPCAREFUL = LPCAREFUL;
unsigned IOCTL_MTIOCSETCONFIG = MTIOCSETCONFIG;
unsigned IOCTL_PIO_CMAP = PIO_CMAP;
unsigned IOCTL_PIO_FONT = PIO_FONT;
- unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP;
unsigned IOCTL_PIO_UNIMAP = PIO_UNIMAP;
unsigned IOCTL_PIO_UNIMAPCLR = PIO_UNIMAPCLR;
unsigned IOCTL_PIO_UNISCRNMAP = PIO_UNISCRNMAP;
unsigned IOCTL_SIOCNRGETPARMS = SIOCNRGETPARMS;
unsigned IOCTL_SIOCNRRTCTL = SIOCNRRTCTL;
unsigned IOCTL_SIOCNRSETPARMS = SIOCNRSETPARMS;
- unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
- unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE;
unsigned IOCTL_TIOCGSERIAL = TIOCGSERIAL;
unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI;
unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI;
unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL;
-#endif
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+ unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP;
+ unsigned IOCTL_KDDISABIO = KDDISABIO;
+ unsigned IOCTL_KDENABIO = KDENABIO;
+ unsigned IOCTL_KDGETLED = KDGETLED;
+ unsigned IOCTL_KDGETMODE = KDGETMODE;
+ unsigned IOCTL_KDGKBMODE = KDGKBMODE;
+ unsigned IOCTL_KDGKBTYPE = KDGKBTYPE;
+ unsigned IOCTL_KDMKTONE = KDMKTONE;
+ unsigned IOCTL_KDSETLED = KDSETLED;
+ unsigned IOCTL_KDSETMODE = KDSETMODE;
+ unsigned IOCTL_KDSKBMODE = KDSKBMODE;
+ unsigned IOCTL_KIOCSOUND = KIOCSOUND;
+ unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP;
+ unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
+ unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE;
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+
+ const int errno_EINVAL = EINVAL;
// EOWNERDEAD is not present in some older platforms.
#if defined(EOWNERDEAD)
- extern const int errno_EOWNERDEAD = EOWNERDEAD;
+ const int errno_EOWNERDEAD = EOWNERDEAD;
#else
- extern const int errno_EOWNERDEAD = -1;
+ const int errno_EOWNERDEAD = -1;
#endif
-} // namespace __sanitizer
+
+ const int si_SEGV_MAPERR = SEGV_MAPERR;
+ const int si_SEGV_ACCERR = SEGV_ACCERR;
+} // namespace __sanitizer
COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
CHECK_TYPE_SIZE(pthread_key_t);
#if SANITIZER_LINUX
+// FIXME: We define those on Linux and Mac, but only check on Linux.
+COMPILER_CHECK(IOC_NRBITS == _IOC_NRBITS);
+COMPILER_CHECK(IOC_TYPEBITS == _IOC_TYPEBITS);
+COMPILER_CHECK(IOC_SIZEBITS == _IOC_SIZEBITS);
+COMPILER_CHECK(IOC_DIRBITS == _IOC_DIRBITS);
+COMPILER_CHECK(IOC_NRMASK == _IOC_NRMASK);
+COMPILER_CHECK(IOC_TYPEMASK == _IOC_TYPEMASK);
+COMPILER_CHECK(IOC_SIZEMASK == _IOC_SIZEMASK);
+COMPILER_CHECK(IOC_DIRMASK == _IOC_DIRMASK);
+COMPILER_CHECK(IOC_NRSHIFT == _IOC_NRSHIFT);
+COMPILER_CHECK(IOC_TYPESHIFT == _IOC_TYPESHIFT);
+COMPILER_CHECK(IOC_SIZESHIFT == _IOC_SIZESHIFT);
+COMPILER_CHECK(IOC_DIRSHIFT == _IOC_DIRSHIFT);
+COMPILER_CHECK(IOC_NONE == _IOC_NONE);
+COMPILER_CHECK(IOC_WRITE == _IOC_WRITE);
+COMPILER_CHECK(IOC_READ == _IOC_READ);
+COMPILER_CHECK(EVIOC_ABS_MAX == ABS_MAX);
+COMPILER_CHECK(EVIOC_EV_MAX == EV_MAX);
+COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678));
+COMPILER_CHECK(IOC_DIR(0x12345678) == _IOC_DIR(0x12345678));
+COMPILER_CHECK(IOC_NR(0x12345678) == _IOC_NR(0x12345678));
+COMPILER_CHECK(IOC_TYPE(0x12345678) == _IOC_TYPE(0x12345678));
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
// There are more undocumented fields in dl_phdr_info that we are not interested
// in.
COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
-COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678));
-#endif
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
CHECK_TYPE_SIZE(glob_t);
CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
CHECK_SIZE_AND_OFFSET(dirent, d_ino);
#if SANITIZER_MAC
CHECK_SIZE_AND_OFFSET(dirent, d_seekoff);
+#elif SANITIZER_FREEBSD
+// There is no 'd_off' field on FreeBSD.
#else
CHECK_SIZE_AND_OFFSET(dirent, d_off);
#endif
CHECK_TYPE_SIZE(ether_addr);
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
CHECK_TYPE_SIZE(ipc_perm);
+# if SANITIZER_FREEBSD
+CHECK_SIZE_AND_OFFSET(ipc_perm, key);
+CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
+# else
CHECK_SIZE_AND_OFFSET(ipc_perm, __key);
+CHECK_SIZE_AND_OFFSET(ipc_perm, __seq);
+# endif
CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
+#ifndef __GLIBC_PREREQ
+#define __GLIBC_PREREQ(x, y) 0
+#endif
+#if !defined(__aarch64__) || !SANITIZER_LINUX || __GLIBC_PREREQ (2, 21)
+/* On aarch64 glibc 2.20 and earlier provided incorrect mode field. */
CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
-CHECK_SIZE_AND_OFFSET(ipc_perm, __seq);
+#endif
CHECK_TYPE_SIZE(shmid_ds);
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
CHECK_TYPE_SIZE(clock_t);
-#endif // SANITIZER_LINUX || SANITIZER_MAC
+#if SANITIZER_LINUX
+CHECK_TYPE_SIZE(clockid_t);
+#endif
+
+#if !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(ifaddrs);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+// Compare against the union, because we can't reach into the union in a
+// compliant way.
+#ifdef ifa_dstaddr
+#undef ifa_dstaddr
+#endif
+# if SANITIZER_FREEBSD
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
+# else
+COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)nullptr)->ifa_dstaddr) ==
+ sizeof(((ifaddrs *)nullptr)->ifa_ifu));
+COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) ==
+ offsetof(ifaddrs, ifa_ifu));
+# endif // SANITIZER_FREEBSD
+#else
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
+#endif // SANITIZER_LINUX
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
+#endif
+
+#if SANITIZER_LINUX
+COMPILER_CHECK(sizeof(__sanitizer_mallinfo) == sizeof(struct mallinfo));
+#endif
+
+#if !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(timeb);
+CHECK_SIZE_AND_OFFSET(timeb, time);
+CHECK_SIZE_AND_OFFSET(timeb, millitm);
+CHECK_SIZE_AND_OFFSET(timeb, timezone);
+CHECK_SIZE_AND_OFFSET(timeb, dstflag);
+#endif
+
+CHECK_TYPE_SIZE(passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_name);
+CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
+CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
+
+#if !SANITIZER_ANDROID
+CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
+#endif
+
+#if SANITIZER_MAC
+CHECK_SIZE_AND_OFFSET(passwd, pw_change);
+CHECK_SIZE_AND_OFFSET(passwd, pw_expire);
+CHECK_SIZE_AND_OFFSET(passwd, pw_class);
+#endif
+
+
+CHECK_TYPE_SIZE(group);
+CHECK_SIZE_AND_OFFSET(group, gr_name);
+CHECK_SIZE_AND_OFFSET(group, gr_passwd);
+CHECK_SIZE_AND_OFFSET(group, gr_gid);
+CHECK_SIZE_AND_OFFSET(group, gr_mem);
+
+#if HAVE_RPC_XDR_H || HAVE_TIRPC_RPC_XDR_H
+CHECK_TYPE_SIZE(XDR);
+CHECK_SIZE_AND_OFFSET(XDR, x_op);
+CHECK_SIZE_AND_OFFSET(XDR, x_ops);
+CHECK_SIZE_AND_OFFSET(XDR, x_public);
+CHECK_SIZE_AND_OFFSET(XDR, x_private);
+CHECK_SIZE_AND_OFFSET(XDR, x_base);
+CHECK_SIZE_AND_OFFSET(XDR, x_handy);
+COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE);
+COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE);
+COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE);
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+COMPILER_CHECK(sizeof(__sanitizer_FILE) <= sizeof(FILE));
+CHECK_SIZE_AND_OFFSET(FILE, _flags);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_read_ptr);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_read_end);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_read_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_write_ptr);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_write_end);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_write_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_end);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_save_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_backup_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_save_end);
+CHECK_SIZE_AND_OFFSET(FILE, _markers);
+CHECK_SIZE_AND_OFFSET(FILE, _chain);
+CHECK_SIZE_AND_OFFSET(FILE, _fileno);
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+COMPILER_CHECK(sizeof(__sanitizer__obstack_chunk) <= sizeof(_obstack_chunk));
+CHECK_SIZE_AND_OFFSET(_obstack_chunk, limit);
+CHECK_SIZE_AND_OFFSET(_obstack_chunk, prev);
+CHECK_TYPE_SIZE(obstack);
+CHECK_SIZE_AND_OFFSET(obstack, chunk_size);
+CHECK_SIZE_AND_OFFSET(obstack, chunk);
+CHECK_SIZE_AND_OFFSET(obstack, object_base);
+CHECK_SIZE_AND_OFFSET(obstack, next_free);
+
+CHECK_TYPE_SIZE(cookie_io_functions_t);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, read);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, write);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, seek);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close);
+#endif
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+CHECK_TYPE_SIZE(sem_t);
+#endif
+
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
#include "sanitizer_internal_defs.h"
#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD
+// FreeBSD's dlopen() returns a pointer to an Obj_Entry structure that
+// incroporates the map structure.
+# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
+ ((link_map*)((handle) == nullptr ? nullptr : ((char*)(handle) + 544)))
+#else
+# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) ((link_map*)(handle))
+#endif // !SANITIZER_FREEBSD
+
namespace __sanitizer {
extern unsigned struct_utsname_sz;
extern unsigned struct_stat_sz;
-#if !SANITIZER_IOS
+#if !SANITIZER_FREEBSD && !SANITIZER_IOS
extern unsigned struct_stat64_sz;
#endif
extern unsigned struct_rusage_sz;
- extern unsigned struct_passwd_sz;
- extern unsigned struct_group_sz;
extern unsigned siginfo_t_sz;
extern unsigned struct_itimerval_sz;
extern unsigned pthread_t_sz;
extern unsigned pid_t_sz;
extern unsigned timeval_sz;
extern unsigned uid_t_sz;
+ extern unsigned gid_t_sz;
extern unsigned mbstate_t_sz;
extern unsigned struct_timezone_sz;
extern unsigned struct_tms_sz;
extern unsigned struct_itimerspec_sz;
extern unsigned struct_sigevent_sz;
extern unsigned struct_sched_param_sz;
- extern unsigned struct_statfs_sz;
extern unsigned struct_statfs64_sz;
#if !SANITIZER_ANDROID
+ extern unsigned struct_statfs_sz;
+ extern unsigned struct_sockaddr_sz;
extern unsigned ucontext_t_sz;
#endif // !SANITIZER_ANDROID
#if SANITIZER_LINUX
#if defined(__x86_64__)
- const unsigned struct___old_kernel_stat_sz = 32;
const unsigned struct_kernel_stat_sz = 144;
const unsigned struct_kernel_stat64_sz = 0;
#elif defined(__i386__)
- const unsigned struct___old_kernel_stat_sz = 32;
const unsigned struct_kernel_stat_sz = 64;
const unsigned struct_kernel_stat64_sz = 96;
#elif defined(__arm__)
- const unsigned struct___old_kernel_stat_sz = 32;
const unsigned struct_kernel_stat_sz = 64;
const unsigned struct_kernel_stat64_sz = 104;
+#elif defined(__aarch64__)
+ const unsigned struct_kernel_stat_sz = 128;
+ const unsigned struct_kernel_stat64_sz = 104;
#elif defined(__powerpc__) && !defined(__powerpc64__)
- const unsigned struct___old_kernel_stat_sz = 32;
const unsigned struct_kernel_stat_sz = 72;
const unsigned struct_kernel_stat64_sz = 104;
#elif defined(__powerpc64__)
+ const unsigned struct_kernel_stat_sz = 144;
+ const unsigned struct_kernel_stat64_sz = 104;
+#elif defined(__sparc__) && defined(__arch64__)
const unsigned struct___old_kernel_stat_sz = 0;
+ const unsigned struct_kernel_stat_sz = 104;
+ const unsigned struct_kernel_stat64_sz = 144;
+#elif defined(__sparc__) && !defined(__arch64__)
+ const unsigned struct___old_kernel_stat_sz = 0;
+ const unsigned struct_kernel_stat_sz = 64;
+#elif defined(__mips__)
+ #if SANITIZER_WORDSIZE == 64
+ const unsigned struct_kernel_stat_sz = 216;
+ #else
const unsigned struct_kernel_stat_sz = 144;
+ #endif
const unsigned struct_kernel_stat64_sz = 104;
#elif defined(__sparc__) && defined(__arch64__)
const unsigned struct___old_kernel_stat_sz = 0;
// More fields that vary with the kernel version.
};
- extern unsigned struct_utimbuf_sz;
+ extern unsigned struct_epoll_event_sz;
+ extern unsigned struct_sysinfo_sz;
+ extern unsigned __user_cap_header_struct_sz;
+ extern unsigned __user_cap_data_struct_sz;
extern unsigned struct_new_utsname_sz;
extern unsigned struct_old_utsname_sz;
extern unsigned struct_oldold_utsname_sz;
- extern unsigned struct_msqid_ds_sz;
- extern unsigned struct_mq_attr_sz;
- extern unsigned struct_timex_sz;
- extern unsigned struct_ustat_sz;
+
+ const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long);
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if defined(__powerpc64__)
+ const unsigned struct___old_kernel_stat_sz = 0;
+#elif !defined(__sparc__)
+ const unsigned struct___old_kernel_stat_sz = 32;
+#endif
extern unsigned struct_rlimit_sz;
- extern unsigned struct_epoll_event_sz;
- extern unsigned struct_sysinfo_sz;
+ extern unsigned struct_utimbuf_sz;
extern unsigned struct_timespec_sz;
- extern unsigned __user_cap_header_struct_sz;
- extern unsigned __user_cap_data_struct_sz;
- const unsigned old_sigset_t_sz = sizeof(unsigned long);
- const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long);
struct __sanitizer_iocb {
u64 aio_data;
uptr newlen;
unsigned long ___unused[4];
};
-#endif // SANITIZER_LINUX
+
+ const unsigned old_sigset_t_sz = sizeof(unsigned long);
+
+ struct __sanitizer_sem_t {
+#if SANITIZER_ANDROID && defined(_LP64)
+ int data[4];
+#elif SANITIZER_ANDROID && !defined(_LP64)
+ int data;
+#elif SANITIZER_LINUX
+ uptr data[4];
+#elif SANITIZER_FREEBSD
+ u32 data[4];
+#endif
+ };
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if SANITIZER_ANDROID
+ struct __sanitizer_mallinfo {
+ uptr v[10];
+ };
+#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ struct __sanitizer_mallinfo {
+ int v[10];
+ };
+
+ extern unsigned struct_ustat_sz;
extern unsigned struct_rlimit64_sz;
- extern unsigned struct_statvfs_sz;
extern unsigned struct_statvfs64_sz;
struct __sanitizer_ipc_perm {
unsigned __seq;
u64 __unused1;
u64 __unused2;
+#elif defined(__mips__) || defined(__aarch64__)
+ unsigned int mode;
+ unsigned short __seq;
+ unsigned short __pad1;
+ unsigned long __unused1;
+ unsigned long __unused2;
#elif defined(__sparc__)
# if defined(__arch64__)
unsigned mode;
u64 shm_ctime;
#else
uptr shm_atime;
- #ifndef _LP64
+ #if !defined(_LP64) && !defined(__mips__)
uptr __unused1;
#endif
uptr shm_dtime;
- #ifndef _LP64
+ #if !defined(_LP64) && !defined(__mips__)
uptr __unused2;
#endif
uptr shm_ctime;
- #ifndef _LP64
+ #if !defined(_LP64) && !defined(__mips__)
uptr __unused3;
#endif
#endif
#endif
#endif
};
- #endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+#elif SANITIZER_FREEBSD
+ struct __sanitizer_ipc_perm {
+ unsigned int cuid;
+ unsigned int cgid;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned short mode;
+ unsigned short seq;
+ long key;
+ };
+
+ struct __sanitizer_shmid_ds {
+ __sanitizer_ipc_perm shm_perm;
+ unsigned long shm_segsz;
+ unsigned int shm_lpid;
+ unsigned int shm_cpid;
+ int shm_nattch;
+ unsigned long shm_atime;
+ unsigned long shm_dtime;
+ unsigned long shm_ctime;
+ };
+#endif
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+ extern unsigned struct_msqid_ds_sz;
+ extern unsigned struct_mq_attr_sz;
+ extern unsigned struct_timex_sz;
+ extern unsigned struct_statvfs_sz;
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
struct __sanitizer_iovec {
- void *iov_base;
+ void *iov_base;
uptr iov_len;
};
+#if !SANITIZER_ANDROID
+ struct __sanitizer_ifaddrs {
+ struct __sanitizer_ifaddrs *ifa_next;
+ char *ifa_name;
+ unsigned int ifa_flags;
+ void *ifa_addr; // (struct sockaddr *)
+ void *ifa_netmask; // (struct sockaddr *)
+ // This is a union on Linux.
+# ifdef ifa_dstaddr
+# undef ifa_dstaddr
+# endif
+ void *ifa_dstaddr; // (struct sockaddr *)
+ void *ifa_data;
+ };
+#endif // !SANITIZER_ANDROID
+
#if SANITIZER_MAC
typedef unsigned long __sanitizer_pthread_key_t;
#else
typedef unsigned __sanitizer_pthread_key_t;
#endif
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+
+ struct __sanitizer_XDR {
+ int x_op;
+ void *x_ops;
+ uptr x_public;
+ uptr x_private;
+ uptr x_base;
+ unsigned x_handy;
+ };
+
+ const int __sanitizer_XDR_ENCODE = 0;
+ const int __sanitizer_XDR_DECODE = 1;
+ const int __sanitizer_XDR_FREE = 2;
+#endif
+
+ struct __sanitizer_passwd {
+ char *pw_name;
+ char *pw_passwd;
+ int pw_uid;
+ int pw_gid;
+#if SANITIZER_MAC || SANITIZER_FREEBSD
+ long pw_change;
+ char *pw_class;
+#endif
+#if !(SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32))
+ char *pw_gecos;
+#endif
+ char *pw_dir;
+ char *pw_shell;
+#if SANITIZER_MAC || SANITIZER_FREEBSD
+ long pw_expire;
+#endif
+#if SANITIZER_FREEBSD
+ int pw_fields;
+#endif
+ };
+
+ struct __sanitizer_group {
+ char *gr_name;
+ char *gr_passwd;
+ int gr_gid;
+ char **gr_mem;
+ };
+
+#if defined(__x86_64__) && !defined(_LP64)
+ typedef long long __sanitizer_time_t;
+#else
+ typedef long __sanitizer_time_t;
+#endif
+
+ struct __sanitizer_timeb {
+ __sanitizer_time_t time;
+ unsigned short millitm;
+ short timezone;
+ short dstflag;
+ };
+
struct __sanitizer_ether_addr {
u8 octet[6];
};
};
#endif
-#if SANITIZER_ANDROID || SANITIZER_MAC
+#if SANITIZER_MAC || SANITIZER_FREEBSD
struct __sanitizer_msghdr {
void *msg_name;
unsigned msg_namelen;
unsigned short d_reclen;
// more fields that we don't care about
};
+#elif SANITIZER_FREEBSD
+ struct __sanitizer_dirent {
+ unsigned int d_fileno;
+ unsigned short d_reclen;
+ // more fields that we don't care about
+ };
#elif SANITIZER_ANDROID || defined(__x86_64__)
struct __sanitizer_dirent {
unsigned long long d_ino;
};
#endif
-#if defined(__x86_64__) && !defined(_LP64)
+// 'clock_t' is 32 bits wide on x64 FreeBSD
+#if SANITIZER_FREEBSD
+ typedef int __sanitizer_clock_t;
+#elif defined(__x86_64__) && !defined(_LP64)
typedef long long __sanitizer_clock_t;
#else
typedef long __sanitizer_clock_t;
#endif
#if SANITIZER_LINUX
-#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)
+ typedef int __sanitizer_clockid_t;
+#endif
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)\
+ || defined(__mips__)
typedef unsigned __sanitizer___kernel_uid_t;
typedef unsigned __sanitizer___kernel_gid_t;
#else
typedef long __sanitizer___kernel_off_t;
#endif
-#if defined(__powerpc__)
+#if defined(__powerpc__) || defined(__mips__)
typedef unsigned int __sanitizer___kernel_old_uid_t;
typedef unsigned int __sanitizer___kernel_old_gid_t;
#else
// The size is determined by looking at sizeof of real sigset_t on linux.
uptr val[128 / sizeof(uptr)];
};
+#elif SANITIZER_FREEBSD
+ struct __sanitizer_sigset_t {
+ // uint32_t * 4
+ unsigned int __bits[4];
+ };
#endif
+ // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
+#if SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 64)
struct __sanitizer_sigaction {
+ unsigned sa_flags;
union {
- void (*sa_handler)(int sig);
- void (*sa_sigaction)(int sig, void *siginfo, void *uctx);
+ void (*sigaction)(int sig, void *siginfo, void *uctx);
+ void (*handler)(int sig);
};
__sanitizer_sigset_t sa_mask;
+ void (*sa_restorer)();
+ };
+#elif SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32)
+ struct __sanitizer_sigaction {
+ union {
+ void (*sigaction)(int sig, void *siginfo, void *uctx);
+ void (*handler)(int sig);
+ };
+ __sanitizer_sigset_t sa_mask;
+ uptr sa_flags;
+ void (*sa_restorer)();
+ };
+#else // !SANITIZER_ANDROID
+ struct __sanitizer_sigaction {
+#if defined(__mips__) && !SANITIZER_FREEBSD
+ unsigned int sa_flags;
+#endif
+ union {
+ void (*sigaction)(int sig, void *siginfo, void *uctx);
+ void (*handler)(int sig);
+ };
+#if SANITIZER_FREEBSD
+ int sa_flags;
+ __sanitizer_sigset_t sa_mask;
+#else
+ __sanitizer_sigset_t sa_mask;
+#ifndef __mips__
#if defined(__sparc__)
unsigned long sa_flags;
#else
int sa_flags;
#endif
+#endif
+#endif
#if SANITIZER_LINUX
void (*sa_restorer)();
#endif
+#if defined(__mips__) && (SANITIZER_WORDSIZE == 32)
+ int sa_resv[1];
+#endif
};
+#endif // !SANITIZER_ANDROID
+#if SANITIZER_FREEBSD
+ typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
+#elif defined(__mips__)
+ struct __sanitizer_kernel_sigset_t {
+ u8 sig[16];
+ };
+#else
struct __sanitizer_kernel_sigset_t {
u8 sig[8];
};
+#endif
+ // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
struct __sanitizer_kernel_sigaction_t {
union {
- void (*sigaction)(int signo, void *info, void *ctx);
void (*handler)(int signo);
+ void (*sigaction)(int signo, void *info, void *ctx);
};
unsigned long sa_flags;
void (*sa_restorer)(void);
extern int af_inet6;
uptr __sanitizer_in_addr_sz(int af);
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
struct __sanitizer_dl_phdr_info {
uptr dlpi_addr;
const char *dlpi_name;
const void *dlpi_phdr;
short dlpi_phnum;
};
+
+ extern unsigned struct_ElfW_Phdr_sz;
#endif
struct __sanitizer_addrinfo {
int ai_family;
int ai_socktype;
int ai_protocol;
-#if SANITIZER_ANDROID || SANITIZER_MAC
+#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD
unsigned ai_addrlen;
char *ai_canonname;
void *ai_addr;
short revents;
};
-#if SANITIZER_ANDROID || SANITIZER_MAC
+#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD
typedef unsigned __sanitizer_nfds_t;
#else
typedef unsigned long __sanitizer_nfds_t;
#endif
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#if !SANITIZER_ANDROID
+# if SANITIZER_LINUX
struct __sanitizer_glob_t {
uptr gl_pathc;
char **gl_pathv;
int (*gl_lstat)(const char *, void *);
int (*gl_stat)(const char *, void *);
};
+# elif SANITIZER_FREEBSD
+ struct __sanitizer_glob_t {
+ uptr gl_pathc;
+ uptr gl_matchc;
+ uptr gl_offs;
+ int gl_flags;
+ char **gl_pathv;
+ int (*gl_errfunc)(const char*, int);
+ void (*gl_closedir)(void *dirp);
+ struct dirent *(*gl_readdir)(void *dirp);
+ void *(*gl_opendir)(const char*);
+ int (*gl_lstat)(const char*, void* /* struct stat* */);
+ int (*gl_stat)(const char*, void* /* struct stat* */);
+ };
+# endif // SANITIZER_FREEBSD
+# if SANITIZER_LINUX || SANITIZER_FREEBSD
extern int glob_nomatch;
extern int glob_altdirfunc;
-#endif
+# endif
+#endif // !SANITIZER_ANDROID
extern unsigned path_max;
uptr we_wordc;
char **we_wordv;
uptr we_offs;
+#if SANITIZER_FREEBSD
+ char *we_strings;
+ uptr we_nbytes;
+#endif
};
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ struct __sanitizer_FILE {
+ int _flags;
+ char *_IO_read_ptr;
+ char *_IO_read_end;
+ char *_IO_read_base;
+ char *_IO_write_base;
+ char *_IO_write_ptr;
+ char *_IO_write_end;
+ char *_IO_buf_base;
+ char *_IO_buf_end;
+ char *_IO_save_base;
+ char *_IO_backup_base;
+ char *_IO_save_end;
+ void *_markers;
+ __sanitizer_FILE *_chain;
+ int _fileno;
+ };
+# define SANITIZER_HAS_STRUCT_FILE 1
+#else
+ typedef void __sanitizer_FILE;
+# define SANITIZER_HAS_STRUCT_FILE 0
+#endif
+
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined (__x86_64)) // NOLINT
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__))
extern unsigned struct_user_regs_struct_sz;
extern unsigned struct_user_fpregs_struct_sz;
extern unsigned struct_user_fpxregs_struct_sz;
+ extern unsigned struct_user_vfpregs_struct_sz;
extern int ptrace_peektext;
extern int ptrace_peekdata;
extern int ptrace_setfpregs;
extern int ptrace_getfpxregs;
extern int ptrace_setfpxregs;
+ extern int ptrace_getvfpregs;
+ extern int ptrace_setvfpregs;
extern int ptrace_getsiginfo;
extern int ptrace_setsiginfo;
extern int ptrace_getregset;
extern int ptrace_setregset;
+ extern int ptrace_geteventmsg;
#endif
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
extern unsigned struct_shminfo_sz;
extern unsigned struct_shm_info_sz;
extern int shmctl_ipc_stat;
extern int shmctl_shm_stat;
#endif
+ extern int map_fixed;
+
// ioctl arguments
struct __sanitizer_ifconf {
int ifc_len;
};
#endif
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+struct __sanitizer__obstack_chunk {
+ char *limit;
+ struct __sanitizer__obstack_chunk *prev;
+};
+
+struct __sanitizer_obstack {
+ long chunk_size;
+ struct __sanitizer__obstack_chunk *chunk;
+ char *object_base;
+ char *next_free;
+ uptr more_fields[7];
+};
+
+typedef uptr (*__sanitizer_cookie_io_read)(void *cookie, char *buf, uptr size);
+typedef uptr (*__sanitizer_cookie_io_write)(void *cookie, const char *buf,
+ uptr size);
+typedef int (*__sanitizer_cookie_io_seek)(void *cookie, u64 *offset,
+ int whence);
+typedef int (*__sanitizer_cookie_io_close)(void *cookie);
+
+struct __sanitizer_cookie_io_functions_t {
+ __sanitizer_cookie_io_read read;
+ __sanitizer_cookie_io_write write;
+ __sanitizer_cookie_io_seek seek;
+ __sanitizer_cookie_io_close close;
+};
+#endif
+
+#define IOC_NRBITS 8
+#define IOC_TYPEBITS 8
+#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__) || defined(__sparc__)
+#define IOC_SIZEBITS 13
+#define IOC_DIRBITS 3
+#define IOC_NONE 1U
+#define IOC_WRITE 4U
+#define IOC_READ 2U
+#else
+#define IOC_SIZEBITS 14
+#define IOC_DIRBITS 2
+#define IOC_NONE 0U
+#define IOC_WRITE 1U
+#define IOC_READ 2U
+#endif
+#define IOC_NRMASK ((1 << IOC_NRBITS) - 1)
+#define IOC_TYPEMASK ((1 << IOC_TYPEBITS) - 1)
+#define IOC_SIZEMASK ((1 << IOC_SIZEBITS) - 1)
+#if defined(IOC_DIRMASK)
+#undef IOC_DIRMASK
+#endif
+#define IOC_DIRMASK ((1 << IOC_DIRBITS) - 1)
+#define IOC_NRSHIFT 0
+#define IOC_TYPESHIFT (IOC_NRSHIFT + IOC_NRBITS)
+#define IOC_SIZESHIFT (IOC_TYPESHIFT + IOC_TYPEBITS)
+#define IOC_DIRSHIFT (IOC_SIZESHIFT + IOC_SIZEBITS)
+#define EVIOC_EV_MAX 0x1f
+#define EVIOC_ABS_MAX 0x3f
+
+#define IOC_DIR(nr) (((nr) >> IOC_DIRSHIFT) & IOC_DIRMASK)
+#define IOC_TYPE(nr) (((nr) >> IOC_TYPESHIFT) & IOC_TYPEMASK)
+#define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK)
+
#if defined(__sparc__)
// In sparc the 14 bits SIZE field overlaps with the
// least significant bit of DIR, so either IOC_READ or
((((((nr) >> 29) & 0x7) & (4U|2U)) == 0)? \
0 : (((nr) >> 16) & 0x3fff))
#else
-# define IOC_SIZE(nr) (((nr) >> 16) & 0x3fff)
+#define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK)
#endif
- extern unsigned struct_arpreq_sz;
extern unsigned struct_ifreq_sz;
extern unsigned struct_termios_sz;
extern unsigned struct_winsize_sz;
#if SANITIZER_LINUX
+ extern unsigned struct_arpreq_sz;
extern unsigned struct_cdrom_msf_sz;
extern unsigned struct_cdrom_multisession_sz;
extern unsigned struct_cdrom_read_audio_sz;
extern unsigned struct_cdrom_tocentry_sz;
extern unsigned struct_cdrom_tochdr_sz;
extern unsigned struct_cdrom_volctrl_sz;
- extern unsigned struct_copr_buffer_sz;
- extern unsigned struct_copr_debug_buf_sz;
- extern unsigned struct_copr_msg_sz;
extern unsigned struct_ff_effect_sz;
extern unsigned struct_floppy_drive_params_sz;
extern unsigned struct_floppy_drive_struct_sz;
extern unsigned struct_hd_geometry_sz;
extern unsigned struct_input_absinfo_sz;
extern unsigned struct_input_id_sz;
+ extern unsigned struct_mtpos_sz;
+ extern unsigned struct_termio_sz;
+ extern unsigned struct_vt_consize_sz;
+ extern unsigned struct_vt_sizes_sz;
+ extern unsigned struct_vt_stat_sz;
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+ extern unsigned struct_copr_buffer_sz;
+ extern unsigned struct_copr_debug_buf_sz;
+ extern unsigned struct_copr_msg_sz;
extern unsigned struct_midi_info_sz;
extern unsigned struct_mtget_sz;
extern unsigned struct_mtop_sz;
- extern unsigned struct_mtpos_sz;
extern unsigned struct_rtentry_sz;
extern unsigned struct_sbi_instrument_sz;
extern unsigned struct_seq_event_rec_sz;
extern unsigned struct_synth_info_sz;
- extern unsigned struct_termio_sz;
- extern unsigned struct_vt_consize_sz;
extern unsigned struct_vt_mode_sz;
- extern unsigned struct_vt_sizes_sz;
- extern unsigned struct_vt_stat_sz;
-#endif
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
#if SANITIZER_LINUX && !SANITIZER_ANDROID
- extern unsigned struct_audio_buf_info_sz;
extern unsigned struct_ax25_parms_struct_sz;
extern unsigned struct_cyclades_monitor_sz;
extern unsigned struct_input_keymap_entry_sz;
extern unsigned struct_kbsentry_sz;
extern unsigned struct_mtconfiginfo_sz;
extern unsigned struct_nr_parms_struct_sz;
- extern unsigned struct_ppp_stats_sz;
extern unsigned struct_scc_modem_sz;
extern unsigned struct_scc_stat_sz;
extern unsigned struct_serial_multiport_struct_sz;
extern unsigned struct_sockaddr_ax25_sz;
extern unsigned struct_unimapdesc_sz;
extern unsigned struct_unimapinit_sz;
-#endif
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+ extern unsigned struct_audio_buf_info_sz;
+ extern unsigned struct_ppp_stats_sz;
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
#if !SANITIZER_ANDROID && !SANITIZER_MAC
extern unsigned struct_sioc_sg_req_sz;
// A special value to mark ioctls that are not present on the target platform,
// when it can not be determined without including any system headers.
- extern unsigned IOCTL_NOT_PRESENT;
+ extern const unsigned IOCTL_NOT_PRESENT;
extern unsigned IOCTL_FIOASYNC;
extern unsigned IOCTL_FIOCLEX;
extern unsigned IOCTL_TIOCSPGRP;
extern unsigned IOCTL_TIOCSTI;
extern unsigned IOCTL_TIOCSWINSZ;
-#if (SANITIZER_LINUX && !SANITIZER_ANDROID)
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
extern unsigned IOCTL_SIOCGETSGCNT;
extern unsigned IOCTL_SIOCGETVIFCNT;
#endif
extern unsigned IOCTL_HDIO_SET_MULTCOUNT;
extern unsigned IOCTL_HDIO_SET_NOWERR;
extern unsigned IOCTL_HDIO_SET_UNMASKINTR;
- extern unsigned IOCTL_MTIOCGET;
extern unsigned IOCTL_MTIOCPOS;
- extern unsigned IOCTL_MTIOCTOP;
extern unsigned IOCTL_PPPIOCGASYNCMAP;
extern unsigned IOCTL_PPPIOCGDEBUG;
extern unsigned IOCTL_PPPIOCGFLAGS;
extern unsigned IOCTL_PPPIOCSMAXCID;
extern unsigned IOCTL_PPPIOCSMRU;
extern unsigned IOCTL_PPPIOCSXASYNCMAP;
- extern unsigned IOCTL_SIOCADDRT;
extern unsigned IOCTL_SIOCDARP;
- extern unsigned IOCTL_SIOCDELRT;
extern unsigned IOCTL_SIOCDRARP;
extern unsigned IOCTL_SIOCGARP;
extern unsigned IOCTL_SIOCGIFENCAP;
extern unsigned IOCTL_SNDCTL_COPR_SENDMSG;
extern unsigned IOCTL_SNDCTL_COPR_WCODE;
extern unsigned IOCTL_SNDCTL_COPR_WDATA;
+ extern unsigned IOCTL_TCFLSH;
+ extern unsigned IOCTL_TCGETA;
+ extern unsigned IOCTL_TCGETS;
+ extern unsigned IOCTL_TCSBRK;
+ extern unsigned IOCTL_TCSBRKP;
+ extern unsigned IOCTL_TCSETA;
+ extern unsigned IOCTL_TCSETAF;
+ extern unsigned IOCTL_TCSETAW;
+ extern unsigned IOCTL_TCSETS;
+ extern unsigned IOCTL_TCSETSF;
+ extern unsigned IOCTL_TCSETSW;
+ extern unsigned IOCTL_TCXONC;
+ extern unsigned IOCTL_TIOCGLCKTRMIOS;
+ extern unsigned IOCTL_TIOCGSOFTCAR;
+ extern unsigned IOCTL_TIOCINQ;
+ extern unsigned IOCTL_TIOCLINUX;
+ extern unsigned IOCTL_TIOCSERCONFIG;
+ extern unsigned IOCTL_TIOCSERGETLSR;
+ extern unsigned IOCTL_TIOCSERGWILD;
+ extern unsigned IOCTL_TIOCSERSWILD;
+ extern unsigned IOCTL_TIOCSLCKTRMIOS;
+ extern unsigned IOCTL_TIOCSSOFTCAR;
+ extern unsigned IOCTL_VT_DISALLOCATE;
+ extern unsigned IOCTL_VT_GETSTATE;
+ extern unsigned IOCTL_VT_RESIZE;
+ extern unsigned IOCTL_VT_RESIZEX;
+ extern unsigned IOCTL_VT_SENDSIG;
+#endif // SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+ extern unsigned IOCTL_MTIOCGET;
+ extern unsigned IOCTL_MTIOCTOP;
+ extern unsigned IOCTL_SIOCADDRT;
+ extern unsigned IOCTL_SIOCDELRT;
extern unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE;
extern unsigned IOCTL_SNDCTL_DSP_GETFMTS;
extern unsigned IOCTL_SNDCTL_DSP_NONBLOCK;
extern unsigned IOCTL_SOUND_PCM_READ_RATE;
extern unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS;
extern unsigned IOCTL_SOUND_PCM_WRITE_FILTER;
- extern unsigned IOCTL_TCFLSH;
- extern unsigned IOCTL_TCGETA;
- extern unsigned IOCTL_TCGETS;
- extern unsigned IOCTL_TCSBRK;
- extern unsigned IOCTL_TCSBRKP;
- extern unsigned IOCTL_TCSETA;
- extern unsigned IOCTL_TCSETAF;
- extern unsigned IOCTL_TCSETAW;
- extern unsigned IOCTL_TCSETS;
- extern unsigned IOCTL_TCSETSF;
- extern unsigned IOCTL_TCSETSW;
- extern unsigned IOCTL_TCXONC;
- extern unsigned IOCTL_TIOCGLCKTRMIOS;
- extern unsigned IOCTL_TIOCGSOFTCAR;
- extern unsigned IOCTL_TIOCINQ;
- extern unsigned IOCTL_TIOCLINUX;
- extern unsigned IOCTL_TIOCSERCONFIG;
- extern unsigned IOCTL_TIOCSERGETLSR;
- extern unsigned IOCTL_TIOCSERGWILD;
- extern unsigned IOCTL_TIOCSERSWILD;
- extern unsigned IOCTL_TIOCSLCKTRMIOS;
- extern unsigned IOCTL_TIOCSSOFTCAR;
extern unsigned IOCTL_VT_ACTIVATE;
- extern unsigned IOCTL_VT_DISALLOCATE;
extern unsigned IOCTL_VT_GETMODE;
- extern unsigned IOCTL_VT_GETSTATE;
extern unsigned IOCTL_VT_OPENQRY;
extern unsigned IOCTL_VT_RELDISP;
- extern unsigned IOCTL_VT_RESIZE;
- extern unsigned IOCTL_VT_RESIZEX;
- extern unsigned IOCTL_VT_SENDSIG;
extern unsigned IOCTL_VT_SETMODE;
extern unsigned IOCTL_VT_WAITACTIVE;
-#endif
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned IOCTL_CYGETDEFTHRESH;
extern unsigned IOCTL_CYGETDEFTIMEOUT;
extern unsigned IOCTL_FS_IOC_SETVERSION;
extern unsigned IOCTL_GIO_CMAP;
extern unsigned IOCTL_GIO_FONT;
- extern unsigned IOCTL_GIO_SCRNMAP;
extern unsigned IOCTL_GIO_UNIMAP;
extern unsigned IOCTL_GIO_UNISCRNMAP;
extern unsigned IOCTL_KDADDIO;
extern unsigned IOCTL_KDDELIO;
- extern unsigned IOCTL_KDDISABIO;
- extern unsigned IOCTL_KDENABIO;
extern unsigned IOCTL_KDGETKEYCODE;
- extern unsigned IOCTL_KDGETLED;
- extern unsigned IOCTL_KDGETMODE;
extern unsigned IOCTL_KDGKBDIACR;
extern unsigned IOCTL_KDGKBENT;
extern unsigned IOCTL_KDGKBLED;
extern unsigned IOCTL_KDGKBMETA;
- extern unsigned IOCTL_KDGKBMODE;
extern unsigned IOCTL_KDGKBSENT;
- extern unsigned IOCTL_KDGKBTYPE;
extern unsigned IOCTL_KDMAPDISP;
- extern unsigned IOCTL_KDMKTONE;
extern unsigned IOCTL_KDSETKEYCODE;
- extern unsigned IOCTL_KDSETLED;
- extern unsigned IOCTL_KDSETMODE;
extern unsigned IOCTL_KDSIGACCEPT;
extern unsigned IOCTL_KDSKBDIACR;
extern unsigned IOCTL_KDSKBENT;
extern unsigned IOCTL_KDSKBLED;
extern unsigned IOCTL_KDSKBMETA;
- extern unsigned IOCTL_KDSKBMODE;
extern unsigned IOCTL_KDSKBSENT;
extern unsigned IOCTL_KDUNMAPDISP;
- extern unsigned IOCTL_KIOCSOUND;
extern unsigned IOCTL_LPABORT;
extern unsigned IOCTL_LPABORTOPEN;
extern unsigned IOCTL_LPCAREFUL;
extern unsigned IOCTL_MTIOCSETCONFIG;
extern unsigned IOCTL_PIO_CMAP;
extern unsigned IOCTL_PIO_FONT;
- extern unsigned IOCTL_PIO_SCRNMAP;
extern unsigned IOCTL_PIO_UNIMAP;
extern unsigned IOCTL_PIO_UNIMAPCLR;
extern unsigned IOCTL_PIO_UNISCRNMAP;
extern unsigned IOCTL_TIOCSERGETMULTI;
extern unsigned IOCTL_TIOCSERSETMULTI;
extern unsigned IOCTL_TIOCSSERIAL;
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+ extern unsigned IOCTL_GIO_SCRNMAP;
+ extern unsigned IOCTL_KDDISABIO;
+ extern unsigned IOCTL_KDENABIO;
+ extern unsigned IOCTL_KDGETLED;
+ extern unsigned IOCTL_KDGETMODE;
+ extern unsigned IOCTL_KDGKBMODE;
+ extern unsigned IOCTL_KDGKBTYPE;
+ extern unsigned IOCTL_KDMKTONE;
+ extern unsigned IOCTL_KDSETLED;
+ extern unsigned IOCTL_KDSETMODE;
+ extern unsigned IOCTL_KDSKBMODE;
+ extern unsigned IOCTL_KIOCSOUND;
+ extern unsigned IOCTL_PIO_SCRNMAP;
#endif
+ extern const int errno_EINVAL;
extern const int errno_EOWNERDEAD;
+
+ extern const int si_SEGV_MAPERR;
+ extern const int si_SEGV_ACCERR;
} // namespace __sanitizer
#define CHECK_TYPE_SIZE(TYPE) \
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries and implements POSIX-specific functions from
-// sanitizer_libc.h.
+// sanitizer_posix.h.
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX || SANITIZER_MAC
+
+#if SANITIZER_POSIX
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
+#include "sanitizer_posix.h"
#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
+#include <fcntl.h>
+#include <signal.h>
#include <sys/mman.h>
+#if SANITIZER_LINUX
+#include <sys/utsname.h>
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#include <sys/personality.h>
+#endif
+
+#if SANITIZER_FREEBSD
+// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
+// that, it was never implemented. So just define it to zero.
+#undef MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
namespace __sanitizer {
// ------------- sanitizer_common.h
return GetPageSize();
}
+#if SANITIZER_WORDSIZE == 32
+// Take care of unusable kernel area in top gigabyte.
+static uptr GetKernelAreaSize() {
+#if SANITIZER_LINUX && !SANITIZER_X32
+ const uptr gbyte = 1UL << 30;
+
+ // Firstly check if there are writable segments
+ // mapped to top gigabyte (e.g. stack).
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ uptr end, prot;
+ while (proc_maps.Next(/*start*/nullptr, &end,
+ /*offset*/nullptr, /*filename*/nullptr,
+ /*filename_size*/0, &prot)) {
+ if ((end >= 3 * gbyte)
+ && (prot & MemoryMappingLayout::kProtectionWrite) != 0)
+ return 0;
+ }
+
+#if !SANITIZER_ANDROID
+ // Even if nothing is mapped, top Gb may still be accessible
+ // if we are running on 64-bit kernel.
+ // Uname may report misleading results if personality type
+ // is modified (e.g. under schroot) so check this as well.
+ struct utsname uname_info;
+ int pers = personality(0xffffffffUL);
+ if (!(pers & PER_MASK)
+ && uname(&uname_info) == 0
+ && internal_strstr(uname_info.machine, "64"))
+ return 0;
+#endif // SANITIZER_ANDROID
+
+ // Top gigabyte is reserved for kernel.
+ return gbyte;
+#else
+ return 0;
+#endif // SANITIZER_LINUX && !SANITIZER_X32
+}
+#endif // SANITIZER_WORDSIZE == 32
+
uptr GetMaxVirtualAddress() {
#if SANITIZER_WORDSIZE == 64
-# if defined(__powerpc64__)
+# if defined(__powerpc64__) || defined(__aarch64__)
// On PowerPC64 we have two different address space layouts: 44- and 46-bit.
- // We somehow need to figure our which one we are using now and choose
+ // We somehow need to figure out which one we are using now and choose
// one of 0x00000fffffffffffUL and 0x00003fffffffffffUL.
// Note that with 'ulimit -s unlimited' the stack is moved away from the top
// of the address space, so simply checking the stack address is not enough.
- return (1ULL << 44) - 1; // 0x00000fffffffffffUL
+ // This should (does) work for both PowerPC64 Endian modes.
+ // Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit.
+ return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1;
+# elif defined(__mips64)
+ return (1ULL << 40) - 1; // 0x000000ffffffffffUL;
# else
return (1ULL << 47) - 1; // 0x00007fffffffffffUL;
# endif
#else // SANITIZER_WORDSIZE == 32
- // FIXME: We can probably lower this on Android?
- return (1ULL << 32) - 1; // 0xffffffff;
+ uptr res = (1ULL << 32) - 1; // 0xffffffff;
+ if (!common_flags()->full_address_space)
+ res -= GetKernelAreaSize();
+ CHECK_LT(reinterpret_cast<uptr>(&res), res);
+ return res;
#endif // SANITIZER_WORDSIZE
}
void *MmapOrDie(uptr size, const char *mem_type) {
size = RoundUpTo(size, GetPageSizeCached());
- uptr res = internal_mmap(0, size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON, -1, 0);
+ uptr res = internal_mmap(nullptr, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
int reserrno;
- if (internal_iserror(res, &reserrno)) {
- static int recursion_count;
- if (recursion_count) {
- // The Report() and CHECK calls below may call mmap recursively and fail.
- // If we went into recursion, just die.
- RawWrite("ERROR: Failed to mmap\n");
- Die();
- }
- recursion_count++;
- Report("ERROR: %s failed to allocate 0x%zx (%zd) bytes of %s: %d\n",
- SanitizerToolName, size, size, mem_type, reserrno);
- DumpProcessMap();
- CHECK("unable to mmap" && 0);
- }
+ if (internal_iserror(res, &reserrno))
+ ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
+ IncreaseTotalMmap(size);
return (void *)res;
}
SanitizerToolName, size, size, addr);
CHECK("unable to unmap" && 0);
}
+ DecreaseTotalMmap(size);
}
-void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
+void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
uptr PageSize = GetPageSizeCached();
- uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
- RoundUpTo(size, PageSize),
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
- -1, 0);
+ uptr p = internal_mmap(nullptr,
+ RoundUpTo(size, PageSize),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
+ -1, 0);
int reserrno;
if (internal_iserror(p, &reserrno))
- Report("ERROR: "
- "%s failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n",
- SanitizerToolName, size, size, fixed_addr, reserrno);
+ ReportMmapFailureAndDie(size, mem_type, "allocate noreserve", reserrno);
+ IncreaseTotalMmap(size);
return (void *)p;
}
-1, 0);
int reserrno;
if (internal_iserror(p, &reserrno)) {
- Report("ERROR:"
- " %s failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n",
- SanitizerToolName, size, size, fixed_addr, reserrno);
- CHECK("unable to mmap" && 0);
+ char mem_type[30];
+ internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
+ fixed_addr);
+ ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
}
+ IncreaseTotalMmap(size);
return (void *)p;
}
-void *Mprotect(uptr fixed_addr, uptr size) {
- return (void *)internal_mmap((void*)fixed_addr, size,
- PROT_NONE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED |
- MAP_NORESERVE, -1, 0);
+bool MprotectNoAccess(uptr addr, uptr size) {
+ return 0 == internal_mprotect((void*)addr, size, PROT_NONE);
+}
+
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
+ int flags;
+ switch (mode) {
+ case RdOnly: flags = O_RDONLY; break;
+ case WrOnly: flags = O_WRONLY | O_CREAT; break;
+ case RdWr: flags = O_RDWR | O_CREAT; break;
+ }
+ fd_t res = internal_open(filename, flags, 0660);
+ if (internal_iserror(res, errno_p))
+ return kInvalidFd;
+ return res;
+}
+
+void CloseFile(fd_t fd) {
+ internal_close(fd);
+}
+
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+ error_t *error_p) {
+ uptr res = internal_read(fd, buff, buff_size);
+ if (internal_iserror(res, error_p))
+ return false;
+ if (bytes_read)
+ *bytes_read = res;
+ return true;
+}
+
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+ error_t *error_p) {
+ uptr res = internal_write(fd, buff, buff_size);
+ if (internal_iserror(res, error_p))
+ return false;
+ if (bytes_written)
+ *bytes_written = res;
+ return true;
+}
+
+bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
+ uptr res = internal_rename(oldpath, newpath);
+ return !internal_iserror(res, error_p);
}
void *MapFileToMemory(const char *file_name, uptr *buff_size) {
- uptr openrv = OpenFile(file_name, false);
- CHECK(!internal_iserror(openrv));
- fd_t fd = openrv;
+ fd_t fd = OpenFile(file_name, RdOnly);
+ CHECK(fd != kInvalidFd);
uptr fsize = internal_filesize(fd);
CHECK_NE(fsize, (uptr)-1);
CHECK_GT(fsize, 0);
*buff_size = RoundUpTo(fsize, GetPageSizeCached());
- uptr map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);
- return internal_iserror(map) ? 0 : (void *)map;
+ uptr map = internal_mmap(nullptr, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ return internal_iserror(map) ? nullptr : (void *)map;
}
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
+ uptr flags = MAP_SHARED;
+ if (addr) flags |= MAP_FIXED;
+ uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
+ int mmap_errno = 0;
+ if (internal_iserror(p, &mmap_errno)) {
+ Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n",
+ fd, (long long)offset, size, p, mmap_errno);
+ return nullptr;
+ }
+ return (void *)p;
+}
static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
uptr start2, uptr end2) {
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
uptr start, end;
while (proc_maps.Next(&start, &end,
- /*offset*/0, /*filename*/0, /*filename_size*/0,
- /*protection*/0)) {
- if (!IntervalsAreSeparate(start, end, range_start, range_end))
+ /*offset*/nullptr, /*filename*/nullptr,
+ /*filename_size*/0, /*protection*/nullptr)) {
+ if (start == end) continue; // Empty range.
+ CHECK_NE(0, end);
+ if (!IntervalsAreSeparate(start, end - 1, range_start, range_end))
return false;
}
return true;
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
uptr start, end;
const sptr kBufSize = 4095;
- char *filename = (char*)MmapOrDie(kBufSize, __FUNCTION__);
+ char *filename = (char*)MmapOrDie(kBufSize, __func__);
Report("Process memory map follows:\n");
- while (proc_maps.Next(&start, &end, /* file_offset */0,
- filename, kBufSize, /* protection */0)) {
+ while (proc_maps.Next(&start, &end, /* file_offset */nullptr,
+ filename, kBufSize, /* protection */nullptr)) {
Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename);
}
Report("End of process memory map.\n");
return GetEnv("PWD");
}
-char *FindPathToBinary(const char *name) {
- const char *path = GetEnv("PATH");
- if (!path)
- return 0;
- uptr name_len = internal_strlen(name);
- InternalScopedBuffer<char> buffer(kMaxPathLength);
- const char *beg = path;
- while (true) {
- const char *end = internal_strchrnul(beg, ':');
- uptr prefix_len = end - beg;
- if (prefix_len + name_len + 2 <= kMaxPathLength) {
- internal_memcpy(buffer.data(), beg, prefix_len);
- buffer[prefix_len] = '/';
- internal_memcpy(&buffer[prefix_len + 1], name, name_len);
- buffer[prefix_len + 1 + name_len] = '\0';
- if (FileExists(buffer.data()))
- return internal_strdup(buffer.data());
- }
- if (*end == '\0') break;
- beg = end + 1;
- }
- return 0;
+bool IsPathSeparator(const char c) {
+ return c == '/';
}
-void MaybeOpenReportFile() {
- if (!log_to_file) return;
- uptr pid = internal_getpid();
- // If in tracer, use the parent's file.
- if (pid == stoptheworld_tracer_pid)
- pid = stoptheworld_tracer_ppid;
- if (report_fd_pid == pid) return;
- InternalScopedBuffer<char> report_path_full(4096);
- internal_snprintf(report_path_full.data(), report_path_full.size(),
- "%s.%d", report_path_prefix, pid);
- uptr openrv = OpenFile(report_path_full.data(), true);
- if (internal_iserror(openrv)) {
- report_fd = kStderrFd;
- log_to_file = false;
- Report("ERROR: Can't open file: %s\n", report_path_full.data());
- Die();
- }
- if (report_fd != kInvalidFd) {
- // We're in the child. Close the parent's log.
- internal_close(report_fd);
- }
- report_fd = openrv;
- report_fd_pid = pid;
+bool IsAbsolutePath(const char *path) {
+ return path != nullptr && IsPathSeparator(path[0]);
}
-void RawWrite(const char *buffer) {
- static const char *kRawWriteError =
- "RawWrite can't output requested buffer!\n";
- uptr length = (uptr)internal_strlen(buffer);
- MaybeOpenReportFile();
- if (length != internal_write(report_fd, buffer, length)) {
- internal_write(report_fd, kRawWriteError, internal_strlen(kRawWriteError));
+void ReportFile::Write(const char *buffer, uptr length) {
+ SpinMutexLock l(mu);
+ static const char *kWriteError =
+ "ReportFile::Write() can't output requested buffer!\n";
+ ReopenIfNecessary();
+ if (length != internal_write(fd, buffer, length)) {
+ internal_write(fd, kWriteError, internal_strlen(kWriteError));
Die();
}
}
bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
uptr s, e, off, prot;
- InternalScopedString buff(4096);
+ InternalScopedString buff(kMaxPathLength);
MemoryMappingLayout proc_maps(/*cache_enabled*/false);
while (proc_maps.Next(&s, &e, &off, buff.data(), buff.size(), &prot)) {
if ((prot & MemoryMappingLayout::kProtectionExecute) != 0
return false;
}
-} // namespace __sanitizer
+SignalContext SignalContext::Create(void *siginfo, void *context) {
+ uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
+ uptr pc, sp, bp;
+ GetPcSpBp(context, &pc, &sp, &bp);
+ return SignalContext(context, addr, pc, sp, bp);
+}
+
+// This function check is the built VMA matches the runtime one for
+// architectures with multiple VMA size.
+void CheckVMASize() {
+#ifdef __aarch64__
+ static const unsigned kBuiltVMA = SANITIZER_AARCH64_VMA;
+ unsigned maxRuntimeVMA =
+ (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
+ if (kBuiltVMA != maxRuntimeVMA) {
+ Printf("WARNING: %s runtime VMA is not the one built for.\n",
+ SanitizerToolName);
+ Printf("\tBuilt VMA: %u bits\n", kBuiltVMA);
+ Printf("\tRuntime VMA: %u bits\n", maxRuntimeVMA);
+ }
+#endif
+}
+
+} // namespace __sanitizer
-#endif // SANITIZER_LINUX || SANITIZER_MAC
+#endif // SANITIZER_POSIX
--- /dev/null
+//===-- sanitizer_posix.h -------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and declares some useful POSIX-specific functions.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_POSIX_H
+#define SANITIZER_POSIX_H
+
+// ----------- ATTENTION -------------
+// This header should NOT include any other headers from sanitizer runtime.
+#include "sanitizer_internal_defs.h"
+
+#if !SANITIZER_POSIX
+// Make it hard to accidentally use any of functions declared in this file:
+#error This file should only be included on POSIX
+#endif
+
+namespace __sanitizer {
+
+// I/O
+// Don't use directly, use __sanitizer::OpenFile() instead.
+uptr internal_open(const char *filename, int flags);
+uptr internal_open(const char *filename, int flags, u32 mode);
+uptr internal_close(fd_t fd);
+
+uptr internal_read(fd_t fd, void *buf, uptr count);
+uptr internal_write(fd_t fd, const void *buf, uptr count);
+
+// Memory
+uptr internal_mmap(void *addr, uptr length, int prot, int flags,
+ int fd, OFF_T offset);
+uptr internal_munmap(void *addr, uptr length);
+int internal_mprotect(void *addr, uptr length, int prot);
+
+// OS
+uptr internal_filesize(fd_t fd); // -1 on error.
+uptr internal_stat(const char *path, void *buf);
+uptr internal_lstat(const char *path, void *buf);
+uptr internal_fstat(fd_t fd, void *buf);
+uptr internal_dup2(int oldfd, int newfd);
+uptr internal_readlink(const char *path, char *buf, uptr bufsize);
+uptr internal_unlink(const char *path);
+uptr internal_rename(const char *oldpath, const char *newpath);
+uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
+
+uptr internal_ptrace(int request, int pid, void *addr, void *data);
+uptr internal_waitpid(int pid, int *status, int options);
+
+int internal_fork();
+
+// These functions call appropriate pthread_ functions directly, bypassing
+// the interceptor. They are weak and may not be present in some tools.
+SANITIZER_WEAK_ATTRIBUTE
+int real_pthread_create(void *th, void *attr, void *(*callback)(void *),
+ void *param);
+SANITIZER_WEAK_ATTRIBUTE
+int real_pthread_join(void *th, void **ret);
+
+#define DEFINE_REAL_PTHREAD_FUNCTIONS \
+ namespace __sanitizer { \
+ int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \
+ void *param) { \
+ return REAL(pthread_create)(th, attr, callback, param); \
+ } \
+ int real_pthread_join(void *th, void **ret) { \
+ return REAL(pthread_join(th, ret)); \
+ } \
+ } // namespace __sanitizer
+
+int my_pthread_attr_getstack(void *attr, void **addr, uptr *size);
+
+int internal_sigaction(int signum, const void *act, void *oldact);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX_H
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX || SANITIZER_MAC
+#if SANITIZER_POSIX
+
#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
#include <errno.h>
+#include <fcntl.h>
#include <pthread.h>
+#include <signal.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/resource.h>
+#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
+#if SANITIZER_FREEBSD
+// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
+// that, it was never implemented. So just define it to zero.
+#undef MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
namespace __sanitizer {
u32 GetUid() {
madvise((void*)addr, size, MADV_DONTNEED);
}
-void DisableCoreDumper() {
- struct rlimit nocore;
- nocore.rlim_cur = 0;
- nocore.rlim_max = 0;
- setrlimit(RLIMIT_CORE, &nocore);
+void NoHugePagesInRegion(uptr addr, uptr size) {
+#ifdef MADV_NOHUGEPAGE // May not be defined on old systems.
+ madvise((void *)addr, size, MADV_NOHUGEPAGE);
+#endif // MADV_NOHUGEPAGE
}
-bool StackSizeIsUnlimited() {
- struct rlimit rlim;
- CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim));
- return (rlim.rlim_cur == (uptr)-1);
+void DontDumpShadowMemory(uptr addr, uptr length) {
+#ifdef MADV_DONTDUMP
+ madvise((void *)addr, length, MADV_DONTDUMP);
+#endif
}
-void SetStackSizeLimitInBytes(uptr limit) {
- struct rlimit rlim;
- rlim.rlim_cur = limit;
- rlim.rlim_max = limit;
- if (setrlimit(RLIMIT_STACK, &rlim)) {
+static rlim_t getlim(int res) {
+ rlimit rlim;
+ CHECK_EQ(0, getrlimit(res, &rlim));
+ return rlim.rlim_cur;
+}
+
+static void setlim(int res, rlim_t lim) {
+ // The following magic is to prevent clang from replacing it with memset.
+ volatile struct rlimit rlim;
+ rlim.rlim_cur = lim;
+ rlim.rlim_max = lim;
+ if (setrlimit(res, const_cast<struct rlimit *>(&rlim))) {
Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
Die();
}
+}
+
+void DisableCoreDumperIfNecessary() {
+ if (common_flags()->disable_coredump) {
+ setlim(RLIMIT_CORE, 0);
+ }
+}
+
+bool StackSizeIsUnlimited() {
+ rlim_t stack_size = getlim(RLIMIT_STACK);
+ return (stack_size == RLIM_INFINITY);
+}
+
+void SetStackSizeLimitInBytes(uptr limit) {
+ setlim(RLIMIT_STACK, (rlim_t)limit);
CHECK(!StackSizeIsUnlimited());
}
+bool AddressSpaceIsUnlimited() {
+ rlim_t as_size = getlim(RLIMIT_AS);
+ return (as_size == RLIM_INFINITY);
+}
+
+void SetAddressSpaceUnlimited() {
+ setlim(RLIMIT_AS, RLIM_INFINITY);
+ CHECK(AddressSpaceIsUnlimited());
+}
+
void SleepForSeconds(int seconds) {
sleep(seconds);
}
#endif
}
-int internal_isatty(fd_t fd) {
- return isatty(fd);
+bool SupportsColoredOutput(fd_t fd) {
+ return isatty(fd) != 0;
+}
+
+#ifndef SANITIZER_GO
+// TODO(glider): different tools may require different altstack size.
+static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough.
+
+void SetAlternateSignalStack() {
+ stack_t altstack, oldstack;
+ CHECK_EQ(0, sigaltstack(nullptr, &oldstack));
+ // If the alternate stack is already in place, do nothing.
+ // Android always sets an alternate stack, but it's too small for us.
+ if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE)) return;
+ // TODO(glider): the mapped stack should have the MAP_STACK flag in the
+ // future. It is not required by man 2 sigaltstack now (they're using
+ // malloc()).
+ void* base = MmapOrDie(kAltStackSize, __func__);
+ altstack.ss_sp = (char*) base;
+ altstack.ss_flags = 0;
+ altstack.ss_size = kAltStackSize;
+ CHECK_EQ(0, sigaltstack(&altstack, nullptr));
+}
+
+void UnsetAlternateSignalStack() {
+ stack_t altstack, oldstack;
+ altstack.ss_sp = nullptr;
+ altstack.ss_flags = SS_DISABLE;
+ altstack.ss_size = kAltStackSize; // Some sane value required on Darwin.
+ CHECK_EQ(0, sigaltstack(&altstack, &oldstack));
+ UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
+}
+
+typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
+static void MaybeInstallSigaction(int signum,
+ SignalHandlerType handler) {
+ if (!IsDeadlySignal(signum))
+ return;
+ struct sigaction sigact;
+ internal_memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = (sa_sigaction_t)handler;
+ // Do not block the signal from being received in that signal's handler.
+ // Clients are responsible for handling this correctly.
+ sigact.sa_flags = SA_SIGINFO | SA_NODEFER;
+ if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
+ CHECK_EQ(0, internal_sigaction(signum, &sigact, nullptr));
+ VReport(1, "Installed the sigaction for signal %d\n", signum);
+}
+
+void InstallDeadlySignalHandlers(SignalHandlerType handler) {
+ // Set the alternate signal stack for the main thread.
+ // This will cause SetAlternateSignalStack to be called twice, but the stack
+ // will be actually set only once.
+ if (common_flags()->use_sigaltstack) SetAlternateSignalStack();
+ MaybeInstallSigaction(SIGSEGV, handler);
+ MaybeInstallSigaction(SIGBUS, handler);
+ MaybeInstallSigaction(SIGABRT, handler);
+ MaybeInstallSigaction(SIGFPE, handler);
+}
+#endif // SANITIZER_GO
+
+bool IsAccessibleMemoryRange(uptr beg, uptr size) {
+ uptr page_size = GetPageSizeCached();
+ // Checking too large memory ranges is slow.
+ CHECK_LT(size, page_size * 10);
+ int sock_pair[2];
+ if (pipe(sock_pair))
+ return false;
+ uptr bytes_written =
+ internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size);
+ int write_errno;
+ bool result;
+ if (internal_iserror(bytes_written, &write_errno)) {
+ CHECK_EQ(EFAULT, write_errno);
+ result = false;
+ } else {
+ result = (bytes_written == size);
+ }
+ internal_close(sock_pair[0]);
+ internal_close(sock_pair[1]);
+ return result;
+}
+
+void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
+ // Some kinds of sandboxes may forbid filesystem access, so we won't be able
+ // to read the file mappings from /proc/self/maps. Luckily, neither the
+ // process will be able to load additional libraries, so it's fine to use the
+ // cached mappings.
+ MemoryMappingLayout::CacheMemoryMappings();
+ // Same for /proc/self/exe in the symbolizer.
+#if !SANITIZER_GO
+ Symbolizer::GetOrInit()->PrepareForSandboxing();
+ CovPrepareForSandboxing(args);
+#endif
+}
+
+#if SANITIZER_ANDROID || SANITIZER_GO
+int GetNamedMappingFd(const char *name, uptr size) {
+ return -1;
+}
+#else
+int GetNamedMappingFd(const char *name, uptr size) {
+ if (!common_flags()->decorate_proc_maps)
+ return -1;
+ char shmname[200];
+ CHECK(internal_strlen(name) < sizeof(shmname) - 10);
+ internal_snprintf(shmname, sizeof(shmname), "%zu [%s]", internal_getpid(),
+ name);
+ int fd = shm_open(shmname, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
+ CHECK_GE(fd, 0);
+ int res = internal_ftruncate(fd, size);
+ CHECK_EQ(0, res);
+ res = shm_unlink(shmname);
+ CHECK_EQ(0, res);
+ return fd;
+}
+#endif
+
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
+ int fd = name ? GetNamedMappingFd(name, size) : -1;
+ unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
+ if (fd == -1) flags |= MAP_ANON;
+
+ uptr PageSize = GetPageSizeCached();
+ uptr p = internal_mmap((void *)(fixed_addr & ~(PageSize - 1)),
+ RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE,
+ flags, fd, 0);
+ int reserrno;
+ if (internal_iserror(p, &reserrno))
+ Report("ERROR: %s failed to "
+ "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
+ SanitizerToolName, size, size, fixed_addr, reserrno);
+ IncreaseTotalMmap(size);
+ return (void *)p;
+}
+
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
+ int fd = name ? GetNamedMappingFd(name, size) : -1;
+ unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
+ if (fd == -1) flags |= MAP_ANON;
+
+ return (void *)internal_mmap((void *)fixed_addr, size, PROT_NONE, flags, fd,
+ 0);
}
-} // namespace __sanitizer
+// This function is defined elsewhere if we intercepted pthread_attr_getstack.
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE int
+real_pthread_attr_getstack(void *attr, void **addr, size_t *size);
+} // extern "C"
+int my_pthread_attr_getstack(void *attr, void **addr, uptr *size) {
+#if !SANITIZER_GO && !SANITIZER_MAC
+ if (&real_pthread_attr_getstack)
+ return real_pthread_attr_getstack((pthread_attr_t *)attr, addr,
+ (size_t *)size);
#endif
+ return pthread_attr_getstack((pthread_attr_t *)attr, addr, (size_t *)size);
+}
+
+#if !SANITIZER_GO
+void AdjustStackSize(void *attr_) {
+ pthread_attr_t *attr = (pthread_attr_t *)attr_;
+ uptr stackaddr = 0;
+ uptr stacksize = 0;
+ my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize);
+ // GLibC will return (0 - stacksize) as the stack address in the case when
+ // stacksize is set, but stackaddr is not.
+ bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0);
+ // We place a lot of tool data into TLS, account for that.
+ const uptr minstacksize = GetTlsSize() + 128*1024;
+ if (stacksize < minstacksize) {
+ if (!stack_set) {
+ if (stacksize != 0) {
+ VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize,
+ minstacksize);
+ pthread_attr_setstacksize(attr, minstacksize);
+ }
+ } else {
+ Printf("Sanitizer: pre-allocated stack size is insufficient: "
+ "%zu < %zu\n", stacksize, minstacksize);
+ Printf("Sanitizer: pthread_create is likely to fail.\n");
+ }
+ }
+}
+#endif // !SANITIZER_GO
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX
// inside it.
//===----------------------------------------------------------------------===//
-
#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
#include <stdio.h>
#include <stdarg.h>
-#if SANITIZER_WINDOWS && !defined(va_copy)
+#if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 && \
+ !defined(va_copy)
# define va_copy(dst, src) ((dst) = (src))
#endif
minimal_num_length, pad_with_zero, negative);
}
-static int AppendString(char **buff, const char *buff_end, const char *s) {
- if (s == 0)
+static int AppendString(char **buff, const char *buff_end, int precision,
+ const char *s) {
+ if (!s)
s = "<null>";
int result = 0;
for (; *s; s++) {
+ if (precision >= 0 && result >= precision)
+ break;
result += AppendChar(buff, buff_end, *s);
}
return result;
static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
int result = 0;
- result += AppendString(buff, buff_end, "0x");
+ result += AppendString(buff, buff_end, -1, "0x");
result += AppendUnsigned(buff, buff_end, ptr_value, 16,
- (SANITIZER_WORDSIZE == 64) ? 12 : 8, true);
+ SANITIZER_POINTER_FORMAT_LENGTH, true);
return result;
}
int VSNPrintf(char *buff, int buff_length,
const char *format, va_list args) {
static const char *kPrintfFormatsHelp =
- "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %s; %c\n";
+ "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %(\\.\\*)?s; %c\n";
RAW_CHECK(format);
RAW_CHECK(buff_length > 0);
const char *buff_end = &buff[buff_length - 1];
width = width * 10 + *cur++ - '0';
}
}
+ bool have_precision = (cur[0] == '.' && cur[1] == '*');
+ int precision = -1;
+ if (have_precision) {
+ cur += 2;
+ precision = va_arg(args, int);
+ }
bool have_z = (*cur == 'z');
cur += have_z;
bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l');
s64 dval;
u64 uval;
bool have_flags = have_width | have_z | have_ll;
+ // Only %s supports precision for now
+ CHECK(!(precision >= 0 && *cur != 's'));
switch (*cur) {
case 'd': {
dval = have_ll ? va_arg(args, s64)
}
case 's': {
RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
- result += AppendString(&buff, buff_end, va_arg(args, char*));
+ result += AppendString(&buff, buff_end, precision, va_arg(args, char*));
break;
}
case 'c': {
buffer_size = kLen;
}
needed_length = 0;
+ // Check that data fits into the current buffer.
+# define CHECK_NEEDED_LENGTH \
+ if (needed_length >= buffer_size) { \
+ if (!use_mmap) continue; \
+ RAW_CHECK_MSG(needed_length < kLen, \
+ "Buffer in Report is too short!\n"); \
+ }
if (append_pid) {
int pid = internal_getpid();
- needed_length += internal_snprintf(buffer, buffer_size, "==%d==", pid);
- if (needed_length >= buffer_size) {
- // The pid doesn't fit into the current buffer.
- if (!use_mmap)
- continue;
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
+ const char *exe_name = GetProcessName();
+ if (common_flags()->log_exe_name && exe_name) {
+ needed_length += internal_snprintf(buffer, buffer_size,
+ "==%s", exe_name);
+ CHECK_NEEDED_LENGTH
}
+ needed_length += internal_snprintf(buffer + needed_length,
+ buffer_size - needed_length,
+ "==%d==", pid);
+ CHECK_NEEDED_LENGTH
}
needed_length += VSNPrintf(buffer + needed_length,
buffer_size - needed_length, format, args);
- if (needed_length >= buffer_size) {
- // The message doesn't fit into the current buffer.
- if (!use_mmap)
- continue;
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
- }
+ CHECK_NEEDED_LENGTH
// If the message fit into the buffer, print it and exit.
break;
+# undef CHECK_NEEDED_LENGTH
}
RawWrite(buffer);
+ if (common_flags()->log_to_syslog)
+ WriteToSyslog(buffer);
CallPrintfAndReportCallback(buffer);
// If we had mapped any memory, clean up.
if (buffer != local_buffer)
va_end(args2);
}
+FORMAT(1, 2)
void Printf(const char *format, ...) {
va_list args;
va_start(args, format);
}
// Like Printf, but prints the current PID before the output string.
+FORMAT(1, 2)
void Report(const char *format, ...) {
va_list args;
va_start(args, format);
// Returns the number of symbols that should have been written to buffer
// (not including trailing '\0'). Thus, the string is truncated
// iff return value is not less than "length".
+FORMAT(3, 4)
int internal_snprintf(char *buffer, uptr length, const char *format, ...) {
va_list args;
va_start(args, format);
return needed_length;
}
+FORMAT(2, 3)
void InternalScopedString::append(const char *format, ...) {
CHECK_LT(length_, size());
va_list args;
VSNPrintf(data() + length_, size() - length_, format, args);
va_end(args);
length_ += internal_strlen(data() + length_);
+ CHECK_LT(length_, size());
}
-} // namespace __sanitizer
+} // namespace __sanitizer
#ifndef SANITIZER_PROCMAPS_H
#define SANITIZER_PROCMAPS_H
+#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_mutex.h"
namespace __sanitizer {
-#if SANITIZER_WINDOWS
-class MemoryMappingLayout {
- public:
- explicit MemoryMappingLayout(bool cache_enabled) {
- (void)cache_enabled;
- }
- bool GetObjectNameAndOffset(uptr addr, uptr *offset,
- char filename[], uptr filename_size,
- uptr *protection) {
- UNIMPLEMENTED();
- }
-};
-
-#else // SANITIZER_WINDOWS
-#if SANITIZER_LINUX
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
struct ProcSelfMapsBuff {
char *data;
uptr mmaped_size;
uptr len;
};
-#endif // SANITIZER_LINUX
+
+// Reads process memory map in an OS-specific way.
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps);
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
class MemoryMappingLayout {
public:
explicit MemoryMappingLayout(bool cache_enabled);
+ ~MemoryMappingLayout();
bool Next(uptr *start, uptr *end, uptr *offset,
char filename[], uptr filename_size, uptr *protection);
void Reset();
- // Gets the object file name and the offset in that object for a given
- // address 'addr'. Returns true on success.
- bool GetObjectNameAndOffset(uptr addr, uptr *offset,
- char filename[], uptr filename_size,
- uptr *protection);
// In some cases, e.g. when running under a sandbox on Linux, ASan is unable
// to obtain the memory mappings. It should fall back to pre-cached data
// instead of aborting.
static void CacheMemoryMappings();
- ~MemoryMappingLayout();
+
+ // Stores the list of mapped objects into an array.
+ uptr DumpListOfModules(LoadedModule *modules, uptr max_modules,
+ string_predicate_t filter);
// Memory protection masks.
static const uptr kProtectionRead = 1;
private:
void LoadFromCache();
- // Default implementation of GetObjectNameAndOffset.
- // Quite slow, because it iterates through the whole process map for each
- // lookup.
- bool IterateForObjectNameAndOffset(uptr addr, uptr *offset,
- char filename[], uptr filename_size,
- uptr *protection) {
- Reset();
- uptr start, end, file_offset;
- for (int i = 0; Next(&start, &end, &file_offset, filename, filename_size,
- protection);
- i++) {
- if (addr >= start && addr < end) {
- // Don't subtract 'start' for the first entry:
- // * If a binary is compiled w/o -pie, then the first entry in
- // process maps is likely the binary itself (all dynamic libs
- // are mapped higher in address space). For such a binary,
- // instruction offset in binary coincides with the actual
- // instruction address in virtual memory (as code section
- // is mapped to a fixed memory range).
- // * If a binary is compiled with -pie, all the modules are
- // mapped high at address space (in particular, higher than
- // shadow memory of the tool), so the module can't be the
- // first entry.
- *offset = (addr - (i ? start : 0)) + file_offset;
- return true;
- }
- }
- if (filename_size)
- filename[0] = '\0';
- return false;
- }
-
-# if SANITIZER_LINUX
+
+ // FIXME: Hide implementation details for different platforms in
+ // platform-specific files.
+# if SANITIZER_FREEBSD || SANITIZER_LINUX
ProcSelfMapsBuff proc_self_maps_;
- char *current_;
+ const char *current_;
// Static mappings cache.
static ProcSelfMapsBuff cached_proc_self_maps_;
// Returns code range for the specified module.
bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end);
-#endif // SANITIZER_WINDOWS
+bool IsDecimal(char c);
+uptr ParseDecimal(const char **p);
+bool IsHex(char c);
+uptr ParseHex(const char **p);
} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_procmaps_common.cc --------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (common parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+
+#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+// Linker initialized.
+ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
+StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
+
+static int TranslateDigit(char c) {
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+// Parse a number and promote 'p' up to the first non-digit character.
+static uptr ParseNumber(const char **p, int base) {
+ uptr n = 0;
+ int d;
+ CHECK(base >= 2 && base <= 16);
+ while ((d = TranslateDigit(**p)) >= 0 && d < base) {
+ n = n * base + d;
+ (*p)++;
+ }
+ return n;
+}
+
+bool IsDecimal(char c) {
+ int d = TranslateDigit(c);
+ return d >= 0 && d < 10;
+}
+
+uptr ParseDecimal(const char **p) {
+ return ParseNumber(p, 10);
+}
+
+bool IsHex(char c) {
+ int d = TranslateDigit(c);
+ return d >= 0 && d < 16;
+}
+
+uptr ParseHex(const char **p) {
+ return ParseNumber(p, 16);
+}
+
+MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
+ ReadProcMaps(&proc_self_maps_);
+ if (cache_enabled) {
+ if (proc_self_maps_.mmaped_size == 0) {
+ LoadFromCache();
+ CHECK_GT(proc_self_maps_.len, 0);
+ }
+ } else {
+ CHECK_GT(proc_self_maps_.mmaped_size, 0);
+ }
+ Reset();
+ // FIXME: in the future we may want to cache the mappings on demand only.
+ if (cache_enabled)
+ CacheMemoryMappings();
+}
+
+MemoryMappingLayout::~MemoryMappingLayout() {
+ // Only unmap the buffer if it is different from the cached one. Otherwise
+ // it will be unmapped when the cache is refreshed.
+ if (proc_self_maps_.data != cached_proc_self_maps_.data) {
+ UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
+ }
+}
+
+void MemoryMappingLayout::Reset() {
+ current_ = proc_self_maps_.data;
+}
+
+// static
+void MemoryMappingLayout::CacheMemoryMappings() {
+ SpinMutexLock l(&cache_lock_);
+ // Don't invalidate the cache if the mappings are unavailable.
+ ProcSelfMapsBuff old_proc_self_maps;
+ old_proc_self_maps = cached_proc_self_maps_;
+ ReadProcMaps(&cached_proc_self_maps_);
+ if (cached_proc_self_maps_.mmaped_size == 0) {
+ cached_proc_self_maps_ = old_proc_self_maps;
+ } else {
+ if (old_proc_self_maps.mmaped_size) {
+ UnmapOrDie(old_proc_self_maps.data,
+ old_proc_self_maps.mmaped_size);
+ }
+ }
+}
+
+void MemoryMappingLayout::LoadFromCache() {
+ SpinMutexLock l(&cache_lock_);
+ if (cached_proc_self_maps_.data) {
+ proc_self_maps_ = cached_proc_self_maps_;
+ }
+}
+
+uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
+ uptr max_modules,
+ string_predicate_t filter) {
+ Reset();
+ uptr cur_beg, cur_end, cur_offset, prot;
+ InternalScopedString module_name(kMaxPathLength);
+ uptr n_modules = 0;
+ for (uptr i = 0; n_modules < max_modules &&
+ Next(&cur_beg, &cur_end, &cur_offset, module_name.data(),
+ module_name.size(), &prot);
+ i++) {
+ const char *cur_name = module_name.data();
+ if (cur_name[0] == '\0')
+ continue;
+ if (filter && !filter(cur_name))
+ continue;
+ // Don't subtract 'cur_beg' from the first entry:
+ // * If a binary is compiled w/o -pie, then the first entry in
+ // process maps is likely the binary itself (all dynamic libs
+ // are mapped higher in address space). For such a binary,
+ // instruction offset in binary coincides with the actual
+ // instruction address in virtual memory (as code section
+ // is mapped to a fixed memory range).
+ // * If a binary is compiled with -pie, all the modules are
+ // mapped high at address space (in particular, higher than
+ // shadow memory of the tool), so the module can't be the
+ // first entry.
+ uptr base_address = (i ? cur_beg : 0) - cur_offset;
+ LoadedModule *cur_module = &modules[n_modules];
+ cur_module->set(cur_name, base_address);
+ cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
+ n_modules++;
+ }
+ return n_modules;
+}
+
+void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
+ char *smaps = nullptr;
+ uptr smaps_cap = 0;
+ uptr smaps_len = 0;
+ if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len))
+ return;
+ uptr start = 0;
+ bool file = false;
+ const char *pos = smaps;
+ while (pos < smaps + smaps_len) {
+ if (IsHex(pos[0])) {
+ start = ParseHex(&pos);
+ for (; *pos != '/' && *pos > '\n'; pos++) {}
+ file = *pos == '/';
+ } else if (internal_strncmp(pos, "Rss:", 4) == 0) {
+ while (!IsDecimal(*pos)) pos++;
+ uptr rss = ParseDecimal(&pos) * 1024;
+ cb(start, rss, file, stats, stats_size);
+ }
+ while (*pos++ != '\n') {}
+ }
+ UnmapOrDie(smaps, smaps_cap);
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
--- /dev/null
+//===-- sanitizer_procmaps_freebsd.cc -------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (FreeBSD-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD
+#include "sanitizer_common.h"
+#include "sanitizer_freebsd.h"
+#include "sanitizer_procmaps.h"
+
+#include <unistd.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+// Fix 'kinfo_vmentry' definition on FreeBSD prior v9.2 in 32-bit mode.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+# include <osreldate.h>
+# if __FreeBSD_version <= 902001 // v9.2
+# define kinfo_vmentry xkinfo_vmentry
+# endif
+#endif
+
+namespace __sanitizer {
+
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
+ const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
+ size_t Size = 0;
+ int Err = sysctl(Mib, 4, NULL, &Size, NULL, 0);
+ CHECK_EQ(Err, 0);
+ CHECK_GT(Size, 0);
+
+ size_t MmapedSize = Size * 4 / 3;
+ void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
+ Size = MmapedSize;
+ Err = sysctl(Mib, 4, VmMap, &Size, NULL, 0);
+ CHECK_EQ(Err, 0);
+
+ proc_maps->data = (char*)VmMap;
+ proc_maps->mmaped_size = MmapedSize;
+ proc_maps->len = Size;
+}
+
+bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
+ char filename[], uptr filename_size,
+ uptr *protection) {
+ char *last = proc_self_maps_.data + proc_self_maps_.len;
+ if (current_ >= last) return false;
+ uptr dummy;
+ if (!start) start = &dummy;
+ if (!end) end = &dummy;
+ if (!offset) offset = &dummy;
+ if (!protection) protection = &dummy;
+ struct kinfo_vmentry *VmEntry = (struct kinfo_vmentry*)current_;
+
+ *start = (uptr)VmEntry->kve_start;
+ *end = (uptr)VmEntry->kve_end;
+ *offset = (uptr)VmEntry->kve_offset;
+
+ *protection = 0;
+ if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
+ *protection |= kProtectionRead;
+ if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
+ *protection |= kProtectionWrite;
+ if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
+ *protection |= kProtectionExecute;
+
+ if (filename != NULL && filename_size > 0) {
+ internal_snprintf(filename,
+ Min(filename_size, (uptr)PATH_MAX),
+ "%s", VmEntry->kve_path);
+ }
+
+ current_ += VmEntry->kve_structsize;
+
+ return true;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD
--- /dev/null
+//===-- sanitizer_procmaps_linux.cc ---------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (Linux-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_LINUX
+#include "sanitizer_common.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
+ CHECK(ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
+ &proc_maps->mmaped_size, &proc_maps->len));
+}
+
+static bool IsOneOf(char c, char c1, char c2) {
+ return c == c1 || c == c2;
+}
+
+bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
+ char filename[], uptr filename_size,
+ uptr *protection) {
+ char *last = proc_self_maps_.data + proc_self_maps_.len;
+ if (current_ >= last) return false;
+ uptr dummy;
+ if (!start) start = &dummy;
+ if (!end) end = &dummy;
+ if (!offset) offset = &dummy;
+ if (!protection) protection = &dummy;
+ char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
+ if (next_line == 0)
+ next_line = last;
+ // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar
+ *start = ParseHex(¤t_);
+ CHECK_EQ(*current_++, '-');
+ *end = ParseHex(¤t_);
+ CHECK_EQ(*current_++, ' ');
+ CHECK(IsOneOf(*current_, '-', 'r'));
+ *protection = 0;
+ if (*current_++ == 'r')
+ *protection |= kProtectionRead;
+ CHECK(IsOneOf(*current_, '-', 'w'));
+ if (*current_++ == 'w')
+ *protection |= kProtectionWrite;
+ CHECK(IsOneOf(*current_, '-', 'x'));
+ if (*current_++ == 'x')
+ *protection |= kProtectionExecute;
+ CHECK(IsOneOf(*current_, 's', 'p'));
+ if (*current_++ == 's')
+ *protection |= kProtectionShared;
+ CHECK_EQ(*current_++, ' ');
+ *offset = ParseHex(¤t_);
+ CHECK_EQ(*current_++, ' ');
+ ParseHex(¤t_);
+ CHECK_EQ(*current_++, ':');
+ ParseHex(¤t_);
+ CHECK_EQ(*current_++, ' ');
+ while (IsDecimal(*current_))
+ current_++;
+ // Qemu may lack the trailing space.
+ // http://code.google.com/p/address-sanitizer/issues/detail?id=160
+ // CHECK_EQ(*current_++, ' ');
+ // Skip spaces.
+ while (current_ < next_line && *current_ == ' ')
+ current_++;
+ // Fill in the filename.
+ uptr i = 0;
+ while (current_ < next_line) {
+ if (filename && i < filename_size - 1)
+ filename[i++] = *current_;
+ current_++;
+ }
+ if (filename && i < filename_size)
+ filename[i] = 0;
+ current_ = next_line + 1;
+ return true;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_LINUX
--- /dev/null
+//===-- sanitizer_procmaps_mac.cc -----------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (Mac-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+#include <mach-o/dyld.h>
+#include <mach-o/loader.h>
+
+namespace __sanitizer {
+
+MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
+ Reset();
+}
+
+MemoryMappingLayout::~MemoryMappingLayout() {
+}
+
+// More information about Mach-O headers can be found in mach-o/loader.h
+// Each Mach-O image has a header (mach_header or mach_header_64) starting with
+// a magic number, and a list of linker load commands directly following the
+// header.
+// A load command is at least two 32-bit words: the command type and the
+// command size in bytes. We're interested only in segment load commands
+// (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped
+// into the task's address space.
+// The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or
+// segment_command_64 correspond to the memory address, memory size and the
+// file offset of the current memory segment.
+// Because these fields are taken from the images as is, one needs to add
+// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime.
+
+void MemoryMappingLayout::Reset() {
+ // Count down from the top.
+ // TODO(glider): as per man 3 dyld, iterating over the headers with
+ // _dyld_image_count is thread-unsafe. We need to register callbacks for
+ // adding and removing images which will invalidate the MemoryMappingLayout
+ // state.
+ current_image_ = _dyld_image_count();
+ current_load_cmd_count_ = -1;
+ current_load_cmd_addr_ = 0;
+ current_magic_ = 0;
+ current_filetype_ = 0;
+}
+
+// static
+void MemoryMappingLayout::CacheMemoryMappings() {
+ // No-op on Mac for now.
+}
+
+void MemoryMappingLayout::LoadFromCache() {
+ // No-op on Mac for now.
+}
+
+// Next and NextSegmentLoad were inspired by base/sysinfo.cc in
+// Google Perftools, http://code.google.com/p/google-perftools.
+
+// NextSegmentLoad scans the current image for the next segment load command
+// and returns the start and end addresses and file offset of the corresponding
+// segment.
+// Note that the segment addresses are not necessarily sorted.
+template<u32 kLCSegment, typename SegmentCommand>
+bool MemoryMappingLayout::NextSegmentLoad(
+ uptr *start, uptr *end, uptr *offset,
+ char filename[], uptr filename_size, uptr *protection) {
+ const char* lc = current_load_cmd_addr_;
+ current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize;
+ if (((const load_command *)lc)->cmd == kLCSegment) {
+ const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_);
+ const SegmentCommand* sc = (const SegmentCommand *)lc;
+ if (start) *start = sc->vmaddr + dlloff;
+ if (protection) {
+ // Return the initial protection.
+ *protection = sc->initprot;
+ }
+ if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
+ if (offset) {
+ if (current_filetype_ == /*MH_EXECUTE*/ 0x2) {
+ *offset = sc->vmaddr;
+ } else {
+ *offset = sc->fileoff;
+ }
+ }
+ if (filename) {
+ internal_strncpy(filename, _dyld_get_image_name(current_image_),
+ filename_size);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
+ char filename[], uptr filename_size,
+ uptr *protection) {
+ for (; current_image_ >= 0; current_image_--) {
+ const mach_header* hdr = _dyld_get_image_header(current_image_);
+ if (!hdr) continue;
+ if (current_load_cmd_count_ < 0) {
+ // Set up for this image;
+ current_load_cmd_count_ = hdr->ncmds;
+ current_magic_ = hdr->magic;
+ current_filetype_ = hdr->filetype;
+ switch (current_magic_) {
+#ifdef MH_MAGIC_64
+ case MH_MAGIC_64: {
+ current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header_64);
+ break;
+ }
+#endif
+ case MH_MAGIC: {
+ current_load_cmd_addr_ = (char*)hdr + sizeof(mach_header);
+ break;
+ }
+ default: {
+ continue;
+ }
+ }
+ }
+
+ for (; current_load_cmd_count_ >= 0; current_load_cmd_count_--) {
+ switch (current_magic_) {
+ // current_magic_ may be only one of MH_MAGIC, MH_MAGIC_64.
+#ifdef MH_MAGIC_64
+ case MH_MAGIC_64: {
+ if (NextSegmentLoad<LC_SEGMENT_64, struct segment_command_64>(
+ start, end, offset, filename, filename_size, protection))
+ return true;
+ break;
+ }
+#endif
+ case MH_MAGIC: {
+ if (NextSegmentLoad<LC_SEGMENT, struct segment_command>(
+ start, end, offset, filename, filename_size, protection))
+ return true;
+ break;
+ }
+ }
+ }
+ // If we get here, no more load_cmd's in this image talk about
+ // segments. Go on to the next image.
+ }
+ return false;
+}
+
+uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
+ uptr max_modules,
+ string_predicate_t filter) {
+ Reset();
+ uptr cur_beg, cur_end, prot;
+ InternalScopedString module_name(kMaxPathLength);
+ uptr n_modules = 0;
+ for (uptr i = 0; n_modules < max_modules &&
+ Next(&cur_beg, &cur_end, 0, module_name.data(),
+ module_name.size(), &prot);
+ i++) {
+ const char *cur_name = module_name.data();
+ if (cur_name[0] == '\0')
+ continue;
+ if (filter && !filter(cur_name))
+ continue;
+ LoadedModule *cur_module = nullptr;
+ if (n_modules > 0 &&
+ 0 == internal_strcmp(cur_name, modules[n_modules - 1].full_name())) {
+ cur_module = &modules[n_modules - 1];
+ } else {
+ cur_module = &modules[n_modules];
+ cur_module->set(cur_name, cur_beg);
+ n_modules++;
+ }
+ cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
+ }
+ return n_modules;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
}
void Init(uptr size, uptr cache_size) {
- max_size_ = size;
- min_size_ = size / 10 * 9; // 90% of max size.
+ atomic_store(&max_size_, size, memory_order_release);
+ atomic_store(&min_size_, size / 10 * 9,
+ memory_order_release); // 90% of max size.
max_cache_size_ = cache_size;
}
+ uptr GetSize() const { return atomic_load(&max_size_, memory_order_acquire); }
+
void Put(Cache *c, Callback cb, Node *ptr, uptr size) {
c->Enqueue(cb, ptr, size);
if (c->Size() > max_cache_size_)
SpinMutexLock l(&cache_mutex_);
cache_.Transfer(c);
}
- if (cache_.Size() > max_size_ && recycle_mutex_.TryLock())
+ if (cache_.Size() > GetSize() && recycle_mutex_.TryLock())
Recycle(cb);
}
private:
// Read-only data.
char pad0_[kCacheLineSize];
- uptr max_size_;
- uptr min_size_;
+ atomic_uintptr_t max_size_;
+ atomic_uintptr_t min_size_;
uptr max_cache_size_;
char pad1_[kCacheLineSize];
SpinMutex cache_mutex_;
void NOINLINE Recycle(Callback cb) {
Cache tmp;
+ uptr min_size = atomic_load(&min_size_, memory_order_acquire);
{
SpinMutexLock l(&cache_mutex_);
- while (cache_.Size() > min_size_) {
+ while (cache_.Size() > min_size) {
QuarantineBatch *b = cache_.DequeueBatch();
tmp.EnqueueBatch(b);
}
size += sizeof(QuarantineBatch); // Count the batch in Quarantine size.
}
QuarantineBatch *b = list_.back();
+ CHECK(b);
b->batch[b->count++] = ptr;
b->size += size;
SizeAdd(size);
QuarantineBatch *DequeueBatch() {
if (list_.empty())
- return 0;
+ return nullptr;
QuarantineBatch *b = list_.front();
list_.pop_front();
SizeSub(b->size);
NOINLINE QuarantineBatch* AllocBatch(Callback cb) {
QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b));
+ CHECK(b);
b->count = 0;
b->size = 0;
list_.push_back(b);
return b;
}
};
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // #ifndef SANITIZER_QUARANTINE_H
+#endif // SANITIZER_QUARANTINE_H
#ifndef SANITIZER_REPORT_DECORATOR_H
#define SANITIZER_REPORT_DECORATOR_H
+#include "sanitizer_common.h"
+
namespace __sanitizer {
-class AnsiColorDecorator {
+class SanitizerCommonDecorator {
// FIXME: This is not portable. It assumes the special strings are printed to
// stdout, which is not the case on Windows (see SetConsoleTextAttribute()).
public:
- explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { }
+ SanitizerCommonDecorator() : ansi_(ColorizeReports()) {}
const char *Bold() const { return ansi_ ? "\033[1m" : ""; }
+ const char *Default() const { return ansi_ ? "\033[1m\033[0m" : ""; }
+ const char *Warning() { return Red(); }
+ const char *EndWarning() { return Default(); }
+ protected:
const char *Black() const { return ansi_ ? "\033[1m\033[30m" : ""; }
const char *Red() const { return ansi_ ? "\033[1m\033[31m" : ""; }
const char *Green() const { return ansi_ ? "\033[1m\033[32m" : ""; }
const char *Magenta() const { return ansi_ ? "\033[1m\033[35m" : ""; }
const char *Cyan() const { return ansi_ ? "\033[1m\033[36m" : ""; }
const char *White() const { return ansi_ ? "\033[1m\033[37m" : ""; }
- const char *Default() const { return ansi_ ? "\033[1m\033[0m" : ""; }
private:
bool ansi_;
};
+
} // namespace __sanitizer
#endif // SANITIZER_REPORT_DECORATOR_H
//===----------------------------------------------------------------------===//
#include "sanitizer_stackdepot.h"
+
#include "sanitizer_common.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_mutex.h"
-#include "sanitizer_atomic.h"
+#include "sanitizer_stackdepotbase.h"
namespace __sanitizer {
-const int kTabSize = 1024 * 1024; // Hash table size.
-const int kPartBits = 8;
-const int kPartShift = sizeof(u32) * 8 - kPartBits - 1;
-const int kPartCount = 1 << kPartBits; // Number of subparts in the table.
-const int kPartSize = kTabSize / kPartCount;
-const int kMaxId = 1 << kPartShift;
-
-struct StackDesc {
- StackDesc *link;
+struct StackDepotNode {
+ StackDepotNode *link;
u32 id;
- u32 hash;
- uptr size;
+ atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20;
+ u32 size;
+ u32 tag;
uptr stack[1]; // [size]
-};
-static struct {
- StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator.
- atomic_uintptr_t region_pos; // Region allocator for StackDesc's.
- atomic_uintptr_t region_end;
- atomic_uintptr_t tab[kTabSize]; // Hash table of StackDesc's.
- atomic_uint32_t seq[kPartCount]; // Unique id generators.
-} depot;
+ static const u32 kTabSizeLog = 20;
+ // Lower kTabSizeLog bits are equal for all items in one bucket.
+ // We use these bits to store the per-stack use counter.
+ static const u32 kUseCountBits = kTabSizeLog;
+ static const u32 kMaxUseCount = 1 << kUseCountBits;
+ static const u32 kUseCountMask = (1 << kUseCountBits) - 1;
+ static const u32 kHashMask = ~kUseCountMask;
+
+ typedef StackTrace args_type;
+ bool eq(u32 hash, const args_type &args) const {
+ u32 hash_bits =
+ atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask;
+ if ((hash & kHashMask) != hash_bits || args.size != size || args.tag != tag)
+ return false;
+ uptr i = 0;
+ for (; i < size; i++) {
+ if (stack[i] != args.trace[i]) return false;
+ }
+ return true;
+ }
+ static uptr storage_size(const args_type &args) {
+ return sizeof(StackDepotNode) + (args.size - 1) * sizeof(uptr);
+ }
+ static u32 hash(const args_type &args) {
+ // murmur2
+ const u32 m = 0x5bd1e995;
+ const u32 seed = 0x9747b28c;
+ const u32 r = 24;
+ u32 h = seed ^ (args.size * sizeof(uptr));
+ for (uptr i = 0; i < args.size; i++) {
+ u32 k = args.trace[i];
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h *= m;
+ h ^= k;
+ }
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+ return h;
+ }
+ static bool is_valid(const args_type &args) {
+ return args.size > 0 && args.trace;
+ }
+ void store(const args_type &args, u32 hash) {
+ atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed);
+ size = args.size;
+ tag = args.tag;
+ internal_memcpy(stack, args.trace, size * sizeof(uptr));
+ }
+ args_type load() const {
+ return args_type(&stack[0], size, tag);
+ }
+ StackDepotHandle get_handle() { return StackDepotHandle(this); }
-static StackDepotStats stats;
+ typedef StackDepotHandle handle_type;
+};
-StackDepotStats *StackDepotGetStats() {
- return &stats;
-}
+COMPILER_CHECK(StackDepotNode::kMaxUseCount == (u32)kStackDepotMaxUseCount);
-static u32 hash(const uptr *stack, uptr size) {
- // murmur2
- const u32 m = 0x5bd1e995;
- const u32 seed = 0x9747b28c;
- const u32 r = 24;
- u32 h = seed ^ (size * sizeof(uptr));
- for (uptr i = 0; i < size; i++) {
- u32 k = stack[i];
- k *= m;
- k ^= k >> r;
- k *= m;
- h *= m;
- h ^= k;
- }
- h ^= h >> 13;
- h *= m;
- h ^= h >> 15;
- return h;
+u32 StackDepotHandle::id() { return node_->id; }
+int StackDepotHandle::use_count() {
+ return atomic_load(&node_->hash_and_use_count, memory_order_relaxed) &
+ StackDepotNode::kUseCountMask;
}
-
-static StackDesc *tryallocDesc(uptr memsz) {
- // Optimisic lock-free allocation, essentially try to bump the region ptr.
- for (;;) {
- uptr cmp = atomic_load(&depot.region_pos, memory_order_acquire);
- uptr end = atomic_load(&depot.region_end, memory_order_acquire);
- if (cmp == 0 || cmp + memsz > end)
- return 0;
- if (atomic_compare_exchange_weak(
- &depot.region_pos, &cmp, cmp + memsz,
- memory_order_acquire))
- return (StackDesc*)cmp;
- }
+void StackDepotHandle::inc_use_count_unsafe() {
+ u32 prev =
+ atomic_fetch_add(&node_->hash_and_use_count, 1, memory_order_relaxed) &
+ StackDepotNode::kUseCountMask;
+ CHECK_LT(prev + 1, StackDepotNode::kMaxUseCount);
}
-static StackDesc *allocDesc(uptr size) {
- // First, try to allocate optimisitically.
- uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr);
- StackDesc *s = tryallocDesc(memsz);
- if (s)
- return s;
- // If failed, lock, retry and alloc new superblock.
- SpinMutexLock l(&depot.mtx);
- for (;;) {
- s = tryallocDesc(memsz);
- if (s)
- return s;
- atomic_store(&depot.region_pos, 0, memory_order_relaxed);
- uptr allocsz = 64 * 1024;
- if (allocsz < memsz)
- allocsz = memsz;
- uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
- stats.mapped += allocsz;
- atomic_store(&depot.region_end, mem + allocsz, memory_order_release);
- atomic_store(&depot.region_pos, mem, memory_order_release);
- }
+// FIXME(dvyukov): this single reserved bit is used in TSan.
+typedef StackDepotBase<StackDepotNode, 1, StackDepotNode::kTabSizeLog>
+ StackDepot;
+static StackDepot theDepot;
+
+StackDepotStats *StackDepotGetStats() {
+ return theDepot.GetStats();
}
-static u32 find(StackDesc *s, const uptr *stack, uptr size, u32 hash) {
- // Searches linked list s for the stack, returns its id.
- for (; s; s = s->link) {
- if (s->hash == hash && s->size == size) {
- uptr i = 0;
- for (; i < size; i++) {
- if (stack[i] != s->stack[i])
- break;
- }
- if (i == size)
- return s->id;
- }
- }
- return 0;
+u32 StackDepotPut(StackTrace stack) {
+ StackDepotHandle h = theDepot.Put(stack);
+ return h.valid() ? h.id() : 0;
}
-static StackDesc *lock(atomic_uintptr_t *p) {
- // Uses the pointer lsb as mutex.
- for (int i = 0;; i++) {
- uptr cmp = atomic_load(p, memory_order_relaxed);
- if ((cmp & 1) == 0
- && atomic_compare_exchange_weak(p, &cmp, cmp | 1,
- memory_order_acquire))
- return (StackDesc*)cmp;
- if (i < 10)
- proc_yield(10);
- else
- internal_sched_yield();
- }
+StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) {
+ return theDepot.Put(stack);
}
-static void unlock(atomic_uintptr_t *p, StackDesc *s) {
- DCHECK_EQ((uptr)s & 1, 0);
- atomic_store(p, (uptr)s, memory_order_release);
+StackTrace StackDepotGet(u32 id) {
+ return theDepot.Get(id);
}
-u32 StackDepotPut(const uptr *stack, uptr size) {
- if (stack == 0 || size == 0)
- return 0;
- uptr h = hash(stack, size);
- atomic_uintptr_t *p = &depot.tab[h % kTabSize];
- uptr v = atomic_load(p, memory_order_consume);
- StackDesc *s = (StackDesc*)(v & ~1);
- // First, try to find the existing stack.
- u32 id = find(s, stack, size, h);
- if (id)
- return id;
- // If failed, lock, retry and insert new.
- StackDesc *s2 = lock(p);
- if (s2 != s) {
- id = find(s2, stack, size, h);
- if (id) {
- unlock(p, s2);
- return id;
- }
- }
- uptr part = (h % kTabSize) / kPartSize;
- id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1;
- stats.n_uniq_ids++;
- CHECK_LT(id, kMaxId);
- id |= part << kPartShift;
- CHECK_NE(id, 0);
- CHECK_EQ(id & (1u << 31), 0);
- s = allocDesc(size);
- s->id = id;
- s->hash = h;
- s->size = size;
- internal_memcpy(s->stack, stack, size * sizeof(uptr));
- s->link = s2;
- unlock(p, s);
- return id;
+void StackDepotLockAll() {
+ theDepot.LockAll();
}
-const uptr *StackDepotGet(u32 id, uptr *size) {
- if (id == 0)
- return 0;
- CHECK_EQ(id & (1u << 31), 0);
- // High kPartBits contain part id, so we need to scan at most kPartSize lists.
- uptr part = id >> kPartShift;
- for (int i = 0; i != kPartSize; i++) {
- uptr idx = part * kPartSize + i;
- CHECK_LT(idx, kTabSize);
- atomic_uintptr_t *p = &depot.tab[idx];
- uptr v = atomic_load(p, memory_order_consume);
- StackDesc *s = (StackDesc*)(v & ~1);
- for (; s; s = s->link) {
- if (s->id == id) {
- *size = s->size;
- return s->stack;
- }
- }
- }
- *size = 0;
- return 0;
+void StackDepotUnlockAll() {
+ theDepot.UnlockAll();
}
bool StackDepotReverseMap::IdDescPair::IdComparator(
StackDepotReverseMap::StackDepotReverseMap()
: map_(StackDepotGetStats()->n_uniq_ids + 100) {
- for (int idx = 0; idx < kTabSize; idx++) {
- atomic_uintptr_t *p = &depot.tab[idx];
+ for (int idx = 0; idx < StackDepot::kTabSize; idx++) {
+ atomic_uintptr_t *p = &theDepot.tab[idx];
uptr v = atomic_load(p, memory_order_consume);
- StackDesc *s = (StackDesc*)(v & ~1);
+ StackDepotNode *s = (StackDepotNode*)(v & ~1);
for (; s; s = s->link) {
IdDescPair pair = {s->id, s};
map_.push_back(pair);
InternalSort(&map_, map_.size(), IdDescPair::IdComparator);
}
-const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) {
- if (!map_.size()) return 0;
- IdDescPair pair = {id, 0};
+StackTrace StackDepotReverseMap::Get(u32 id) {
+ if (!map_.size())
+ return StackTrace();
+ IdDescPair pair = {id, nullptr};
uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair,
IdDescPair::IdComparator);
- if (idx > map_.size()) {
- *size = 0;
- return 0;
- }
- StackDesc *desc = map_[idx].desc;
- *size = desc->size;
- return desc->stack;
+ if (idx > map_.size())
+ return StackTrace();
+ return map_[idx].desc->load();
}
-} // namespace __sanitizer
+} // namespace __sanitizer
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries.
//===----------------------------------------------------------------------===//
+
#ifndef SANITIZER_STACKDEPOT_H
#define SANITIZER_STACKDEPOT_H
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
+#include "sanitizer_stacktrace.h"
namespace __sanitizer {
// StackDepot efficiently stores huge amounts of stack traces.
-
-// Maps stack trace to an unique id.
-u32 StackDepotPut(const uptr *stack, uptr size);
-// Retrieves a stored stack trace by the id.
-const uptr *StackDepotGet(u32 id, uptr *size);
-
-struct StackDepotStats {
- uptr n_uniq_ids;
- uptr mapped;
+struct StackDepotNode;
+struct StackDepotHandle {
+ StackDepotNode *node_;
+ StackDepotHandle() : node_(nullptr) {}
+ explicit StackDepotHandle(StackDepotNode *node) : node_(node) {}
+ bool valid() { return node_; }
+ u32 id();
+ int use_count();
+ void inc_use_count_unsafe();
};
+const int kStackDepotMaxUseCount = 1U << 20;
+
StackDepotStats *StackDepotGetStats();
+u32 StackDepotPut(StackTrace stack);
+StackDepotHandle StackDepotPut_WithHandle(StackTrace stack);
+// Retrieves a stored stack trace by the id.
+StackTrace StackDepotGet(u32 id);
-struct StackDesc;
+void StackDepotLockAll();
+void StackDepotUnlockAll();
// Instantiating this class creates a snapshot of StackDepot which can be
// efficiently queried with StackDepotGet(). You can use it concurrently with
class StackDepotReverseMap {
public:
StackDepotReverseMap();
- const uptr *Get(u32 id, uptr *size);
+ StackTrace Get(u32 id);
private:
struct IdDescPair {
u32 id;
- StackDesc *desc;
+ StackDepotNode *desc;
static bool IdComparator(const IdDescPair &a, const IdDescPair &b);
};
StackDepotReverseMap(const StackDepotReverseMap&);
void operator=(const StackDepotReverseMap&);
};
-} // namespace __sanitizer
-#endif // SANITIZER_STACKDEPOT_H
+} // namespace __sanitizer
+
+#endif // SANITIZER_STACKDEPOT_H
--- /dev/null
+//===-- sanitizer_stackdepotbase.h ------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of a mapping from arbitrary values to unique 32-bit
+// identifiers.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_STACKDEPOTBASE_H
+#define SANITIZER_STACKDEPOTBASE_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_persistent_allocator.h"
+
+namespace __sanitizer {
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+class StackDepotBase {
+ public:
+ typedef typename Node::args_type args_type;
+ typedef typename Node::handle_type handle_type;
+ // Maps stack trace to an unique id.
+ handle_type Put(args_type args, bool *inserted = nullptr);
+ // Retrieves a stored stack trace by the id.
+ args_type Get(u32 id);
+
+ StackDepotStats *GetStats() { return &stats; }
+
+ void LockAll();
+ void UnlockAll();
+
+ private:
+ static Node *find(Node *s, args_type args, u32 hash);
+ static Node *lock(atomic_uintptr_t *p);
+ static void unlock(atomic_uintptr_t *p, Node *s);
+
+ static const int kTabSize = 1 << kTabSizeLog; // Hash table size.
+ static const int kPartBits = 8;
+ static const int kPartShift = sizeof(u32) * 8 - kPartBits - kReservedBits;
+ static const int kPartCount =
+ 1 << kPartBits; // Number of subparts in the table.
+ static const int kPartSize = kTabSize / kPartCount;
+ static const int kMaxId = 1 << kPartShift;
+
+ atomic_uintptr_t tab[kTabSize]; // Hash table of Node's.
+ atomic_uint32_t seq[kPartCount]; // Unique id generators.
+
+ StackDepotStats stats;
+
+ friend class StackDepotReverseMap;
+};
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+Node *StackDepotBase<Node, kReservedBits, kTabSizeLog>::find(Node *s,
+ args_type args,
+ u32 hash) {
+ // Searches linked list s for the stack, returns its id.
+ for (; s; s = s->link) {
+ if (s->eq(hash, args)) {
+ return s;
+ }
+ }
+ return nullptr;
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+Node *StackDepotBase<Node, kReservedBits, kTabSizeLog>::lock(
+ atomic_uintptr_t *p) {
+ // Uses the pointer lsb as mutex.
+ for (int i = 0;; i++) {
+ uptr cmp = atomic_load(p, memory_order_relaxed);
+ if ((cmp & 1) == 0 &&
+ atomic_compare_exchange_weak(p, &cmp, cmp | 1, memory_order_acquire))
+ return (Node *)cmp;
+ if (i < 10)
+ proc_yield(10);
+ else
+ internal_sched_yield();
+ }
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::unlock(
+ atomic_uintptr_t *p, Node *s) {
+ DCHECK_EQ((uptr)s & 1, 0);
+ atomic_store(p, (uptr)s, memory_order_release);
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::handle_type
+StackDepotBase<Node, kReservedBits, kTabSizeLog>::Put(args_type args,
+ bool *inserted) {
+ if (inserted) *inserted = false;
+ if (!Node::is_valid(args)) return handle_type();
+ uptr h = Node::hash(args);
+ atomic_uintptr_t *p = &tab[h % kTabSize];
+ uptr v = atomic_load(p, memory_order_consume);
+ Node *s = (Node *)(v & ~1);
+ // First, try to find the existing stack.
+ Node *node = find(s, args, h);
+ if (node) return node->get_handle();
+ // If failed, lock, retry and insert new.
+ Node *s2 = lock(p);
+ if (s2 != s) {
+ node = find(s2, args, h);
+ if (node) {
+ unlock(p, s2);
+ return node->get_handle();
+ }
+ }
+ uptr part = (h % kTabSize) / kPartSize;
+ u32 id = atomic_fetch_add(&seq[part], 1, memory_order_relaxed) + 1;
+ stats.n_uniq_ids++;
+ CHECK_LT(id, kMaxId);
+ id |= part << kPartShift;
+ CHECK_NE(id, 0);
+ CHECK_EQ(id & (((u32)-1) >> kReservedBits), id);
+ uptr memsz = Node::storage_size(args);
+ s = (Node *)PersistentAlloc(memsz);
+ stats.allocated += memsz;
+ s->id = id;
+ s->store(args, h);
+ s->link = s2;
+ unlock(p, s);
+ if (inserted) *inserted = true;
+ return s->get_handle();
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::args_type
+StackDepotBase<Node, kReservedBits, kTabSizeLog>::Get(u32 id) {
+ if (id == 0) {
+ return args_type();
+ }
+ CHECK_EQ(id & (((u32)-1) >> kReservedBits), id);
+ // High kPartBits contain part id, so we need to scan at most kPartSize lists.
+ uptr part = id >> kPartShift;
+ for (int i = 0; i != kPartSize; i++) {
+ uptr idx = part * kPartSize + i;
+ CHECK_LT(idx, kTabSize);
+ atomic_uintptr_t *p = &tab[idx];
+ uptr v = atomic_load(p, memory_order_consume);
+ Node *s = (Node *)(v & ~1);
+ for (; s; s = s->link) {
+ if (s->id == id) {
+ return s->load();
+ }
+ }
+ }
+ return args_type();
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockAll() {
+ for (int i = 0; i < kTabSize; ++i) {
+ lock(&tab[i]);
+ }
+}
+
+template <class Node, int kReservedBits, int kTabSizeLog>
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() {
+ for (int i = 0; i < kTabSize; ++i) {
+ atomic_uintptr_t *p = &tab[i];
+ uptr s = atomic_load(p, memory_order_relaxed);
+ unlock(p, (Node *)(s & ~1UL));
+ }
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_STACKDEPOTBASE_H
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
-#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
-#include "sanitizer_symbolizer.h"
namespace __sanitizer {
-uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
-#ifdef __arm__
- // Cancel Thumb bit.
- pc = pc & (~1);
-#endif
-#if defined(__powerpc__) || defined(__powerpc64__)
- // PCs are always 4 byte aligned.
- return pc - 4;
-#elif defined(__sparc__)
- return pc - 8;
+uptr StackTrace::GetNextInstructionPc(uptr pc) {
+#if defined(__mips__)
+ return pc + 8;
+#elif defined(__powerpc__)
+ return pc + 4;
#else
- return pc - 1;
+ return pc + 1;
#endif
}
-static void PrintStackFramePrefix(InternalScopedString *buffer, uptr frame_num,
- uptr pc) {
- buffer->append(" #%zu 0x%zx", frame_num, pc);
+uptr StackTrace::GetCurrentPc() {
+ return GET_CALLER_PC();
}
-void StackTrace::PrintStack(const uptr *addr, uptr size,
- SymbolizeCallback symbolize_callback) {
- if (addr == 0 || size == 0) {
- Printf(" <empty stack>\n\n");
- return;
- }
- MemoryMappingLayout proc_maps(/*cache_enabled*/true);
- InternalScopedBuffer<char> buff(GetPageSizeCached() * 2);
- InternalScopedBuffer<AddressInfo> addr_frames(64);
- InternalScopedString frame_desc(GetPageSizeCached() * 2);
- uptr frame_num = 0;
- for (uptr i = 0; i < size && addr[i]; i++) {
- // PCs in stack traces are actually the return addresses, that is,
- // addresses of the next instructions after the call.
- uptr pc = GetPreviousInstructionPc(addr[i]);
- uptr addr_frames_num = 0; // The number of stack frames for current
- // instruction address.
- if (symbolize_callback) {
- if (symbolize_callback((void*)pc, buff.data(), buff.size())) {
- addr_frames_num = 1;
- frame_desc.clear();
- PrintStackFramePrefix(&frame_desc, frame_num, pc);
- // We can't know anything about the string returned by external
- // symbolizer, but if it starts with filename, try to strip path prefix
- // from it.
- frame_desc.append(
- " %s",
- StripPathPrefix(buff.data(), common_flags()->strip_path_prefix));
- Printf("%s\n", frame_desc.data());
- frame_num++;
- }
- }
- if (common_flags()->symbolize && addr_frames_num == 0) {
- // Use our own (online) symbolizer, if necessary.
- if (Symbolizer *sym = Symbolizer::GetOrNull())
- addr_frames_num =
- sym->SymbolizeCode(pc, addr_frames.data(), addr_frames.size());
- for (uptr j = 0; j < addr_frames_num; j++) {
- AddressInfo &info = addr_frames[j];
- frame_desc.clear();
- PrintStackFramePrefix(&frame_desc, frame_num, pc);
- if (info.function) {
- frame_desc.append(" in %s", info.function);
- }
- if (info.file) {
- frame_desc.append(" ");
- PrintSourceLocation(&frame_desc, info.file, info.line, info.column);
- } else if (info.module) {
- frame_desc.append(" ");
- PrintModuleAndOffset(&frame_desc, info.module, info.module_offset);
- }
- Printf("%s\n", frame_desc.data());
- frame_num++;
- info.Clear();
- }
- }
- if (addr_frames_num == 0) {
- // If online symbolization failed, try to output at least module and
- // offset for instruction.
- frame_desc.clear();
- PrintStackFramePrefix(&frame_desc, frame_num, pc);
- uptr offset;
- if (proc_maps.GetObjectNameAndOffset(pc, &offset,
- buff.data(), buff.size(),
- /* protection */0)) {
- frame_desc.append(" ");
- PrintModuleAndOffset(&frame_desc, buff.data(), offset);
- }
- Printf("%s\n", frame_desc.data());
- frame_num++;
- }
- }
- // Always print a trailing empty line after stack trace.
- Printf("\n");
+void BufferedStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
+ size = cnt + !!extra_top_pc;
+ CHECK_LE(size, kStackTraceMax);
+ internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
+ if (extra_top_pc)
+ trace_buffer[cnt] = extra_top_pc;
+ top_frame_bp = 0;
}
-uptr StackTrace::GetCurrentPc() {
- return GET_CALLER_PC();
+// Check if given pointer points into allocated stack area.
+static inline bool IsValidFrame(uptr frame, uptr stack_top, uptr stack_bottom) {
+ return frame > stack_bottom && frame < stack_top - 2 * sizeof (uhwptr);
}
-void StackTrace::FastUnwindStack(uptr pc, uptr bp,
- uptr stack_top, uptr stack_bottom,
- uptr max_depth) {
- if (max_depth == 0) {
- size = 0;
- return;
- }
- trace[0] = pc;
+// In GCC on ARM bp points to saved lr, not fp, so we should check the next
+// cell in stack to be a saved frame pointer. GetCanonicFrame returns the
+// pointer to saved frame pointer in any case.
+static inline uhwptr *GetCanonicFrame(uptr bp,
+ uptr stack_top,
+ uptr stack_bottom) {
+#ifdef __arm__
+ if (!IsValidFrame(bp, stack_top, stack_bottom)) return 0;
+ uhwptr *bp_prev = (uhwptr *)bp;
+ if (IsValidFrame((uptr)bp_prev[0], stack_top, stack_bottom)) return bp_prev;
+ // The next frame pointer does not look right. This could be a GCC frame, step
+ // back by 1 word and try again.
+ if (IsValidFrame((uptr)bp_prev[-1], stack_top, stack_bottom))
+ return bp_prev - 1;
+ // Nope, this does not look right either. This means the frame after next does
+ // not have a valid frame pointer, but we can still extract the caller PC.
+ // Unfortunately, there is no way to decide between GCC and LLVM frame
+ // layouts. Assume GCC.
+ return bp_prev - 1;
+#else
+ return (uhwptr*)bp;
+#endif
+}
+
+void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top,
+ uptr stack_bottom, u32 max_depth) {
+ CHECK_GE(max_depth, 2);
+ trace_buffer[0] = pc;
size = 1;
- uhwptr *frame = (uhwptr *)bp;
- uhwptr *prev_frame = frame - 1;
if (stack_top < 4096) return; // Sanity check for stack top.
+ uhwptr *frame = GetCanonicFrame(bp, stack_top, stack_bottom);
+ // Lowest possible address that makes sense as the next frame pointer.
+ // Goes up as we walk the stack.
+ uptr bottom = stack_bottom;
// Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
- while (frame > prev_frame &&
- frame < (uhwptr *)stack_top - 2 &&
- frame > (uhwptr *)stack_bottom &&
+ while (IsValidFrame((uptr)frame, stack_top, bottom) &&
IsAligned((uptr)frame, sizeof(*frame)) &&
size < max_depth) {
+#ifdef __powerpc__
+ // PowerPC ABIs specify that the return address is saved at offset
+ // 16 of the *caller's* stack frame. Thus we must dereference the
+ // back chain to find the caller frame before extracting it.
+ uhwptr *caller_frame = (uhwptr*)frame[0];
+ if (!IsValidFrame((uptr)caller_frame, stack_top, bottom) ||
+ !IsAligned((uptr)caller_frame, sizeof(uhwptr)))
+ break;
+ uhwptr pc1 = caller_frame[2];
+#else
uhwptr pc1 = frame[1];
+#endif
if (pc1 != pc) {
- trace[size++] = (uptr) pc1;
+ trace_buffer[size++] = (uptr) pc1;
}
- prev_frame = frame;
- frame = (uhwptr *)frame[0];
- }
-}
-
-void StackTrace::PopStackFrames(uptr count) {
- CHECK(size >= count);
- size -= count;
- for (uptr i = 0; i < size; i++) {
- trace[i] = trace[i + count];
+ bottom = (uptr)frame;
+ frame = GetCanonicFrame((uptr)frame[0], stack_top, bottom);
}
}
return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold;
}
-uptr StackTrace::LocatePcInTrace(uptr pc) {
+void BufferedStackTrace::PopStackFrames(uptr count) {
+ CHECK_LT(count, size);
+ size -= count;
+ for (uptr i = 0; i < size; ++i) {
+ trace_buffer[i] = trace_buffer[i + count];
+ }
+}
+
+uptr BufferedStackTrace::LocatePcInTrace(uptr pc) {
// Use threshold to find PC in stack trace, as PC we want to unwind from may
// slightly differ from return address in the actual unwinded stack trace.
- const int kPcThreshold = 192;
+ const int kPcThreshold = 304;
for (uptr i = 0; i < size; ++i) {
if (MatchPc(pc, trace[i], kPcThreshold))
return i;
namespace __sanitizer {
-static const uptr kStackTraceMax = 256;
+static const u32 kStackTraceMax = 256;
-#if SANITIZER_LINUX && (defined(__arm__) || \
- defined(__powerpc__) || defined(__powerpc64__) || \
- defined(__sparc__) || \
- defined(__mips__))
+#if SANITIZER_LINUX && (defined(__sparc__) || defined(__mips__))
# define SANITIZER_CAN_FAST_UNWIND 0
#elif SANITIZER_WINDOWS
# define SANITIZER_CAN_FAST_UNWIND 0
# define SANITIZER_CAN_FAST_UNWIND 1
#endif
+// Fast unwind is the only option on Mac for now; we will need to
+// revisit this macro when slow unwind works on Mac, see
+// https://code.google.com/p/address-sanitizer/issues/detail?id=137
+#if SANITIZER_MAC
+# define SANITIZER_CAN_SLOW_UNWIND 0
+#else
+# define SANITIZER_CAN_SLOW_UNWIND 1
+#endif
+
struct StackTrace {
- typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
- int out_size);
- uptr top_frame_bp;
- uptr size;
- uptr trace[kStackTraceMax];
+ const uptr *trace;
+ u32 size;
+ u32 tag;
+
+ static const int TAG_UNKNOWN = 0;
+ static const int TAG_ALLOC = 1;
+ static const int TAG_DEALLOC = 2;
+ static const int TAG_CUSTOM = 100; // Tool specific tags start here.
+
+ StackTrace() : trace(nullptr), size(0), tag(0) {}
+ StackTrace(const uptr *trace, u32 size) : trace(trace), size(size), tag(0) {}
+ StackTrace(const uptr *trace, u32 size, u32 tag)
+ : trace(trace), size(size), tag(tag) {}
// Prints a symbolized stacktrace, followed by an empty line.
- static void PrintStack(const uptr *addr, uptr size,
- SymbolizeCallback symbolize_callback = 0);
-
- void CopyFrom(const uptr *src, uptr src_size) {
- top_frame_bp = 0;
- size = src_size;
- if (size > kStackTraceMax) size = kStackTraceMax;
- for (uptr i = 0; i < size; i++)
- trace[i] = src[i];
- }
+ void Print() const;
static bool WillUseFastUnwind(bool request_fast_unwind) {
- // Check if fast unwind is available. Fast unwind is the only option on Mac.
if (!SANITIZER_CAN_FAST_UNWIND)
return false;
- else if (SANITIZER_MAC)
+ else if (!SANITIZER_CAN_SLOW_UNWIND)
return true;
return request_fast_unwind;
}
- void Unwind(uptr max_depth, uptr pc, uptr bp, uptr stack_top,
- uptr stack_bottom, bool request_fast_unwind);
-
static uptr GetCurrentPc();
- static uptr GetPreviousInstructionPc(uptr pc);
+ static inline uptr GetPreviousInstructionPc(uptr pc);
+ static uptr GetNextInstructionPc(uptr pc);
+ typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
+ int out_size);
+};
+
+// Performance-critical, must be in the header.
+ALWAYS_INLINE
+uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
+#if defined(__arm__)
+ // Cancel Thumb bit.
+ pc = pc & (~1);
+#endif
+#if defined(__powerpc__) || defined(__powerpc64__)
+ // PCs are always 4 byte aligned.
+ return pc - 4;
+#elif defined(__sparc__) || defined(__mips__)
+ return pc - 8;
+#else
+ return pc - 1;
+#endif
+}
+
+// StackTrace that owns the buffer used to store the addresses.
+struct BufferedStackTrace : public StackTrace {
+ uptr trace_buffer[kStackTraceMax];
+ uptr top_frame_bp; // Optional bp of a top frame.
+
+ BufferedStackTrace() : StackTrace(trace_buffer, 0), top_frame_bp(0) {}
+
+ void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
+ void Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
+ uptr stack_bottom, bool request_fast_unwind);
private:
void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom,
- uptr max_depth);
- void SlowUnwindStack(uptr pc, uptr max_depth);
+ u32 max_depth);
+ void SlowUnwindStack(uptr pc, u32 max_depth);
+ void SlowUnwindStackWithContext(uptr pc, void *context,
+ u32 max_depth);
void PopStackFrames(uptr count);
uptr LocatePcInTrace(uptr pc);
+
+ BufferedStackTrace(const BufferedStackTrace &);
+ void operator=(const BufferedStackTrace &);
};
} // namespace __sanitizer
uptr local_stack; \
uptr sp = (uptr)&local_stack
+#define GET_CALLER_PC_BP \
+ uptr bp = GET_CURRENT_FRAME(); \
+ uptr pc = GET_CALLER_PC();
+
// Use this macro if you want to print stack trace with the current
// function in the top frame.
#define GET_CURRENT_PC_BP_SP \
// run-time libraries.
//===----------------------------------------------------------------------===//
+#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
#include "sanitizer_stacktrace.h"
+#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_symbolizer.h"
namespace __sanitizer {
-void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, uptr stack_top,
- uptr stack_bottom, bool request_fast_unwind) {
- if (!WillUseFastUnwind(request_fast_unwind))
- SlowUnwindStack(pc, max_depth);
- else
- FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth);
+void StackTrace::Print() const {
+ if (trace == nullptr || size == 0) {
+ Printf(" <empty stack>\n\n");
+ return;
+ }
+ InternalScopedString frame_desc(GetPageSizeCached() * 2);
+ uptr frame_num = 0;
+ for (uptr i = 0; i < size && trace[i]; i++) {
+ // PCs in stack traces are actually the return addresses, that is,
+ // addresses of the next instructions after the call.
+ uptr pc = GetPreviousInstructionPc(trace[i]);
+ SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc);
+ CHECK(frames);
+ for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ frame_desc.clear();
+ RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
+ cur->info, common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ Printf("%s\n", frame_desc.data());
+ }
+ frames->ClearAll();
+ }
+ // Always print a trailing empty line after stack trace.
+ Printf("\n");
+}
- top_frame_bp = size ? bp : 0;
+void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
+ uptr stack_top, uptr stack_bottom,
+ bool request_fast_unwind) {
+ top_frame_bp = (max_depth > 0) ? bp : 0;
+ // Avoid doing any work for small max_depth.
+ if (max_depth == 0) {
+ size = 0;
+ return;
+ }
+ if (max_depth == 1) {
+ size = 1;
+ trace_buffer[0] = pc;
+ return;
+ }
+ if (!WillUseFastUnwind(request_fast_unwind)) {
+#if SANITIZER_CAN_SLOW_UNWIND
+ if (context)
+ SlowUnwindStackWithContext(pc, context, max_depth);
+ else
+ SlowUnwindStack(pc, max_depth);
+#else
+ UNREACHABLE("slow unwind requested but not available");
+#endif
+ } else {
+ FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth);
+ }
}
} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_common.cc -----------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizers' run-time libraries.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_stacktrace_printer.h"
+
+namespace __sanitizer {
+
+static const char *StripFunctionName(const char *function, const char *prefix) {
+ if (!function) return nullptr;
+ if (!prefix) return function;
+ uptr prefix_len = internal_strlen(prefix);
+ if (0 == internal_strncmp(function, prefix, prefix_len))
+ return function + prefix_len;
+ return function;
+}
+
+static const char kDefaultFormat[] = " #%n %p %F %L";
+
+void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
+ const AddressInfo &info, bool vs_style,
+ const char *strip_path_prefix, const char *strip_func_prefix) {
+ if (0 == internal_strcmp(format, "DEFAULT"))
+ format = kDefaultFormat;
+ for (const char *p = format; *p != '\0'; p++) {
+ if (*p != '%') {
+ buffer->append("%c", *p);
+ continue;
+ }
+ p++;
+ switch (*p) {
+ case '%':
+ buffer->append("%%");
+ break;
+ // Frame number and all fields of AddressInfo structure.
+ case 'n':
+ buffer->append("%zu", frame_no);
+ break;
+ case 'p':
+ buffer->append("0x%zx", info.address);
+ break;
+ case 'm':
+ buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix));
+ break;
+ case 'o':
+ buffer->append("0x%zx", info.module_offset);
+ break;
+ case 'f':
+ buffer->append("%s", StripFunctionName(info.function, strip_func_prefix));
+ break;
+ case 'q':
+ buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown
+ ? info.function_offset
+ : 0x0);
+ break;
+ case 's':
+ buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix));
+ break;
+ case 'l':
+ buffer->append("%d", info.line);
+ break;
+ case 'c':
+ buffer->append("%d", info.column);
+ break;
+ // Smarter special cases.
+ case 'F':
+ // Function name and offset, if file is unknown.
+ if (info.function) {
+ buffer->append("in %s",
+ StripFunctionName(info.function, strip_func_prefix));
+ if (!info.file && info.function_offset != AddressInfo::kUnknown)
+ buffer->append("+0x%zx", info.function_offset);
+ }
+ break;
+ case 'S':
+ // File/line information.
+ RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style,
+ strip_path_prefix);
+ break;
+ case 'L':
+ // Source location, or module location.
+ if (info.file) {
+ RenderSourceLocation(buffer, info.file, info.line, info.column,
+ vs_style, strip_path_prefix);
+ } else if (info.module) {
+ RenderModuleLocation(buffer, info.module, info.module_offset,
+ strip_path_prefix);
+ } else {
+ buffer->append("(<unknown module>)");
+ }
+ break;
+ case 'M':
+ // Module basename and offset, or PC.
+ if (info.address & kExternalPCBit)
+ {} // There PCs are not meaningful.
+ else if (info.module)
+ buffer->append("(%s+%p)", StripModuleName(info.module),
+ (void *)info.module_offset);
+ else
+ buffer->append("(%p)", (void *)info.address);
+ break;
+ default:
+ Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
+ *p);
+ Die();
+ }
+ }
+}
+
+void RenderSourceLocation(InternalScopedString *buffer, const char *file,
+ int line, int column, bool vs_style,
+ const char *strip_path_prefix) {
+ if (vs_style && line > 0) {
+ buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
+ if (column > 0)
+ buffer->append(",%d", column);
+ buffer->append(")");
+ return;
+ }
+
+ buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
+ if (line > 0) {
+ buffer->append(":%d", line);
+ if (column > 0)
+ buffer->append(":%d", column);
+ }
+}
+
+void RenderModuleLocation(InternalScopedString *buffer, const char *module,
+ uptr offset, const char *strip_path_prefix) {
+ buffer->append("(%s+0x%zx)", StripPathPrefix(module, strip_path_prefix),
+ offset);
+}
+
+} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_stacktrace_printer.h --------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizers' run-time libraries.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_STACKTRACE_PRINTER_H
+#define SANITIZER_STACKTRACE_PRINTER_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+// Render the contents of "info" structure, which represents the contents of
+// stack frame "frame_no" and appends it to the "buffer". "format" is a
+// string with placeholders, which is copied to the output with
+// placeholders substituted with the contents of "info". For example,
+// format string
+// " frame %n: function %F at %S"
+// will be turned into
+// " frame 10: function foo::bar() at my/file.cc:10"
+// You may additionally pass "strip_path_prefix" to strip prefixes of paths to
+// source files and modules, and "strip_func_prefix" to strip prefixes of
+// function names.
+// Here's the full list of available placeholders:
+// %% - represents a '%' character;
+// %n - frame number (copy of frame_no);
+// %p - PC in hex format;
+// %m - path to module (binary or shared object);
+// %o - offset in the module in hex format;
+// %f - function name;
+// %q - offset in the function in hex format (*if available*);
+// %s - path to source file;
+// %l - line in the source file;
+// %c - column in the source file;
+// %F - if function is known to be <foo>, prints "in <foo>", possibly
+// followed by the offset in this function, but only if source file
+// is unknown;
+// %S - prints file/line/column information;
+// %L - prints location information: file/line/column, if it is known, or
+// module+offset if it is known, or (<unknown module>) string.
+// %M - prints module basename and offset, if it is known, or PC.
+void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
+ const AddressInfo &info, bool vs_style,
+ const char *strip_path_prefix = "",
+ const char *strip_func_prefix = "");
+
+void RenderSourceLocation(InternalScopedString *buffer, const char *file,
+ int line, int column, bool vs_style,
+ const char *strip_path_prefix);
+
+void RenderModuleLocation(InternalScopedString *buffer, const char *module,
+ uptr offset, const char *strip_path_prefix);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_STACKTRACE_PRINTER_H
// Suspend all threads in the current process and run the callback on the list
// of suspended threads. This function will resume the threads before returning.
-// The callback should not call any libc functions.
+// The callback should not call any libc functions. The callback must not call
+// exit() nor _exit() and instead return to the caller.
// This function should NOT be called from multiple threads simultaneously.
void StopTheWorld(StopTheWorldCallback callback, void *argument);
//
//===----------------------------------------------------------------------===//
-
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX && defined(__x86_64__)
+
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__))
#include "sanitizer_stoptheworld.h"
#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_atomic.h"
#include <errno.h>
#include <sched.h> // for CLONE_* definitions
#include <sys/prctl.h> // for PR_* definitions
#include <sys/ptrace.h> // for PTRACE_* definitions
#include <sys/types.h> // for pid_t
+#include <sys/uio.h> // for iovec
+#include <elf.h> // for NT_PRSTATUS
#if SANITIZER_ANDROID && defined(__arm__)
# include <linux/user.h> // for pt_regs
#else
+# ifdef __aarch64__
+// GLIBC 2.20+ sys/user does not include asm/ptrace.h
+# include <asm/ptrace.h>
+# endif
# include <sys/user.h> // for user_regs_struct
#endif
#include <sys/wait.h> // for signal-related stuff
COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t));
namespace __sanitizer {
+
+// Structure for passing arguments into the tracer thread.
+struct TracerThreadArgument {
+ StopTheWorldCallback callback;
+ void *callback_argument;
+ // The tracer thread waits on this mutex while the parent finishes its
+ // preparations.
+ BlockingMutex mutex;
+ // Tracer thread signals its completion by setting done.
+ atomic_uintptr_t done;
+ uptr parent_pid;
+};
+
// This class handles thread suspending/unsuspending in the tracer thread.
class ThreadSuspender {
public:
- explicit ThreadSuspender(pid_t pid)
- : pid_(pid) {
+ explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg)
+ : arg(arg)
+ , pid_(pid) {
CHECK_GE(pid, 0);
}
bool SuspendAllThreads();
SuspendedThreadsList &suspended_threads_list() {
return suspended_threads_list_;
}
+ TracerThreadArgument *arg;
private:
SuspendedThreadsList suspended_threads_list_;
pid_t pid_;
bool SuspendThread(SuspendedThreadID thread_id);
};
-bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) {
+bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) {
// Are we already attached to this thread?
// Currently this check takes linear time, however the number of threads is
// usually small.
- if (suspended_threads_list_.Contains(thread_id))
+ if (suspended_threads_list_.Contains(tid))
return false;
int pterrno;
- if (internal_iserror(internal_ptrace(PTRACE_ATTACH, thread_id, NULL, NULL),
+ if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr),
&pterrno)) {
// Either the thread is dead, or something prevented us from attaching.
// Log this event and move on.
- if (common_flags()->verbosity)
- Report("Could not attach to thread %d (errno %d).\n", thread_id, pterrno);
+ VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno);
return false;
} else {
- if (common_flags()->verbosity)
- Report("Attached to thread %d.\n", thread_id);
+ VReport(2, "Attached to thread %d.\n", tid);
// The thread is not guaranteed to stop before ptrace returns, so we must
- // wait on it.
- uptr waitpid_status;
- HANDLE_EINTR(waitpid_status, internal_waitpid(thread_id, NULL, __WALL));
- int wperrno;
- if (internal_iserror(waitpid_status, &wperrno)) {
- // Got a ECHILD error. I don't think this situation is possible, but it
- // doesn't hurt to report it.
- if (common_flags()->verbosity)
- Report("Waiting on thread %d failed, detaching (errno %d).\n",
- thread_id, wperrno);
- internal_ptrace(PTRACE_DETACH, thread_id, NULL, NULL);
- return false;
+ // wait on it. Note: if the thread receives a signal concurrently,
+ // we can get notification about the signal before notification about stop.
+ // In such case we need to forward the signal to the thread, otherwise
+ // the signal will be missed (as we do PTRACE_DETACH with arg=0) and
+ // any logic relying on signals will break. After forwarding we need to
+ // continue to wait for stopping, because the thread is not stopped yet.
+ // We do ignore delivery of SIGSTOP, because we want to make stop-the-world
+ // as invisible as possible.
+ for (;;) {
+ int status;
+ uptr waitpid_status;
+ HANDLE_EINTR(waitpid_status, internal_waitpid(tid, &status, __WALL));
+ int wperrno;
+ if (internal_iserror(waitpid_status, &wperrno)) {
+ // Got a ECHILD error. I don't think this situation is possible, but it
+ // doesn't hurt to report it.
+ VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n",
+ tid, wperrno);
+ internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr);
+ return false;
+ }
+ if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) {
+ internal_ptrace(PTRACE_CONT, tid, nullptr,
+ (void*)(uptr)WSTOPSIG(status));
+ continue;
+ }
+ break;
}
- suspended_threads_list_.Append(thread_id);
+ suspended_threads_list_.Append(tid);
return true;
}
}
for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) {
pid_t tid = suspended_threads_list_.GetThreadID(i);
int pterrno;
- if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL),
+ if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr),
&pterrno)) {
- if (common_flags()->verbosity)
- Report("Detached from thread %d.\n", tid);
+ VReport(2, "Detached from thread %d.\n", tid);
} else {
// Either the thread is dead, or we are already detached.
// The latter case is possible, for instance, if this function was called
// from a signal handler.
- if (common_flags()->verbosity)
- Report("Could not detach from thread %d (errno %d).\n", tid, pterrno);
+ VReport(1, "Could not detach from thread %d (errno %d).\n", tid, pterrno);
}
}
}
void ThreadSuspender::KillAllThreads() {
for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++)
internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i),
- NULL, NULL);
+ nullptr, nullptr);
}
bool ThreadSuspender::SuspendAllThreads() {
}
// Pointer to the ThreadSuspender instance for use in signal handler.
-static ThreadSuspender *thread_suspender_instance = NULL;
-
-// Signals that should not be blocked (this is used in the parent thread as well
-// as the tracer thread).
-static const int kUnblockedSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV,
- SIGBUS, SIGXCPU, SIGXFSZ };
-
-// Structure for passing arguments into the tracer thread.
-struct TracerThreadArgument {
- StopTheWorldCallback callback;
- void *callback_argument;
- // The tracer thread waits on this mutex while the parent finishes its
- // preparations.
- BlockingMutex mutex;
- uptr parent_pid;
-};
-
-static DieCallbackType old_die_callback;
+static ThreadSuspender *thread_suspender_instance = nullptr;
-// Signal handler to wake up suspended threads when the tracer thread dies.
-void TracerThreadSignalHandler(int signum, void *siginfo, void *) {
- if (thread_suspender_instance != NULL) {
- if (signum == SIGABRT)
- thread_suspender_instance->KillAllThreads();
- else
- thread_suspender_instance->ResumeAllThreads();
- }
- internal__exit((signum == SIGABRT) ? 1 : 2);
-}
+// Synchronous signals that should not be blocked.
+static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,
+ SIGXCPU, SIGXFSZ };
static void TracerThreadDieCallback() {
// Generally a call to Die() in the tracer thread should be fatal to the
// point. So we correctly handle calls to Die() from within the callback, but
// not those that happen before or after the callback. Hopefully there aren't
// a lot of opportunities for that to happen...
- if (thread_suspender_instance)
- thread_suspender_instance->KillAllThreads();
- if (old_die_callback)
- old_die_callback();
+ ThreadSuspender *inst = thread_suspender_instance;
+ if (inst && stoptheworld_tracer_pid == internal_getpid()) {
+ inst->KillAllThreads();
+ thread_suspender_instance = nullptr;
+ }
+}
+
+// Signal handler to wake up suspended threads when the tracer thread dies.
+static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) {
+ SignalContext ctx = SignalContext::Create(siginfo, uctx);
+ VPrintf(1, "Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n",
+ signum, ctx.addr, ctx.pc, ctx.sp);
+ ThreadSuspender *inst = thread_suspender_instance;
+ if (inst) {
+ if (signum == SIGABRT)
+ inst->KillAllThreads();
+ else
+ inst->ResumeAllThreads();
+ RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
+ thread_suspender_instance = nullptr;
+ atomic_store(&inst->arg->done, 1, memory_order_relaxed);
+ }
+ internal__exit((signum == SIGABRT) ? 1 : 2);
}
// Size of alternative stack for signal handlers in the tracer thread.
tracer_thread_argument->mutex.Lock();
tracer_thread_argument->mutex.Unlock();
- SetDieCallback(TracerThreadDieCallback);
+ RAW_CHECK(AddDieCallback(TracerThreadDieCallback));
- ThreadSuspender thread_suspender(internal_getppid());
+ ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument);
// Global pointer for the signal handler.
thread_suspender_instance = &thread_suspender;
internal_memset(&handler_stack, 0, sizeof(handler_stack));
handler_stack.ss_sp = handler_stack_memory.data();
handler_stack.ss_size = kHandlerStackSize;
- internal_sigaltstack(&handler_stack, NULL);
-
- // Install our handler for fatal signals. Other signals should be blocked by
- // the mask we inherited from the caller thread.
- for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
- signal_index++) {
- __sanitizer_kernel_sigaction_t new_sigaction;
- internal_memset(&new_sigaction, 0, sizeof(new_sigaction));
- new_sigaction.sigaction = TracerThreadSignalHandler;
- new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO;
- internal_sigfillset(&new_sigaction.sa_mask);
- internal_sigaction(kUnblockedSignals[signal_index], &new_sigaction, NULL);
+ internal_sigaltstack(&handler_stack, nullptr);
+
+ // Install our handler for synchronous signals. Other signals should be
+ // blocked by the mask we inherited from the parent thread.
+ for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) {
+ __sanitizer_sigaction act;
+ internal_memset(&act, 0, sizeof(act));
+ act.sigaction = TracerThreadSignalHandler;
+ act.sa_flags = SA_ONSTACK | SA_SIGINFO;
+ internal_sigaction_norestorer(kSyncSignals[i], &act, 0);
}
int exit_code = 0;
if (!thread_suspender.SuspendAllThreads()) {
- if (common_flags()->verbosity)
- Report("Failed suspending threads.\n");
+ VReport(1, "Failed suspending threads.\n");
exit_code = 3;
} else {
tracer_thread_argument->callback(thread_suspender.suspended_threads_list(),
thread_suspender.ResumeAllThreads();
exit_code = 0;
}
- thread_suspender_instance = NULL;
- handler_stack.ss_flags = SS_DISABLE;
- internal_sigaltstack(&handler_stack, NULL);
+ RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
+ thread_suspender_instance = nullptr;
+ atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed);
return exit_code;
}
// in the future.
guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_,
"ScopedStackWithGuard");
- CHECK_EQ(guard_start_, (uptr)Mprotect((uptr)guard_start_, guard_size_));
+ CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_));
}
~ScopedStackSpaceWithGuard() {
UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_);
// We have a limitation on the stack frame size, so some stuff had to be moved
// into globals.
-static __sanitizer_kernel_sigset_t blocked_sigset;
-static __sanitizer_kernel_sigset_t old_sigset;
-static __sanitizer_kernel_sigaction_t old_sigactions
- [ARRAY_SIZE(kUnblockedSignals)];
+static __sanitizer_sigset_t blocked_sigset;
+static __sanitizer_sigset_t old_sigset;
class StopTheWorldScope {
public:
StopTheWorldScope() {
- // Block all signals that can be blocked safely, and install
- // default handlers for the remaining signals.
- // We cannot allow user-defined handlers to run while the ThreadSuspender
- // thread is active, because they could conceivably call some libc functions
- // which modify errno (which is shared between the two threads).
- internal_sigfillset(&blocked_sigset);
- for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
- signal_index++) {
- // Remove the signal from the set of blocked signals.
- internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]);
- // Install the default handler.
- __sanitizer_kernel_sigaction_t new_sigaction;
- internal_memset(&new_sigaction, 0, sizeof(new_sigaction));
- new_sigaction.handler = SIG_DFL;
- internal_sigfillset(&new_sigaction.sa_mask);
- internal_sigaction(kUnblockedSignals[signal_index], &new_sigaction,
- &old_sigactions[signal_index]);
- }
- int sigprocmask_status =
- internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
- CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail
// Make this process dumpable. Processes that are not dumpable cannot be
// attached to.
process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
if (!process_was_dumpable_)
internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
- old_die_callback = GetDieCallback();
}
~StopTheWorldScope() {
- SetDieCallback(old_die_callback);
// Restore the dumpable flag.
if (!process_was_dumpable_)
internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
- // Restore the signal handlers.
- for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
- signal_index++) {
- internal_sigaction(kUnblockedSignals[signal_index],
- &old_sigactions[signal_index], NULL);
- }
- internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset);
}
private:
tracer_thread_argument.callback = callback;
tracer_thread_argument.callback_argument = argument;
tracer_thread_argument.parent_pid = internal_getpid();
+ atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed);
const uptr kTracerStackSize = 2 * 1024 * 1024;
ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);
// Block the execution of TracerThread until after we have set ptrace
// permissions.
tracer_thread_argument.mutex.Lock();
+ // Signal handling story.
+ // We don't want async signals to be delivered to the tracer thread,
+ // so we block all async signals before creating the thread. An async signal
+ // handler can temporary modify errno, which is shared with this thread.
+ // We ought to use pthread_sigmask here, because sigprocmask has undefined
+ // behavior in multithreaded programs. However, on linux sigprocmask is
+ // equivalent to pthread_sigmask with the exception that pthread_sigmask
+ // does not allow to block some signals used internally in pthread
+ // implementation. We are fine with blocking them here, we are really not
+ // going to pthread_cancel the thread.
+ // The tracer thread should not raise any synchronous signals. But in case it
+ // does, we setup a special handler for sync signals that properly kills the
+ // parent as well. Note: we don't pass CLONE_SIGHAND to clone, so handlers
+ // in the tracer thread won't interfere with user program. Double note: if a
+ // user does something along the lines of 'kill -11 pid', that can kill the
+ // process even if user setup own handler for SEGV.
+ // Thing to watch out for: this code should not change behavior of user code
+ // in any observable way. In particular it should not override user signal
+ // handlers.
+ internal_sigfillset(&blocked_sigset);
+ for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++)
+ internal_sigdelset(&blocked_sigset, kSyncSignals[i]);
+ int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
+ CHECK_EQ(rv, 0);
uptr tracer_pid = internal_clone(
TracerThread, tracer_stack.Bottom(),
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
- &tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0
- /* child_tidptr */);
+ &tracer_thread_argument, nullptr /* parent_tidptr */,
+ nullptr /* newtls */, nullptr /* child_tidptr */);
+ internal_sigprocmask(SIG_SETMASK, &old_sigset, 0);
int local_errno = 0;
if (internal_iserror(tracer_pid, &local_errno)) {
- if (common_flags()->verbosity)
- Report("Failed spawning a tracer thread (errno %d).\n", local_errno);
+ VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno);
tracer_thread_argument.mutex.Unlock();
} else {
ScopedSetTracerPID scoped_set_tracer_pid(tracer_pid);
#endif
// Allow the tracer thread to start.
tracer_thread_argument.mutex.Unlock();
- // Since errno is shared between this thread and the tracer thread, we
- // must avoid using errno while the tracer thread is running.
- // At this point, any signal will either be blocked or kill us, so waitpid
- // should never return (and set errno) while the tracer thread is alive.
- uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL);
- if (internal_iserror(waitpid_status, &local_errno)) {
- if (common_flags()->verbosity)
- Report("Waiting on the tracer thread failed (errno %d).\n",
- local_errno);
+ // NOTE: errno is shared between this thread and the tracer thread.
+ // internal_waitpid() may call syscall() which can access/spoil errno,
+ // so we can't call it now. Instead we for the tracer thread to finish using
+ // the spin loop below. Man page for sched_yield() says "In the Linux
+ // implementation, sched_yield() always succeeds", so let's hope it does not
+ // spoil errno. Note that this spin loop runs only for brief periods before
+ // the tracer thread has suspended us and when it starts unblocking threads.
+ while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0)
+ sched_yield();
+ // Now the tracer thread is about to exit and does not touch errno,
+ // wait for it.
+ for (;;) {
+ uptr waitpid_status = internal_waitpid(tracer_pid, nullptr, __WALL);
+ if (!internal_iserror(waitpid_status, &local_errno))
+ break;
+ if (local_errno == EINTR)
+ continue;
+ VReport(1, "Waiting on the tracer thread failed (errno %d).\n",
+ local_errno);
+ break;
}
}
}
typedef struct user regs_struct;
#define REG_SP regs[EF_REG29]
+#elif defined(__aarch64__)
+typedef struct user_pt_regs regs_struct;
+#define REG_SP sp
+#define ARCH_IOVEC_FOR_GETREGSET
+
#else
#error "Unsupported architecture"
#endif // SANITIZER_ANDROID && defined(__arm__)
pid_t tid = GetThreadID(index);
regs_struct regs;
int pterrno;
- if (internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, NULL, ®s),
- &pterrno)) {
- if (common_flags()->verbosity)
- Report("Could not get registers from thread %d (errno %d).\n",
- tid, pterrno);
+#ifdef ARCH_IOVEC_FOR_GETREGSET
+ struct iovec regset_io;
+ regset_io.iov_base = ®s;
+ regset_io.iov_len = sizeof(regs_struct);
+ bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGSET, tid,
+ (void*)NT_PRSTATUS, (void*)®set_io),
+ &pterrno);
+#else
+ bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, nullptr,
+ ®s), &pterrno);
+#endif
+ if (isErr) {
+ VReport(1, "Could not get registers from thread %d (errno %d).\n", tid,
+ pterrno);
return -1;
}
uptr SuspendedThreadsList::RegisterCount() {
return sizeof(regs_struct) / sizeof(uptr);
}
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_LINUX && defined(__x86_64__)
+#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
+ // || defined(__aarch64__)
//
//===----------------------------------------------------------------------===//
//
-// Suppression parsing/matching code shared between TSan and LSan.
+// Suppression parsing/matching code.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
namespace __sanitizer {
-static const char *const kTypeStrings[SuppressionTypeCount] = {
- "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib"
-};
+SuppressionContext::SuppressionContext(const char *suppression_types[],
+ int suppression_types_num)
+ : suppression_types_(suppression_types),
+ suppression_types_num_(suppression_types_num), suppressions_(1),
+ can_parse_(true) {
+ CHECK_LE(suppression_types_num_, kMaxSuppressionTypes);
+ internal_memset(has_suppression_type_, 0, suppression_types_num_);
+}
-bool TemplateMatch(char *templ, const char *str) {
- if (str == 0 || str[0] == 0)
- return false;
- bool start = false;
- if (templ && templ[0] == '^') {
- start = true;
- templ++;
+static bool GetPathAssumingFileIsRelativeToExec(const char *file_path,
+ /*out*/char *new_file_path,
+ uptr new_file_path_size) {
+ InternalScopedString exec(kMaxPathLength);
+ if (ReadBinaryNameCached(exec.data(), exec.size())) {
+ const char *file_name_pos = StripModuleName(exec.data());
+ uptr path_to_exec_len = file_name_pos - exec.data();
+ internal_strncat(new_file_path, exec.data(),
+ Min(path_to_exec_len, new_file_path_size - 1));
+ internal_strncat(new_file_path, file_path,
+ new_file_path_size - internal_strlen(new_file_path) - 1);
+ return true;
}
- bool asterisk = false;
- while (templ && templ[0]) {
- if (templ[0] == '*') {
- templ++;
- start = false;
- asterisk = true;
- continue;
- }
- if (templ[0] == '$')
- return str[0] == 0 || asterisk;
- if (str[0] == 0)
- return false;
- char *tpos = (char*)internal_strchr(templ, '*');
- char *tpos1 = (char*)internal_strchr(templ, '$');
- if (tpos == 0 || (tpos1 && tpos1 < tpos))
- tpos = tpos1;
- if (tpos != 0)
- tpos[0] = 0;
- const char *str0 = str;
- const char *spos = internal_strstr(str, templ);
- str = spos + internal_strlen(templ);
- templ = tpos;
- if (tpos)
- tpos[0] = tpos == tpos1 ? '$' : '*';
- if (spos == 0)
- return false;
- if (start && spos != str0)
- return false;
- start = false;
- asterisk = false;
+ return false;
+}
+
+void SuppressionContext::ParseFromFile(const char *filename) {
+ if (filename[0] == '\0')
+ return;
+
+ // If we cannot find the file, check if its location is relative to
+ // the location of the executable.
+ InternalScopedString new_file_path(kMaxPathLength);
+ if (!FileExists(filename) && !IsAbsolutePath(filename) &&
+ GetPathAssumingFileIsRelativeToExec(filename, new_file_path.data(),
+ new_file_path.size())) {
+ filename = new_file_path.data();
+ }
+
+ // Read the file.
+ VPrintf(1, "%s: reading suppressions file at %s\n",
+ SanitizerToolName, filename);
+ char *file_contents;
+ uptr buffer_size;
+ uptr contents_size;
+ if (!ReadFileToBuffer(filename, &file_contents, &buffer_size,
+ &contents_size)) {
+ Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
+ filename);
+ Die();
}
- return true;
+
+ Parse(file_contents);
}
-bool SuppressionContext::Match(const char *str, SuppressionType type,
+bool SuppressionContext::Match(const char *str, const char *type,
Suppression **s) {
can_parse_ = false;
- uptr i;
- for (i = 0; i < suppressions_.size(); i++)
- if (type == suppressions_[i].type &&
- TemplateMatch(suppressions_[i].templ, str))
- break;
- if (i == suppressions_.size()) return false;
- *s = &suppressions_[i];
- return true;
+ if (!HasSuppressionType(type))
+ return false;
+ for (uptr i = 0; i < suppressions_.size(); i++) {
+ Suppression &cur = suppressions_[i];
+ if (0 == internal_strcmp(cur.type, type) && TemplateMatch(cur.templ, str)) {
+ *s = &cur;
+ return true;
+ }
+ }
+ return false;
}
static const char *StripPrefix(const char *str, const char *prefix) {
end = line + internal_strlen(line);
if (line != end && line[0] != '#') {
const char *end2 = end;
- while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t'))
+ while (line != end2 &&
+ (end2[-1] == ' ' || end2[-1] == '\t' || end2[-1] == '\r'))
end2--;
int type;
- for (type = 0; type < SuppressionTypeCount; type++) {
- const char *next_char = StripPrefix(line, kTypeStrings[type]);
+ for (type = 0; type < suppression_types_num_; type++) {
+ const char *next_char = StripPrefix(line, suppression_types_[type]);
if (next_char && *next_char == ':') {
line = ++next_char;
break;
}
}
- if (type == SuppressionTypeCount) {
+ if (type == suppression_types_num_) {
Printf("%s: failed to parse suppressions\n", SanitizerToolName);
Die();
}
- Suppression s;
- s.type = static_cast<SuppressionType>(type);
+ Suppression s = {};
+ s.type = suppression_types_[type];
s.templ = (char*)InternalAlloc(end2 - line + 1);
internal_memcpy(s.templ, line, end2 - line);
s.templ[end2 - line] = 0;
- s.hit_count = 0;
- s.weight = 0;
suppressions_.push_back(s);
+ has_suppression_type_[type] = true;
}
if (end[0] == 0)
break;
return suppressions_.size();
}
+bool SuppressionContext::HasSuppressionType(const char *type) const {
+ for (int i = 0; i < suppression_types_num_; i++) {
+ if (0 == internal_strcmp(type, suppression_types_[i]))
+ return has_suppression_type_[i];
+ }
+ return false;
+}
+
const Suppression *SuppressionContext::SuppressionAt(uptr i) const {
CHECK_LT(i, suppressions_.size());
return &suppressions_[i];
void SuppressionContext::GetMatched(
InternalMmapVector<Suppression *> *matched) {
for (uptr i = 0; i < suppressions_.size(); i++)
- if (suppressions_[i].hit_count)
+ if (atomic_load_relaxed(&suppressions_[i].hit_count))
matched->push_back(&suppressions_[i]);
}
-const char *SuppressionTypeString(SuppressionType t) {
- CHECK(t < SuppressionTypeCount);
- return kTypeStrings[t];
-}
-
} // namespace __sanitizer
//
//===----------------------------------------------------------------------===//
//
-// Suppression parsing/matching code shared between TSan and LSan.
+// Suppression parsing/matching code.
//
//===----------------------------------------------------------------------===//
#ifndef SANITIZER_SUPPRESSIONS_H
#define SANITIZER_SUPPRESSIONS_H
#include "sanitizer_common.h"
+#include "sanitizer_atomic.h"
#include "sanitizer_internal_defs.h"
namespace __sanitizer {
-enum SuppressionType {
- SuppressionNone,
- SuppressionRace,
- SuppressionMutex,
- SuppressionThread,
- SuppressionSignal,
- SuppressionLeak,
- SuppressionLib,
- SuppressionTypeCount
-};
-
struct Suppression {
- SuppressionType type;
+ const char *type;
char *templ;
- unsigned hit_count;
+ atomic_uint32_t hit_count;
uptr weight;
};
class SuppressionContext {
public:
- SuppressionContext() : suppressions_(1), can_parse_(true) {}
+ // Create new SuppressionContext capable of parsing given suppression types.
+ SuppressionContext(const char *supprression_types[],
+ int suppression_types_num);
+
+ void ParseFromFile(const char *filename);
void Parse(const char *str);
- bool Match(const char* str, SuppressionType type, Suppression **s);
+
+ bool Match(const char *str, const char *type, Suppression **s);
uptr SuppressionCount() const;
+ bool HasSuppressionType(const char *type) const;
const Suppression *SuppressionAt(uptr i) const;
void GetMatched(InternalMmapVector<Suppression *> *matched);
private:
+ static const int kMaxSuppressionTypes = 16;
+ const char **const suppression_types_;
+ const int suppression_types_num_;
+
InternalMmapVector<Suppression> suppressions_;
+ bool has_suppression_type_[kMaxSuppressionTypes];
bool can_parse_;
-
- friend class SuppressionContextTest;
};
-const char *SuppressionTypeString(SuppressionType t);
-
-bool TemplateMatch(char *templ, const char *str);
-
} // namespace __sanitizer
#endif // SANITIZER_SUPPRESSIONS_H
// run-time libraries.
//===----------------------------------------------------------------------===//
+#include "sanitizer_allocator_internal.h"
#include "sanitizer_platform.h"
#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
#include "sanitizer_placement_new.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
namespace __sanitizer {
-Symbolizer *Symbolizer::symbolizer_;
-StaticSpinMutex Symbolizer::init_mu_;
-LowLevelAllocator Symbolizer::symbolizer_allocator_;
+AddressInfo::AddressInfo() {
+ internal_memset(this, 0, sizeof(AddressInfo));
+ function_offset = kUnknown;
+}
+
+void AddressInfo::Clear() {
+ InternalFree(module);
+ InternalFree(function);
+ InternalFree(file);
+ internal_memset(this, 0, sizeof(AddressInfo));
+ function_offset = kUnknown;
+}
-Symbolizer *Symbolizer::GetOrNull() {
- SpinMutexLock l(&init_mu_);
- return symbolizer_;
+void AddressInfo::FillModuleInfo(const char *mod_name, uptr mod_offset) {
+ module = internal_strdup(mod_name);
+ module_offset = mod_offset;
}
-Symbolizer *Symbolizer::Get() {
- SpinMutexLock l(&init_mu_);
- RAW_CHECK_MSG(symbolizer_ != 0, "Using uninitialized symbolizer!");
- return symbolizer_;
+SymbolizedStack::SymbolizedStack() : next(nullptr), info() {}
+
+SymbolizedStack *SymbolizedStack::New(uptr addr) {
+ void *mem = InternalAlloc(sizeof(SymbolizedStack));
+ SymbolizedStack *res = new(mem) SymbolizedStack();
+ res->info.address = addr;
+ return res;
}
-Symbolizer *Symbolizer::Disable() {
- CHECK_EQ(0, symbolizer_);
- // Initialize a dummy symbolizer.
- symbolizer_ = new(symbolizer_allocator_) Symbolizer;
- return symbolizer_;
+void SymbolizedStack::ClearAll() {
+ info.Clear();
+ if (next)
+ next->ClearAll();
+ InternalFree(this);
}
+DataInfo::DataInfo() {
+ internal_memset(this, 0, sizeof(DataInfo));
+}
+
+void DataInfo::Clear() {
+ InternalFree(module);
+ InternalFree(name);
+ internal_memset(this, 0, sizeof(DataInfo));
+}
+
+Symbolizer *Symbolizer::symbolizer_;
+StaticSpinMutex Symbolizer::init_mu_;
+LowLevelAllocator Symbolizer::symbolizer_allocator_;
+
void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook,
Symbolizer::EndSymbolizationHook end_hook) {
CHECK(start_hook_ == 0 && end_hook_ == 0);
end_hook_ = end_hook;
}
-Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {}
+const char *Symbolizer::ModuleNameOwner::GetOwnedCopy(const char *str) {
+ mu_->CheckLocked();
+
+ // 'str' will be the same string multiple times in a row, optimize this case.
+ if (last_match_ && !internal_strcmp(last_match_, str))
+ return last_match_;
+
+ // FIXME: this is linear search.
+ // We should optimize this further if this turns out to be a bottleneck later.
+ for (uptr i = 0; i < storage_.size(); ++i) {
+ if (!internal_strcmp(storage_[i], str)) {
+ last_match_ = storage_[i];
+ return last_match_;
+ }
+ }
+ last_match_ = internal_strdup(str);
+ storage_.push_back(last_match_);
+ return last_match_;
+}
+
+Symbolizer::Symbolizer(IntrusiveList<SymbolizerTool> tools)
+ : module_names_(&mu_), n_modules_(0), modules_fresh_(false), tools_(tools),
+ start_hook_(0), end_hook_(0) {}
Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym)
: sym_(sym) {
#ifndef SANITIZER_SYMBOLIZER_H
#define SANITIZER_SYMBOLIZER_H
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_libc.h"
+#include "sanitizer_common.h"
+#include "sanitizer_mutex.h"
namespace __sanitizer {
struct AddressInfo {
+ // Owns all the string members. Storage for them is
+ // (de)allocated using sanitizer internal allocator.
uptr address;
+
char *module;
uptr module_offset;
+
+ static const uptr kUnknown = ~(uptr)0;
char *function;
+ uptr function_offset;
+
char *file;
int line;
int column;
- AddressInfo() {
- internal_memset(this, 0, sizeof(AddressInfo));
- }
+ AddressInfo();
+ // Deletes all strings and resets all fields.
+ void Clear();
+ void FillModuleInfo(const char *mod_name, uptr mod_offset);
+};
- // Deletes all strings and sets all fields to zero.
- void Clear() {
- InternalFree(module);
- InternalFree(function);
- InternalFree(file);
- internal_memset(this, 0, sizeof(AddressInfo));
- }
+// Linked list of symbolized frames (each frame is described by AddressInfo).
+struct SymbolizedStack {
+ SymbolizedStack *next;
+ AddressInfo info;
+ static SymbolizedStack *New(uptr addr);
+ // Deletes current, and all subsequent frames in the linked list.
+ // The object cannot be accessed after the call to this function.
+ void ClearAll();
- void FillAddressAndModuleInfo(uptr addr, const char *mod_name,
- uptr mod_offset) {
- address = addr;
- module = internal_strdup(mod_name);
- module_offset = mod_offset;
- }
+ private:
+ SymbolizedStack();
};
+// For now, DataInfo is used to describe global variable.
struct DataInfo {
- uptr address;
+ // Owns all the string members. Storage for them is
+ // (de)allocated using sanitizer internal allocator.
char *module;
uptr module_offset;
char *name;
uptr start;
uptr size;
+
+ DataInfo();
+ void Clear();
};
-class Symbolizer {
+class SymbolizerTool;
+
+class Symbolizer final {
public:
- /// Returns platform-specific implementation of Symbolizer. The symbolizer
- /// must be initialized (with init or disable) before calling this function.
- static Symbolizer *Get();
- /// Returns platform-specific implementation of Symbolizer, or null if not
- /// initialized.
- static Symbolizer *GetOrNull();
- /// Returns platform-specific implementation of Symbolizer. Will
- /// automatically initialize symbolizer as if by calling Init(0) if needed.
+ /// Initialize and return platform-specific implementation of symbolizer
+ /// (if it wasn't already initialized).
static Symbolizer *GetOrInit();
- /// Initialize and return the symbolizer, given an optional path to an
- /// external symbolizer. The path argument is only required for legacy
- /// reasons as this function will check $PATH for an external symbolizer. Not
- /// thread safe.
- static Symbolizer *Init(const char* path_to_external = 0);
- /// Initialize the symbolizer in a disabled state. Not thread safe.
- static Symbolizer *Disable();
- // Fills at most "max_frames" elements of "frames" with descriptions
- // for a given address (in all inlined functions). Returns the number
- // of descriptions actually filled.
- virtual uptr SymbolizeCode(uptr address, AddressInfo *frames,
- uptr max_frames) {
- return 0;
- }
- virtual bool SymbolizeData(uptr address, DataInfo *info) {
- return false;
- }
- virtual bool IsAvailable() {
- return false;
- }
- virtual bool IsExternalAvailable() {
- return false;
+ // Returns a list of symbolized frames for a given address (containing
+ // all inlined functions, if necessary).
+ SymbolizedStack *SymbolizePC(uptr address);
+ bool SymbolizeData(uptr address, DataInfo *info);
+
+ // The module names Symbolizer returns are stable and unique for every given
+ // module. It is safe to store and compare them as pointers.
+ bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+ uptr *module_address);
+ const char *GetModuleNameForPc(uptr pc) {
+ const char *module_name = nullptr;
+ uptr unused;
+ if (GetModuleNameAndOffsetForPC(pc, &module_name, &unused))
+ return module_name;
+ return nullptr;
}
+
// Release internal caches (if any).
- virtual void Flush() {}
+ void Flush();
// Attempts to demangle the provided C++ mangled name.
- virtual const char *Demangle(const char *name) {
- return name;
- }
- virtual void PrepareForSandboxing() {}
+ const char *Demangle(const char *name);
+ void PrepareForSandboxing();
// Allow user to install hooks that would be called before/after Symbolizer
// does the actual file/line info fetching. Specific sanitizers may need this
EndSymbolizationHook end_hook);
private:
+ // GetModuleNameAndOffsetForPC has to return a string to the caller.
+ // Since the corresponding module might get unloaded later, we should create
+ // our owned copies of the strings that we can safely return.
+ // ModuleNameOwner does not provide any synchronization, thus calls to
+ // its method should be protected by |mu_|.
+ class ModuleNameOwner {
+ public:
+ explicit ModuleNameOwner(BlockingMutex *synchronized_by)
+ : storage_(kInitialCapacity), last_match_(nullptr),
+ mu_(synchronized_by) {}
+ const char *GetOwnedCopy(const char *str);
+
+ private:
+ static const uptr kInitialCapacity = 1000;
+ InternalMmapVector<const char*> storage_;
+ const char *last_match_;
+
+ BlockingMutex *mu_;
+ } module_names_;
+
/// Platform-specific function for creating a Symbolizer object.
- static Symbolizer *PlatformInit(const char *path_to_external);
- /// Create a symbolizer and store it to symbolizer_ without checking if one
- /// already exists. Not thread safe.
- static Symbolizer *CreateAndStore(const char *path_to_external);
+ static Symbolizer *PlatformInit();
+
+ bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name,
+ uptr *module_offset);
+ LoadedModule *FindModuleForAddress(uptr address);
+ LoadedModule modules_[kMaxNumberOfModules];
+ uptr n_modules_;
+ // If stale, need to reload the modules before looking up addresses.
+ bool modules_fresh_;
+
+ // Platform-specific default demangler, must not return nullptr.
+ const char *PlatformDemangle(const char *name);
+ void PlatformPrepareForSandboxing();
static Symbolizer *symbolizer_;
static StaticSpinMutex init_mu_;
- protected:
- Symbolizer();
+ // Mutex locked from public methods of |Symbolizer|, so that the internals
+ // (including individual symbolizer tools and platform-specific methods) are
+ // always synchronized.
+ BlockingMutex mu_;
+
+ typedef IntrusiveList<SymbolizerTool>::Iterator Iterator;
+ IntrusiveList<SymbolizerTool> tools_;
+
+ explicit Symbolizer(IntrusiveList<SymbolizerTool> tools);
static LowLevelAllocator symbolizer_allocator_;
--- /dev/null
+//===-- sanitizer_symbolizer_internal.h -------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Header for internal classes and functions to be used by implementations of
+// symbolizers.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_INTERNAL_H
+#define SANITIZER_SYMBOLIZER_INTERNAL_H
+
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+// Parsing helpers, 'str' is searched for delimiter(s) and a string or uptr
+// is extracted. When extracting a string, a newly allocated (using
+// InternalAlloc) and null-terminataed buffer is returned. They return a pointer
+// to the next characted after the found delimiter.
+const char *ExtractToken(const char *str, const char *delims, char **result);
+const char *ExtractInt(const char *str, const char *delims, int *result);
+const char *ExtractUptr(const char *str, const char *delims, uptr *result);
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+ char **result);
+
+const char *DemangleCXXABI(const char *name);
+
+// SymbolizerTool is an interface that is implemented by individual "tools"
+// that can perform symbolication (external llvm-symbolizer, libbacktrace,
+// Windows DbgHelp symbolizer, etc.).
+class SymbolizerTool {
+ public:
+ // The main |Symbolizer| class implements a "fallback chain" of symbolizer
+ // tools. In a request to symbolize an address, if one tool returns false,
+ // the next tool in the chain will be tried.
+ SymbolizerTool *next;
+
+ SymbolizerTool() : next(nullptr) { }
+
+ // Can't declare pure virtual functions in sanitizer runtimes:
+ // __cxa_pure_virtual might be unavailable.
+
+ // The |stack| parameter is inout. It is pre-filled with the address,
+ // module base and module offset values and is to be used to construct
+ // other stack frames.
+ virtual bool SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ UNIMPLEMENTED();
+ }
+
+ // The |info| parameter is inout. It is pre-filled with the module base
+ // and module offset values.
+ virtual bool SymbolizeData(uptr addr, DataInfo *info) {
+ UNIMPLEMENTED();
+ }
+
+ virtual void Flush() {}
+
+ // Return nullptr to fallback to the default platform-specific demangler.
+ virtual const char *Demangle(const char *name) {
+ return nullptr;
+ }
+};
+
+// SymbolizerProcess encapsulates communication between the tool and
+// external symbolizer program, running in a different subprocess.
+// SymbolizerProcess may not be used from two threads simultaneously.
+class SymbolizerProcess {
+ public:
+ explicit SymbolizerProcess(const char *path, bool use_forkpty = false);
+ const char *SendCommand(const char *command);
+
+ protected:
+ virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const {
+ UNIMPLEMENTED();
+ }
+
+ /// The maximum number of arguments required to invoke a tool process.
+ enum { kArgVMax = 6 };
+
+ /// Fill in an argv array to invoke the child process.
+ virtual void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const {
+ UNIMPLEMENTED();
+ }
+
+ virtual bool ReadFromSymbolizer(char *buffer, uptr max_length);
+
+ private:
+ bool Restart();
+ const char *SendCommandImpl(const char *command);
+ bool WriteToSymbolizer(const char *buffer, uptr length);
+ bool StartSymbolizerSubprocess();
+
+ const char *path_;
+ fd_t input_fd_;
+ fd_t output_fd_;
+
+ static const uptr kBufferSize = 16 * 1024;
+ char buffer_[kBufferSize];
+
+ static const uptr kMaxTimesRestarted = 5;
+ static const int kSymbolizerStartupTimeMillis = 10;
+ uptr times_restarted_;
+ bool failed_to_start_;
+ bool reported_invalid_path_;
+ bool use_forkpty_;
+};
+
+class LLVMSymbolizerProcess;
+
+// This tool invokes llvm-symbolizer in a subprocess. It should be as portable
+// as the llvm-symbolizer tool is.
+class LLVMSymbolizer : public SymbolizerTool {
+ public:
+ explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator);
+
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
+
+ private:
+ const char *SendCommand(bool is_data, const char *module_name,
+ uptr module_offset);
+
+ LLVMSymbolizerProcess *symbolizer_process_;
+ static const uptr kBufferSize = 16 * 1024;
+ char buffer_[kBufferSize];
+};
+
+// Parses one or more two-line strings in the following format:
+// <function_name>
+// <file_name>:<line_number>[:<column_number>]
+// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of
+// them use the same output format. Returns true if any useful debug
+// information was found.
+void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res);
+
+// Parses a two-line string in the following format:
+// <symbol_name>
+// <start_address> <size>
+// Used by LLVMSymbolizer and InternalSymbolizer.
+void ParseSymbolizeDataOutput(const char *str, DataInfo *info);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_SYMBOLIZER_INTERNAL_H
namespace __sanitizer {
+static char *DemangleAlloc(const char *name, bool always_alloc);
+
#if SANITIZER_LIBBACKTRACE
namespace {
-#if SANITIZER_CP_DEMANGLE
+# if SANITIZER_CP_DEMANGLE
struct CplusV3DemangleData {
char *buf;
uptr size, allocated;
}
} // extern "C"
-char *CplusV3Demangle(const char *name, bool always_alloc) {
+char *CplusV3Demangle(const char *name) {
CplusV3DemangleData data;
data.buf = 0;
data.size = 0;
data.allocated = 0;
if (cplus_demangle_v3_callback(name, DMGL_PARAMS | DMGL_ANSI,
- CplusV3DemangleCallback, &data)) {
+ CplusV3DemangleCallback, &data)) {
if (data.size + 64 > data.allocated)
return data.buf;
char *buf = internal_strdup(data.buf);
}
if (data.buf)
InternalFree(data.buf);
- if (always_alloc)
- return internal_strdup(name);
- return 0;
-}
-#else
-const char *CplusV3Demangle(const char *name, bool always_alloc) {
- if (always_alloc)
- return internal_strdup(name);
return 0;
}
-#endif
-
-struct SymbolizeCodeData {
- AddressInfo *frames;
- uptr n_frames;
- uptr max_frames;
- const char *module_name;
- uptr module_offset;
+# endif // SANITIZER_CP_DEMANGLE
+
+struct SymbolizeCodeCallbackArg {
+ SymbolizedStack *first;
+ SymbolizedStack *last;
+ uptr frames_symbolized;
+
+ AddressInfo *get_new_frame(uintptr_t addr) {
+ CHECK(last);
+ if (frames_symbolized > 0) {
+ SymbolizedStack *cur = SymbolizedStack::New(addr);
+ AddressInfo *info = &cur->info;
+ info->FillModuleInfo(first->info.module, first->info.module_offset);
+ last->next = cur;
+ last = cur;
+ }
+ CHECK_EQ(addr, first->info.address);
+ CHECK_EQ(addr, last->info.address);
+ return &last->info;
+ }
};
extern "C" {
static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr,
const char *filename, int lineno,
const char *function) {
- SymbolizeCodeData *cdata = (SymbolizeCodeData *)vdata;
+ SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
if (function) {
- AddressInfo *info = &cdata->frames[cdata->n_frames++];
- info->Clear();
- info->FillAddressAndModuleInfo(addr, cdata->module_name,
- cdata->module_offset);
- info->function = CplusV3Demangle(function, true);
+ AddressInfo *info = cdata->get_new_frame(addr);
+ info->function = DemangleAlloc(function, /*always_alloc*/ true);
if (filename)
info->file = internal_strdup(filename);
info->line = lineno;
- if (cdata->n_frames == cdata->max_frames)
- return 1;
+ cdata->frames_symbolized++;
}
return 0;
}
static void SymbolizeCodeCallback(void *vdata, uintptr_t addr,
const char *symname, uintptr_t, uintptr_t) {
- SymbolizeCodeData *cdata = (SymbolizeCodeData *)vdata;
+ SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
if (symname) {
- AddressInfo *info = &cdata->frames[0];
- info->Clear();
- info->FillAddressAndModuleInfo(addr, cdata->module_name,
- cdata->module_offset);
- info->function = CplusV3Demangle(symname, true);
- cdata->n_frames = 1;
+ AddressInfo *info = cdata->get_new_frame(addr);
+ info->function = DemangleAlloc(symname, /*always_alloc*/ true);
+ cdata->frames_symbolized++;
}
}
uintptr_t symval, uintptr_t symsize) {
DataInfo *info = (DataInfo *)vdata;
if (symname && symval) {
- info->name = CplusV3Demangle(symname, true);
+ info->name = DemangleAlloc(symname, /*always_alloc*/ true);
info->start = symval;
info->size = symsize;
}
return new(*alloc) LibbacktraceSymbolizer(state);
}
-uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames,
- uptr max_frames,
- const char *module_name,
- uptr module_offset) {
- SymbolizeCodeData data;
- data.frames = frames;
- data.n_frames = 0;
- data.max_frames = max_frames;
- data.module_name = module_name;
- data.module_offset = module_offset;
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ SymbolizeCodeCallbackArg data;
+ data.first = stack;
+ data.last = stack;
+ data.frames_symbolized = 0;
backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback,
ErrorCallback, &data);
- if (data.n_frames)
- return data.n_frames;
+ if (data.frames_symbolized > 0)
+ return true;
backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback,
ErrorCallback, &data);
- return data.n_frames;
+ return (data.frames_symbolized > 0);
}
-bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) {
- backtrace_syminfo((backtrace_state *)state_, info->address,
- SymbolizeDataCallback, ErrorCallback, info);
+bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeDataCallback,
+ ErrorCallback, info);
return true;
}
-const char *LibbacktraceSymbolizer::Demangle(const char *name) {
-#if SANITIZER_CP_DEMANGLE
- const char *demangled = CplusV3Demangle(name, false);
- if (demangled)
- return demangled;
- return name;
-#else
- return 0;
-#endif
-}
-
#else // SANITIZER_LIBBACKTRACE
LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
return 0;
}
-uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames,
- uptr max_frames,
- const char *module_name,
- uptr module_offset) {
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
(void)state_;
- return 0;
+ return false;
}
-bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) {
+bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
return false;
}
-const char *LibbacktraceSymbolizer::Demangle(const char *name) {
+#endif // SANITIZER_LIBBACKTRACE
+
+static char *DemangleAlloc(const char *name, bool always_alloc) {
+#if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE
+ if (char *demangled = CplusV3Demangle(name))
+ return demangled;
+#endif
+ if (always_alloc)
+ return internal_strdup(name);
return 0;
}
-#endif // SANITIZER_LIBBACKTRACE
+const char *LibbacktraceSymbolizer::Demangle(const char *name) {
+ return DemangleAlloc(name, /*always_alloc*/ false);
+}
} // namespace __sanitizer
-//===-- sanitizer_symbolizer_libbacktrace.h -------------------------------===//
+//===-- sanitizer_symbolizer_libbacktrace.h ---------------------*- C++ -*-===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
// run-time libraries.
// Header for libbacktrace symbolizer.
//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_LIBBACKTRACE_H
+#define SANITIZER_SYMBOLIZER_LIBBACKTRACE_H
#include "sanitizer_platform.h"
#include "sanitizer_common.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_symbolizer_internal.h"
#ifndef SANITIZER_LIBBACKTRACE
# define SANITIZER_LIBBACKTRACE 0
#endif
+#ifndef SANITIZER_CP_DEMANGLE
+# define SANITIZER_CP_DEMANGLE 0
+#endif
+
namespace __sanitizer {
-class LibbacktraceSymbolizer {
+class LibbacktraceSymbolizer : public SymbolizerTool {
public:
static LibbacktraceSymbolizer *get(LowLevelAllocator *alloc);
- uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames,
- const char *module_name, uptr module_offset);
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
- bool SymbolizeData(DataInfo *info);
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
- const char *Demangle(const char *name);
+ // May return NULL if demangling failed.
+ const char *Demangle(const char *name) override;
private:
explicit LibbacktraceSymbolizer(void *state) : state_(state) {}
};
} // namespace __sanitizer
+#endif // SANITIZER_SYMBOLIZER_LIBBACKTRACE_H
// run-time libraries.
//===----------------------------------------------------------------------===//
+#include "sanitizer_allocator_internal.h"
#include "sanitizer_internal_defs.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
namespace __sanitizer {
-Symbolizer *Symbolizer::CreateAndStore(const char *path_to_external) {
- Symbolizer *platform_symbolizer = PlatformInit(path_to_external);
- if (!platform_symbolizer)
- return Disable();
- symbolizer_ = platform_symbolizer;
- return platform_symbolizer;
+const char *ExtractToken(const char *str, const char *delims, char **result) {
+ uptr prefix_len = internal_strcspn(str, delims);
+ *result = (char*)InternalAlloc(prefix_len + 1);
+ internal_memcpy(*result, str, prefix_len);
+ (*result)[prefix_len] = '\0';
+ const char *prefix_end = str + prefix_len;
+ if (*prefix_end != '\0') prefix_end++;
+ return prefix_end;
}
-Symbolizer *Symbolizer::Init(const char *path_to_external) {
- CHECK_EQ(0, symbolizer_);
- return CreateAndStore(path_to_external);
+const char *ExtractInt(const char *str, const char *delims, int *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (int)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
+}
+
+const char *ExtractUptr(const char *str, const char *delims, uptr *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (uptr)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
+}
+
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+ char **result) {
+ const char *found_delimiter = internal_strstr(str, delimiter);
+ uptr prefix_len =
+ found_delimiter ? found_delimiter - str : internal_strlen(str);
+ *result = (char *)InternalAlloc(prefix_len + 1);
+ internal_memcpy(*result, str, prefix_len);
+ (*result)[prefix_len] = '\0';
+ const char *prefix_end = str + prefix_len;
+ if (*prefix_end != '\0') prefix_end += internal_strlen(delimiter);
+ return prefix_end;
+}
+
+SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
+ BlockingMutexLock l(&mu_);
+ const char *module_name;
+ uptr module_offset;
+ SymbolizedStack *res = SymbolizedStack::New(addr);
+ if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
+ return res;
+ // Always fill data about module name and offset.
+ res->info.FillModuleInfo(module_name, module_offset);
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ if (tool->SymbolizePC(addr, res)) {
+ return res;
+ }
+ }
+ return res;
+}
+
+bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ BlockingMutexLock l(&mu_);
+ const char *module_name;
+ uptr module_offset;
+ if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
+ return false;
+ info->Clear();
+ info->module = internal_strdup(module_name);
+ info->module_offset = module_offset;
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ if (tool->SymbolizeData(addr, info)) {
+ return true;
+ }
+ }
+ return true;
+}
+
+bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+ uptr *module_address) {
+ BlockingMutexLock l(&mu_);
+ const char *internal_module_name = nullptr;
+ if (!FindModuleNameAndOffsetForAddress(pc, &internal_module_name,
+ module_address))
+ return false;
+
+ if (module_name)
+ *module_name = module_names_.GetOwnedCopy(internal_module_name);
+ return true;
+}
+
+void Symbolizer::Flush() {
+ BlockingMutexLock l(&mu_);
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ tool->Flush();
+ }
+}
+
+const char *Symbolizer::Demangle(const char *name) {
+ BlockingMutexLock l(&mu_);
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ if (const char *demangled = tool->Demangle(name))
+ return demangled;
+ }
+ return PlatformDemangle(name);
+}
+
+void Symbolizer::PrepareForSandboxing() {
+ BlockingMutexLock l(&mu_);
+ PlatformPrepareForSandboxing();
+}
+
+bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address,
+ const char **module_name,
+ uptr *module_offset) {
+ LoadedModule *module = FindModuleForAddress(address);
+ if (module == 0)
+ return false;
+ *module_name = module->full_name();
+ *module_offset = address - module->base_address();
+ return true;
+}
+
+LoadedModule *Symbolizer::FindModuleForAddress(uptr address) {
+ bool modules_were_reloaded = false;
+ if (!modules_fresh_) {
+ for (uptr i = 0; i < n_modules_; i++)
+ modules_[i].clear();
+ n_modules_ =
+ GetListOfModules(modules_, kMaxNumberOfModules, /* filter */ nullptr);
+ CHECK_GT(n_modules_, 0);
+ CHECK_LT(n_modules_, kMaxNumberOfModules);
+ modules_fresh_ = true;
+ modules_were_reloaded = true;
+ }
+ for (uptr i = 0; i < n_modules_; i++) {
+ if (modules_[i].containsAddress(address)) {
+ return &modules_[i];
+ }
+ }
+ // Reload the modules and look up again, if we haven't tried it yet.
+ if (!modules_were_reloaded) {
+ // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors.
+ // It's too aggressive to reload the list of modules each time we fail
+ // to find a module for a given address.
+ modules_fresh_ = false;
+ return FindModuleForAddress(address);
+ }
+ return 0;
}
Symbolizer *Symbolizer::GetOrInit() {
SpinMutexLock l(&init_mu_);
- if (symbolizer_ == 0)
- return CreateAndStore(0);
+ if (symbolizer_)
+ return symbolizer_;
+ symbolizer_ = PlatformInit();
+ CHECK(symbolizer_);
return symbolizer_;
}
+// For now we assume the following protocol:
+// For each request of the form
+// <module_name> <module_offset>
+// passed to STDIN, external symbolizer prints to STDOUT response:
+// <function_name>
+// <file_name>:<line_number>:<column_number>
+// <function_name>
+// <file_name>:<line_number>:<column_number>
+// ...
+// <empty line>
+class LLVMSymbolizerProcess : public SymbolizerProcess {
+ public:
+ explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {}
+
+ private:
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
+ // Empty line marks the end of llvm-symbolizer output.
+ return length >= 2 && buffer[length - 1] == '\n' &&
+ buffer[length - 2] == '\n';
+ }
+
+ void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const override {
+#if defined(__x86_64h__)
+ const char* const kSymbolizerArch = "--default-arch=x86_64h";
+#elif defined(__x86_64__)
+ const char* const kSymbolizerArch = "--default-arch=x86_64";
+#elif defined(__i386__)
+ const char* const kSymbolizerArch = "--default-arch=i386";
+#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__)
+ const char* const kSymbolizerArch = "--default-arch=powerpc64";
+#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
+ const char* const kSymbolizerArch = "--default-arch=powerpc64le";
+#else
+ const char* const kSymbolizerArch = "--default-arch=unknown";
+#endif
+
+ const char *const inline_flag = common_flags()->symbolize_inline_frames
+ ? "--inlining=true"
+ : "--inlining=false";
+ int i = 0;
+ argv[i++] = path_to_binary;
+ argv[i++] = inline_flag;
+ argv[i++] = kSymbolizerArch;
+ argv[i++] = nullptr;
+ }
+};
+
+LLVMSymbolizer::LLVMSymbolizer(const char *path, LowLevelAllocator *allocator)
+ : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {}
+
+// Parse a <file>:<line>[:<column>] buffer. The file path may contain colons on
+// Windows, so extract tokens from the right hand side first. The column info is
+// also optional.
+static const char *ParseFileLineInfo(AddressInfo *info, const char *str) {
+ char *file_line_info = 0;
+ str = ExtractToken(str, "\n", &file_line_info);
+ CHECK(file_line_info);
+ // Parse the last :<int>, which must be there.
+ char *last_colon = internal_strrchr(file_line_info, ':');
+ CHECK(last_colon);
+ int line_or_column = internal_atoll(last_colon + 1);
+ // Truncate the string at the last colon and find the next-to-last colon.
+ *last_colon = '\0';
+ last_colon = internal_strrchr(file_line_info, ':');
+ if (last_colon && IsDigit(last_colon[1])) {
+ // If the second-to-last colon is followed by a digit, it must be the line
+ // number, and the previous parsed number was a column.
+ info->line = internal_atoll(last_colon + 1);
+ info->column = line_or_column;
+ *last_colon = '\0';
+ } else {
+ // Otherwise, we have line info but no column info.
+ info->line = line_or_column;
+ info->column = 0;
+ }
+ ExtractToken(file_line_info, "", &info->file);
+ InternalFree(file_line_info);
+ return str;
+}
+
+// Parses one or more two-line strings in the following format:
+// <function_name>
+// <file_name>:<line_number>[:<column_number>]
+// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of
+// them use the same output format.
+void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) {
+ bool top_frame = true;
+ SymbolizedStack *last = res;
+ while (true) {
+ char *function_name = 0;
+ str = ExtractToken(str, "\n", &function_name);
+ CHECK(function_name);
+ if (function_name[0] == '\0') {
+ // There are no more frames.
+ InternalFree(function_name);
+ break;
+ }
+ SymbolizedStack *cur;
+ if (top_frame) {
+ cur = res;
+ top_frame = false;
+ } else {
+ cur = SymbolizedStack::New(res->info.address);
+ cur->info.FillModuleInfo(res->info.module, res->info.module_offset);
+ last->next = cur;
+ last = cur;
+ }
+
+ AddressInfo *info = &cur->info;
+ info->function = function_name;
+ str = ParseFileLineInfo(info, str);
+
+ // Functions and filenames can be "??", in which case we write 0
+ // to address info to mark that names are unknown.
+ if (0 == internal_strcmp(info->function, "??")) {
+ InternalFree(info->function);
+ info->function = 0;
+ }
+ if (0 == internal_strcmp(info->file, "??")) {
+ InternalFree(info->file);
+ info->file = 0;
+ }
+ }
+}
+
+// Parses a two-line string in the following format:
+// <symbol_name>
+// <start_address> <size>
+// Used by LLVMSymbolizer and InternalSymbolizer.
+void ParseSymbolizeDataOutput(const char *str, DataInfo *info) {
+ str = ExtractToken(str, "\n", &info->name);
+ str = ExtractUptr(str, " ", &info->start);
+ str = ExtractUptr(str, "\n", &info->size);
+}
+
+bool LLVMSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module,
+ stack->info.module_offset)) {
+ ParseSymbolizePCOutput(buf, stack);
+ return true;
+ }
+ return false;
+}
+
+bool LLVMSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ if (const char *buf =
+ SendCommand(/*is_data*/ true, info->module, info->module_offset)) {
+ ParseSymbolizeDataOutput(buf, info);
+ info->start += (addr - info->module_offset); // Add the base address.
+ return true;
+ }
+ return false;
+}
+
+const char *LLVMSymbolizer::SendCommand(bool is_data, const char *module_name,
+ uptr module_offset) {
+ CHECK(module_name);
+ internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n",
+ is_data ? "DATA " : "", module_name, module_offset);
+ return symbolizer_process_->SendCommand(buffer_);
+}
+
+SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty)
+ : path_(path),
+ input_fd_(kInvalidFd),
+ output_fd_(kInvalidFd),
+ times_restarted_(0),
+ failed_to_start_(false),
+ reported_invalid_path_(false),
+ use_forkpty_(use_forkpty) {
+ CHECK(path_);
+ CHECK_NE(path_[0], '\0');
+}
+
+const char *SymbolizerProcess::SendCommand(const char *command) {
+ for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
+ // Start or restart symbolizer if we failed to send command to it.
+ if (const char *res = SendCommandImpl(command))
+ return res;
+ Restart();
+ }
+ if (!failed_to_start_) {
+ Report("WARNING: Failed to use and restart external symbolizer!\n");
+ failed_to_start_ = true;
+ }
+ return 0;
+}
+
+const char *SymbolizerProcess::SendCommandImpl(const char *command) {
+ if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
+ return 0;
+ if (!WriteToSymbolizer(command, internal_strlen(command)))
+ return 0;
+ if (!ReadFromSymbolizer(buffer_, kBufferSize))
+ return 0;
+ return buffer_;
+}
+
+bool SymbolizerProcess::Restart() {
+ if (input_fd_ != kInvalidFd)
+ CloseFile(input_fd_);
+ if (output_fd_ != kInvalidFd)
+ CloseFile(output_fd_);
+ return StartSymbolizerSubprocess();
+}
+
+bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) {
+ if (max_length == 0)
+ return true;
+ uptr read_len = 0;
+ while (true) {
+ uptr just_read = 0;
+ bool success = ReadFromFile(input_fd_, buffer + read_len,
+ max_length - read_len - 1, &just_read);
+ // We can't read 0 bytes, as we don't expect external symbolizer to close
+ // its stdout.
+ if (!success || just_read == 0) {
+ Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
+ return false;
+ }
+ read_len += just_read;
+ if (ReachedEndOfOutput(buffer, read_len))
+ break;
+ }
+ buffer[read_len] = '\0';
+ return true;
+}
+
+bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) {
+ if (length == 0)
+ return true;
+ uptr write_len = 0;
+ bool success = WriteToFile(output_fd_, buffer, length, &write_len);
+ if (!success || write_len != length) {
+ Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
+ return false;
+ }
+ return true;
+}
+
} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_symbolizer_mac.cc ---------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries.
+//
+// Implementation of Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_mac.h"
+#include "sanitizer_symbolizer_mac.h"
+
+namespace __sanitizer {
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <util.h>
+
+bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ Dl_info info;
+ int result = dladdr((const void *)addr, &info);
+ if (!result) return false;
+ const char *demangled = DemangleCXXABI(info.dli_sname);
+ stack->info.function = internal_strdup(demangled);
+ return true;
+}
+
+bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
+ Dl_info info;
+ int result = dladdr((const void *)addr, &info);
+ if (!result) return false;
+ const char *demangled = DemangleCXXABI(info.dli_sname);
+ datainfo->name = internal_strdup(demangled);
+ datainfo->start = (uptr)info.dli_saddr;
+ return true;
+}
+
+class AtosSymbolizerProcess : public SymbolizerProcess {
+ public:
+ explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
+ : SymbolizerProcess(path, /*use_forkpty*/ true) {
+ // Put the string command line argument in the object so that it outlives
+ // the call to GetArgV.
+ internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
+ }
+
+ private:
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
+ return (length >= 1 && buffer[length - 1] == '\n');
+ }
+
+ void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const override {
+ int i = 0;
+ argv[i++] = path_to_binary;
+ argv[i++] = "-p";
+ argv[i++] = &pid_str_[0];
+ if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
+ // On Mavericks atos prints a deprecation warning which we suppress by
+ // passing -d. The warning isn't present on other OSX versions, even the
+ // newer ones.
+ argv[i++] = "-d";
+ }
+ argv[i++] = nullptr;
+ }
+
+ char pid_str_[16];
+};
+
+static const char *kAtosErrorMessages[] = {
+ "atos cannot examine process",
+ "unable to get permission to examine process",
+ "An admin user name and password is required",
+ "could not load inserted library",
+ "architecture mismatch between analysis process",
+};
+
+static bool IsAtosErrorMessage(const char *str) {
+ for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
+ if (internal_strstr(str, kAtosErrorMessages[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
+ char **out_module, char **out_file, uptr *line,
+ uptr *start_address) {
+ // Trim ending newlines.
+ char *trim;
+ ExtractTokenUpToDelimiter(str, "\n", &trim);
+
+ // The line from `atos` is in one of these formats:
+ // myfunction (in library.dylib) (sourcefile.c:17)
+ // myfunction (in library.dylib) + 0x1fe
+ // myfunction (in library.dylib) + 15
+ // 0xdeadbeef (in library.dylib) + 0x1fe
+ // 0xdeadbeef (in library.dylib) + 15
+ // 0xdeadbeef (in library.dylib)
+ // 0xdeadbeef
+
+ if (IsAtosErrorMessage(trim)) {
+ Report("atos returned an error: %s\n", trim);
+ InternalFree(trim);
+ return false;
+ }
+
+ const char *rest = trim;
+ char *symbol_name;
+ rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
+ if (internal_strncmp(symbol_name, "0x", 2) != 0)
+ *out_name = symbol_name;
+ else
+ InternalFree(symbol_name);
+ rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
+
+ if (rest[0] == '(') {
+ if (out_file) {
+ rest++;
+ rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
+ char *extracted_line_number;
+ rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
+ if (line) *line = (uptr)internal_atoll(extracted_line_number);
+ InternalFree(extracted_line_number);
+ }
+ } else if (rest[0] == '+') {
+ rest += 2;
+ uptr offset = internal_atoll(rest);
+ if (start_address) *start_address = addr - offset;
+ }
+
+ InternalFree(trim);
+ return true;
+}
+
+AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
+ : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
+
+bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ if (!process_) return false;
+ char command[32];
+ internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
+ const char *buf = process_->SendCommand(command);
+ if (!buf) return false;
+ uptr line;
+ if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
+ &stack->info.file, &line, nullptr)) {
+ process_ = nullptr;
+ return false;
+ }
+ stack->info.line = (int)line;
+ return true;
+}
+
+bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ if (!process_) return false;
+ char command[32];
+ internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
+ const char *buf = process_->SendCommand(command);
+ if (!buf) return false;
+ if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
+ nullptr, &info->start)) {
+ process_ = nullptr;
+ return false;
+ }
+ return true;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
--- /dev/null
+//===-- sanitizer_symbolizer_mac.h ------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries.
+//
+// Header for Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_SYMBOLIZER_MAC_H
+#define SANITIZER_SYMBOLIZER_MAC_H
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+class DlAddrSymbolizer : public SymbolizerTool {
+ public:
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
+};
+
+class AtosSymbolizerProcess;
+
+class AtosSymbolizer : public SymbolizerTool {
+ public:
+ explicit AtosSymbolizer(const char *path, LowLevelAllocator *allocator);
+
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
+
+ private:
+ AtosSymbolizerProcess *process_;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
+
+#endif // SANITIZER_SYMBOLIZER_MAC_H
#if SANITIZER_POSIX
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_linux.h"
#include "sanitizer_placement_new.h"
+#include "sanitizer_posix.h"
#include "sanitizer_procmaps.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
#include "sanitizer_symbolizer_libbacktrace.h"
+#include "sanitizer_symbolizer_mac.h"
#include <errno.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
+#if SANITIZER_MAC
+#include <util.h> // for forkpty()
+#endif // SANITIZER_MAC
+
// C++ demangling function, as required by Itanium C++ ABI. This is weak,
// because we do not require a C++ ABI library to be linked to a program
// using sanitizers; if it's not present, we'll just use the mangled name.
namespace __sanitizer {
// Attempts to demangle the name via __cxa_demangle from __cxxabiv1.
-static const char *DemangleCXXABI(const char *name) {
+const char *DemangleCXXABI(const char *name) {
// FIXME: __cxa_demangle aggressively insists on allocating memory.
// There's not much we can do about that, short of providing our
// own demangler (libc++abi's implementation could be adapted so that
return name;
}
-#if defined(__x86_64__)
-static const char* const kSymbolizerArch = "--default-arch=x86_64";
-#elif defined(__i386__)
-static const char* const kSymbolizerArch = "--default-arch=i386";
-#elif defined(__powerpc64__)
-static const char* const kSymbolizerArch = "--default-arch=powerpc64";
-#else
-static const char* const kSymbolizerArch = "--default-arch=unknown";
-#endif
-
-static const int kSymbolizerStartupTimeMillis = 10;
-
-// Creates external symbolizer connected via pipe, user should write
-// to output_fd and read from input_fd.
-static bool StartSymbolizerSubprocess(const char *path_to_symbolizer,
- int *input_fd, int *output_fd) {
- if (!FileExists(path_to_symbolizer)) {
- Report("WARNING: invalid path to external symbolizer!\n");
+bool SymbolizerProcess::StartSymbolizerSubprocess() {
+ if (!FileExists(path_)) {
+ if (!reported_invalid_path_) {
+ Report("WARNING: invalid path to external symbolizer!\n");
+ reported_invalid_path_ = true;
+ }
return false;
}
- int *infd = NULL;
- int *outfd = NULL;
- // The client program may close its stdin and/or stdout and/or stderr
- // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
- // In this case the communication between the forked processes may be
- // broken if either the parent or the child tries to close or duplicate
- // these descriptors. The loop below produces two pairs of file
- // descriptors, each greater than 2 (stderr).
- int sock_pair[5][2];
- for (int i = 0; i < 5; i++) {
- if (pipe(sock_pair[i]) == -1) {
- for (int j = 0; j < i; j++) {
- internal_close(sock_pair[j][0]);
- internal_close(sock_pair[j][1]);
- }
- Report("WARNING: Can't create a socket pair to start "
- "external symbolizer (errno: %d)\n", errno);
+ int pid;
+ if (use_forkpty_) {
+#if SANITIZER_MAC
+ fd_t fd = kInvalidFd;
+ // Use forkpty to disable buffering in the new terminal.
+ pid = forkpty(&fd, 0, 0, 0);
+ if (pid == -1) {
+ // forkpty() failed.
+ Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
+ errno);
return false;
- } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
- if (infd == NULL) {
- infd = sock_pair[i];
- } else {
- outfd = sock_pair[i];
+ } else if (pid == 0) {
+ // Child subprocess.
+ const char *argv[kArgVMax];
+ GetArgV(path_, argv);
+ execv(path_, const_cast<char **>(&argv[0]));
+ internal__exit(1);
+ }
+
+ // Continue execution in parent process.
+ input_fd_ = output_fd_ = fd;
+
+ // Disable echo in the new terminal, disable CR.
+ struct termios termflags;
+ tcgetattr(fd, &termflags);
+ termflags.c_oflag &= ~ONLCR;
+ termflags.c_lflag &= ~ECHO;
+ tcsetattr(fd, TCSANOW, &termflags);
+#else // SANITIZER_MAC
+ UNIMPLEMENTED();
+#endif // SANITIZER_MAC
+ } else {
+ int *infd = NULL;
+ int *outfd = NULL;
+ // The client program may close its stdin and/or stdout and/or stderr
+ // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
+ // In this case the communication between the forked processes may be
+ // broken if either the parent or the child tries to close or duplicate
+ // these descriptors. The loop below produces two pairs of file
+ // descriptors, each greater than 2 (stderr).
+ int sock_pair[5][2];
+ for (int i = 0; i < 5; i++) {
+ if (pipe(sock_pair[i]) == -1) {
for (int j = 0; j < i; j++) {
- if (sock_pair[j] == infd) continue;
internal_close(sock_pair[j][0]);
internal_close(sock_pair[j][1]);
}
- break;
+ Report("WARNING: Can't create a socket pair to start "
+ "external symbolizer (errno: %d)\n", errno);
+ return false;
+ } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
+ if (infd == NULL) {
+ infd = sock_pair[i];
+ } else {
+ outfd = sock_pair[i];
+ for (int j = 0; j < i; j++) {
+ if (sock_pair[j] == infd) continue;
+ internal_close(sock_pair[j][0]);
+ internal_close(sock_pair[j][1]);
+ }
+ break;
+ }
}
}
- }
- CHECK(infd);
- CHECK(outfd);
+ CHECK(infd);
+ CHECK(outfd);
+
+ // Real fork() may call user callbacks registered with pthread_atfork().
+ pid = internal_fork();
+ if (pid == -1) {
+ // Fork() failed.
+ internal_close(infd[0]);
+ internal_close(infd[1]);
+ internal_close(outfd[0]);
+ internal_close(outfd[1]);
+ Report("WARNING: failed to fork external symbolizer "
+ " (errno: %d)\n", errno);
+ return false;
+ } else if (pid == 0) {
+ // Child subprocess.
+ internal_close(STDOUT_FILENO);
+ internal_close(STDIN_FILENO);
+ internal_dup2(outfd[0], STDIN_FILENO);
+ internal_dup2(infd[1], STDOUT_FILENO);
+ internal_close(outfd[0]);
+ internal_close(outfd[1]);
+ internal_close(infd[0]);
+ internal_close(infd[1]);
+ for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--)
+ internal_close(fd);
+ const char *argv[kArgVMax];
+ GetArgV(path_, argv);
+ execv(path_, const_cast<char **>(&argv[0]));
+ internal__exit(1);
+ }
- int pid = fork();
- if (pid == -1) {
- // Fork() failed.
- internal_close(infd[0]);
- internal_close(infd[1]);
+ // Continue execution in parent process.
internal_close(outfd[0]);
- internal_close(outfd[1]);
- Report("WARNING: failed to fork external symbolizer "
- " (errno: %d)\n", errno);
- return false;
- } else if (pid == 0) {
- // Child subprocess.
- internal_close(STDOUT_FILENO);
- internal_close(STDIN_FILENO);
- internal_dup2(outfd[0], STDIN_FILENO);
- internal_dup2(infd[1], STDOUT_FILENO);
- internal_close(outfd[0]);
- internal_close(outfd[1]);
- internal_close(infd[0]);
internal_close(infd[1]);
- for (int fd = getdtablesize(); fd > 2; fd--)
- internal_close(fd);
- execl(path_to_symbolizer, path_to_symbolizer, kSymbolizerArch, (char*)0);
- internal__exit(1);
+ input_fd_ = infd[0];
+ output_fd_ = outfd[1];
}
- // Continue execution in parent process.
- internal_close(outfd[0]);
- internal_close(infd[1]);
- *input_fd = infd[0];
- *output_fd = outfd[1];
-
// Check that symbolizer subprocess started successfully.
int pid_status;
SleepForMillis(kSymbolizerStartupTimeMillis);
return true;
}
-// Extracts the prefix of "str" that consists of any characters not
-// present in "delims" string, and copies this prefix to "result", allocating
-// space for it.
-// Returns a pointer to "str" after skipping extracted prefix and first
-// delimiter char.
-static const char *ExtractToken(const char *str, const char *delims,
- char **result) {
- uptr prefix_len = internal_strcspn(str, delims);
- *result = (char*)InternalAlloc(prefix_len + 1);
- internal_memcpy(*result, str, prefix_len);
- (*result)[prefix_len] = '\0';
- const char *prefix_end = str + prefix_len;
- if (*prefix_end != '\0') prefix_end++;
- return prefix_end;
-}
+class Addr2LineProcess : public SymbolizerProcess {
+ public:
+ Addr2LineProcess(const char *path, const char *module_name)
+ : SymbolizerProcess(path), module_name_(internal_strdup(module_name)) {}
+
+ const char *module_name() const { return module_name_; }
-// Same as ExtractToken, but converts extracted token to integer.
-static const char *ExtractInt(const char *str, const char *delims,
- int *result) {
- char *buff;
- const char *ret = ExtractToken(str, delims, &buff);
- if (buff != 0) {
- *result = (int)internal_atoll(buff);
+ private:
+ void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const override {
+ int i = 0;
+ argv[i++] = path_to_binary;
+ argv[i++] = "-iCfe";
+ argv[i++] = module_name_;
+ argv[i++] = nullptr;
}
- InternalFree(buff);
- return ret;
-}
-static const char *ExtractUptr(const char *str, const char *delims,
- uptr *result) {
- char *buff;
- const char *ret = ExtractToken(str, delims, &buff);
- if (buff != 0) {
- *result = (uptr)internal_atoll(buff);
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override;
+
+ bool ReadFromSymbolizer(char *buffer, uptr max_length) override {
+ if (!SymbolizerProcess::ReadFromSymbolizer(buffer, max_length))
+ return false;
+ // We should cut out output_terminator_ at the end of given buffer,
+ // appended by addr2line to mark the end of its meaningful output.
+ // We cannot scan buffer from it's beginning, because it is legal for it
+ // to start with output_terminator_ in case given offset is invalid. So,
+ // scanning from second character.
+ char *garbage = internal_strstr(buffer + 1, output_terminator_);
+ // This should never be NULL since buffer must end up with
+ // output_terminator_.
+ CHECK(garbage);
+ // Trim the buffer.
+ garbage[0] = '\0';
+ return true;
}
- InternalFree(buff);
- return ret;
+
+ const char *module_name_; // Owned, leaked.
+ static const char output_terminator_[];
+};
+
+const char Addr2LineProcess::output_terminator_[] = "??\n??:0\n";
+
+bool Addr2LineProcess::ReachedEndOfOutput(const char *buffer,
+ uptr length) const {
+ const size_t kTerminatorLen = sizeof(output_terminator_) - 1;
+ // Skip, if we read just kTerminatorLen bytes, because Addr2Line output
+ // should consist at least of two pairs of lines:
+ // 1. First one, corresponding to given offset to be symbolized
+ // (may be equal to output_terminator_, if offset is not valid).
+ // 2. Second one for output_terminator_, itself to mark the end of output.
+ if (length <= kTerminatorLen) return false;
+ // Addr2Line output should end up with output_terminator_.
+ return !internal_memcmp(buffer + length - kTerminatorLen,
+ output_terminator_, kTerminatorLen);
}
-// ExternalSymbolizer encapsulates communication between the tool and
-// external symbolizer program, running in a different subprocess,
-// For now we assume the following protocol:
-// For each request of the form
-// <module_name> <module_offset>
-// passed to STDIN, external symbolizer prints to STDOUT response:
-// <function_name>
-// <file_name>:<line_number>:<column_number>
-// <function_name>
-// <file_name>:<line_number>:<column_number>
-// ...
-// <empty line>
-// ExternalSymbolizer may not be used from two threads simultaneously.
-class ExternalSymbolizer {
+class Addr2LinePool : public SymbolizerTool {
public:
- explicit ExternalSymbolizer(const char *path)
- : path_(path),
- input_fd_(kInvalidFd),
- output_fd_(kInvalidFd),
- times_restarted_(0),
- failed_to_start_(false) {
- CHECK(path_);
- CHECK_NE(path[0], '\0');
- }
-
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
- // Start or restart symbolizer if we failed to send command to it.
- if (char *res = SendCommandImpl(is_data, module_name, module_offset))
- return res;
- Restart();
- }
- if (!failed_to_start_) {
- Report("WARNING: Failed to use and restart external symbolizer!\n");
- failed_to_start_ = true;
+ explicit Addr2LinePool(const char *addr2line_path,
+ LowLevelAllocator *allocator)
+ : addr2line_path_(addr2line_path), allocator_(allocator),
+ addr2line_pool_(16) {}
+
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+ if (const char *buf =
+ SendCommand(stack->info.module, stack->info.module_offset)) {
+ ParseSymbolizePCOutput(buf, stack);
+ return true;
}
- return 0;
+ return false;
}
- void Flush() {
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ return false;
}
private:
- bool Restart() {
- if (input_fd_ != kInvalidFd)
- internal_close(input_fd_);
- if (output_fd_ != kInvalidFd)
- internal_close(output_fd_);
- return StartSymbolizerSubprocess(path_, &input_fd_, &output_fd_);
- }
-
- char *SendCommandImpl(bool is_data, const char *module_name,
- uptr module_offset) {
- if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
- return 0;
- CHECK(module_name);
- internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n",
- is_data ? "DATA " : "", module_name, module_offset);
- if (!writeToSymbolizer(buffer_, internal_strlen(buffer_)))
- return 0;
- if (!readFromSymbolizer(buffer_, kBufferSize))
- return 0;
- return buffer_;
- }
-
- bool readFromSymbolizer(char *buffer, uptr max_length) {
- if (max_length == 0)
- return true;
- uptr read_len = 0;
- while (true) {
- uptr just_read = internal_read(input_fd_, buffer + read_len,
- max_length - read_len);
- // We can't read 0 bytes, as we don't expect external symbolizer to close
- // its stdout.
- if (just_read == 0 || just_read == (uptr)-1) {
- Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
- return false;
- }
- read_len += just_read;
- // Empty line marks the end of symbolizer output.
- if (read_len >= 2 && buffer[read_len - 1] == '\n' &&
- buffer[read_len - 2] == '\n') {
+ const char *SendCommand(const char *module_name, uptr module_offset) {
+ Addr2LineProcess *addr2line = 0;
+ for (uptr i = 0; i < addr2line_pool_.size(); ++i) {
+ if (0 ==
+ internal_strcmp(module_name, addr2line_pool_[i]->module_name())) {
+ addr2line = addr2line_pool_[i];
break;
}
}
- return true;
- }
-
- bool writeToSymbolizer(const char *buffer, uptr length) {
- if (length == 0)
- return true;
- uptr write_len = internal_write(output_fd_, buffer, length);
- if (write_len == 0 || write_len == (uptr)-1) {
- Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
- return false;
+ if (!addr2line) {
+ addr2line =
+ new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);
+ addr2line_pool_.push_back(addr2line);
}
- return true;
+ CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name()));
+ char buffer[kBufferSize];
+ internal_snprintf(buffer, kBufferSize, "0x%zx\n0x%zx\n",
+ module_offset, dummy_address_);
+ return addr2line->SendCommand(buffer);
}
- const char *path_;
- int input_fd_;
- int output_fd_;
-
- static const uptr kBufferSize = 16 * 1024;
- char buffer_[kBufferSize];
-
- static const uptr kMaxTimesRestarted = 5;
- uptr times_restarted_;
- bool failed_to_start_;
+ static const uptr kBufferSize = 64;
+ const char *addr2line_path_;
+ LowLevelAllocator *allocator_;
+ InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
+ static const uptr dummy_address_ =
+ FIRST_32_SECOND_64(UINT32_MAX, UINT64_MAX);
};
#if SANITIZER_SUPPORTS_WEAK_HOOKS
int MaxLength);
} // extern "C"
-class InternalSymbolizer {
+class InternalSymbolizer : public SymbolizerTool {
public:
- typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int);
-
static InternalSymbolizer *get(LowLevelAllocator *alloc) {
if (__sanitizer_symbolize_code != 0 &&
__sanitizer_symbolize_data != 0) {
return 0;
}
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data
- : __sanitizer_symbolize_code;
- if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize))
- return buffer_;
- return 0;
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+ bool result = __sanitizer_symbolize_code(
+ stack->info.module, stack->info.module_offset, buffer_, kBufferSize);
+ if (result) ParseSymbolizePCOutput(buffer_, stack);
+ return result;
}
- void Flush() {
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ bool result = __sanitizer_symbolize_data(info->module, info->module_offset,
+ buffer_, kBufferSize);
+ if (result) {
+ ParseSymbolizeDataOutput(buffer_, info);
+ info->start += (addr - info->module_offset); // Add the base address.
+ }
+ return result;
+ }
+
+ void Flush() override {
if (__sanitizer_symbolize_flush)
__sanitizer_symbolize_flush();
}
- const char *Demangle(const char *name) {
+ const char *Demangle(const char *name) override {
if (__sanitizer_symbolize_demangle) {
for (uptr res_length = 1024;
res_length <= InternalSizeClassMap::kMaxSize;) {
};
#else // SANITIZER_SUPPORTS_WEAK_HOOKS
-class InternalSymbolizer {
+class InternalSymbolizer : public SymbolizerTool {
public:
static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; }
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- return 0;
- }
- void Flush() { }
- const char *Demangle(const char *name) { return name; }
};
#endif // SANITIZER_SUPPORTS_WEAK_HOOKS
-class POSIXSymbolizer : public Symbolizer {
- public:
- POSIXSymbolizer(ExternalSymbolizer *external_symbolizer,
- InternalSymbolizer *internal_symbolizer,
- LibbacktraceSymbolizer *libbacktrace_symbolizer)
- : Symbolizer(),
- external_symbolizer_(external_symbolizer),
- internal_symbolizer_(internal_symbolizer),
- libbacktrace_symbolizer_(libbacktrace_symbolizer) {}
-
- uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) {
- BlockingMutexLock l(&mu_);
- if (max_frames == 0)
- return 0;
- LoadedModule *module = FindModuleForAddress(addr);
- if (module == 0)
- return 0;
- const char *module_name = module->full_name();
- uptr module_offset = addr - module->base_address();
- // First, try to use libbacktrace symbolizer (if it's available).
- if (libbacktrace_symbolizer_ != 0) {
- mu_.CheckLocked();
- uptr res = libbacktrace_symbolizer_->SymbolizeCode(
- addr, frames, max_frames, module_name, module_offset);
- if (res > 0)
- return res;
- }
- const char *str = SendCommand(false, module_name, module_offset);
- if (str == 0) {
- // Symbolizer was not initialized or failed. Fill only data
- // about module name and offset.
- AddressInfo *info = &frames[0];
- info->Clear();
- info->FillAddressAndModuleInfo(addr, module_name, module_offset);
- return 1;
- }
- uptr frame_id = 0;
- for (frame_id = 0; frame_id < max_frames; frame_id++) {
- AddressInfo *info = &frames[frame_id];
- char *function_name = 0;
- str = ExtractToken(str, "\n", &function_name);
- CHECK(function_name);
- if (function_name[0] == '\0') {
- // There are no more frames.
- break;
- }
- info->Clear();
- info->FillAddressAndModuleInfo(addr, module_name, module_offset);
- info->function = function_name;
- // Parse <file>:<line>:<column> buffer.
- char *file_line_info = 0;
- str = ExtractToken(str, "\n", &file_line_info);
- CHECK(file_line_info);
- const char *line_info = ExtractToken(file_line_info, ":", &info->file);
- line_info = ExtractInt(line_info, ":", &info->line);
- line_info = ExtractInt(line_info, "", &info->column);
- InternalFree(file_line_info);
-
- // Functions and filenames can be "??", in which case we write 0
- // to address info to mark that names are unknown.
- if (0 == internal_strcmp(info->function, "??")) {
- InternalFree(info->function);
- info->function = 0;
- }
- if (0 == internal_strcmp(info->file, "??")) {
- InternalFree(info->file);
- info->file = 0;
- }
- }
- if (frame_id == 0) {
- // Make sure we return at least one frame.
- AddressInfo *info = &frames[0];
- info->Clear();
- info->FillAddressAndModuleInfo(addr, module_name, module_offset);
- frame_id = 1;
- }
- return frame_id;
- }
+const char *Symbolizer::PlatformDemangle(const char *name) {
+ return DemangleCXXABI(name);
+}
- bool SymbolizeData(uptr addr, DataInfo *info) {
- BlockingMutexLock l(&mu_);
- LoadedModule *module = FindModuleForAddress(addr);
- if (module == 0)
- return false;
- const char *module_name = module->full_name();
- uptr module_offset = addr - module->base_address();
- internal_memset(info, 0, sizeof(*info));
- info->address = addr;
- info->module = internal_strdup(module_name);
- info->module_offset = module_offset;
- if (libbacktrace_symbolizer_ != 0) {
- mu_.CheckLocked();
- if (libbacktrace_symbolizer_->SymbolizeData(info))
- return true;
- }
- const char *str = SendCommand(true, module_name, module_offset);
- if (str == 0)
- return true;
- str = ExtractToken(str, "\n", &info->name);
- str = ExtractUptr(str, " ", &info->start);
- str = ExtractUptr(str, "\n", &info->size);
- info->start += module->base_address();
- return true;
+void Symbolizer::PlatformPrepareForSandboxing() {}
+
+static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) {
+ const char *path = common_flags()->external_symbolizer_path;
+ const char *binary_name = path ? StripModuleName(path) : "";
+ if (path && path[0] == '\0') {
+ VReport(2, "External symbolizer is explicitly disabled.\n");
+ return nullptr;
+ } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) {
+ VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path);
+ return new(*allocator) LLVMSymbolizer(path, allocator);
+ } else if (!internal_strcmp(binary_name, "atos")) {
+#if SANITIZER_MAC
+ VReport(2, "Using atos at user-specified path: %s\n", path);
+ return new(*allocator) AtosSymbolizer(path, allocator);
+#else // SANITIZER_MAC
+ Report("ERROR: Using `atos` is only supported on Darwin.\n");
+ Die();
+#endif // SANITIZER_MAC
+ } else if (!internal_strcmp(binary_name, "addr2line")) {
+ VReport(2, "Using addr2line at user-specified path: %s\n", path);
+ return new(*allocator) Addr2LinePool(path, allocator);
+ } else if (path) {
+ Report("ERROR: External symbolizer path is set to '%s' which isn't "
+ "a known symbolizer. Please set the path to the llvm-symbolizer "
+ "binary or other known tool.\n", path);
+ Die();
}
- bool IsAvailable() {
- return internal_symbolizer_ != 0 || external_symbolizer_ != 0 ||
- libbacktrace_symbolizer_ != 0;
+ // Otherwise symbolizer program is unknown, let's search $PATH
+ CHECK(path == nullptr);
+ if (const char *found_path = FindPathToBinary("llvm-symbolizer")) {
+ VReport(2, "Using llvm-symbolizer found at: %s\n", found_path);
+ return new(*allocator) LLVMSymbolizer(found_path, allocator);
}
-
- bool IsExternalAvailable() {
- return external_symbolizer_ != 0;
+#if SANITIZER_MAC
+ if (const char *found_path = FindPathToBinary("atos")) {
+ VReport(2, "Using atos found at: %s\n", found_path);
+ return new(*allocator) AtosSymbolizer(found_path, allocator);
}
-
- void Flush() {
- BlockingMutexLock l(&mu_);
- if (internal_symbolizer_ != 0) {
- SymbolizerScope sym_scope(this);
- internal_symbolizer_->Flush();
+#endif // SANITIZER_MAC
+ if (common_flags()->allow_addr2line) {
+ if (const char *found_path = FindPathToBinary("addr2line")) {
+ VReport(2, "Using addr2line found at: %s\n", found_path);
+ return new(*allocator) Addr2LinePool(found_path, allocator);
}
- if (external_symbolizer_ != 0)
- external_symbolizer_->Flush();
}
+ return nullptr;
+}
- const char *Demangle(const char *name) {
- BlockingMutexLock l(&mu_);
- // Run hooks even if we don't use internal symbolizer, as cxxabi
- // demangle may call system functions.
- SymbolizerScope sym_scope(this);
- if (internal_symbolizer_ != 0)
- return internal_symbolizer_->Demangle(name);
- if (libbacktrace_symbolizer_ != 0) {
- const char *demangled = libbacktrace_symbolizer_->Demangle(name);
- if (demangled)
- return demangled;
- }
- return DemangleCXXABI(name);
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+ LowLevelAllocator *allocator) {
+ if (!common_flags()->symbolize) {
+ VReport(2, "Symbolizer is disabled.\n");
+ return;
}
-
- void PrepareForSandboxing() {
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
- BlockingMutexLock l(&mu_);
- // Cache /proc/self/exe on Linux.
- CacheBinaryName();
-#endif
+ if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
+ VReport(2, "Using internal symbolizer.\n");
+ list->push_back(tool);
+ return;
}
-
- private:
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- mu_.CheckLocked();
- // First, try to use internal symbolizer.
- if (internal_symbolizer_) {
- SymbolizerScope sym_scope(this);
- return internal_symbolizer_->SendCommand(is_data, module_name,
- module_offset);
- }
- // Otherwise, fall back to external symbolizer.
- if (external_symbolizer_) {
- return external_symbolizer_->SendCommand(is_data, module_name,
- module_offset);
- }
- return 0;
+ if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) {
+ VReport(2, "Using libbacktrace symbolizer.\n");
+ list->push_back(tool);
+ return;
}
- LoadedModule *FindModuleForAddress(uptr address) {
- mu_.CheckLocked();
- bool modules_were_reloaded = false;
- if (modules_ == 0 || !modules_fresh_) {
- modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate(
- kMaxNumberOfModuleContexts * sizeof(LoadedModule)));
- CHECK(modules_);
- n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts,
- /* filter */ 0);
- // FIXME: Return this check when GetListOfModules is implemented on Mac.
- // CHECK_GT(n_modules_, 0);
- CHECK_LT(n_modules_, kMaxNumberOfModuleContexts);
- modules_fresh_ = true;
- modules_were_reloaded = true;
- }
- for (uptr i = 0; i < n_modules_; i++) {
- if (modules_[i].containsAddress(address)) {
- return &modules_[i];
- }
- }
- // Reload the modules and look up again, if we haven't tried it yet.
- if (!modules_were_reloaded) {
- // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors.
- // It's too aggressive to reload the list of modules each time we fail
- // to find a module for a given address.
- modules_fresh_ = false;
- return FindModuleForAddress(address);
- }
- return 0;
+ if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
+ list->push_back(tool);
}
- // 16K loaded modules should be enough for everyone.
- static const uptr kMaxNumberOfModuleContexts = 1 << 14;
- LoadedModule *modules_; // Array of module descriptions is leaked.
- uptr n_modules_;
- // If stale, need to reload the modules before looking up addresses.
- bool modules_fresh_;
- BlockingMutex mu_;
-
- ExternalSymbolizer *external_symbolizer_; // Leaked.
- InternalSymbolizer *const internal_symbolizer_; // Leaked.
- LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked.
-};
+#if SANITIZER_MAC
+ VReport(2, "Using dladdr symbolizer.\n");
+ list->push_back(new(*allocator) DlAddrSymbolizer());
+#endif // SANITIZER_MAC
-Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) {
- InternalSymbolizer* internal_symbolizer =
- InternalSymbolizer::get(&symbolizer_allocator_);
- ExternalSymbolizer *external_symbolizer = 0;
- LibbacktraceSymbolizer *libbacktrace_symbolizer = 0;
-
- if (!internal_symbolizer) {
- libbacktrace_symbolizer =
- LibbacktraceSymbolizer::get(&symbolizer_allocator_);
- if (!libbacktrace_symbolizer) {
- // Find path to llvm-symbolizer if it's not provided.
- if (!path_to_external)
- path_to_external = FindPathToBinary("llvm-symbolizer");
- if (path_to_external && path_to_external[0] != '\0')
- external_symbolizer = new(symbolizer_allocator_)
- ExternalSymbolizer(path_to_external);
- }
+ if (list->size() == 0) {
+ Report("WARNING: no internal or external symbolizer found.\n");
}
+}
- return new(symbolizer_allocator_) POSIXSymbolizer(
- external_symbolizer, internal_symbolizer, libbacktrace_symbolizer);
+Symbolizer *Symbolizer::PlatformInit() {
+ IntrusiveList<SymbolizerTool> list;
+ list.clear();
+ ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+ return new(symbolizer_allocator_) Symbolizer(list);
}
} // namespace __sanitizer
#include "sanitizer_platform.h"
#if SANITIZER_WINDOWS
-#include "sanitizer_symbolizer.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <dbghelp.h>
+#pragma comment(lib, "dbghelp.lib")
+
+#include "sanitizer_symbolizer_internal.h"
namespace __sanitizer {
-Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) { return 0; }
+namespace {
+
+class WinSymbolizerTool : public SymbolizerTool {
+ public:
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ return false;
+ }
+ const char *Demangle(const char *name) override;
+};
+
+bool is_dbghelp_initialized = false;
+
+bool TrySymInitialize() {
+ SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
+ return SymInitialize(GetCurrentProcess(), 0, TRUE);
+ // FIXME: We don't call SymCleanup() on exit yet - should we?
+}
+
+// Initializes DbgHelp library, if it's not yet initialized. Calls to this
+// function should be synchronized with respect to other calls to DbgHelp API
+// (e.g. from WinSymbolizerTool).
+void InitializeDbgHelpIfNeeded() {
+ if (is_dbghelp_initialized)
+ return;
+ if (!TrySymInitialize()) {
+ // OK, maybe the client app has called SymInitialize already.
+ // That's a bit unfortunate for us as all the DbgHelp functions are
+ // single-threaded and we can't coordinate with the app.
+ // FIXME: Can we stop the other threads at this point?
+ // Anyways, we have to reconfigure stuff to make sure that SymInitialize
+ // has all the appropriate options set.
+ // Cross our fingers and reinitialize DbgHelp.
+ Report("*** WARNING: Failed to initialize DbgHelp! ***\n");
+ Report("*** Most likely this means that the app is already ***\n");
+ Report("*** using DbgHelp, possibly with incompatible flags. ***\n");
+ Report("*** Due to technical reasons, symbolization might crash ***\n");
+ Report("*** or produce wrong results. ***\n");
+ SymCleanup(GetCurrentProcess());
+ TrySymInitialize();
+ }
+ is_dbghelp_initialized = true;
+
+ // When an executable is run from a location different from the one where it
+ // was originally built, we may not see the nearby PDB files.
+ // To work around this, let's append the directory of the main module
+ // to the symbol search path. All the failures below are not fatal.
+ const size_t kSymPathSize = 2048;
+ static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
+ if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
+ Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
+ return;
+ }
+ size_t sz = wcslen(path_buffer);
+ if (sz) {
+ CHECK_EQ(0, wcscat_s(path_buffer, L";"));
+ sz++;
+ }
+ DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
+ if (res == 0 || res == MAX_PATH) {
+ Report("*** WARNING: Failed to getting the EXE directory ***\n");
+ return;
+ }
+ // Write the zero character in place of the last backslash to get the
+ // directory of the main module at the end of path_buffer.
+ wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
+ CHECK_NE(last_bslash, 0);
+ *last_bslash = L'\0';
+ if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
+ Report("*** WARNING: Failed to SymSetSearchPathW\n");
+ return;
+ }
+}
+
+} // namespace
+
+bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) {
+ InitializeDbgHelpIfNeeded();
+
+ // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
+ char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
+ PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = MAX_SYM_NAME;
+ DWORD64 offset = 0;
+ BOOL got_objname = SymFromAddr(GetCurrentProcess(),
+ (DWORD64)addr, &offset, symbol);
+ if (!got_objname)
+ return false;
+
+ DWORD unused;
+ IMAGEHLP_LINE64 line_info;
+ line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
+ &unused, &line_info);
+ frame->info.function = internal_strdup(symbol->Name);
+ frame->info.function_offset = (uptr)offset;
+ if (got_fileline) {
+ frame->info.file = internal_strdup(line_info.FileName);
+ frame->info.line = line_info.LineNumber;
+ }
+ // Only consider this a successful symbolization attempt if we got file info.
+ // Otherwise, try llvm-symbolizer.
+ return got_fileline;
+}
+
+const char *WinSymbolizerTool::Demangle(const char *name) {
+ CHECK(is_dbghelp_initialized);
+ static char demangle_buffer[1000];
+ if (name[0] == '\01' &&
+ UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
+ UNDNAME_NAME_ONLY))
+ return demangle_buffer;
+ else
+ return name;
+}
+
+const char *Symbolizer::PlatformDemangle(const char *name) {
+ return name;
+}
+
+void Symbolizer::PlatformPrepareForSandboxing() {
+ // Do nothing.
+}
+
+namespace {
+struct ScopedHandle {
+ ScopedHandle() : h_(nullptr) {}
+ explicit ScopedHandle(HANDLE h) : h_(h) {}
+ ~ScopedHandle() {
+ if (h_)
+ ::CloseHandle(h_);
+ }
+ HANDLE get() { return h_; }
+ HANDLE *receive() { return &h_; }
+ HANDLE release() {
+ HANDLE h = h_;
+ h_ = nullptr;
+ return h;
+ }
+ HANDLE h_;
+};
+} // namespace
+
+bool SymbolizerProcess::StartSymbolizerSubprocess() {
+ // Create inherited pipes for stdin and stdout.
+ ScopedHandle stdin_read, stdin_write;
+ ScopedHandle stdout_read, stdout_write;
+ SECURITY_ATTRIBUTES attrs;
+ attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+ attrs.bInheritHandle = TRUE;
+ attrs.lpSecurityDescriptor = nullptr;
+ if (!::CreatePipe(stdin_read.receive(), stdin_write.receive(), &attrs, 0) ||
+ !::CreatePipe(stdout_read.receive(), stdout_write.receive(), &attrs, 0)) {
+ VReport(2, "WARNING: %s CreatePipe failed (error code: %d)\n",
+ SanitizerToolName, path_, GetLastError());
+ return false;
+ }
+
+ // Don't inherit the writing end of stdin or the reading end of stdout.
+ if (!SetHandleInformation(stdin_write.get(), HANDLE_FLAG_INHERIT, 0) ||
+ !SetHandleInformation(stdout_read.get(), HANDLE_FLAG_INHERIT, 0)) {
+ VReport(2, "WARNING: %s SetHandleInformation failed (error code: %d)\n",
+ SanitizerToolName, path_, GetLastError());
+ return false;
+ }
+
+ // Compute the command line. Wrap double quotes around everything.
+ const char *argv[kArgVMax];
+ GetArgV(path_, argv);
+ InternalScopedString command_line(kMaxPathLength * 3);
+ for (int i = 0; argv[i]; i++) {
+ const char *arg = argv[i];
+ int arglen = internal_strlen(arg);
+ // Check that tool command lines are simple and that complete escaping is
+ // unnecessary.
+ CHECK(!internal_strchr(arg, '"') && "quotes in args unsupported");
+ CHECK(!internal_strstr(arg, "\\\\") &&
+ "double backslashes in args unsupported");
+ CHECK(arglen > 0 && arg[arglen - 1] != '\\' &&
+ "args ending in backslash and empty args unsupported");
+ command_line.append("\"%s\" ", arg);
+ }
+ VReport(3, "Launching symbolizer command: %s\n", command_line.data());
+
+ // Launch llvm-symbolizer with stdin and stdout redirected.
+ STARTUPINFOA si;
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags |= STARTF_USESTDHANDLES;
+ si.hStdInput = stdin_read.get();
+ si.hStdOutput = stdout_write.get();
+ PROCESS_INFORMATION pi;
+ memset(&pi, 0, sizeof(pi));
+ if (!CreateProcessA(path_, // Executable
+ command_line.data(), // Command line
+ nullptr, // Process handle not inheritable
+ nullptr, // Thread handle not inheritable
+ TRUE, // Set handle inheritance to TRUE
+ 0, // Creation flags
+ nullptr, // Use parent's environment block
+ nullptr, // Use parent's starting directory
+ &si, &pi)) {
+ VReport(2, "WARNING: %s failed to create process for %s (error code: %d)\n",
+ SanitizerToolName, path_, GetLastError());
+ return false;
+ }
+
+ // Process creation succeeded, so transfer handle ownership into the fields.
+ input_fd_ = stdout_read.release();
+ output_fd_ = stdin_write.release();
+
+ // The llvm-symbolizer process is responsible for quitting itself when the
+ // stdin pipe is closed, so we don't need these handles. Close them to prevent
+ // leaks. If we ever want to try to kill the symbolizer process from the
+ // parent, we'll want to hang on to these handles.
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return true;
+}
+
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+ LowLevelAllocator *allocator) {
+ if (!common_flags()->symbolize) {
+ VReport(2, "Symbolizer is disabled.\n");
+ return;
+ }
+
+ // Add llvm-symbolizer in case the binary has dwarf.
+ const char *user_path = common_flags()->external_symbolizer_path;
+ const char *path =
+ user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe");
+ if (path) {
+ VReport(2, "Using llvm-symbolizer at %spath: %s\n",
+ user_path ? "user-specified " : "", path);
+ list->push_back(new(*allocator) LLVMSymbolizer(path, allocator));
+ } else {
+ if (user_path && user_path[0] == '\0') {
+ VReport(2, "External symbolizer is explicitly disabled.\n");
+ } else {
+ VReport(2, "External symbolizer is not present.\n");
+ }
+ }
+
+ // Add the dbghelp based symbolizer.
+ list->push_back(new(*allocator) WinSymbolizerTool());
+}
+
+Symbolizer *Symbolizer::PlatformInit() {
+ IntrusiveList<SymbolizerTool> list;
+ list.clear();
+ ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+
+ return new(symbolizer_allocator_) Symbolizer(list);
+}
} // namespace __sanitizer
//
//===----------------------------------------------------------------------===//
-#define internal_syscall syscall
+#if SANITIZER_FREEBSD || SANITIZER_MAC
+# define SYSCALL(name) SYS_ ## name
+#else
+# define SYSCALL(name) __NR_ ## name
+#endif
+
+#if (SANITIZER_FREEBSD || SANITIZER_MAC) && defined(__x86_64__)
+# define internal_syscall __syscall
+# else
+# define internal_syscall syscall
+#endif
bool internal_iserror(uptr retval, int *rverrno) {
if (retval == (uptr)-1) {
--- /dev/null
+//===-- sanitizer_syscall_linux_aarch64.inc --------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementations of internal_syscall and internal_iserror for Linux/aarch64.
+//
+//===----------------------------------------------------------------------===//
+
+#define SYSCALL(name) __NR_ ## name
+
+static uptr __internal_syscall(u64 nr) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0");
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall0(n) \
+ (__internal_syscall)(n)
+
+static uptr __internal_syscall(u64 nr, u64 arg1) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall1(n, a1) \
+ (__internal_syscall)(n, (u64)(a1))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ register u64 x1 asm("x1") = arg2;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0), "r"(x1)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall2(n, a1, a2) \
+ (__internal_syscall)(n, (u64)(a1), (long)(a2))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ register u64 x1 asm("x1") = arg2;
+ register u64 x2 asm("x2") = arg3;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0), "r"(x1), "r"(x2)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall3(n, a1, a2, a3) \
+ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
+ u64 arg4) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ register u64 x1 asm("x1") = arg2;
+ register u64 x2 asm("x2") = arg3;
+ register u64 x3 asm("x3") = arg4;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall4(n, a1, a2, a3, a4) \
+ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
+ u64 arg4, long arg5) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ register u64 x1 asm("x1") = arg2;
+ register u64 x2 asm("x2") = arg3;
+ register u64 x3 asm("x3") = arg4;
+ register u64 x4 asm("x4") = arg5;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall5(n, a1, a2, a3, a4, a5) \
+ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
+ (u64)(a5))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
+ u64 arg4, long arg5, long arg6) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ register u64 x1 asm("x1") = arg2;
+ register u64 x2 asm("x2") = arg3;
+ register u64 x3 asm("x3") = arg4;
+ register u64 x4 asm("x4") = arg5;
+ register u64 x5 asm("x5") = arg6;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall6(n, a1, a2, a3, a4, a5, a6) \
+ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
+ (u64)(a5), (long)(a6))
+
+#define __SYSCALL_NARGS_X(a1, a2, a3, a4, a5, a6, a7, a8, n, ...) n
+#define __SYSCALL_NARGS(...) \
+ __SYSCALL_NARGS_X(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0, )
+#define __SYSCALL_CONCAT_X(a, b) a##b
+#define __SYSCALL_CONCAT(a, b) __SYSCALL_CONCAT_X(a, b)
+#define __SYSCALL_DISP(b, ...) \
+ __SYSCALL_CONCAT(b, __SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
+
+#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__)
+
+// Helper function used to avoid cobbler errno.
+bool internal_iserror(uptr retval, int *rverrno) {
+ if (retval >= (uptr)-4095) {
+ if (rverrno)
+ *rverrno = -retval;
+ return true;
+ }
+ return false;
+}
//
//===----------------------------------------------------------------------===//
+#define SYSCALL(name) __NR_ ## name
+
static uptr internal_syscall(u64 nr) {
u64 retval;
asm volatile("syscall" : "=a"(retval) : "a"(nr) : "rcx", "r11",
namespace __sanitizer {
ThreadContextBase::ThreadContextBase(u32 tid)
- : tid(tid), unique_id(0), os_id(0), user_id(0), status(ThreadStatusInvalid),
- detached(false), reuse_count(0), parent_tid(0), next(0) {
+ : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0),
+ status(ThreadStatusInvalid),
+ detached(false), parent_tid(0), next(0) {
name[0] = '\0';
}
void ThreadContextBase::Reset() {
status = ThreadStatusInvalid;
- reuse_count++;
SetName(0);
OnReset();
}
const u32 ThreadRegistry::kUnknownTid = ~0U;
ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
- u32 thread_quarantine_size)
+ u32 thread_quarantine_size, u32 max_reuse)
: context_factory_(factory),
max_threads_(max_threads),
thread_quarantine_size_(thread_quarantine_size),
+ max_reuse_(max_reuse),
mtx_(),
n_contexts_(0),
total_threads_(0),
tctx = context_factory_(tid);
threads_[tid] = tctx;
} else {
+#ifndef SANITIZER_GO
Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
SanitizerToolName, max_threads_);
+#else
+ Printf("race: limit on %u simultaneously alive goroutines is exceeded,"
+ " dying\n", max_threads_);
+#endif
Die();
}
CHECK_NE(tctx, 0);
}
}
-void ThreadRegistry::DetachThread(u32 tid) {
+void ThreadRegistry::DetachThread(u32 tid, void *arg) {
BlockingMutexLock l(&mtx_);
CHECK_LT(tid, n_contexts_);
ThreadContextBase *tctx = threads_[tid];
Report("%s: Detach of non-existent thread\n", SanitizerToolName);
return;
}
+ tctx->OnDetached(arg);
if (tctx->status == ThreadStatusFinished) {
tctx->SetDead();
QuarantinePush(tctx);
dead_threads_.pop_front();
CHECK_EQ(tctx->status, ThreadStatusDead);
tctx->Reset();
+ tctx->reuse_count++;
+ if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_)
+ return;
invalid_threads_.push_back(tctx);
}
const u32 tid; // Thread ID. Main thread should have tid = 0.
u64 unique_id; // Unique thread ID.
+ u32 reuse_count; // Number of times this tid was reused.
uptr os_id; // PID (used for reporting).
uptr user_id; // Some opaque user thread id (e.g. pthread_t).
char name[64]; // As annotated by user.
ThreadStatus status;
bool detached;
- int reuse_count;
u32 parent_tid;
ThreadContextBase *next; // For storing thread contexts in a list.
virtual void OnStarted(void *arg) {}
virtual void OnCreated(void *arg) {}
virtual void OnReset() {}
+ virtual void OnDetached(void *arg) {}
};
typedef ThreadContextBase* (*ThreadContextFactory)(u32 tid);
static const u32 kUnknownTid;
ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
- u32 thread_quarantine_size);
- void GetNumberOfThreads(uptr *total = 0, uptr *running = 0, uptr *alive = 0);
+ u32 thread_quarantine_size, u32 max_reuse = 0);
+ void GetNumberOfThreads(uptr *total = nullptr, uptr *running = nullptr,
+ uptr *alive = nullptr);
uptr GetMaxAliveThreads();
void Lock() { mtx_.Lock(); }
void SetThreadName(u32 tid, const char *name);
void SetThreadNameByUserId(uptr user_id, const char *name);
- void DetachThread(u32 tid);
+ void DetachThread(u32 tid, void *arg);
void JoinThread(u32 tid, void *arg);
void FinishThread(u32 tid);
void StartThread(u32 tid, uptr os_id, void *arg);
const ThreadContextFactory context_factory_;
const u32 max_threads_;
const u32 thread_quarantine_size_;
+ const u32 max_reuse_;
BlockingMutex mtx_;
typedef GenericScopedLock<ThreadRegistry> ThreadRegistryLock;
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_THREAD_REGISTRY_H
+#endif // SANITIZER_THREAD_REGISTRY_H
--- /dev/null
+//===-- sanitizer_tls_get_addr.cc -----------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Handle the __tls_get_addr call.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_tls_get_addr.h"
+
+#include "sanitizer_flags.h"
+#include "sanitizer_platform_interceptors.h"
+
+namespace __sanitizer {
+#if SANITIZER_INTERCEPT_TLS_GET_ADDR
+
+// The actual parameter that comes to __tls_get_addr
+// is a pointer to a struct with two words in it:
+struct TlsGetAddrParam {
+ uptr dso_id;
+ uptr offset;
+};
+
+// Glibc starting from 2.19 allocates tls using __signal_safe_memalign,
+// which has such header.
+struct Glibc_2_19_tls_header {
+ uptr size;
+ uptr start;
+};
+
+// This must be static TLS
+__attribute__((tls_model("initial-exec")))
+static __thread DTLS dtls;
+
+// Make sure we properly destroy the DTLS objects:
+// this counter should never get too large.
+static atomic_uintptr_t number_of_live_dtls;
+
+static const uptr kDestroyedThread = -1;
+
+static inline void DTLS_Deallocate(DTLS::DTV *dtv, uptr size) {
+ if (!size) return;
+ VPrintf(2, "__tls_get_addr: DTLS_Deallocate %p %zd\n", dtv, size);
+ UnmapOrDie(dtv, size * sizeof(DTLS::DTV));
+ atomic_fetch_sub(&number_of_live_dtls, 1, memory_order_relaxed);
+}
+
+static inline void DTLS_Resize(uptr new_size) {
+ if (dtls.dtv_size >= new_size) return;
+ new_size = RoundUpToPowerOfTwo(new_size);
+ new_size = Max(new_size, 4096UL / sizeof(DTLS::DTV));
+ DTLS::DTV *new_dtv =
+ (DTLS::DTV *)MmapOrDie(new_size * sizeof(DTLS::DTV), "DTLS_Resize");
+ uptr num_live_dtls =
+ atomic_fetch_add(&number_of_live_dtls, 1, memory_order_relaxed);
+ VPrintf(2, "__tls_get_addr: DTLS_Resize %p %zd\n", &dtls, num_live_dtls);
+ CHECK_LT(num_live_dtls, 1 << 20);
+ uptr old_dtv_size = dtls.dtv_size;
+ DTLS::DTV *old_dtv = dtls.dtv;
+ if (old_dtv_size)
+ internal_memcpy(new_dtv, dtls.dtv, dtls.dtv_size * sizeof(DTLS::DTV));
+ dtls.dtv = new_dtv;
+ dtls.dtv_size = new_size;
+ if (old_dtv_size)
+ DTLS_Deallocate(old_dtv, old_dtv_size);
+}
+
+void DTLS_Destroy() {
+ if (!common_flags()->intercept_tls_get_addr) return;
+ VPrintf(2, "__tls_get_addr: DTLS_Destroy %p %zd\n", &dtls, dtls.dtv_size);
+ uptr s = dtls.dtv_size;
+ dtls.dtv_size = kDestroyedThread; // Do this before unmap for AS-safety.
+ DTLS_Deallocate(dtls.dtv, s);
+}
+
+#if defined(__powerpc64__)
+// This is glibc's TLS_DTV_OFFSET:
+// "Dynamic thread vector pointers point 0x8000 past the start of each
+// TLS block."
+static const uptr kDtvOffset = 0x8000;
+#else
+static const uptr kDtvOffset = 0;
+#endif
+
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
+ uptr static_tls_begin, uptr static_tls_end) {
+ if (!common_flags()->intercept_tls_get_addr) return 0;
+ TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void);
+ uptr dso_id = arg->dso_id;
+ if (dtls.dtv_size == kDestroyedThread) return 0;
+ DTLS_Resize(dso_id + 1);
+ if (dtls.dtv[dso_id].beg) return 0;
+ uptr tls_size = 0;
+ uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset;
+ VPrintf(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p "
+ "num_live_dtls %zd\n",
+ arg, arg->dso_id, arg->offset, res, tls_beg, &tls_beg,
+ atomic_load(&number_of_live_dtls, memory_order_relaxed));
+ if (dtls.last_memalign_ptr == tls_beg) {
+ tls_size = dtls.last_memalign_size;
+ VPrintf(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n",
+ tls_beg, tls_size);
+ } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
+ // This is the static TLS block which was initialized / unpoisoned at thread
+ // creation.
+ VPrintf(2, "__tls_get_addr: static tls: %p\n", tls_beg);
+ tls_size = 0;
+ } else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) {
+ // We may want to check gnu_get_libc_version().
+ Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1;
+ tls_size = header->size;
+ tls_beg = header->start;
+ VPrintf(2, "__tls_get_addr: glibc >=2.19 suspected; tls={%p %p}\n",
+ tls_beg, tls_size);
+ } else {
+ VPrintf(2, "__tls_get_addr: Can't guess glibc version\n");
+ // This may happen inside the DTOR of main thread, so just ignore it.
+ tls_size = 0;
+ }
+ dtls.dtv[dso_id].beg = tls_beg;
+ dtls.dtv[dso_id].size = tls_size;
+ return dtls.dtv + dso_id;
+}
+
+void DTLS_on_libc_memalign(void *ptr, uptr size) {
+ if (!common_flags()->intercept_tls_get_addr) return;
+ VPrintf(2, "DTLS_on_libc_memalign: %p %p\n", ptr, size);
+ dtls.last_memalign_ptr = reinterpret_cast<uptr>(ptr);
+ dtls.last_memalign_size = size;
+}
+
+DTLS *DTLS_Get() { return &dtls; }
+
+#else
+void DTLS_on_libc_memalign(void *ptr, uptr size) {}
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res) { return 0; }
+DTLS *DTLS_Get() { return 0; }
+void DTLS_Destroy() {}
+#endif // SANITIZER_INTERCEPT_TLS_GET_ADDR
+
+} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_tls_get_addr.h --------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Handle the __tls_get_addr call.
+//
+// All this magic is specific to glibc and is required to workaround
+// the lack of interface that would tell us about the Dynamic TLS (DTLS).
+// https://sourceware.org/bugzilla/show_bug.cgi?id=16291
+//
+// The matters get worse because the glibc implementation changed between
+// 2.18 and 2.19:
+// https://groups.google.com/forum/#!topic/address-sanitizer/BfwYD8HMxTM
+//
+// Before 2.19, every DTLS chunk is allocated with __libc_memalign,
+// which we intercept and thus know where is the DTLS.
+// Since 2.19, DTLS chunks are allocated with __signal_safe_memalign,
+// which is an internal function that wraps a mmap call, neither of which
+// we can intercept. Luckily, __signal_safe_memalign has a simple parseable
+// header which we can use.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_TLS_GET_ADDR_H
+#define SANITIZER_TLS_GET_ADDR_H
+
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+struct DTLS {
+ // Array of DTLS chunks for the current Thread.
+ // If beg == 0, the chunk is unused.
+ struct DTV {
+ uptr beg, size;
+ };
+
+ uptr dtv_size;
+ DTV *dtv; // dtv_size elements, allocated by MmapOrDie.
+
+ // Auxiliary fields, don't access them outside sanitizer_tls_get_addr.cc
+ uptr last_memalign_size;
+ uptr last_memalign_ptr;
+};
+
+// Returns pointer and size of a linker-allocated TLS block.
+// Each block is returned exactly once.
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res, uptr static_tls_begin,
+ uptr static_tls_end);
+void DTLS_on_libc_memalign(void *ptr, uptr size);
+DTLS *DTLS_Get();
+void DTLS_Destroy(); // Make sure to call this before the thread is destroyed.
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_TLS_GET_ADDR_H
--- /dev/null
+//===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the unwind.h-based (aka "slow") stack unwinding routines
+// available to the tools on Linux, Android, and FreeBSD.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
+#include "sanitizer_common.h"
+#include "sanitizer_stacktrace.h"
+
+#if SANITIZER_ANDROID
+#include <dlfcn.h> // for dlopen()
+#endif
+
+#if SANITIZER_FREEBSD
+#define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
+#endif
+#include <unwind.h>
+
+namespace __sanitizer {
+
+//------------------------- SlowUnwindStack -----------------------------------
+
+typedef struct {
+ uptr absolute_pc;
+ uptr stack_top;
+ uptr stack_size;
+} backtrace_frame_t;
+
+extern "C" {
+typedef void *(*acquire_my_map_info_list_func)();
+typedef void (*release_my_map_info_list_func)(void *map);
+typedef sptr (*unwind_backtrace_signal_arch_func)(
+ void *siginfo, void *sigcontext, void *map_info_list,
+ backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
+acquire_my_map_info_list_func acquire_my_map_info_list;
+release_my_map_info_list_func release_my_map_info_list;
+unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
+} // extern "C"
+
+#if SANITIZER_ANDROID
+void SanitizerInitializeUnwinder() {
+ void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
+ if (!p) {
+ VReport(1,
+ "Failed to open libcorkscrew.so. You may see broken stack traces "
+ "in SEGV reports.");
+ return;
+ }
+ acquire_my_map_info_list =
+ (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
+ release_my_map_info_list =
+ (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
+ unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
+ p, "unwind_backtrace_signal_arch");
+ if (!acquire_my_map_info_list || !release_my_map_info_list ||
+ !unwind_backtrace_signal_arch) {
+ VReport(1,
+ "Failed to find one of the required symbols in libcorkscrew.so. "
+ "You may see broken stack traces in SEGV reports.");
+ acquire_my_map_info_list = 0;
+ unwind_backtrace_signal_arch = 0;
+ release_my_map_info_list = 0;
+ }
+}
+#endif
+
+#ifdef __arm__
+#define UNWIND_STOP _URC_END_OF_STACK
+#define UNWIND_CONTINUE _URC_NO_REASON
+#else
+#define UNWIND_STOP _URC_NORMAL_STOP
+#define UNWIND_CONTINUE _URC_NO_REASON
+#endif
+
+uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
+#if defined(__arm__) && !SANITIZER_MAC
+ uptr val;
+ _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
+ 15 /* r15 = PC */, _UVRSD_UINT32, &val);
+ CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
+ // Clear the Thumb bit.
+ return val & ~(uptr)1;
+#else
+ return _Unwind_GetIP(ctx);
+#endif
+}
+
+struct UnwindTraceArg {
+ BufferedStackTrace *stack;
+ u32 max_depth;
+};
+
+_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
+ UnwindTraceArg *arg = (UnwindTraceArg*)param;
+ CHECK_LT(arg->stack->size, arg->max_depth);
+ uptr pc = Unwind_GetIP(ctx);
+ arg->stack->trace_buffer[arg->stack->size++] = pc;
+ if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
+ return UNWIND_CONTINUE;
+}
+
+void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
+ CHECK_GE(max_depth, 2);
+ size = 0;
+ UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
+ _Unwind_Backtrace(Unwind_Trace, &arg);
+ // We need to pop a few frames so that pc is on top.
+ uptr to_pop = LocatePcInTrace(pc);
+ // trace_buffer[0] belongs to the current function so we always pop it,
+ // unless there is only 1 frame in the stack trace (1 frame is always better
+ // than 0!).
+ // 1-frame stacks don't normally happen, but this depends on the actual
+ // unwinder implementation (libgcc, libunwind, etc) which is outside of our
+ // control.
+ if (to_pop == 0 && size > 1)
+ to_pop = 1;
+ PopStackFrames(to_pop);
+ trace_buffer[0] = pc;
+}
+
+void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
+ u32 max_depth) {
+ CHECK_GE(max_depth, 2);
+ if (!unwind_backtrace_signal_arch) {
+ SlowUnwindStack(pc, max_depth);
+ return;
+ }
+
+ void *map = acquire_my_map_info_list();
+ CHECK(map);
+ InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax);
+ // siginfo argument appears to be unused.
+ sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
+ frames.data(),
+ /* ignore_depth */ 0, max_depth);
+ release_my_map_info_list(map);
+ if (res < 0) return;
+ CHECK_LE((uptr)res, kStackTraceMax);
+
+ size = 0;
+ // +2 compensate for libcorkscrew unwinder returning addresses of call
+ // instructions instead of raw return addresses.
+ for (sptr i = 0; i < res; ++i)
+ trace_buffer[size++] = frames[i].absolute_pc + 2;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
#define WIN32_LEAN_AND_MEAN
#define NOGDI
-#include <stdlib.h>
-#include <io.h>
#include <windows.h>
+#include <dbghelp.h>
+#include <io.h>
+#include <psapi.h>
+#include <stdlib.h>
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
// --------------------- sanitizer_common.h
uptr GetPageSize() {
- return 1U << 14; // FIXME: is this configurable?
+ // FIXME: there is an API for getting the system page size (GetSystemInfo or
+ // GetNativeSystemInfo), but if we use it here we get test failures elsewhere.
+ return 1U << 14;
}
uptr GetMmapGranularity() {
}
bool FileExists(const char *filename) {
- UNIMPLEMENTED();
+ return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES;
}
uptr internal_getpid() {
return GetTid();
}
+#if !SANITIZER_GO
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
uptr *stack_bottom) {
CHECK(stack_top);
*stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize;
*stack_bottom = (uptr)mbi.AllocationBase;
}
+#endif // #if !SANITIZER_GO
void *MmapOrDie(uptr size, const char *mem_type) {
void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
- if (rv == 0) {
- Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s\n",
- size, size, mem_type);
- CHECK("unable to mmap" && 0);
- }
+ if (rv == 0)
+ ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError());
return rv;
}
void UnmapOrDie(void *addr, uptr size) {
+ if (!size || !addr)
+ return;
+
if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
- Report("ERROR: Failed to deallocate 0x%zx (%zd) bytes at address %p\n",
- size, size, addr);
+ Report("ERROR: %s failed to "
+ "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n",
+ SanitizerToolName, size, size, addr, GetLastError());
CHECK("unable to unmap" && 0);
}
}
-void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
// FIXME: is this really "NoReserve"? On Win32 this does not matter much,
// but on Win64 it does.
+ (void)name; // unsupported
void *p = VirtualAlloc((LPVOID)fixed_addr, size,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (p == 0)
- Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at %p (%d)\n",
- size, size, fixed_addr, GetLastError());
+ Report("ERROR: %s failed to "
+ "allocate %p (%zd) bytes at %p (error code: %d)\n",
+ SanitizerToolName, size, size, fixed_addr, GetLastError());
return p;
}
return MmapFixedNoReserve(fixed_addr, size);
}
-void *Mprotect(uptr fixed_addr, uptr size) {
- return VirtualAlloc((LPVOID)fixed_addr, size,
- MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
+void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
+ // FIXME: make this really NoReserve?
+ return MmapOrDie(size, mem_type);
+}
+
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
+ (void)name; // unsupported
+ void *res = VirtualAlloc((LPVOID)fixed_addr, size,
+ MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
+ if (res == 0)
+ Report("WARNING: %s failed to "
+ "mprotect %p (%zd) bytes at %p (error code: %d)\n",
+ SanitizerToolName, size, size, fixed_addr, GetLastError());
+ return res;
}
+bool MprotectNoAccess(uptr addr, uptr size) {
+ DWORD old_protection;
+ return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection);
+}
+
+
void FlushUnneededShadowMemory(uptr addr, uptr size) {
// This is almost useless on 32-bits.
- // FIXME: add madvice-analog when we move to 64-bits.
+ // FIXME: add madvise-analog when we move to 64-bits.
+}
+
+void NoHugePagesInRegion(uptr addr, uptr size) {
+ // FIXME: probably similar to FlushUnneededShadowMemory.
+}
+
+void DontDumpShadowMemory(uptr addr, uptr length) {
+ // This is almost useless on 32-bits.
+ // FIXME: add madvise-analog when we move to 64-bits.
}
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
- // FIXME: shall we do anything here on Windows?
- return true;
+ MEMORY_BASIC_INFORMATION mbi;
+ CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi)));
+ return mbi.Protect == PAGE_NOACCESS &&
+ (uptr)mbi.BaseAddress + mbi.RegionSize >= range_end;
}
void *MapFileToMemory(const char *file_name, uptr *buff_size) {
UNIMPLEMENTED();
}
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
+ UNIMPLEMENTED();
+}
+
static const int kMaxEnvNameLength = 128;
static const DWORD kMaxEnvValueLength = 32767;
UNIMPLEMENTED();
}
+namespace {
+struct ModuleInfo {
+ const char *filepath;
+ uptr base_address;
+ uptr end_address;
+};
+
+#ifndef SANITIZER_GO
+int CompareModulesBase(const void *pl, const void *pr) {
+ const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr;
+ if (l->base_address < r->base_address)
+ return -1;
+ return l->base_address > r->base_address;
+}
+#endif
+} // namespace
+
+#ifndef SANITIZER_GO
void DumpProcessMap() {
- UNIMPLEMENTED();
+ Report("Dumping process modules:\n");
+ InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules);
+ uptr num_modules =
+ GetListOfModules(modules.data(), kMaxNumberOfModules, nullptr);
+
+ InternalScopedBuffer<ModuleInfo> module_infos(num_modules);
+ for (size_t i = 0; i < num_modules; ++i) {
+ module_infos[i].filepath = modules[i].full_name();
+ module_infos[i].base_address = modules[i].base_address();
+ module_infos[i].end_address = modules[i].ranges().next()->end;
+ }
+ qsort(module_infos.data(), num_modules, sizeof(ModuleInfo),
+ CompareModulesBase);
+
+ for (size_t i = 0; i < num_modules; ++i) {
+ const ModuleInfo &mi = module_infos[i];
+ if (mi.end_address != 0) {
+ Printf("\t%p-%p %s\n", mi.base_address, mi.end_address,
+ mi.filepath[0] ? mi.filepath : "[no name]");
+ } else if (mi.filepath[0]) {
+ Printf("\t??\?-??? %s\n", mi.filepath);
+ } else {
+ Printf("\t???\n");
+ }
+ }
}
+#endif
-void DisableCoreDumper() {
- UNIMPLEMENTED();
+void DisableCoreDumperIfNecessary() {
+ // Do nothing.
}
void ReExec() {
UNIMPLEMENTED();
}
-void PrepareForSandboxing() {
- // Nothing here for now.
+void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
+#if !SANITIZER_GO
+ CovPrepareForSandboxing(args);
+#endif
}
bool StackSizeIsUnlimited() {
UNIMPLEMENTED();
}
-char *FindPathToBinary(const char *name) {
- // Nothing here for now.
- return 0;
+bool AddressSpaceIsUnlimited() {
+ UNIMPLEMENTED();
+}
+
+void SetAddressSpaceUnlimited() {
+ UNIMPLEMENTED();
+}
+
+bool IsPathSeparator(const char c) {
+ return c == '\\' || c == '/';
+}
+
+bool IsAbsolutePath(const char *path) {
+ UNIMPLEMENTED();
}
void SleepForSeconds(int seconds) {
}
void Abort() {
- abort();
- internal__exit(-1); // abort is not NORETURN on Windows.
+ if (::IsDebuggerPresent())
+ __debugbreak();
+ internal__exit(3);
+}
+
+// Read the file to extract the ImageBase field from the PE header. If ASLR is
+// disabled and this virtual address is available, the loader will typically
+// load the image at this address. Therefore, we call it the preferred base. Any
+// addresses in the DWARF typically assume that the object has been loaded at
+// this address.
+static uptr GetPreferredBase(const char *modname) {
+ fd_t fd = OpenFile(modname, RdOnly, nullptr);
+ if (fd == kInvalidFd)
+ return 0;
+ FileCloser closer(fd);
+
+ // Read just the DOS header.
+ IMAGE_DOS_HEADER dos_header;
+ uptr bytes_read;
+ if (!ReadFromFile(fd, &dos_header, sizeof(dos_header), &bytes_read) ||
+ bytes_read != sizeof(dos_header))
+ return 0;
+
+ // The file should start with the right signature.
+ if (dos_header.e_magic != IMAGE_DOS_SIGNATURE)
+ return 0;
+
+ // The layout at e_lfanew is:
+ // "PE\0\0"
+ // IMAGE_FILE_HEADER
+ // IMAGE_OPTIONAL_HEADER
+ // Seek to e_lfanew and read all that data.
+ char buf[4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)];
+ if (::SetFilePointer(fd, dos_header.e_lfanew, nullptr, FILE_BEGIN) ==
+ INVALID_SET_FILE_POINTER)
+ return 0;
+ if (!ReadFromFile(fd, &buf[0], sizeof(buf), &bytes_read) ||
+ bytes_read != sizeof(buf))
+ return 0;
+
+ // Check for "PE\0\0" before the PE header.
+ char *pe_sig = &buf[0];
+ if (internal_memcmp(pe_sig, "PE\0\0", 4) != 0)
+ return 0;
+
+ // Skip over IMAGE_FILE_HEADER. We could do more validation here if we wanted.
+ IMAGE_OPTIONAL_HEADER *pe_header =
+ (IMAGE_OPTIONAL_HEADER *)(pe_sig + 4 + sizeof(IMAGE_FILE_HEADER));
+
+ // Check for more magic in the PE header.
+ if (pe_header->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+ return 0;
+
+ // Finally, return the ImageBase.
+ return (uptr)pe_header->ImageBase;
}
+#ifndef SANITIZER_GO
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter) {
- UNIMPLEMENTED();
+ HANDLE cur_process = GetCurrentProcess();
+
+ // Query the list of modules. Start by assuming there are no more than 256
+ // modules and retry if that's not sufficient.
+ HMODULE *hmodules = 0;
+ uptr modules_buffer_size = sizeof(HMODULE) * 256;
+ DWORD bytes_required;
+ while (!hmodules) {
+ hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__);
+ CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size,
+ &bytes_required));
+ if (bytes_required > modules_buffer_size) {
+ // Either there turned out to be more than 256 hmodules, or new hmodules
+ // could have loaded since the last try. Retry.
+ UnmapOrDie(hmodules, modules_buffer_size);
+ hmodules = 0;
+ modules_buffer_size = bytes_required;
+ }
+ }
+
+ // |num_modules| is the number of modules actually present,
+ // |count| is the number of modules we return.
+ size_t nun_modules = bytes_required / sizeof(HMODULE),
+ count = 0;
+ for (size_t i = 0; i < nun_modules && count < max_modules; ++i) {
+ HMODULE handle = hmodules[i];
+ MODULEINFO mi;
+ if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi)))
+ continue;
+
+ // Get the UTF-16 path and convert to UTF-8.
+ wchar_t modname_utf16[kMaxPathLength];
+ int modname_utf16_len =
+ GetModuleFileNameW(handle, modname_utf16, kMaxPathLength);
+ if (modname_utf16_len == 0)
+ modname_utf16[0] = '\0';
+ char module_name[kMaxPathLength];
+ int module_name_len =
+ ::WideCharToMultiByte(CP_UTF8, 0, modname_utf16, modname_utf16_len + 1,
+ &module_name[0], kMaxPathLength, NULL, NULL);
+ module_name[module_name_len] = '\0';
+
+ if (filter && !filter(module_name))
+ continue;
+
+ uptr base_address = (uptr)mi.lpBaseOfDll;
+ uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage;
+
+ // Adjust the base address of the module so that we get a VA instead of an
+ // RVA when computing the module offset. This helps llvm-symbolizer find the
+ // right DWARF CU. In the common case that the image is loaded at it's
+ // preferred address, we will now print normal virtual addresses.
+ uptr preferred_base = GetPreferredBase(&module_name[0]);
+ uptr adjusted_base = base_address - preferred_base;
+
+ LoadedModule *cur_module = &modules[count];
+ cur_module->set(module_name, adjusted_base);
+ // We add the whole module as one single address range.
+ cur_module->addAddressRange(base_address, end_address, /*executable*/ true);
+ count++;
+ }
+ UnmapOrDie(hmodules, modules_buffer_size);
+
+ return count;
};
-#ifndef SANITIZER_GO
+// We can't use atexit() directly at __asan_init time as the CRT is not fully
+// initialized at this point. Place the functions into a vector and use
+// atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers).
+InternalMmapVectorNoCtor<void (*)(void)> atexit_functions;
+
int Atexit(void (*function)(void)) {
- return atexit(function);
+ atexit_functions.push_back(function);
+ return 0;
+}
+
+static int RunAtexit() {
+ int ret = 0;
+ for (uptr i = 0; i < atexit_functions.size(); ++i) {
+ ret |= atexit(atexit_functions[i]);
+ }
+ return ret;
}
+
+#pragma section(".CRT$XID", long, read) // NOLINT
+__declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit;
#endif
// ------------------ sanitizer_libc.h
-uptr internal_mmap(void *addr, uptr length, int prot, int flags,
- int fd, u64 offset) {
- UNIMPLEMENTED();
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) {
+ fd_t res;
+ if (mode == RdOnly) {
+ res = CreateFile(filename, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ } else if (mode == WrOnly) {
+ res = CreateFile(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ } else {
+ UNIMPLEMENTED();
+ }
+ CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd);
+ CHECK(res != kStderrFd || kStderrFd == kInvalidFd);
+ if (res == kInvalidFd && last_error)
+ *last_error = GetLastError();
+ return res;
}
-uptr internal_munmap(void *addr, uptr length) {
- UNIMPLEMENTED();
+void CloseFile(fd_t fd) {
+ CloseHandle(fd);
}
-uptr internal_close(fd_t fd) {
- UNIMPLEMENTED();
-}
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+ error_t *error_p) {
+ CHECK(fd != kInvalidFd);
-int internal_isatty(fd_t fd) {
- return _isatty(fd);
-}
+ // bytes_read can't be passed directly to ReadFile:
+ // uptr is unsigned long long on 64-bit Windows.
+ unsigned long num_read_long;
-uptr internal_open(const char *filename, int flags) {
- UNIMPLEMENTED();
+ bool success = ::ReadFile(fd, buff, buff_size, &num_read_long, nullptr);
+ if (!success && error_p)
+ *error_p = GetLastError();
+ if (bytes_read)
+ *bytes_read = num_read_long;
+ return success;
}
-uptr internal_open(const char *filename, int flags, u32 mode) {
- UNIMPLEMENTED();
+bool SupportsColoredOutput(fd_t fd) {
+ // FIXME: support colored output.
+ return false;
}
-uptr OpenFile(const char *filename, bool write) {
- UNIMPLEMENTED();
-}
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+ error_t *error_p) {
+ CHECK(fd != kInvalidFd);
-uptr internal_read(fd_t fd, void *buf, uptr count) {
- UNIMPLEMENTED();
-}
+ // Handle null optional parameters.
+ error_t dummy_error;
+ error_p = error_p ? error_p : &dummy_error;
+ uptr dummy_bytes_written;
+ bytes_written = bytes_written ? bytes_written : &dummy_bytes_written;
-uptr internal_write(fd_t fd, const void *buf, uptr count) {
- if (fd != kStderrFd)
- UNIMPLEMENTED();
- HANDLE err = GetStdHandle(STD_ERROR_HANDLE);
- if (err == 0)
- return 0; // FIXME: this might not work on some apps.
- DWORD ret;
- if (!WriteFile(err, buf, count, &ret, 0))
- return 0;
- return ret;
-}
+ // Initialize output parameters in case we fail.
+ *error_p = 0;
+ *bytes_written = 0;
-uptr internal_stat(const char *path, void *buf) {
- UNIMPLEMENTED();
-}
+ // Map the conventional Unix fds 1 and 2 to Windows handles. They might be
+ // closed, in which case this will fail.
+ if (fd == kStdoutFd || fd == kStderrFd) {
+ fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
+ if (fd == 0) {
+ *error_p = ERROR_INVALID_HANDLE;
+ return false;
+ }
+ }
-uptr internal_lstat(const char *path, void *buf) {
- UNIMPLEMENTED();
+ DWORD bytes_written_32;
+ if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) {
+ *error_p = GetLastError();
+ return false;
+ } else {
+ *bytes_written = bytes_written_32;
+ return true;
+ }
}
-uptr internal_fstat(fd_t fd, void *buf) {
+bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
UNIMPLEMENTED();
}
-uptr internal_filesize(fd_t fd) {
- UNIMPLEMENTED();
+uptr internal_sched_yield() {
+ Sleep(0);
+ return 0;
}
-uptr internal_dup2(int oldfd, int newfd) {
- UNIMPLEMENTED();
+void internal__exit(int exitcode) {
+ ExitProcess(exitcode);
}
-uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+uptr internal_ftruncate(fd_t fd, uptr size) {
UNIMPLEMENTED();
}
-uptr internal_sched_yield() {
- Sleep(0);
+uptr GetRSS() {
return 0;
}
-void internal__exit(int exitcode) {
- ExitProcess(exitcode);
-}
+void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
+void internal_join_thread(void *th) { }
// ---------------------- BlockingMutex ---------------- {{{1
const uptr LOCK_UNINITIALIZED = 0;
#endif
}
-void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
+#if !SANITIZER_GO
+void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
+ CHECK_GE(max_depth, 2);
// FIXME: CaptureStackBackTrace might be too slow for us.
// FIXME: Compare with StackWalk64.
// FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
size = CaptureStackBackTrace(2, Min(max_depth, kStackTraceMax),
(void**)trace, 0);
+ if (size == 0)
+ return;
+
// Skip the RTL frames by searching for the PC in the stacktrace.
uptr pc_location = LocatePcInTrace(pc);
PopStackFrames(pc_location);
}
-void MaybeOpenReportFile() {
- // Windows doesn't have native fork, and we don't support Cygwin or other
- // environments that try to fake it, so the initial report_fd will always be
- // correct.
+void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
+ u32 max_depth) {
+ CONTEXT ctx = *(CONTEXT *)context;
+ STACKFRAME64 stack_frame;
+ memset(&stack_frame, 0, sizeof(stack_frame));
+ size = 0;
+#if defined(_WIN64)
+ int machine_type = IMAGE_FILE_MACHINE_AMD64;
+ stack_frame.AddrPC.Offset = ctx.Rip;
+ stack_frame.AddrFrame.Offset = ctx.Rbp;
+ stack_frame.AddrStack.Offset = ctx.Rsp;
+#else
+ int machine_type = IMAGE_FILE_MACHINE_I386;
+ stack_frame.AddrPC.Offset = ctx.Eip;
+ stack_frame.AddrFrame.Offset = ctx.Ebp;
+ stack_frame.AddrStack.Offset = ctx.Esp;
+#endif
+ stack_frame.AddrPC.Mode = AddrModeFlat;
+ stack_frame.AddrFrame.Mode = AddrModeFlat;
+ stack_frame.AddrStack.Mode = AddrModeFlat;
+ while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
+ &stack_frame, &ctx, NULL, &SymFunctionTableAccess64,
+ &SymGetModuleBase64, NULL) &&
+ size < Min(max_depth, kStackTraceMax)) {
+ trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset;
+ }
}
+#endif // #if !SANITIZER_GO
-void RawWrite(const char *buffer) {
- static const char *kRawWriteError =
- "RawWrite can't output requested buffer!\n";
- uptr length = (uptr)internal_strlen(buffer);
- if (length != internal_write(report_fd, buffer, length)) {
+void ReportFile::Write(const char *buffer, uptr length) {
+ SpinMutexLock l(mu);
+ ReopenIfNecessary();
+ if (!WriteToFile(fd, buffer, length)) {
// stderr may be closed, but we may be able to print to the debugger
// instead. This is the case when launching a program from Visual Studio,
// and the following routine should write to its console.
}
}
+void SetAlternateSignalStack() {
+ // FIXME: Decide what to do on Windows.
+}
+
+void UnsetAlternateSignalStack() {
+ // FIXME: Decide what to do on Windows.
+}
+
+void InstallDeadlySignalHandlers(SignalHandlerType handler) {
+ (void)handler;
+ // FIXME: Decide what to do on Windows.
+}
+
+bool IsDeadlySignal(int signum) {
+ // FIXME: Decide what to do on Windows.
+ return false;
+}
+
+bool IsAccessibleMemoryRange(uptr beg, uptr size) {
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ uptr page_size = si.dwPageSize;
+ uptr page_mask = ~(page_size - 1);
+
+ for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask;
+ page <= end;) {
+ MEMORY_BASIC_INFORMATION info;
+ if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info))
+ return false;
+
+ if (info.Protect == 0 || info.Protect == PAGE_NOACCESS ||
+ info.Protect == PAGE_EXECUTE)
+ return false;
+
+ if (info.RegionSize == 0)
+ return false;
+
+ page += info.RegionSize;
+ }
+
+ return true;
+}
+
+SignalContext SignalContext::Create(void *siginfo, void *context) {
+ EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo;
+ CONTEXT *context_record = (CONTEXT*)context;
+
+ uptr pc = (uptr)exception_record->ExceptionAddress;
+#ifdef _WIN64
+ uptr bp = (uptr)context_record->Rbp;
+ uptr sp = (uptr)context_record->Rsp;
+#else
+ uptr bp = (uptr)context_record->Ebp;
+ uptr sp = (uptr)context_record->Esp;
+#endif
+ uptr access_addr = exception_record->ExceptionInformation[1];
+
+ return SignalContext(context, access_addr, pc, sp, bp);
+}
+
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+ // FIXME: Actually implement this function.
+ CHECK_GT(buf_len, 0);
+ buf[0] = 0;
+ return 0;
+}
+
+uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
+ return ReadBinaryName(buf, buf_len);
+}
+
+void CheckVMASize() {
+ // Do nothing.
+}
+
} // namespace __sanitizer
#endif // _WIN32
# May be used by toolexeclibdir.
gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
-DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 @OBSTACK_DEFS@
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++11
ACLOCAL_AMFLAGS = -I m4
toolexeclib_LTLIBRARIES = libtsan.la
tsan_files = \
- tsan_clock.cc \
- tsan_interface_atomic.cc \
- tsan_mutex.cc \
- tsan_report.cc \
- tsan_rtl_thread.cc \
- tsan_symbolize.cc \
- tsan_flags.cc \
- tsan_interface.cc \
- tsan_platform_linux.cc \
- tsan_rtl.cc \
- tsan_stat.cc \
- tsan_sync.cc \
- tsan_ignoreset.cc \
- tsan_interceptors.cc \
- tsan_md5.cc \
- tsan_platform_mac.cc \
- tsan_rtl_mutex.cc \
- tsan_suppressions.cc \
- tsan_interface_ann.cc \
- tsan_mman.cc \
- tsan_rtl_report.cc \
+ tsan_clock.cc \
tsan_fd.cc \
- tsan_interface_java.cc \
- tsan_mutexset.cc \
- tsan_symbolize_addr2line_linux.cc \
- tsan_rtl_amd64.S
+ tsan_flags.cc \
+ tsan_ignoreset.cc \
+ tsan_interceptors.cc \
+ tsan_interface_ann.cc \
+ tsan_interface_atomic.cc \
+ tsan_interface.cc \
+ tsan_interface_java.cc \
+ tsan_libdispatch_mac.cc \
+ tsan_malloc_mac.cc \
+ tsan_md5.cc \
+ tsan_mman.cc \
+ tsan_mutex.cc \
+ tsan_mutexset.cc \
+ tsan_new_delete.cc \
+ tsan_platform_linux.cc \
+ tsan_platform_mac.cc \
+ tsan_platform_posix.cc \
+ tsan_platform_windows.cc \
+ tsan_report.cc \
+ tsan_rtl.cc \
+ tsan_rtl_mutex.cc \
+ tsan_rtl_report.cc \
+ tsan_rtl_thread.cc \
+ tsan_stack_trace.cc \
+ tsan_stat.cc \
+ tsan_suppressions.cc \
+ tsan_symbolize.cc \
+ tsan_sync.cc
-libtsan_la_SOURCES = $(tsan_files)
-libtsan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la
+libtsan_la_SOURCES = $(tsan_files)
+EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S tsan_rtl_aarch64.S
+libtsan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(TSAN_TARGET_DEPENDENT_OBJECTS)
+libtsan_la_DEPENDENCIES = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(TSAN_TARGET_DEPENDENT_OBJECTS)
if LIBBACKTRACE_SUPPORTED
libtsan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+libtsan_la_DEPENDENCIES +=$(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
endif
libtsan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS)
libtsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libtsan)
host_triplet = @host@
target_triplet = @target@
@LIBBACKTRACE_SUPPORTED_TRUE@am__append_1 = $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
+@LIBBACKTRACE_SUPPORTED_TRUE@am__append_2 = $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la
subdir = tsan
DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__installdirs = "$(DESTDIR)$(toolexeclibdir)"
LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
am__DEPENDENCIES_1 =
-libtsan_la_DEPENDENCIES = \
- $(top_builddir)/sanitizer_common/libsanitizer_common.la \
- $(top_builddir)/interception/libinterception.la \
- $(am__append_1) $(am__DEPENDENCIES_1)
-am__objects_1 = tsan_clock.lo tsan_interface_atomic.lo tsan_mutex.lo \
- tsan_report.lo tsan_rtl_thread.lo tsan_symbolize.lo \
- tsan_flags.lo tsan_interface.lo tsan_platform_linux.lo \
- tsan_rtl.lo tsan_stat.lo tsan_sync.lo tsan_ignoreset.lo \
- tsan_interceptors.lo tsan_md5.lo tsan_platform_mac.lo \
- tsan_rtl_mutex.lo tsan_suppressions.lo tsan_interface_ann.lo \
- tsan_mman.lo tsan_rtl_report.lo tsan_fd.lo \
- tsan_interface_java.lo tsan_mutexset.lo \
- tsan_symbolize_addr2line_linux.lo tsan_rtl_amd64.lo
+am__objects_1 = tsan_clock.lo tsan_fd.lo tsan_flags.lo \
+ tsan_ignoreset.lo tsan_interceptors.lo tsan_interface_ann.lo \
+ tsan_interface_atomic.lo tsan_interface.lo \
+ tsan_interface_java.lo tsan_libdispatch_mac.lo \
+ tsan_malloc_mac.lo tsan_md5.lo tsan_mman.lo tsan_mutex.lo \
+ tsan_mutexset.lo tsan_new_delete.lo tsan_platform_linux.lo \
+ tsan_platform_mac.lo tsan_platform_posix.lo \
+ tsan_platform_windows.lo tsan_report.lo tsan_rtl.lo \
+ tsan_rtl_mutex.lo tsan_rtl_report.lo tsan_rtl_thread.lo \
+ tsan_stack_trace.lo tsan_stat.lo tsan_suppressions.lo \
+ tsan_symbolize.lo tsan_sync.lo
am_libtsan_la_OBJECTS = $(am__objects_1)
libtsan_la_OBJECTS = $(am_libtsan_la_OBJECTS)
libtsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
$(LDFLAGS) -o $@
-SOURCES = $(libtsan_la_SOURCES)
+SOURCES = $(libtsan_la_SOURCES) $(EXTRA_libtsan_la_SOURCES)
ETAGS = etags
CTAGS = ctags
ACLOCAL = @ACLOCAL@
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
-DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DCAN_SANITIZE_UB=0 @OBSTACK_DEFS@
DEPDIR = @DEPDIR@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
+OBSTACK_DEFS = @OBSTACK_DEFS@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
VERSION = @VERSION@
VIEW_FILE = @VIEW_FILE@
abs_builddir = @abs_builddir@
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
- -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+ -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) \
+ -std=gnu++11
ACLOCAL_AMFLAGS = -I m4
toolexeclib_LTLIBRARIES = libtsan.la
tsan_files = \
- tsan_clock.cc \
- tsan_interface_atomic.cc \
- tsan_mutex.cc \
- tsan_report.cc \
- tsan_rtl_thread.cc \
- tsan_symbolize.cc \
- tsan_flags.cc \
- tsan_interface.cc \
- tsan_platform_linux.cc \
- tsan_rtl.cc \
- tsan_stat.cc \
- tsan_sync.cc \
- tsan_ignoreset.cc \
- tsan_interceptors.cc \
- tsan_md5.cc \
- tsan_platform_mac.cc \
- tsan_rtl_mutex.cc \
- tsan_suppressions.cc \
- tsan_interface_ann.cc \
- tsan_mman.cc \
- tsan_rtl_report.cc \
+ tsan_clock.cc \
tsan_fd.cc \
- tsan_interface_java.cc \
- tsan_mutexset.cc \
- tsan_symbolize_addr2line_linux.cc \
- tsan_rtl_amd64.S
-
-libtsan_la_SOURCES = $(tsan_files)
+ tsan_flags.cc \
+ tsan_ignoreset.cc \
+ tsan_interceptors.cc \
+ tsan_interface_ann.cc \
+ tsan_interface_atomic.cc \
+ tsan_interface.cc \
+ tsan_interface_java.cc \
+ tsan_libdispatch_mac.cc \
+ tsan_malloc_mac.cc \
+ tsan_md5.cc \
+ tsan_mman.cc \
+ tsan_mutex.cc \
+ tsan_mutexset.cc \
+ tsan_new_delete.cc \
+ tsan_platform_linux.cc \
+ tsan_platform_mac.cc \
+ tsan_platform_posix.cc \
+ tsan_platform_windows.cc \
+ tsan_report.cc \
+ tsan_rtl.cc \
+ tsan_rtl_mutex.cc \
+ tsan_rtl_report.cc \
+ tsan_rtl_thread.cc \
+ tsan_stack_trace.cc \
+ tsan_stat.cc \
+ tsan_suppressions.cc \
+ tsan_symbolize.cc \
+ tsan_sync.cc
+
+libtsan_la_SOURCES = $(tsan_files)
+EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S tsan_rtl_aarch64.S
libtsan_la_LIBADD = \
$(top_builddir)/sanitizer_common/libsanitizer_common.la \
$(top_builddir)/interception/libinterception.la \
- $(am__append_1) $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+ $(TSAN_TARGET_DEPENDENT_OBJECTS) $(am__append_1) \
+ $(LIBSTDCXX_RAW_CXX_LDFLAGS)
+libtsan_la_DEPENDENCIES = \
+ $(top_builddir)/sanitizer_common/libsanitizer_common.la \
+ $(top_builddir)/interception/libinterception.la \
+ $(TSAN_TARGET_DEPENDENT_OBJECTS) $(am__append_2)
libtsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libtsan)
# Work around what appears to be a GNU make bug handling MAKEFLAGS
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_ann.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_atomic.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_java.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_libdispatch_mac.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_malloc_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_md5.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mman.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutex.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutexset.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_new_delete.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_mac.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_posix.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_windows.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_report.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_aarch64.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_amd64.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_mutex.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_report.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_thread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_stack_trace.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_stat.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_suppressions.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_symbolize.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_symbolize_addr2line_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_sync.Plo@am__quote@
.S.o:
//===----------------------------------------------------------------------===//
#include "tsan_clock.h"
#include "tsan_rtl.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
-// It's possible to optimize clock operations for some important cases
-// so that they are O(1). The cases include singletons, once's, local mutexes.
-// First, SyncClock must be re-implemented to allow indexing by tid.
-// It must not necessarily be a full vector clock, though. For example it may
-// be a multi-level table.
-// Then, each slot in SyncClock must contain a dirty bit (it's united with
-// the clock value, so no space increase). The acquire algorithm looks
-// as follows:
-// void acquire(thr, tid, thr_clock, sync_clock) {
-// if (!sync_clock[tid].dirty)
-// return; // No new info to acquire.
-// // This handles constant reads of singleton pointers and
-// // stop-flags.
-// acquire_impl(thr_clock, sync_clock); // As usual, O(N).
-// sync_clock[tid].dirty = false;
-// sync_clock.dirty_count--;
-// }
-// The release operation looks as follows:
-// void release(thr, tid, thr_clock, sync_clock) {
-// // thr->sync_cache is a simple fixed-size hash-based cache that holds
-// // several previous sync_clock's.
-// if (thr->sync_cache[sync_clock] >= thr->last_acquire_epoch) {
-// // The thread did no acquire operations since last release on this clock.
-// // So update only the thread's slot (other slots can't possibly change).
-// sync_clock[tid].clock = thr->epoch;
-// if (sync_clock.dirty_count == sync_clock.cnt
-// || (sync_clock.dirty_count == sync_clock.cnt - 1
-// && sync_clock[tid].dirty == false))
-// // All dirty flags are set, bail out.
-// return;
-// set all dirty bits, but preserve the thread's bit. // O(N)
-// update sync_clock.dirty_count;
-// return;
+// SyncClock and ThreadClock implement vector clocks for sync variables
+// (mutexes, atomic variables, file descriptors, etc) and threads, respectively.
+// ThreadClock contains fixed-size vector clock for maximum number of threads.
+// SyncClock contains growable vector clock for currently necessary number of
+// threads.
+// Together they implement very simple model of operations, namely:
+//
+// void ThreadClock::acquire(const SyncClock *src) {
+// for (int i = 0; i < kMaxThreads; i++)
+// clock[i] = max(clock[i], src->clock[i]);
+// }
+//
+// void ThreadClock::release(SyncClock *dst) const {
+// for (int i = 0; i < kMaxThreads; i++)
+// dst->clock[i] = max(dst->clock[i], clock[i]);
+// }
+//
+// void ThreadClock::ReleaseStore(SyncClock *dst) const {
+// for (int i = 0; i < kMaxThreads; i++)
+// dst->clock[i] = clock[i];
// }
-// release_impl(thr_clock, sync_clock); // As usual, O(N).
-// set all dirty bits, but preserve the thread's bit.
-// // The previous step is combined with release_impl(), so that
-// // we scan the arrays only once.
-// update sync_clock.dirty_count;
-// }
+//
+// void ThreadClock::acq_rel(SyncClock *dst) {
+// acquire(dst);
+// release(dst);
+// }
+//
+// Conformance to this model is extensively verified in tsan_clock_test.cc.
+// However, the implementation is significantly more complex. The complexity
+// allows to implement important classes of use cases in O(1) instead of O(N).
+//
+// The use cases are:
+// 1. Singleton/once atomic that has a single release-store operation followed
+// by zillions of acquire-loads (the acquire-load is O(1)).
+// 2. Thread-local mutex (both lock and unlock can be O(1)).
+// 3. Leaf mutex (unlock is O(1)).
+// 4. A mutex shared by 2 threads (both lock and unlock can be O(1)).
+// 5. An atomic with a single writer (writes can be O(1)).
+// The implementation dynamically adopts to workload. So if an atomic is in
+// read-only phase, these reads will be O(1); if it later switches to read/write
+// phase, the implementation will correctly handle that by switching to O(N).
+//
+// Thread-safety note: all const operations on SyncClock's are conducted under
+// a shared lock; all non-const operations on SyncClock's are conducted under
+// an exclusive lock; ThreadClock's are private to respective threads and so
+// do not need any protection.
+//
+// Description of ThreadClock state:
+// clk_ - fixed size vector clock.
+// nclk_ - effective size of the vector clock (the rest is zeros).
+// tid_ - index of the thread associated with he clock ("current thread").
+// last_acquire_ - current thread time when it acquired something from
+// other threads.
+//
+// Description of SyncClock state:
+// clk_ - variable size vector clock, low kClkBits hold timestamp,
+// the remaining bits hold "acquired" flag (the actual value is thread's
+// reused counter);
+// if acquried == thr->reused_, then the respective thread has already
+// acquired this clock (except possibly dirty_tids_).
+// dirty_tids_ - holds up to two indeces in the vector clock that other threads
+// need to acquire regardless of "acquired" flag value;
+// release_store_tid_ - denotes that the clock state is a result of
+// release-store operation by the thread with release_store_tid_ index.
+// release_store_reused_ - reuse count of release_store_tid_.
+
+// We don't have ThreadState in these methods, so this is an ugly hack that
+// works only in C++.
+#ifndef SANITIZER_GO
+# define CPP_STAT_INC(typ) StatInc(cur_thread(), typ)
+#else
+# define CPP_STAT_INC(typ) (void)0
+#endif
namespace __tsan {
-ThreadClock::ThreadClock() {
- nclk_ = 0;
- for (uptr i = 0; i < (uptr)kMaxTidInClock; i++)
- clk_[i] = 0;
+ThreadClock::ThreadClock(unsigned tid, unsigned reused)
+ : tid_(tid)
+ , reused_(reused + 1) { // 0 has special meaning
+ CHECK_LT(tid, kMaxTidInClock);
+ CHECK_EQ(reused_, ((u64)reused_ << kClkBits) >> kClkBits);
+ nclk_ = tid_ + 1;
+ last_acquire_ = 0;
+ internal_memset(clk_, 0, sizeof(clk_));
+ clk_[tid_].reused = reused_;
}
-void ThreadClock::acquire(const SyncClock *src) {
- DCHECK(nclk_ <= kMaxTid);
- DCHECK(src->clk_.Size() <= kMaxTid);
+void ThreadClock::acquire(ClockCache *c, const SyncClock *src) {
+ DCHECK_LE(nclk_, kMaxTid);
+ DCHECK_LE(src->size_, kMaxTid);
+ CPP_STAT_INC(StatClockAcquire);
- const uptr nclk = src->clk_.Size();
- if (nclk == 0)
+ // Check if it's empty -> no need to do anything.
+ const uptr nclk = src->size_;
+ if (nclk == 0) {
+ CPP_STAT_INC(StatClockAcquireEmpty);
return;
+ }
+
+ // Check if we've already acquired src after the last release operation on src
+ bool acquired = false;
+ if (nclk > tid_) {
+ CPP_STAT_INC(StatClockAcquireLarge);
+ if (src->elem(tid_).reused == reused_) {
+ CPP_STAT_INC(StatClockAcquireRepeat);
+ for (unsigned i = 0; i < kDirtyTids; i++) {
+ unsigned tid = src->dirty_tids_[i];
+ if (tid != kInvalidTid) {
+ u64 epoch = src->elem(tid).epoch;
+ if (clk_[tid].epoch < epoch) {
+ clk_[tid].epoch = epoch;
+ acquired = true;
+ }
+ }
+ }
+ if (acquired) {
+ CPP_STAT_INC(StatClockAcquiredSomething);
+ last_acquire_ = clk_[tid_].epoch;
+ }
+ return;
+ }
+ }
+
+ // O(N) acquire.
+ CPP_STAT_INC(StatClockAcquireFull);
nclk_ = max(nclk_, nclk);
for (uptr i = 0; i < nclk; i++) {
- if (clk_[i] < src->clk_[i])
- clk_[i] = src->clk_[i];
+ u64 epoch = src->elem(i).epoch;
+ if (clk_[i].epoch < epoch) {
+ clk_[i].epoch = epoch;
+ acquired = true;
+ }
+ }
+
+ // Remember that this thread has acquired this clock.
+ if (nclk > tid_)
+ src->elem(tid_).reused = reused_;
+
+ if (acquired) {
+ CPP_STAT_INC(StatClockAcquiredSomething);
+ last_acquire_ = clk_[tid_].epoch;
}
}
-void ThreadClock::release(SyncClock *dst) const {
- DCHECK(nclk_ <= kMaxTid);
- DCHECK(dst->clk_.Size() <= kMaxTid);
+void ThreadClock::release(ClockCache *c, SyncClock *dst) const {
+ DCHECK_LE(nclk_, kMaxTid);
+ DCHECK_LE(dst->size_, kMaxTid);
+
+ if (dst->size_ == 0) {
+ // ReleaseStore will correctly set release_store_tid_,
+ // which can be important for future operations.
+ ReleaseStore(c, dst);
+ return;
+ }
+
+ CPP_STAT_INC(StatClockRelease);
+ // Check if we need to resize dst.
+ if (dst->size_ < nclk_)
+ dst->Resize(c, nclk_);
+
+ // Check if we had not acquired anything from other threads
+ // since the last release on dst. If so, we need to update
+ // only dst->elem(tid_).
+ if (dst->elem(tid_).epoch > last_acquire_) {
+ UpdateCurrentThread(dst);
+ if (dst->release_store_tid_ != tid_ ||
+ dst->release_store_reused_ != reused_)
+ dst->release_store_tid_ = kInvalidTid;
+ return;
+ }
- if (dst->clk_.Size() < nclk_)
- dst->clk_.Resize(nclk_);
+ // O(N) release.
+ CPP_STAT_INC(StatClockReleaseFull);
+ // First, remember whether we've acquired dst.
+ bool acquired = IsAlreadyAcquired(dst);
+ if (acquired)
+ CPP_STAT_INC(StatClockReleaseAcquired);
+ // Update dst->clk_.
for (uptr i = 0; i < nclk_; i++) {
- if (dst->clk_[i] < clk_[i])
- dst->clk_[i] = clk_[i];
+ ClockElem &ce = dst->elem(i);
+ ce.epoch = max(ce.epoch, clk_[i].epoch);
+ ce.reused = 0;
}
+ // Clear 'acquired' flag in the remaining elements.
+ if (nclk_ < dst->size_)
+ CPP_STAT_INC(StatClockReleaseClearTail);
+ for (uptr i = nclk_; i < dst->size_; i++)
+ dst->elem(i).reused = 0;
+ for (unsigned i = 0; i < kDirtyTids; i++)
+ dst->dirty_tids_[i] = kInvalidTid;
+ dst->release_store_tid_ = kInvalidTid;
+ dst->release_store_reused_ = 0;
+ // If we've acquired dst, remember this fact,
+ // so that we don't need to acquire it on next acquire.
+ if (acquired)
+ dst->elem(tid_).reused = reused_;
}
-void ThreadClock::ReleaseStore(SyncClock *dst) const {
- DCHECK(nclk_ <= kMaxTid);
- DCHECK(dst->clk_.Size() <= kMaxTid);
+void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) const {
+ DCHECK_LE(nclk_, kMaxTid);
+ DCHECK_LE(dst->size_, kMaxTid);
+ CPP_STAT_INC(StatClockStore);
- if (dst->clk_.Size() < nclk_)
- dst->clk_.Resize(nclk_);
- for (uptr i = 0; i < nclk_; i++)
- dst->clk_[i] = clk_[i];
- for (uptr i = nclk_; i < dst->clk_.Size(); i++)
- dst->clk_[i] = 0;
+ // Check if we need to resize dst.
+ if (dst->size_ < nclk_)
+ dst->Resize(c, nclk_);
+
+ if (dst->release_store_tid_ == tid_ &&
+ dst->release_store_reused_ == reused_ &&
+ dst->elem(tid_).epoch > last_acquire_) {
+ CPP_STAT_INC(StatClockStoreFast);
+ UpdateCurrentThread(dst);
+ return;
+ }
+
+ // O(N) release-store.
+ CPP_STAT_INC(StatClockStoreFull);
+ for (uptr i = 0; i < nclk_; i++) {
+ ClockElem &ce = dst->elem(i);
+ ce.epoch = clk_[i].epoch;
+ ce.reused = 0;
+ }
+ // Clear the tail of dst->clk_.
+ if (nclk_ < dst->size_) {
+ for (uptr i = nclk_; i < dst->size_; i++) {
+ ClockElem &ce = dst->elem(i);
+ ce.epoch = 0;
+ ce.reused = 0;
+ }
+ CPP_STAT_INC(StatClockStoreTail);
+ }
+ for (unsigned i = 0; i < kDirtyTids; i++)
+ dst->dirty_tids_[i] = kInvalidTid;
+ dst->release_store_tid_ = tid_;
+ dst->release_store_reused_ = reused_;
+ // Rememeber that we don't need to acquire it in future.
+ dst->elem(tid_).reused = reused_;
+}
+
+void ThreadClock::acq_rel(ClockCache *c, SyncClock *dst) {
+ CPP_STAT_INC(StatClockAcquireRelease);
+ acquire(c, dst);
+ ReleaseStore(c, dst);
+}
+
+// Updates only single element related to the current thread in dst->clk_.
+void ThreadClock::UpdateCurrentThread(SyncClock *dst) const {
+ // Update the threads time, but preserve 'acquired' flag.
+ dst->elem(tid_).epoch = clk_[tid_].epoch;
+
+ for (unsigned i = 0; i < kDirtyTids; i++) {
+ if (dst->dirty_tids_[i] == tid_) {
+ CPP_STAT_INC(StatClockReleaseFast1);
+ return;
+ }
+ if (dst->dirty_tids_[i] == kInvalidTid) {
+ CPP_STAT_INC(StatClockReleaseFast2);
+ dst->dirty_tids_[i] = tid_;
+ return;
+ }
+ }
+ // Reset all 'acquired' flags, O(N).
+ CPP_STAT_INC(StatClockReleaseSlow);
+ for (uptr i = 0; i < dst->size_; i++)
+ dst->elem(i).reused = 0;
+ for (unsigned i = 0; i < kDirtyTids; i++)
+ dst->dirty_tids_[i] = kInvalidTid;
+}
+
+// Checks whether the current threads has already acquired src.
+bool ThreadClock::IsAlreadyAcquired(const SyncClock *src) const {
+ if (src->elem(tid_).reused != reused_)
+ return false;
+ for (unsigned i = 0; i < kDirtyTids; i++) {
+ unsigned tid = src->dirty_tids_[i];
+ if (tid != kInvalidTid) {
+ if (clk_[tid].epoch < src->elem(tid).epoch)
+ return false;
+ }
+ }
+ return true;
+}
+
+void SyncClock::Resize(ClockCache *c, uptr nclk) {
+ CPP_STAT_INC(StatClockReleaseResize);
+ if (RoundUpTo(nclk, ClockBlock::kClockCount) <=
+ RoundUpTo(size_, ClockBlock::kClockCount)) {
+ // Growing within the same block.
+ // Memory is already allocated, just increase the size.
+ size_ = nclk;
+ return;
+ }
+ if (nclk <= ClockBlock::kClockCount) {
+ // Grow from 0 to one-level table.
+ CHECK_EQ(size_, 0);
+ CHECK_EQ(tab_, 0);
+ CHECK_EQ(tab_idx_, 0);
+ size_ = nclk;
+ tab_idx_ = ctx->clock_alloc.Alloc(c);
+ tab_ = ctx->clock_alloc.Map(tab_idx_);
+ internal_memset(tab_, 0, sizeof(*tab_));
+ return;
+ }
+ // Growing two-level table.
+ if (size_ == 0) {
+ // Allocate first level table.
+ tab_idx_ = ctx->clock_alloc.Alloc(c);
+ tab_ = ctx->clock_alloc.Map(tab_idx_);
+ internal_memset(tab_, 0, sizeof(*tab_));
+ } else if (size_ <= ClockBlock::kClockCount) {
+ // Transform one-level table to two-level table.
+ u32 old = tab_idx_;
+ tab_idx_ = ctx->clock_alloc.Alloc(c);
+ tab_ = ctx->clock_alloc.Map(tab_idx_);
+ internal_memset(tab_, 0, sizeof(*tab_));
+ tab_->table[0] = old;
+ }
+ // At this point we have first level table allocated.
+ // Add second level tables as necessary.
+ for (uptr i = RoundUpTo(size_, ClockBlock::kClockCount);
+ i < nclk; i += ClockBlock::kClockCount) {
+ u32 idx = ctx->clock_alloc.Alloc(c);
+ ClockBlock *cb = ctx->clock_alloc.Map(idx);
+ internal_memset(cb, 0, sizeof(*cb));
+ CHECK_EQ(tab_->table[i/ClockBlock::kClockCount], 0);
+ tab_->table[i/ClockBlock::kClockCount] = idx;
+ }
+ size_ = nclk;
+}
+
+// Sets a single element in the vector clock.
+// This function is called only from weird places like AcquireGlobal.
+void ThreadClock::set(unsigned tid, u64 v) {
+ DCHECK_LT(tid, kMaxTid);
+ DCHECK_GE(v, clk_[tid].epoch);
+ clk_[tid].epoch = v;
+ if (nclk_ <= tid)
+ nclk_ = tid + 1;
+ last_acquire_ = clk_[tid_].epoch;
}
-void ThreadClock::acq_rel(SyncClock *dst) {
- acquire(dst);
- release(dst);
+void ThreadClock::DebugDump(int(*printf)(const char *s, ...)) {
+ printf("clock=[");
+ for (uptr i = 0; i < nclk_; i++)
+ printf("%s%llu", i == 0 ? "" : ",", clk_[i].epoch);
+ printf("] reused=[");
+ for (uptr i = 0; i < nclk_; i++)
+ printf("%s%llu", i == 0 ? "" : ",", clk_[i].reused);
+ printf("] tid=%u/%u last_acq=%llu",
+ tid_, reused_, last_acquire_);
}
SyncClock::SyncClock()
- : clk_(MBlockClock) {
+ : release_store_tid_(kInvalidTid)
+ , release_store_reused_()
+ , tab_()
+ , tab_idx_()
+ , size_() {
+ for (uptr i = 0; i < kDirtyTids; i++)
+ dirty_tids_[i] = kInvalidTid;
+}
+
+SyncClock::~SyncClock() {
+ // Reset must be called before dtor.
+ CHECK_EQ(size_, 0);
+ CHECK_EQ(tab_, 0);
+ CHECK_EQ(tab_idx_, 0);
+}
+
+void SyncClock::Reset(ClockCache *c) {
+ if (size_ == 0) {
+ // nothing
+ } else if (size_ <= ClockBlock::kClockCount) {
+ // One-level table.
+ ctx->clock_alloc.Free(c, tab_idx_);
+ } else {
+ // Two-level table.
+ for (uptr i = 0; i < size_; i += ClockBlock::kClockCount)
+ ctx->clock_alloc.Free(c, tab_->table[i / ClockBlock::kClockCount]);
+ ctx->clock_alloc.Free(c, tab_idx_);
+ }
+ tab_ = 0;
+ tab_idx_ = 0;
+ size_ = 0;
+ release_store_tid_ = kInvalidTid;
+ release_store_reused_ = 0;
+ for (uptr i = 0; i < kDirtyTids; i++)
+ dirty_tids_[i] = kInvalidTid;
+}
+
+ClockElem &SyncClock::elem(unsigned tid) const {
+ DCHECK_LT(tid, size_);
+ if (size_ <= ClockBlock::kClockCount)
+ return tab_->clock[tid];
+ u32 idx = tab_->table[tid / ClockBlock::kClockCount];
+ ClockBlock *cb = ctx->clock_alloc.Map(idx);
+ return cb->clock[tid % ClockBlock::kClockCount];
+}
+
+void SyncClock::DebugDump(int(*printf)(const char *s, ...)) {
+ printf("clock=[");
+ for (uptr i = 0; i < size_; i++)
+ printf("%s%llu", i == 0 ? "" : ",", elem(i).epoch);
+ printf("] reused=[");
+ for (uptr i = 0; i < size_; i++)
+ printf("%s%llu", i == 0 ? "" : ",", elem(i).reused);
+ printf("] release_store_tid=%d/%d dirty_tids=%d/%d",
+ release_store_tid_, release_store_reused_,
+ dirty_tids_[0], dirty_tids_[1]);
}
} // namespace __tsan
#define TSAN_CLOCK_H
#include "tsan_defs.h"
-#include "tsan_vector.h"
+#include "tsan_dense_alloc.h"
namespace __tsan {
+struct ClockElem {
+ u64 epoch : kClkBits;
+ u64 reused : 64 - kClkBits;
+};
+
+struct ClockBlock {
+ static const uptr kSize = 512;
+ static const uptr kTableSize = kSize / sizeof(u32);
+ static const uptr kClockCount = kSize / sizeof(ClockElem);
+
+ union {
+ u32 table[kTableSize];
+ ClockElem clock[kClockCount];
+ };
+
+ ClockBlock() {
+ }
+};
+
+typedef DenseSlabAlloc<ClockBlock, 1<<16, 1<<10> ClockAlloc;
+typedef DenseSlabAllocCache ClockCache;
+
// The clock that lives in sync variables (mutexes, atomics, etc).
class SyncClock {
public:
SyncClock();
+ ~SyncClock();
uptr size() const {
- return clk_.Size();
+ return size_;
}
- void Reset() {
- clk_.Reset();
+ u64 get(unsigned tid) const {
+ return elem(tid).epoch;
}
+ void Resize(ClockCache *c, uptr nclk);
+ void Reset(ClockCache *c);
+
+ void DebugDump(int(*printf)(const char *s, ...));
+
private:
- Vector<u64> clk_;
friend struct ThreadClock;
+ static const uptr kDirtyTids = 2;
+
+ unsigned release_store_tid_;
+ unsigned release_store_reused_;
+ unsigned dirty_tids_[kDirtyTids];
+ // tab_ contains indirect pointer to a 512b block using DenseSlabAlloc.
+ // If size_ <= 64, then tab_ points to an array with 64 ClockElem's.
+ // Otherwise, tab_ points to an array with 128 u32 elements,
+ // each pointing to the second-level 512b block with 64 ClockElem's.
+ ClockBlock *tab_;
+ u32 tab_idx_;
+ u32 size_;
+
+ ClockElem &elem(unsigned tid) const;
};
// The clock that lives in threads.
struct ThreadClock {
public:
- ThreadClock();
+ typedef DenseSlabAllocCache Cache;
+
+ explicit ThreadClock(unsigned tid, unsigned reused = 0);
u64 get(unsigned tid) const {
DCHECK_LT(tid, kMaxTidInClock);
- return clk_[tid];
+ return clk_[tid].epoch;
}
- void set(unsigned tid, u64 v) {
- DCHECK_LT(tid, kMaxTid);
- DCHECK_GE(v, clk_[tid]);
- clk_[tid] = v;
- if (nclk_ <= tid)
- nclk_ = tid + 1;
+ void set(unsigned tid, u64 v);
+
+ void set(u64 v) {
+ DCHECK_GE(v, clk_[tid_].epoch);
+ clk_[tid_].epoch = v;
}
- void tick(unsigned tid) {
- DCHECK_LT(tid, kMaxTid);
- clk_[tid]++;
- if (nclk_ <= tid)
- nclk_ = tid + 1;
+ void tick() {
+ clk_[tid_].epoch++;
}
uptr size() const {
return nclk_;
}
- void acquire(const SyncClock *src);
- void release(SyncClock *dst) const;
- void acq_rel(SyncClock *dst);
- void ReleaseStore(SyncClock *dst) const;
+ void acquire(ClockCache *c, const SyncClock *src);
+ void release(ClockCache *c, SyncClock *dst) const;
+ void acq_rel(ClockCache *c, SyncClock *dst);
+ void ReleaseStore(ClockCache *c, SyncClock *dst) const;
+
+ void DebugReset();
+ void DebugDump(int(*printf)(const char *s, ...));
private:
+ static const uptr kDirtyTids = SyncClock::kDirtyTids;
+ const unsigned tid_;
+ const unsigned reused_;
+ u64 last_acquire_;
uptr nclk_;
- u64 clk_[kMaxTidInClock];
+ ClockElem clk_[kMaxTidInClock];
+
+ bool IsAlreadyAcquired(const SyncClock *src) const;
+ void UpdateCurrentThread(SyncClock *dst) const;
};
} // namespace __tsan
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_stat.h"
+#include "ubsan/ubsan_platform.h"
-#ifndef TSAN_DEBUG
-#define TSAN_DEBUG 0
-#endif // TSAN_DEBUG
+// Setup defaults for compile definitions.
+#ifndef TSAN_NO_HISTORY
+# define TSAN_NO_HISTORY 0
+#endif
+
+#ifndef TSAN_COLLECT_STATS
+# define TSAN_COLLECT_STATS 0
+#endif
+
+#ifndef TSAN_CONTAINS_UBSAN
+# define TSAN_CONTAINS_UBSAN (CAN_SANITIZE_UB && !defined(SANITIZER_GO))
+#endif
namespace __tsan {
-#ifdef TSAN_GO
+#ifdef SANITIZER_GO
const bool kGoMode = true;
const bool kCppMode = false;
const char *const kTsanOptionsEnv = "GORACE";
const int kTidBits = 13;
const unsigned kMaxTid = 1 << kTidBits;
+#ifndef SANITIZER_GO
const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit.
+#else
+const unsigned kMaxTidInClock = kMaxTid; // Go does not track freed memory.
+#endif
const int kClkBits = 42;
+const unsigned kMaxTidReuse = (1 << (64 - kClkBits)) - 1;
const uptr kShadowStackSize = 64 * 1024;
-const uptr kTraceStackSize = 256;
-
-#ifdef TSAN_SHADOW_COUNT
-# if TSAN_SHADOW_COUNT == 2 \
- || TSAN_SHADOW_COUNT == 4 || TSAN_SHADOW_COUNT == 8
-const uptr kShadowCnt = TSAN_SHADOW_COUNT;
-# else
-# error "TSAN_SHADOW_COUNT must be one of 2,4,8"
-# endif
-#else
+
// Count of shadow values in a shadow cell.
const uptr kShadowCnt = 4;
-#endif
// That many user bytes are mapped onto a single shadow cell.
const uptr kShadowCell = 8;
// Shadow memory is kShadowMultiplier times larger than user memory.
const uptr kShadowMultiplier = kShadowSize * kShadowCnt / kShadowCell;
-#if defined(TSAN_COLLECT_STATS) && TSAN_COLLECT_STATS
-const bool kCollectStats = true;
+// That many user bytes are mapped onto a single meta shadow cell.
+// Must be less or equal to minimal memory allocator alignment.
+const uptr kMetaShadowCell = 8;
+
+// Size of a single meta shadow value (u32).
+const uptr kMetaShadowSize = 4;
+
+#if TSAN_NO_HISTORY
+const bool kCollectHistory = false;
#else
-const bool kCollectStats = false;
+const bool kCollectHistory = true;
#endif
+const unsigned kInvalidTid = (unsigned)-1;
+
// The following "build consistency" machinery ensures that all source files
// are built in the same configuration. Inconsistent builds lead to
// hard to debug crashes.
-#if TSAN_DEBUG
+#if SANITIZER_DEBUG
void build_consistency_debug();
#else
void build_consistency_release();
void build_consistency_nostats();
#endif
-#if TSAN_SHADOW_COUNT == 1
-void build_consistency_shadow1();
-#elif TSAN_SHADOW_COUNT == 2
-void build_consistency_shadow2();
-#elif TSAN_SHADOW_COUNT == 4
-void build_consistency_shadow4();
-#else
-void build_consistency_shadow8();
-#endif
-
static inline void USED build_consistency() {
-#if TSAN_DEBUG
+#if SANITIZER_DEBUG
build_consistency_debug();
#else
build_consistency_release();
#else
build_consistency_nostats();
#endif
-#if TSAN_SHADOW_COUNT == 1
- build_consistency_shadow1();
-#elif TSAN_SHADOW_COUNT == 2
- build_consistency_shadow2();
-#elif TSAN_SHADOW_COUNT == 4
- build_consistency_shadow4();
-#else
- build_consistency_shadow8();
-#endif
}
template<typename T>
struct ReportStack;
class ReportDesc;
class RegionAlloc;
-class StackTrace;
-struct MBlock;
+
+// Descriptor of user's memory block.
+struct MBlock {
+ u64 siz;
+ u32 stk;
+ u16 tid;
+};
+
+COMPILER_CHECK(sizeof(MBlock) == 16);
} // namespace __tsan
--- /dev/null
+//===-- tsan_dense_alloc.h --------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// A DenseSlabAlloc is a freelist-based allocator of fixed-size objects.
+// DenseSlabAllocCache is a thread-local cache for DenseSlabAlloc.
+// The only difference with traditional slab allocators is that DenseSlabAlloc
+// allocates/free indices of objects and provide a functionality to map
+// the index onto the real pointer. The index is u32, that is, 2 times smaller
+// than uptr (hense the Dense prefix).
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_DENSE_ALLOC_H
+#define TSAN_DENSE_ALLOC_H
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "tsan_defs.h"
+#include "tsan_mutex.h"
+
+namespace __tsan {
+
+class DenseSlabAllocCache {
+ static const uptr kSize = 128;
+ typedef u32 IndexT;
+ uptr pos;
+ IndexT cache[kSize];
+ template<typename T, uptr kL1Size, uptr kL2Size> friend class DenseSlabAlloc;
+};
+
+template<typename T, uptr kL1Size, uptr kL2Size>
+class DenseSlabAlloc {
+ public:
+ typedef DenseSlabAllocCache Cache;
+ typedef typename Cache::IndexT IndexT;
+
+ DenseSlabAlloc() {
+ // Check that kL1Size and kL2Size are sane.
+ CHECK_EQ(kL1Size & (kL1Size - 1), 0);
+ CHECK_EQ(kL2Size & (kL2Size - 1), 0);
+ CHECK_GE(1ull << (sizeof(IndexT) * 8), kL1Size * kL2Size);
+ // Check that it makes sense to use the dense alloc.
+ CHECK_GE(sizeof(T), sizeof(IndexT));
+ internal_memset(map_, 0, sizeof(map_));
+ freelist_ = 0;
+ fillpos_ = 0;
+ }
+
+ ~DenseSlabAlloc() {
+ for (uptr i = 0; i < kL1Size; i++) {
+ if (map_[i] != 0)
+ UnmapOrDie(map_[i], kL2Size * sizeof(T));
+ }
+ }
+
+ IndexT Alloc(Cache *c) {
+ if (c->pos == 0)
+ Refill(c);
+ return c->cache[--c->pos];
+ }
+
+ void Free(Cache *c, IndexT idx) {
+ DCHECK_NE(idx, 0);
+ if (c->pos == Cache::kSize)
+ Drain(c);
+ c->cache[c->pos++] = idx;
+ }
+
+ T *Map(IndexT idx) {
+ DCHECK_NE(idx, 0);
+ DCHECK_LE(idx, kL1Size * kL2Size);
+ return &map_[idx / kL2Size][idx % kL2Size];
+ }
+
+ void FlushCache(Cache *c) {
+ SpinMutexLock lock(&mtx_);
+ while (c->pos) {
+ IndexT idx = c->cache[--c->pos];
+ *(IndexT*)Map(idx) = freelist_;
+ freelist_ = idx;
+ }
+ }
+
+ void InitCache(Cache *c) {
+ c->pos = 0;
+ internal_memset(c->cache, 0, sizeof(c->cache));
+ }
+
+ private:
+ T *map_[kL1Size];
+ SpinMutex mtx_;
+ IndexT freelist_;
+ uptr fillpos_;
+
+ void Refill(Cache *c) {
+ SpinMutexLock lock(&mtx_);
+ if (freelist_ == 0) {
+ if (fillpos_ == kL1Size) {
+ Printf("ThreadSanitizer: DenseSlabAllocator overflow. Dying.\n");
+ Die();
+ }
+ T *batch = (T*)MmapOrDie(kL2Size * sizeof(T), "DenseSlabAllocator");
+ // Reserve 0 as invalid index.
+ IndexT start = fillpos_ == 0 ? 1 : 0;
+ for (IndexT i = start; i < kL2Size; i++) {
+ new(batch + i) T();
+ *(IndexT*)(batch + i) = i + 1 + fillpos_ * kL2Size;
+ }
+ *(IndexT*)(batch + kL2Size - 1) = 0;
+ freelist_ = fillpos_ * kL2Size + start;
+ map_[fillpos_++] = batch;
+ }
+ for (uptr i = 0; i < Cache::kSize / 2 && freelist_ != 0; i++) {
+ IndexT idx = freelist_;
+ c->cache[c->pos++] = idx;
+ freelist_ = *(IndexT*)Map(idx);
+ }
+ }
+
+ void Drain(Cache *c) {
+ SpinMutexLock lock(&mtx_);
+ for (uptr i = 0; i < Cache::kSize / 2; i++) {
+ IndexT idx = c->cache[--c->pos];
+ *(IndexT*)Map(idx) = freelist_;
+ freelist_ = idx;
+ }
+ }
+};
+
+} // namespace __tsan
+
+#endif // TSAN_DENSE_ALLOC_H
return fd < 0 || fd >= kTableSize;
}
-static FdSync *allocsync() {
- FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync));
+static FdSync *allocsync(ThreadState *thr, uptr pc) {
+ FdSync *s = (FdSync*)user_alloc(thr, pc, sizeof(FdSync), kDefaultAlignment,
+ false);
atomic_store(&s->rc, 1, memory_order_relaxed);
return s;
}
CHECK_NE(s, &fdctx.globsync);
CHECK_NE(s, &fdctx.filesync);
CHECK_NE(s, &fdctx.socksync);
- SyncVar *v = CTX()->synctab.GetAndRemove(thr, pc, (uptr)s);
- if (v)
- DestroyAndFree(v);
- internal_free(s);
+ user_free(thr, pc, s, false);
}
}
}
if (l1 == 0) {
uptr size = kTableSizeL2 * sizeof(FdDesc);
// We need this to reside in user memory to properly catch races on it.
- void *p = user_alloc(thr, pc, size);
+ void *p = user_alloc(thr, pc, size, kDefaultAlignment, false);
internal_memset(p, 0, size);
MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size);
if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel))
l1 = (uptr)p;
else
- user_free(thr, pc, p);
+ user_free(thr, pc, p, false);
}
return &((FdDesc*)l1)[fd % kTableSizeL2]; // NOLINT
}
// pd must be already ref'ed.
-static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) {
+static void init(ThreadState *thr, uptr pc, int fd, FdSync *s,
+ bool write = true) {
FdDesc *d = fddesc(thr, pc, fd);
// As a matter of fact, we don't intercept all close calls.
// See e.g. libc __res_iclose().
}
d->creation_tid = thr->tid;
d->creation_stack = CurrentStackId(thr, pc);
- // To catch races between fd usage and open.
- MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
+ if (write) {
+ // To catch races between fd usage and open.
+ MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
+ } else {
+ // See the dup-related comment in FdClose.
+ MemoryRead(thr, pc, (uptr)d, kSizeLog8);
+ }
}
void FdInit() {
MemoryRead(thr, pc, (uptr)d, kSizeLog8);
}
-void FdClose(ThreadState *thr, uptr pc, int fd) {
+void FdClose(ThreadState *thr, uptr pc, int fd, bool write) {
DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
if (bogusfd(fd))
return;
FdDesc *d = fddesc(thr, pc, fd);
- // To catch races between fd usage and close.
- MemoryWrite(thr, pc, (uptr)d, kSizeLog8);
+ if (write) {
+ // To catch races between fd usage and close.
+ MemoryWrite(thr, pc, (uptr)d, kSizeLog8);
+ } else {
+ // This path is used only by dup2/dup3 calls.
+ // We do read instead of write because there is a number of legitimate
+ // cases where write would lead to false positives:
+ // 1. Some software dups a closed pipe in place of a socket before closing
+ // the socket (to prevent races actually).
+ // 2. Some daemons dup /dev/null in place of stdin/stdout.
+ // On the other hand we have not seen cases when write here catches real
+ // bugs.
+ MemoryRead(thr, pc, (uptr)d, kSizeLog8);
+ }
// We need to clear it, because if we do not intercept any call out there
// that creates fd, we will hit false postives.
MemoryResetRange(thr, pc, (uptr)d, 8);
init(thr, pc, fd, &fdctx.filesync);
}
-void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) {
+void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write) {
DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);
if (bogusfd(oldfd) || bogusfd(newfd))
return;
// Ignore the case when user dups not yet connected socket.
FdDesc *od = fddesc(thr, pc, oldfd);
MemoryRead(thr, pc, (uptr)od, kSizeLog8);
- FdClose(thr, pc, newfd);
- init(thr, pc, newfd, ref(od->sync));
+ FdClose(thr, pc, newfd, write);
+ init(thr, pc, newfd, ref(od->sync), write);
}
void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd);
- FdSync *s = allocsync();
+ FdSync *s = allocsync(thr, pc);
init(thr, pc, rfd, ref(s));
init(thr, pc, wfd, ref(s));
unref(thr, pc, s);
DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd);
if (bogusfd(fd))
return;
- init(thr, pc, fd, allocsync());
+ init(thr, pc, fd, allocsync(thr, pc));
}
void FdSignalCreate(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd);
if (bogusfd(fd))
return;
- init(thr, pc, fd, allocsync());
+ init(thr, pc, fd, allocsync(thr, pc));
}
void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {
init(thr, pc, fd, &fdctx.socksync);
}
-uptr File2addr(char *path) {
+uptr File2addr(const char *path) {
(void)path;
static u64 addr;
return (uptr)&addr;
}
-uptr Dir2addr(char *path) {
+uptr Dir2addr(const char *path) {
(void)path;
static u64 addr;
return (uptr)&addr;
void FdAcquire(ThreadState *thr, uptr pc, int fd);
void FdRelease(ThreadState *thr, uptr pc, int fd);
void FdAccess(ThreadState *thr, uptr pc, int fd);
-void FdClose(ThreadState *thr, uptr pc, int fd);
+void FdClose(ThreadState *thr, uptr pc, int fd, bool write = true);
void FdFileCreate(ThreadState *thr, uptr pc, int fd);
-void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd);
+void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write);
void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd);
void FdEventCreate(ThreadState *thr, uptr pc, int fd);
void FdSignalCreate(ThreadState *thr, uptr pc, int fd);
bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack);
void FdOnFork(ThreadState *thr, uptr pc);
-uptr File2addr(char *path);
-uptr Dir2addr(char *path);
+uptr File2addr(const char *path);
+uptr Dir2addr(const char *path);
} // namespace __tsan
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_flags.h"
#include "tsan_rtl.h"
#include "tsan_mman.h"
+#include "ubsan/ubsan_flags.h"
namespace __tsan {
Flags *flags() {
- return &CTX()->flags;
+ return &ctx->flags;
}
// Can be overriden in frontend.
#ifdef TSAN_EXTERNAL_HOOKS
-void OverrideFlags(Flags *f);
extern "C" const char* __tsan_default_options();
#else
-void WEAK OverrideFlags(Flags *f) {
- (void)f;
-}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
const char *WEAK __tsan_default_options() {
return "";
}
#endif
-static void ParseFlags(Flags *f, const char *env) {
- ParseFlag(env, &f->enable_annotations, "enable_annotations");
- ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks");
- ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses");
- ParseFlag(env, &f->suppress_java, "suppress_java");
- ParseFlag(env, &f->report_bugs, "report_bugs");
- ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks");
- ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked");
- ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe");
- ParseFlag(env, &f->report_atomic_races, "report_atomic_races");
- ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics");
- ParseFlag(env, &f->suppressions, "suppressions");
- ParseFlag(env, &f->print_suppressions, "print_suppressions");
- ParseFlag(env, &f->print_benign, "print_benign");
- ParseFlag(env, &f->exitcode, "exitcode");
- ParseFlag(env, &f->halt_on_error, "halt_on_error");
- ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms");
- ParseFlag(env, &f->profile_memory, "profile_memory");
- ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms");
- ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms");
- ParseFlag(env, &f->memory_limit_mb, "memory_limit_mb");
- ParseFlag(env, &f->stop_on_start, "stop_on_start");
- ParseFlag(env, &f->running_on_valgrind, "running_on_valgrind");
- ParseFlag(env, &f->history_size, "history_size");
- ParseFlag(env, &f->io_sync, "io_sync");
+void Flags::SetDefaults() {
+#define TSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "tsan_flags.inc"
+#undef TSAN_FLAG
+ // DDFlags
+ second_deadlock_stack = false;
+}
+
+void RegisterTsanFlags(FlagParser *parser, Flags *f) {
+#define TSAN_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &f->Name);
+#include "tsan_flags.inc"
+#undef TSAN_FLAG
+ // DDFlags
+ RegisterFlag(parser, "second_deadlock_stack",
+ "Report where each mutex is locked in deadlock reports",
+ &f->second_deadlock_stack);
}
void InitializeFlags(Flags *f, const char *env) {
- internal_memset(f, 0, sizeof(*f));
-
- // Default values.
- f->enable_annotations = true;
- f->suppress_equal_stacks = true;
- f->suppress_equal_addresses = true;
- f->suppress_java = false;
- f->report_bugs = true;
- f->report_thread_leaks = true;
- f->report_destroy_locked = true;
- f->report_signal_unsafe = true;
- f->report_atomic_races = true;
- f->force_seq_cst_atomics = false;
- f->suppressions = "";
- f->print_suppressions = false;
- f->print_benign = false;
- f->exitcode = 66;
- f->halt_on_error = false;
- f->atexit_sleep_ms = 1000;
- f->profile_memory = "";
- f->flush_memory_ms = 0;
- f->flush_symbolizer_ms = 5000;
- f->memory_limit_mb = 0;
- f->stop_on_start = false;
- f->running_on_valgrind = false;
- f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go.
- f->io_sync = 1;
-
- SetCommonFlagsDefaults(f);
+ SetCommonFlagsDefaults();
+ {
+ // Override some common flags defaults.
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.allow_addr2line = true;
+ if (kGoMode) {
+ // Does not work as expected for Go: runtime handles SIGABRT and crashes.
+ cf.abort_on_error = false;
+ // Go does not have mutexes.
+ } else {
+ cf.detect_deadlocks = true;
+ }
+ cf.print_suppressions = false;
+ cf.stack_trace_format = " #%n %f %S %M";
+ cf.exitcode = 66;
+ OverrideCommonFlags(cf);
+ }
+
+ f->SetDefaults();
+
+ FlagParser parser;
+ RegisterTsanFlags(&parser, f);
+ RegisterCommonFlags(&parser);
+
+#if TSAN_CONTAINS_UBSAN
+ __ubsan::Flags *uf = __ubsan::flags();
+ uf->SetDefaults();
+
+ FlagParser ubsan_parser;
+ __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
+ RegisterCommonFlags(&ubsan_parser);
+#endif
// Let a frontend override.
- OverrideFlags(f);
- ParseFlags(f, __tsan_default_options());
- ParseCommonFlagsFromString(f, __tsan_default_options());
+ parser.ParseString(__tsan_default_options());
+#if TSAN_CONTAINS_UBSAN
+ const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
+ ubsan_parser.ParseString(ubsan_default_options);
+#endif
// Override from command line.
- ParseFlags(f, env);
- ParseCommonFlagsFromString(f, env);
-
- // Copy back to common flags.
- *common_flags() = *f;
+ parser.ParseString(env);
+#if TSAN_CONTAINS_UBSAN
+ ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
+#endif
// Sanity check.
if (!f->report_bugs) {
f->report_signal_unsafe = false;
}
+ SetVerbosity(common_flags()->verbosity);
+
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (common_flags()->help) parser.PrintFlagDescriptions();
+
if (f->history_size < 0 || f->history_size > 7) {
Printf("ThreadSanitizer: incorrect value for history_size"
" (must be [0..7])\n");
#ifndef TSAN_FLAGS_H
#define TSAN_FLAGS_H
-// ----------- ATTENTION -------------
-// ThreadSanitizer user may provide its implementation of weak
-// symbol __tsan::OverrideFlags(__tsan::Flags). Therefore, this
-// header may be included in the user code, and shouldn't include
-// other headers from TSan or common sanitizer runtime.
-
#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_deadlock_detector_interface.h"
namespace __tsan {
-struct Flags : CommonFlags {
- // Enable dynamic annotations, otherwise they are no-ops.
- bool enable_annotations;
- // Supress a race report if we've already output another race report
- // with the same stack.
- bool suppress_equal_stacks;
- // Supress a race report if we've already output another race report
- // on the same address.
- bool suppress_equal_addresses;
- // Suppress weird race reports that can be seen if JVM is embed
- // into the process.
- bool suppress_java;
- // Turns off bug reporting entirely (useful for benchmarking).
- bool report_bugs;
- // Report thread leaks at exit?
- bool report_thread_leaks;
- // Report destruction of a locked mutex?
- bool report_destroy_locked;
- // Report violations of async signal-safety
- // (e.g. malloc() call from a signal handler).
- bool report_signal_unsafe;
- // Report races between atomic and plain memory accesses.
- bool report_atomic_races;
- // If set, all atomics are effectively sequentially consistent (seq_cst),
- // regardless of what user actually specified.
- bool force_seq_cst_atomics;
- // Suppressions filename.
- const char *suppressions;
- // Print matched suppressions at exit.
- bool print_suppressions;
- // Print matched "benign" races at exit.
- bool print_benign;
- // Override exit status if something was reported.
- int exitcode;
- // Exit after first reported error.
- bool halt_on_error;
- // Sleep in main thread before exiting for that many ms
- // (useful to catch "at exit" races).
- int atexit_sleep_ms;
- // If set, periodically write memory profile to that file.
- const char *profile_memory;
- // Flush shadow memory every X ms.
- int flush_memory_ms;
- // Flush symbolizer caches every X ms.
- int flush_symbolizer_ms;
- // Resident memory limit in MB to aim at.
- // If the process consumes more memory, then TSan will flush shadow memory.
- int memory_limit_mb;
- // Stops on start until __tsan_resume() is called (for debugging).
- bool stop_on_start;
- // Controls whether RunningOnValgrind() returns true or false.
- bool running_on_valgrind;
- // Per-thread history size, controls how many previous memory accesses
- // are remembered per thread. Possible values are [0..7].
- // history_size=0 amounts to 32K memory accesses. Each next value doubles
- // the amount of memory accesses, up to history_size=7 that amounts to
- // 4M memory accesses. The default value is 2 (128K memory accesses).
- int history_size;
- // Controls level of synchronization implied by IO operations.
- // 0 - no synchronization
- // 1 - reasonable level of synchronization (write->read)
- // 2 - global synchronization of all IO operations
- int io_sync;
+struct Flags : DDFlags {
+#define TSAN_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "tsan_flags.inc"
+#undef TSAN_FLAG
+
+ void SetDefaults();
+ void ParseFromString(const char *str);
};
Flags *flags();
--- /dev/null
+//===-- tsan_flags.inc ------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// TSan runtime flags.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_FLAG
+# error "Define TSAN_FLAG prior to including this file!"
+#endif
+
+// TSAN_FLAG(Type, Name, DefaultValue, Description)
+// See COMMON_FLAG in sanitizer_flags.inc for more details.
+
+TSAN_FLAG(bool, enable_annotations, true,
+ "Enable dynamic annotations, otherwise they are no-ops.")
+// Suppress a race report if we've already output another race report
+// with the same stack.
+TSAN_FLAG(bool, suppress_equal_stacks, true,
+ "Suppress a race report if we've already output another race report "
+ "with the same stack.")
+TSAN_FLAG(bool, suppress_equal_addresses, true,
+ "Suppress a race report if we've already output another race report "
+ "on the same address.")
+
+TSAN_FLAG(bool, report_bugs, true,
+ "Turns off bug reporting entirely (useful for benchmarking).")
+TSAN_FLAG(bool, report_thread_leaks, true, "Report thread leaks at exit?")
+TSAN_FLAG(bool, report_destroy_locked, true,
+ "Report destruction of a locked mutex?")
+TSAN_FLAG(bool, report_mutex_bugs, true,
+ "Report incorrect usages of mutexes and mutex annotations?")
+TSAN_FLAG(bool, report_signal_unsafe, true,
+ "Report violations of async signal-safety "
+ "(e.g. malloc() call from a signal handler).")
+TSAN_FLAG(bool, report_atomic_races, true,
+ "Report races between atomic and plain memory accesses.")
+TSAN_FLAG(
+ bool, force_seq_cst_atomics, false,
+ "If set, all atomics are effectively sequentially consistent (seq_cst), "
+ "regardless of what user actually specified.")
+TSAN_FLAG(bool, print_benign, false, "Print matched \"benign\" races at exit.")
+TSAN_FLAG(bool, halt_on_error, false, "Exit after first reported error.")
+TSAN_FLAG(int, atexit_sleep_ms, 1000,
+ "Sleep in main thread before exiting for that many ms "
+ "(useful to catch \"at exit\" races).")
+TSAN_FLAG(const char *, profile_memory, "",
+ "If set, periodically write memory profile to that file.")
+TSAN_FLAG(int, flush_memory_ms, 0, "Flush shadow memory every X ms.")
+TSAN_FLAG(int, flush_symbolizer_ms, 5000, "Flush symbolizer caches every X ms.")
+TSAN_FLAG(
+ int, memory_limit_mb, 0,
+ "Resident memory limit in MB to aim at."
+ "If the process consumes more memory, then TSan will flush shadow memory.")
+TSAN_FLAG(bool, stop_on_start, false,
+ "Stops on start until __tsan_resume() is called (for debugging).")
+TSAN_FLAG(bool, running_on_valgrind, false,
+ "Controls whether RunningOnValgrind() returns true or false.")
+TSAN_FLAG(
+ int, history_size, kGoMode ? 1 : 3, // There are a lot of goroutines in Go.
+ "Per-thread history size, controls how many previous memory accesses "
+ "are remembered per thread. Possible values are [0..7]. "
+ "history_size=0 amounts to 32K memory accesses. Each next value doubles "
+ "the amount of memory accesses, up to history_size=7 that amounts to "
+ "4M memory accesses. The default value is 2 (128K memory accesses).")
+TSAN_FLAG(int, io_sync, 1,
+ "Controls level of synchronization implied by IO operations. "
+ "0 - no synchronization "
+ "1 - reasonable level of synchronization (write->read)"
+ "2 - global synchronization of all IO operations.")
+TSAN_FLAG(bool, die_after_fork, true,
+ "Die after multi-threaded fork if the child creates new threads.")
+TSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "interception/interception.h"
+#include "tsan_interceptors.h"
#include "tsan_interface.h"
#include "tsan_platform.h"
#include "tsan_suppressions.h"
#include "tsan_mman.h"
#include "tsan_fd.h"
+#if SANITIZER_POSIX
+#include "sanitizer_common/sanitizer_posix.h"
+#endif
+
using namespace __tsan; // NOLINT
-const int kSigCount = 64;
+#if SANITIZER_FREEBSD || SANITIZER_MAC
+#define __errno_location __error
+#define stdout __stdoutp
+#define stderr __stderrp
+#endif
+
+#if SANITIZER_FREEBSD
+#define __libc_realloc __realloc
+#define __libc_calloc __calloc
+#elif SANITIZER_MAC
+#define __libc_malloc REAL(malloc)
+#define __libc_realloc REAL(realloc)
+#define __libc_calloc REAL(calloc)
+#define __libc_free REAL(free)
+#endif
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#define PTHREAD_CREATE_DETACHED 1
+#elif SANITIZER_MAC
+#define PTHREAD_CREATE_DETACHED 2
+#endif
+
+
+#ifdef __mips__
+const int kSigCount = 129;
+#else
+const int kSigCount = 65;
+#endif
struct my_siginfo_t {
// The size is determined by looking at sizeof of real siginfo_t on linux.
u64 opaque[128 / sizeof(u64)];
};
+#ifdef __mips__
+struct ucontext_t {
+ u64 opaque[768 / sizeof(u64) + 1];
+};
+#else
struct ucontext_t {
// The size is determined by looking at sizeof of real ucontext_t on linux.
u64 opaque[936 / sizeof(u64) + 1];
};
+#endif
+
+#if defined(__x86_64__) || defined(__mips__)
+#define PTHREAD_ABI_BASE "GLIBC_2.3.2"
+#elif defined(__aarch64__)
+#define PTHREAD_ABI_BASE "GLIBC_2.17"
+#endif
extern "C" int pthread_attr_init(void *attr);
extern "C" int pthread_attr_destroy(void *attr);
extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize);
extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v));
extern "C" int pthread_setspecific(unsigned key, const void *v);
-extern "C" int pthread_mutexattr_gettype(void *a, int *type);
-extern "C" int pthread_yield();
+DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *)
extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set,
__sanitizer_sigset_t *oldset);
// REAL(sigfillset) defined in common interceptors.
DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set)
+DECLARE_REAL(int, fflush, __sanitizer_FILE *fp)
+DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr size)
+DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
extern "C" void *pthread_self();
extern "C" void _exit(int status);
extern "C" int *__errno_location();
extern "C" int fileno_unlocked(void *stream);
-extern "C" void *__libc_malloc(uptr size);
extern "C" void *__libc_calloc(uptr size, uptr n);
extern "C" void *__libc_realloc(void *ptr, uptr size);
-extern "C" void __libc_free(void *ptr);
+extern "C" int dirfd(void *dirp);
+#if !SANITIZER_FREEBSD
extern "C" int mallopt(int param, int value);
+#endif
+extern __sanitizer_FILE *stdout, *stderr;
const int PTHREAD_MUTEX_RECURSIVE = 1;
const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
const int EINVAL = 22;
const int EBUSY = 16;
const int EOWNERDEAD = 130;
+#if !SANITIZER_MAC
const int EPOLL_CTL_ADD = 1;
+#endif
const int SIGILL = 4;
const int SIGABRT = 6;
const int SIGFPE = 8;
const int SIGSEGV = 11;
const int SIGPIPE = 13;
+const int SIGTERM = 15;
+#ifdef __mips__
+const int SIGBUS = 10;
+const int SIGSYS = 12;
+#else
const int SIGBUS = 7;
const int SIGSYS = 31;
+#endif
void *const MAP_FAILED = (void*)-1;
+#if !SANITIZER_MAC
const int PTHREAD_BARRIER_SERIAL_THREAD = -1;
+#endif
const int MAP_FIXED = 0x10;
typedef long long_t; // NOLINT
# define F_TLOCK 2 /* Test and lock a region for exclusive use. */
# define F_TEST 3 /* Test a region for other processes locks. */
-typedef void (*sighandler_t)(int sig);
-
#define errno (*__errno_location())
+typedef void (*sighandler_t)(int sig);
+typedef void (*sigactionhandler_t)(int sig, my_siginfo_t *siginfo, void *uctx);
+
struct sigaction_t {
+#ifdef __mips__
+ u32 sa_flags;
+#endif
union {
sighandler_t sa_handler;
- void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx);
+ sigactionhandler_t sa_sigaction;
};
+#if SANITIZER_FREEBSD
+ int sa_flags;
__sanitizer_sigset_t sa_mask;
+#else
+ __sanitizer_sigset_t sa_mask;
+#ifndef __mips__
int sa_flags;
+#endif
void (*sa_restorer)();
+#endif
};
const sighandler_t SIG_DFL = (sighandler_t)0;
const sighandler_t SIG_IGN = (sighandler_t)1;
const sighandler_t SIG_ERR = (sighandler_t)-1;
+#if SANITIZER_FREEBSD
+const int SA_SIGINFO = 0x40;
+const int SIG_SETMASK = 3;
+#elif defined(__mips__)
+const int SA_SIGINFO = 8;
+const int SIG_SETMASK = 3;
+#else
const int SA_SIGINFO = 4;
const int SIG_SETMASK = 2;
+#endif
-namespace std {
-struct nothrow_t {};
-} // namespace std
+#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED \
+ (!cur_thread()->is_inited)
static sigaction_t sigactions[kSigCount];
ucontext_t ctx;
};
-struct SignalContext {
- int in_blocking_func;
+struct ThreadSignalContext {
int int_signal_send;
- int pending_signal_count;
+ atomic_uintptr_t in_blocking_func;
+ atomic_uintptr_t have_pending_signals;
SignalDesc pending_signals[kSigCount];
};
}
void InitializeLibIgnore() {
- libignore()->Init(*GetSuppressionContext());
+ const SuppressionContext &supp = *Suppressions();
+ const uptr n = supp.SuppressionCount();
+ for (uptr i = 0; i < n; i++) {
+ const Suppression *s = supp.SuppressionAt(i);
+ if (0 == internal_strcmp(s->type, kSuppressionLib))
+ libignore()->AddIgnoredLibrary(s->templ);
+ }
libignore()->OnLibraryLoaded(0);
}
} // namespace __tsan
-static SignalContext *SigCtx(ThreadState *thr) {
- SignalContext *ctx = (SignalContext*)thr->signal_ctx;
- if (ctx == 0 && thr->is_alive) {
- ScopedInRtl in_rtl;
- ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext");
+static ThreadSignalContext *SigCtx(ThreadState *thr) {
+ ThreadSignalContext *ctx = (ThreadSignalContext*)thr->signal_ctx;
+ if (ctx == 0 && !thr->is_dead) {
+ ctx = (ThreadSignalContext*)MmapOrDie(sizeof(*ctx), "ThreadSignalContext");
MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
thr->signal_ctx = ctx;
}
static unsigned g_thread_finalize_key;
-class ScopedInterceptor {
- public:
- ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc);
- ~ScopedInterceptor();
- private:
- ThreadState *const thr_;
- const uptr pc_;
- const int in_rtl_;
- bool in_ignored_lib_;
-};
-
ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
uptr pc)
: thr_(thr)
, pc_(pc)
- , in_rtl_(thr->in_rtl)
, in_ignored_lib_(false) {
- if (thr_->in_rtl == 0) {
+ if (!thr_->ignore_interceptors) {
Initialize(thr);
FuncEntry(thr, pc);
- thr_->in_rtl++;
- DPrintf("#%d: intercept %s()\n", thr_->tid, fname);
- } else {
- thr_->in_rtl++;
}
+ DPrintf("#%d: intercept %s()\n", thr_->tid, fname);
if (!thr_->in_ignored_lib && libignore()->IsIgnored(pc)) {
in_ignored_lib_ = true;
thr_->in_ignored_lib = true;
thr_->in_ignored_lib = false;
ThreadIgnoreEnd(thr_, pc_);
}
- thr_->in_rtl--;
- if (thr_->in_rtl == 0) {
- FuncExit(thr_);
+ if (!thr_->ignore_interceptors) {
ProcessPendingSignals(thr_);
+ FuncExit(thr_);
+ CheckNoLocks(thr_);
}
- CHECK_EQ(in_rtl_, thr_->in_rtl);
}
-#define SCOPED_INTERCEPTOR_RAW(func, ...) \
- ThreadState *thr = cur_thread(); \
- StatInc(thr, StatInterceptor); \
- StatInc(thr, StatInt_##func); \
- const uptr caller_pc = GET_CALLER_PC(); \
- ScopedInterceptor si(thr, #func, caller_pc); \
- const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
- (void)pc; \
-/**/
+#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
+#if SANITIZER_FREEBSD
+# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
+#else
+# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
+#endif
-#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
- SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
- if (REAL(func) == 0) { \
- Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
- Die(); \
- } \
- if (thr->in_rtl > 1 || thr->in_ignored_lib) \
- return REAL(func)(__VA_ARGS__); \
-/**/
+#define READ_STRING_OF_LEN(thr, pc, s, len, n) \
+ MemoryAccessRange((thr), (pc), (uptr)(s), \
+ common_flags()->strict_string_checks ? (len) + 1 : (n), false)
-#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
-#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
+#define READ_STRING(thr, pc, s, n) \
+ READ_STRING_OF_LEN((thr), (pc), (s), internal_strlen(s), (n))
#define BLOCK_REAL(name) (BlockingCall(thr), REAL(name))
struct BlockingCall {
explicit BlockingCall(ThreadState *thr)
- : ctx(SigCtx(thr)) {
- ctx->in_blocking_func++;
+ : thr(thr)
+ , ctx(SigCtx(thr)) {
+ for (;;) {
+ atomic_store(&ctx->in_blocking_func, 1, memory_order_relaxed);
+ if (atomic_load(&ctx->have_pending_signals, memory_order_relaxed) == 0)
+ break;
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
+ ProcessPendingSignals(thr);
+ }
+ // When we are in a "blocking call", we process signals asynchronously
+ // (right when they arrive). In this context we do not expect to be
+ // executing any user/runtime code. The known interceptor sequence when
+ // this is not true is: pthread_join -> munmap(stack). It's fine
+ // to ignore munmap in this case -- we handle stack shadow separately.
+ thr->ignore_interceptors++;
}
~BlockingCall() {
- ctx->in_blocking_func--;
+ thr->ignore_interceptors--;
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
}
- SignalContext *ctx;
+ ThreadState *thr;
+ ThreadSignalContext *ctx;
};
TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) {
return res;
}
-TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
- SCOPED_INTERCEPTOR_RAW(dlopen, filename, flag);
- // dlopen will execute global constructors, so it must be not in rtl.
- CHECK_EQ(thr->in_rtl, 1);
- thr->in_rtl = 0;
- void *res = REAL(dlopen)(filename, flag);
- thr->in_rtl = 1;
- libignore()->OnLibraryLoaded(filename);
- return res;
-}
+// The sole reason tsan wraps atexit callbacks is to establish synchronization
+// between callback setup and callback execution.
+struct AtExitCtx {
+ void (*f)();
+ void *arg;
+};
-TSAN_INTERCEPTOR(int, dlclose, void *handle) {
- SCOPED_INTERCEPTOR_RAW(dlclose, handle);
- // dlclose will execute global destructors, so it must be not in rtl.
- CHECK_EQ(thr->in_rtl, 1);
- thr->in_rtl = 0;
- int res = REAL(dlclose)(handle);
- thr->in_rtl = 1;
- libignore()->OnLibraryUnloaded();
- return res;
+static void at_exit_wrapper(void *arg) {
+ ThreadState *thr = cur_thread();
+ uptr pc = 0;
+ Acquire(thr, pc, (uptr)arg);
+ AtExitCtx *ctx = (AtExitCtx*)arg;
+ ((void(*)(void *arg))ctx->f)(ctx->arg);
+ __libc_free(ctx);
}
-class AtExitContext {
- public:
- AtExitContext()
- : mtx_(MutexTypeAtExit, StatMtxAtExit)
- , pos_() {
- }
-
- typedef void(*atexit_t)();
-
- int atexit(ThreadState *thr, uptr pc, bool is_on_exit,
- atexit_t f, void *arg) {
- Lock l(&mtx_);
- if (pos_ == kMaxAtExit)
- return 1;
- Release(thr, pc, (uptr)this);
- stack_[pos_] = f;
- args_[pos_] = arg;
- is_on_exits_[pos_] = is_on_exit;
- pos_++;
- return 0;
- }
-
- void exit(ThreadState *thr, uptr pc) {
- CHECK_EQ(thr->in_rtl, 0);
- for (;;) {
- atexit_t f = 0;
- void *arg = 0;
- bool is_on_exit = false;
- {
- Lock l(&mtx_);
- if (pos_) {
- pos_--;
- f = stack_[pos_];
- arg = args_[pos_];
- is_on_exit = is_on_exits_[pos_];
- ScopedInRtl in_rtl;
- Acquire(thr, pc, (uptr)this);
- }
- }
- if (f == 0)
- break;
- DPrintf("#%d: executing atexit func %p\n", thr->tid, f);
- CHECK_EQ(thr->in_rtl, 0);
- if (is_on_exit)
- ((void(*)(int status, void *arg))f)(0, arg);
- else
- ((void(*)(void *arg, void *dso))f)(arg, 0);
- }
- }
-
- private:
- static const int kMaxAtExit = 128;
- Mutex mtx_;
- atexit_t stack_[kMaxAtExit];
- void *args_[kMaxAtExit];
- bool is_on_exits_[kMaxAtExit];
- int pos_;
-};
-
-static AtExitContext *atexit_ctx;
+static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
+ void *arg, void *dso);
TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
if (cur_thread()->in_symbolizer)
return 0;
- SCOPED_TSAN_INTERCEPTOR(atexit, f);
- return atexit_ctx->atexit(thr, pc, false, (void(*)())f, 0);
+ // We want to setup the atexit callback even if we are in ignored lib
+ // or after fork.
+ SCOPED_INTERCEPTOR_RAW(atexit, f);
+ return setup_at_exit_wrapper(thr, pc, (void(*)())f, 0, 0);
}
-TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) {
+TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) {
if (cur_thread()->in_symbolizer)
return 0;
- SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg);
- return atexit_ctx->atexit(thr, pc, true, (void(*)())f, arg);
+ SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso);
+ return setup_at_exit_wrapper(thr, pc, (void(*)())f, arg, dso);
}
-TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) {
+static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
+ void *arg, void *dso) {
+ AtExitCtx *ctx = (AtExitCtx*)__libc_malloc(sizeof(AtExitCtx));
+ ctx->f = f;
+ ctx->arg = arg;
+ Release(thr, pc, (uptr)ctx);
+ // Memory allocation in __cxa_atexit will race with free during exit,
+ // because we do not see synchronization around atexit callback list.
+ ThreadIgnoreBegin(thr, pc);
+ int res = REAL(__cxa_atexit)(at_exit_wrapper, ctx, dso);
+ ThreadIgnoreEnd(thr, pc);
+ return res;
+}
+
+#if !SANITIZER_MAC
+static void on_exit_wrapper(int status, void *arg) {
+ ThreadState *thr = cur_thread();
+ uptr pc = 0;
+ Acquire(thr, pc, (uptr)arg);
+ AtExitCtx *ctx = (AtExitCtx*)arg;
+ ((void(*)(int status, void *arg))ctx->f)(status, ctx->arg);
+ __libc_free(ctx);
+}
+
+TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) {
if (cur_thread()->in_symbolizer)
return 0;
- SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso);
- if (dso) {
- // Memory allocation in __cxa_atexit will race with free during exit,
- // because we do not see synchronization around atexit callback list.
- ThreadIgnoreBegin(thr, pc);
- int res = REAL(__cxa_atexit)(f, arg, dso);
- ThreadIgnoreEnd(thr, pc);
- return res;
- }
- return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg);
+ SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg);
+ AtExitCtx *ctx = (AtExitCtx*)__libc_malloc(sizeof(AtExitCtx));
+ ctx->f = (void(*)())f;
+ ctx->arg = arg;
+ Release(thr, pc, (uptr)ctx);
+ // Memory allocation in __cxa_atexit will race with free during exit,
+ // because we do not see synchronization around atexit callback list.
+ ThreadIgnoreBegin(thr, pc);
+ int res = REAL(on_exit)(on_exit_wrapper, ctx);
+ ThreadIgnoreEnd(thr, pc);
+ return res;
}
+#endif
// Cleanup old bufs.
static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) {
}
static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) {
- if (thr->shadow_stack_pos == 0) // called from libc guts during bootstrap
+ if (!thr->is_inited) // called from libc guts during bootstrap
return;
// Cleanup old bufs.
JmpBufGarbageCollect(thr, sp);
buf->sp = sp;
buf->mangled_sp = mangled_sp;
buf->shadow_stack_pos = thr->shadow_stack_pos;
+ ThreadSignalContext *sctx = SigCtx(thr);
+ buf->int_signal_send = sctx ? sctx->int_signal_send : 0;
+ buf->in_blocking_func = sctx ?
+ atomic_load(&sctx->in_blocking_func, memory_order_relaxed) :
+ false;
+ buf->in_signal_handler = atomic_load(&thr->in_signal_handler,
+ memory_order_relaxed);
}
static void LongJmp(ThreadState *thr, uptr *env) {
+#if SANITIZER_FREEBSD
+ uptr mangled_sp = env[2];
+#elif defined(SANITIZER_LINUX)
+# ifdef __aarch64__
+ uptr mangled_sp = env[13];
+# else
uptr mangled_sp = env[6];
+# endif
+#endif // SANITIZER_FREEBSD
// Find the saved buf by mangled_sp.
for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
JmpBuf *buf = &thr->jmp_bufs[i];
// Unwind the stack.
while (thr->shadow_stack_pos > buf->shadow_stack_pos)
FuncExit(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
+ if (sctx) {
+ sctx->int_signal_send = buf->int_signal_send;
+ atomic_store(&sctx->in_blocking_func, buf->in_blocking_func,
+ memory_order_relaxed);
+ }
+ atomic_store(&thr->in_signal_handler, buf->in_signal_handler,
+ memory_order_relaxed);
JmpBufGarbageCollect(thr, buf->sp - 1); // do not collect buf->sp
return;
}
// FIXME: put everything below into a common extern "C" block?
extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) {
- ScopedInRtl in_rtl;
SetJmp(cur_thread(), sp, mangled_sp);
}
REAL(siglongjmp)(env, val);
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(void*, malloc, uptr size) {
if (cur_thread()->in_symbolizer)
return __libc_malloc(size);
TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
if (cur_thread()->in_symbolizer)
return __libc_calloc(size, n);
- if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n))
- return AllocatorReturnNull();
void *p = 0;
{
SCOPED_INTERCEPTOR_RAW(calloc, size, n);
- p = user_alloc(thr, pc, n * size);
- if (p)
- internal_memset(p, 0, n * size);
+ p = user_calloc(thr, pc, size, n);
}
invoke_malloc_hook(p, n * size);
return p;
TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) {
SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p);
- return user_alloc_usable_size(thr, pc, p);
-}
-
-#define OPERATOR_NEW_BODY(mangled_name) \
- if (cur_thread()->in_symbolizer) \
- return __libc_malloc(size); \
- void *p = 0; \
- { \
- SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
- p = user_alloc(thr, pc, size); \
- } \
- invoke_malloc_hook(p, size); \
- return p;
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new(__sanitizer::uptr size);
-void *operator new(__sanitizer::uptr size) {
- OPERATOR_NEW_BODY(_Znwm);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new[](__sanitizer::uptr size);
-void *operator new[](__sanitizer::uptr size) {
- OPERATOR_NEW_BODY(_Znam);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new(__sanitizer::uptr size, std::nothrow_t const&);
-void *operator new(__sanitizer::uptr size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new[](__sanitizer::uptr size, std::nothrow_t const&);
-void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t);
-}
-
-#define OPERATOR_DELETE_BODY(mangled_name) \
- if (ptr == 0) return; \
- if (cur_thread()->in_symbolizer) \
- return __libc_free(ptr); \
- invoke_free_hook(ptr); \
- SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \
- user_free(thr, pc, ptr);
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete(void *ptr);
-void operator delete(void *ptr) {
- OPERATOR_DELETE_BODY(_ZdlPv);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete[](void *ptr);
-void operator delete[](void *ptr) {
- OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete(void *ptr, std::nothrow_t const&);
-void operator delete(void *ptr, std::nothrow_t const&) {
- OPERATOR_DELETE_BODY(_ZdaPv);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete[](void *ptr, std::nothrow_t const&);
-void operator delete[](void *ptr, std::nothrow_t const&) {
- OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t);
+ return user_alloc_usable_size(p);
}
+#endif
TSAN_INTERCEPTOR(uptr, strlen, const char *s) {
SCOPED_TSAN_INTERCEPTOR(strlen, s);
}
TSAN_INTERCEPTOR(void*, memset, void *dst, int v, uptr size) {
- SCOPED_TSAN_INTERCEPTOR(memset, dst, v, size);
- MemoryAccessRange(thr, pc, (uptr)dst, size, true);
+ // On FreeBSD we get here from libthr internals on thread initialization.
+ if (!COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) {
+ SCOPED_TSAN_INTERCEPTOR(memset, dst, v, size);
+ MemoryAccessRange(thr, pc, (uptr)dst, size, true);
+ }
return internal_memset(dst, v, size);
}
TSAN_INTERCEPTOR(void*, memcpy, void *dst, const void *src, uptr size) {
- SCOPED_TSAN_INTERCEPTOR(memcpy, dst, src, size);
- MemoryAccessRange(thr, pc, (uptr)dst, size, true);
- MemoryAccessRange(thr, pc, (uptr)src, size, false);
- return internal_memcpy(dst, src, size);
-}
-
-TSAN_INTERCEPTOR(int, memcmp, const void *s1, const void *s2, uptr n) {
- SCOPED_TSAN_INTERCEPTOR(memcmp, s1, s2, n);
- int res = 0;
- uptr len = 0;
- for (; len < n; len++) {
- if ((res = ((unsigned char*)s1)[len] - ((unsigned char*)s2)[len]))
- break;
+ // On FreeBSD we get here from libthr internals on thread initialization.
+ if (!COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) {
+ SCOPED_TSAN_INTERCEPTOR(memcpy, dst, src, size);
+ MemoryAccessRange(thr, pc, (uptr)dst, size, true);
+ MemoryAccessRange(thr, pc, (uptr)src, size, false);
}
- MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false);
- MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false);
- return res;
-}
-
-TSAN_INTERCEPTOR(void*, memchr, void *s, int c, uptr n) {
- SCOPED_TSAN_INTERCEPTOR(memchr, s, c, n);
- void *res = REAL(memchr)(s, c, n);
- uptr len = res ? (char*)res - (char*)s + 1 : n;
- MemoryAccessRange(thr, pc, (uptr)s, len, false);
- return res;
-}
-
-TSAN_INTERCEPTOR(void*, memrchr, char *s, int c, uptr n) {
- SCOPED_TSAN_INTERCEPTOR(memrchr, s, c, n);
- MemoryAccessRange(thr, pc, (uptr)s, n, false);
- return REAL(memrchr)(s, c, n);
+ // On OS X, calling internal_memcpy here will cause memory corruptions,
+ // because memcpy and memmove are actually aliases of the same implementation.
+ // We need to use internal_memmove here.
+ return internal_memmove(dst, src, size);
}
TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) {
- SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n);
- MemoryAccessRange(thr, pc, (uptr)dst, n, true);
- MemoryAccessRange(thr, pc, (uptr)src, n, false);
+ if (!COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) {
+ SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n);
+ MemoryAccessRange(thr, pc, (uptr)dst, n, true);
+ MemoryAccessRange(thr, pc, (uptr)src, n, false);
+ }
return REAL(memmove)(dst, src, n);
}
TSAN_INTERCEPTOR(char*, strchr, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strchr, s, c);
char *res = REAL(strchr)(s, c);
- uptr len = res ? (char*)res - (char*)s + 1 : internal_strlen(s) + 1;
- MemoryAccessRange(thr, pc, (uptr)s, len, false);
+ uptr len = internal_strlen(s);
+ uptr n = res ? (char*)res - (char*)s + 1 : len + 1;
+ READ_STRING_OF_LEN(thr, pc, s, len, n);
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c);
char *res = REAL(strchrnul)(s, c);
uptr len = (char*)res - (char*)s + 1;
- MemoryAccessRange(thr, pc, (uptr)s, len, false);
+ READ_STRING(thr, pc, s, len);
return res;
}
+#endif
TSAN_INTERCEPTOR(char*, strrchr, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strrchr, s, c);
return REAL(strncpy)(dst, src, n);
}
-TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) {
- SCOPED_TSAN_INTERCEPTOR(strstr, s1, s2);
- const char *res = REAL(strstr)(s1, s2);
- uptr len1 = internal_strlen(s1);
- uptr len2 = internal_strlen(s2);
- MemoryAccessRange(thr, pc, (uptr)s1, len1 + 1, false);
- MemoryAccessRange(thr, pc, (uptr)s2, len2 + 1, false);
- return res;
-}
-
TSAN_INTERCEPTOR(char*, strdup, const char *str) {
SCOPED_TSAN_INTERCEPTOR(strdup, str);
// strdup will call malloc, so no instrumentation is required here.
return true;
}
-TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot,
- int flags, int fd, unsigned off) {
+TSAN_INTERCEPTOR(void *, mmap, void *addr, SIZE_T sz, int prot, int flags,
+ int fd, OFF_T off) {
SCOPED_TSAN_INTERCEPTOR(mmap, addr, sz, prot, flags, fd, off);
if (!fix_mmap_addr(&addr, sz, flags))
return MAP_FAILED;
return res;
}
-TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot,
- int flags, int fd, u64 off) {
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(void *, mmap64, void *addr, SIZE_T sz, int prot, int flags,
+ int fd, OFF64_T off) {
SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off);
if (!fix_mmap_addr(&addr, sz, flags))
return MAP_FAILED;
}
return res;
}
+#define TSAN_MAYBE_INTERCEPT_MMAP64 TSAN_INTERCEPT(mmap64)
+#else
+#define TSAN_MAYBE_INTERCEPT_MMAP64
+#endif
TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz);
- DontNeedShadowFor((uptr)addr, sz);
+ if (sz != 0) {
+ // If sz == 0, munmap will return EINVAL and don't unmap any memory.
+ DontNeedShadowFor((uptr)addr, sz);
+ ctx->metamap.ResetRange(thr, pc, (uptr)addr, (uptr)sz);
+ }
int res = REAL(munmap)(addr, sz);
return res;
}
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
return user_alloc(thr, pc, sz, align);
}
+#define TSAN_MAYBE_INTERCEPT_MEMALIGN TSAN_INTERCEPT(memalign)
+#else
+#define TSAN_MAYBE_INTERCEPT_MEMALIGN
+#endif
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) {
+ SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
+ return user_alloc(thr, pc, sz, align);
+}
TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
SCOPED_INTERCEPTOR_RAW(valloc, sz);
return user_alloc(thr, pc, sz, GetPageSizeCached());
}
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
SCOPED_INTERCEPTOR_RAW(pvalloc, sz);
sz = RoundUp(sz, GetPageSizeCached());
return user_alloc(thr, pc, sz, GetPageSizeCached());
}
+#define TSAN_MAYBE_INTERCEPT_PVALLOC TSAN_INTERCEPT(pvalloc)
+#else
+#define TSAN_MAYBE_INTERCEPT_PVALLOC
+#endif
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz);
*memptr = user_alloc(thr, pc, sz, align);
return 0;
}
+#endif
// Used in thread-safe function static initialization.
extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) {
atomic_store(g, 0, memory_order_relaxed);
}
+namespace __tsan {
+void DestroyThreadState() {
+ ThreadState *thr = cur_thread();
+ ThreadFinish(thr);
+ ThreadSignalContext *sctx = thr->signal_ctx;
+ if (sctx) {
+ thr->signal_ctx = 0;
+ UnmapOrDie(sctx, sizeof(*sctx));
+ }
+ cur_thread_finalize();
+}
+} // namespace __tsan
+
static void thread_finalize(void *v) {
uptr iter = (uptr)v;
if (iter > 1) {
}
return;
}
- {
- ScopedInRtl in_rtl;
- ThreadState *thr = cur_thread();
- ThreadFinish(thr);
- SignalContext *sctx = thr->signal_ctx;
- if (sctx) {
- thr->signal_ctx = 0;
- UnmapOrDie(sctx, sizeof(*sctx));
- }
- }
+ DestroyThreadState();
}
int tid = 0;
{
ThreadState *thr = cur_thread();
- ScopedInRtl in_rtl;
+ // Thread-local state is not initialized yet.
+ ScopedIgnoreInterceptors ignore;
+ ThreadIgnoreBegin(thr, 0);
if (pthread_setspecific(g_thread_finalize_key,
- (void *)kPthreadDestructorIterations)) {
+ (void *)GetPthreadDestructorIterations())) {
Printf("ThreadSanitizer: failed to set thread key\n");
Die();
}
+ ThreadIgnoreEnd(thr, 0);
while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
- pthread_yield();
- atomic_store(&p->tid, 0, memory_order_release);
+ internal_sched_yield();
ThreadStart(thr, tid, GetTid());
- CHECK_EQ(thr->in_rtl, 1);
+ atomic_store(&p->tid, 0, memory_order_release);
}
void *res = callback(param);
// Prevent the callback from being tail called,
TSAN_INTERCEPTOR(int, pthread_create,
void *th, void *attr, void *(*callback)(void*), void * param) {
SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param);
+ if (ctx->after_multithreaded_fork) {
+ if (flags()->die_after_fork) {
+ Report("ThreadSanitizer: starting new threads after multi-threaded "
+ "fork is not supported. Dying (set die_after_fork=0 to override)\n");
+ Die();
+ } else {
+ VPrintf(1, "ThreadSanitizer: starting new threads after multi-threaded "
+ "fork is not supported (pid %d). Continuing because of "
+ "die_after_fork=0, but you are on your own\n", internal_getpid());
+ }
+ }
__sanitizer_pthread_attr_t myattr;
if (attr == 0) {
pthread_attr_init(&myattr);
}
int detached = 0;
REAL(pthread_attr_getdetachstate)(attr, &detached);
- AdjustStackSizeLinux(attr);
+ AdjustStackSize(attr);
ThreadParam p;
p.callback = callback;
p.param = param;
atomic_store(&p.tid, 0, memory_order_relaxed);
- int res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p);
+ int res = -1;
+ {
+ // Otherwise we see false positives in pthread stack manipulation.
+ ScopedIgnoreInterceptors ignore;
+ ThreadIgnoreBegin(thr, pc);
+ res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p);
+ ThreadIgnoreEnd(thr, pc);
+ }
if (res == 0) {
- int tid = ThreadCreate(thr, pc, *(uptr*)th, detached);
+ int tid = ThreadCreate(thr, pc, *(uptr*)th,
+ detached == PTHREAD_CREATE_DETACHED);
CHECK_NE(tid, 0);
+ // Synchronization on p.tid serves two purposes:
+ // 1. ThreadCreate must finish before the new thread starts.
+ // Otherwise the new thread can call pthread_detach, but the pthread_t
+ // identifier is not yet registered in ThreadRegistry by ThreadCreate.
+ // 2. ThreadStart must finish before this thread continues.
+ // Otherwise, this thread can call pthread_detach and reset thr->sync
+ // before the new thread got a chance to acquire from it in ThreadStart.
atomic_store(&p.tid, tid, memory_order_release);
while (atomic_load(&p.tid, memory_order_acquire) != 0)
- pthread_yield();
+ internal_sched_yield();
}
if (attr == &myattr)
pthread_attr_destroy(&myattr);
TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret);
int tid = ThreadTid(thr, pc, (uptr)th);
+ ThreadIgnoreBegin(thr, pc);
int res = BLOCK_REAL(pthread_join)(th, ret);
+ ThreadIgnoreEnd(thr, pc);
if (res == 0) {
ThreadJoin(thr, pc, tid);
}
return res;
}
+DEFINE_REAL_PTHREAD_FUNCTIONS
+
TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
SCOPED_TSAN_INTERCEPTOR(pthread_detach, th);
int tid = ThreadTid(thr, pc, (uptr)th);
return res;
}
+// Problem:
+// NPTL implementation of pthread_cond has 2 versions (2.2.5 and 2.3.2).
+// pthread_cond_t has different size in the different versions.
+// If call new REAL functions for old pthread_cond_t, they will corrupt memory
+// after pthread_cond_t (old cond is smaller).
+// If we call old REAL functions for new pthread_cond_t, we will lose some
+// functionality (e.g. old functions do not support waiting against
+// CLOCK_REALTIME).
+// Proper handling would require to have 2 versions of interceptors as well.
+// But this is messy, in particular requires linker scripts when sanitizer
+// runtime is linked into a shared library.
+// Instead we assume we don't have dynamic libraries built against old
+// pthread (2.2.5 is dated by 2002). And provide legacy_pthread_cond flag
+// that allows to work with old libraries (but this mode does not support
+// some features, e.g. pthread_condattr_getpshared).
+static void *init_cond(void *c, bool force = false) {
+ // sizeof(pthread_cond_t) >= sizeof(uptr) in both versions.
+ // So we allocate additional memory on the side large enough to hold
+ // any pthread_cond_t object. Always call new REAL functions, but pass
+ // the aux object to them.
+ // Note: the code assumes that PTHREAD_COND_INITIALIZER initializes
+ // first word of pthread_cond_t to zero.
+ // It's all relevant only for linux.
+ if (!common_flags()->legacy_pthread_cond)
+ return c;
+ atomic_uintptr_t *p = (atomic_uintptr_t*)c;
+ uptr cond = atomic_load(p, memory_order_acquire);
+ if (!force && cond != 0)
+ return (void*)cond;
+ void *newcond = WRAP(malloc)(pthread_cond_t_sz);
+ internal_memset(newcond, 0, pthread_cond_t_sz);
+ if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond,
+ memory_order_acq_rel))
+ return newcond;
+ WRAP(free)(newcond);
+ return (void*)cond;
+}
+
+struct CondMutexUnlockCtx {
+ ScopedInterceptor *si;
+ ThreadState *thr;
+ uptr pc;
+ void *m;
+};
+
+static void cond_mutex_unlock(CondMutexUnlockCtx *arg) {
+ // pthread_cond_wait interceptor has enabled async signal delivery
+ // (see BlockingCall below). Disable async signals since we are running
+ // tsan code. Also ScopedInterceptor and BlockingCall destructors won't run
+ // since the thread is cancelled, so we have to manually execute them
+ // (the thread still can run some user code due to pthread_cleanup_push).
+ ThreadSignalContext *ctx = SigCtx(arg->thr);
+ CHECK_EQ(atomic_load(&ctx->in_blocking_func, memory_order_relaxed), 1);
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
+ MutexLock(arg->thr, arg->pc, (uptr)arg->m);
+ // Undo BlockingCall ctor effects.
+ arg->thr->ignore_interceptors--;
+ arg->si->~ScopedInterceptor();
+}
+
+INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
+ void *cond = init_cond(c, true);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, cond, a);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), true);
+ return REAL(pthread_cond_init)(cond, a);
+}
+
+INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, cond, m);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ MutexUnlock(thr, pc, (uptr)m);
+ CondMutexUnlockCtx arg = {&si, thr, pc, m};
+ int res = 0;
+ // This ensures that we handle mutex lock even in case of pthread_cancel.
+ // See test/tsan/cond_cancel.cc.
+ {
+ // Enable signal delivery while the thread is blocked.
+ BlockingCall bc(thr);
+ res = call_pthread_cancel_with_cleanup(
+ (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait),
+ cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg);
+ }
+ if (res == errno_EOWNERDEAD)
+ MutexRepair(thr, pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
+ return res;
+}
+
+INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, cond, m, abstime);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ MutexUnlock(thr, pc, (uptr)m);
+ CondMutexUnlockCtx arg = {&si, thr, pc, m};
+ int res = 0;
+ // This ensures that we handle mutex lock even in case of pthread_cancel.
+ // See test/tsan/cond_cancel.cc.
+ {
+ BlockingCall bc(thr);
+ res = call_pthread_cancel_with_cleanup(
+ REAL(pthread_cond_timedwait), cond, m, abstime,
+ (void(*)(void *arg))cond_mutex_unlock, &arg);
+ }
+ if (res == errno_EOWNERDEAD)
+ MutexRepair(thr, pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
+ return res;
+}
+
+INTERCEPTOR(int, pthread_cond_signal, void *c) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, cond);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ return REAL(pthread_cond_signal)(cond);
+}
+
+INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, cond);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ return REAL(pthread_cond_broadcast)(cond);
+}
+
+INTERCEPTOR(int, pthread_cond_destroy, void *c) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, cond);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), true);
+ int res = REAL(pthread_cond_destroy)(cond);
+ if (common_flags()->legacy_pthread_cond) {
+ // Free our aux cond and zero the pointer to not leave dangling pointers.
+ WRAP(free)(cond);
+ atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed);
+ }
+ return res;
+}
+
TSAN_INTERCEPTOR(int, pthread_mutex_init, void *m, void *a) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_init, m, a);
int res = REAL(pthread_mutex_init)(m, a);
bool recursive = false;
if (a) {
int type = 0;
- if (pthread_mutexattr_gettype(a, &type) == 0)
+ if (REAL(pthread_mutexattr_gettype)(a, &type) == 0)
recursive = (type == PTHREAD_MUTEX_RECURSIVE
|| type == PTHREAD_MUTEX_RECURSIVE_NP);
}
if (res == EOWNERDEAD)
MutexRepair(thr, pc, (uptr)m);
if (res == 0 || res == EOWNERDEAD)
- MutexLock(thr, pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true);
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime);
int res = REAL(pthread_mutex_timedlock)(m, abstime);
}
return res;
}
+#endif
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) {
SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared);
int res = REAL(pthread_spin_init)(m, pshared);
SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m);
int res = REAL(pthread_spin_trylock)(m);
if (res == 0) {
- MutexLock(thr, pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true);
}
return res;
}
int res = REAL(pthread_spin_unlock)(m);
return res;
}
+#endif
TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a);
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_tryrdlock, m);
int res = REAL(pthread_rwlock_tryrdlock)(m);
if (res == 0) {
- MutexReadLock(thr, pc, (uptr)m);
+ MutexReadLock(thr, pc, (uptr)m, /*try_lock=*/true);
}
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime);
int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
}
return res;
}
+#endif
TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m);
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_trywrlock, m);
int res = REAL(pthread_rwlock_trywrlock)(m);
if (res == 0) {
- MutexLock(thr, pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true);
}
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime);
int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
}
return res;
}
+#endif
TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m);
return res;
}
-TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) {
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c);
- MemoryWrite(thr, pc, (uptr)c, kSizeLog1);
- int res = REAL(pthread_cond_destroy)(c);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m,
- void *abstime) {
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, c, m, abstime);
- MutexUnlock(thr, pc, (uptr)m);
- MemoryRead(thr, pc, (uptr)c, kSizeLog1);
- int res = REAL(pthread_cond_timedwait)(c, m, abstime);
- MutexLock(thr, pc, (uptr)m);
- return res;
-}
-
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) {
SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count);
MemoryWrite(thr, pc, (uptr)b, kSizeLog1);
}
return res;
}
+#endif
TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
SCOPED_INTERCEPTOR_RAW(pthread_once, o, f);
- // Using SCOPED_INTERCEPTOR_RAW, because if we are called from an ignored lib,
- // the user callback must be executed with thr->in_rtl == 0.
if (o == 0 || f == 0)
return EINVAL;
- atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o);
+ atomic_uint32_t *a;
+ if (!SANITIZER_MAC)
+ a = static_cast<atomic_uint32_t*>(o);
+ else // On OS X, pthread_once_t has a header with a long-sized signature.
+ a = static_cast<atomic_uint32_t*>((void *)((char *)o + sizeof(long_t)));
u32 v = atomic_load(a, memory_order_acquire);
if (v == 0 && atomic_compare_exchange_strong(a, &v, 1,
memory_order_relaxed)) {
- const int old_in_rtl = thr->in_rtl;
- thr->in_rtl = 0;
(*f)();
- CHECK_EQ(thr->in_rtl, 0);
- thr->in_rtl = old_in_rtl;
if (!thr->in_ignored_lib)
Release(thr, pc, (uptr)o);
atomic_store(a, 2, memory_order_release);
} else {
while (v != 2) {
- pthread_yield();
+ internal_sched_yield();
v = atomic_load(a, memory_order_acquire);
}
if (!thr->in_ignored_lib)
return 0;
}
-TSAN_INTERCEPTOR(int, sem_init, void *s, int pshared, unsigned value) {
- SCOPED_TSAN_INTERCEPTOR(sem_init, s, pshared, value);
- int res = REAL(sem_init)(s, pshared, value);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, sem_destroy, void *s) {
- SCOPED_TSAN_INTERCEPTOR(sem_destroy, s);
- int res = REAL(sem_destroy)(s);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, sem_wait, void *s) {
- SCOPED_TSAN_INTERCEPTOR(sem_wait, s);
- int res = BLOCK_REAL(sem_wait)(s);
- if (res == 0) {
- Acquire(thr, pc, (uptr)s);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, sem_trywait, void *s) {
- SCOPED_TSAN_INTERCEPTOR(sem_trywait, s);
- int res = BLOCK_REAL(sem_trywait)(s);
- if (res == 0) {
- Acquire(thr, pc, (uptr)s);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, sem_timedwait, void *s, void *abstime) {
- SCOPED_TSAN_INTERCEPTOR(sem_timedwait, s, abstime);
- int res = BLOCK_REAL(sem_timedwait)(s, abstime);
- if (res == 0) {
- Acquire(thr, pc, (uptr)s);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, sem_post, void *s) {
- SCOPED_TSAN_INTERCEPTOR(sem_post, s);
- Release(thr, pc, (uptr)s);
- int res = REAL(sem_post)(s);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) {
- SCOPED_TSAN_INTERCEPTOR(sem_getvalue, s, sval);
- int res = REAL(sem_getvalue)(s, sval);
- if (res == 0) {
- Acquire(thr, pc, (uptr)s);
- }
- return res;
-}
-
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__xstat)(version, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT___XSTAT TSAN_INTERCEPT(__xstat)
+#else
+#define TSAN_MAYBE_INTERCEPT___XSTAT
+#endif
TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
+#if SANITIZER_FREEBSD || SANITIZER_MAC
+ SCOPED_TSAN_INTERCEPTOR(stat, path, buf);
+ READ_STRING(thr, pc, path, 0);
+ return REAL(stat)(path, buf);
+#else
SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__xstat)(0, path, buf);
+#endif
}
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__xstat64)(version, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT___XSTAT64 TSAN_INTERCEPT(__xstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT___XSTAT64
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__xstat64)(0, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT_STAT64 TSAN_INTERCEPT(stat64)
+#else
+#define TSAN_MAYBE_INTERCEPT_STAT64
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__lxstat)(version, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT___LXSTAT TSAN_INTERCEPT(__lxstat)
+#else
+#define TSAN_MAYBE_INTERCEPT___LXSTAT
+#endif
TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
+#if SANITIZER_FREEBSD || SANITIZER_MAC
+ SCOPED_TSAN_INTERCEPTOR(lstat, path, buf);
+ READ_STRING(thr, pc, path, 0);
+ return REAL(lstat)(path, buf);
+#else
SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__lxstat)(0, path, buf);
+#endif
}
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__lxstat64)(version, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT___LXSTAT64 TSAN_INTERCEPT(__lxstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT___LXSTAT64
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__lxstat64)(0, path, buf);
}
+#define TSAN_MAYBE_INTERCEPT_LSTAT64 TSAN_INTERCEPT(lstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT_LSTAT64
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat)(version, fd, buf);
}
+#define TSAN_MAYBE_INTERCEPT___FXSTAT TSAN_INTERCEPT(__fxstat)
+#else
+#define TSAN_MAYBE_INTERCEPT___FXSTAT
+#endif
TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
+#if SANITIZER_FREEBSD || SANITIZER_MAC
+ SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf);
+ if (fd > 0)
+ FdAccess(thr, pc, fd);
+ return REAL(fstat)(fd, buf);
+#else
SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat)(0, fd, buf);
+#endif
}
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat64)(version, fd, buf);
}
+#define TSAN_MAYBE_INTERCEPT___FXSTAT64 TSAN_INTERCEPT(__fxstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT___FXSTAT64
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat64)(0, fd, buf);
}
+#define TSAN_MAYBE_INTERCEPT_FSTAT64 TSAN_INTERCEPT(fstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT_FSTAT64
+#endif
TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode);
+ READ_STRING(thr, pc, name, 0);
int fd = REAL(open)(name, flags, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
return fd;
}
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
+ READ_STRING(thr, pc, name, 0);
int fd = REAL(open64)(name, flags, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_OPEN64 TSAN_INTERCEPT(open64)
+#else
+#define TSAN_MAYBE_INTERCEPT_OPEN64
+#endif
TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat, name, mode);
+ READ_STRING(thr, pc, name, 0);
int fd = REAL(creat)(name, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
return fd;
}
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
+ READ_STRING(thr, pc, name, 0);
int fd = REAL(creat64)(name, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_CREAT64 TSAN_INTERCEPT(creat64)
+#else
+#define TSAN_MAYBE_INTERCEPT_CREAT64
+#endif
TSAN_INTERCEPTOR(int, dup, int oldfd) {
SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
int newfd = REAL(dup)(oldfd);
if (oldfd >= 0 && newfd >= 0 && newfd != oldfd)
- FdDup(thr, pc, oldfd, newfd);
+ FdDup(thr, pc, oldfd, newfd, true);
return newfd;
}
SCOPED_TSAN_INTERCEPTOR(dup2, oldfd, newfd);
int newfd2 = REAL(dup2)(oldfd, newfd);
if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
- FdDup(thr, pc, oldfd, newfd2);
+ FdDup(thr, pc, oldfd, newfd2, false);
return newfd2;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags);
int newfd2 = REAL(dup3)(oldfd, newfd, flags);
if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
- FdDup(thr, pc, oldfd, newfd2);
+ FdDup(thr, pc, oldfd, newfd2, false);
return newfd2;
}
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags);
int fd = REAL(eventfd)(initval, flags);
FdEventCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_EVENTFD TSAN_INTERCEPT(eventfd)
+#else
+#define TSAN_MAYBE_INTERCEPT_EVENTFD
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags);
if (fd >= 0)
FdSignalCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_SIGNALFD TSAN_INTERCEPT(signalfd)
+#else
+#define TSAN_MAYBE_INTERCEPT_SIGNALFD
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, inotify_init, int fake) {
SCOPED_TSAN_INTERCEPTOR(inotify_init, fake);
int fd = REAL(inotify_init)(fake);
FdInotifyCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT TSAN_INTERCEPT(inotify_init)
+#else
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, inotify_init1, int flags) {
SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags);
int fd = REAL(inotify_init1)(flags);
FdInotifyCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1 TSAN_INTERCEPT(inotify_init1)
+#else
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1
+#endif
TSAN_INTERCEPTOR(int, socket, int domain, int type, int protocol) {
SCOPED_TSAN_INTERCEPTOR(socket, domain, type, protocol);
return res;
}
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_create, int size) {
SCOPED_TSAN_INTERCEPTOR(epoll_create, size);
int fd = REAL(epoll_create)(size);
FdPollCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE TSAN_INTERCEPT(epoll_create)
+#else
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags);
int fd = REAL(epoll_create1)(flags);
FdPollCreate(thr, pc, fd);
return fd;
}
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1 TSAN_INTERCEPT(epoll_create1)
+#else
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1
+#endif
TSAN_INTERCEPTOR(int, close, int fd) {
SCOPED_TSAN_INTERCEPTOR(close, fd);
return REAL(close)(fd);
}
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __close, int fd) {
SCOPED_TSAN_INTERCEPTOR(__close, fd);
if (fd >= 0)
FdClose(thr, pc, fd);
return REAL(__close)(fd);
}
+#define TSAN_MAYBE_INTERCEPT___CLOSE TSAN_INTERCEPT(__close)
+#else
+#define TSAN_MAYBE_INTERCEPT___CLOSE
+#endif
// glibc guts
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) {
SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr);
int fds[64];
}
REAL(__res_iclose)(state, free_addr);
}
+#define TSAN_MAYBE_INTERCEPT___RES_ICLOSE TSAN_INTERCEPT(__res_iclose)
+#else
+#define TSAN_MAYBE_INTERCEPT___RES_ICLOSE
+#endif
TSAN_INTERCEPTOR(int, pipe, int *pipefd) {
SCOPED_TSAN_INTERCEPTOR(pipe, pipefd);
return res;
}
+#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags);
int res = REAL(pipe2)(pipefd, flags);
FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
return res;
}
+#endif
TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) {
SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags);
return res;
}
-TSAN_INTERCEPTOR(void*, fopen, char *path, char *mode) {
- SCOPED_TSAN_INTERCEPTOR(fopen, path, mode);
- void *res = REAL(fopen)(path, mode);
- Acquire(thr, pc, File2addr(path));
+TSAN_INTERCEPTOR(void*, tmpfile, int fake) {
+ SCOPED_TSAN_INTERCEPTOR(tmpfile, fake);
+ void *res = REAL(tmpfile)(fake);
if (res) {
int fd = fileno_unlocked(res);
if (fd >= 0)
return res;
}
-TSAN_INTERCEPTOR(void*, freopen, char *path, char *mode, void *stream) {
- SCOPED_TSAN_INTERCEPTOR(freopen, path, mode, stream);
- if (stream) {
- int fd = fileno_unlocked(stream);
- if (fd >= 0)
- FdClose(thr, pc, fd);
- }
- void *res = REAL(freopen)(path, mode, stream);
- Acquire(thr, pc, File2addr(path));
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(void*, tmpfile64, int fake) {
+ SCOPED_TSAN_INTERCEPTOR(tmpfile64, fake);
+ void *res = REAL(tmpfile64)(fake);
if (res) {
int fd = fileno_unlocked(res);
if (fd >= 0)
}
return res;
}
-
-TSAN_INTERCEPTOR(int, fclose, void *stream) {
- // libc file streams can call user-supplied functions, see fopencookie.
- {
- SCOPED_TSAN_INTERCEPTOR(fclose, stream);
- if (stream) {
- int fd = fileno_unlocked(stream);
- if (fd >= 0)
- FdClose(thr, pc, fd);
- }
- }
- return REAL(fclose)(stream);
-}
+#define TSAN_MAYBE_INTERCEPT_TMPFILE64 TSAN_INTERCEPT(tmpfile64)
+#else
+#define TSAN_MAYBE_INTERCEPT_TMPFILE64
+#endif
TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) {
// libc file streams can call user-supplied functions, see fopencookie.
return REAL(fwrite)(p, size, nmemb, f);
}
-TSAN_INTERCEPTOR(int, fflush, void *stream) {
- // libc file streams can call user-supplied functions, see fopencookie.
- {
- SCOPED_TSAN_INTERCEPTOR(fflush, stream);
- }
- return REAL(fflush)(stream);
+static void FlushStreams() {
+ // Flushing all the streams here may freeze the process if a child thread is
+ // performing file stream operations at the same time.
+ REAL(fflush)(stdout);
+ REAL(fflush)(stderr);
}
TSAN_INTERCEPTOR(void, abort, int fake) {
SCOPED_TSAN_INTERCEPTOR(abort, fake);
- REAL(fflush)(0);
+ FlushStreams();
REAL(abort)(fake);
}
return res;
}
-TSAN_INTERCEPTOR(void*, opendir, char *path) {
- SCOPED_TSAN_INTERCEPTOR(opendir, path);
- void *res = REAL(opendir)(path);
- if (res != 0)
- Acquire(thr, pc, Dir2addr(path));
- return res;
+TSAN_INTERCEPTOR(int, closedir, void *dirp) {
+ SCOPED_TSAN_INTERCEPTOR(closedir, dirp);
+ int fd = dirfd(dirp);
+ FdClose(thr, pc, fd);
+ return REAL(closedir)(dirp);
}
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
if (epfd >= 0)
int res = REAL(epoll_ctl)(epfd, op, fd, ev);
return res;
}
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CTL TSAN_INTERCEPT(epoll_ctl)
+#else
+#define TSAN_MAYBE_INTERCEPT_EPOLL_CTL
+#endif
+#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
if (epfd >= 0)
FdAcquire(thr, pc, epfd);
return res;
}
+#define TSAN_MAYBE_INTERCEPT_EPOLL_WAIT TSAN_INTERCEPT(epoll_wait)
+#else
+#define TSAN_MAYBE_INTERCEPT_EPOLL_WAIT
+#endif
+
+namespace __tsan {
+
+static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
+ bool sigact, int sig, my_siginfo_t *info, void *uctx) {
+ if (acquire)
+ Acquire(thr, 0, (uptr)&sigactions[sig]);
+ // Ensure that the handler does not spoil errno.
+ const int saved_errno = errno;
+ errno = 99;
+ // This code races with sigaction. Be careful to not read sa_sigaction twice.
+ // Also need to remember pc for reporting before the call,
+ // because the handler can reset it.
+ volatile uptr pc = sigact ?
+ (uptr)sigactions[sig].sa_sigaction :
+ (uptr)sigactions[sig].sa_handler;
+ if (pc != (uptr)SIG_DFL && pc != (uptr)SIG_IGN) {
+ if (sigact)
+ ((sigactionhandler_t)pc)(sig, info, uctx);
+ else
+ ((sighandler_t)pc)(sig);
+ }
+ // We do not detect errno spoiling for SIGTERM,
+ // because some SIGTERM handlers do spoil errno but reraise SIGTERM,
+ // tsan reports false positive in such case.
+ // It's difficult to properly detect this situation (reraise),
+ // because in async signal processing case (when handler is called directly
+ // from rtl_generic_sighandler) we have not yet received the reraised
+ // signal; and it looks too fragile to intercept all ways to reraise a signal.
+ if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) {
+ VarSizeStackTrace stack;
+ // StackTrace::GetNestInstructionPc(pc) is used because return address is
+ // expected, OutputReport() will undo this.
+ ObtainCurrentStack(thr, StackTrace::GetNextInstructionPc(pc), &stack);
+ ThreadRegistryLock l(ctx->thread_registry);
+ ScopedReport rep(ReportTypeErrnoInSignal);
+ if (!IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack)) {
+ rep.AddStack(stack, true);
+ OutputReport(thr, rep);
+ }
+ }
+ errno = saved_errno;
+}
+
+void ProcessPendingSignals(ThreadState *thr) {
+ ThreadSignalContext *sctx = SigCtx(thr);
+ if (sctx == 0 ||
+ atomic_load(&sctx->have_pending_signals, memory_order_relaxed) == 0)
+ return;
+ atomic_store(&sctx->have_pending_signals, 0, memory_order_relaxed);
+ atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
+ // These are too big for stack.
+ static THREADLOCAL __sanitizer_sigset_t emptyset, oldset;
+ CHECK_EQ(0, REAL(sigfillset)(&emptyset));
+ CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &emptyset, &oldset));
+ for (int sig = 0; sig < kSigCount; sig++) {
+ SignalDesc *signal = &sctx->pending_signals[sig];
+ if (signal->armed) {
+ signal->armed = false;
+ CallUserSignalHandler(thr, false, true, signal->sigaction, sig,
+ &signal->siginfo, &signal->ctx);
+ }
+ }
+ CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &oldset, 0));
+ atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed);
+}
+
+} // namespace __tsan
+
+static bool is_sync_signal(ThreadSignalContext *sctx, int sig) {
+ return sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
+ sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS ||
+ // If we are sending signal to ourselves, we must process it now.
+ (sctx && sig == sctx->int_signal_send);
+}
void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
my_siginfo_t *info, void *ctx) {
ThreadState *thr = cur_thread();
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
+ if (sig < 0 || sig >= kSigCount) {
+ VPrintf(1, "ThreadSanitizer: ignoring signal %d\n", sig);
+ return;
+ }
// Don't mess with synchronous signals.
- if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
- sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS ||
- // If we are sending signal to ourselves, we must process it now.
- (sctx && sig == sctx->int_signal_send) ||
+ const bool sync = is_sync_signal(sctx, sig);
+ if (sync ||
// If we are in blocking function, we can safely process it now
// (but check if we are in a recursive interceptor,
// i.e. pthread_join()->munmap()).
- (sctx && sctx->in_blocking_func == 1 && thr->in_rtl == 1)) {
- int in_rtl = thr->in_rtl;
- thr->in_rtl = 0;
- CHECK_EQ(thr->in_signal_handler, false);
- thr->in_signal_handler = true;
- if (sigact)
- sigactions[sig].sa_sigaction(sig, info, ctx);
- else
- sigactions[sig].sa_handler(sig);
- CHECK_EQ(thr->in_signal_handler, true);
- thr->in_signal_handler = false;
- thr->in_rtl = in_rtl;
+ (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed))) {
+ atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
+ if (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed)) {
+ // We ignore interceptors in blocking functions,
+ // temporary enbled them again while we are calling user function.
+ int const i = thr->ignore_interceptors;
+ thr->ignore_interceptors = 0;
+ atomic_store(&sctx->in_blocking_func, 0, memory_order_relaxed);
+ CallUserSignalHandler(thr, sync, true, sigact, sig, info, ctx);
+ thr->ignore_interceptors = i;
+ atomic_store(&sctx->in_blocking_func, 1, memory_order_relaxed);
+ } else {
+ // Be very conservative with when we do acquire in this case.
+ // It's unsafe to do acquire in async handlers, because ThreadState
+ // can be in inconsistent state.
+ // SIGSYS looks relatively safe -- it's synchronous and can actually
+ // need some global state.
+ bool acq = (sig == SIGSYS);
+ CallUserSignalHandler(thr, sync, acq, sigact, sig, info, ctx);
+ }
+ atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed);
return;
}
internal_memcpy(&signal->siginfo, info, sizeof(*info));
if (ctx)
internal_memcpy(&signal->ctx, ctx, sizeof(signal->ctx));
- sctx->pending_signal_count++;
+ atomic_store(&sctx->have_pending_signals, 1, memory_order_relaxed);
}
}
internal_memcpy(old, &sigactions[sig], sizeof(*old));
if (act == 0)
return 0;
- internal_memcpy(&sigactions[sig], act, sizeof(*act));
+ // Copy act into sigactions[sig].
+ // Can't use struct copy, because compiler can emit call to memcpy.
+ // Can't use internal_memcpy, because it copies byte-by-byte,
+ // and signal handler reads the sa_handler concurrently. It it can read
+ // some bytes from old value and some bytes from new value.
+ // Use volatile to prevent insertion of memcpy.
+ sigactions[sig].sa_handler = *(volatile sighandler_t*)&act->sa_handler;
+ sigactions[sig].sa_flags = *(volatile int*)&act->sa_flags;
+ internal_memcpy(&sigactions[sig].sa_mask, &act->sa_mask,
+ sizeof(sigactions[sig].sa_mask));
+#if !SANITIZER_FREEBSD
+ sigactions[sig].sa_restorer = act->sa_restorer;
+#endif
sigaction_t newact;
internal_memcpy(&newact, act, sizeof(newact));
REAL(sigfillset)(&newact.sa_mask);
else
newact.sa_handler = rtl_sighandler;
}
+ ReleaseStore(thr, pc, (uptr)&sigactions[sig]);
int res = REAL(sigaction)(sig, &newact, 0);
return res;
}
TSAN_INTERCEPTOR(int, raise, int sig) {
SCOPED_TSAN_INTERCEPTOR(raise, sig);
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
CHECK_NE(sctx, 0);
int prev = sctx->int_signal_send;
sctx->int_signal_send = sig;
TSAN_INTERCEPTOR(int, kill, int pid, int sig) {
SCOPED_TSAN_INTERCEPTOR(kill, pid, sig);
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
CHECK_NE(sctx, 0);
int prev = sctx->int_signal_send;
if (pid == (int)internal_getpid()) {
TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) {
SCOPED_TSAN_INTERCEPTOR(pthread_kill, tid, sig);
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
CHECK_NE(sctx, 0);
int prev = sctx->int_signal_send;
if (tid == pthread_self()) {
// and can report false race between malloc and free
// inside of getaddrinfo. So ignore memory accesses.
ThreadIgnoreBegin(thr, pc);
- // getaddrinfo calls fopen, which can be intercepted by user.
- thr->in_rtl--;
- CHECK_EQ(thr->in_rtl, 0);
int res = REAL(getaddrinfo)(node, service, hints, rv);
- thr->in_rtl++;
ThreadIgnoreEnd(thr, pc);
return res;
}
-// Linux kernel has a bug that leads to kernel deadlock if a process
-// maps TBs of memory and then calls mlock().
-static void MlockIsUnsupported() {
- static atomic_uint8_t printed;
- if (atomic_exchange(&printed, 1, memory_order_relaxed))
- return;
- if (flags()->verbosity > 0)
- Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n");
-}
-
-TSAN_INTERCEPTOR(int, mlock, const void *addr, uptr len) {
- MlockIsUnsupported();
- return 0;
-}
-
-TSAN_INTERCEPTOR(int, munlock, const void *addr, uptr len) {
- MlockIsUnsupported();
- return 0;
-}
-
-TSAN_INTERCEPTOR(int, mlockall, int flags) {
- MlockIsUnsupported();
- return 0;
-}
-
-TSAN_INTERCEPTOR(int, munlockall, void) {
- MlockIsUnsupported();
- return 0;
-}
-
TSAN_INTERCEPTOR(int, fork, int fake) {
+ if (cur_thread()->in_symbolizer)
+ return REAL(fork)(fake);
SCOPED_INTERCEPTOR_RAW(fork, fake);
+ ForkBefore(thr, pc);
int pid = REAL(fork)(fake);
if (pid == 0) {
// child
+ ForkChildAfter(thr, pc);
FdOnFork(thr, pc);
} else if (pid > 0) {
// parent
+ ForkParentAfter(thr, pc);
+ } else {
+ // error
+ ForkParentAfter(thr, pc);
}
return pid;
}
+TSAN_INTERCEPTOR(int, vfork, int fake) {
+ // Some programs (e.g. openjdk) call close for all file descriptors
+ // in the child process. Under tsan it leads to false positives, because
+ // address space is shared, so the parent process also thinks that
+ // the descriptors are closed (while they are actually not).
+ // This leads to false positives due to missed synchronization.
+ // Strictly saying this is undefined behavior, because vfork child is not
+ // allowed to call any functions other than exec/exit. But this is what
+ // openjdk does, so we want to handle it.
+ // We could disable interceptors in the child process. But it's not possible
+ // to simply intercept and wrap vfork, because vfork child is not allowed
+ // to return from the function that calls vfork, and that's exactly what
+ // we would do. So this would require some assembly trickery as well.
+ // Instead we simply turn vfork into fork.
+ return WRAP(fork)(fake);
+}
+
+#if !SANITIZER_MAC
+typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size,
+ void *data);
+struct dl_iterate_phdr_data {
+ ThreadState *thr;
+ uptr pc;
+ dl_iterate_phdr_cb_t cb;
+ void *data;
+};
+
+static bool IsAppNotRodata(uptr addr) {
+ return IsAppMem(addr) && *(u64*)MemToShadow(addr) != kShadowRodata;
+}
+
+static int dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size,
+ void *data) {
+ dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data;
+ // dlopen/dlclose allocate/free dynamic-linker-internal memory, which is later
+ // accessible in dl_iterate_phdr callback. But we don't see synchronization
+ // inside of dynamic linker, so we "unpoison" it here in order to not
+ // produce false reports. Ignoring malloc/free in dlopen/dlclose is not enough
+ // because some libc functions call __libc_dlopen.
+ if (info && IsAppNotRodata((uptr)info->dlpi_name))
+ MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name,
+ internal_strlen(info->dlpi_name));
+ int res = cbdata->cb(info, size, cbdata->data);
+ // Perform the check one more time in case info->dlpi_name was overwritten
+ // by user callback.
+ if (info && IsAppNotRodata((uptr)info->dlpi_name))
+ MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name,
+ internal_strlen(info->dlpi_name));
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb_t cb, void *data) {
+ SCOPED_TSAN_INTERCEPTOR(dl_iterate_phdr, cb, data);
+ dl_iterate_phdr_data cbdata;
+ cbdata.thr = thr;
+ cbdata.pc = pc;
+ cbdata.cb = cb;
+ cbdata.data = data;
+ int res = REAL(dl_iterate_phdr)(dl_iterate_phdr_cb, &cbdata);
+ return res;
+}
+#endif
+
static int OnExit(ThreadState *thr) {
int status = Finalize(thr);
- REAL(fflush)(0);
+ FlushStreams();
return status;
}
const uptr pc;
};
+#if !SANITIZER_MAC
static void HandleRecvmsg(ThreadState *thr, uptr pc,
__sanitizer_msghdr *msg) {
int fds[64];
for (int i = 0; i < cnt; i++)
FdEventCreate(thr, pc, fds[i]);
}
+#endif
#include "sanitizer_common/sanitizer_platform_interceptors.h"
-// Causes interceptor recursion (getpwuid_r() calls fopen())
-#undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
-#undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
// Causes interceptor recursion (getaddrinfo() and fopen())
#undef SANITIZER_INTERCEPT_GETADDRINFO
-#undef SANITIZER_INTERCEPT_GETNAMEINFO
-// Causes interceptor recursion (glob64() calls lstat64())
-#undef SANITIZER_INTERCEPT_GLOB
+// There interceptors do not seem to be strictly necessary for tsan.
+// But we see cases where the interceptors consume 70% of execution time.
+// Memory blocks passed to fgetgrent_r are "written to" by tsan several times.
+// First, there is some recursion (getgrnam_r calls fgetgrent_r), and each
+// function "writes to" the buffer. Then, the same memory is "written to"
+// twice, first as buf and then as pwbufp (both of them refer to the same
+// addresses).
+#undef SANITIZER_INTERCEPT_GETPWENT
+#undef SANITIZER_INTERCEPT_GETPWENT_R
+#undef SANITIZER_INTERCEPT_FGETPWENT
+#undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
+#undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
+// __tls_get_addr can be called with mis-aligned stack due to:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066
+// There are two potential issues:
+// 1. Sanitizer code contains a MOVDQA spill (it does not seem to be the case
+// right now). or 2. ProcessPendingSignal calls user handler which contains
+// MOVDQA spill (this happens right now).
+// Since the interceptor only initializes memory for msan, the simplest solution
+// is to disable the interceptor in tsan (other sanitizers do not call
+// signal handlers from COMMON_INTERCEPTOR_ENTER).
+// As __tls_get_addr has been intercepted in the past, to avoid breaking
+// libtsan ABI, keep it around, but just call the real function.
+#if SANITIZER_INTERCEPT_TLS_GET_ADDR
+#define NEED_TLS_GET_ADDR
+#endif
+#undef SANITIZER_INTERCEPT_TLS_GET_ADDR
#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
-#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \
- do { \
- } while (false)
#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \
ctx = (void *)&_ctx; \
(void) ctx;
+#define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, func, ...) \
+ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
+ TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \
+ ctx = (void *)&_ctx; \
+ (void) ctx;
+
+#define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) \
+ Acquire(thr, pc, File2addr(path)); \
+ if (file) { \
+ int fd = fileno_unlocked(file); \
+ if (fd >= 0) FdFileCreate(thr, pc, fd); \
+ }
+
+#define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) \
+ if (file) { \
+ int fd = fileno_unlocked(file); \
+ if (fd >= 0) FdClose(thr, pc, fd); \
+ }
+
+#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \
+ libignore()->OnLibraryLoaded(filename)
+
+#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() \
+ libignore()->OnLibraryUnloaded()
+
+#define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) \
+ Acquire(((TsanInterceptorContext *) ctx)->thr, pc, u)
+
+#define COMMON_INTERCEPTOR_RELEASE(ctx, u) \
+ Release(((TsanInterceptorContext *) ctx)->thr, pc, u)
+
+#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
+ Acquire(((TsanInterceptorContext *) ctx)->thr, pc, Dir2addr(path))
+
#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd)
ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name)
#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
- CTX()->thread_registry->SetThreadNameByUserId(thread, name)
+ __tsan::ctx->thread_registry->SetThreadNameByUserId(thread, name)
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name)
MutexRepair(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+#if !SANITIZER_MAC
#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, msg)
+#endif
+
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
+ if (TsanThread *t = GetCurrentThread()) { \
+ *begin = t->tls_begin(); \
+ *end = t->tls_end(); \
+ } else { \
+ *begin = *end = 0; \
+ }
#include "sanitizer_common/sanitizer_common_interceptors.inc"
#define TSAN_SYSCALL() \
ThreadState *thr = cur_thread(); \
+ if (thr->ignore_interceptors) \
+ return; \
ScopedSyscall scoped_syscall(thr) \
/**/
explicit ScopedSyscall(ThreadState *thr)
: thr(thr) {
- if (thr->in_rtl == 0)
- Initialize(thr);
- thr->in_rtl++;
+ Initialize(thr);
}
~ScopedSyscall() {
- thr->in_rtl--;
- if (thr->in_rtl == 0)
- ProcessPendingSignals(thr);
+ ProcessPendingSignals(thr);
}
};
+#if !SANITIZER_MAC
static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) {
TSAN_SYSCALL();
MemoryAccessRange(thr, pc, p, s, write);
static void syscall_acquire(uptr pc, uptr addr) {
TSAN_SYSCALL();
Acquire(thr, pc, addr);
- Printf("syscall_acquire(%p)\n", addr);
+ DPrintf("syscall_acquire(%p)\n", addr);
}
static void syscall_release(uptr pc, uptr addr) {
TSAN_SYSCALL();
- Printf("syscall_release(%p)\n", addr);
+ DPrintf("syscall_release(%p)\n", addr);
Release(thr, pc, addr);
}
static USED void syscall_fd_acquire(uptr pc, int fd) {
TSAN_SYSCALL();
FdAcquire(thr, pc, fd);
- Printf("syscall_fd_acquire(%p)\n", fd);
+ DPrintf("syscall_fd_acquire(%p)\n", fd);
}
static USED void syscall_fd_release(uptr pc, int fd) {
TSAN_SYSCALL();
- Printf("syscall_fd_release(%p)\n", fd);
+ DPrintf("syscall_fd_release(%p)\n", fd);
FdRelease(thr, pc, fd);
}
static void syscall_pre_fork(uptr pc) {
TSAN_SYSCALL();
+ ForkBefore(thr, pc);
}
-static void syscall_post_fork(uptr pc, int res) {
+static void syscall_post_fork(uptr pc, int pid) {
TSAN_SYSCALL();
- if (res == 0) {
+ if (pid == 0) {
// child
+ ForkChildAfter(thr, pc);
FdOnFork(thr, pc);
- } else if (res > 0) {
+ } else if (pid > 0) {
// parent
+ ForkParentAfter(thr, pc);
+ } else {
+ // error
+ ForkParentAfter(thr, pc);
}
}
+#endif
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \
syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false)
#include "sanitizer_common/sanitizer_common_syscalls.inc"
+#ifdef NEED_TLS_GET_ADDR
+TSAN_INTERCEPTOR(void *, __tls_get_addr, void *arg) {
+ return REAL(__tls_get_addr)(arg);
+}
+#endif
+
namespace __tsan {
static void finalize(void *arg) {
ThreadState *thr = cur_thread();
- uptr pc = 0;
- atexit_ctx->exit(thr, pc);
int status = Finalize(thr);
- REAL(fflush)(0);
+ // Make sure the output is not lost.
+ FlushStreams();
if (status)
- REAL(_exit)(status);
-}
-
-void ProcessPendingSignals(ThreadState *thr) {
- CHECK_EQ(thr->in_rtl, 0);
- SignalContext *sctx = SigCtx(thr);
- if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler)
- return;
- Context *ctx = CTX();
- thr->in_signal_handler = true;
- sctx->pending_signal_count = 0;
- // These are too big for stack.
- static THREADLOCAL __sanitizer_sigset_t emptyset, oldset;
- REAL(sigfillset)(&emptyset);
- pthread_sigmask(SIG_SETMASK, &emptyset, &oldset);
- for (int sig = 0; sig < kSigCount; sig++) {
- SignalDesc *signal = &sctx->pending_signals[sig];
- if (signal->armed) {
- signal->armed = false;
- if (sigactions[sig].sa_handler != SIG_DFL
- && sigactions[sig].sa_handler != SIG_IGN) {
- // Insure that the handler does not spoil errno.
- const int saved_errno = errno;
- errno = 0;
- if (signal->sigaction)
- sigactions[sig].sa_sigaction(sig, &signal->siginfo, &signal->ctx);
- else
- sigactions[sig].sa_handler(sig);
- if (flags()->report_bugs && errno != 0) {
- ScopedInRtl in_rtl;
- __tsan::StackTrace stack;
- uptr pc = signal->sigaction ?
- (uptr)sigactions[sig].sa_sigaction :
- (uptr)sigactions[sig].sa_handler;
- pc += 1; // return address is expected, OutputReport() will undo this
- stack.Init(&pc, 1);
- ThreadRegistryLock l(ctx->thread_registry);
- ScopedReport rep(ReportTypeErrnoInSignal);
- if (!IsFiredSuppression(ctx, rep, stack)) {
- rep.AddStack(&stack);
- OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
- }
- }
- errno = saved_errno;
- }
- }
- }
- pthread_sigmask(SIG_SETMASK, &oldset, 0);
- CHECK_EQ(thr->in_signal_handler, true);
- thr->in_signal_handler = false;
+ Die();
}
+#if !SANITIZER_MAC
static void unreachable() {
- Printf("FATAL: ThreadSanitizer: unreachable called\n");
+ Report("FATAL: ThreadSanitizer: unreachable called\n");
Die();
}
+#endif
void InitializeInterceptors() {
- CHECK_GT(cur_thread()->in_rtl, 0);
-
+#if !SANITIZER_MAC
// We need to setup it early, because functions like dlsym() can call it.
REAL(memset) = internal_memset;
REAL(memcpy) = internal_memcpy;
- REAL(memcmp) = internal_memcmp;
+#endif
// Instruct libc malloc to consume less memory.
+#if SANITIZER_LINUX
mallopt(1, 0); // M_MXFAST
mallopt(-3, 32*1024); // M_MMAP_THRESHOLD
+#endif
- SANITIZER_COMMON_INTERCEPTORS_INIT;
+ InitializeCommonInterceptors();
+#if !SANITIZER_MAC
// We can not use TSAN_INTERCEPT to get setjmp addr,
// because it does &setjmp and setjmp is not present in some versions of libc.
using __interception::GetRealFunctionAddress;
GetRealFunctionAddress("_setjmp", (uptr*)&REAL(_setjmp), 0, 0);
GetRealFunctionAddress("sigsetjmp", (uptr*)&REAL(sigsetjmp), 0, 0);
GetRealFunctionAddress("__sigsetjmp", (uptr*)&REAL(__sigsetjmp), 0, 0);
+#endif
TSAN_INTERCEPT(longjmp);
TSAN_INTERCEPT(siglongjmp);
TSAN_INTERCEPT(free);
TSAN_INTERCEPT(cfree);
TSAN_INTERCEPT(mmap);
- TSAN_INTERCEPT(mmap64);
+ TSAN_MAYBE_INTERCEPT_MMAP64;
TSAN_INTERCEPT(munmap);
- TSAN_INTERCEPT(memalign);
+ TSAN_MAYBE_INTERCEPT_MEMALIGN;
TSAN_INTERCEPT(valloc);
- TSAN_INTERCEPT(pvalloc);
+ TSAN_MAYBE_INTERCEPT_PVALLOC;
TSAN_INTERCEPT(posix_memalign);
TSAN_INTERCEPT(strlen);
TSAN_INTERCEPT(memset);
TSAN_INTERCEPT(memcpy);
- TSAN_INTERCEPT(memchr);
- TSAN_INTERCEPT(memrchr);
TSAN_INTERCEPT(memmove);
- TSAN_INTERCEPT(memcmp);
TSAN_INTERCEPT(strchr);
TSAN_INTERCEPT(strchrnul);
TSAN_INTERCEPT(strrchr);
TSAN_INTERCEPT(strcpy); // NOLINT
TSAN_INTERCEPT(strncpy);
- TSAN_INTERCEPT(strstr);
TSAN_INTERCEPT(strdup);
TSAN_INTERCEPT(pthread_create);
TSAN_INTERCEPT(pthread_join);
TSAN_INTERCEPT(pthread_detach);
+ TSAN_INTERCEPT_VER(pthread_cond_init, PTHREAD_ABI_BASE);
+ TSAN_INTERCEPT_VER(pthread_cond_signal, PTHREAD_ABI_BASE);
+ TSAN_INTERCEPT_VER(pthread_cond_broadcast, PTHREAD_ABI_BASE);
+ TSAN_INTERCEPT_VER(pthread_cond_wait, PTHREAD_ABI_BASE);
+ TSAN_INTERCEPT_VER(pthread_cond_timedwait, PTHREAD_ABI_BASE);
+ TSAN_INTERCEPT_VER(pthread_cond_destroy, PTHREAD_ABI_BASE);
+
TSAN_INTERCEPT(pthread_mutex_init);
TSAN_INTERCEPT(pthread_mutex_destroy);
TSAN_INTERCEPT(pthread_mutex_trylock);
TSAN_INTERCEPT(pthread_rwlock_timedwrlock);
TSAN_INTERCEPT(pthread_rwlock_unlock);
- INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2");
- INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2");
-
TSAN_INTERCEPT(pthread_barrier_init);
TSAN_INTERCEPT(pthread_barrier_destroy);
TSAN_INTERCEPT(pthread_barrier_wait);
TSAN_INTERCEPT(pthread_once);
- TSAN_INTERCEPT(sem_init);
- TSAN_INTERCEPT(sem_destroy);
- TSAN_INTERCEPT(sem_wait);
- TSAN_INTERCEPT(sem_trywait);
- TSAN_INTERCEPT(sem_timedwait);
- TSAN_INTERCEPT(sem_post);
- TSAN_INTERCEPT(sem_getvalue);
-
TSAN_INTERCEPT(stat);
- TSAN_INTERCEPT(__xstat);
- TSAN_INTERCEPT(stat64);
- TSAN_INTERCEPT(__xstat64);
+ TSAN_MAYBE_INTERCEPT___XSTAT;
+ TSAN_MAYBE_INTERCEPT_STAT64;
+ TSAN_MAYBE_INTERCEPT___XSTAT64;
TSAN_INTERCEPT(lstat);
- TSAN_INTERCEPT(__lxstat);
- TSAN_INTERCEPT(lstat64);
- TSAN_INTERCEPT(__lxstat64);
+ TSAN_MAYBE_INTERCEPT___LXSTAT;
+ TSAN_MAYBE_INTERCEPT_LSTAT64;
+ TSAN_MAYBE_INTERCEPT___LXSTAT64;
TSAN_INTERCEPT(fstat);
- TSAN_INTERCEPT(__fxstat);
- TSAN_INTERCEPT(fstat64);
- TSAN_INTERCEPT(__fxstat64);
+ TSAN_MAYBE_INTERCEPT___FXSTAT;
+ TSAN_MAYBE_INTERCEPT_FSTAT64;
+ TSAN_MAYBE_INTERCEPT___FXSTAT64;
TSAN_INTERCEPT(open);
- TSAN_INTERCEPT(open64);
+ TSAN_MAYBE_INTERCEPT_OPEN64;
TSAN_INTERCEPT(creat);
- TSAN_INTERCEPT(creat64);
+ TSAN_MAYBE_INTERCEPT_CREAT64;
TSAN_INTERCEPT(dup);
TSAN_INTERCEPT(dup2);
TSAN_INTERCEPT(dup3);
- TSAN_INTERCEPT(eventfd);
- TSAN_INTERCEPT(signalfd);
- TSAN_INTERCEPT(inotify_init);
- TSAN_INTERCEPT(inotify_init1);
+ TSAN_MAYBE_INTERCEPT_EVENTFD;
+ TSAN_MAYBE_INTERCEPT_SIGNALFD;
+ TSAN_MAYBE_INTERCEPT_INOTIFY_INIT;
+ TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1;
TSAN_INTERCEPT(socket);
TSAN_INTERCEPT(socketpair);
TSAN_INTERCEPT(connect);
TSAN_INTERCEPT(bind);
TSAN_INTERCEPT(listen);
- TSAN_INTERCEPT(epoll_create);
- TSAN_INTERCEPT(epoll_create1);
+ TSAN_MAYBE_INTERCEPT_EPOLL_CREATE;
+ TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1;
TSAN_INTERCEPT(close);
- TSAN_INTERCEPT(__close);
- TSAN_INTERCEPT(__res_iclose);
+ TSAN_MAYBE_INTERCEPT___CLOSE;
+ TSAN_MAYBE_INTERCEPT___RES_ICLOSE;
TSAN_INTERCEPT(pipe);
TSAN_INTERCEPT(pipe2);
TSAN_INTERCEPT(recv);
TSAN_INTERCEPT(unlink);
- TSAN_INTERCEPT(fopen);
- TSAN_INTERCEPT(freopen);
- TSAN_INTERCEPT(fclose);
+ TSAN_INTERCEPT(tmpfile);
+ TSAN_MAYBE_INTERCEPT_TMPFILE64;
TSAN_INTERCEPT(fread);
TSAN_INTERCEPT(fwrite);
- TSAN_INTERCEPT(fflush);
TSAN_INTERCEPT(abort);
TSAN_INTERCEPT(puts);
TSAN_INTERCEPT(rmdir);
- TSAN_INTERCEPT(opendir);
+ TSAN_INTERCEPT(closedir);
- TSAN_INTERCEPT(epoll_ctl);
- TSAN_INTERCEPT(epoll_wait);
+ TSAN_MAYBE_INTERCEPT_EPOLL_CTL;
+ TSAN_MAYBE_INTERCEPT_EPOLL_WAIT;
TSAN_INTERCEPT(sigaction);
TSAN_INTERCEPT(signal);
TSAN_INTERCEPT(gettimeofday);
TSAN_INTERCEPT(getaddrinfo);
- TSAN_INTERCEPT(mlock);
- TSAN_INTERCEPT(munlock);
- TSAN_INTERCEPT(mlockall);
- TSAN_INTERCEPT(munlockall);
-
TSAN_INTERCEPT(fork);
- TSAN_INTERCEPT(dlopen);
- TSAN_INTERCEPT(dlclose);
+ TSAN_INTERCEPT(vfork);
+ TSAN_INTERCEPT(dl_iterate_phdr);
TSAN_INTERCEPT(on_exit);
TSAN_INTERCEPT(__cxa_atexit);
TSAN_INTERCEPT(_exit);
+#ifdef NEED_TLS_GET_ADDR
+ TSAN_INTERCEPT(__tls_get_addr);
+#endif
+
+#if !SANITIZER_MAC
// Need to setup it, because interceptors check that the function is resolved.
// But atexit is emitted directly into the module, so can't be resolved.
REAL(atexit) = (int(*)(void(*)()))unreachable;
- atexit_ctx = new(internal_alloc(MBlockAtExit, sizeof(AtExitContext)))
- AtExitContext();
+#endif
if (REAL(__cxa_atexit)(&finalize, 0, 0)) {
Printf("ThreadSanitizer: failed to setup atexit callback\n");
FdInit();
}
-void internal_start_thread(void(*func)(void *arg), void *arg) {
- // Start the thread with signals blocked, otherwise it can steal users
- // signals.
- __sanitizer_kernel_sigset_t set, old;
- internal_sigfillset(&set);
- internal_sigprocmask(SIG_SETMASK, &set, &old);
- void *th;
- REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg);
- REAL(pthread_detach)(th);
- internal_sigprocmask(SIG_SETMASK, &old, 0);
-}
-
} // namespace __tsan
--- /dev/null
+#ifndef TSAN_INTERCEPTORS_H
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+class ScopedInterceptor {
+ public:
+ ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc);
+ ~ScopedInterceptor();
+ private:
+ ThreadState *const thr_;
+ const uptr pc_;
+ bool in_ignored_lib_;
+};
+
+} // namespace __tsan
+
+#define SCOPED_INTERCEPTOR_RAW(func, ...) \
+ ThreadState *thr = cur_thread(); \
+ const uptr caller_pc = GET_CALLER_PC(); \
+ ScopedInterceptor si(thr, #func, caller_pc); \
+ const uptr pc = StackTrace::GetCurrentPc(); \
+ (void)pc; \
+/**/
+
+#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
+ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
+ if (REAL(func) == 0) { \
+ Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
+ Die(); \
+ } \
+ if (thr->ignore_interceptors || thr->in_ignored_lib) \
+ return REAL(func)(__VA_ARGS__); \
+/**/
+
+#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
+
+#if SANITIZER_FREEBSD
+#define __libc_free __free
+#define __libc_malloc __malloc
+#endif
+
+extern "C" void __libc_free(void *ptr);
+extern "C" void *__libc_malloc(uptr size);
+
+#endif // TSAN_INTERCEPTORS_H
MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8);
}
-u16 __tsan_unaligned_read2(const uu16 *addr) {
+void __tsan_read16_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8);
+}
+
+void __tsan_write16_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8);
+}
+
+// __tsan_unaligned_read/write calls are emitted by compiler.
+
+void __tsan_unaligned_read2(const void *addr) {
UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, false, false);
- return *addr;
}
-u32 __tsan_unaligned_read4(const uu32 *addr) {
+void __tsan_unaligned_read4(const void *addr) {
UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, false, false);
- return *addr;
}
-u64 __tsan_unaligned_read8(const uu64 *addr) {
+void __tsan_unaligned_read8(const void *addr) {
UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, false, false);
- return *addr;
}
-void __tsan_unaligned_write2(uu16 *addr, u16 v) {
+void __tsan_unaligned_read16(const void *addr) {
+ UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 16, false, false);
+}
+
+void __tsan_unaligned_write2(void *addr) {
UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, true, false);
- *addr = v;
}
-void __tsan_unaligned_write4(uu32 *addr, u32 v) {
+void __tsan_unaligned_write4(void *addr) {
UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, true, false);
- *addr = v;
}
-void __tsan_unaligned_write8(uu64 *addr, u64 v) {
+void __tsan_unaligned_write8(void *addr) {
UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, true, false);
- *addr = v;
}
+void __tsan_unaligned_write16(void *addr) {
+ UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 16, true, false);
+}
+
+// __sanitizer_unaligned_load/store are for user instrumentation.
+
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
-uint16_t __sanitizer_unaligned_load16(void *addr)
- ALIAS("__tsan_unaligned_read2");
+u16 __sanitizer_unaligned_load16(const uu16 *addr) {
+ __tsan_unaligned_read2(addr);
+ return *addr;
+}
+
SANITIZER_INTERFACE_ATTRIBUTE
-uint32_t __sanitizer_unaligned_load32(void *addr)
- ALIAS("__tsan_unaligned_read4");
+u32 __sanitizer_unaligned_load32(const uu32 *addr) {
+ __tsan_unaligned_read4(addr);
+ return *addr;
+}
+
SANITIZER_INTERFACE_ATTRIBUTE
-uint64_t __sanitizer_unaligned_load64(void *addr)
- ALIAS("__tsan_unaligned_read8");
+u64 __sanitizer_unaligned_load64(const uu64 *addr) {
+ __tsan_unaligned_read8(addr);
+ return *addr;
+}
+
SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_unaligned_store16(void *addr, uint16_t v)
- ALIAS("__tsan_unaligned_write2");
+void __sanitizer_unaligned_store16(uu16 *addr, u16 v) {
+ __tsan_unaligned_write2(addr);
+ *addr = v;
+}
+
SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_unaligned_store32(void *addr, uint32_t v)
- ALIAS("__tsan_unaligned_write4");
+void __sanitizer_unaligned_store32(uu32 *addr, u32 v) {
+ __tsan_unaligned_write4(addr);
+ *addr = v;
+}
+
SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_unaligned_store64(void *addr, uint64_t v)
- ALIAS("__tsan_unaligned_write8");
+void __sanitizer_unaligned_store64(uu64 *addr, u64 v) {
+ __tsan_unaligned_write8(addr);
+ *addr = v;
}
+} // extern "C"
void __tsan_acquire(void *addr) {
Acquire(cur_thread(), CALLERPC, (uptr)addr);
SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8(void *addr);
SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16(void *addr);
-SANITIZER_INTERFACE_ATTRIBUTE u16 __tsan_unaligned_read2(const uu16 *addr);
-SANITIZER_INTERFACE_ATTRIBUTE u32 __tsan_unaligned_read4(const uu32 *addr);
-SANITIZER_INTERFACE_ATTRIBUTE u64 __tsan_unaligned_read8(const uu64 *addr);
-SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(uu16 *addr, u16 v);
-SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(uu32 *addr, u32 v);
-SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(uu64 *addr, u64 v);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read2(const void *addr);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read4(const void *addr);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read8(const void *addr);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read16(const void *addr);
+
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(void *addr);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(void *addr);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(void *addr);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write16(void *addr);
+
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16_pc(void *addr, void *pc);
+
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16_pc(void *addr, void *pc);
SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p);
SANITIZER_INTERFACE_ATTRIBUTE
public:
ScopedAnnotation(ThreadState *thr, const char *aname, const char *f, int l,
uptr pc)
- : thr_(thr)
- , in_rtl_(thr->in_rtl) {
- CHECK_EQ(thr_->in_rtl, 0);
+ : thr_(thr) {
FuncEntry(thr_, pc);
- thr_->in_rtl++;
DPrintf("#%d: annotation %s() %s:%d\n", thr_->tid, aname, f, l);
}
~ScopedAnnotation() {
- thr_->in_rtl--;
- CHECK_EQ(in_rtl_, thr_->in_rtl);
FuncExit(thr_);
+ CheckNoLocks(thr_);
}
private:
ThreadState *const thr_;
- const int in_rtl_;
};
#define SCOPED_ANNOTATION(typ) \
const uptr caller_pc = (uptr)__builtin_return_address(0); \
StatInc(thr, StatAnnotation); \
StatInc(thr, Stat##typ); \
- ScopedAnnotation sa(thr, __FUNCTION__, f, l, caller_pc); \
- const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
+ ScopedAnnotation sa(thr, __func__, f, l, caller_pc); \
+ const uptr pc = StackTrace::GetCurrentPc(); \
(void)pc; \
/**/
struct ExpectRace {
ExpectRace *next;
ExpectRace *prev;
- int hitcount;
- int addcount;
+ atomic_uintptr_t hitcount;
+ atomic_uintptr_t addcount;
uptr addr;
uptr size;
char *file;
ExpectRace *race = list->next;
for (; race != list; race = race->next) {
if (race->addr == addr && race->size == size) {
- race->addcount++;
+ atomic_store_relaxed(&race->addcount,
+ atomic_load_relaxed(&race->addcount) + 1);
return;
}
}
race->file = f;
race->line = l;
race->desc[0] = 0;
- race->hitcount = 0;
- race->addcount = 1;
+ atomic_store_relaxed(&race->hitcount, 0);
+ atomic_store_relaxed(&race->addcount, 1);
if (desc) {
int i = 0;
for (; i < kMaxDescLen - 1 && desc[i]; i++)
static bool CheckContains(ExpectRace *list, uptr addr, uptr size) {
ExpectRace *race = FindRace(list, addr, size);
- if (race == 0 && AlternativeAddress(addr))
- race = FindRace(list, AlternativeAddress(addr), size);
if (race == 0)
return false;
DPrintf("Hit expected/benign race: %s addr=%zx:%d %s:%d\n",
race->desc, race->addr, (int)race->size, race->file, race->line);
- race->hitcount++;
+ atomic_fetch_add(&race->hitcount, 1, memory_order_relaxed);
return true;
}
}
bool IsExpectedReport(uptr addr, uptr size) {
- Lock lock(&dyn_ann_ctx->mtx);
+ ReadLock lock(&dyn_ann_ctx->mtx);
if (CheckContains(&dyn_ann_ctx->expect, addr, size))
return true;
if (CheckContains(&dyn_ann_ctx->benign, addr, size))
}
static void CollectMatchedBenignRaces(Vector<ExpectRace> *matched,
- int *unique_count, int *hit_count, int ExpectRace::*counter) {
+ int *unique_count, int *hit_count, atomic_uintptr_t ExpectRace::*counter) {
ExpectRace *list = &dyn_ann_ctx->benign;
for (ExpectRace *race = list->next; race != list; race = race->next) {
(*unique_count)++;
- if (race->*counter == 0)
+ const uptr cnt = atomic_load_relaxed(&(race->*counter));
+ if (cnt == 0)
continue;
- (*hit_count) += race->*counter;
+ *hit_count += cnt;
uptr i = 0;
for (; i < matched->Size(); i++) {
ExpectRace *race0 = &(*matched)[i];
if (race->line == race0->line
&& internal_strcmp(race->file, race0->file) == 0
&& internal_strcmp(race->desc, race0->desc) == 0) {
- race0->*counter += race->*counter;
+ atomic_fetch_add(&(race0->*counter), cnt, memory_order_relaxed);
break;
}
}
hit_count, (int)internal_getpid());
for (uptr i = 0; i < hit_matched.Size(); i++) {
Printf("%d %s:%d %s\n",
- hit_matched[i].hitcount, hit_matched[i].file,
- hit_matched[i].line, hit_matched[i].desc);
+ atomic_load_relaxed(&hit_matched[i].hitcount),
+ hit_matched[i].file, hit_matched[i].line, hit_matched[i].desc);
}
}
if (hit_matched.Size()) {
add_count, unique_count, (int)internal_getpid());
for (uptr i = 0; i < add_matched.Size(); i++) {
Printf("%d %s:%d %s\n",
- add_matched[i].addcount, add_matched[i].file,
- add_matched[i].line, add_matched[i].desc);
+ atomic_load_relaxed(&add_matched[i].addcount),
+ add_matched[i].file, add_matched[i].line, add_matched[i].desc);
}
}
}
Lock lock(&dyn_ann_ctx->mtx);
while (dyn_ann_ctx->expect.next != &dyn_ann_ctx->expect) {
ExpectRace *race = dyn_ann_ctx->expect.next;
- if (race->hitcount == 0) {
- CTX()->nmissed_expected++;
+ if (atomic_load_relaxed(&race->hitcount) == 0) {
+ ctx->nmissed_expected++;
ReportMissedExpectedRace(race);
}
race->prev->next = race->next;
void INTERFACE_ATTRIBUTE
AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {}
+void INTERFACE_ATTRIBUTE
+AnnotateMemoryIsUninitialized(char *f, int l, uptr mem, uptr sz) {}
} // extern "C"
//===----------------------------------------------------------------------===//
// ThreadSanitizer atomic operations are based on C++11/C1x standards.
-// For background see C++11 standard. A slightly older, publically
+// For background see C++11 standard. A slightly older, publicly
// available draft of the standard (not entirely up-to-date, but close enough
// for casual browsing) is available here:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "tsan_interface_atomic.h"
+#include "sanitizer_common/sanitizer_mutex.h"
#include "tsan_flags.h"
#include "tsan_rtl.h"
using namespace __tsan; // NOLINT
-#define SCOPED_ATOMIC(func, ...) \
- const uptr callpc = (uptr)__builtin_return_address(0); \
- uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
- mo = ConvertOrder(mo); \
- mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \
- ThreadState *const thr = cur_thread(); \
- AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \
- ScopedAtomic sa(thr, callpc, a, mo, __FUNCTION__); \
- return Atomic##func(thr, pc, __VA_ARGS__); \
-/**/
-
-// Some shortcuts.
-typedef __tsan_memory_order morder;
-typedef __tsan_atomic8 a8;
-typedef __tsan_atomic16 a16;
-typedef __tsan_atomic32 a32;
-typedef __tsan_atomic64 a64;
-typedef __tsan_atomic128 a128;
-const morder mo_relaxed = __tsan_memory_order_relaxed;
-const morder mo_consume = __tsan_memory_order_consume;
-const morder mo_acquire = __tsan_memory_order_acquire;
-const morder mo_release = __tsan_memory_order_release;
-const morder mo_acq_rel = __tsan_memory_order_acq_rel;
-const morder mo_seq_cst = __tsan_memory_order_seq_cst;
+// These should match declarations from public tsan_interface_atomic.h header.
+typedef unsigned char a8;
+typedef unsigned short a16; // NOLINT
+typedef unsigned int a32;
+typedef unsigned long long a64; // NOLINT
+#if !defined(SANITIZER_GO) && (defined(__SIZEOF_INT128__) \
+ || (__clang_major__ * 100 + __clang_minor__ >= 302)) && !defined(__mips64)
+__extension__ typedef __int128 a128;
+# define __TSAN_HAS_INT128 1
+#else
+# define __TSAN_HAS_INT128 0
+#endif
-class ScopedAtomic {
- public:
- ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a,
- morder mo, const char *func)
- : thr_(thr) {
- CHECK_EQ(thr_->in_rtl, 0);
- ProcessPendingSignals(thr);
- FuncEntry(thr_, pc);
- DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo);
- thr_->in_rtl++;
- }
- ~ScopedAtomic() {
- thr_->in_rtl--;
- CHECK_EQ(thr_->in_rtl, 0);
- FuncExit(thr_);
- }
- private:
- ThreadState *thr_;
-};
+#if !defined(SANITIZER_GO) && __TSAN_HAS_INT128
+// Protects emulation of 128-bit atomic operations.
+static StaticSpinMutex mutex128;
+#endif
-static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
- StatInc(thr, StatAtomic);
- StatInc(thr, t);
- StatInc(thr, size == 1 ? StatAtomic1
- : size == 2 ? StatAtomic2
- : size == 4 ? StatAtomic4
- : size == 8 ? StatAtomic8
- : StatAtomic16);
- StatInc(thr, mo == mo_relaxed ? StatAtomicRelaxed
- : mo == mo_consume ? StatAtomicConsume
- : mo == mo_acquire ? StatAtomicAcquire
- : mo == mo_release ? StatAtomicRelease
- : mo == mo_acq_rel ? StatAtomicAcq_Rel
- : StatAtomicSeq_Cst);
-}
+// Part of ABI, do not change.
+// http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/atomic?view=markup
+typedef enum {
+ mo_relaxed,
+ mo_consume,
+ mo_acquire,
+ mo_release,
+ mo_acq_rel,
+ mo_seq_cst
+} morder;
static bool IsLoadOrder(morder mo) {
return mo == mo_relaxed || mo == mo_consume
return mo == mo_acq_rel || mo == mo_seq_cst;
}
-static morder ConvertOrder(morder mo) {
- if (mo > (morder)100500) {
- mo = morder(mo - 100500);
- if (mo == morder(1 << 0))
- mo = mo_relaxed;
- else if (mo == morder(1 << 1))
- mo = mo_consume;
- else if (mo == morder(1 << 2))
- mo = mo_acquire;
- else if (mo == morder(1 << 3))
- mo = mo_release;
- else if (mo == morder(1 << 4))
- mo = mo_acq_rel;
- else if (mo == morder(1 << 5))
- mo = mo_seq_cst;
- }
- CHECK_GE(mo, mo_relaxed);
- CHECK_LE(mo, mo_seq_cst);
- return mo;
-}
-
template<typename T> T func_xchg(volatile T *v, T op) {
T res = __sync_lock_test_and_set(v, op);
// __sync_lock_test_and_set does not contain full barrier.
// Atomic ops are executed under tsan internal mutex,
// here we assume that the atomic variables are not accessed
// from non-instrumented code.
-#ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
+#if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) && !defined(SANITIZER_GO) \
+ && __TSAN_HAS_INT128
a128 func_xchg(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
a128 cmp = *v;
*v = op;
return cmp;
}
a128 func_add(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
a128 cmp = *v;
*v = cmp + op;
return cmp;
}
a128 func_sub(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
a128 cmp = *v;
*v = cmp - op;
return cmp;
}
a128 func_and(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
a128 cmp = *v;
*v = cmp & op;
return cmp;
}
a128 func_or(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
a128 cmp = *v;
*v = cmp | op;
return cmp;
}
a128 func_xor(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
a128 cmp = *v;
*v = cmp ^ op;
return cmp;
}
a128 func_nand(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
a128 cmp = *v;
*v = ~(cmp & op);
return cmp;
}
a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) {
+ SpinMutexLock lock(&mutex128);
a128 cur = *v;
if (cur == cmp)
*v = xch;
// this leads to false negatives only in very obscure cases.
}
+#ifndef SANITIZER_GO
+static atomic_uint8_t *to_atomic(const volatile a8 *a) {
+ return reinterpret_cast<atomic_uint8_t *>(const_cast<a8 *>(a));
+}
+
+static atomic_uint16_t *to_atomic(const volatile a16 *a) {
+ return reinterpret_cast<atomic_uint16_t *>(const_cast<a16 *>(a));
+}
+#endif
+
+static atomic_uint32_t *to_atomic(const volatile a32 *a) {
+ return reinterpret_cast<atomic_uint32_t *>(const_cast<a32 *>(a));
+}
+
+static atomic_uint64_t *to_atomic(const volatile a64 *a) {
+ return reinterpret_cast<atomic_uint64_t *>(const_cast<a64 *>(a));
+}
+
+static memory_order to_mo(morder mo) {
+ switch (mo) {
+ case mo_relaxed: return memory_order_relaxed;
+ case mo_consume: return memory_order_consume;
+ case mo_acquire: return memory_order_acquire;
+ case mo_release: return memory_order_release;
+ case mo_acq_rel: return memory_order_acq_rel;
+ case mo_seq_cst: return memory_order_seq_cst;
+ }
+ CHECK(0);
+ return memory_order_seq_cst;
+}
+
+template<typename T>
+static T NoTsanAtomicLoad(const volatile T *a, morder mo) {
+ return atomic_load(to_atomic(a), to_mo(mo));
+}
+
+#if __TSAN_HAS_INT128 && !defined(SANITIZER_GO)
+static a128 NoTsanAtomicLoad(const volatile a128 *a, morder mo) {
+ SpinMutexLock lock(&mutex128);
+ return *a;
+}
+#endif
+
template<typename T>
static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a,
morder mo) {
CHECK(IsLoadOrder(mo));
// This fast-path is critical for performance.
// Assume the access is atomic.
- if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) {
+ if (!IsAcquireOrder(mo)) {
MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>());
- return *a; // as if atomic
+ return NoTsanAtomicLoad(a, mo);
}
- SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, false);
AcquireImpl(thr, pc, &s->clock);
- T v = *a;
+ T v = NoTsanAtomicLoad(a, mo);
s->mtx.ReadUnlock();
- __sync_synchronize();
MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>());
return v;
}
template<typename T>
+static void NoTsanAtomicStore(volatile T *a, T v, morder mo) {
+ atomic_store(to_atomic(a), v, to_mo(mo));
+}
+
+#if __TSAN_HAS_INT128 && !defined(SANITIZER_GO)
+static void NoTsanAtomicStore(volatile a128 *a, a128 v, morder mo) {
+ SpinMutexLock lock(&mutex128);
+ *a = v;
+}
+#endif
+
+template<typename T>
static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
morder mo) {
CHECK(IsStoreOrder(mo));
// Assume the access is atomic.
// Strictly saying even relaxed store cuts off release sequence,
// so must reset the clock.
- if (!IsReleaseOrder(mo) && sizeof(T) <= sizeof(a)) {
- *a = v; // as if atomic
+ if (!IsReleaseOrder(mo)) {
+ NoTsanAtomicStore(a, v, mo);
return;
}
__sync_synchronize();
- SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, true);
thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
ReleaseImpl(thr, pc, &s->clock);
- *a = v;
+ NoTsanAtomicStore(a, v, mo);
s->mtx.Unlock();
- // Trainling memory barrier to provide sequential consistency
- // for Dekker-like store-load synchronization.
- __sync_synchronize();
}
template<typename T, T (*F)(volatile T *v, T op)>
MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>());
SyncVar *s = 0;
if (mo != mo_relaxed) {
- s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
+ s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, true);
thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
}
template<typename T>
+static T NoTsanAtomicExchange(volatile T *a, T v, morder mo) {
+ return func_xchg(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchAdd(volatile T *a, T v, morder mo) {
+ return func_add(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchSub(volatile T *a, T v, morder mo) {
+ return func_sub(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchAnd(volatile T *a, T v, morder mo) {
+ return func_and(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchOr(volatile T *a, T v, morder mo) {
+ return func_or(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchXor(volatile T *a, T v, morder mo) {
+ return func_xor(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchNand(volatile T *a, T v, morder mo) {
+ return func_nand(a, v);
+}
+
+template<typename T>
static T AtomicExchange(ThreadState *thr, uptr pc, volatile T *a, T v,
morder mo) {
return AtomicRMW<T, func_xchg>(thr, pc, a, v, mo);
}
template<typename T>
+static bool NoTsanAtomicCAS(volatile T *a, T *c, T v, morder mo, morder fmo) {
+ return atomic_compare_exchange_strong(to_atomic(a), c, v, to_mo(mo));
+}
+
+#if __TSAN_HAS_INT128
+static bool NoTsanAtomicCAS(volatile a128 *a, a128 *c, a128 v,
+ morder mo, morder fmo) {
+ a128 old = *c;
+ a128 cur = func_cas(a, old, v);
+ if (cur == old)
+ return true;
+ *c = cur;
+ return false;
+}
+#endif
+
+template<typename T>
+static T NoTsanAtomicCAS(volatile T *a, T c, T v, morder mo, morder fmo) {
+ NoTsanAtomicCAS(a, &c, v, mo, fmo);
+ return c;
+}
+
+template<typename T>
static bool AtomicCAS(ThreadState *thr, uptr pc,
volatile T *a, T *c, T v, morder mo, morder fmo) {
(void)fmo; // Unused because llvm does not pass it yet.
MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>());
SyncVar *s = 0;
+ bool write_lock = mo != mo_acquire && mo != mo_consume;
if (mo != mo_relaxed) {
- s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
+ s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, write_lock);
thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
}
T cc = *c;
T pr = func_cas(a, cc, v);
- if (s)
- s->mtx.Unlock();
+ if (s) {
+ if (write_lock)
+ s->mtx.Unlock();
+ else
+ s->mtx.ReadUnlock();
+ }
if (pr == cc)
return true;
*c = pr;
return c;
}
+#ifndef SANITIZER_GO
+static void NoTsanAtomicFence(morder mo) {
+ __sync_synchronize();
+}
+
static void AtomicFence(ThreadState *thr, uptr pc, morder mo) {
// FIXME(dvyukov): not implemented.
__sync_synchronize();
}
+#endif
+
+// Interface functions follow.
+#ifndef SANITIZER_GO
+
+// C/C++
+
+#define SCOPED_ATOMIC(func, ...) \
+ const uptr callpc = (uptr)__builtin_return_address(0); \
+ uptr pc = StackTrace::GetCurrentPc(); \
+ mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \
+ ThreadState *const thr = cur_thread(); \
+ if (thr->ignore_interceptors) \
+ return NoTsanAtomic##func(__VA_ARGS__); \
+ AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \
+ ScopedAtomic sa(thr, callpc, a, mo, __func__); \
+ return Atomic##func(thr, pc, __VA_ARGS__); \
+/**/
+
+class ScopedAtomic {
+ public:
+ ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a,
+ morder mo, const char *func)
+ : thr_(thr) {
+ FuncEntry(thr_, pc);
+ DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo);
+ }
+ ~ScopedAtomic() {
+ ProcessPendingSignals(thr_);
+ FuncExit(thr_);
+ }
+ private:
+ ThreadState *thr_;
+};
+
+static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
+ StatInc(thr, StatAtomic);
+ StatInc(thr, t);
+ StatInc(thr, size == 1 ? StatAtomic1
+ : size == 2 ? StatAtomic2
+ : size == 4 ? StatAtomic4
+ : size == 8 ? StatAtomic8
+ : StatAtomic16);
+ StatInc(thr, mo == mo_relaxed ? StatAtomicRelaxed
+ : mo == mo_consume ? StatAtomicConsume
+ : mo == mo_acquire ? StatAtomicAcquire
+ : mo == mo_release ? StatAtomicRelease
+ : mo == mo_acq_rel ? StatAtomicAcq_Rel
+ : StatAtomicSeq_Cst);
+}
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
a8 __tsan_atomic8_load(const volatile a8 *a, morder mo) {
SCOPED_ATOMIC(Load, a, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a16 __tsan_atomic16_load(const volatile a16 *a, morder mo) {
SCOPED_ATOMIC(Load, a, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a32 __tsan_atomic32_load(const volatile a32 *a, morder mo) {
SCOPED_ATOMIC(Load, a, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a64 __tsan_atomic64_load(const volatile a64 *a, morder mo) {
SCOPED_ATOMIC(Load, a, mo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
a128 __tsan_atomic128_load(const volatile a128 *a, morder mo) {
SCOPED_ATOMIC(Load, a, mo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_atomic8_store(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(Store, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_atomic16_store(volatile a16 *a, a16 v, morder mo) {
SCOPED_ATOMIC(Store, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_atomic32_store(volatile a32 *a, a32 v, morder mo) {
SCOPED_ATOMIC(Store, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_atomic64_store(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(Store, a, v, mo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_atomic128_store(volatile a128 *a, a128 v, morder mo) {
SCOPED_ATOMIC(Store, a, v, mo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(Exchange, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, morder mo) {
SCOPED_ATOMIC(Exchange, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, morder mo) {
SCOPED_ATOMIC(Exchange, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(Exchange, a, v, mo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
a128 __tsan_atomic128_exchange(volatile a128 *a, a128 v, morder mo) {
SCOPED_ATOMIC(Exchange, a, v, mo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(FetchAdd, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, morder mo) {
SCOPED_ATOMIC(FetchAdd, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, morder mo) {
SCOPED_ATOMIC(FetchAdd, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(FetchAdd, a, v, mo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
a128 __tsan_atomic128_fetch_add(volatile a128 *a, a128 v, morder mo) {
SCOPED_ATOMIC(FetchAdd, a, v, mo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
a8 __tsan_atomic8_fetch_sub(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(FetchSub, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, morder mo) {
SCOPED_ATOMIC(FetchSub, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, morder mo) {
SCOPED_ATOMIC(FetchSub, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(FetchSub, a, v, mo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
a128 __tsan_atomic128_fetch_sub(volatile a128 *a, a128 v, morder mo) {
SCOPED_ATOMIC(FetchSub, a, v, mo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(FetchAnd, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, morder mo) {
SCOPED_ATOMIC(FetchAnd, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, morder mo) {
SCOPED_ATOMIC(FetchAnd, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(FetchAnd, a, v, mo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
a128 __tsan_atomic128_fetch_and(volatile a128 *a, a128 v, morder mo) {
SCOPED_ATOMIC(FetchAnd, a, v, mo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(FetchOr, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, morder mo) {
SCOPED_ATOMIC(FetchOr, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, morder mo) {
SCOPED_ATOMIC(FetchOr, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(FetchOr, a, v, mo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
a128 __tsan_atomic128_fetch_or(volatile a128 *a, a128 v, morder mo) {
SCOPED_ATOMIC(FetchOr, a, v, mo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(FetchXor, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, morder mo) {
SCOPED_ATOMIC(FetchXor, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, morder mo) {
SCOPED_ATOMIC(FetchXor, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(FetchXor, a, v, mo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
a128 __tsan_atomic128_fetch_xor(volatile a128 *a, a128 v, morder mo) {
SCOPED_ATOMIC(FetchXor, a, v, mo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
a8 __tsan_atomic8_fetch_nand(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(FetchNand, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a16 __tsan_atomic16_fetch_nand(volatile a16 *a, a16 v, morder mo) {
SCOPED_ATOMIC(FetchNand, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a32 __tsan_atomic32_fetch_nand(volatile a32 *a, a32 v, morder mo) {
SCOPED_ATOMIC(FetchNand, a, v, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a64 __tsan_atomic64_fetch_nand(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(FetchNand, a, v, mo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
a128 __tsan_atomic128_fetch_nand(volatile a128 *a, a128 v, morder mo) {
SCOPED_ATOMIC(FetchNand, a, v, mo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic16_compare_exchange_strong(volatile a16 *a, a16 *c, a16 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic32_compare_exchange_strong(volatile a32 *a, a32 *c, a32 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic64_compare_exchange_strong(volatile a64 *a, a64 *c, a64 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic128_compare_exchange_strong(volatile a128 *a, a128 *c, a128 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic16_compare_exchange_weak(volatile a16 *a, a16 *c, a16 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic32_compare_exchange_weak(volatile a32 *a, a32 *c, a32 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic64_compare_exchange_weak(volatile a64 *a, a64 *c, a64 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
int __tsan_atomic128_compare_exchange_weak(volatile a128 *a, a128 *c, a128 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
a8 __tsan_atomic8_compare_exchange_val(volatile a8 *a, a8 c, a8 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+
+SANITIZER_INTERFACE_ATTRIBUTE
a16 __tsan_atomic16_compare_exchange_val(volatile a16 *a, a16 c, a16 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a32 __tsan_atomic32_compare_exchange_val(volatile a32 *a, a32 c, a32 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
a128 __tsan_atomic128_compare_exchange_val(volatile a128 *a, a128 c, a128 v,
morder mo, morder fmo) {
SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
#endif
+SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_atomic_thread_fence(morder mo) {
char* a = 0;
SCOPED_ATOMIC(Fence, mo);
}
+SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_atomic_signal_fence(morder mo) {
}
+} // extern "C"
+
+#else // #ifndef SANITIZER_GO
+
+// Go
+
+#define ATOMIC(func, ...) \
+ if (thr->ignore_sync) { \
+ NoTsanAtomic##func(__VA_ARGS__); \
+ } else { \
+ FuncEntry(thr, cpc); \
+ Atomic##func(thr, pc, __VA_ARGS__); \
+ FuncExit(thr); \
+ } \
+/**/
+
+#define ATOMIC_RET(func, ret, ...) \
+ if (thr->ignore_sync) { \
+ (ret) = NoTsanAtomic##func(__VA_ARGS__); \
+ } else { \
+ FuncEntry(thr, cpc); \
+ (ret) = Atomic##func(thr, pc, __VA_ARGS__); \
+ FuncExit(thr); \
+ } \
+/**/
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Load, *(a32*)(a+8), *(a32**)a, mo_acquire);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Load, *(a64*)(a+8), *(a64**)a, mo_acquire);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC(Store, *(a32**)a, *(a32*)(a+8), mo_release);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC(Store, *(a64**)a, *(a64*)(a+8), mo_release);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(FetchAdd, *(a32*)(a+16), *(a32**)a, *(a32*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(FetchAdd, *(a64*)(a+16), *(a64**)a, *(a64*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Exchange, *(a32*)(a+16), *(a32**)a, *(a32*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Exchange, *(a64*)(a+16), *(a64**)a, *(a64*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_compare_exchange(
+ ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ a32 cur = 0;
+ a32 cmp = *(a32*)(a+8);
+ ATOMIC_RET(CAS, cur, *(a32**)a, cmp, *(a32*)(a+12), mo_acq_rel, mo_acquire);
+ *(bool*)(a+16) = (cur == cmp);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_compare_exchange(
+ ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ a64 cur = 0;
+ a64 cmp = *(a64*)(a+8);
+ ATOMIC_RET(CAS, cur, *(a64**)a, cmp, *(a64*)(a+16), mo_acq_rel, mo_acquire);
+ *(bool*)(a+24) = (cur == cmp);
+}
+} // extern "C"
+#endif // #ifndef SANITIZER_GO
MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8);
}
+void __tsan_read1_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog1);
+}
+
+void __tsan_read2_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog2);
+}
+
+void __tsan_read4_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog4);
+}
+
+void __tsan_read8_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
+}
+
+void __tsan_write1_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog1);
+}
+
+void __tsan_write2_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog2);
+}
+
+void __tsan_write4_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog4);
+}
+
+void __tsan_write8_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
+}
+
void __tsan_vptr_update(void **vptr_p, void *new_val) {
CHECK_EQ(sizeof(vptr_p), 8);
if (*vptr_p != new_val) {
using namespace __tsan; // NOLINT
-namespace __tsan {
-
-const uptr kHeapShadow = 0x300000000000ull;
-const uptr kHeapAlignment = 8;
-
-struct BlockDesc {
- bool begin;
- Mutex mtx;
- SyncVar *head;
+const jptr kHeapAlignment = 8;
- BlockDesc()
- : mtx(MutexTypeJavaMBlock, StatMtxJavaMBlock)
- , head() {
- CHECK_EQ(begin, false);
- begin = true;
- }
-
- ~BlockDesc() {
- CHECK_EQ(begin, true);
- begin = false;
- ThreadState *thr = cur_thread();
- SyncVar *s = head;
- while (s) {
- SyncVar *s1 = s->next;
- StatInc(thr, StatSyncDestroyed);
- s->mtx.Lock();
- s->mtx.Unlock();
- thr->mset.Remove(s->GetId());
- DestroyAndFree(s);
- s = s1;
- }
- }
-};
+namespace __tsan {
struct JavaContext {
const uptr heap_begin;
const uptr heap_size;
- BlockDesc *heap_shadow;
JavaContext(jptr heap_begin, jptr heap_size)
: heap_begin(heap_begin)
, heap_size(heap_size) {
- uptr size = heap_size / kHeapAlignment * sizeof(BlockDesc);
- heap_shadow = (BlockDesc*)MmapFixedNoReserve(kHeapShadow, size);
- if ((uptr)heap_shadow != kHeapShadow) {
- Printf("ThreadSanitizer: failed to mmap Java heap shadow\n");
- Die();
- }
}
};
: thr_(thr) {
Initialize(thr_);
FuncEntry(thr, pc);
- CHECK_EQ(thr_->in_rtl, 0);
- thr_->in_rtl++;
}
~ScopedJavaFunc() {
- thr_->in_rtl--;
- CHECK_EQ(thr_->in_rtl, 0);
FuncExit(thr_);
// FIXME(dvyukov): process pending signals.
}
static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1];
static JavaContext *jctx;
-static BlockDesc *getblock(uptr addr) {
- uptr i = (addr - jctx->heap_begin) / kHeapAlignment;
- return &jctx->heap_shadow[i];
-}
-
-static uptr USED getmem(BlockDesc *b) {
- uptr i = b - jctx->heap_shadow;
- uptr p = jctx->heap_begin + i * kHeapAlignment;
- CHECK_GE(p, jctx->heap_begin);
- CHECK_LT(p, jctx->heap_begin + jctx->heap_size);
- return p;
-}
-
-static BlockDesc *getblockbegin(uptr addr) {
- for (BlockDesc *b = getblock(addr);; b--) {
- CHECK_GE(b, jctx->heap_shadow);
- if (b->begin)
- return b;
- }
- return 0;
-}
-
-SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr,
- bool write_lock, bool create) {
- if (jctx == 0 || addr < jctx->heap_begin
- || addr >= jctx->heap_begin + jctx->heap_size)
- return 0;
- BlockDesc *b = getblockbegin(addr);
- DPrintf("#%d: GetJavaSync %p->%p\n", thr->tid, addr, b);
- Lock l(&b->mtx);
- SyncVar *s = b->head;
- for (; s; s = s->next) {
- if (s->addr == addr) {
- DPrintf("#%d: found existing sync for %p\n", thr->tid, addr);
- break;
- }
- }
- if (s == 0 && create) {
- DPrintf("#%d: creating new sync for %p\n", thr->tid, addr);
- s = CTX()->synctab.Create(thr, pc, addr);
- s->next = b->head;
- b->head = s;
- }
- if (s) {
- if (write_lock)
- s->mtx.Lock();
- else
- s->mtx.ReadLock();
- }
- return s;
-}
-
-SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) {
- // We do not destroy Java mutexes other than in __tsan_java_free().
- return 0;
-}
-
} // namespace __tsan
#define SCOPED_JAVA_FUNC(func) \
ThreadState *thr = cur_thread(); \
const uptr caller_pc = GET_CALLER_PC(); \
- const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
+ const uptr pc = StackTrace::GetCurrentPc(); \
(void)pc; \
ScopedJavaFunc scoped(thr, caller_pc); \
/**/
CHECK_GE(ptr, jctx->heap_begin);
CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
- BlockDesc *b = getblock(ptr);
- new(b) BlockDesc();
+ OnUserAlloc(thr, pc, ptr, size, false);
}
void __tsan_java_free(jptr ptr, jptr size) {
CHECK_GE(ptr, jctx->heap_begin);
CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
- BlockDesc *beg = getblock(ptr);
- BlockDesc *end = getblock(ptr + size);
- for (BlockDesc *b = beg; b != end; b++) {
- if (b->begin)
- b->~BlockDesc();
- }
+ ctx->metamap.FreeRange(thr, pc, ptr, size);
}
void __tsan_java_move(jptr src, jptr dst, jptr size) {
CHECK_LE(src + size, jctx->heap_begin + jctx->heap_size);
CHECK_GE(dst, jctx->heap_begin);
CHECK_LE(dst + size, jctx->heap_begin + jctx->heap_size);
- CHECK(dst >= src + size || src >= dst + size);
+ CHECK_NE(dst, src);
+ CHECK_NE(size, 0);
// Assuming it's not running concurrently with threads that do
// memory accesses and mutex operations (stop-the-world phase).
- { // NOLINT
- BlockDesc *s = getblock(src);
- BlockDesc *d = getblock(dst);
- BlockDesc *send = getblock(src + size);
- for (; s != send; s++, d++) {
- CHECK_EQ(d->begin, false);
- if (s->begin) {
- DPrintf("#%d: moving block %p->%p\n", thr->tid, getmem(s), getmem(d));
- new(d) BlockDesc;
- d->head = s->head;
- for (SyncVar *sync = d->head; sync; sync = sync->next) {
- uptr newaddr = sync->addr - src + dst;
- DPrintf("#%d: moving sync %p->%p\n", thr->tid, sync->addr, newaddr);
- sync->addr = newaddr;
- }
- s->head = 0;
- s->~BlockDesc();
- }
- }
+ ctx->metamap.MoveMemory(src, dst, size);
+
+ // Move shadow.
+ u64 *s = (u64*)MemToShadow(src);
+ u64 *d = (u64*)MemToShadow(dst);
+ u64 *send = (u64*)MemToShadow(src + size);
+ uptr inc = 1;
+ if (dst > src) {
+ s = (u64*)MemToShadow(src + size) - 1;
+ d = (u64*)MemToShadow(dst + size) - 1;
+ send = (u64*)MemToShadow(src) - 1;
+ inc = -1;
}
-
- { // NOLINT
- u64 *s = (u64*)MemToShadow(src);
- u64 *d = (u64*)MemToShadow(dst);
- u64 *send = (u64*)MemToShadow(src + size);
- for (; s != send; s++, d++) {
- *d = *s;
- *s = 0;
- }
+ for (; s != send; s += inc, d += inc) {
+ *d = *s;
+ *s = 0;
}
}
+void __tsan_java_finalize() {
+ SCOPED_JAVA_FUNC(__tsan_java_finalize);
+ DPrintf("#%d: java_mutex_finalize()\n", thr->tid);
+ AcquireGlobal(thr, 0);
+}
+
void __tsan_java_mutex_lock(jptr addr) {
SCOPED_JAVA_FUNC(__tsan_java_mutex_lock);
DPrintf("#%d: java_mutex_lock(%p)\n", thr->tid, addr);
return MutexUnlock(thr, pc, addr, true);
}
+
+void __tsan_java_acquire(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_acquire);
+ DPrintf("#%d: java_acquire(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ Acquire(thr, caller_pc, addr);
+}
+
+void __tsan_java_release(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_release);
+ DPrintf("#%d: java_release(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ Release(thr, caller_pc, addr);
+}
+
+void __tsan_java_release_store(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_release);
+ DPrintf("#%d: java_release_store(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ ReleaseStore(thr, caller_pc, addr);
+}
void __tsan_java_free(jptr ptr, jptr size) INTERFACE_ATTRIBUTE;
// Callback for memory move by GC.
// Can be aggregated for several objects (preferably).
-// The ranges must not overlap.
+// The ranges can overlap.
void __tsan_java_move(jptr src, jptr dst, jptr size) INTERFACE_ATTRIBUTE;
+// This function must be called on the finalizer thread
+// before executing a batch of finalizers.
+// It ensures necessary synchronization between
+// java object creation and finalization.
+void __tsan_java_finalize() INTERFACE_ATTRIBUTE;
// Mutex lock.
// Addr is any unique address associated with the mutex.
// the same recursion level.
int __tsan_java_mutex_unlock_rec(jptr addr) INTERFACE_ATTRIBUTE;
+// Raw acquire/release primitives.
+// Can be used to establish happens-before edges on volatile/final fields,
+// in atomic operations, etc. release_store is the same as release, but it
+// breaks release sequence on addr (see C++ standard 1.10/7 for details).
+void __tsan_java_acquire(jptr addr) INTERFACE_ATTRIBUTE;
+void __tsan_java_release(jptr addr) INTERFACE_ATTRIBUTE;
+void __tsan_java_release_store(jptr addr) INTERFACE_ATTRIBUTE;
+
#ifdef __cplusplus
} // extern "C"
#endif
--- /dev/null
+//===-- tsan_libdispatch_mac.cc -------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific libdispatch (GCD) support.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "interception/interception.h"
+#include "tsan_interceptors.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+#include <dispatch/dispatch.h>
+#include <pthread.h>
+
+namespace __tsan {
+
+// GCD's dispatch_once implementation has a fast path that contains a racy read
+// and it's inlined into user's code. Furthermore, this fast path doesn't
+// establish a proper happens-before relations between the initialization and
+// code following the call to dispatch_once. We could deal with this in
+// instrumented code, but there's not much we can do about it in system
+// libraries. Let's disable the fast path (by never storing the value ~0 to
+// predicate), so the interceptor is always called, and let's add proper release
+// and acquire semantics. Since TSan does not see its own atomic stores, the
+// race on predicate won't be reported - the only accesses to it that TSan sees
+// are the loads on the fast path. Loads don't race. Secondly, dispatch_once is
+// both a macro and a real function, we want to intercept the function, so we
+// need to undefine the macro.
+#undef dispatch_once
+TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate,
+ dispatch_block_t block) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_once, predicate, block);
+ atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate);
+ u32 v = atomic_load(a, memory_order_acquire);
+ if (v == 0 &&
+ atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) {
+ block();
+ Release(thr, pc, (uptr)a);
+ atomic_store(a, 2, memory_order_release);
+ } else {
+ while (v != 2) {
+ internal_sched_yield();
+ v = atomic_load(a, memory_order_acquire);
+ }
+ Acquire(thr, pc, (uptr)a);
+ }
+}
+
+#undef dispatch_once_f
+TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate,
+ void *context, dispatch_function_t function) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_once_f, predicate, context, function);
+ WRAP(dispatch_once)(predicate, ^(void) {
+ function(context);
+ });
+}
+
+} // namespace __tsan
+
+#endif // SANITIZER_MAC
--- /dev/null
+//===-- tsan_malloc_mac.cc ------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific malloc interception.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "tsan_interceptors.h"
+#include "tsan_stack_trace.h"
+
+using namespace __tsan;
+#define COMMON_MALLOC_ZONE_NAME "tsan"
+#define COMMON_MALLOC_ENTER()
+#define COMMON_MALLOC_SANITIZER_INITIALIZED (cur_thread()->is_inited)
+#define COMMON_MALLOC_FORCE_LOCK()
+#define COMMON_MALLOC_FORCE_UNLOCK()
+#define COMMON_MALLOC_MEMALIGN(alignment, size) \
+ void *p = \
+ user_alloc(cur_thread(), StackTrace::GetCurrentPc(), size, alignment)
+#define COMMON_MALLOC_MALLOC(size) \
+ if (cur_thread()->in_symbolizer) \
+ return REAL(malloc)(size); \
+ SCOPED_INTERCEPTOR_RAW(malloc, size); \
+ void *p = user_alloc(thr, pc, size)
+#define COMMON_MALLOC_REALLOC(ptr, size) \
+ if (cur_thread()->in_symbolizer) \
+ return REAL(realloc)(ptr, size); \
+ SCOPED_INTERCEPTOR_RAW(realloc, ptr, size); \
+ void *p = user_realloc(thr, pc, ptr, size)
+#define COMMON_MALLOC_CALLOC(count, size) \
+ if (cur_thread()->in_symbolizer) \
+ return REAL(calloc)(count, size); \
+ SCOPED_INTERCEPTOR_RAW(calloc, size, count); \
+ void *p = user_calloc(thr, pc, size, count)
+#define COMMON_MALLOC_VALLOC(size) \
+ if (cur_thread()->in_symbolizer) \
+ return REAL(valloc)(size); \
+ SCOPED_INTERCEPTOR_RAW(valloc, size); \
+ void *p = user_alloc(thr, pc, size, GetPageSizeCached())
+#define COMMON_MALLOC_FREE(ptr) \
+ if (cur_thread()->in_symbolizer) \
+ return REAL(free)(ptr); \
+ SCOPED_INTERCEPTOR_RAW(free, ptr); \
+ user_free(thr, pc, ptr)
+#define COMMON_MALLOC_SIZE(ptr) \
+ uptr size = user_alloc_usable_size(ptr);
+#define COMMON_MALLOC_FILL_STATS(zone, stats)
+#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
+ (void)zone_name; \
+ Report("mz_realloc(%p) -- attempting to realloc unallocated memory.\n", ptr);
+#define COMMON_MALLOC_IGNORE_INVALID_FREE false
+#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \
+ (void)zone_name; \
+ Report("free_common(%p) -- attempting to free unallocated memory.\n", ptr);
+#define COMMON_MALLOC_NAMESPACE __tsan
+
+#include "sanitizer_common/sanitizer_malloc_mac.inc"
+
+#endif
(a) += (b);
#define SET(n) \
- (*(MD5_u32plus *)&ptr[(n) * 4])
+ (*(const MD5_u32plus *)&ptr[(n) * 4])
#define GET(n) \
SET(n)
MD5_u32plus block[16];
} MD5_CTX;
-static void *body(MD5_CTX *ctx, void *data, ulong_t size) {
- unsigned char *ptr;
+static const void *body(MD5_CTX *ctx, const void *data, ulong_t size) {
+ const unsigned char *ptr = (const unsigned char *)data;
MD5_u32plus a, b, c, d;
MD5_u32plus saved_a, saved_b, saved_c, saved_d;
- ptr = (unsigned char*)data;
-
a = ctx->a;
b = ctx->b;
c = ctx->c;
ctx->hi = 0;
}
-void MD5_Update(MD5_CTX *ctx, void *data, ulong_t size) {
+void MD5_Update(MD5_CTX *ctx, const void *data, ulong_t size) {
MD5_u32plus saved_lo;
ulong_t used, free;
}
internal_memcpy(&ctx->buffer[used], data, free);
- data = (unsigned char *)data + free;
+ data = (const unsigned char *)data + free;
size -= free;
body(ctx, ctx->buffer, 64);
}
MD5Hash res;
MD5_CTX ctx;
MD5_Init(&ctx);
- MD5_Update(&ctx, (void*)data, size);
+ MD5_Update(&ctx, data, size);
MD5_Final((unsigned char*)&res.hash[0], &ctx);
return res;
}
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "tsan_mman.h"
#include "tsan_flags.h"
// May be overriden by front-end.
-extern "C" void WEAK __tsan_malloc_hook(void *ptr, uptr size) {
+extern "C" void WEAK __sanitizer_malloc_hook(void *ptr, uptr size) {
(void)ptr;
(void)size;
}
-extern "C" void WEAK __tsan_free_hook(void *ptr) {
+extern "C" void WEAK __sanitizer_free_hook(void *ptr) {
(void)ptr;
}
namespace __tsan {
-COMPILER_CHECK(sizeof(MBlock) == 16);
-
-void MBlock::Lock() {
- atomic_uintptr_t *a = reinterpret_cast<atomic_uintptr_t*>(this);
- uptr v = atomic_load(a, memory_order_relaxed);
- for (int iter = 0;; iter++) {
- if (v & 1) {
- if (iter < 10)
- proc_yield(20);
- else
- internal_sched_yield();
- v = atomic_load(a, memory_order_relaxed);
- continue;
- }
- if (atomic_compare_exchange_weak(a, &v, v | 1, memory_order_acquire))
- break;
- }
-}
-
-void MBlock::Unlock() {
- atomic_uintptr_t *a = reinterpret_cast<atomic_uintptr_t*>(this);
- uptr v = atomic_load(a, memory_order_relaxed);
- DCHECK(v & 1);
- atomic_store(a, v & ~1, memory_order_relaxed);
-}
-
struct MapUnmapCallback {
void OnMap(uptr p, uptr size) const { }
void OnUnmap(uptr p, uptr size) const {
// We are about to unmap a chunk of user memory.
// Mark the corresponding shadow memory as not needed.
DontNeedShadowFor(p, size);
+ // Mark the corresponding meta shadow memory as not needed.
+ // Note the block does not contain any meta info at this point
+ // (this happens after free).
+ const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
+ const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
+ // Block came from LargeMmapAllocator, so must be large.
+ // We rely on this in the calculations below.
+ CHECK_GE(size, 2 * kPageSize);
+ uptr diff = RoundUp(p, kPageSize) - p;
+ if (diff != 0) {
+ p += diff;
+ size -= diff;
+ }
+ diff = p + size - RoundDown(p + size, kPageSize);
+ if (diff != 0)
+ size -= diff;
+ FlushUnneededShadowMemory((uptr)MemToMeta(p), size / kMetaRatio);
}
};
}
void InitializeAllocator() {
- allocator()->Init();
+ allocator()->Init(common_flags()->allocator_may_return_null);
}
void AllocatorThreadStart(ThreadState *thr) {
}
static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
- if (!thr->in_signal_handler || !flags()->report_signal_unsafe)
+ if (atomic_load_relaxed(&thr->in_signal_handler) == 0 ||
+ !flags()->report_signal_unsafe)
+ return;
+ VarSizeStackTrace stack;
+ ObtainCurrentStack(thr, pc, &stack);
+ if (IsFiredSuppression(ctx, ReportTypeSignalUnsafe, stack))
return;
- Context *ctx = CTX();
- StackTrace stack;
- stack.ObtainCurrent(thr, pc);
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeSignalUnsafe);
- if (!IsFiredSuppression(ctx, rep, stack)) {
- rep.AddStack(&stack);
- OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
- }
+ rep.AddStack(stack, true);
+ OutputReport(thr, rep);
}
-void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
- CHECK_GT(thr->in_rtl, 0);
+void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) {
if ((sz >= (1ull << 40)) || (align >= (1ull << 40)))
- return AllocatorReturnNull();
+ return allocator()->ReturnNullOrDie();
void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
if (p == 0)
return 0;
- MBlock *b = new(allocator()->GetMetaData(p)) MBlock;
- b->Init(sz, thr->tid, CurrentStackId(thr, pc));
- if (CTX() && CTX()->initialized) {
- if (thr->ignore_reads_and_writes == 0)
- MemoryRangeImitateWrite(thr, pc, (uptr)p, sz);
- else
- MemoryResetRange(thr, pc, (uptr)p, sz);
- }
- DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p);
- SignalUnsafeCall(thr, pc);
+ if (ctx && ctx->initialized)
+ OnUserAlloc(thr, pc, (uptr)p, sz, true);
+ if (signal)
+ SignalUnsafeCall(thr, pc);
return p;
}
-void user_free(ThreadState *thr, uptr pc, void *p) {
- CHECK_GT(thr->in_rtl, 0);
- CHECK_NE(p, (void*)0);
- DPrintf("#%d: free(%p)\n", thr->tid, p);
- MBlock *b = (MBlock*)allocator()->GetMetaData(p);
- if (b->ListHead()) {
- MBlock::ScopedLock l(b);
- for (SyncVar *s = b->ListHead(); s;) {
- SyncVar *res = s;
- s = s->next;
- StatInc(thr, StatSyncDestroyed);
- res->mtx.Lock();
- res->mtx.Unlock();
- DestroyAndFree(res);
- }
- b->ListReset();
- }
- if (CTX() && CTX()->initialized && thr->in_rtl == 1) {
- if (thr->ignore_reads_and_writes == 0)
- MemoryRangeFreed(thr, pc, (uptr)p, b->Size());
- }
+void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) {
+ if (CallocShouldReturnNullDueToOverflow(size, n))
+ return allocator()->ReturnNullOrDie();
+ void *p = user_alloc(thr, pc, n * size);
+ if (p)
+ internal_memset(p, 0, n * size);
+ return p;
+}
+
+void user_free(ThreadState *thr, uptr pc, void *p, bool signal) {
+ if (ctx && ctx->initialized)
+ OnUserFree(thr, pc, (uptr)p, true);
allocator()->Deallocate(&thr->alloc_cache, p);
- SignalUnsafeCall(thr, pc);
+ if (signal)
+ SignalUnsafeCall(thr, pc);
+}
+
+void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) {
+ DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p);
+ ctx->metamap.AllocBlock(thr, pc, p, sz);
+ if (write && thr->ignore_reads_and_writes == 0)
+ MemoryRangeImitateWrite(thr, pc, (uptr)p, sz);
+ else
+ MemoryResetRange(thr, pc, (uptr)p, sz);
+}
+
+void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write) {
+ CHECK_NE(p, (void*)0);
+ uptr sz = ctx->metamap.FreeBlock(thr, pc, p);
+ DPrintf("#%d: free(%p, %zu)\n", thr->tid, p, sz);
+ if (write && thr->ignore_reads_and_writes == 0)
+ MemoryRangeFreed(thr, pc, (uptr)p, sz);
}
void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
- CHECK_GT(thr->in_rtl, 0);
void *p2 = 0;
// FIXME: Handle "shrinking" more efficiently,
// it seems that some software actually does this.
if (p2 == 0)
return 0;
if (p) {
- MBlock *b = user_mblock(thr, p);
- CHECK_NE(b, 0);
- internal_memcpy(p2, p, min(b->Size(), sz));
+ uptr oldsz = user_alloc_usable_size(p);
+ internal_memcpy(p2, p, min(oldsz, sz));
}
}
if (p)
return p2;
}
-uptr user_alloc_usable_size(ThreadState *thr, uptr pc, void *p) {
- CHECK_GT(thr->in_rtl, 0);
+uptr user_alloc_usable_size(const void *p) {
if (p == 0)
return 0;
- MBlock *b = (MBlock*)allocator()->GetMetaData(p);
- return b ? b->Size() : 0;
-}
-
-MBlock *user_mblock(ThreadState *thr, void *p) {
- CHECK_NE(p, 0);
- Allocator *a = allocator();
- void *b = a->GetBlockBegin(p);
- if (b == 0)
- return 0;
- return (MBlock*)a->GetMetaData(b);
+ MBlock *b = ctx->metamap.GetBlock((uptr)p);
+ return b ? b->siz : 0;
}
void invoke_malloc_hook(void *ptr, uptr size) {
- Context *ctx = CTX();
ThreadState *thr = cur_thread();
- if (ctx == 0 || !ctx->initialized || thr->in_rtl)
+ if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
return;
- __tsan_malloc_hook(ptr, size);
+ __sanitizer_malloc_hook(ptr, size);
}
void invoke_free_hook(void *ptr) {
- Context *ctx = CTX();
ThreadState *thr = cur_thread();
- if (ctx == 0 || !ctx->initialized || thr->in_rtl)
+ if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
return;
- __tsan_free_hook(ptr);
+ __sanitizer_free_hook(ptr);
}
void *internal_alloc(MBlockType typ, uptr sz) {
ThreadState *thr = cur_thread();
- CHECK_GT(thr->in_rtl, 0);
- CHECK_LE(sz, InternalSizeClassMap::kMaxSize);
if (thr->nomalloc) {
thr->nomalloc = 0; // CHECK calls internal_malloc().
CHECK(0);
void internal_free(void *p) {
ThreadState *thr = cur_thread();
- CHECK_GT(thr->in_rtl, 0);
if (thr->nomalloc) {
thr->nomalloc = 0; // CHECK calls internal_malloc().
CHECK(0);
using namespace __tsan;
extern "C" {
-uptr __tsan_get_current_allocated_bytes() {
- u64 stats[AllocatorStatCount];
+uptr __sanitizer_get_current_allocated_bytes() {
+ uptr stats[AllocatorStatCount];
allocator()->GetStats(stats);
- u64 m = stats[AllocatorStatMalloced];
- u64 f = stats[AllocatorStatFreed];
- return m >= f ? m - f : 1;
+ return stats[AllocatorStatAllocated];
}
-uptr __tsan_get_heap_size() {
- u64 stats[AllocatorStatCount];
+uptr __sanitizer_get_heap_size() {
+ uptr stats[AllocatorStatCount];
allocator()->GetStats(stats);
- u64 m = stats[AllocatorStatMmapped];
- u64 f = stats[AllocatorStatUnmapped];
- return m >= f ? m - f : 1;
+ return stats[AllocatorStatMapped];
}
-uptr __tsan_get_free_bytes() {
+uptr __sanitizer_get_free_bytes() {
return 1;
}
-uptr __tsan_get_unmapped_bytes() {
+uptr __sanitizer_get_unmapped_bytes() {
return 1;
}
-uptr __tsan_get_estimated_allocated_size(uptr size) {
+uptr __sanitizer_get_estimated_allocated_size(uptr size) {
return size;
}
-bool __tsan_get_ownership(void *p) {
+int __sanitizer_get_ownership(const void *p) {
return allocator()->GetBlockBegin(p) != 0;
}
-uptr __tsan_get_allocated_size(void *p) {
- if (p == 0)
- return 0;
- p = allocator()->GetBlockBegin(p);
- if (p == 0)
- return 0;
- MBlock *b = (MBlock*)allocator()->GetMetaData(p);
- return b->Size();
+uptr __sanitizer_get_allocated_size(const void *p) {
+ return user_alloc_usable_size(p);
}
void __tsan_on_thread_idle() {
ThreadState *thr = cur_thread();
allocator()->SwallowCache(&thr->alloc_cache);
internal_allocator()->SwallowCache(&thr->internal_alloc_cache);
+ ctx->metamap.OnThreadIdle(thr);
}
} // extern "C"
const uptr kDefaultAlignment = 16;
void InitializeAllocator();
+void ReplaceSystemMalloc();
void AllocatorThreadStart(ThreadState *thr);
void AllocatorThreadFinish(ThreadState *thr);
void AllocatorPrintStats();
// For user allocations.
void *user_alloc(ThreadState *thr, uptr pc, uptr sz,
- uptr align = kDefaultAlignment);
+ uptr align = kDefaultAlignment, bool signal = true);
+void *user_calloc(ThreadState *thr, uptr pc, uptr sz, uptr n);
// Does not accept NULL.
-void user_free(ThreadState *thr, uptr pc, void *p);
+void user_free(ThreadState *thr, uptr pc, void *p, bool signal = true);
void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz);
void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align);
-uptr user_alloc_usable_size(ThreadState *thr, uptr pc, void *p);
-// Given the pointer p into a valid allocated block,
-// returns the descriptor of the block.
-MBlock *user_mblock(ThreadState *thr, void *p);
+uptr user_alloc_usable_size(const void *p);
// Invoking malloc/free hooks that may be installed by the user.
void invoke_malloc_hook(void *ptr, uptr size);
MBlockSuppression,
MBlockExpectRace,
MBlockSignal,
- MBlockFD,
MBlockJmpBuf,
// This must be the last.
// then Report mutex can be locked while under Threads mutex.
// The leaf mutexes can be locked under any other mutexes.
// Recursive locking is not supported.
-#if TSAN_DEBUG && !TSAN_GO
+#if SANITIZER_DEBUG && !SANITIZER_GO
const MutexType MutexTypeLeaf = (MutexType)-1;
static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
/*0 MutexTypeInvalid*/ {},
/*1 MutexTypeTrace*/ {MutexTypeLeaf},
/*2 MutexTypeThreads*/ {MutexTypeReport},
- /*3 MutexTypeReport*/ {MutexTypeSyncTab, MutexTypeSyncVar,
+ /*3 MutexTypeReport*/ {MutexTypeSyncVar,
MutexTypeMBlock, MutexTypeJavaMBlock},
- /*4 MutexTypeSyncVar*/ {},
- /*5 MutexTypeSyncTab*/ {MutexTypeSyncVar},
+ /*4 MutexTypeSyncVar*/ {MutexTypeDDetector},
+ /*5 MutexTypeSyncTab*/ {}, // unused
/*6 MutexTypeSlab*/ {MutexTypeLeaf},
/*7 MutexTypeAnnotations*/ {},
- /*8 MutexTypeAtExit*/ {MutexTypeSyncTab},
+ /*8 MutexTypeAtExit*/ {MutexTypeSyncVar},
/*9 MutexTypeMBlock*/ {MutexTypeSyncVar},
/*10 MutexTypeJavaMBlock*/ {MutexTypeSyncVar},
+ /*11 MutexTypeDDetector*/ {},
+ /*12 MutexTypeFired*/ {MutexTypeLeaf},
+ /*13 MutexTypeRacy*/ {MutexTypeLeaf},
};
static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
#endif
void InitializeMutex() {
-#if TSAN_DEBUG && !TSAN_GO
+#if SANITIZER_DEBUG && !SANITIZER_GO
// Build the "can lock" adjacency matrix.
// If [i][j]==true, then one can lock mutex j while under mutex i.
const int N = MutexTypeCount;
#endif
}
-DeadlockDetector::DeadlockDetector() {
+InternalDeadlockDetector::InternalDeadlockDetector() {
// Rely on zero initialization because some mutexes can be locked before ctor.
}
-#if TSAN_DEBUG && !TSAN_GO
-void DeadlockDetector::Lock(MutexType t) {
+#if SANITIZER_DEBUG && !SANITIZER_GO
+void InternalDeadlockDetector::Lock(MutexType t) {
// Printf("LOCK %d @%zu\n", t, seq_ + 1);
CHECK_GT(t, MutexTypeInvalid);
CHECK_LT(t, MutexTypeCount);
}
}
-void DeadlockDetector::Unlock(MutexType t) {
+void InternalDeadlockDetector::Unlock(MutexType t) {
// Printf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]);
CHECK(locked_[t]);
locked_[t] = 0;
}
+
+void InternalDeadlockDetector::CheckNoLocks() {
+ for (int i = 0; i != MutexTypeCount; i++) {
+ CHECK_EQ(locked_[i], 0);
+ }
+}
#endif
+void CheckNoLocks(ThreadState *thr) {
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ thr->internal_deadlock_detector.CheckNoLocks();
+#endif
+}
+
const uptr kUnlocked = 0;
const uptr kWriteLock = 1;
const uptr kReadLock = 2;
Mutex::Mutex(MutexType type, StatType stat_type) {
CHECK_GT(type, MutexTypeInvalid);
CHECK_LT(type, MutexTypeCount);
-#if TSAN_DEBUG
+#if SANITIZER_DEBUG
type_ = type;
#endif
#if TSAN_COLLECT_STATS
}
void Mutex::Lock() {
-#if TSAN_DEBUG && !TSAN_GO
- cur_thread()->deadlock_detector.Lock(type_);
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ cur_thread()->internal_deadlock_detector.Lock(type_);
#endif
uptr cmp = kUnlocked;
if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
cmp = kUnlocked;
if (atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
memory_order_acquire)) {
-#if TSAN_COLLECT_STATS
+#if TSAN_COLLECT_STATS && !SANITIZER_GO
StatInc(cur_thread(), stat_type_, backoff.Contention());
#endif
return;
uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
(void)prev;
DCHECK_NE(prev & kWriteLock, 0);
-#if TSAN_DEBUG && !TSAN_GO
- cur_thread()->deadlock_detector.Unlock(type_);
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ cur_thread()->internal_deadlock_detector.Unlock(type_);
#endif
}
void Mutex::ReadLock() {
-#if TSAN_DEBUG && !TSAN_GO
- cur_thread()->deadlock_detector.Lock(type_);
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ cur_thread()->internal_deadlock_detector.Lock(type_);
#endif
uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
if ((prev & kWriteLock) == 0)
for (Backoff backoff; backoff.Do();) {
prev = atomic_load(&state_, memory_order_acquire);
if ((prev & kWriteLock) == 0) {
-#if TSAN_COLLECT_STATS
+#if TSAN_COLLECT_STATS && !SANITIZER_GO
StatInc(cur_thread(), stat_type_, backoff.Contention());
#endif
return;
(void)prev;
DCHECK_EQ(prev & kWriteLock, 0);
DCHECK_GT(prev & ~kWriteLock, 0);
-#if TSAN_DEBUG && !TSAN_GO
- cur_thread()->deadlock_detector.Unlock(type_);
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ cur_thread()->internal_deadlock_detector.Unlock(type_);
#endif
}
MutexTypeAtExit,
MutexTypeMBlock,
MutexTypeJavaMBlock,
+ MutexTypeDDetector,
+ MutexTypeFired,
+ MutexTypeRacy,
// This must be the last.
MutexTypeCount
private:
atomic_uintptr_t state_;
-#if TSAN_DEBUG
+#if SANITIZER_DEBUG
MutexType type_;
#endif
#if TSAN_COLLECT_STATS
typedef GenericScopedLock<Mutex> Lock;
typedef GenericScopedReadLock<Mutex> ReadLock;
-class DeadlockDetector {
+class InternalDeadlockDetector {
public:
- DeadlockDetector();
+ InternalDeadlockDetector();
void Lock(MutexType t);
void Unlock(MutexType t);
+ void CheckNoLocks();
private:
u64 seq_;
u64 locked_[MutexTypeCount];
void InitializeMutex();
+// Checks that the current thread does not hold any runtime locks
+// (e.g. when returning from an interceptor).
+void CheckNoLocks(ThreadState *thr);
+
} // namespace __tsan
#endif // TSAN_MUTEX_H
uptr Size() const;
Desc Get(uptr i) const;
+ void operator=(const MutexSet &other) {
+ internal_memcpy(this, &other, sizeof(*this));
+ }
+
private:
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
uptr size_;
Desc descs_[kMaxSize];
#endif
void RemovePos(uptr i);
+ MutexSet(const MutexSet&);
};
// Go does not have mutexes, so do not spend memory and time.
// (Go sync.Mutex is actually a semaphore -- can be unlocked
// in different goroutine).
-#ifdef TSAN_GO
+#ifdef SANITIZER_GO
MutexSet::MutexSet() {}
void MutexSet::Add(u64 id, bool write, u64 epoch) {}
void MutexSet::Del(u64 id, bool write) {}
--- /dev/null
+//===-- tsan_new_delete.cc ----------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Interceptors for operators new and delete.
+//===----------------------------------------------------------------------===//
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "tsan_interceptors.h"
+
+using namespace __tsan; // NOLINT
+
+namespace std {
+struct nothrow_t {};
+} // namespace std
+
+DECLARE_REAL(void *, malloc, uptr size)
+DECLARE_REAL(void, free, void *ptr)
+#if SANITIZER_MAC
+#define __libc_malloc REAL(malloc)
+#define __libc_free REAL(free)
+#endif
+
+#define OPERATOR_NEW_BODY(mangled_name) \
+ if (cur_thread()->in_symbolizer) \
+ return __libc_malloc(size); \
+ void *p = 0; \
+ { \
+ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
+ p = user_alloc(thr, pc, size); \
+ } \
+ invoke_malloc_hook(p, size); \
+ return p;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new(__sanitizer::uptr size);
+void *operator new(__sanitizer::uptr size) {
+ OPERATOR_NEW_BODY(_Znwm);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new[](__sanitizer::uptr size);
+void *operator new[](__sanitizer::uptr size) {
+ OPERATOR_NEW_BODY(_Znam);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new(__sanitizer::uptr size, std::nothrow_t const&);
+void *operator new(__sanitizer::uptr size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new[](__sanitizer::uptr size, std::nothrow_t const&);
+void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t);
+}
+
+#define OPERATOR_DELETE_BODY(mangled_name) \
+ if (ptr == 0) return; \
+ if (cur_thread()->in_symbolizer) \
+ return __libc_free(ptr); \
+ invoke_free_hook(ptr); \
+ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \
+ user_free(thr, pc, ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete(void *ptr) NOEXCEPT;
+void operator delete(void *ptr) NOEXCEPT {
+ OPERATOR_DELETE_BODY(_ZdlPv);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete[](void *ptr) NOEXCEPT;
+void operator delete[](void *ptr) NOEXCEPT {
+ OPERATOR_DELETE_BODY(_ZdaPv);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&);
+void operator delete(void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const&);
+void operator delete[](void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t);
+}
// Platform-specific code.
//===----------------------------------------------------------------------===//
+#ifndef TSAN_PLATFORM_H
+#define TSAN_PLATFORM_H
+
+#if !defined(__LP64__) && !defined(_WIN64)
+# error "Only 64-bit is supported"
+#endif
+
+#include "tsan_defs.h"
+#include "tsan_trace.h"
+
+namespace __tsan {
+
+#if !defined(SANITIZER_GO)
+
+#if defined(__x86_64__)
/*
-C++ linux memory layout:
-0000 0000 0000 - 03c0 0000 0000: protected
-03c0 0000 0000 - 1000 0000 0000: shadow
-1000 0000 0000 - 6000 0000 0000: protected
-6000 0000 0000 - 6200 0000 0000: traces
-6200 0000 0000 - 7d00 0000 0000: -
-7d00 0000 0000 - 7e00 0000 0000: heap
-7e00 0000 0000 - 7fff ffff ffff: modules and main thread stack
-
-C++ COMPAT linux memory layout:
-0000 0000 0000 - 0400 0000 0000: protected
-0400 0000 0000 - 1000 0000 0000: shadow
-1000 0000 0000 - 2900 0000 0000: protected
-2900 0000 0000 - 2c00 0000 0000: modules
-2c00 0000 0000 - 6000 0000 0000: -
+C/C++ on linux/x86_64 and freebsd/x86_64
+0000 0000 1000 - 0100 0000 0000: main binary and/or MAP_32BIT mappings
+0100 0000 0000 - 0200 0000 0000: -
+0200 0000 0000 - 1000 0000 0000: shadow
+1000 0000 0000 - 3000 0000 0000: -
+3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
+4000 0000 0000 - 6000 0000 0000: -
6000 0000 0000 - 6200 0000 0000: traces
6200 0000 0000 - 7d00 0000 0000: -
7d00 0000 0000 - 7e00 0000 0000: heap
-7e00 0000 0000 - 7f00 0000 0000: -
-7f00 0000 0000 - 7fff ffff ffff: main thread stack
+7e00 0000 0000 - 7e80 0000 0000: -
+7e80 0000 0000 - 8000 0000 0000: modules and main thread stack
+*/
+const uptr kMetaShadowBeg = 0x300000000000ull;
+const uptr kMetaShadowEnd = 0x400000000000ull;
+const uptr kTraceMemBeg = 0x600000000000ull;
+const uptr kTraceMemEnd = 0x620000000000ull;
+const uptr kShadowBeg = 0x020000000000ull;
+const uptr kShadowEnd = 0x100000000000ull;
+const uptr kHeapMemBeg = 0x7d0000000000ull;
+const uptr kHeapMemEnd = 0x7e0000000000ull;
+const uptr kLoAppMemBeg = 0x000000001000ull;
+const uptr kLoAppMemEnd = 0x010000000000ull;
+const uptr kHiAppMemBeg = 0x7e8000000000ull;
+const uptr kHiAppMemEnd = 0x800000000000ull;
+const uptr kAppMemMsk = 0x7c0000000000ull;
+const uptr kAppMemXor = 0x020000000000ull;
+const uptr kVdsoBeg = 0xf000000000000000ull;
+#elif defined(__mips64)
+/*
+C/C++ on linux/mips64
+0100 0000 00 - 0200 0000 00: main binary
+0200 0000 00 - 1400 0000 00: -
+1400 0000 00 - 2400 0000 00: shadow
+2400 0000 00 - 3000 0000 00: -
+3000 0000 00 - 4000 0000 00: metainfo (memory blocks and sync objects)
+4000 0000 00 - 6000 0000 00: -
+6000 0000 00 - 6200 0000 00: traces
+6200 0000 00 - fe00 0000 00: -
+fe00 0000 00 - ff00 0000 00: heap
+ff00 0000 00 - ff80 0000 00: -
+ff80 0000 00 - ffff ffff ff: modules and main thread stack
+*/
+const uptr kMetaShadowBeg = 0x3000000000ull;
+const uptr kMetaShadowEnd = 0x4000000000ull;
+const uptr kTraceMemBeg = 0x6000000000ull;
+const uptr kTraceMemEnd = 0x6200000000ull;
+const uptr kShadowBeg = 0x1400000000ull;
+const uptr kShadowEnd = 0x2400000000ull;
+const uptr kHeapMemBeg = 0xfe00000000ull;
+const uptr kHeapMemEnd = 0xff00000000ull;
+const uptr kLoAppMemBeg = 0x0100000000ull;
+const uptr kLoAppMemEnd = 0x0200000000ull;
+const uptr kHiAppMemBeg = 0xff80000000ull;
+const uptr kHiAppMemEnd = 0xffffffffffull;
+const uptr kAppMemMsk = 0xfc00000000ull;
+const uptr kAppMemXor = 0x0400000000ull;
+const uptr kVdsoBeg = 0xfffff00000ull;
+#elif defined(__aarch64__)
+# if SANITIZER_AARCH64_VMA == 39
+/*
+C/C++ on linux/aarch64 (39-bit VMA)
+0000 4000 00 - 0200 0000 00: main binary
+2000 0000 00 - 4000 0000 00: shadow memory
+4000 0000 00 - 5000 0000 00: metainfo
+5000 0000 00 - 6000 0000 00: -
+6000 0000 00 - 6200 0000 00: traces
+6200 0000 00 - 7d00 0000 00: -
+7d00 0000 00 - 7e00 0000 00: heap
+7e00 0000 00 - 7fff ffff ff: modules and main thread stack
+*/
+const uptr kLoAppMemBeg = 0x0000400000ull;
+const uptr kLoAppMemEnd = 0x0200000000ull;
+const uptr kShadowBeg = 0x2000000000ull;
+const uptr kShadowEnd = 0x4000000000ull;
+const uptr kMetaShadowBeg = 0x4000000000ull;
+const uptr kMetaShadowEnd = 0x5000000000ull;
+const uptr kTraceMemBeg = 0x6000000000ull;
+const uptr kTraceMemEnd = 0x6200000000ull;
+const uptr kHeapMemBeg = 0x7d00000000ull;
+const uptr kHeapMemEnd = 0x7e00000000ull;
+const uptr kHiAppMemBeg = 0x7e00000000ull;
+const uptr kHiAppMemEnd = 0x7fffffffffull;
+const uptr kAppMemMsk = 0x7800000000ull;
+const uptr kAppMemXor = 0x0800000000ull;
+const uptr kVdsoBeg = 0x7f00000000ull;
+# elif SANITIZER_AARCH64_VMA == 42
+/*
+C/C++ on linux/aarch64 (42-bit VMA)
+00000 4000 00 - 01000 0000 00: main binary
+01000 0000 00 - 10000 0000 00: -
+10000 0000 00 - 20000 0000 00: shadow memory
+20000 0000 00 - 26000 0000 00: -
+26000 0000 00 - 28000 0000 00: metainfo
+28000 0000 00 - 36200 0000 00: -
+36200 0000 00 - 36240 0000 00: traces
+36240 0000 00 - 3e000 0000 00: -
+3e000 0000 00 - 3f000 0000 00: heap
+3c000 0000 00 - 3ff00 0000 00: -
+3ff00 0000 00 - 3ffff f000 00: modules and main thread stack
+*/
+const uptr kLoAppMemBeg = 0x00000400000ull;
+const uptr kLoAppMemEnd = 0x01000000000ull;
+const uptr kShadowBeg = 0x10000000000ull;
+const uptr kShadowEnd = 0x20000000000ull;
+const uptr kMetaShadowBeg = 0x26000000000ull;
+const uptr kMetaShadowEnd = 0x28000000000ull;
+const uptr kTraceMemBeg = 0x36200000000ull;
+const uptr kTraceMemEnd = 0x36400000000ull;
+const uptr kHeapMemBeg = 0x3e000000000ull;
+const uptr kHeapMemEnd = 0x3f000000000ull;
+const uptr kHiAppMemBeg = 0x3ff00000000ull;
+const uptr kHiAppMemEnd = 0x3fffff00000ull;
+const uptr kAppMemMsk = 0x3c000000000ull;
+const uptr kAppMemXor = 0x04000000000ull;
+const uptr kVdsoBeg = 0x37f00000000ull;
+# endif
+#endif
-Go linux and darwin memory layout:
-0000 0000 0000 - 0000 1000 0000: executable
-0000 1000 0000 - 00f8 0000 0000: -
-00c0 0000 0000 - 00e0 0000 0000: heap
-00e0 0000 0000 - 1000 0000 0000: -
-1000 0000 0000 - 1380 0000 0000: shadow
-1460 0000 0000 - 6000 0000 0000: -
-6000 0000 0000 - 6200 0000 0000: traces
-6200 0000 0000 - 7fff ffff ffff: -
+ALWAYS_INLINE
+bool IsAppMem(uptr mem) {
+ return (mem >= kHeapMemBeg && mem < kHeapMemEnd) ||
+ (mem >= kLoAppMemBeg && mem < kLoAppMemEnd) ||
+ (mem >= kHiAppMemBeg && mem < kHiAppMemEnd);
+}
-Go windows memory layout:
-0000 0000 0000 - 0000 1000 0000: executable
-0000 1000 0000 - 00f8 0000 0000: -
+ALWAYS_INLINE
+bool IsShadowMem(uptr mem) {
+ return mem >= kShadowBeg && mem <= kShadowEnd;
+}
+
+ALWAYS_INLINE
+bool IsMetaMem(uptr mem) {
+ return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
+}
+
+ALWAYS_INLINE
+uptr MemToShadow(uptr x) {
+ DCHECK(IsAppMem(x));
+ return (((x) & ~(kAppMemMsk | (kShadowCell - 1)))
+ ^ kAppMemXor) * kShadowCnt;
+}
+
+ALWAYS_INLINE
+u32 *MemToMeta(uptr x) {
+ DCHECK(IsAppMem(x));
+ return (u32*)(((((x) & ~(kAppMemMsk | (kMetaShadowCell - 1)))
+ ^ kAppMemXor) / kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
+}
+
+ALWAYS_INLINE
+uptr ShadowToMem(uptr s) {
+ CHECK(IsShadowMem(s));
+ if (s >= MemToShadow(kLoAppMemBeg) && s <= MemToShadow(kLoAppMemEnd - 1))
+ return (s / kShadowCnt) ^ kAppMemXor;
+ else
+ return ((s / kShadowCnt) ^ kAppMemXor) | kAppMemMsk;
+}
+
+static USED uptr UserRegions[] = {
+ kLoAppMemBeg, kLoAppMemEnd,
+ kHiAppMemBeg, kHiAppMemEnd,
+ kHeapMemBeg, kHeapMemEnd,
+};
+
+#elif defined(SANITIZER_GO) && !SANITIZER_WINDOWS
+
+/* Go on linux, darwin and freebsd
+0000 0000 1000 - 0000 1000 0000: executable
+0000 1000 0000 - 00c0 0000 0000: -
00c0 0000 0000 - 00e0 0000 0000: heap
-00e0 0000 0000 - 0100 0000 0000: -
-0100 0000 0000 - 0560 0000 0000: shadow
-0560 0000 0000 - 0760 0000 0000: traces
-0760 0000 0000 - 07ff ffff ffff: -
+00e0 0000 0000 - 2000 0000 0000: -
+2000 0000 0000 - 2380 0000 0000: shadow
+2380 0000 0000 - 3000 0000 0000: -
+3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
+4000 0000 0000 - 6000 0000 0000: -
+6000 0000 0000 - 6200 0000 0000: traces
+6200 0000 0000 - 8000 0000 0000: -
*/
-#ifndef TSAN_PLATFORM_H
-#define TSAN_PLATFORM_H
+const uptr kMetaShadowBeg = 0x300000000000ull;
+const uptr kMetaShadowEnd = 0x400000000000ull;
+const uptr kTraceMemBeg = 0x600000000000ull;
+const uptr kTraceMemEnd = 0x620000000000ull;
+const uptr kShadowBeg = 0x200000000000ull;
+const uptr kShadowEnd = 0x238000000000ull;
+const uptr kAppMemBeg = 0x000000001000ull;
+const uptr kAppMemEnd = 0x00e000000000ull;
-#include "tsan_defs.h"
-#include "tsan_trace.h"
+ALWAYS_INLINE
+bool IsAppMem(uptr mem) {
+ return mem >= kAppMemBeg && mem < kAppMemEnd;
+}
-#if defined(__LP64__) || defined(_WIN64)
-namespace __tsan {
+ALWAYS_INLINE
+bool IsShadowMem(uptr mem) {
+ return mem >= kShadowBeg && mem <= kShadowEnd;
+}
-#if defined(TSAN_GO)
-static const uptr kLinuxAppMemBeg = 0x000000000000ULL;
-static const uptr kLinuxAppMemEnd = 0x04dfffffffffULL;
-# if SANITIZER_WINDOWS
-static const uptr kLinuxShadowMsk = 0x010000000000ULL;
-# else
-static const uptr kLinuxShadowMsk = 0x200000000000ULL;
-# endif
-// TSAN_COMPAT_SHADOW is intended for COMPAT virtual memory layout,
-// when memory addresses are of the 0x2axxxxxxxxxx form.
-// The option is enabled with 'setarch x86_64 -L'.
-#elif defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
-static const uptr kLinuxAppMemBeg = 0x290000000000ULL;
-static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
-#else
-static const uptr kLinuxAppMemBeg = 0x7cf000000000ULL;
-static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
-#endif
+ALWAYS_INLINE
+bool IsMetaMem(uptr mem) {
+ return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
+}
-static const uptr kLinuxAppMemMsk = 0x7c0000000000ULL;
+ALWAYS_INLINE
+uptr MemToShadow(uptr x) {
+ DCHECK(IsAppMem(x));
+ return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg;
+}
-#if SANITIZER_WINDOWS
-const uptr kTraceMemBegin = 0x056000000000ULL;
-#else
-const uptr kTraceMemBegin = 0x600000000000ULL;
-#endif
-const uptr kTraceMemSize = 0x020000000000ULL;
+ALWAYS_INLINE
+u32 *MemToMeta(uptr x) {
+ DCHECK(IsAppMem(x));
+ return (u32*)(((x & ~(kMetaShadowCell - 1)) / \
+ kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
+}
-// This has to be a macro to allow constant initialization of constants below.
-#ifndef TSAN_GO
-#define MemToShadow(addr) \
- (((addr) & ~(kLinuxAppMemMsk | (kShadowCell - 1))) * kShadowCnt)
-#else
-#define MemToShadow(addr) \
- ((((addr) & ~(kShadowCell - 1)) * kShadowCnt) | kLinuxShadowMsk)
-#endif
+ALWAYS_INLINE
+uptr ShadowToMem(uptr s) {
+ CHECK(IsShadowMem(s));
+ return (s & ~kShadowBeg) / kShadowCnt;
+}
+
+static USED uptr UserRegions[] = {
+ kAppMemBeg, kAppMemEnd,
+};
-static const uptr kLinuxShadowBeg = MemToShadow(kLinuxAppMemBeg);
-static const uptr kLinuxShadowEnd =
- MemToShadow(kLinuxAppMemEnd) | 0xff;
+#elif defined(SANITIZER_GO) && SANITIZER_WINDOWS
-static inline bool IsAppMem(uptr mem) {
- return mem >= kLinuxAppMemBeg && mem <= kLinuxAppMemEnd;
+/* Go on windows
+0000 0000 1000 - 0000 1000 0000: executable
+0000 1000 0000 - 00f8 0000 0000: -
+00c0 0000 0000 - 00e0 0000 0000: heap
+00e0 0000 0000 - 0100 0000 0000: -
+0100 0000 0000 - 0500 0000 0000: shadow
+0500 0000 0000 - 0560 0000 0000: -
+0560 0000 0000 - 0760 0000 0000: traces
+0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects)
+07d0 0000 0000 - 8000 0000 0000: -
+*/
+
+const uptr kMetaShadowBeg = 0x076000000000ull;
+const uptr kMetaShadowEnd = 0x07d000000000ull;
+const uptr kTraceMemBeg = 0x056000000000ull;
+const uptr kTraceMemEnd = 0x076000000000ull;
+const uptr kShadowBeg = 0x010000000000ull;
+const uptr kShadowEnd = 0x050000000000ull;
+const uptr kAppMemBeg = 0x000000001000ull;
+const uptr kAppMemEnd = 0x00e000000000ull;
+
+ALWAYS_INLINE
+bool IsAppMem(uptr mem) {
+ return mem >= kAppMemBeg && mem < kAppMemEnd;
}
-static inline bool IsShadowMem(uptr mem) {
- return mem >= kLinuxShadowBeg && mem <= kLinuxShadowEnd;
+ALWAYS_INLINE
+bool IsShadowMem(uptr mem) {
+ return mem >= kShadowBeg && mem <= kShadowEnd;
}
-static inline uptr ShadowToMem(uptr shadow) {
- CHECK(IsShadowMem(shadow));
-#ifdef TSAN_GO
- return (shadow & ~kLinuxShadowMsk) / kShadowCnt;
-#else
- return (shadow / kShadowCnt) | kLinuxAppMemMsk;
-#endif
+ALWAYS_INLINE
+bool IsMetaMem(uptr mem) {
+ return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
}
-// For COMPAT mapping returns an alternative address
-// that mapped to the same shadow address.
-// COMPAT mapping is not quite one-to-one.
-static inline uptr AlternativeAddress(uptr addr) {
-#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
- return (addr & ~kLinuxAppMemMsk) | 0x280000000000ULL;
-#else
- return 0;
-#endif
+ALWAYS_INLINE
+uptr MemToShadow(uptr x) {
+ DCHECK(IsAppMem(x));
+ return ((x & ~(kShadowCell - 1)) * kShadowCnt) + kShadowBeg;
}
-void FlushShadowMemory();
-void WriteMemoryProfile(char *buf, uptr buf_size);
-uptr GetRSS();
+ALWAYS_INLINE
+u32 *MemToMeta(uptr x) {
+ DCHECK(IsAppMem(x));
+ return (u32*)(((x & ~(kMetaShadowCell - 1)) / \
+ kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
+}
-const char *InitializePlatform();
-void FinalizePlatform();
+ALWAYS_INLINE
+uptr ShadowToMem(uptr s) {
+ CHECK(IsShadowMem(s));
+ // FIXME(dvyukov): this is most likely wrong as the mapping is not bijection.
+ return (s - kShadowBeg) / kShadowCnt;
+}
+
+static USED uptr UserRegions[] = {
+ kAppMemBeg, kAppMemEnd,
+};
+
+#else
+# error "Unknown platform"
+#endif
// The additional page is to catch shadow stack overflow as paging fault.
-const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace) + 4096
- + 4095) & ~4095;
+// Windows wants 64K alignment for mmaps.
+const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace)
+ + (64 << 10) + (64 << 10) - 1) & ~((64 << 10) - 1);
uptr ALWAYS_INLINE GetThreadTrace(int tid) {
- uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize;
- DCHECK_LT(p, kTraceMemBegin + kTraceMemSize);
+ uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize;
+ DCHECK_LT(p, kTraceMemEnd);
return p;
}
uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) {
- uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize
+ uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize
+ kTraceSize * sizeof(Event);
- DCHECK_LT(p, kTraceMemBegin + kTraceMemSize);
+ DCHECK_LT(p, kTraceMemEnd);
return p;
}
-void internal_start_thread(void(*func)(void*), void *arg);
+void InitializePlatform();
+void CheckAndProtect();
+void InitializeShadowMemoryPlatform();
+void FlushShadowMemory();
+void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive);
// Says whether the addr relates to a global var.
// Guesses with high probability, may yield both false positives and negatives.
int ExtractResolvFDs(void *state, int *fds, int nfd);
int ExtractRecvmsgFDs(void *msg, int *fds, int nfd);
-} // namespace __tsan
+int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
+ void *abstime), void *c, void *m, void *abstime,
+ void(*cleanup)(void *arg), void *arg);
-#else // defined(__LP64__) || defined(_WIN64)
-# error "Only 64-bit is supported"
-#endif
+void DestroyThreadState();
+
+} // namespace __tsan
#endif // TSAN_PLATFORM_H
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
-// Linux-specific code.
+// Linux- and FreeBSD-specific code.
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_posix.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stoptheworld.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include "tsan_flags.h"
#include <string.h>
#include <stdarg.h>
#include <sys/mman.h>
-#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <errno.h>
#include <sched.h>
#include <dlfcn.h>
+#if SANITIZER_LINUX
#define __need_res_state
#include <resolv.h>
-#include <malloc.h>
+#endif
#ifdef sa_handler
# undef sa_handler
# undef sa_sigaction
#endif
-extern "C" struct mallinfo __libc_mallinfo();
+#if SANITIZER_FREEBSD
+extern "C" void *__libc_stack_end;
+void *__libc_stack_end = 0;
+#endif
namespace __tsan {
-const uptr kPageSize = 4096;
-
-#ifndef TSAN_GO
-ScopedInRtl::ScopedInRtl()
- : thr_(cur_thread()) {
- in_rtl_ = thr_->in_rtl;
- thr_->in_rtl++;
- errno_ = errno;
-}
+static uptr g_data_start;
+static uptr g_data_end;
-ScopedInRtl::~ScopedInRtl() {
- thr_->in_rtl--;
- errno = errno_;
- CHECK_EQ(in_rtl_, thr_->in_rtl);
-}
+enum {
+ MemTotal = 0,
+ MemShadow = 1,
+ MemMeta = 2,
+ MemFile = 3,
+ MemMmap = 4,
+ MemTrace = 5,
+ MemHeap = 6,
+ MemOther = 7,
+ MemCount = 8,
+};
+
+void FillProfileCallback(uptr p, uptr rss, bool file,
+ uptr *mem, uptr stats_size) {
+ mem[MemTotal] += rss;
+ if (p >= kShadowBeg && p < kShadowEnd)
+ mem[MemShadow] += rss;
+ else if (p >= kMetaShadowBeg && p < kMetaShadowEnd)
+ mem[MemMeta] += rss;
+#ifndef SANITIZER_GO
+ else if (p >= kHeapMemBeg && p < kHeapMemEnd)
+ mem[MemHeap] += rss;
+ else if (p >= kLoAppMemBeg && p < kLoAppMemEnd)
+ mem[file ? MemFile : MemMmap] += rss;
+ else if (p >= kHiAppMemBeg && p < kHiAppMemEnd)
+ mem[file ? MemFile : MemMmap] += rss;
#else
-ScopedInRtl::ScopedInRtl() {
-}
-
-ScopedInRtl::~ScopedInRtl() {
-}
+ else if (p >= kAppMemBeg && p < kAppMemEnd)
+ mem[file ? MemFile : MemMmap] += rss;
#endif
-
-void FillProfileCallback(uptr start, uptr rss, bool file,
- uptr *mem, uptr stats_size) {
- CHECK_EQ(7, stats_size);
- mem[6] += rss; // total
- start >>= 40;
- if (start < 0x10) // shadow
- mem[0] += rss;
- else if (start >= 0x20 && start < 0x30) // compat modules
- mem[file ? 1 : 2] += rss;
- else if (start >= 0x7e) // modules
- mem[file ? 1 : 2] += rss;
- else if (start >= 0x60 && start < 0x62) // traces
- mem[3] += rss;
- else if (start >= 0x7d && start < 0x7e) // heap
- mem[4] += rss;
- else // other
- mem[5] += rss;
+ else if (p >= kTraceMemBeg && p < kTraceMemEnd)
+ mem[MemTrace] += rss;
+ else
+ mem[MemOther] += rss;
}
-void WriteMemoryProfile(char *buf, uptr buf_size) {
- uptr mem[7] = {};
+void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
+ uptr mem[MemCount] = {};
__sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7);
- char *buf_pos = buf;
- char *buf_end = buf + buf_size;
- buf_pos += internal_snprintf(buf_pos, buf_end - buf_pos,
- "RSS %zd MB: shadow:%zd file:%zd mmap:%zd trace:%zd heap:%zd other:%zd\n",
- mem[6] >> 20, mem[0] >> 20, mem[1] >> 20, mem[2] >> 20,
- mem[3] >> 20, mem[4] >> 20, mem[5] >> 20);
- struct mallinfo mi = __libc_mallinfo();
- buf_pos += internal_snprintf(buf_pos, buf_end - buf_pos,
- "mallinfo: arena=%d mmap=%d fordblks=%d keepcost=%d\n",
- mi.arena >> 20, mi.hblkhd >> 20, mi.fordblks >> 20, mi.keepcost >> 20);
+ StackDepotStats *stacks = StackDepotGetStats();
+ internal_snprintf(buf, buf_size,
+ "RSS %zd MB: shadow:%zd meta:%zd file:%zd mmap:%zd"
+ " trace:%zd heap:%zd other:%zd stacks=%zd[%zd] nthr=%zd/%zd\n",
+ mem[MemTotal] >> 20, mem[MemShadow] >> 20, mem[MemMeta] >> 20,
+ mem[MemFile] >> 20, mem[MemMmap] >> 20, mem[MemTrace] >> 20,
+ mem[MemHeap] >> 20, mem[MemOther] >> 20,
+ stacks->allocated >> 20, stacks->n_uniq_ids,
+ nlive, nthread);
}
-uptr GetRSS() {
- uptr mem[7] = {};
- __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7);
- return mem[6];
-}
-
-
+#if SANITIZER_LINUX
void FlushShadowMemoryCallback(
const SuspendedThreadsList &suspended_threads_list,
void *argument) {
- FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg);
+ FlushUnneededShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
}
+#endif
void FlushShadowMemory() {
+#if SANITIZER_LINUX
StopTheWorld(FlushShadowMemoryCallback, 0);
-}
-
-#ifndef TSAN_GO
-static void ProtectRange(uptr beg, uptr end) {
- ScopedInRtl in_rtl;
- CHECK_LE(beg, end);
- if (beg == end)
- return;
- if (beg != (uptr)Mprotect(beg, end - beg)) {
- Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
- Printf("FATAL: Make sure you are not using unlimited stack\n");
- Die();
- }
-}
#endif
+}
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
// Mark shadow for .rodata sections with the special kShadowRodata marker.
// Accesses to .rodata can't race, so this saves time, memory and trace space.
static void MapRodata() {
#endif
if (tmpdir == 0)
return;
- char filename[256];
- internal_snprintf(filename, sizeof(filename), "%s/tsan.rodata.%d",
+ char name[256];
+ internal_snprintf(name, sizeof(name), "%s/tsan.rodata.%d",
tmpdir, (int)internal_getpid());
- uptr openrv = internal_open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
+ uptr openrv = internal_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (internal_iserror(openrv))
return;
+ internal_unlink(name); // Unlink it now, so that we can reuse the buffer.
fd_t fd = openrv;
// Fill the file with kShadowRodata.
const uptr kMarkerSize = 512 * 1024 / sizeof(u64);
InternalScopedBuffer<u64> marker(kMarkerSize);
- for (u64 *p = marker.data(); p < marker.data() + kMarkerSize; p++)
+ // volatile to prevent insertion of memset
+ for (volatile u64 *p = marker.data(); p < marker.data() + kMarkerSize; p++)
*p = kShadowRodata;
internal_write(fd, marker.data(), marker.size());
// Map the file into memory.
- uptr page = internal_mmap(0, kPageSize, PROT_READ | PROT_WRITE,
+ uptr page = internal_mmap(0, GetPageSizeCached(), PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
if (internal_iserror(page)) {
internal_close(fd);
- internal_unlink(filename);
return;
}
// Map the file into shadow of .rodata sections.
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
uptr start, end, offset, prot;
- char name[128];
+ // Reusing the buffer 'name'.
while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name), &prot)) {
if (name[0] != 0 && name[0] != '['
&& (prot & MemoryMappingLayout::kProtectionRead)
}
}
internal_close(fd);
- internal_unlink(filename);
}
-void InitializeShadowMemory() {
- uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
- kLinuxShadowEnd - kLinuxShadowBeg);
- if (shadow != kLinuxShadowBeg) {
- Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
- Printf("FATAL: Make sure to compile with -fPIE and "
- "to link with -pie (%p, %p).\n", shadow, kLinuxShadowBeg);
- Die();
- }
- const uptr kClosedLowBeg = 0x200000;
- const uptr kClosedLowEnd = kLinuxShadowBeg - 1;
- const uptr kClosedMidBeg = kLinuxShadowEnd + 1;
- const uptr kClosedMidEnd = min(kLinuxAppMemBeg, kTraceMemBegin);
- ProtectRange(kClosedLowBeg, kClosedLowEnd);
- ProtectRange(kClosedMidBeg, kClosedMidEnd);
- DPrintf("kClosedLow %zx-%zx (%zuGB)\n",
- kClosedLowBeg, kClosedLowEnd, (kClosedLowEnd - kClosedLowBeg) >> 30);
- DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
- kLinuxShadowBeg, kLinuxShadowEnd,
- (kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
- DPrintf("kClosedMid %zx-%zx (%zuGB)\n",
- kClosedMidBeg, kClosedMidEnd, (kClosedMidEnd - kClosedMidBeg) >> 30);
- DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n",
- kLinuxAppMemBeg, kLinuxAppMemEnd,
- (kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
- DPrintf("stack %zx\n", (uptr)&shadow);
-
+void InitializeShadowMemoryPlatform() {
MapRodata();
}
-#endif
-
-static uptr g_data_start;
-static uptr g_data_end;
-
-#ifndef TSAN_GO
-static void CheckPIE() {
- // Ensure that the binary is indeed compiled with -pie.
- MemoryMappingLayout proc_maps(true);
- uptr start, end;
- if (proc_maps.Next(&start, &end,
- /*offset*/0, /*filename*/0, /*filename_size*/0,
- /*protection*/0)) {
- if ((u64)start < kLinuxAppMemBeg) {
- Printf("FATAL: ThreadSanitizer can not mmap the shadow memory ("
- "something is mapped at 0x%zx < 0x%zx)\n",
- start, kLinuxAppMemBeg);
- Printf("FATAL: Make sure to compile with -fPIE"
- " and to link with -pie.\n");
- Die();
- }
- }
-}
static void InitDataSeg() {
MemoryMappingLayout proc_maps(true);
uptr start, end, offset;
char name[128];
+#if SANITIZER_FREEBSD
+ // On FreeBSD BSS is usually the last block allocated within the
+ // low range and heap is the last block allocated within the range
+ // 0x800000000-0x8ffffffff.
+ while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name),
+ /*protection*/ 0)) {
+ DPrintf("%p-%p %p %s\n", start, end, offset, name);
+ if ((start & 0xffff00000000ULL) == 0 && (end & 0xffff00000000ULL) == 0 &&
+ name[0] == '\0') {
+ g_data_start = start;
+ g_data_end = end;
+ }
+ }
+#else
bool prev_is_data = false;
while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name),
/*protection*/ 0)) {
g_data_end = end;
prev_is_data = is_data;
}
+#endif
DPrintf("guessed data_start=%p data_end=%p\n", g_data_start, g_data_end);
CHECK_LT(g_data_start, g_data_end);
CHECK_GE((uptr)&g_data_start, g_data_start);
CHECK_LT((uptr)&g_data_start, g_data_end);
}
-#endif // #ifndef TSAN_GO
-
-static rlim_t getlim(int res) {
- rlimit rlim;
- CHECK_EQ(0, getrlimit(res, &rlim));
- return rlim.rlim_cur;
-}
-
-static void setlim(int res, rlim_t lim) {
- // The following magic is to prevent clang from replacing it with memset.
- volatile rlimit rlim;
- rlim.rlim_cur = lim;
- rlim.rlim_max = lim;
- setrlimit(res, (rlimit*)&rlim);
-}
+#endif // #ifndef SANITIZER_GO
-const char *InitializePlatform() {
- void *p = 0;
- if (sizeof(p) == 8) {
- // Disable core dumps, dumping of 16TB usually takes a bit long.
- setlim(RLIMIT_CORE, 0);
- }
+void InitializePlatform() {
+ DisableCoreDumperIfNecessary();
// Go maps shadow memory lazily and works fine with limited address space.
// Unlimited stack is not a problem as well, because the executable
// TSan doesn't play well with unlimited stack size (as stack
// overlaps with shadow memory). If we detect unlimited stack size,
// we re-exec the program with limited stack size as a best effort.
- if (getlim(RLIMIT_STACK) == (rlim_t)-1) {
+ if (StackSizeIsUnlimited()) {
const uptr kMaxStackSize = 32 * 1024 * 1024;
- Report("WARNING: Program is run with unlimited stack size, which "
- "wouldn't work with ThreadSanitizer.\n");
- Report("Re-execing with stack size limited to %zd bytes.\n",
- kMaxStackSize);
+ VReport(1, "Program is run with unlimited stack size, which wouldn't "
+ "work with ThreadSanitizer.\n"
+ "Re-execing with stack size limited to %zd bytes.\n",
+ kMaxStackSize);
SetStackSizeLimitInBytes(kMaxStackSize);
reexec = true;
}
- if (getlim(RLIMIT_AS) != (rlim_t)-1) {
+ if (!AddressSpaceIsUnlimited()) {
Report("WARNING: Program is run with limited virtual address space,"
" which wouldn't work with ThreadSanitizer.\n");
Report("Re-execing with unlimited virtual address space.\n");
- setlim(RLIMIT_AS, -1);
+ SetAddressSpaceUnlimited();
reexec = true;
}
if (reexec)
ReExec();
}
-#ifndef TSAN_GO
- CheckPIE();
+#ifndef SANITIZER_GO
+ CheckAndProtect();
InitTlsSize();
InitDataSeg();
#endif
- return GetEnv(kTsanOptionsEnv);
}
bool IsGlobalVar(uptr addr) {
return g_data_start && addr >= g_data_start && addr < g_data_end;
}
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
// Extract file descriptors passed to glibc internal __res_iclose function.
// This is required to properly "close" the fds, because we do not see internal
// closes within glibc. The code is a pure hack.
int ExtractResolvFDs(void *state, int *fds, int nfd) {
+#if SANITIZER_LINUX
int cnt = 0;
__res_state *statp = (__res_state*)state;
for (int i = 0; i < MAXNS && cnt < nfd; i++) {
fds[cnt++] = statp->_u._ext.nssocks[i];
}
return cnt;
+#else
+ return 0;
+#endif
}
// Extract file descriptors passed via UNIX domain sockets.
}
return res;
}
+
+// Note: this function runs with async signals enabled,
+// so it must not touch any tsan state.
+int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
+ void *abstime), void *c, void *m, void *abstime,
+ void(*cleanup)(void *arg), void *arg) {
+ // pthread_cleanup_push/pop are hardcore macros mess.
+ // We can't intercept nor call them w/o including pthread.h.
+ int res;
+ pthread_cleanup_push(cleanup, arg);
+ res = fn(c, m, abstime);
+ pthread_cleanup_pop(0);
+ return res;
+}
#endif
+#ifndef SANITIZER_GO
+void ReplaceSystemMalloc() { }
+#endif
} // namespace __tsan
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_MAC
+#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_posix.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
namespace __tsan {
-ScopedInRtl::ScopedInRtl() {
+#ifndef SANITIZER_GO
+static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) {
+ atomic_uintptr_t *a = (atomic_uintptr_t *)dst;
+ void *val = (void *)atomic_load_relaxed(a);
+ atomic_signal_fence(memory_order_acquire); // Turns the previous load into
+ // acquire wrt signals.
+ if (UNLIKELY(val == nullptr)) {
+ val = (void *)internal_mmap(nullptr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ CHECK(val);
+ void *cmp = nullptr;
+ if (!atomic_compare_exchange_strong(a, (uintptr_t *)&cmp, (uintptr_t)val,
+ memory_order_acq_rel)) {
+ internal_munmap(val, size);
+ val = cmp;
+ }
+ }
+ return val;
+}
+
+// On OS X, accessing TLVs via __thread or manually by using pthread_key_* is
+// problematic, because there are several places where interceptors are called
+// when TLVs are not accessible (early process startup, thread cleanup, ...).
+// The following provides a "poor man's TLV" implementation, where we use the
+// shadow memory of the pointer returned by pthread_self() to store a pointer to
+// the ThreadState object. The main thread's ThreadState pointer is stored
+// separately in a static variable, because we need to access it even before the
+// shadow memory is set up.
+static uptr main_thread_identity = 0;
+static ThreadState *main_thread_state = nullptr;
+
+ThreadState *cur_thread() {
+ ThreadState **fake_tls;
+ uptr thread_identity = (uptr)pthread_self();
+ if (thread_identity == main_thread_identity || main_thread_identity == 0) {
+ fake_tls = &main_thread_state;
+ } else {
+ fake_tls = (ThreadState **)MemToShadow(thread_identity);
+ }
+ ThreadState *thr = (ThreadState *)SignalSafeGetOrAllocate(
+ (uptr *)fake_tls, sizeof(ThreadState));
+ return thr;
}
-ScopedInRtl::~ScopedInRtl() {
+// TODO(kuba.brecka): This is not async-signal-safe. In particular, we call
+// munmap first and then clear `fake_tls`; if we receive a signal in between,
+// handler will try to access the unmapped ThreadState.
+void cur_thread_finalize() {
+ uptr thread_identity = (uptr)pthread_self();
+ CHECK_NE(thread_identity, main_thread_identity);
+ ThreadState **fake_tls = (ThreadState **)MemToShadow(thread_identity);
+ internal_munmap(*fake_tls, sizeof(ThreadState));
+ *fake_tls = nullptr;
}
+#endif
uptr GetShadowMemoryConsumption() {
return 0;
void FlushShadowMemory() {
}
-#ifndef TSAN_GO
-void InitializeShadowMemory() {
- uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
- kLinuxShadowEnd - kLinuxShadowBeg);
- if (shadow != kLinuxShadowBeg) {
- Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
- Printf("FATAL: Make sure to compile with -fPIE and "
- "to link with -pie.\n");
- Die();
+void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
+}
+
+#ifndef SANITIZER_GO
+void InitializeShadowMemoryPlatform() { }
+
+// On OS X, GCD worker threads are created without a call to pthread_create. We
+// need to properly register these threads with ThreadCreate and ThreadStart.
+// These threads don't have a parent thread, as they are created "spuriously".
+// We're using a libpthread API that notifies us about a newly created thread.
+// The `thread == pthread_self()` check indicates this is actually a worker
+// thread. If it's just a regular thread, this hook is called on the parent
+// thread.
+typedef void (*pthread_introspection_hook_t)(unsigned int event,
+ pthread_t thread, void *addr,
+ size_t size);
+extern "C" pthread_introspection_hook_t pthread_introspection_hook_install(
+ pthread_introspection_hook_t hook);
+static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE = 1;
+static const uptr PTHREAD_INTROSPECTION_THREAD_DESTROY = 4;
+static pthread_introspection_hook_t prev_pthread_introspection_hook;
+static void my_pthread_introspection_hook(unsigned int event, pthread_t thread,
+ void *addr, size_t size) {
+ if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) {
+ if (thread == pthread_self()) {
+ // The current thread is a newly created GCD worker thread.
+ ThreadState *parent_thread_state = nullptr; // No parent.
+ int tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true);
+ CHECK_NE(tid, 0);
+ ThreadState *thr = cur_thread();
+ ThreadStart(thr, tid, GetTid());
+ }
+ } else if (event == PTHREAD_INTROSPECTION_THREAD_DESTROY) {
+ ThreadState *thr = cur_thread();
+ if (thr->tctx && thr->tctx->parent_tid == kInvalidTid) {
+ DestroyThreadState();
+ }
}
- DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
- kLinuxShadowBeg, kLinuxShadowEnd,
- (kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
- DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n",
- kLinuxAppMemBeg, kLinuxAppMemEnd,
- (kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
+
+ if (prev_pthread_introspection_hook != nullptr)
+ prev_pthread_introspection_hook(event, thread, addr, size);
}
#endif
-const char *InitializePlatform() {
- void *p = 0;
- if (sizeof(p) == 8) {
- // Disable core dumps, dumping of 16TB usually takes a bit long.
- // The following magic is to prevent clang from replacing it with memset.
- volatile rlimit lim;
- lim.rlim_cur = 0;
- lim.rlim_max = 0;
- setrlimit(RLIMIT_CORE, (rlimit*)&lim);
- }
+void InitializePlatform() {
+ DisableCoreDumperIfNecessary();
+#ifndef SANITIZER_GO
+ CheckAndProtect();
+
+ CHECK_EQ(main_thread_identity, 0);
+ main_thread_identity = (uptr)pthread_self();
- return GetEnv(kTsanOptionsEnv);
+ prev_pthread_introspection_hook =
+ pthread_introspection_hook_install(&my_pthread_introspection_hook);
+#endif
+}
+
+#ifndef SANITIZER_GO
+// Note: this function runs with async signals enabled,
+// so it must not touch any tsan state.
+int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
+ void *abstime), void *c, void *m, void *abstime,
+ void(*cleanup)(void *arg), void *arg) {
+ // pthread_cleanup_push/pop are hardcore macros mess.
+ // We can't intercept nor call them w/o including pthread.h.
+ int res;
+ pthread_cleanup_push(cleanup, arg);
+ res = fn(c, m, abstime);
+ pthread_cleanup_pop(0);
+ return res;
}
+#endif
-void FinalizePlatform() {
- fflush(0);
+bool IsGlobalVar(uptr addr) {
+ return false;
}
} // namespace __tsan
--- /dev/null
+//===-- tsan_platform_posix.cc --------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// POSIX-specific code.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_POSIX
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+#ifndef SANITIZER_GO
+void InitializeShadowMemory() {
+ // Map memory shadow.
+ uptr shadow =
+ (uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg, "shadow");
+ if (shadow != kShadowBeg) {
+ Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
+ Printf("FATAL: Make sure to compile with -fPIE and "
+ "to link with -pie (%p, %p).\n", shadow, kShadowBeg);
+ Die();
+ }
+ // This memory range is used for thread stacks and large user mmaps.
+ // Frequently a thread uses only a small part of stack and similarly
+ // a program uses a small part of large mmap. On some programs
+ // we see 20% memory usage reduction without huge pages for this range.
+ // FIXME: don't use constants here.
+#if defined(__x86_64__)
+ const uptr kMadviseRangeBeg = 0x7f0000000000ull;
+ const uptr kMadviseRangeSize = 0x010000000000ull;
+#elif defined(__mips64)
+ const uptr kMadviseRangeBeg = 0xff00000000ull;
+ const uptr kMadviseRangeSize = 0x0100000000ull;
+#elif defined(__aarch64__)
+ const uptr kMadviseRangeBeg = 0x7e00000000ull;
+ const uptr kMadviseRangeSize = 0x0100000000ull;
+#endif
+ NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg),
+ kMadviseRangeSize * kShadowMultiplier);
+ // Meta shadow is compressing and we don't flush it,
+ // so it makes sense to mark it as NOHUGEPAGE to not over-allocate memory.
+ // On one program it reduces memory consumption from 5GB to 2.5GB.
+ NoHugePagesInRegion(kMetaShadowBeg, kMetaShadowEnd - kMetaShadowBeg);
+ if (common_flags()->use_madv_dontdump)
+ DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
+ DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
+ kShadowBeg, kShadowEnd,
+ (kShadowEnd - kShadowBeg) >> 30);
+
+ // Map meta shadow.
+ uptr meta_size = kMetaShadowEnd - kMetaShadowBeg;
+ uptr meta =
+ (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size, "meta shadow");
+ if (meta != kMetaShadowBeg) {
+ Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
+ Printf("FATAL: Make sure to compile with -fPIE and "
+ "to link with -pie (%p, %p).\n", meta, kMetaShadowBeg);
+ Die();
+ }
+ if (common_flags()->use_madv_dontdump)
+ DontDumpShadowMemory(meta, meta_size);
+ DPrintf("meta shadow: %zx-%zx (%zuGB)\n",
+ meta, meta + meta_size, meta_size >> 30);
+
+ InitializeShadowMemoryPlatform();
+}
+
+static void ProtectRange(uptr beg, uptr end) {
+ CHECK_LE(beg, end);
+ if (beg == end)
+ return;
+ if (beg != (uptr)MmapNoAccess(beg, end - beg)) {
+ Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
+ Printf("FATAL: Make sure you are not using unlimited stack\n");
+ Die();
+ }
+}
+
+void CheckAndProtect() {
+ // Ensure that the binary is indeed compiled with -pie.
+ MemoryMappingLayout proc_maps(true);
+ uptr p, end, prot;
+ while (proc_maps.Next(&p, &end, 0, 0, 0, &prot)) {
+ if (IsAppMem(p))
+ continue;
+ if (p >= kHeapMemEnd &&
+ p < HeapEnd())
+ continue;
+ if (prot == 0) // Zero page or mprotected.
+ continue;
+ if (p >= kVdsoBeg) // vdso
+ break;
+ Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end);
+ Die();
+ }
+
+ ProtectRange(kLoAppMemEnd, kShadowBeg);
+ ProtectRange(kShadowEnd, kMetaShadowBeg);
+ ProtectRange(kMetaShadowEnd, kTraceMemBeg);
+ // Memory for traces is mapped lazily in MapThreadTrace.
+ // Protect the whole range for now, so that user does not map something here.
+ ProtectRange(kTraceMemBeg, kTraceMemEnd);
+ ProtectRange(kTraceMemEnd, kHeapMemBeg);
+ ProtectRange(HeapEnd(), kHiAppMemBeg);
+}
+#endif
+
+} // namespace __tsan
+
+#endif // SANITIZER_POSIX
namespace __tsan {
-ScopedInRtl::ScopedInRtl() {
-}
-
-ScopedInRtl::~ScopedInRtl() {
-}
-
uptr GetShadowMemoryConsumption() {
return 0;
}
void FlushShadowMemory() {
}
-const char *InitializePlatform() {
- return GetEnv(kTsanOptionsEnv);
+void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
}
-void FinalizePlatform() {
- fflush(0);
+void InitializePlatform() {
}
} // namespace __tsan
#include "tsan_report.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_stacktrace_printer.h"
namespace __tsan {
-class Decorator: private __sanitizer::AnsiColorDecorator {
+ReportStack::ReportStack() : frames(nullptr), suppressable(false) {}
+
+ReportStack *ReportStack::New() {
+ void *mem = internal_alloc(MBlockReportStack, sizeof(ReportStack));
+ return new(mem) ReportStack();
+}
+
+ReportLocation::ReportLocation(ReportLocationType type)
+ : type(type), global(), heap_chunk_start(0), heap_chunk_size(0), tid(0),
+ fd(0), suppressable(false), stack(nullptr) {}
+
+ReportLocation *ReportLocation::New(ReportLocationType type) {
+ void *mem = internal_alloc(MBlockReportStack, sizeof(ReportLocation));
+ return new(mem) ReportLocation(type);
+}
+
+class Decorator: public __sanitizer::SanitizerCommonDecorator {
public:
- Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
+ Decorator() : SanitizerCommonDecorator() { }
const char *Warning() { return Red(); }
const char *EndWarning() { return Default(); }
const char *Access() { return Blue(); }
, locs(MBlockReportLoc)
, mutexes(MBlockReportMutex)
, threads(MBlockReportThread)
+ , unique_tids(MBlockReportThread)
, sleep()
, count() {
}
// FIXME(dvyukov): it must be leaking a lot of memory.
}
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
const int kThreadBufSize = 32;
const char *thread_name(char *buf, int tid) {
return "data race on vptr (ctor/dtor vs virtual call)";
if (typ == ReportTypeUseAfterFree)
return "heap-use-after-free";
+ if (typ == ReportTypeVptrUseAfterFree)
+ return "heap-use-after-free (virtual call vs free)";
if (typ == ReportTypeThreadLeak)
return "thread leak";
if (typ == ReportTypeMutexDestroyLocked)
return "destroy of a locked mutex";
+ if (typ == ReportTypeMutexDoubleLock)
+ return "double lock of a mutex";
+ if (typ == ReportTypeMutexBadUnlock)
+ return "unlock of an unlocked mutex (or by a wrong thread)";
+ if (typ == ReportTypeMutexBadReadLock)
+ return "read lock of a write locked mutex";
+ if (typ == ReportTypeMutexBadReadUnlock)
+ return "read unlock of a write locked mutex";
if (typ == ReportTypeSignalUnsafe)
return "signal-unsafe call inside of a signal";
if (typ == ReportTypeErrnoInSignal)
return "signal handler spoils errno";
+ if (typ == ReportTypeDeadlock)
+ return "lock-order-inversion (potential deadlock)";
return "";
}
+#if SANITIZER_MAC
+static const char *const kInterposedFunctionPrefix = "wrap_";
+#else
+static const char *const kInterposedFunctionPrefix = "__interceptor_";
+#endif
+
void PrintStack(const ReportStack *ent) {
- if (ent == 0) {
+ if (ent == 0 || ent->frames == 0) {
Printf(" [failed to restore the stack]\n\n");
return;
}
- for (int i = 0; ent; ent = ent->next, i++) {
- Printf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line);
- if (ent->col)
- Printf(":%d", ent->col);
- if (ent->module && ent->offset)
- Printf(" (%s+%p)\n", ent->module, (void*)ent->offset);
- else
- Printf(" (%p)\n", (void*)ent->pc);
+ SymbolizedStack *frame = ent->frames;
+ for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {
+ InternalScopedString res(2 * GetPageSizeCached());
+ RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info,
+ common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix, kInterposedFunctionPrefix);
+ Printf("%s\n", res.data());
}
Printf("\n");
}
bool print_stack = false;
Printf("%s", d.Location());
if (loc->type == ReportLocationGlobal) {
- Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
- loc->name, loc->size, loc->addr, loc->module, loc->offset);
+ const DataInfo &global = loc->global;
+ if (global.size != 0)
+ Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
+ global.name, global.size, global.start,
+ StripModuleName(global.module), global.module_offset);
+ else
+ Printf(" Location is global '%s' at %p (%s+%p)\n\n", global.name,
+ global.start, StripModuleName(global.module),
+ global.module_offset);
} else if (loc->type == ReportLocationHeap) {
char thrbuf[kThreadBufSize];
Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
- loc->size, loc->addr, thread_name(thrbuf, loc->tid));
+ loc->heap_chunk_size, loc->heap_chunk_start,
+ thread_name(thrbuf, loc->tid));
print_stack = true;
} else if (loc->type == ReportLocationStack) {
Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
PrintStack(loc->stack);
}
+static void PrintMutexShort(const ReportMutex *rm, const char *after) {
+ Decorator d;
+ Printf("%sM%zd%s%s", d.Mutex(), rm->id, d.EndMutex(), after);
+}
+
+static void PrintMutexShortWithAddress(const ReportMutex *rm,
+ const char *after) {
+ Decorator d;
+ Printf("%sM%zd (%p)%s%s", d.Mutex(), rm->id, rm->addr, d.EndMutex(), after);
+}
+
static void PrintMutex(const ReportMutex *rm) {
Decorator d;
if (rm->destroyed) {
Printf("%s", d.EndMutex());
} else {
Printf("%s", d.Mutex());
- Printf(" Mutex M%llu created at:\n", rm->id);
+ Printf(" Mutex M%llu (%p) created at:\n", rm->id, rm->addr);
Printf("%s", d.EndMutex());
PrintStack(rm->stack);
}
return 0;
}
-ReportStack *SkipTsanInternalFrames(ReportStack *ent) {
- while (FrameIsInternal(ent) && ent->next)
- ent = ent->next;
- return ent;
+static bool FrameIsInternal(const SymbolizedStack *frame) {
+ if (frame == 0)
+ return false;
+ const char *file = frame->info.file;
+ return file != 0 &&
+ (internal_strstr(file, "tsan_interceptors.cc") ||
+ internal_strstr(file, "sanitizer_common_interceptors.inc") ||
+ internal_strstr(file, "tsan_interface_"));
+}
+
+static SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) {
+ while (FrameIsInternal(frames) && frames->next)
+ frames = frames->next;
+ return frames;
}
void PrintReport(const ReportDesc *rep) {
(int)internal_getpid());
Printf("%s", d.EndWarning());
- for (uptr i = 0; i < rep->stacks.Size(); i++) {
- if (i)
- Printf(" and:\n");
- PrintStack(rep->stacks[i]);
+ if (rep->typ == ReportTypeDeadlock) {
+ char thrbuf[kThreadBufSize];
+ Printf(" Cycle in lock order graph: ");
+ for (uptr i = 0; i < rep->mutexes.Size(); i++)
+ PrintMutexShortWithAddress(rep->mutexes[i], " => ");
+ PrintMutexShort(rep->mutexes[0], "\n\n");
+ CHECK_GT(rep->mutexes.Size(), 0U);
+ CHECK_EQ(rep->mutexes.Size() * (flags()->second_deadlock_stack ? 2 : 1),
+ rep->stacks.Size());
+ for (uptr i = 0; i < rep->mutexes.Size(); i++) {
+ Printf(" Mutex ");
+ PrintMutexShort(rep->mutexes[(i + 1) % rep->mutexes.Size()],
+ " acquired here while holding mutex ");
+ PrintMutexShort(rep->mutexes[i], " in ");
+ Printf("%s", d.ThreadDescription());
+ Printf("%s:\n", thread_name(thrbuf, rep->unique_tids[i]));
+ Printf("%s", d.EndThreadDescription());
+ if (flags()->second_deadlock_stack) {
+ PrintStack(rep->stacks[2*i]);
+ Printf(" Mutex ");
+ PrintMutexShort(rep->mutexes[i],
+ " previously acquired by the same thread here:\n");
+ PrintStack(rep->stacks[2*i+1]);
+ } else {
+ PrintStack(rep->stacks[i]);
+ if (i == 0)
+ Printf(" Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "
+ "to get more informative warning message\n\n");
+ }
+ }
+ } else {
+ for (uptr i = 0; i < rep->stacks.Size(); i++) {
+ if (i)
+ Printf(" and:\n");
+ PrintStack(rep->stacks[i]);
+ }
}
for (uptr i = 0; i < rep->mops.Size(); i++)
for (uptr i = 0; i < rep->locs.Size(); i++)
PrintLocation(rep->locs[i]);
- for (uptr i = 0; i < rep->mutexes.Size(); i++)
- PrintMutex(rep->mutexes[i]);
+ if (rep->typ != ReportTypeDeadlock) {
+ for (uptr i = 0; i < rep->mutexes.Size(); i++)
+ PrintMutex(rep->mutexes[i]);
+ }
for (uptr i = 0; i < rep->threads.Size(); i++)
PrintThread(rep->threads[i]);
if (rep->typ == ReportTypeThreadLeak && rep->count > 1)
Printf(" And %d more similar thread leaks.\n\n", rep->count - 1);
- if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep)))
- ReportErrorSummary(rep_typ_str, ent->file, ent->line, ent->func);
+ if (ReportStack *stack = ChooseSummaryStack(rep)) {
+ if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames))
+ ReportErrorSummary(rep_typ_str, frame->info);
+ }
Printf("==================\n");
}
-#else // #ifndef TSAN_GO
+#else // #ifndef SANITIZER_GO
const int kMainThreadId = 1;
void PrintStack(const ReportStack *ent) {
- if (ent == 0) {
+ if (ent == 0 || ent->frames == 0) {
Printf(" [failed to restore the stack]\n");
return;
}
- for (int i = 0; ent; ent = ent->next, i++) {
- Printf(" %s()\n %s:%d +0x%zx\n",
- ent->func, ent->file, ent->line, (void*)ent->offset);
+ SymbolizedStack *frame = ent->frames;
+ for (int i = 0; frame; frame = frame->next, i++) {
+ const AddressInfo &info = frame->info;
+ Printf(" %s()\n %s:%d +0x%zx\n", info.function,
+ StripPathPrefix(info.file, common_flags()->strip_path_prefix),
+ info.line, (void *)info.module_offset);
}
}
void PrintReport(const ReportDesc *rep) {
Printf("==================\n");
- Printf("WARNING: DATA RACE");
- for (uptr i = 0; i < rep->mops.Size(); i++)
- PrintMop(rep->mops[i], i == 0);
- for (uptr i = 0; i < rep->threads.Size(); i++)
- PrintThread(rep->threads[i]);
+ if (rep->typ == ReportTypeRace) {
+ Printf("WARNING: DATA RACE");
+ for (uptr i = 0; i < rep->mops.Size(); i++)
+ PrintMop(rep->mops[i], i == 0);
+ for (uptr i = 0; i < rep->threads.Size(); i++)
+ PrintThread(rep->threads[i]);
+ } else if (rep->typ == ReportTypeDeadlock) {
+ Printf("WARNING: DEADLOCK\n");
+ for (uptr i = 0; i < rep->mutexes.Size(); i++) {
+ Printf("Goroutine %d lock mutex %d while holding mutex %d:\n",
+ 999, rep->mutexes[i]->id,
+ rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
+ PrintStack(rep->stacks[2*i]);
+ Printf("\n");
+ Printf("Mutex %d was previously locked here:\n",
+ rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
+ PrintStack(rep->stacks[2*i + 1]);
+ Printf("\n");
+ }
+ }
Printf("==================\n");
}
#ifndef TSAN_REPORT_H
#define TSAN_REPORT_H
+#include "sanitizer_common/sanitizer_symbolizer.h"
#include "tsan_defs.h"
#include "tsan_vector.h"
ReportTypeRace,
ReportTypeVptrRace,
ReportTypeUseAfterFree,
+ ReportTypeVptrUseAfterFree,
ReportTypeThreadLeak,
ReportTypeMutexDestroyLocked,
+ ReportTypeMutexDoubleLock,
+ ReportTypeMutexBadUnlock,
+ ReportTypeMutexBadReadLock,
+ ReportTypeMutexBadReadUnlock,
ReportTypeSignalUnsafe,
- ReportTypeErrnoInSignal
+ ReportTypeErrnoInSignal,
+ ReportTypeDeadlock
};
struct ReportStack {
- ReportStack *next;
- char *module;
- uptr offset;
- uptr pc;
- char *func;
- char *file;
- int line;
- int col;
+ SymbolizedStack *frames;
+ bool suppressable;
+ static ReportStack *New();
+
+ private:
+ ReportStack();
};
struct ReportMopMutex {
struct ReportLocation {
ReportLocationType type;
- uptr addr;
- uptr size;
- char *module;
- uptr offset;
+ DataInfo global;
+ uptr heap_chunk_start;
+ uptr heap_chunk_size;
int tid;
int fd;
- char *name;
- char *file;
- int line;
+ bool suppressable;
ReportStack *stack;
+
+ static ReportLocation *New(ReportLocationType type);
+ private:
+ explicit ReportLocation(ReportLocationType type);
};
struct ReportThread {
struct ReportMutex {
u64 id;
+ uptr addr;
bool destroyed;
ReportStack *stack;
};
Vector<ReportLocation*> locs;
Vector<ReportMutex*> mutexes;
Vector<ReportThread*> threads;
+ Vector<int> unique_tids;
ReportStack *sleep;
int count;
#include "tsan_mman.h"
#include "tsan_suppressions.h"
#include "tsan_symbolize.h"
+#include "ubsan/ubsan_init.h"
+
+#ifdef __SSE3__
+// <emmintrin.h> transitively includes <stdlib.h>,
+// and it's prohibited to include std headers into tsan runtime.
+// So we do this dirty trick.
+#define _MM_MALLOC_H_INCLUDED
+#define __MM_MALLOC_H
+#include <emmintrin.h>
+typedef __m128i m128;
+#endif
volatile int __tsan_resumed = 0;
namespace __tsan {
-#ifndef TSAN_GO
+#if !defined(SANITIZER_GO) && !SANITIZER_MAC
THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64);
#endif
static char ctx_placeholder[sizeof(Context)] ALIGNED(64);
+Context *ctx;
// Can be overriden by a front-end.
#ifdef TSAN_EXTERNAL_HOOKS
bool OnFinalize(bool failed);
+void OnInitialize();
#else
SANITIZER_INTERFACE_ATTRIBUTE
bool WEAK OnFinalize(bool failed) {
return failed;
}
+SANITIZER_INTERFACE_ATTRIBUTE
+void WEAK OnInitialize() {}
#endif
-static Context *ctx;
-Context *CTX() {
- return ctx;
-}
-
static char thread_registry_placeholder[sizeof(ThreadRegistry)];
static ThreadContextBase *CreateThreadContext(u32 tid) {
// Map thread trace when context is created.
- MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event));
- MapThreadTrace(GetThreadTraceHeader(tid), sizeof(Trace));
- new(ThreadTrace(tid)) Trace();
+ char name[50];
+ internal_snprintf(name, sizeof(name), "trace %u", tid);
+ MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event), name);
+ const uptr hdr = GetThreadTraceHeader(tid);
+ internal_snprintf(name, sizeof(name), "trace header %u", tid);
+ MapThreadTrace(hdr, sizeof(Trace), name);
+ new((void*)hdr) Trace();
+ // We are going to use only a small part of the trace with the default
+ // value of history_size. However, the constructor writes to the whole trace.
+ // Unmap the unused part.
+ uptr hdr_end = hdr + sizeof(Trace);
+ hdr_end -= sizeof(TraceHeader) * (kTraceParts - TraceParts());
+ hdr_end = RoundUp(hdr_end, GetPageSizeCached());
+ if (hdr_end < hdr + sizeof(Trace))
+ UnmapOrDie((void*)hdr_end, hdr + sizeof(Trace) - hdr_end);
void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext));
return new(mem) ThreadContext(tid);
}
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
static const u32 kThreadQuarantineSize = 16;
#else
static const u32 kThreadQuarantineSize = 64;
, nreported()
, nmissed_expected()
, thread_registry(new(thread_registry_placeholder) ThreadRegistry(
- CreateThreadContext, kMaxTid, kThreadQuarantineSize))
+ CreateThreadContext, kMaxTid, kThreadQuarantineSize, kMaxTidReuse))
+ , racy_mtx(MutexTypeRacy, StatMtxRacy)
, racy_stacks(MBlockRacyStacks)
, racy_addresses(MBlockRacyAddresses)
+ , fired_suppressions_mtx(MutexTypeFired, StatMtxFired)
, fired_suppressions(8) {
}
// The objects are allocated in TLS, so one may rely on zero-initialization.
ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
+ unsigned reuse_count,
uptr stk_addr, uptr stk_size,
uptr tls_addr, uptr tls_size)
: fast_state(tid, epoch)
// Do not touch these, rely on zero initialization,
// they may be accessed before the ctor.
// , ignore_reads_and_writes()
- // , in_rtl()
-#ifndef TSAN_GO
+ // , ignore_interceptors()
+ , clock(tid, reuse_count)
+#ifndef SANITIZER_GO
, jmp_bufs(MBlockJmpBuf)
#endif
, tid(tid)
, stk_addr(stk_addr)
, stk_size(stk_size)
, tls_addr(tls_addr)
- , tls_size(tls_size) {
+ , tls_size(tls_size)
+#ifndef SANITIZER_GO
+ , last_sleep_clock(tid)
+#endif
+{
}
+#ifndef SANITIZER_GO
static void MemoryProfiler(Context *ctx, fd_t fd, int i) {
uptr n_threads;
uptr n_running_threads;
ctx->thread_registry->GetNumberOfThreads(&n_threads, &n_running_threads);
InternalScopedBuffer<char> buf(4096);
- internal_snprintf(buf.data(), buf.size(), "%d: nthr=%d nlive=%d\n",
- i, n_threads, n_running_threads);
- internal_write(fd, buf.data(), internal_strlen(buf.data()));
- WriteMemoryProfile(buf.data(), buf.size());
- internal_write(fd, buf.data(), internal_strlen(buf.data()));
+ WriteMemoryProfile(buf.data(), buf.size(), n_threads, n_running_threads);
+ WriteToFile(fd, buf.data(), internal_strlen(buf.data()));
}
static void BackgroundThread(void *arg) {
- ScopedInRtl in_rtl;
- Context *ctx = CTX();
+ // This is a non-initialized non-user thread, nothing to see here.
+ // We don't use ScopedIgnoreInterceptors, because we want ignores to be
+ // enabled even when the thread function exits (e.g. during pthread thread
+ // shutdown code).
+ cur_thread()->ignore_interceptors++;
const u64 kMs2Ns = 1000 * 1000;
fd_t mprof_fd = kInvalidFd;
if (flags()->profile_memory && flags()->profile_memory[0]) {
- InternalScopedBuffer<char> filename(4096);
- internal_snprintf(filename.data(), filename.size(), "%s.%d",
- flags()->profile_memory, (int)internal_getpid());
- uptr openrv = OpenFile(filename.data(), true);
- if (internal_iserror(openrv)) {
- Printf("ThreadSanitizer: failed to open memory profile file '%s'\n",
- &filename[0]);
+ if (internal_strcmp(flags()->profile_memory, "stdout") == 0) {
+ mprof_fd = 1;
+ } else if (internal_strcmp(flags()->profile_memory, "stderr") == 0) {
+ mprof_fd = 2;
} else {
- mprof_fd = openrv;
+ InternalScopedString filename(kMaxPathLength);
+ filename.append("%s.%d", flags()->profile_memory, (int)internal_getpid());
+ fd_t fd = OpenFile(filename.data(), WrOnly);
+ if (fd == kInvalidFd) {
+ Printf("ThreadSanitizer: failed to open memory profile file '%s'\n",
+ &filename[0]);
+ } else {
+ mprof_fd = fd;
+ }
}
}
u64 last_flush = NanoTime();
uptr last_rss = 0;
- for (int i = 0; ; i++) {
- SleepForSeconds(1);
+ for (int i = 0;
+ atomic_load(&ctx->stop_background_thread, memory_order_relaxed) == 0;
+ i++) {
+ SleepForMillis(100);
u64 now = NanoTime();
// Flush memory if requested.
if (flags()->flush_memory_ms > 0) {
if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) {
- if (flags()->verbosity > 0)
- Printf("ThreadSanitizer: periodic memory flush\n");
+ VPrintf(1, "ThreadSanitizer: periodic memory flush\n");
FlushShadowMemory();
last_flush = NanoTime();
}
}
+ // GetRSS can be expensive on huge programs, so don't do it every 100ms.
if (flags()->memory_limit_mb > 0) {
uptr rss = GetRSS();
uptr limit = uptr(flags()->memory_limit_mb) << 20;
- if (flags()->verbosity > 0) {
- Printf("ThreadSanitizer: memory flush check"
- " RSS=%llu LAST=%llu LIMIT=%llu\n",
- (u64)rss>>20, (u64)last_rss>>20, (u64)limit>>20);
- }
+ VPrintf(1, "ThreadSanitizer: memory flush check"
+ " RSS=%llu LAST=%llu LIMIT=%llu\n",
+ (u64)rss >> 20, (u64)last_rss >> 20, (u64)limit >> 20);
if (2 * rss > limit + last_rss) {
- if (flags()->verbosity > 0)
- Printf("ThreadSanitizer: flushing memory due to RSS\n");
+ VPrintf(1, "ThreadSanitizer: flushing memory due to RSS\n");
FlushShadowMemory();
rss = GetRSS();
- if (flags()->verbosity > 0)
- Printf("ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20);
+ VPrintf(1, "ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20);
}
last_rss = rss;
}
if (mprof_fd != kInvalidFd)
MemoryProfiler(ctx, mprof_fd, i);
-#ifndef TSAN_GO
// Flush symbolizer cache if requested.
if (flags()->flush_symbolizer_ms > 0) {
u64 last = atomic_load(&ctx->last_symbolize_time_ns,
atomic_store(&ctx->last_symbolize_time_ns, 0, memory_order_relaxed);
}
}
-#endif
}
}
+static void StartBackgroundThread() {
+ ctx->background_thread = internal_start_thread(&BackgroundThread, 0);
+}
+
+#ifndef __mips__
+static void StopBackgroundThread() {
+ atomic_store(&ctx->stop_background_thread, 1, memory_order_relaxed);
+ internal_join_thread(ctx->background_thread);
+ ctx->background_thread = 0;
+}
+#endif
+#endif
+
void DontNeedShadowFor(uptr addr, uptr size) {
uptr shadow_beg = MemToShadow(addr);
uptr shadow_end = MemToShadow(addr + size);
}
void MapShadow(uptr addr, uptr size) {
- MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier);
+ // Global data is not 64K aligned, but there are no adjacent mappings,
+ // so we can get away with unaligned mapping.
+ // CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
+ MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier, "shadow");
+
+ // Meta shadow is 2:1, so tread carefully.
+ static bool data_mapped = false;
+ static uptr mapped_meta_end = 0;
+ uptr meta_begin = (uptr)MemToMeta(addr);
+ uptr meta_end = (uptr)MemToMeta(addr + size);
+ meta_begin = RoundDownTo(meta_begin, 64 << 10);
+ meta_end = RoundUpTo(meta_end, 64 << 10);
+ if (!data_mapped) {
+ // First call maps data+bss.
+ data_mapped = true;
+ MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow");
+ } else {
+ // Mapping continous heap.
+ // Windows wants 64K alignment.
+ meta_begin = RoundDownTo(meta_begin, 64 << 10);
+ meta_end = RoundUpTo(meta_end, 64 << 10);
+ if (meta_end <= mapped_meta_end)
+ return;
+ if (meta_begin < mapped_meta_end)
+ meta_begin = mapped_meta_end;
+ MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow");
+ mapped_meta_end = meta_end;
+ }
+ VPrintf(2, "mapped meta shadow for (%p-%p) at (%p-%p)\n",
+ addr, addr+size, meta_begin, meta_end);
}
-void MapThreadTrace(uptr addr, uptr size) {
+void MapThreadTrace(uptr addr, uptr size, const char *name) {
DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
- CHECK_GE(addr, kTraceMemBegin);
- CHECK_LE(addr + size, kTraceMemBegin + kTraceMemSize);
- uptr addr1 = (uptr)MmapFixedNoReserve(addr, size);
+ CHECK_GE(addr, kTraceMemBeg);
+ CHECK_LE(addr + size, kTraceMemEnd);
+ CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
+ uptr addr1 = (uptr)MmapFixedNoReserve(addr, size, name);
if (addr1 != addr) {
Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n",
addr, size, addr1);
}
}
+static void CheckShadowMapping() {
+ for (uptr i = 0; i < ARRAY_SIZE(UserRegions); i += 2) {
+ const uptr beg = UserRegions[i];
+ const uptr end = UserRegions[i + 1];
+ VPrintf(3, "checking shadow region %p-%p\n", beg, end);
+ for (uptr p0 = beg; p0 <= end; p0 += (end - beg) / 4) {
+ for (int x = -1; x <= 1; x++) {
+ const uptr p = p0 + x;
+ if (p < beg || p >= end)
+ continue;
+ const uptr s = MemToShadow(p);
+ const uptr m = (uptr)MemToMeta(p);
+ VPrintf(3, " checking pointer %p: shadow=%p meta=%p\n", p, s, m);
+ CHECK(IsAppMem(p));
+ CHECK(IsShadowMem(s));
+ CHECK_EQ(p & ~(kShadowCell - 1), ShadowToMem(s));
+ CHECK(IsMetaMem(m));
+ }
+ }
+ }
+}
+
void Initialize(ThreadState *thr) {
// Thread safe because done before all threads exist.
static bool is_initialized = false;
if (is_initialized)
return;
is_initialized = true;
+ // We are not ready to handle interceptors yet.
+ ScopedIgnoreInterceptors ignore;
SanitizerToolName = "ThreadSanitizer";
// Install tool-specific callbacks in sanitizer_common.
SetCheckFailedCallback(TsanCheckFailed);
- ScopedInRtl in_rtl;
-#ifndef TSAN_GO
+ ctx = new(ctx_placeholder) Context;
+ const char *options = GetEnv(kTsanOptionsEnv);
+ CacheBinaryName();
+ InitializeFlags(&ctx->flags, options);
+ CheckVMASize();
+#ifndef SANITIZER_GO
InitializeAllocator();
+ ReplaceSystemMalloc();
#endif
InitializeInterceptors();
- const char *env = InitializePlatform();
+ CheckShadowMapping();
+ InitializePlatform();
InitializeMutex();
InitializeDynamicAnnotations();
- ctx = new(ctx_placeholder) Context;
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
InitializeShadowMemory();
#endif
- InitializeFlags(&ctx->flags, env);
// Setup correct file descriptor for error reports.
- __sanitizer_set_report_path(flags()->log_path);
+ __sanitizer_set_report_path(common_flags()->log_path);
InitializeSuppressions();
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
InitializeLibIgnore();
- // Initialize external symbolizer before internal threads are started.
- const char *external_symbolizer = flags()->external_symbolizer_path;
- bool external_symbolizer_started =
- Symbolizer::Init(external_symbolizer)->IsExternalAvailable();
- if (external_symbolizer != 0 && external_symbolizer[0] != '\0' &&
- !external_symbolizer_started) {
- Printf("Failed to start external symbolizer: '%s'\n",
- external_symbolizer);
- Die();
- }
- Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer);
+ Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer);
+ // On MIPS, TSan initialization is run before
+ // __pthread_initialize_minimal_internal() is finished, so we can not spawn
+ // new threads.
+#ifndef __mips__
+ StartBackgroundThread();
+ SetSandboxingCallback(StopBackgroundThread);
#endif
- internal_start_thread(&BackgroundThread, 0);
+#endif
+ if (common_flags()->detect_deadlocks)
+ ctx->dd = DDetector::Create(flags());
- if (ctx->flags.verbosity)
- Printf("***** Running under ThreadSanitizer v2 (pid %d) *****\n",
- (int)internal_getpid());
+ VPrintf(1, "***** Running under ThreadSanitizer v2 (pid %d) *****\n",
+ (int)internal_getpid());
// Initialize thread 0.
int tid = ThreadCreate(thr, 0, 0, true);
CHECK_EQ(tid, 0);
ThreadStart(thr, tid, internal_getpid());
- CHECK_EQ(thr->in_rtl, 1);
+#if TSAN_CONTAINS_UBSAN
+ __ubsan::InitAsPlugin();
+#endif
ctx->initialized = true;
if (flags()->stop_on_start) {
(int)internal_getpid());
while (__tsan_resumed == 0) {}
}
+
+ OnInitialize();
}
int Finalize(ThreadState *thr) {
- ScopedInRtl in_rtl;
- Context *ctx = __tsan::ctx;
bool failed = false;
if (flags()->atexit_sleep_ms > 0 && ThreadCount(thr) > 1)
CommonSanitizerReportMutex.Unlock();
ctx->report_mtx.Unlock();
-#ifndef TSAN_GO
- if (ctx->flags.verbosity)
- AllocatorPrintStats();
+#ifndef SANITIZER_GO
+ if (Verbosity()) AllocatorPrintStats();
#endif
ThreadFinalize(thr);
if (ctx->nreported) {
failed = true;
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
Printf("ThreadSanitizer: reported %d warnings\n", ctx->nreported);
#else
Printf("Found %d data race(s)\n", ctx->nreported);
ctx->nmissed_expected);
}
- if (flags()->print_suppressions)
+ if (common_flags()->print_suppressions)
PrintMatchedSuppressions();
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
if (flags()->print_benign)
PrintMatchedBenignRaces();
#endif
failed = OnFinalize(failed);
+#if TSAN_COLLECT_STATS
StatAggregate(ctx->stat, thr->stat);
StatOutput(ctx->stat);
- return failed ? flags()->exitcode : 0;
+#endif
+
+ return failed ? common_flags()->exitcode : 0;
+}
+
+#ifndef SANITIZER_GO
+void ForkBefore(ThreadState *thr, uptr pc) {
+ ctx->thread_registry->Lock();
+ ctx->report_mtx.Lock();
+}
+
+void ForkParentAfter(ThreadState *thr, uptr pc) {
+ ctx->report_mtx.Unlock();
+ ctx->thread_registry->Unlock();
+}
+
+void ForkChildAfter(ThreadState *thr, uptr pc) {
+ ctx->report_mtx.Unlock();
+ ctx->thread_registry->Unlock();
+
+ uptr nthread = 0;
+ ctx->thread_registry->GetNumberOfThreads(0, 0, &nthread /* alive threads */);
+ VPrintf(1, "ThreadSanitizer: forked new process with pid %d,"
+ " parent had %d threads\n", (int)internal_getpid(), (int)nthread);
+ if (nthread == 1) {
+ StartBackgroundThread();
+ } else {
+ // We've just forked a multi-threaded process. We cannot reasonably function
+ // after that (some mutexes may be locked before fork). So just enable
+ // ignores for everything in the hope that we will exec soon.
+ ctx->after_multithreaded_fork = true;
+ thr->ignore_interceptors++;
+ ThreadIgnoreBegin(thr, pc);
+ ThreadIgnoreSyncBegin(thr, pc);
+ }
+}
+#endif
+
+#ifdef SANITIZER_GO
+NOINLINE
+void GrowShadowStack(ThreadState *thr) {
+ const int sz = thr->shadow_stack_end - thr->shadow_stack;
+ const int newsz = 2 * sz;
+ uptr *newstack = (uptr*)internal_alloc(MBlockShadowStack,
+ newsz * sizeof(uptr));
+ internal_memcpy(newstack, thr->shadow_stack, sz * sizeof(uptr));
+ internal_free(thr->shadow_stack);
+ thr->shadow_stack = newstack;
+ thr->shadow_stack_pos = newstack + sz;
+ thr->shadow_stack_end = newstack + newsz;
}
+#endif
-#ifndef TSAN_GO
u32 CurrentStackId(ThreadState *thr, uptr pc) {
- if (thr->shadow_stack_pos == 0) // May happen during bootstrap.
+ if (!thr->is_inited) // May happen during bootstrap.
return 0;
- if (pc) {
+ if (pc != 0) {
+#ifndef SANITIZER_GO
+ DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
+#else
+ if (thr->shadow_stack_pos == thr->shadow_stack_end)
+ GrowShadowStack(thr);
+#endif
thr->shadow_stack_pos[0] = pc;
thr->shadow_stack_pos++;
}
- u32 id = StackDepotPut(thr->shadow_stack,
- thr->shadow_stack_pos - thr->shadow_stack);
- if (pc)
+ u32 id = StackDepotPut(
+ StackTrace(thr->shadow_stack, thr->shadow_stack_pos - thr->shadow_stack));
+ if (pc != 0)
thr->shadow_stack_pos--;
return id;
}
-#endif
void TraceSwitch(ThreadState *thr) {
thr->nomalloc++;
- ScopedInRtl in_rtl;
Trace *thr_trace = ThreadTrace(thr->tid);
Lock l(&thr_trace->mtx);
unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % TraceParts();
TraceHeader *hdr = &thr_trace->headers[trace];
hdr->epoch0 = thr->fast_state.epoch();
- hdr->stack0.ObtainCurrent(thr, 0);
+ ObtainCurrentStack(thr, 0, &hdr->stack0);
hdr->mset0 = thr->mset;
thr->nomalloc--;
}
return TraceSize() / kTracePartSize;
}
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
extern "C" void __tsan_trace_switch() {
TraceSwitch(cur_thread());
}
*s = 0;
}
-static inline void HandleRace(ThreadState *thr, u64 *shadow_mem,
+ALWAYS_INLINE
+void HandleRace(ThreadState *thr, u64 *shadow_mem,
Shadow cur, Shadow old) {
thr->racy_state[0] = cur.raw();
thr->racy_state[1] = old.raw();
thr->racy_shadow_addr = shadow_mem;
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
HACKY_CALL(__tsan_report_race);
#else
ReportRace(thr);
#endif
}
-static inline bool OldIsInSameSynchEpoch(Shadow old, ThreadState *thr) {
- return old.epoch() >= thr->fast_synch_epoch;
-}
-
static inline bool HappensBefore(Shadow old, ThreadState *thr) {
return thr->clock.get(old.TidWithIgnore()) >= old.epoch();
}
-ALWAYS_INLINE USED
-void MemoryAccessImpl(ThreadState *thr, uptr addr,
+ALWAYS_INLINE
+void MemoryAccessImpl1(ThreadState *thr, uptr addr,
int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic,
u64 *shadow_mem, Shadow cur) {
StatInc(thr, StatMop);
// it's just not worth it (performance- and complexity-wise).
Shadow old(0);
- if (kShadowCnt == 1) {
- int idx = 0;
-#include "tsan_update_shadow_word_inl.h"
- } else if (kShadowCnt == 2) {
- int idx = 0;
-#include "tsan_update_shadow_word_inl.h"
- idx = 1;
-#include "tsan_update_shadow_word_inl.h"
- } else if (kShadowCnt == 4) {
- int idx = 0;
-#include "tsan_update_shadow_word_inl.h"
- idx = 1;
-#include "tsan_update_shadow_word_inl.h"
- idx = 2;
-#include "tsan_update_shadow_word_inl.h"
- idx = 3;
-#include "tsan_update_shadow_word_inl.h"
- } else if (kShadowCnt == 8) {
- int idx = 0;
-#include "tsan_update_shadow_word_inl.h"
- idx = 1;
-#include "tsan_update_shadow_word_inl.h"
- idx = 2;
-#include "tsan_update_shadow_word_inl.h"
- idx = 3;
+
+ // It release mode we manually unroll the loop,
+ // because empirically gcc generates better code this way.
+ // However, we can't afford unrolling in debug mode, because the function
+ // consumes almost 4K of stack. Gtest gives only 4K of stack to death test
+ // threads, which is not enough for the unrolled loop.
+#if SANITIZER_DEBUG
+ for (int idx = 0; idx < 4; idx++) {
#include "tsan_update_shadow_word_inl.h"
- idx = 4;
+ }
+#else
+ int idx = 0;
#include "tsan_update_shadow_word_inl.h"
- idx = 5;
+ idx = 1;
#include "tsan_update_shadow_word_inl.h"
- idx = 6;
+ idx = 2;
#include "tsan_update_shadow_word_inl.h"
- idx = 7;
+ idx = 3;
#include "tsan_update_shadow_word_inl.h"
- } else {
- CHECK(false);
- }
+#endif
// we did not find any races and had already stored
// the current access info, so we are done
while (size) {
int size1 = 1;
int kAccessSizeLog = kSizeLog1;
- if (size >= 8 && (addr & ~7) == ((addr + 8) & ~7)) {
+ if (size >= 8 && (addr & ~7) == ((addr + 7) & ~7)) {
size1 = 8;
kAccessSizeLog = kSizeLog8;
- } else if (size >= 4 && (addr & ~7) == ((addr + 4) & ~7)) {
+ } else if (size >= 4 && (addr & ~7) == ((addr + 3) & ~7)) {
size1 = 4;
kAccessSizeLog = kSizeLog4;
- } else if (size >= 2 && (addr & ~7) == ((addr + 2) & ~7)) {
+ } else if (size >= 2 && (addr & ~7) == ((addr + 1) & ~7)) {
size1 = 2;
kAccessSizeLog = kSizeLog2;
}
}
}
+ALWAYS_INLINE
+bool ContainsSameAccessSlow(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
+ Shadow cur(a);
+ for (uptr i = 0; i < kShadowCnt; i++) {
+ Shadow old(LoadShadow(&s[i]));
+ if (Shadow::Addr0AndSizeAreEqual(cur, old) &&
+ old.TidWithIgnore() == cur.TidWithIgnore() &&
+ old.epoch() > sync_epoch &&
+ old.IsAtomic() == cur.IsAtomic() &&
+ old.IsRead() <= cur.IsRead())
+ return true;
+ }
+ return false;
+}
+
+#if defined(__SSE3__)
+#define SHUF(v0, v1, i0, i1, i2, i3) _mm_castps_si128(_mm_shuffle_ps( \
+ _mm_castsi128_ps(v0), _mm_castsi128_ps(v1), \
+ (i0)*1 + (i1)*4 + (i2)*16 + (i3)*64))
+ALWAYS_INLINE
+bool ContainsSameAccessFast(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
+ // This is an optimized version of ContainsSameAccessSlow.
+ // load current access into access[0:63]
+ const m128 access = _mm_cvtsi64_si128(a);
+ // duplicate high part of access in addr0:
+ // addr0[0:31] = access[32:63]
+ // addr0[32:63] = access[32:63]
+ // addr0[64:95] = access[32:63]
+ // addr0[96:127] = access[32:63]
+ const m128 addr0 = SHUF(access, access, 1, 1, 1, 1);
+ // load 4 shadow slots
+ const m128 shadow0 = _mm_load_si128((__m128i*)s);
+ const m128 shadow1 = _mm_load_si128((__m128i*)s + 1);
+ // load high parts of 4 shadow slots into addr_vect:
+ // addr_vect[0:31] = shadow0[32:63]
+ // addr_vect[32:63] = shadow0[96:127]
+ // addr_vect[64:95] = shadow1[32:63]
+ // addr_vect[96:127] = shadow1[96:127]
+ m128 addr_vect = SHUF(shadow0, shadow1, 1, 3, 1, 3);
+ if (!is_write) {
+ // set IsRead bit in addr_vect
+ const m128 rw_mask1 = _mm_cvtsi64_si128(1<<15);
+ const m128 rw_mask = SHUF(rw_mask1, rw_mask1, 0, 0, 0, 0);
+ addr_vect = _mm_or_si128(addr_vect, rw_mask);
+ }
+ // addr0 == addr_vect?
+ const m128 addr_res = _mm_cmpeq_epi32(addr0, addr_vect);
+ // epoch1[0:63] = sync_epoch
+ const m128 epoch1 = _mm_cvtsi64_si128(sync_epoch);
+ // epoch[0:31] = sync_epoch[0:31]
+ // epoch[32:63] = sync_epoch[0:31]
+ // epoch[64:95] = sync_epoch[0:31]
+ // epoch[96:127] = sync_epoch[0:31]
+ const m128 epoch = SHUF(epoch1, epoch1, 0, 0, 0, 0);
+ // load low parts of shadow cell epochs into epoch_vect:
+ // epoch_vect[0:31] = shadow0[0:31]
+ // epoch_vect[32:63] = shadow0[64:95]
+ // epoch_vect[64:95] = shadow1[0:31]
+ // epoch_vect[96:127] = shadow1[64:95]
+ const m128 epoch_vect = SHUF(shadow0, shadow1, 0, 2, 0, 2);
+ // epoch_vect >= sync_epoch?
+ const m128 epoch_res = _mm_cmpgt_epi32(epoch_vect, epoch);
+ // addr_res & epoch_res
+ const m128 res = _mm_and_si128(addr_res, epoch_res);
+ // mask[0] = res[7]
+ // mask[1] = res[15]
+ // ...
+ // mask[15] = res[127]
+ const int mask = _mm_movemask_epi8(res);
+ return mask != 0;
+}
+#endif
+
+ALWAYS_INLINE
+bool ContainsSameAccess(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
+#if defined(__SSE3__)
+ bool res = ContainsSameAccessFast(s, a, sync_epoch, is_write);
+ // NOTE: this check can fail if the shadow is concurrently mutated
+ // by other threads. But it still can be useful if you modify
+ // ContainsSameAccessFast and want to ensure that it's not completely broken.
+ // DCHECK_EQ(res, ContainsSameAccessSlow(s, a, sync_epoch, is_write));
+ return res;
+#else
+ return ContainsSameAccessSlow(s, a, sync_epoch, is_write);
+#endif
+}
+
ALWAYS_INLINE USED
void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic) {
(int)(1 << kAccessSizeLog), kAccessIsWrite, shadow_mem,
(uptr)shadow_mem[0], (uptr)shadow_mem[1],
(uptr)shadow_mem[2], (uptr)shadow_mem[3]);
-#if TSAN_DEBUG
+#if SANITIZER_DEBUG
if (!IsAppMem(addr)) {
Printf("Access to non app mem %zx\n", addr);
DCHECK(IsAppMem(addr));
}
#endif
- if (*shadow_mem == kShadowRodata) {
+ if (kCppMode && *shadow_mem == kShadowRodata) {
// Access to .rodata section, no races here.
// Measurements show that it can be 10-20% of all memory accesses.
StatInc(thr, StatMop);
}
FastState fast_state = thr->fast_state;
- if (fast_state.GetIgnoreBit())
+ if (fast_state.GetIgnoreBit()) {
+ StatInc(thr, StatMop);
+ StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
+ StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
+ StatInc(thr, StatMopIgnored);
return;
- fast_state.IncrementEpoch();
- thr->fast_state = fast_state;
+ }
+
Shadow cur(fast_state);
cur.SetAddr0AndSizeLog(addr & 7, kAccessSizeLog);
cur.SetWrite(kAccessIsWrite);
cur.SetAtomic(kIsAtomic);
- // We must not store to the trace if we do not store to the shadow.
- // That is, this call must be moved somewhere below.
- TraceAddEvent(thr, fast_state, EventTypeMop, pc);
+ if (LIKELY(ContainsSameAccess(shadow_mem, cur.raw(),
+ thr->fast_synch_epoch, kAccessIsWrite))) {
+ StatInc(thr, StatMop);
+ StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
+ StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
+ StatInc(thr, StatMopSame);
+ return;
+ }
+
+ if (kCollectHistory) {
+ fast_state.IncrementEpoch();
+ thr->fast_state = fast_state;
+ TraceAddEvent(thr, fast_state, EventTypeMop, pc);
+ cur.IncrementEpoch();
+ }
+
+ MemoryAccessImpl1(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic,
+ shadow_mem, cur);
+}
+
+// Called by MemoryAccessRange in tsan_rtl_thread.cc
+ALWAYS_INLINE USED
+void MemoryAccessImpl(ThreadState *thr, uptr addr,
+ int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic,
+ u64 *shadow_mem, Shadow cur) {
+ if (LIKELY(ContainsSameAccess(shadow_mem, cur.raw(),
+ thr->fast_synch_epoch, kAccessIsWrite))) {
+ StatInc(thr, StatMop);
+ StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
+ StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
+ StatInc(thr, StatMopSame);
+ return;
+ }
- MemoryAccessImpl(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic,
+ MemoryAccessImpl1(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic,
shadow_mem, cur);
}
size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1);
// UnmapOrDie/MmapFixedNoReserve does not work on Windows,
// so we do it only for C/C++.
- if (kGoMode || size < 64*1024) {
+ if (kGoMode || size < common_flags()->clear_shadow_mmap_threshold) {
u64 *p = (u64*)MemToShadow(addr);
CHECK(IsShadowMem((uptr)p));
CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1)));
}
} else {
// The region is big, reset only beginning and end.
- const uptr kPageSize = 4096;
+ const uptr kPageSize = GetPageSizeCached();
u64 *begin = (u64*)MemToShadow(addr);
u64 *end = begin + size / kShadowCell * kShadowCnt;
u64 *p = begin;
thr->is_freeing = true;
MemoryAccessRange(thr, pc, addr, size, true);
thr->is_freeing = false;
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc);
+ if (kCollectHistory) {
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc);
+ }
Shadow s(thr->fast_state);
s.ClearIgnoreBit();
s.MarkAsFreed();
}
void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) {
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc);
+ if (kCollectHistory) {
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc);
+ }
Shadow s(thr->fast_state);
s.ClearIgnoreBit();
s.SetWrite(true);
ALWAYS_INLINE USED
void FuncEntry(ThreadState *thr, uptr pc) {
- DCHECK_EQ(thr->in_rtl, 0);
StatInc(thr, StatFuncEnter);
DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc);
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeFuncEnter, pc);
+ if (kCollectHistory) {
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeFuncEnter, pc);
+ }
// Shadow stack maintenance can be replaced with
// stack unwinding during trace switch (which presumably must be faster).
DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack);
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
#else
- if (thr->shadow_stack_pos == thr->shadow_stack_end) {
- const int sz = thr->shadow_stack_end - thr->shadow_stack;
- const int newsz = 2 * sz;
- uptr *newstack = (uptr*)internal_alloc(MBlockShadowStack,
- newsz * sizeof(uptr));
- internal_memcpy(newstack, thr->shadow_stack, sz * sizeof(uptr));
- internal_free(thr->shadow_stack);
- thr->shadow_stack = newstack;
- thr->shadow_stack_pos = newstack + sz;
- thr->shadow_stack_end = newstack + newsz;
- }
+ if (thr->shadow_stack_pos == thr->shadow_stack_end)
+ GrowShadowStack(thr);
#endif
thr->shadow_stack_pos[0] = pc;
thr->shadow_stack_pos++;
ALWAYS_INLINE USED
void FuncExit(ThreadState *thr) {
- DCHECK_EQ(thr->in_rtl, 0);
StatInc(thr, StatFuncExit);
DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid());
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0);
+ if (kCollectHistory) {
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0);
+ }
DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack);
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
#endif
thr->shadow_stack_pos--;
thr->ignore_reads_and_writes++;
CHECK_GT(thr->ignore_reads_and_writes, 0);
thr->fast_state.SetIgnoreBit();
-#ifndef TSAN_GO
- thr->mop_ignore_set.Add(CurrentStackId(thr, pc));
+#ifndef SANITIZER_GO
+ if (!ctx->after_multithreaded_fork)
+ thr->mop_ignore_set.Add(CurrentStackId(thr, pc));
#endif
}
CHECK_GE(thr->ignore_reads_and_writes, 0);
if (thr->ignore_reads_and_writes == 0) {
thr->fast_state.ClearIgnoreBit();
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
thr->mop_ignore_set.Reset();
#endif
}
DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid);
thr->ignore_sync++;
CHECK_GT(thr->ignore_sync, 0);
-#ifndef TSAN_GO
- thr->sync_ignore_set.Add(CurrentStackId(thr, pc));
+#ifndef SANITIZER_GO
+ if (!ctx->after_multithreaded_fork)
+ thr->sync_ignore_set.Add(CurrentStackId(thr, pc));
#endif
}
DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid);
thr->ignore_sync--;
CHECK_GE(thr->ignore_sync, 0);
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
if (thr->ignore_sync == 0)
- thr->mop_ignore_set.Reset();
+ thr->sync_ignore_set.Reset();
#endif
}
return hash[0] == other.hash[0] && hash[1] == other.hash[1];
}
-#if TSAN_DEBUG
+#if SANITIZER_DEBUG
void build_consistency_debug() {}
#else
void build_consistency_release() {}
void build_consistency_nostats() {}
#endif
-#if TSAN_SHADOW_COUNT == 1
-void build_consistency_shadow1() {}
-#elif TSAN_SHADOW_COUNT == 2
-void build_consistency_shadow2() {}
-#elif TSAN_SHADOW_COUNT == 4
-void build_consistency_shadow4() {}
-#else
-void build_consistency_shadow8() {}
-#endif
-
} // namespace __tsan
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
// Must be included in this file to make sure everything is inlined.
#include "tsan_interface_inl.h"
#endif
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_asm.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_deadlock_detector_interface.h"
#include "sanitizer_common/sanitizer_libignore.h"
#include "sanitizer_common/sanitizer_suppressions.h"
#include "sanitizer_common/sanitizer_thread_registry.h"
#include "tsan_platform.h"
#include "tsan_mutexset.h"
#include "tsan_ignoreset.h"
+#include "tsan_stack_trace.h"
#if SANITIZER_WORDSIZE != 64
# error "ThreadSanitizer is supported only on 64-bit platforms"
namespace __tsan {
-// Descriptor of user's memory block.
-struct MBlock {
- /*
- u64 mtx : 1; // must be first
- u64 lst : 44;
- u64 stk : 31; // on word boundary
- u64 tid : kTidBits;
- u64 siz : 128 - 1 - 31 - 44 - kTidBits; // 39
- */
- u64 raw[2];
-
- void Init(uptr siz, u32 tid, u32 stk) {
- raw[0] = raw[1] = 0;
- raw[1] |= (u64)siz << ((1 + 44 + 31 + kTidBits) % 64);
- raw[1] |= (u64)tid << ((1 + 44 + 31) % 64);
- raw[0] |= (u64)stk << (1 + 44);
- raw[1] |= (u64)stk >> (64 - 44 - 1);
- DCHECK_EQ(Size(), siz);
- DCHECK_EQ(Tid(), tid);
- DCHECK_EQ(StackId(), stk);
- }
-
- u32 Tid() const {
- return GetLsb(raw[1] >> ((1 + 44 + 31) % 64), kTidBits);
- }
-
- uptr Size() const {
- return raw[1] >> ((1 + 31 + 44 + kTidBits) % 64);
- }
-
- u32 StackId() const {
- return (raw[0] >> (1 + 44)) | GetLsb(raw[1] << (64 - 44 - 1), 31);
- }
-
- SyncVar *ListHead() const {
- return (SyncVar*)(GetLsb(raw[0] >> 1, 44) << 3);
- }
-
- void ListPush(SyncVar *v) {
- SyncVar *lst = ListHead();
- v->next = lst;
- u64 x = (u64)v ^ (u64)lst;
- x = (x >> 3) << 1;
- raw[0] ^= x;
- DCHECK_EQ(ListHead(), v);
- }
-
- SyncVar *ListPop() {
- SyncVar *lst = ListHead();
- SyncVar *nxt = lst->next;
- lst->next = 0;
- u64 x = (u64)lst ^ (u64)nxt;
- x = (x >> 3) << 1;
- raw[0] ^= x;
- DCHECK_EQ(ListHead(), nxt);
- return lst;
- }
-
- void ListReset() {
- SyncVar *lst = ListHead();
- u64 x = (u64)lst;
- x = (x >> 3) << 1;
- raw[0] ^= x;
- DCHECK_EQ(ListHead(), 0);
- }
-
- void Lock();
- void Unlock();
- typedef GenericScopedLock<MBlock> ScopedLock;
-};
-
-#ifndef TSAN_GO
-#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
-const uptr kAllocatorSpace = 0x7d0000000000ULL;
-#else
-const uptr kAllocatorSpace = 0x7d0000000000ULL;
-#endif
-const uptr kAllocatorSize = 0x10000000000ULL; // 1T.
-
+#ifndef SANITIZER_GO
struct MapUnmapCallback;
-typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, sizeof(MBlock),
+#if defined(__mips64) || defined(__aarch64__)
+static const uptr kAllocatorSpace = 0;
+static const uptr kAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
+static const uptr kAllocatorRegionSizeLog = 20;
+static const uptr kAllocatorNumRegions =
+ kAllocatorSize >> kAllocatorRegionSizeLog;
+typedef TwoLevelByteMap<(kAllocatorNumRegions >> 12), 1 << 12,
+ MapUnmapCallback> ByteMap;
+typedef SizeClassAllocator32<kAllocatorSpace, kAllocatorSize, 0,
+ CompactSizeClassMap, kAllocatorRegionSizeLog, ByteMap,
+ MapUnmapCallback> PrimaryAllocator;
+#else
+typedef SizeClassAllocator64<kHeapMemBeg, kHeapMemEnd - kHeapMemBeg, 0,
DefaultSizeClassMap, MapUnmapCallback> PrimaryAllocator;
+#endif
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
typedef LargeMmapAllocator<MapUnmapCallback> SecondaryAllocator;
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
// FastState (from most significant bit):
// ignore : 1
// tid : kTidBits
-// epoch : kClkBits
// unused : -
// history_size : 3
+// epoch : kClkBits
class FastState {
public:
FastState(u64 tid, u64 epoch) {
x_ = tid << kTidShift;
- x_ |= epoch << kClkShift;
+ x_ |= epoch;
DCHECK_EQ(tid, this->tid());
DCHECK_EQ(epoch, this->epoch());
DCHECK_EQ(GetIgnoreBit(), false);
}
u64 epoch() const {
- u64 res = (x_ << (kTidBits + 1)) >> (64 - kClkBits);
+ u64 res = x_ & ((1ull << kClkBits) - 1);
return res;
}
void IncrementEpoch() {
u64 old_epoch = epoch();
- x_ += 1 << kClkShift;
+ x_ += 1;
DCHECK_EQ(old_epoch + 1, epoch());
(void)old_epoch;
}
void SetHistorySize(int hs) {
CHECK_GE(hs, 0);
CHECK_LE(hs, 7);
- x_ = (x_ & ~7) | hs;
+ x_ = (x_ & ~(kHistoryMask << kHistoryShift)) | (u64(hs) << kHistoryShift);
}
+ ALWAYS_INLINE
int GetHistorySize() const {
- return (int)(x_ & 7);
+ return (int)((x_ >> kHistoryShift) & kHistoryMask);
}
void ClearHistorySize() {
- x_ &= ~7;
+ SetHistorySize(0);
}
+ ALWAYS_INLINE
u64 GetTracePos() const {
const int hs = GetHistorySize();
// When hs == 0, the trace consists of 2 parts.
private:
friend class Shadow;
static const int kTidShift = 64 - kTidBits - 1;
- static const int kClkShift = kTidShift - kClkBits;
static const u64 kIgnoreBit = 1ull << 63;
static const u64 kFreedBit = 1ull << 63;
+ static const u64 kHistoryShift = kClkBits;
+ static const u64 kHistoryMask = 7;
u64 x_;
};
// Shadow (from most significant bit):
// freed : 1
// tid : kTidBits
-// epoch : kClkBits
// is_atomic : 1
// is_read : 1
// size_log : 2
// addr0 : 3
+// epoch : kClkBits
class Shadow : public FastState {
public:
explicit Shadow(u64 x)
}
void SetAddr0AndSizeLog(u64 addr0, unsigned kAccessSizeLog) {
- DCHECK_EQ(x_ & 31, 0);
+ DCHECK_EQ((x_ >> kClkBits) & 31, 0);
DCHECK_LE(addr0, 7);
DCHECK_LE(kAccessSizeLog, 3);
- x_ |= (kAccessSizeLog << 3) | addr0;
+ x_ |= ((kAccessSizeLog << 3) | addr0) << kClkBits;
DCHECK_EQ(kAccessSizeLog, size_log());
DCHECK_EQ(addr0, this->addr0());
}
return shifted_xor == 0;
}
- static inline bool Addr0AndSizeAreEqual(const Shadow s1, const Shadow s2) {
- u64 masked_xor = (s1.x_ ^ s2.x_) & 31;
+ static ALWAYS_INLINE
+ bool Addr0AndSizeAreEqual(const Shadow s1, const Shadow s2) {
+ u64 masked_xor = ((s1.x_ ^ s2.x_) >> kClkBits) & 31;
return masked_xor == 0;
}
- static inline bool TwoRangesIntersect(Shadow s1, Shadow s2,
+ static ALWAYS_INLINE bool TwoRangesIntersect(Shadow s1, Shadow s2,
unsigned kS2AccessSize) {
bool res = false;
u64 diff = s1.addr0() - s2.addr0();
if ((s64)diff < 0) { // s1.addr0 < s2.addr0 // NOLINT
// if (s1.addr0() + size1) > s2.addr0()) return true;
- if (s1.size() > -diff) res = true;
+ if (s1.size() > -diff)
+ res = true;
} else {
// if (s2.addr0() + kS2AccessSize > s1.addr0()) return true;
- if (kS2AccessSize > diff) res = true;
+ if (kS2AccessSize > diff)
+ res = true;
}
- DCHECK_EQ(res, TwoRangesIntersectSLOW(s1, s2));
- DCHECK_EQ(res, TwoRangesIntersectSLOW(s2, s1));
+ DCHECK_EQ(res, TwoRangesIntersectSlow(s1, s2));
+ DCHECK_EQ(res, TwoRangesIntersectSlow(s2, s1));
return res;
}
- // The idea behind the offset is as follows.
- // Consider that we have 8 bool's contained within a single 8-byte block
- // (mapped to a single shadow "cell"). Now consider that we write to the bools
- // from a single thread (which we consider the common case).
- // W/o offsetting each access will have to scan 4 shadow values at average
- // to find the corresponding shadow value for the bool.
- // With offsetting we start scanning shadow with the offset so that
- // each access hits necessary shadow straight off (at least in an expected
- // optimistic case).
- // This logic works seamlessly for any layout of user data. For example,
- // if user data is {int, short, char, char}, then accesses to the int are
- // offsetted to 0, short - 4, 1st char - 6, 2nd char - 7. Hopefully, accesses
- // from a single thread won't need to scan all 8 shadow values.
- unsigned ComputeSearchOffset() {
- return x_ & 7;
- }
- u64 addr0() const { return x_ & 7; }
- u64 size() const { return 1ull << size_log(); }
- bool IsWrite() const { return !IsRead(); }
- bool IsRead() const { return x_ & kReadBit; }
+ u64 ALWAYS_INLINE addr0() const { return (x_ >> kClkBits) & 7; }
+ u64 ALWAYS_INLINE size() const { return 1ull << size_log(); }
+ bool ALWAYS_INLINE IsWrite() const { return !IsRead(); }
+ bool ALWAYS_INLINE IsRead() const { return x_ & kReadBit; }
// The idea behind the freed bit is as follows.
// When the memory is freed (or otherwise unaccessible) we write to the shadow
return res;
}
- bool IsBothReadsOrAtomic(bool kIsWrite, bool kIsAtomic) const {
- // analyzes 5-th bit (is_read) and 6-th bit (is_atomic)
- bool v = x_ & u64(((kIsWrite ^ 1) << kReadShift)
- | (kIsAtomic << kAtomicShift));
+ bool ALWAYS_INLINE IsBothReadsOrAtomic(bool kIsWrite, bool kIsAtomic) const {
+ bool v = x_ & ((u64(kIsWrite ^ 1) << kReadShift)
+ | (u64(kIsAtomic) << kAtomicShift));
DCHECK_EQ(v, (!IsWrite() && !kIsWrite) || (IsAtomic() && kIsAtomic));
return v;
}
- bool IsRWNotWeaker(bool kIsWrite, bool kIsAtomic) const {
+ bool ALWAYS_INLINE IsRWNotWeaker(bool kIsWrite, bool kIsAtomic) const {
bool v = ((x_ >> kReadShift) & 3)
<= u64((kIsWrite ^ 1) | (kIsAtomic << 1));
DCHECK_EQ(v, (IsAtomic() < kIsAtomic) ||
return v;
}
- bool IsRWWeakerOrEqual(bool kIsWrite, bool kIsAtomic) const {
+ bool ALWAYS_INLINE IsRWWeakerOrEqual(bool kIsWrite, bool kIsAtomic) const {
bool v = ((x_ >> kReadShift) & 3)
>= u64((kIsWrite ^ 1) | (kIsAtomic << 1));
DCHECK_EQ(v, (IsAtomic() > kIsAtomic) ||
}
private:
- static const u64 kReadShift = 5;
+ static const u64 kReadShift = 5 + kClkBits;
static const u64 kReadBit = 1ull << kReadShift;
- static const u64 kAtomicShift = 6;
+ static const u64 kAtomicShift = 6 + kClkBits;
static const u64 kAtomicBit = 1ull << kAtomicShift;
- u64 size_log() const { return (x_ >> 3) & 3; }
+ u64 size_log() const { return (x_ >> (3 + kClkBits)) & 3; }
- static bool TwoRangesIntersectSLOW(const Shadow s1, const Shadow s2) {
+ static bool TwoRangesIntersectSlow(const Shadow s1, const Shadow s2) {
if (s1.addr0() == s2.addr0()) return true;
if (s1.addr0() < s2.addr0() && s1.addr0() + s1.size() > s2.addr0())
return true;
}
};
-struct SignalContext;
+struct ThreadSignalContext;
struct JmpBuf {
uptr sp;
uptr mangled_sp;
+ int int_signal_send;
+ bool in_blocking_func;
+ uptr in_signal_handler;
uptr *shadow_stack_pos;
};
int ignore_reads_and_writes;
int ignore_sync;
// Go does not support ignores.
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
IgnoreSet mop_ignore_set;
IgnoreSet sync_ignore_set;
#endif
u64 racy_state[2];
MutexSet mset;
ThreadClock clock;
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
AllocatorCache alloc_cache;
InternalAllocatorCache internal_alloc_cache;
Vector<JmpBuf> jmp_bufs;
+ int ignore_interceptors;
#endif
+#if TSAN_COLLECT_STATS
u64 stat[StatCnt];
+#endif
const int tid;
const int unique_id;
- int in_rtl;
bool in_symbolizer;
bool in_ignored_lib;
- bool is_alive;
+ bool is_inited;
+ bool is_dead;
bool is_freeing;
bool is_vptr_access;
const uptr stk_addr;
const uptr tls_size;
ThreadContext *tctx;
- DeadlockDetector deadlock_detector;
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ InternalDeadlockDetector internal_deadlock_detector;
+#endif
+ DDPhysicalThread *dd_pt;
+ DDLogicalThread *dd_lt;
+
+ atomic_uintptr_t in_signal_handler;
+ ThreadSignalContext *signal_ctx;
- bool in_signal_handler;
- SignalContext *signal_ctx;
+ DenseSlabAllocCache block_cache;
+ DenseSlabAllocCache sync_cache;
+ DenseSlabAllocCache clock_cache;
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
u32 last_sleep_stack_id;
ThreadClock last_sleep_clock;
#endif
int nomalloc;
explicit ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
+ unsigned reuse_count,
uptr stk_addr, uptr stk_size,
uptr tls_addr, uptr tls_size);
};
-Context *CTX();
-
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
+#if SANITIZER_MAC
+ThreadState *cur_thread();
+void cur_thread_finalize();
+#else
+__attribute__((tls_model("initial-exec")))
extern THREADLOCAL char cur_thread_placeholder[];
INLINE ThreadState *cur_thread() {
return reinterpret_cast<ThreadState *>(&cur_thread_placeholder);
}
-#endif
+INLINE void cur_thread_finalize() { }
+#endif // SANITIZER_MAC
+#endif // SANITIZER_GO
class ThreadContext : public ThreadContextBase {
public:
explicit ThreadContext(int tid);
~ThreadContext();
ThreadState *thr;
-#ifdef TSAN_GO
- StackTrace creation_stack;
-#else
u32 creation_stack_id;
-#endif
SyncClock sync;
// Epoch at which the thread had started.
// If we see an event from the thread stamped by an older epoch,
u64 epoch1;
// Override superclass callbacks.
- void OnDead();
- void OnJoined(void *arg);
- void OnFinished();
- void OnStarted(void *arg);
- void OnCreated(void *arg);
- void OnReset();
+ void OnDead() override;
+ void OnJoined(void *arg) override;
+ void OnFinished() override;
+ void OnStarted(void *arg) override;
+ void OnCreated(void *arg) override;
+ void OnReset() override;
+ void OnDetached(void *arg) override;
};
struct RacyStacks {
struct FiredSuppression {
ReportType type;
- uptr pc;
+ uptr pc_or_addr;
Suppression *supp;
};
Context();
bool initialized;
+ bool after_multithreaded_fork;
- SyncTab synctab;
+ MetaMap metamap;
Mutex report_mtx;
int nreported;
int nmissed_expected;
atomic_uint64_t last_symbolize_time_ns;
+ void *background_thread;
+ atomic_uint32_t stop_background_thread;
+
ThreadRegistry *thread_registry;
+ Mutex racy_mtx;
Vector<RacyStacks> racy_stacks;
Vector<RacyAddress> racy_addresses;
// Number of fired suppressions may be large enough.
+ Mutex fired_suppressions_mtx;
InternalMmapVector<FiredSuppression> fired_suppressions;
+ DDetector *dd;
+
+ ClockAlloc clock_alloc;
Flags flags;
u64 int_alloc_siz[MBlockTypeCount];
};
-class ScopedInRtl {
- public:
- ScopedInRtl();
- ~ScopedInRtl();
- private:
- ThreadState*thr_;
- int in_rtl_;
- int errno_;
+extern Context *ctx; // The one and the only global runtime context.
+
+struct ScopedIgnoreInterceptors {
+ ScopedIgnoreInterceptors() {
+#ifndef SANITIZER_GO
+ cur_thread()->ignore_interceptors++;
+#endif
+ }
+
+ ~ScopedIgnoreInterceptors() {
+#ifndef SANITIZER_GO
+ cur_thread()->ignore_interceptors--;
+#endif
+ }
};
class ScopedReport {
explicit ScopedReport(ReportType typ);
~ScopedReport();
- void AddStack(const StackTrace *stack);
- void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack,
+ void AddMemoryAccess(uptr addr, Shadow s, StackTrace stack,
const MutexSet *mset);
- void AddThread(const ThreadContext *tctx);
+ void AddStack(StackTrace stack, bool suppressable = false);
+ void AddThread(const ThreadContext *tctx, bool suppressable = false);
+ void AddThread(int unique_tid, bool suppressable = false);
+ void AddUniqueTid(int unique_tid);
void AddMutex(const SyncVar *s);
+ u64 AddMutex(u64 id);
void AddLocation(uptr addr, uptr size);
void AddSleep(u32 stack_id);
void SetCount(int count);
const ReportDesc *GetReport() const;
private:
- Context *ctx_;
ReportDesc *rep_;
+ // Symbolizer makes lots of intercepted calls. If we try to process them,
+ // at best it will cause deadlocks on internal mutexes.
+ ScopedIgnoreInterceptors ignore_interceptors_;
- void AddMutex(u64 id);
+ void AddDeadMutex(u64 id);
ScopedReport(const ScopedReport&);
void operator = (const ScopedReport&);
};
-void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset);
+void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
+ MutexSet *mset);
+template<typename StackTraceTy>
+void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) {
+ uptr size = thr->shadow_stack_pos - thr->shadow_stack;
+ uptr start = 0;
+ if (size + !!toppc > kStackTraceMax) {
+ start = size + !!toppc - kStackTraceMax;
+ size = kStackTraceMax - !!toppc;
+ }
+ stack->Init(&thr->shadow_stack[start], size, toppc);
+}
+
+
+#if TSAN_COLLECT_STATS
void StatAggregate(u64 *dst, u64 *src);
void StatOutput(u64 *stat);
+#endif
+
void ALWAYS_INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) {
- if (kCollectStats)
- thr->stat[typ] += n;
+#if TSAN_COLLECT_STATS
+ thr->stat[typ] += n;
+#endif
}
void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) {
- if (kCollectStats)
- thr->stat[typ] = n;
+#if TSAN_COLLECT_STATS
+ thr->stat[typ] = n;
+#endif
}
void MapShadow(uptr addr, uptr size);
-void MapThreadTrace(uptr addr, uptr size);
+void MapThreadTrace(uptr addr, uptr size, const char *name);
void DontNeedShadowFor(uptr addr, uptr size);
void InitializeShadowMemory();
void InitializeInterceptors();
void InitializeLibIgnore();
void InitializeDynamicAnnotations();
+void ForkBefore(ThreadState *thr, uptr pc);
+void ForkParentAfter(ThreadState *thr, uptr pc);
+void ForkChildAfter(ThreadState *thr, uptr pc);
+
void ReportRace(ThreadState *thr);
-bool OutputReport(Context *ctx,
- const ScopedReport &srep,
- const ReportStack *suppress_stack1 = 0,
- const ReportStack *suppress_stack2 = 0,
- const ReportLocation *suppress_loc = 0);
-bool IsFiredSuppression(Context *ctx,
- const ScopedReport &srep,
- const StackTrace &trace);
+bool OutputReport(ThreadState *thr, const ScopedReport &srep);
+bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace);
bool IsExpectedReport(uptr addr, uptr size);
void PrintMatchedBenignRaces();
-bool FrameIsInternal(const ReportStack *frame);
-ReportStack *SkipTsanInternalFrames(ReportStack *ent);
#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1
# define DPrintf Printf
u32 CurrentStackId(ThreadState *thr, uptr pc);
ReportStack *SymbolizeStackId(u32 stack_id);
void PrintCurrentStack(ThreadState *thr, uptr pc);
-void PrintCurrentStackSlow(); // uses libunwind
+void PrintCurrentStackSlow(uptr pc); // uses libunwind
void Initialize(ThreadState *thr);
int Finalize(ThreadState *thr);
-SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr,
- bool write_lock, bool create);
-SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr);
+void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write);
+void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write);
void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic);
void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
bool rw, bool recursive, bool linker_init);
void MutexDestroy(ThreadState *thr, uptr pc, uptr addr);
-void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec = 1);
+void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec = 1,
+ bool try_lock = false);
int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false);
-void MutexReadLock(ThreadState *thr, uptr pc, uptr addr);
+void MutexReadLock(ThreadState *thr, uptr pc, uptr addr, bool try_lock = false);
void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr);
void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr);
void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD
void Acquire(ThreadState *thr, uptr pc, uptr addr);
+// AcquireGlobal synchronizes the current thread with all other threads.
+// In terms of happens-before relation, it draws a HB edge from all threads
+// (where they happen to execute right now) to the current thread. We use it to
+// handle Go finalizers. Namely, finalizer goroutine executes AcquireGlobal
+// right before executing finalizers. This provides a coarse, but simple
+// approximation of the actual required synchronization.
void AcquireGlobal(ThreadState *thr, uptr pc);
void Release(ThreadState *thr, uptr pc, uptr addr);
void ReleaseStore(ThreadState *thr, uptr pc, uptr addr);
// The trick is that the call preserves all registers and the compiler
// does not treat it as a call.
// If it does not work for you, use normal call.
-#if TSAN_DEBUG == 0
+#if !SANITIZER_DEBUG && defined(__x86_64__) && !SANITIZER_MAC
// The caller may not create the stack frame for itself at all,
// so we create a reserve stack frame for it (1024b must be enough).
#define HACKY_CALL(f) \
extern "C" void __tsan_trace_switch();
void ALWAYS_INLINE TraceAddEvent(ThreadState *thr, FastState fs,
EventType typ, u64 addr) {
+ if (!kCollectHistory)
+ return;
DCHECK_GE((int)typ, 0);
DCHECK_LE((int)typ, 7);
DCHECK_EQ(GetLsb(addr, 61), addr);
StatInc(thr, StatEvents);
u64 pos = fs.GetTracePos();
if (UNLIKELY((pos % kTracePartSize) == 0)) {
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
HACKY_CALL(__tsan_trace_switch);
#else
TraceSwitch(thr);
*evp = ev;
}
+#ifndef SANITIZER_GO
+uptr ALWAYS_INLINE HeapEnd() {
+ return kHeapMemEnd + PrimaryAllocator::AdditionalSize();
+}
+#endif
+
} // namespace __tsan
#endif // TSAN_RTL_H
--- /dev/null
+#include "sanitizer_common/sanitizer_asm.h"
+.hidden __tsan_setjmp
+.comm _ZN14__interception11real_setjmpE,8,8
+.type setjmp, @function
+setjmp:
+ CFI_STARTPROC
+
+ // save env parameters for function call
+ stp x29, x30, [sp, -32]!
+ CFI_DEF_CFA_OFFSET (32)
+ CFI_OFFSET (29, -32)
+ CFI_OFFSET (30, -24)
+
+ // Adjust the SP for previous frame
+ add x29, sp, 0
+ CFI_DEF_CFA_REGISTER (29)
+
+ // Save jmp_buf
+ str x19, [sp, 16]
+ CFI_OFFSET (19, -16)
+ mov x19, x0
+
+ // SP pointer mangling (see glibc setjmp)
+ adrp x2, :got:__pointer_chk_guard
+ ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
+ add x0, x29, 32
+ ldr x2, [x2]
+ eor x1, x2, x0
+
+ // call tsan interceptor
+ bl __tsan_setjmp
+
+ // restore env parameter
+ mov x0, x19
+ ldr x19, [sp, 16]
+ ldp x29, x30, [sp], 32
+ CFI_RESTORE (30)
+ CFI_RESTORE (19)
+ CFI_DEF_CFA (31, 0)
+
+ // tail jump to libc setjmp
+ adrp x1, :got:_ZN14__interception11real_setjmpE
+ ldr x1, [x1, #:got_lo12:_ZN14__interception11real_setjmpE]
+ ldr x1, [x1]
+ br x1
+
+ CFI_ENDPROC
+.size setjmp, .-setjmp
+
+.comm _ZN14__interception12real__setjmpE,8,8
+.globl _setjmp
+.type _setjmp, @function
+_setjmp:
+ CFI_STARTPROC
+
+ // save env parameters for function call
+ stp x29, x30, [sp, -32]!
+ CFI_DEF_CFA_OFFSET (32)
+ CFI_OFFSET (29, -32)
+ CFI_OFFSET (30, -24)
+
+ // Adjust the SP for previous frame
+ add x29, sp, 0
+ CFI_DEF_CFA_REGISTER (29)
+
+ // Save jmp_buf
+ str x19, [sp, 16]
+ CFI_OFFSET (19, -16)
+ mov x19, x0
+
+ // SP pointer mangling (see glibc setjmp)
+ adrp x2, :got:__pointer_chk_guard
+ ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
+ add x0, x29, 32
+ ldr x2, [x2]
+ eor x1, x2, x0
+
+ // call tsan interceptor
+ bl __tsan_setjmp
+
+ // Restore jmp_buf parameter
+ mov x0, x19
+ ldr x19, [sp, 16]
+ ldp x29, x30, [sp], 32
+ CFI_RESTORE (30)
+ CFI_RESTORE (19)
+ CFI_DEF_CFA (31, 0)
+
+ // tail jump to libc setjmp
+ adrp x1, :got:_ZN14__interception12real__setjmpE
+ ldr x1, [x1, #:got_lo12:_ZN14__interception12real__setjmpE]
+ ldr x1, [x1]
+ br x1
+
+ CFI_ENDPROC
+.size _setjmp, .-_setjmp
+
+.comm _ZN14__interception14real_sigsetjmpE,8,8
+.globl sigsetjmp
+.type sigsetjmp, @function
+sigsetjmp:
+ CFI_STARTPROC
+
+ // save env parameters for function call
+ stp x29, x30, [sp, -32]!
+ CFI_DEF_CFA_OFFSET (32)
+ CFI_OFFSET (29, -32)
+ CFI_OFFSET (30, -24)
+
+ // Adjust the SP for previous frame
+ add x29, sp, 0
+ CFI_DEF_CFA_REGISTER (29)
+
+ // Save jmp_buf and savesigs
+ stp x19, x20, [sp, 16]
+ CFI_OFFSET (19, -16)
+ CFI_OFFSET (20, -8)
+ mov w20, w1
+ mov x19, x0
+
+ // SP pointer mangling (see glibc setjmp)
+ adrp x2, :got:__pointer_chk_guard
+ ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
+ add x0, x29, 32
+ ldr x2, [x2]
+ eor x1, x2, x0
+
+ // call tsan interceptor
+ bl __tsan_setjmp
+
+ // restore env parameter
+ mov w1, w20
+ mov x0, x19
+ ldp x19, x20, [sp, 16]
+ ldp x29, x30, [sp], 32
+ CFI_RESTORE (30)
+ CFI_RESTORE (29)
+ CFI_RESTORE (19)
+ CFI_RESTORE (20)
+ CFI_DEF_CFA (31, 0)
+
+ // tail jump to libc sigsetjmp
+ adrp x2, :got:_ZN14__interception14real_sigsetjmpE
+ ldr x2, [x2, #:got_lo12:_ZN14__interception14real_sigsetjmpE]
+ ldr x2, [x2]
+ br x2
+ CFI_ENDPROC
+.size sigsetjmp, .-sigsetjmp
+
+.comm _ZN14__interception16real___sigsetjmpE,8,8
+.globl __sigsetjmp
+.type __sigsetjmp, @function
+__sigsetjmp:
+ CFI_STARTPROC
+
+ // save env parameters for function call
+ stp x29, x30, [sp, -32]!
+ CFI_DEF_CFA_OFFSET (32)
+ CFI_OFFSET (29, -32)
+ CFI_OFFSET (30, -24)
+
+ // Adjust the SP for previous frame
+ add x29, sp, 0
+ CFI_DEF_CFA_REGISTER (29)
+
+ // Save jmp_buf and savesigs
+ stp x19, x20, [sp, 16]
+ CFI_OFFSET (19, -16)
+ CFI_OFFSET (20, -8)
+ mov w20, w1
+ mov x19, x0
+
+ // SP pointer mangling (see glibc setjmp)
+ adrp x2, :got:__pointer_chk_guard
+ ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
+ add x0, x29, 32
+ ldr x2, [x2]
+ eor x1, x2, x0
+
+ // call tsan interceptor
+ bl __tsan_setjmp
+
+ mov w1, w20
+ mov x0, x19
+ ldp x19, x20, [sp, 16]
+ ldp x29, x30, [sp], 32
+ CFI_RESTORE (30)
+ CFI_RESTORE (29)
+ CFI_RESTORE (19)
+ CFI_RESTORE (20)
+ CFI_DEF_CFA (31, 0)
+
+ // tail jump to libc __sigsetjmp
+ adrp x2, :got:_ZN14__interception16real___sigsetjmpE
+ ldr x2, [x2, #:got_lo12:_ZN14__interception16real___sigsetjmpE]
+ ldr x2, [x2]
+ br x2
+ CFI_ENDPROC
+.size __sigsetjmp, .-__sigsetjmp
+
+#if defined(__linux__)
+/* We do not need executable stack. */
+.section .note.GNU-stack,"",@progbits
+#endif
CFI_ADJUST_CFA_OFFSET(8)
CFI_REL_OFFSET(%rdi, 0)
// obtain %rsp
+#if defined(__FreeBSD__)
+ lea 8(%rsp), %rdi
+ mov %rdi, %rsi
+#else
lea 16(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
+#endif
// call tsan interceptor
call __tsan_setjmp
// restore env parameter
CFI_ADJUST_CFA_OFFSET(8)
CFI_REL_OFFSET(%rdi, 0)
// obtain %rsp
+#if defined(__FreeBSD__)
+ lea 8(%rsp), %rdi
+ mov %rdi, %rsi
+#else
lea 16(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
+#endif
// call tsan interceptor
call __tsan_setjmp
// restore env parameter
sub $8, %rsp
CFI_ADJUST_CFA_OFFSET(8)
// obtain %rsp
+#if defined(__FreeBSD__)
+ lea 24(%rsp), %rdi
+ mov %rdi, %rsi
+#else
lea 32(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
+#endif
// call tsan interceptor
call __tsan_setjmp
// unalign stack frame
sub $8, %rsp
CFI_ADJUST_CFA_OFFSET(8)
// obtain %rsp
+#if defined(__FreeBSD__)
+ lea 24(%rsp), %rdi
+ mov %rdi, %rsi
+#else
lea 32(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
+#endif
// call tsan interceptor
call __tsan_setjmp
// unalign stack frame
CFI_ENDPROC
.size __sigsetjmp, .-__sigsetjmp
-#ifdef __linux__
+#if defined(__FreeBSD__) || defined(__linux__)
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif
//
//===----------------------------------------------------------------------===//
+#include <sanitizer_common/sanitizer_deadlock_detector_interface.h>
+#include <sanitizer_common/sanitizer_stackdepot.h>
+
#include "tsan_rtl.h"
#include "tsan_flags.h"
#include "tsan_sync.h"
namespace __tsan {
+void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r);
+
+struct Callback : DDCallback {
+ ThreadState *thr;
+ uptr pc;
+
+ Callback(ThreadState *thr, uptr pc)
+ : thr(thr)
+ , pc(pc) {
+ DDCallback::pt = thr->dd_pt;
+ DDCallback::lt = thr->dd_lt;
+ }
+
+ u32 Unwind() override { return CurrentStackId(thr, pc); }
+ int UniqueTid() override { return thr->unique_id; }
+};
+
+void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexInit(&cb, &s->dd);
+ s->dd.ctx = s->GetId();
+}
+
+static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
+ uptr addr, u64 mid) {
+ // In Go, these misuses are either impossible, or detected by std lib,
+ // or false positives (e.g. unlock in a different thread).
+ if (kGoMode)
+ return;
+ ThreadRegistryLock l(ctx->thread_registry);
+ ScopedReport rep(typ);
+ rep.AddMutex(mid);
+ VarSizeStackTrace trace;
+ ObtainCurrentStack(thr, pc, &trace);
+ rep.AddStack(trace, true);
+ rep.AddLocation(addr, 1);
+ OutputReport(thr, rep);
+}
+
void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
bool rw, bool recursive, bool linker_init) {
- Context *ctx = CTX();
- CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr);
StatInc(thr, StatMutexCreate);
if (!linker_init && IsAppMem(addr)) {
MemoryWrite(thr, pc, addr, kSizeLog1);
thr->is_freeing = false;
}
- SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
s->is_rw = rw;
s->is_recursive = recursive;
s->is_linker_init = linker_init;
+ if (kCppMode && s->creation_stack_id == 0)
+ s->creation_stack_id = CurrentStackId(thr, pc);
s->mtx.Unlock();
}
void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
- Context *ctx = CTX();
- CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
StatInc(thr, StatMutexDestroy);
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
// Global mutexes not marked as LINKER_INITIALIZED
// cause tons of not interesting reports, so just ignore it.
if (IsGlobalVar(addr))
return;
#endif
- SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr);
- if (s == 0)
- return;
if (IsAppMem(addr)) {
CHECK(!thr->is_freeing);
thr->is_freeing = true;
MemoryWrite(thr, pc, addr, kSizeLog1);
thr->is_freeing = false;
}
+ SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr);
+ if (s == 0)
+ return;
+ if (common_flags()->detect_deadlocks) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexDestroy(&cb, &s->dd);
+ ctx->dd->MutexInit(&cb, &s->dd);
+ }
+ bool unlock_locked = false;
if (flags()->report_destroy_locked
&& s->owner_tid != SyncVar::kInvalidTid
&& !s->is_broken) {
s->is_broken = true;
+ unlock_locked = true;
+ }
+ u64 mid = s->GetId();
+ u32 last_lock = s->last_lock;
+ if (!unlock_locked)
+ s->Reset(thr); // must not reset it before the report is printed
+ s->mtx.Unlock();
+ if (unlock_locked) {
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeMutexDestroyLocked);
- rep.AddMutex(s);
- StackTrace trace;
- trace.ObtainCurrent(thr, pc);
- rep.AddStack(&trace);
- FastState last(s->last_lock);
+ rep.AddMutex(mid);
+ VarSizeStackTrace trace;
+ ObtainCurrentStack(thr, pc, &trace);
+ rep.AddStack(trace);
+ FastState last(last_lock);
RestoreStack(last.tid(), last.epoch(), &trace, 0);
- rep.AddStack(&trace);
- rep.AddLocation(s->addr, 1);
- OutputReport(ctx, rep);
+ rep.AddStack(trace, true);
+ rep.AddLocation(addr, 1);
+ OutputReport(thr, rep);
}
- thr->mset.Remove(s->GetId());
- DestroyAndFree(s);
+ if (unlock_locked) {
+ SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr);
+ if (s != 0) {
+ s->Reset(thr);
+ s->mtx.Unlock();
+ }
+ }
+ thr->mset.Remove(mid);
+ // s will be destroyed and freed in MetaMap::FreeBlock.
}
-void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) {
- CHECK_GT(thr->in_rtl, 0);
+void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec, bool try_lock) {
DPrintf("#%d: MutexLock %zx rec=%d\n", thr->tid, addr, rec);
CHECK_GT(rec, 0);
if (IsAppMem(addr))
MemoryReadAtomic(thr, pc, addr, kSizeLog1);
- SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId());
+ bool report_double_lock = false;
if (s->owner_tid == SyncVar::kInvalidTid) {
CHECK_EQ(s->recursion, 0);
s->owner_tid = thr->tid;
s->last_lock = thr->fast_state.raw();
} else if (s->owner_tid == thr->tid) {
CHECK_GT(s->recursion, 0);
- } else {
- Printf("ThreadSanitizer WARNING: double lock of mutex %p\n", addr);
- PrintCurrentStack(thr, pc);
+ } else if (flags()->report_mutex_bugs && !s->is_broken) {
+ s->is_broken = true;
+ report_double_lock = true;
}
if (s->recursion == 0) {
StatInc(thr, StatMutexLock);
}
s->recursion += rec;
thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
+ if (common_flags()->detect_deadlocks && (s->recursion - rec) == 0) {
+ Callback cb(thr, pc);
+ if (!try_lock)
+ ctx->dd->MutexBeforeLock(&cb, &s->dd, true);
+ ctx->dd->MutexAfterLock(&cb, &s->dd, true, try_lock);
+ }
+ u64 mid = s->GetId();
s->mtx.Unlock();
+ // Can't touch s after this point.
+ if (report_double_lock)
+ ReportMutexMisuse(thr, pc, ReportTypeMutexDoubleLock, addr, mid);
+ if (common_flags()->detect_deadlocks) {
+ Callback cb(thr, pc);
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
}
int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) {
- CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexUnlock %zx all=%d\n", thr->tid, addr, all);
if (IsAppMem(addr))
MemoryReadAtomic(thr, pc, addr, kSizeLog1);
- SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
int rec = 0;
- if (s->recursion == 0) {
- if (!s->is_broken) {
- s->is_broken = true;
- Printf("ThreadSanitizer WARNING: unlock of unlocked mutex %p\n", addr);
- PrintCurrentStack(thr, pc);
- }
- } else if (s->owner_tid != thr->tid) {
- if (!s->is_broken) {
+ bool report_bad_unlock = false;
+ if (kCppMode && (s->recursion == 0 || s->owner_tid != thr->tid)) {
+ if (flags()->report_mutex_bugs && !s->is_broken) {
s->is_broken = true;
- Printf("ThreadSanitizer WARNING: mutex %p is unlocked by wrong thread\n",
- addr);
- PrintCurrentStack(thr, pc);
+ report_bad_unlock = true;
}
} else {
rec = all ? s->recursion : 1;
}
}
thr->mset.Del(s->GetId(), true);
+ if (common_flags()->detect_deadlocks && s->recursion == 0 &&
+ !report_bad_unlock) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexBeforeUnlock(&cb, &s->dd, true);
+ }
+ u64 mid = s->GetId();
s->mtx.Unlock();
+ // Can't touch s after this point.
+ if (report_bad_unlock)
+ ReportMutexMisuse(thr, pc, ReportTypeMutexBadUnlock, addr, mid);
+ if (common_flags()->detect_deadlocks && !report_bad_unlock) {
+ Callback cb(thr, pc);
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
return rec;
}
-void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
- CHECK_GT(thr->in_rtl, 0);
+void MutexReadLock(ThreadState *thr, uptr pc, uptr addr, bool trylock) {
DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr);
StatInc(thr, StatMutexReadLock);
if (IsAppMem(addr))
MemoryReadAtomic(thr, pc, addr, kSizeLog1);
- SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false);
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId());
+ bool report_bad_lock = false;
if (s->owner_tid != SyncVar::kInvalidTid) {
- Printf("ThreadSanitizer WARNING: read lock of a write locked mutex %p\n",
- addr);
- PrintCurrentStack(thr, pc);
+ if (flags()->report_mutex_bugs && !s->is_broken) {
+ s->is_broken = true;
+ report_bad_lock = true;
+ }
}
AcquireImpl(thr, pc, &s->clock);
s->last_lock = thr->fast_state.raw();
thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
+ if (common_flags()->detect_deadlocks && s->recursion == 0) {
+ Callback cb(thr, pc);
+ if (!trylock)
+ ctx->dd->MutexBeforeLock(&cb, &s->dd, false);
+ ctx->dd->MutexAfterLock(&cb, &s->dd, false, trylock);
+ }
+ u64 mid = s->GetId();
s->mtx.ReadUnlock();
+ // Can't touch s after this point.
+ if (report_bad_lock)
+ ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadLock, addr, mid);
+ if (common_flags()->detect_deadlocks) {
+ Callback cb(thr, pc);
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
}
void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
- CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
StatInc(thr, StatMutexReadUnlock);
if (IsAppMem(addr))
MemoryReadAtomic(thr, pc, addr, kSizeLog1);
- SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch();
TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
+ bool report_bad_unlock = false;
if (s->owner_tid != SyncVar::kInvalidTid) {
- Printf("ThreadSanitizer WARNING: read unlock of a write locked mutex %p\n",
- addr);
- PrintCurrentStack(thr, pc);
+ if (flags()->report_mutex_bugs && !s->is_broken) {
+ s->is_broken = true;
+ report_bad_unlock = true;
+ }
}
ReleaseImpl(thr, pc, &s->read_clock);
+ if (common_flags()->detect_deadlocks && s->recursion == 0) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexBeforeUnlock(&cb, &s->dd, false);
+ }
+ u64 mid = s->GetId();
s->mtx.Unlock();
- thr->mset.Del(s->GetId(), false);
+ // Can't touch s after this point.
+ thr->mset.Del(mid, false);
+ if (report_bad_unlock)
+ ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadUnlock, addr, mid);
+ if (common_flags()->detect_deadlocks) {
+ Callback cb(thr, pc);
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
}
void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
- CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
if (IsAppMem(addr))
MemoryReadAtomic(thr, pc, addr, kSizeLog1);
- SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
bool write = true;
+ bool report_bad_unlock = false;
if (s->owner_tid == SyncVar::kInvalidTid) {
// Seems to be read unlock.
write = false;
}
} else if (!s->is_broken) {
s->is_broken = true;
- Printf("ThreadSanitizer WARNING: mutex %p is unlock by wrong thread\n",
- addr);
- PrintCurrentStack(thr, pc);
+ report_bad_unlock = true;
}
thr->mset.Del(s->GetId(), write);
+ if (common_flags()->detect_deadlocks && s->recursion == 0) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexBeforeUnlock(&cb, &s->dd, write);
+ }
+ u64 mid = s->GetId();
s->mtx.Unlock();
+ // Can't touch s after this point.
+ if (report_bad_unlock)
+ ReportMutexMisuse(thr, pc, ReportTypeMutexBadUnlock, addr, mid);
+ if (common_flags()->detect_deadlocks) {
+ Callback cb(thr, pc);
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
}
void MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
- Context *ctx = CTX();
- CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr);
- SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
s->owner_tid = SyncVar::kInvalidTid;
s->recursion = 0;
s->mtx.Unlock();
}
void Acquire(ThreadState *thr, uptr pc, uptr addr) {
- CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
if (thr->ignore_sync)
return;
- SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false);
AcquireImpl(thr, pc, &s->clock);
s->mtx.ReadUnlock();
}
DPrintf("#%d: AcquireGlobal\n", thr->tid);
if (thr->ignore_sync)
return;
- ThreadRegistryLock l(CTX()->thread_registry);
- CTX()->thread_registry->RunCallbackForEachThreadLocked(
+ ThreadRegistryLock l(ctx->thread_registry);
+ ctx->thread_registry->RunCallbackForEachThreadLocked(
UpdateClockCallback, thr);
}
void Release(ThreadState *thr, uptr pc, uptr addr) {
- CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: Release %zx\n", thr->tid, addr);
if (thr->ignore_sync)
return;
- SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
}
void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
- CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
if (thr->ignore_sync)
return;
- SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
s->mtx.Unlock();
}
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) {
ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
if (thr->ignore_sync)
return;
thr->last_sleep_stack_id = CurrentStackId(thr, pc);
- ThreadRegistryLock l(CTX()->thread_registry);
- CTX()->thread_registry->RunCallbackForEachThreadLocked(
+ ThreadRegistryLock l(ctx->thread_registry);
+ ctx->thread_registry->RunCallbackForEachThreadLocked(
UpdateSleepClockCallback, thr);
}
#endif
void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) {
if (thr->ignore_sync)
return;
- thr->clock.set(thr->tid, thr->fast_state.epoch());
- thr->clock.acquire(c);
+ thr->clock.set(thr->fast_state.epoch());
+ thr->clock.acquire(&thr->clock_cache, c);
StatInc(thr, StatSyncAcquire);
}
void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) {
if (thr->ignore_sync)
return;
- thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.set(thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.release(c);
+ thr->clock.release(&thr->clock_cache, c);
StatInc(thr, StatSyncRelease);
}
void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c) {
if (thr->ignore_sync)
return;
- thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.set(thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.ReleaseStore(c);
+ thr->clock.ReleaseStore(&thr->clock_cache, c);
StatInc(thr, StatSyncRelease);
}
void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) {
if (thr->ignore_sync)
return;
- thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.set(thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.acq_rel(c);
+ thr->clock.acq_rel(&thr->clock_cache, c);
StatInc(thr, StatSyncAcquire);
StatInc(thr, StatSyncRelease);
}
+void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
+ if (r == 0)
+ return;
+ ThreadRegistryLock l(ctx->thread_registry);
+ ScopedReport rep(ReportTypeDeadlock);
+ for (int i = 0; i < r->n; i++) {
+ rep.AddMutex(r->loop[i].mtx_ctx0);
+ rep.AddUniqueTid((int)r->loop[i].thr_ctx);
+ rep.AddThread((int)r->loop[i].thr_ctx);
+ }
+ uptr dummy_pc = 0x42;
+ for (int i = 0; i < r->n; i++) {
+ for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
+ u32 stk = r->loop[i].stk[j];
+ if (stk && stk != 0xffffffff) {
+ rep.AddStack(StackDepotGet(stk), true);
+ } else {
+ // Sometimes we fail to extract the stack trace (FIXME: investigate),
+ // but we should still produce some stack trace in the report.
+ rep.AddStack(StackTrace(&dummy_pc, 1), true);
+ }
+ }
+ }
+ OutputReport(thr, rep);
+}
+
} // namespace __tsan
using namespace __sanitizer; // NOLINT
-static ReportStack *SymbolizeStack(const StackTrace& trace);
+static ReportStack *SymbolizeStack(StackTrace trace);
void TsanCheckFailed(const char *file, int line, const char *cond,
u64 v1, u64 v2) {
- ScopedInRtl in_rtl;
+ // There is high probability that interceptors will check-fail as well,
+ // on the other hand there is no sense in processing interceptors
+ // since we are going to die soon.
+ ScopedIgnoreInterceptors ignore;
Printf("FATAL: ThreadSanitizer CHECK failed: "
"%s:%d \"%s\" (0x%zx, 0x%zx)\n",
file, line, cond, (uptr)v1, (uptr)v2);
- PrintCurrentStackSlow();
+ PrintCurrentStackSlow(StackTrace::GetCurrentPc());
Die();
}
}
#endif
-static void StackStripMain(ReportStack *stack) {
- ReportStack *last_frame = 0;
- ReportStack *last_frame2 = 0;
- const char *prefix = "__interceptor_";
- uptr prefix_len = internal_strlen(prefix);
- const char *path_prefix = flags()->strip_path_prefix;
- uptr path_prefix_len = internal_strlen(path_prefix);
- char *pos;
- for (ReportStack *ent = stack; ent; ent = ent->next) {
- if (ent->func && 0 == internal_strncmp(ent->func, prefix, prefix_len))
- ent->func += prefix_len;
- if (ent->file && (pos = internal_strstr(ent->file, path_prefix)))
- ent->file = pos + path_prefix_len;
- if (ent->file && ent->file[0] == '.' && ent->file[1] == '/')
- ent->file += 2;
+static void StackStripMain(SymbolizedStack *frames) {
+ SymbolizedStack *last_frame = nullptr;
+ SymbolizedStack *last_frame2 = nullptr;
+ for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
last_frame2 = last_frame;
- last_frame = ent;
+ last_frame = cur;
}
if (last_frame2 == 0)
return;
- const char *last = last_frame->func;
-#ifndef TSAN_GO
- const char *last2 = last_frame2->func;
+#ifndef SANITIZER_GO
+ const char *last = last_frame->info.function;
+ const char *last2 = last_frame2->info.function;
// Strip frame above 'main'
if (last2 && 0 == internal_strcmp(last2, "main")) {
- last_frame2->next = 0;
+ last_frame->ClearAll();
+ last_frame2->next = nullptr;
// Strip our internal thread start routine.
} else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) {
- last_frame2->next = 0;
+ last_frame->ClearAll();
+ last_frame2->next = nullptr;
// Strip global ctors init.
} else if (last && 0 == internal_strcmp(last, "__do_global_ctors_aux")) {
- last_frame2->next = 0;
+ last_frame->ClearAll();
+ last_frame2->next = nullptr;
// If both are 0, then we probably just failed to symbolize.
} else if (last || last2) {
// Ensure that we recovered stack completely. Trimmed stack
// can actually happen if we do not instrument some code,
// so it's only a debug print. However we must try hard to not miss it
// due to our fault.
- DPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc);
+ DPrintf("Bottom stack frame is missed\n");
}
#else
// The last frame always point into runtime (gosched0, goexit0, runtime.main).
- last_frame2->next = 0;
- (void)last;
+ last_frame->ClearAll();
+ last_frame2->next = nullptr;
#endif
}
-#ifndef TSAN_GO
ReportStack *SymbolizeStackId(u32 stack_id) {
- uptr ssz = 0;
- const uptr *stack = StackDepotGet(stack_id, &ssz);
- if (stack == 0)
+ if (stack_id == 0)
return 0;
- StackTrace trace;
- trace.Init(stack, ssz);
- return SymbolizeStack(trace);
+ StackTrace stack = StackDepotGet(stack_id);
+ if (stack.trace == nullptr)
+ return nullptr;
+ return SymbolizeStack(stack);
}
-#endif
-static ReportStack *SymbolizeStack(const StackTrace& trace) {
- if (trace.IsEmpty())
+static ReportStack *SymbolizeStack(StackTrace trace) {
+ if (trace.size == 0)
return 0;
- ReportStack *stack = 0;
- for (uptr si = 0; si < trace.Size(); si++) {
- const uptr pc = trace.Get(si);
-#ifndef TSAN_GO
- // We obtain the return address, that is, address of the next instruction,
- // so offset it by 1 byte.
- const uptr pc1 = __sanitizer::StackTrace::GetPreviousInstructionPc(pc);
-#else
- // FIXME(dvyukov): Go sometimes uses address of a function as top pc.
+ SymbolizedStack *top = nullptr;
+ for (uptr si = 0; si < trace.size; si++) {
+ const uptr pc = trace.trace[si];
uptr pc1 = pc;
- if (si != trace.Size() - 1)
- pc1 -= 1;
-#endif
- ReportStack *ent = SymbolizeCode(pc1);
+ // We obtain the return address, but we're interested in the previous
+ // instruction.
+ if ((pc & kExternalPCBit) == 0)
+ pc1 = StackTrace::GetPreviousInstructionPc(pc);
+ SymbolizedStack *ent = SymbolizeCode(pc1);
CHECK_NE(ent, 0);
- ReportStack *last = ent;
+ SymbolizedStack *last = ent;
while (last->next) {
- last->pc = pc; // restore original pc for report
+ last->info.address = pc; // restore original pc for report
last = last->next;
}
- last->pc = pc; // restore original pc for report
- last->next = stack;
- stack = ent;
+ last->info.address = pc; // restore original pc for report
+ last->next = top;
+ top = ent;
}
- StackStripMain(stack);
+ StackStripMain(top);
+
+ ReportStack *stack = ReportStack::New();
+ stack->frames = top;
return stack;
}
ScopedReport::ScopedReport(ReportType typ) {
- ctx_ = CTX();
- ctx_->thread_registry->CheckLocked();
+ ctx->thread_registry->CheckLocked();
void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc));
rep_ = new(mem) ReportDesc;
rep_->typ = typ;
- ctx_->report_mtx.Lock();
+ ctx->report_mtx.Lock();
CommonSanitizerReportMutex.Lock();
}
ScopedReport::~ScopedReport() {
CommonSanitizerReportMutex.Unlock();
- ctx_->report_mtx.Unlock();
+ ctx->report_mtx.Unlock();
DestroyAndFree(rep_);
}
-void ScopedReport::AddStack(const StackTrace *stack) {
+void ScopedReport::AddStack(StackTrace stack, bool suppressable) {
ReportStack **rs = rep_->stacks.PushBack();
- *rs = SymbolizeStack(*stack);
+ *rs = SymbolizeStack(stack);
+ (*rs)->suppressable = suppressable;
}
-void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
- const StackTrace *stack, const MutexSet *mset) {
+void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, StackTrace stack,
+ const MutexSet *mset) {
void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
ReportMop *mop = new(mem) ReportMop;
rep_->mops.PushBack(mop);
mop->size = s.size();
mop->write = s.IsWrite();
mop->atomic = s.IsAtomic();
- mop->stack = SymbolizeStack(*stack);
+ mop->stack = SymbolizeStack(stack);
+ if (mop->stack)
+ mop->stack->suppressable = true;
for (uptr i = 0; i < mset->Size(); i++) {
MutexSet::Desc d = mset->Get(i);
- u64 uid = 0;
- uptr addr = SyncVar::SplitId(d.id, &uid);
- SyncVar *s = ctx_->synctab.GetIfExistsAndLock(addr, false);
- // Check that the mutex is still alive.
- // Another mutex can be created at the same address,
- // so check uid as well.
- if (s && s->CheckId(uid)) {
- ReportMopMutex mtx = {s->uid, d.write};
- mop->mset.PushBack(mtx);
- AddMutex(s);
- } else {
- ReportMopMutex mtx = {d.id, d.write};
- mop->mset.PushBack(mtx);
- AddMutex(d.id);
- }
- if (s)
- s->mtx.ReadUnlock();
+ u64 mid = this->AddMutex(d.id);
+ ReportMopMutex mtx = {mid, d.write};
+ mop->mset.PushBack(mtx);
}
}
-void ScopedReport::AddThread(const ThreadContext *tctx) {
+void ScopedReport::AddUniqueTid(int unique_tid) {
+ rep_->unique_tids.PushBack(unique_tid);
+}
+
+void ScopedReport::AddThread(const ThreadContext *tctx, bool suppressable) {
for (uptr i = 0; i < rep_->threads.Size(); i++) {
if ((u32)rep_->threads[i]->id == tctx->tid)
return;
rt->id = tctx->tid;
rt->pid = tctx->os_id;
rt->running = (tctx->status == ThreadStatusRunning);
- rt->name = tctx->name ? internal_strdup(tctx->name) : 0;
+ rt->name = internal_strdup(tctx->name);
rt->parent_tid = tctx->parent_tid;
rt->stack = 0;
-#ifdef TSAN_GO
- rt->stack = SymbolizeStack(tctx->creation_stack);
-#else
rt->stack = SymbolizeStackId(tctx->creation_stack_id);
-#endif
+ if (rt->stack)
+ rt->stack->suppressable = suppressable;
}
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
static ThreadContext *FindThreadByUidLocked(int unique_id) {
- Context *ctx = CTX();
ctx->thread_registry->CheckLocked();
for (unsigned i = 0; i < kMaxTid; i++) {
ThreadContext *tctx = static_cast<ThreadContext*>(
}
static ThreadContext *FindThreadByTidLocked(int tid) {
- Context *ctx = CTX();
ctx->thread_registry->CheckLocked();
return static_cast<ThreadContext*>(
ctx->thread_registry->GetThreadLocked(tid));
}
ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) {
- Context *ctx = CTX();
ctx->thread_registry->CheckLocked();
ThreadContext *tctx = static_cast<ThreadContext*>(
ctx->thread_registry->FindThreadContextLocked(IsInStackOrTls,
}
#endif
+void ScopedReport::AddThread(int unique_tid, bool suppressable) {
+#ifndef SANITIZER_GO
+ if (const ThreadContext *tctx = FindThreadByUidLocked(unique_tid))
+ AddThread(tctx, suppressable);
+#endif
+}
+
void ScopedReport::AddMutex(const SyncVar *s) {
for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
if (rep_->mutexes[i]->id == s->uid)
ReportMutex *rm = new(mem) ReportMutex();
rep_->mutexes.PushBack(rm);
rm->id = s->uid;
+ rm->addr = s->addr;
rm->destroyed = false;
- rm->stack = 0;
-#ifndef TSAN_GO
rm->stack = SymbolizeStackId(s->creation_stack_id);
-#endif
}
-void ScopedReport::AddMutex(u64 id) {
+u64 ScopedReport::AddMutex(u64 id) {
+ u64 uid = 0;
+ u64 mid = id;
+ uptr addr = SyncVar::SplitId(id, &uid);
+ SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr);
+ // Check that the mutex is still alive.
+ // Another mutex can be created at the same address,
+ // so check uid as well.
+ if (s && s->CheckId(uid)) {
+ mid = s->uid;
+ AddMutex(s);
+ } else {
+ AddDeadMutex(id);
+ }
+ if (s)
+ s->mtx.Unlock();
+ return mid;
+}
+
+void ScopedReport::AddDeadMutex(u64 id) {
for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
if (rep_->mutexes[i]->id == id)
return;
ReportMutex *rm = new(mem) ReportMutex();
rep_->mutexes.PushBack(rm);
rm->id = id;
+ rm->addr = 0;
rm->destroyed = true;
rm->stack = 0;
}
void ScopedReport::AddLocation(uptr addr, uptr size) {
if (addr == 0)
return;
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
int fd = -1;
int creat_tid = -1;
u32 creat_stack = 0;
- if (FdLocation(addr, &fd, &creat_tid, &creat_stack)
- || FdLocation(AlternativeAddress(addr), &fd, &creat_tid, &creat_stack)) {
- void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
- ReportLocation *loc = new(mem) ReportLocation();
- rep_->locs.PushBack(loc);
- loc->type = ReportLocationFD;
+ if (FdLocation(addr, &fd, &creat_tid, &creat_stack)) {
+ ReportLocation *loc = ReportLocation::New(ReportLocationFD);
loc->fd = fd;
loc->tid = creat_tid;
loc->stack = SymbolizeStackId(creat_stack);
+ rep_->locs.PushBack(loc);
ThreadContext *tctx = FindThreadByUidLocked(creat_tid);
if (tctx)
AddThread(tctx);
return;
}
MBlock *b = 0;
- if (allocator()->PointerIsMine((void*)addr)
- && (b = user_mblock(0, (void*)addr))) {
- ThreadContext *tctx = FindThreadByTidLocked(b->Tid());
- void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
- ReportLocation *loc = new(mem) ReportLocation();
+ Allocator *a = allocator();
+ if (a->PointerIsMine((void*)addr)) {
+ void *block_begin = a->GetBlockBegin((void*)addr);
+ if (block_begin)
+ b = ctx->metamap.GetBlock((uptr)block_begin);
+ }
+ if (b != 0) {
+ ThreadContext *tctx = FindThreadByTidLocked(b->tid);
+ ReportLocation *loc = ReportLocation::New(ReportLocationHeap);
+ loc->heap_chunk_start = (uptr)allocator()->GetBlockBegin((void *)addr);
+ loc->heap_chunk_size = b->siz;
+ loc->tid = tctx ? tctx->tid : b->tid;
+ loc->stack = SymbolizeStackId(b->stk);
rep_->locs.PushBack(loc);
- loc->type = ReportLocationHeap;
- loc->addr = (uptr)allocator()->GetBlockBegin((void*)addr);
- loc->size = b->Size();
- loc->tid = tctx ? tctx->tid : b->Tid();
- loc->name = 0;
- loc->file = 0;
- loc->line = 0;
- loc->stack = 0;
- loc->stack = SymbolizeStackId(b->StackId());
if (tctx)
AddThread(tctx);
return;
}
bool is_stack = false;
if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) {
- void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
- ReportLocation *loc = new(mem) ReportLocation();
- rep_->locs.PushBack(loc);
- loc->type = is_stack ? ReportLocationStack : ReportLocationTLS;
+ ReportLocation *loc =
+ ReportLocation::New(is_stack ? ReportLocationStack : ReportLocationTLS);
loc->tid = tctx->tid;
+ rep_->locs.PushBack(loc);
AddThread(tctx);
}
- ReportLocation *loc = SymbolizeData(addr);
- if (loc) {
+ if (ReportLocation *loc = SymbolizeData(addr)) {
+ loc->suppressable = true;
rep_->locs.PushBack(loc);
return;
}
#endif
}
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
void ScopedReport::AddSleep(u32 stack_id) {
rep_->sleep = SymbolizeStackId(stack_id);
}
return rep_;
}
-void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) {
+void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
+ MutexSet *mset) {
// This function restores stack trace and mutex set for the thread/epoch.
// It does so by getting stack trace and mutex set at the beginning of
// trace part, and then replaying the trace till the given epoch.
- Context *ctx = CTX();
- ctx->thread_registry->CheckLocked();
- ThreadContext *tctx = static_cast<ThreadContext*>(
- ctx->thread_registry->GetThreadLocked(tid));
- if (tctx == 0)
- return;
- if (tctx->status != ThreadStatusRunning
- && tctx->status != ThreadStatusFinished
- && tctx->status != ThreadStatusDead)
- return;
- Trace* trace = ThreadTrace(tctx->tid);
- Lock l(&trace->mtx);
+ Trace* trace = ThreadTrace(tid);
+ ReadLock l(&trace->mtx);
const int partidx = (epoch / kTracePartSize) % TraceParts();
TraceHeader* hdr = &trace->headers[partidx];
- if (epoch < hdr->epoch0)
+ if (epoch < hdr->epoch0 || epoch >= hdr->epoch0 + kTracePartSize)
return;
+ CHECK_EQ(RoundDown(epoch, kTracePartSize), hdr->epoch0);
const u64 epoch0 = RoundDown(epoch, TraceSize());
const u64 eend = epoch % TraceSize();
const u64 ebegin = RoundDown(eend, kTracePartSize);
DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx);
- InternalScopedBuffer<uptr> stack(kShadowStackSize);
- for (uptr i = 0; i < hdr->stack0.Size(); i++) {
- stack[i] = hdr->stack0.Get(i);
- DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]);
+ Vector<uptr> stack(MBlockReportStack);
+ stack.Resize(hdr->stack0.size + 64);
+ for (uptr i = 0; i < hdr->stack0.size; i++) {
+ stack[i] = hdr->stack0.trace[i];
+ DPrintf2(" #%02zu: pc=%zx\n", i, stack[i]);
}
if (mset)
*mset = hdr->mset0;
- uptr pos = hdr->stack0.Size();
+ uptr pos = hdr->stack0.size;
Event *events = (Event*)GetThreadTrace(tid);
for (uptr i = ebegin; i <= eend; i++) {
Event ev = events[i];
if (typ == EventTypeMop) {
stack[pos] = pc;
} else if (typ == EventTypeFuncEnter) {
+ if (stack.Size() < pos + 2)
+ stack.Resize(pos + 2);
stack[pos++] = pc;
} else if (typ == EventTypeFuncExit) {
if (pos > 0)
if (pos == 0 && stack[0] == 0)
return;
pos++;
- stk->Init(stack.data(), pos);
+ stk->Init(&stack[0], pos);
}
-static bool HandleRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
- uptr addr_min, uptr addr_max) {
- Context *ctx = CTX();
+static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
+ uptr addr_min, uptr addr_max) {
bool equal_stack = false;
RacyStacks hash;
- if (flags()->suppress_equal_stacks) {
- hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
- hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
- for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
- if (hash == ctx->racy_stacks[i]) {
- DPrintf("ThreadSanitizer: suppressing report as doubled (stack)\n");
- equal_stack = true;
- break;
- }
- }
- }
bool equal_address = false;
RacyAddress ra0 = {addr_min, addr_max};
- if (flags()->suppress_equal_addresses) {
- for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) {
- RacyAddress ra2 = ctx->racy_addresses[i];
- uptr maxbeg = max(ra0.addr_min, ra2.addr_min);
- uptr minend = min(ra0.addr_max, ra2.addr_max);
- if (maxbeg < minend) {
- DPrintf("ThreadSanitizer: suppressing report as doubled (addr)\n");
- equal_address = true;
- break;
+ {
+ ReadLock lock(&ctx->racy_mtx);
+ if (flags()->suppress_equal_stacks) {
+ hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
+ hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
+ for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
+ if (hash == ctx->racy_stacks[i]) {
+ VPrintf(2,
+ "ThreadSanitizer: suppressing report as doubled (stack)\n");
+ equal_stack = true;
+ break;
+ }
+ }
+ }
+ if (flags()->suppress_equal_addresses) {
+ for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) {
+ RacyAddress ra2 = ctx->racy_addresses[i];
+ uptr maxbeg = max(ra0.addr_min, ra2.addr_min);
+ uptr minend = min(ra0.addr_max, ra2.addr_max);
+ if (maxbeg < minend) {
+ VPrintf(2, "ThreadSanitizer: suppressing report as doubled (addr)\n");
+ equal_address = true;
+ break;
+ }
}
}
}
- if (equal_stack || equal_address) {
- if (!equal_stack)
- ctx->racy_stacks.PushBack(hash);
- if (!equal_address)
- ctx->racy_addresses.PushBack(ra0);
- return true;
+ if (!equal_stack && !equal_address)
+ return false;
+ if (!equal_stack) {
+ Lock lock(&ctx->racy_mtx);
+ ctx->racy_stacks.PushBack(hash);
}
- return false;
+ if (!equal_address) {
+ Lock lock(&ctx->racy_mtx);
+ ctx->racy_addresses.PushBack(ra0);
+ }
+ return true;
}
-static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
- uptr addr_min, uptr addr_max) {
- Context *ctx = CTX();
+static void AddRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
+ uptr addr_min, uptr addr_max) {
+ Lock lock(&ctx->racy_mtx);
if (flags()->suppress_equal_stacks) {
RacyStacks hash;
- hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
- hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
+ hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
+ hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
ctx->racy_stacks.PushBack(hash);
}
if (flags()->suppress_equal_addresses) {
}
}
-bool OutputReport(Context *ctx,
- const ScopedReport &srep,
- const ReportStack *suppress_stack1,
- const ReportStack *suppress_stack2,
- const ReportLocation *suppress_loc) {
- atomic_store(&ctx->last_symbolize_time_ns, NanoTime(), memory_order_relaxed);
+bool OutputReport(ThreadState *thr, const ScopedReport &srep) {
+ if (!flags()->report_bugs)
+ return false;
+ atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime());
const ReportDesc *rep = srep.GetReport();
Suppression *supp = 0;
- uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack1, &supp);
- if (suppress_pc == 0)
- suppress_pc = IsSuppressed(rep->typ, suppress_stack2, &supp);
- if (suppress_pc == 0)
- suppress_pc = IsSuppressed(rep->typ, suppress_loc, &supp);
- if (suppress_pc != 0) {
- FiredSuppression s = {srep.GetReport()->typ, suppress_pc, supp};
+ uptr pc_or_addr = 0;
+ for (uptr i = 0; pc_or_addr == 0 && i < rep->mops.Size(); i++)
+ pc_or_addr = IsSuppressed(rep->typ, rep->mops[i]->stack, &supp);
+ for (uptr i = 0; pc_or_addr == 0 && i < rep->stacks.Size(); i++)
+ pc_or_addr = IsSuppressed(rep->typ, rep->stacks[i], &supp);
+ for (uptr i = 0; pc_or_addr == 0 && i < rep->threads.Size(); i++)
+ pc_or_addr = IsSuppressed(rep->typ, rep->threads[i]->stack, &supp);
+ for (uptr i = 0; pc_or_addr == 0 && i < rep->locs.Size(); i++)
+ pc_or_addr = IsSuppressed(rep->typ, rep->locs[i], &supp);
+ if (pc_or_addr != 0) {
+ Lock lock(&ctx->fired_suppressions_mtx);
+ FiredSuppression s = {srep.GetReport()->typ, pc_or_addr, supp};
ctx->fired_suppressions.push_back(s);
}
- if (OnReport(rep, suppress_pc != 0))
- return false;
+ {
+ bool old_is_freeing = thr->is_freeing;
+ thr->is_freeing = false;
+ bool suppressed = OnReport(rep, pc_or_addr != 0);
+ thr->is_freeing = old_is_freeing;
+ if (suppressed)
+ return false;
+ }
PrintReport(rep);
ctx->nreported++;
if (flags()->halt_on_error)
- internal__exit(flags()->exitcode);
+ Die();
return true;
}
-bool IsFiredSuppression(Context *ctx,
- const ScopedReport &srep,
- const StackTrace &trace) {
+bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace) {
+ ReadLock lock(&ctx->fired_suppressions_mtx);
for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
- if (ctx->fired_suppressions[k].type != srep.GetReport()->typ)
+ if (ctx->fired_suppressions[k].type != type)
continue;
- for (uptr j = 0; j < trace.Size(); j++) {
+ for (uptr j = 0; j < trace.size; j++) {
FiredSuppression *s = &ctx->fired_suppressions[k];
- if (trace.Get(j) == s->pc) {
+ if (trace.trace[j] == s->pc_or_addr) {
if (s->supp)
- s->supp->hit_count++;
+ atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed);
return true;
}
}
return false;
}
-static bool IsFiredSuppression(Context *ctx,
- const ScopedReport &srep,
- uptr addr) {
+static bool IsFiredSuppression(Context *ctx, ReportType type, uptr addr) {
+ ReadLock lock(&ctx->fired_suppressions_mtx);
for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
- if (ctx->fired_suppressions[k].type != srep.GetReport()->typ)
+ if (ctx->fired_suppressions[k].type != type)
continue;
FiredSuppression *s = &ctx->fired_suppressions[k];
- if (addr == s->pc) {
+ if (addr == s->pc_or_addr) {
if (s->supp)
- s->supp->hit_count++;
+ atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed);
return true;
}
}
return false;
}
-bool FrameIsInternal(const ReportStack *frame) {
- return frame != 0 && frame->file != 0
- && (internal_strstr(frame->file, "tsan_interceptors.cc") ||
- internal_strstr(frame->file, "sanitizer_common_interceptors.inc") ||
- internal_strstr(frame->file, "tsan_interface_"));
-}
-
-// On programs that use Java we see weird reports like:
-// WARNING: ThreadSanitizer: data race (pid=22512)
-// Read of size 8 at 0x7d2b00084318 by thread 100:
-// #0 memcpy tsan_interceptors.cc:406 (foo+0x00000d8dfae3)
-// #1 <null> <null>:0 (0x7f7ad9b40193)
-// Previous write of size 8 at 0x7d2b00084318 by thread 105:
-// #0 strncpy tsan_interceptors.cc:501 (foo+0x00000d8e0919)
-// #1 <null> <null>:0 (0x7f7ad9b42707)
-static bool IsJavaNonsense(const ReportDesc *rep) {
-#ifndef TSAN_GO
- for (uptr i = 0; i < rep->mops.Size(); i++) {
- ReportMop *mop = rep->mops[i];
- ReportStack *frame = mop->stack;
- if (frame == 0
- || (frame->func == 0 && frame->file == 0 && frame->line == 0
- && frame->module == 0)) {
- return true;
- }
- if (FrameIsInternal(frame)) {
- frame = frame->next;
- if (frame == 0
- || (frame->func == 0 && frame->file == 0 && frame->line == 0
- && frame->module == 0)) {
- if (frame) {
- FiredSuppression supp = {rep->typ, frame->pc, 0};
- CTX()->fired_suppressions.push_back(supp);
- }
- return true;
- }
- }
- }
-#endif
- return false;
-}
-
static bool RaceBetweenAtomicAndFree(ThreadState *thr) {
Shadow s0(thr->racy_state[0]);
Shadow s1(thr->racy_state[1]);
}
void ReportRace(ThreadState *thr) {
+ CheckNoLocks(thr);
+
+ // Symbolizer makes lots of intercepted calls. If we try to process them,
+ // at best it will cause deadlocks on internal mutexes.
+ ScopedIgnoreInterceptors ignore;
+
if (!flags()->report_bugs)
return;
- ScopedInRtl in_rtl;
-
if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr))
return;
return;
}
- Context *ctx = CTX();
- ThreadRegistryLock l0(ctx->thread_registry);
-
ReportType typ = ReportTypeRace;
- if (thr->is_vptr_access)
+ if (thr->is_vptr_access && freed)
+ typ = ReportTypeVptrUseAfterFree;
+ else if (thr->is_vptr_access)
typ = ReportTypeVptrRace;
else if (freed)
typ = ReportTypeUseAfterFree;
- ScopedReport rep(typ);
- if (IsFiredSuppression(ctx, rep, addr))
+
+ if (IsFiredSuppression(ctx, typ, addr))
return;
+
const uptr kMop = 2;
- StackTrace traces[kMop];
+ VarSizeStackTrace traces[kMop];
const uptr toppc = TraceTopPC(thr);
- traces[0].ObtainCurrent(thr, toppc);
- if (IsFiredSuppression(ctx, rep, traces[0]))
+ ObtainCurrentStack(thr, toppc, &traces[0]);
+ if (IsFiredSuppression(ctx, typ, traces[0]))
return;
- InternalScopedBuffer<MutexSet> mset2(1);
- new(mset2.data()) MutexSet();
+
+ // MutexSet is too large to live on stack.
+ Vector<u64> mset_buffer(MBlockScopedBuf);
+ mset_buffer.Resize(sizeof(MutexSet) / sizeof(u64) + 1);
+ MutexSet *mset2 = new(&mset_buffer[0]) MutexSet();
+
Shadow s2(thr->racy_state[1]);
- RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data());
- if (IsFiredSuppression(ctx, rep, traces[1]))
+ RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2);
+ if (IsFiredSuppression(ctx, typ, traces[1]))
return;
if (HandleRacyStacks(thr, traces, addr_min, addr_max))
return;
+ ThreadRegistryLock l0(ctx->thread_registry);
+ ScopedReport rep(typ);
for (uptr i = 0; i < kMop; i++) {
Shadow s(thr->racy_state[i]);
- rep.AddMemoryAccess(addr, s, &traces[i],
- i == 0 ? &thr->mset : mset2.data());
+ rep.AddMemoryAccess(addr, s, traces[i], i == 0 ? &thr->mset : mset2);
}
- if (flags()->suppress_java && IsJavaNonsense(rep.GetReport()))
- return;
-
for (uptr i = 0; i < kMop; i++) {
FastState s(thr->racy_state[i]);
ThreadContext *tctx = static_cast<ThreadContext*>(
rep.AddLocation(addr_min, addr_max - addr_min);
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
{ // NOLINT
Shadow s(thr->racy_state[1]);
if (s.epoch() <= thr->last_sleep_clock.get(s.tid()))
}
#endif
- ReportLocation *suppress_loc = rep.GetReport()->locs.Size() ?
- rep.GetReport()->locs[0] : 0;
- if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack,
- rep.GetReport()->mops[1]->stack,
- suppress_loc))
+ if (!OutputReport(thr, rep))
return;
AddRacyStacks(thr, traces, addr_min, addr_max);
}
void PrintCurrentStack(ThreadState *thr, uptr pc) {
- StackTrace trace;
- trace.ObtainCurrent(thr, pc);
+ VarSizeStackTrace trace;
+ ObtainCurrentStack(thr, pc, &trace);
PrintStack(SymbolizeStack(trace));
}
-void PrintCurrentStackSlow() {
-#ifndef TSAN_GO
- __sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace,
- sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace;
- ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(),
- 0, 0, 0, false);
+void PrintCurrentStackSlow(uptr pc) {
+#ifndef SANITIZER_GO
+ BufferedStackTrace *ptrace =
+ new(internal_alloc(MBlockStackTrace, sizeof(BufferedStackTrace)))
+ BufferedStackTrace();
+ ptrace->Unwind(kStackTraceMax, pc, 0, 0, 0, 0, false);
for (uptr i = 0; i < ptrace->size / 2; i++) {
- uptr tmp = ptrace->trace[i];
- ptrace->trace[i] = ptrace->trace[ptrace->size - i - 1];
- ptrace->trace[ptrace->size - i - 1] = tmp;
+ uptr tmp = ptrace->trace_buffer[i];
+ ptrace->trace_buffer[i] = ptrace->trace_buffer[ptrace->size - i - 1];
+ ptrace->trace_buffer[ptrace->size - i - 1] = tmp;
}
- StackTrace trace;
- trace.Init(ptrace->trace, ptrace->size);
- PrintStack(SymbolizeStack(trace));
+ PrintStack(SymbolizeStack(*ptrace));
#endif
}
} // namespace __tsan
+
+using namespace __tsan;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+ PrintCurrentStackSlow(StackTrace::GetCurrentPc());
+}
+} // extern "C"
, epoch1() {
}
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
ThreadContext::~ThreadContext() {
}
#endif
void ThreadContext::OnDead() {
- sync.Reset();
+ CHECK_EQ(sync.size(), 0);
}
void ThreadContext::OnJoined(void *arg) {
ThreadState *caller_thr = static_cast<ThreadState *>(arg);
AcquireImpl(caller_thr, 0, &sync);
- sync.Reset();
+ sync.Reset(&caller_thr->clock_cache);
}
struct OnCreatedArgs {
if (tid == 0)
return;
OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg);
+ if (!args->thr) // GCD workers don't have a parent thread.
+ return;
args->thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0);
ReleaseImpl(args->thr, 0, &sync);
-#ifdef TSAN_GO
- creation_stack.ObtainCurrent(args->thr, args->pc);
-#else
creation_stack_id = CurrentStackId(args->thr, args->pc);
-#endif
if (reuse_count == 0)
StatInc(args->thr, StatThreadMaxTid);
}
void ThreadContext::OnReset() {
- sync.Reset();
+ CHECK_EQ(sync.size(), 0);
FlushUnneededShadowMemory(GetThreadTrace(tid), TraceSize() * sizeof(Event));
//!!! FlushUnneededShadowMemory(GetThreadTraceHeader(tid), sizeof(Trace));
}
+void ThreadContext::OnDetached(void *arg) {
+ ThreadState *thr1 = static_cast<ThreadState*>(arg);
+ sync.Reset(&thr1->clock_cache);
+}
+
struct OnStartedArgs {
ThreadState *thr;
uptr stk_addr;
// from different threads.
epoch0 = RoundUp(epoch1 + 1, kTracePartSize);
epoch1 = (u64)-1;
- new(thr) ThreadState(CTX(), tid, unique_id,
- epoch0, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size);
-#ifndef TSAN_GO
+ new(thr) ThreadState(ctx, tid, unique_id, epoch0, reuse_count,
+ args->stk_addr, args->stk_size, args->tls_addr, args->tls_size);
+#ifndef SANITIZER_GO
thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0];
thr->shadow_stack_pos = thr->shadow_stack;
thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize;
thr->shadow_stack_pos = thr->shadow_stack;
thr->shadow_stack_end = thr->shadow_stack + kInitStackSize;
#endif
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
AllocatorThreadStart(thr);
#endif
+ if (common_flags()->detect_deadlocks) {
+ thr->dd_pt = ctx->dd->CreatePhysicalThread();
+ thr->dd_lt = ctx->dd->CreateLogicalThread(unique_id);
+ }
thr->fast_synch_epoch = epoch0;
AcquireImpl(thr, 0, &sync);
thr->fast_state.SetHistorySize(flags()->history_size);
Trace *thr_trace = ThreadTrace(thr->tid);
thr_trace->headers[trace].epoch0 = epoch0;
StatInc(thr, StatSyncAcquire);
- sync.Reset();
+ sync.Reset(&thr->clock_cache);
+ thr->is_inited = true;
DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx "
"tls_addr=%zx tls_size=%zx\n",
tid, (uptr)epoch0, args->stk_addr, args->stk_size,
args->tls_addr, args->tls_size);
- thr->is_alive = true;
}
void ThreadContext::OnFinished() {
}
epoch1 = thr->fast_state.epoch();
-#ifndef TSAN_GO
+ if (common_flags()->detect_deadlocks) {
+ ctx->dd->DestroyPhysicalThread(thr->dd_pt);
+ ctx->dd->DestroyLogicalThread(thr->dd_lt);
+ }
+ ctx->clock_alloc.FlushCache(&thr->clock_cache);
+ ctx->metamap.OnThreadIdle(thr);
+#ifndef SANITIZER_GO
AllocatorThreadFinish(thr);
#endif
thr->~ThreadState();
- StatAggregate(CTX()->stat, thr->stat);
+#if TSAN_COLLECT_STATS
+ StatAggregate(ctx->stat, thr->stat);
+#endif
thr = 0;
}
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
struct ThreadLeak {
ThreadContext *tctx;
int count;
}
#endif
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) {
if (tctx->tid == 0) {
Printf("ThreadSanitizer: main thread finished with ignores enabled\n");
}
static void ThreadCheckIgnore(ThreadState *thr) {
+ if (ctx->after_multithreaded_fork)
+ return;
if (thr->ignore_reads_and_writes)
ReportIgnoresEnabled(thr->tctx, &thr->mop_ignore_set);
if (thr->ignore_sync)
#endif
void ThreadFinalize(ThreadState *thr) {
- CHECK_GT(thr->in_rtl, 0);
ThreadCheckIgnore(thr);
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
if (!flags()->report_thread_leaks)
return;
- ThreadRegistryLock l(CTX()->thread_registry);
+ ThreadRegistryLock l(ctx->thread_registry);
Vector<ThreadLeak> leaks(MBlockScopedBuf);
- CTX()->thread_registry->RunCallbackForEachThreadLocked(
+ ctx->thread_registry->RunCallbackForEachThreadLocked(
MaybeReportThreadLeak, &leaks);
for (uptr i = 0; i < leaks.Size(); i++) {
ScopedReport rep(ReportTypeThreadLeak);
- rep.AddThread(leaks[i].tctx);
+ rep.AddThread(leaks[i].tctx, true);
rep.SetCount(leaks[i].count);
- OutputReport(CTX(), rep);
+ OutputReport(thr, rep);
}
#endif
}
int ThreadCount(ThreadState *thr) {
- CHECK_GT(thr->in_rtl, 0);
- Context *ctx = CTX();
uptr result;
ctx->thread_registry->GetNumberOfThreads(0, 0, &result);
return (int)result;
}
int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
- CHECK_GT(thr->in_rtl, 0);
StatInc(thr, StatThreadCreate);
- Context *ctx = CTX();
OnCreatedArgs args = { thr, pc };
- int tid = ctx->thread_registry->CreateThread(uid, detached, thr->tid, &args);
- DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", thr->tid, tid, uid);
+ u32 parent_tid = thr ? thr->tid : kInvalidTid; // No parent for GCD workers.
+ int tid =
+ ctx->thread_registry->CreateThread(uid, detached, parent_tid, &args);
+ DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent_tid, tid, uid);
StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads());
return tid;
}
void ThreadStart(ThreadState *thr, int tid, uptr os_id) {
- Context *ctx = CTX();
- CHECK_GT(thr->in_rtl, 0);
uptr stk_addr = 0;
uptr stk_size = 0;
uptr tls_addr = 0;
uptr tls_size = 0;
+#ifndef SANITIZER_GO
GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size);
if (tid) {
thr_end, tls_addr + tls_size - thr_end);
}
}
+#endif
ThreadRegistry *tr = ctx->thread_registry;
OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size };
tr->Lock();
thr->tctx = (ThreadContext*)tr->GetThreadLocked(tid);
tr->Unlock();
+
+#ifndef SANITIZER_GO
+ if (ctx->after_multithreaded_fork) {
+ thr->ignore_interceptors++;
+ ThreadIgnoreBegin(thr, 0);
+ ThreadIgnoreSyncBegin(thr, 0);
+ }
+#endif
}
void ThreadFinish(ThreadState *thr) {
- CHECK_GT(thr->in_rtl, 0);
ThreadCheckIgnore(thr);
StatInc(thr, StatThreadFinish);
if (thr->stk_addr && thr->stk_size)
DontNeedShadowFor(thr->stk_addr, thr->stk_size);
if (thr->tls_addr && thr->tls_size)
DontNeedShadowFor(thr->tls_addr, thr->tls_size);
- thr->is_alive = false;
- Context *ctx = CTX();
+ thr->is_dead = true;
ctx->thread_registry->FinishThread(thr->tid);
}
}
int ThreadTid(ThreadState *thr, uptr pc, uptr uid) {
- CHECK_GT(thr->in_rtl, 0);
- Context *ctx = CTX();
int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid);
DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res);
return res;
}
void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
- CHECK_GT(thr->in_rtl, 0);
CHECK_GT(tid, 0);
CHECK_LT(tid, kMaxTid);
DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid);
- Context *ctx = CTX();
ctx->thread_registry->JoinThread(tid, thr);
}
void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
- CHECK_GT(thr->in_rtl, 0);
CHECK_GT(tid, 0);
CHECK_LT(tid, kMaxTid);
- Context *ctx = CTX();
- ctx->thread_registry->DetachThread(tid);
+ ctx->thread_registry->DetachThread(tid, thr);
}
void ThreadSetName(ThreadState *thr, const char *name) {
- CHECK_GT(thr->in_rtl, 0);
- CTX()->thread_registry->SetThreadName(thr->tid, name);
+ ctx->thread_registry->SetThreadName(thr->tid, name);
}
void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
thr->tid, (void*)pc, (void*)addr,
(int)size, is_write);
-#if TSAN_DEBUG
+#if SANITIZER_DEBUG
if (!IsAppMem(addr)) {
Printf("Access to non app mem %zx\n", addr);
DCHECK(IsAppMem(addr));
--- /dev/null
+//===-- tsan_stack_trace.cc -----------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_stack_trace.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+
+namespace __tsan {
+
+VarSizeStackTrace::VarSizeStackTrace()
+ : StackTrace(nullptr, 0), trace_buffer(nullptr) {}
+
+VarSizeStackTrace::~VarSizeStackTrace() {
+ ResizeBuffer(0);
+}
+
+void VarSizeStackTrace::ResizeBuffer(uptr new_size) {
+ if (trace_buffer) {
+ internal_free(trace_buffer);
+ }
+ trace_buffer =
+ (new_size > 0)
+ ? (uptr *)internal_alloc(MBlockStackTrace,
+ new_size * sizeof(trace_buffer[0]))
+ : nullptr;
+ trace = trace_buffer;
+ size = new_size;
+}
+
+void VarSizeStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
+ ResizeBuffer(cnt + !!extra_top_pc);
+ internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
+ if (extra_top_pc)
+ trace_buffer[cnt] = extra_top_pc;
+}
+
+} // namespace __tsan
--- /dev/null
+//===-- tsan_stack_trace.h --------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_STACK_TRACE_H
+#define TSAN_STACK_TRACE_H
+
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "tsan_defs.h"
+
+namespace __tsan {
+
+// StackTrace which calls malloc/free to allocate the buffer for
+// addresses in stack traces.
+struct VarSizeStackTrace : public StackTrace {
+ uptr *trace_buffer; // Owned.
+
+ VarSizeStackTrace();
+ ~VarSizeStackTrace();
+ void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
+
+ private:
+ void ResizeBuffer(uptr new_size);
+
+ VarSizeStackTrace(const VarSizeStackTrace &);
+ void operator=(const VarSizeStackTrace &);
+};
+
+} // namespace __tsan
+
+#endif // TSAN_STACK_TRACE_H
namespace __tsan {
+#if TSAN_COLLECT_STATS
+
void StatAggregate(u64 *dst, u64 *src) {
- if (!kCollectStats)
- return;
for (int i = 0; i < StatCnt; i++)
dst[i] += src[i];
}
void StatOutput(u64 *stat) {
- if (!kCollectStats)
- return;
-
stat[StatShadowNonZero] = stat[StatShadowProcessed] - stat[StatShadowZero];
static const char *name[StatCnt] = {};
name[StatMop4] = " size 4 ";
name[StatMop8] = " size 8 ";
name[StatMopSame] = " Including same ";
+ name[StatMopIgnored] = " Including ignored ";
name[StatMopRange] = " Including range ";
name[StatMopRodata] = " Including .rodata ";
name[StatMopRangeRodata] = " Including .rodata range ";
name[StatSyncAcquire] = " acquired ";
name[StatSyncRelease] = " released ";
+ name[StatClockAcquire] = "Clock acquire ";
+ name[StatClockAcquireEmpty] = " empty clock ";
+ name[StatClockAcquireFastRelease] = " fast from release-store ";
+ name[StatClockAcquireLarge] = " contains my tid ";
+ name[StatClockAcquireRepeat] = " repeated (fast) ";
+ name[StatClockAcquireFull] = " full (slow) ";
+ name[StatClockAcquiredSomething] = " acquired something ";
+ name[StatClockRelease] = "Clock release ";
+ name[StatClockReleaseResize] = " resize ";
+ name[StatClockReleaseFast1] = " fast1 ";
+ name[StatClockReleaseFast2] = " fast2 ";
+ name[StatClockReleaseSlow] = " dirty overflow (slow) ";
+ name[StatClockReleaseFull] = " full (slow) ";
+ name[StatClockReleaseAcquired] = " was acquired ";
+ name[StatClockReleaseClearTail] = " clear tail ";
+ name[StatClockStore] = "Clock release store ";
+ name[StatClockStoreResize] = " resize ";
+ name[StatClockStoreFast] = " fast ";
+ name[StatClockStoreFull] = " slow ";
+ name[StatClockStoreTail] = " clear tail ";
+ name[StatClockAcquireRelease] = "Clock acquire-release ";
+
name[StatAtomic] = "Atomic operations ";
name[StatAtomicLoad] = " Including load ";
name[StatAtomicStore] = " store ";
name[StatAtomic8] = " size 8 ";
name[StatAtomic16] = " size 16 ";
- name[StatInterceptor] = "Interceptors ";
- name[StatInt_longjmp] = " longjmp ";
- name[StatInt_siglongjmp] = " siglongjmp ";
- name[StatInt_malloc] = " malloc ";
- name[StatInt___libc_memalign] = " __libc_memalign ";
- name[StatInt_calloc] = " calloc ";
- name[StatInt_realloc] = " realloc ";
- name[StatInt_free] = " free ";
- name[StatInt_cfree] = " cfree ";
- name[StatInt_malloc_usable_size] = " malloc_usable_size ";
- name[StatInt_mmap] = " mmap ";
- name[StatInt_mmap64] = " mmap64 ";
- name[StatInt_munmap] = " munmap ";
- name[StatInt_memalign] = " memalign ";
- name[StatInt_valloc] = " valloc ";
- name[StatInt_pvalloc] = " pvalloc ";
- name[StatInt_posix_memalign] = " posix_memalign ";
- name[StatInt__Znwm] = " _Znwm ";
- name[StatInt__ZnwmRKSt9nothrow_t] = " _ZnwmRKSt9nothrow_t ";
- name[StatInt__Znam] = " _Znam ";
- name[StatInt__ZnamRKSt9nothrow_t] = " _ZnamRKSt9nothrow_t ";
- name[StatInt__ZdlPv] = " _ZdlPv ";
- name[StatInt__ZdlPvRKSt9nothrow_t] = " _ZdlPvRKSt9nothrow_t ";
- name[StatInt__ZdaPv] = " _ZdaPv ";
- name[StatInt__ZdaPvRKSt9nothrow_t] = " _ZdaPvRKSt9nothrow_t ";
- name[StatInt_strlen] = " strlen ";
- name[StatInt_memset] = " memset ";
- name[StatInt_memcpy] = " memcpy ";
- name[StatInt_textdomain] = " textdomain ";
- name[StatInt_strcmp] = " strcmp ";
- name[StatInt_memchr] = " memchr ";
- name[StatInt_memrchr] = " memrchr ";
- name[StatInt_memmove] = " memmove ";
- name[StatInt_memcmp] = " memcmp ";
- name[StatInt_strchr] = " strchr ";
- name[StatInt_strchrnul] = " strchrnul ";
- name[StatInt_strrchr] = " strrchr ";
- name[StatInt_strncmp] = " strncmp ";
- name[StatInt_strcpy] = " strcpy ";
- name[StatInt_strncpy] = " strncpy ";
- name[StatInt_strstr] = " strstr ";
- name[StatInt_strdup] = " strdup ";
- name[StatInt_strcasecmp] = " strcasecmp ";
- name[StatInt_strncasecmp] = " strncasecmp ";
- name[StatInt_atexit] = " atexit ";
- name[StatInt__exit] = " _exit ";
- name[StatInt___cxa_guard_acquire] = " __cxa_guard_acquire ";
- name[StatInt___cxa_guard_release] = " __cxa_guard_release ";
- name[StatInt___cxa_guard_abort] = " __cxa_guard_abort ";
- name[StatInt_pthread_create] = " pthread_create ";
- name[StatInt_pthread_join] = " pthread_join ";
- name[StatInt_pthread_detach] = " pthread_detach ";
- name[StatInt_pthread_mutex_init] = " pthread_mutex_init ";
- name[StatInt_pthread_mutex_destroy] = " pthread_mutex_destroy ";
- name[StatInt_pthread_mutex_lock] = " pthread_mutex_lock ";
- name[StatInt_pthread_mutex_trylock] = " pthread_mutex_trylock ";
- name[StatInt_pthread_mutex_timedlock] = " pthread_mutex_timedlock ";
- name[StatInt_pthread_mutex_unlock] = " pthread_mutex_unlock ";
- name[StatInt_pthread_spin_init] = " pthread_spin_init ";
- name[StatInt_pthread_spin_destroy] = " pthread_spin_destroy ";
- name[StatInt_pthread_spin_lock] = " pthread_spin_lock ";
- name[StatInt_pthread_spin_trylock] = " pthread_spin_trylock ";
- name[StatInt_pthread_spin_unlock] = " pthread_spin_unlock ";
- name[StatInt_pthread_rwlock_init] = " pthread_rwlock_init ";
- name[StatInt_pthread_rwlock_destroy] = " pthread_rwlock_destroy ";
- name[StatInt_pthread_rwlock_rdlock] = " pthread_rwlock_rdlock ";
- name[StatInt_pthread_rwlock_tryrdlock] = " pthread_rwlock_tryrdlock ";
- name[StatInt_pthread_rwlock_timedrdlock]
- = " pthread_rwlock_timedrdlock ";
- name[StatInt_pthread_rwlock_wrlock] = " pthread_rwlock_wrlock ";
- name[StatInt_pthread_rwlock_trywrlock] = " pthread_rwlock_trywrlock ";
- name[StatInt_pthread_rwlock_timedwrlock]
- = " pthread_rwlock_timedwrlock ";
- name[StatInt_pthread_rwlock_unlock] = " pthread_rwlock_unlock ";
- name[StatInt_pthread_cond_init] = " pthread_cond_init ";
- name[StatInt_pthread_cond_destroy] = " pthread_cond_destroy ";
- name[StatInt_pthread_cond_signal] = " pthread_cond_signal ";
- name[StatInt_pthread_cond_broadcast] = " pthread_cond_broadcast ";
- name[StatInt_pthread_cond_wait] = " pthread_cond_wait ";
- name[StatInt_pthread_cond_timedwait] = " pthread_cond_timedwait ";
- name[StatInt_pthread_barrier_init] = " pthread_barrier_init ";
- name[StatInt_pthread_barrier_destroy] = " pthread_barrier_destroy ";
- name[StatInt_pthread_barrier_wait] = " pthread_barrier_wait ";
- name[StatInt_pthread_once] = " pthread_once ";
- name[StatInt_pthread_getschedparam] = " pthread_getschedparam ";
- name[StatInt_pthread_setname_np] = " pthread_setname_np ";
- name[StatInt_sem_init] = " sem_init ";
- name[StatInt_sem_destroy] = " sem_destroy ";
- name[StatInt_sem_wait] = " sem_wait ";
- name[StatInt_sem_trywait] = " sem_trywait ";
- name[StatInt_sem_timedwait] = " sem_timedwait ";
- name[StatInt_sem_post] = " sem_post ";
- name[StatInt_sem_getvalue] = " sem_getvalue ";
- name[StatInt_stat] = " stat ";
- name[StatInt___xstat] = " __xstat ";
- name[StatInt_stat64] = " stat64 ";
- name[StatInt___xstat64] = " __xstat64 ";
- name[StatInt_lstat] = " lstat ";
- name[StatInt___lxstat] = " __lxstat ";
- name[StatInt_lstat64] = " lstat64 ";
- name[StatInt___lxstat64] = " __lxstat64 ";
- name[StatInt_fstat] = " fstat ";
- name[StatInt___fxstat] = " __fxstat ";
- name[StatInt_fstat64] = " fstat64 ";
- name[StatInt___fxstat64] = " __fxstat64 ";
- name[StatInt_open] = " open ";
- name[StatInt_open64] = " open64 ";
- name[StatInt_creat] = " creat ";
- name[StatInt_creat64] = " creat64 ";
- name[StatInt_dup] = " dup ";
- name[StatInt_dup2] = " dup2 ";
- name[StatInt_dup3] = " dup3 ";
- name[StatInt_eventfd] = " eventfd ";
- name[StatInt_signalfd] = " signalfd ";
- name[StatInt_inotify_init] = " inotify_init ";
- name[StatInt_inotify_init1] = " inotify_init1 ";
- name[StatInt_socket] = " socket ";
- name[StatInt_socketpair] = " socketpair ";
- name[StatInt_connect] = " connect ";
- name[StatInt_bind] = " bind ";
- name[StatInt_listen] = " listen ";
- name[StatInt_accept] = " accept ";
- name[StatInt_accept4] = " accept4 ";
- name[StatInt_epoll_create] = " epoll_create ";
- name[StatInt_epoll_create1] = " epoll_create1 ";
- name[StatInt_close] = " close ";
- name[StatInt___close] = " __close ";
- name[StatInt___res_iclose] = " __res_iclose ";
- name[StatInt_pipe] = " pipe ";
- name[StatInt_pipe2] = " pipe2 ";
- name[StatInt_read] = " read ";
- name[StatInt_prctl] = " prctl ";
- name[StatInt_pread] = " pread ";
- name[StatInt_pread64] = " pread64 ";
- name[StatInt_readv] = " readv ";
- name[StatInt_preadv] = " preadv ";
- name[StatInt_preadv64] = " preadv64 ";
- name[StatInt_write] = " write ";
- name[StatInt_pwrite] = " pwrite ";
- name[StatInt_pwrite64] = " pwrite64 ";
- name[StatInt_writev] = " writev ";
- name[StatInt_pwritev] = " pwritev ";
- name[StatInt_pwritev64] = " pwritev64 ";
- name[StatInt_send] = " send ";
- name[StatInt_sendmsg] = " sendmsg ";
- name[StatInt_recv] = " recv ";
- name[StatInt_recvmsg] = " recvmsg ";
- name[StatInt_unlink] = " unlink ";
- name[StatInt_fopen] = " fopen ";
- name[StatInt_freopen] = " freopen ";
- name[StatInt_fclose] = " fclose ";
- name[StatInt_fread] = " fread ";
- name[StatInt_fwrite] = " fwrite ";
- name[StatInt_fflush] = " fflush ";
- name[StatInt_abort] = " abort ";
- name[StatInt_puts] = " puts ";
- name[StatInt_rmdir] = " rmdir ";
- name[StatInt_opendir] = " opendir ";
- name[StatInt_epoll_ctl] = " epoll_ctl ";
- name[StatInt_epoll_wait] = " epoll_wait ";
- name[StatInt_poll] = " poll ";
- name[StatInt_ppoll] = " ppoll ";
- name[StatInt_sigaction] = " sigaction ";
- name[StatInt_signal] = " signal ";
- name[StatInt_sigsuspend] = " sigsuspend ";
- name[StatInt_raise] = " raise ";
- name[StatInt_kill] = " kill ";
- name[StatInt_pthread_kill] = " pthread_kill ";
- name[StatInt_sleep] = " sleep ";
- name[StatInt_usleep] = " usleep ";
- name[StatInt_nanosleep] = " nanosleep ";
- name[StatInt_gettimeofday] = " gettimeofday ";
- name[StatInt_fork] = " fork ";
- name[StatInt_vscanf] = " vscanf ";
- name[StatInt_vsscanf] = " vsscanf ";
- name[StatInt_vfscanf] = " vfscanf ";
- name[StatInt_scanf] = " scanf ";
- name[StatInt_sscanf] = " sscanf ";
- name[StatInt_fscanf] = " fscanf ";
- name[StatInt___isoc99_vscanf] = " vscanf ";
- name[StatInt___isoc99_vsscanf] = " vsscanf ";
- name[StatInt___isoc99_vfscanf] = " vfscanf ";
- name[StatInt___isoc99_scanf] = " scanf ";
- name[StatInt___isoc99_sscanf] = " sscanf ";
- name[StatInt___isoc99_fscanf] = " fscanf ";
- name[StatInt_on_exit] = " on_exit ";
- name[StatInt___cxa_atexit] = " __cxa_atexit ";
- name[StatInt_localtime] = " localtime ";
- name[StatInt_localtime_r] = " localtime_r ";
- name[StatInt_gmtime] = " gmtime ";
- name[StatInt_gmtime_r] = " gmtime_r ";
- name[StatInt_ctime] = " ctime ";
- name[StatInt_ctime_r] = " ctime_r ";
- name[StatInt_asctime] = " asctime ";
- name[StatInt_asctime_r] = " asctime_r ";
- name[StatInt_strptime] = " strptime ";
- name[StatInt_frexp] = " frexp ";
- name[StatInt_frexpf] = " frexpf ";
- name[StatInt_frexpl] = " frexpl ";
- name[StatInt_getpwnam] = " getpwnam ";
- name[StatInt_getpwuid] = " getpwuid ";
- name[StatInt_getgrnam] = " getgrnam ";
- name[StatInt_getgrgid] = " getgrgid ";
- name[StatInt_getpwnam_r] = " getpwnam_r ";
- name[StatInt_getpwuid_r] = " getpwuid_r ";
- name[StatInt_getgrnam_r] = " getgrnam_r ";
- name[StatInt_getgrgid_r] = " getgrgid_r ";
- name[StatInt_clock_getres] = " clock_getres ";
- name[StatInt_clock_gettime] = " clock_gettime ";
- name[StatInt_clock_settime] = " clock_settime ";
- name[StatInt_getitimer] = " getitimer ";
- name[StatInt_setitimer] = " setitimer ";
- name[StatInt_time] = " time ";
- name[StatInt_glob] = " glob ";
- name[StatInt_glob64] = " glob64 ";
- name[StatInt_wait] = " wait ";
- name[StatInt_waitid] = " waitid ";
- name[StatInt_waitpid] = " waitpid ";
- name[StatInt_wait3] = " wait3 ";
- name[StatInt_wait4] = " wait4 ";
- name[StatInt_inet_ntop] = " inet_ntop ";
- name[StatInt_inet_pton] = " inet_pton ";
- name[StatInt_inet_aton] = " inet_aton ";
- name[StatInt_getaddrinfo] = " getaddrinfo ";
- name[StatInt_getnameinfo] = " getnameinfo ";
- name[StatInt_getsockname] = " getsockname ";
- name[StatInt_gethostent] = " gethostent ";
- name[StatInt_gethostbyname] = " gethostbyname ";
- name[StatInt_gethostbyname2] = " gethostbyname2 ";
- name[StatInt_gethostbyaddr] = " gethostbyaddr ";
- name[StatInt_gethostent_r] = " gethostent_r ";
- name[StatInt_gethostbyname_r] = " gethostbyname_r ";
- name[StatInt_gethostbyname2_r] = " gethostbyname2_r ";
- name[StatInt_gethostbyaddr_r] = " gethostbyaddr_r ";
- name[StatInt_getsockopt] = " getsockopt ";
- name[StatInt_modf] = " modf ";
- name[StatInt_modff] = " modff ";
- name[StatInt_modfl] = " modfl ";
- name[StatInt_getpeername] = " getpeername ";
- name[StatInt_ioctl] = " ioctl ";
- name[StatInt_sysinfo] = " sysinfo ";
- name[StatInt_readdir] = " readdir ";
- name[StatInt_readdir64] = " readdir64 ";
- name[StatInt_readdir_r] = " readdir_r ";
- name[StatInt_readdir64_r] = " readdir64_r ";
- name[StatInt_ptrace] = " ptrace ";
- name[StatInt_setlocale] = " setlocale ";
- name[StatInt_getcwd] = " getcwd ";
- name[StatInt_get_current_dir_name] = " get_current_dir_name ";
- name[StatInt_strtoimax] = " strtoimax ";
- name[StatInt_strtoumax] = " strtoumax ";
- name[StatInt_mbstowcs] = " mbstowcs ";
- name[StatInt_mbsrtowcs] = " mbsrtowcs ";
- name[StatInt_mbsnrtowcs] = " mbsnrtowcs ";
- name[StatInt_wcstombs] = " wcstombs ";
- name[StatInt_wcsrtombs] = " wcsrtombs ";
- name[StatInt_wcsnrtombs] = " wcsnrtombs ";
- name[StatInt_tcgetattr] = " tcgetattr ";
- name[StatInt_realpath] = " realpath ";
- name[StatInt_canonicalize_file_name] = " canonicalize_file_name ";
- name[StatInt_confstr] = " confstr ";
- name[StatInt_sched_getaffinity] = " sched_getaffinity ";
- name[StatInt_strerror] = " strerror ";
- name[StatInt_strerror_r] = " strerror_r ";
- name[StatInt___xpg_strerror_r] = " __xpg_strerror_r ";
- name[StatInt_scandir] = " scandir ";
- name[StatInt_scandir64] = " scandir64 ";
- name[StatInt_getgroups] = " getgroups ";
- name[StatInt_wordexp] = " wordexp ";
- name[StatInt_sigwait] = " sigwait ";
- name[StatInt_sigwaitinfo] = " sigwaitinfo ";
- name[StatInt_sigtimedwait] = " sigtimedwait ";
- name[StatInt_sigemptyset] = " sigemptyset ";
- name[StatInt_sigfillset] = " sigfillset ";
- name[StatInt_sigpending] = " sigpending ";
- name[StatInt_sigprocmask] = " sigprocmask ";
- name[StatInt_backtrace] = " backtrace ";
- name[StatInt_backtrace_symbols] = " backtrace_symbols ";
- name[StatInt_dlopen] = " dlopen ";
- name[StatInt_dlclose] = " dlclose ";
- name[StatInt_getmntent] = " getmntent ";
- name[StatInt_getmntent_r] = " getmntent_r ";
- name[StatInt_statfs] = " statfs ";
- name[StatInt_statfs64] = " statfs64 ";
- name[StatInt_fstatfs] = " fstatfs ";
- name[StatInt_fstatfs64] = " fstatfs64 ";
- name[StatInt_statvfs] = " statvfs ";
- name[StatInt_statvfs64] = " statvfs64 ";
- name[StatInt_fstatvfs] = " fstatvfs ";
- name[StatInt_fstatvfs64] = " fstatvfs64 ";
- name[StatInt_initgroups] = " initgroups ";
- name[StatInt_ether_ntoa] = " ether_ntoa ";
- name[StatInt_ether_aton] = " ether_aton ";
- name[StatInt_ether_ntoa_r] = " ether_ntoa_r ";
- name[StatInt_ether_aton_r] = " ether_aton_r ";
- name[StatInt_ether_ntohost] = " ether_ntohost ";
- name[StatInt_ether_hostton] = " ether_hostton ";
- name[StatInt_ether_line] = " ether_line ";
- name[StatInt_shmctl] = " shmctl ";
- name[StatInt_random_r] = " random_r ";
- name[StatInt_tmpnam] = " tmpnam ";
- name[StatInt_tmpnam_r] = " tmpnam_r ";
- name[StatInt_tempnam] = " tempnam ";
- name[StatInt_sincos] = " sincos ";
- name[StatInt_sincosf] = " sincosf ";
- name[StatInt_sincosl] = " sincosl ";
- name[StatInt_remquo] = " remquo ";
- name[StatInt_remquof] = " remquof ";
- name[StatInt_remquol] = " remquol ";
- name[StatInt_lgamma] = " lgamma ";
- name[StatInt_lgammaf] = " lgammaf ";
- name[StatInt_lgammal] = " lgammal ";
- name[StatInt_lgamma_r] = " lgamma_r ";
- name[StatInt_lgammaf_r] = " lgammaf_r ";
- name[StatInt_lgammal_r] = " lgammal_r ";
- name[StatInt_drand48_r] = " drand48_r ";
- name[StatInt_lrand48_r] = " lrand48_r ";
- name[StatInt_getline] = " getline ";
- name[StatInt_getdelim] = " getdelim ";
- name[StatInt_iconv] = " iconv ";
- name[StatInt_times] = " times ";
-
- name[StatInt_pthread_attr_getdetachstate] = " pthread_addr_getdetachstate "; // NOLINT
- name[StatInt_pthread_attr_getguardsize] = " pthread_addr_getguardsize "; // NOLINT
- name[StatInt_pthread_attr_getschedparam] = " pthread_addr_getschedparam "; // NOLINT
- name[StatInt_pthread_attr_getschedpolicy] = " pthread_addr_getschedpolicy "; // NOLINT
- name[StatInt_pthread_attr_getinheritsched] = " pthread_addr_getinheritsched "; // NOLINT
- name[StatInt_pthread_attr_getscope] = " pthread_addr_getscope "; // NOLINT
- name[StatInt_pthread_attr_getstacksize] = " pthread_addr_getstacksize "; // NOLINT
- name[StatInt_pthread_attr_getstack] = " pthread_addr_getstack "; // NOLINT
- name[StatInt_pthread_attr_getaffinity_np] = " pthread_addr_getaffinity_np "; // NOLINT
-
name[StatAnnotation] = "Dynamic annotations ";
name[StatAnnotateHappensBefore] = " HappensBefore ";
name[StatAnnotateHappensAfter] = " HappensAfter ";
name[StatMtxAtExit] = " Atexit ";
name[StatMtxAnnotations] = " Annotations ";
name[StatMtxMBlock] = " MBlock ";
- name[StatMtxJavaMBlock] = " JavaMBlock ";
+ name[StatMtxDeadlockDetector] = " DeadlockDetector ";
+ name[StatMtxFired] = " FiredSuppressions ";
+ name[StatMtxRacy] = " RacyStacks ";
name[StatMtxFD] = " FD ";
Printf("Statistics:\n");
for (int i = 0; i < StatCnt; i++)
- Printf("%s: %zu\n", name[i], (uptr)stat[i]);
+ Printf("%s: %16zu\n", name[i], (uptr)stat[i]);
}
+#endif
+
} // namespace __tsan
StatMop4,
StatMop8,
StatMopSame,
+ StatMopIgnored,
StatMopRange,
StatMopRodata,
StatMopRangeRodata,
StatSyncAcquire,
StatSyncRelease,
+ // Clocks - acquire.
+ StatClockAcquire,
+ StatClockAcquireEmpty,
+ StatClockAcquireFastRelease,
+ StatClockAcquireLarge,
+ StatClockAcquireRepeat,
+ StatClockAcquireFull,
+ StatClockAcquiredSomething,
+ // Clocks - release.
+ StatClockRelease,
+ StatClockReleaseResize,
+ StatClockReleaseFast1,
+ StatClockReleaseFast2,
+ StatClockReleaseSlow,
+ StatClockReleaseFull,
+ StatClockReleaseAcquired,
+ StatClockReleaseClearTail,
+ // Clocks - release store.
+ StatClockStore,
+ StatClockStoreResize,
+ StatClockStoreFast,
+ StatClockStoreFull,
+ StatClockStoreTail,
+ // Clocks - acquire-release.
+ StatClockAcquireRelease,
+
// Atomics.
StatAtomic,
StatAtomicLoad,
StatAtomic8,
StatAtomic16,
- // Interceptors.
- StatInterceptor,
- StatInt_longjmp,
- StatInt_siglongjmp,
- StatInt_malloc,
- StatInt___libc_memalign,
- StatInt_calloc,
- StatInt_realloc,
- StatInt_free,
- StatInt_cfree,
- StatInt_malloc_usable_size,
- StatInt_mmap,
- StatInt_mmap64,
- StatInt_munmap,
- StatInt_memalign,
- StatInt_valloc,
- StatInt_pvalloc,
- StatInt_posix_memalign,
- StatInt__Znwm,
- StatInt__ZnwmRKSt9nothrow_t,
- StatInt__Znam,
- StatInt__ZnamRKSt9nothrow_t,
- StatInt__ZdlPv,
- StatInt__ZdlPvRKSt9nothrow_t,
- StatInt__ZdaPv,
- StatInt__ZdaPvRKSt9nothrow_t,
- StatInt_strlen,
- StatInt_memset,
- StatInt_memcpy,
- StatInt_textdomain,
- StatInt_strcmp,
- StatInt_memchr,
- StatInt_memrchr,
- StatInt_memmove,
- StatInt_memcmp,
- StatInt_strchr,
- StatInt_strchrnul,
- StatInt_strrchr,
- StatInt_strncmp,
- StatInt_strcpy,
- StatInt_strncpy,
- StatInt_strcasecmp,
- StatInt_strncasecmp,
- StatInt_strstr,
- StatInt_strdup,
- StatInt_atexit,
- StatInt__exit,
- StatInt___cxa_guard_acquire,
- StatInt___cxa_guard_release,
- StatInt___cxa_guard_abort,
- StatInt_pthread_create,
- StatInt_pthread_join,
- StatInt_pthread_detach,
- StatInt_pthread_mutex_init,
- StatInt_pthread_mutex_destroy,
- StatInt_pthread_mutex_lock,
- StatInt_pthread_mutex_trylock,
- StatInt_pthread_mutex_timedlock,
- StatInt_pthread_mutex_unlock,
- StatInt_pthread_spin_init,
- StatInt_pthread_spin_destroy,
- StatInt_pthread_spin_lock,
- StatInt_pthread_spin_trylock,
- StatInt_pthread_spin_unlock,
- StatInt_pthread_rwlock_init,
- StatInt_pthread_rwlock_destroy,
- StatInt_pthread_rwlock_rdlock,
- StatInt_pthread_rwlock_tryrdlock,
- StatInt_pthread_rwlock_timedrdlock,
- StatInt_pthread_rwlock_wrlock,
- StatInt_pthread_rwlock_trywrlock,
- StatInt_pthread_rwlock_timedwrlock,
- StatInt_pthread_rwlock_unlock,
- StatInt_pthread_cond_init,
- StatInt_pthread_cond_destroy,
- StatInt_pthread_cond_signal,
- StatInt_pthread_cond_broadcast,
- StatInt_pthread_cond_wait,
- StatInt_pthread_cond_timedwait,
- StatInt_pthread_barrier_init,
- StatInt_pthread_barrier_destroy,
- StatInt_pthread_barrier_wait,
- StatInt_pthread_once,
- StatInt_pthread_getschedparam,
- StatInt_pthread_setname_np,
- StatInt_sem_init,
- StatInt_sem_destroy,
- StatInt_sem_wait,
- StatInt_sem_trywait,
- StatInt_sem_timedwait,
- StatInt_sem_post,
- StatInt_sem_getvalue,
- StatInt_stat,
- StatInt___xstat,
- StatInt_stat64,
- StatInt___xstat64,
- StatInt_lstat,
- StatInt___lxstat,
- StatInt_lstat64,
- StatInt___lxstat64,
- StatInt_fstat,
- StatInt___fxstat,
- StatInt_fstat64,
- StatInt___fxstat64,
- StatInt_open,
- StatInt_open64,
- StatInt_creat,
- StatInt_creat64,
- StatInt_dup,
- StatInt_dup2,
- StatInt_dup3,
- StatInt_eventfd,
- StatInt_signalfd,
- StatInt_inotify_init,
- StatInt_inotify_init1,
- StatInt_socket,
- StatInt_socketpair,
- StatInt_connect,
- StatInt_bind,
- StatInt_listen,
- StatInt_accept,
- StatInt_accept4,
- StatInt_epoll_create,
- StatInt_epoll_create1,
- StatInt_close,
- StatInt___close,
- StatInt___res_iclose,
- StatInt_pipe,
- StatInt_pipe2,
- StatInt_read,
- StatInt_prctl,
- StatInt_pread,
- StatInt_pread64,
- StatInt_readv,
- StatInt_preadv,
- StatInt_preadv64,
- StatInt_write,
- StatInt_pwrite,
- StatInt_pwrite64,
- StatInt_writev,
- StatInt_pwritev,
- StatInt_pwritev64,
- StatInt_send,
- StatInt_sendmsg,
- StatInt_recv,
- StatInt_recvmsg,
- StatInt_unlink,
- StatInt_fopen,
- StatInt_freopen,
- StatInt_fclose,
- StatInt_fread,
- StatInt_fwrite,
- StatInt_fflush,
- StatInt_abort,
- StatInt_puts,
- StatInt_rmdir,
- StatInt_opendir,
- StatInt_epoll_ctl,
- StatInt_epoll_wait,
- StatInt_poll,
- StatInt_ppoll,
- StatInt_sigaction,
- StatInt_signal,
- StatInt_sigsuspend,
- StatInt_raise,
- StatInt_kill,
- StatInt_pthread_kill,
- StatInt_sleep,
- StatInt_usleep,
- StatInt_nanosleep,
- StatInt_gettimeofday,
- StatInt_fork,
- StatInt_vscanf,
- StatInt_vsscanf,
- StatInt_vfscanf,
- StatInt_scanf,
- StatInt_sscanf,
- StatInt_fscanf,
- StatInt___isoc99_vscanf,
- StatInt___isoc99_vsscanf,
- StatInt___isoc99_vfscanf,
- StatInt___isoc99_scanf,
- StatInt___isoc99_sscanf,
- StatInt___isoc99_fscanf,
- StatInt_on_exit,
- StatInt___cxa_atexit,
- StatInt_localtime,
- StatInt_localtime_r,
- StatInt_gmtime,
- StatInt_gmtime_r,
- StatInt_ctime,
- StatInt_ctime_r,
- StatInt_asctime,
- StatInt_asctime_r,
- StatInt_strptime,
- StatInt_frexp,
- StatInt_frexpf,
- StatInt_frexpl,
- StatInt_getpwnam,
- StatInt_getpwuid,
- StatInt_getgrnam,
- StatInt_getgrgid,
- StatInt_getpwnam_r,
- StatInt_getpwuid_r,
- StatInt_getgrnam_r,
- StatInt_getgrgid_r,
- StatInt_clock_getres,
- StatInt_clock_gettime,
- StatInt_clock_settime,
- StatInt_getitimer,
- StatInt_setitimer,
- StatInt_time,
- StatInt_glob,
- StatInt_glob64,
- StatInt_wait,
- StatInt_waitid,
- StatInt_waitpid,
- StatInt_wait3,
- StatInt_wait4,
- StatInt_inet_ntop,
- StatInt_inet_pton,
- StatInt_inet_aton,
- StatInt_getaddrinfo,
- StatInt_getnameinfo,
- StatInt_getsockname,
- StatInt_gethostent,
- StatInt_gethostbyname,
- StatInt_gethostbyname2,
- StatInt_gethostbyaddr,
- StatInt_gethostent_r,
- StatInt_gethostbyname_r,
- StatInt_gethostbyname2_r,
- StatInt_gethostbyaddr_r,
- StatInt_getsockopt,
- StatInt_modf,
- StatInt_modff,
- StatInt_modfl,
- StatInt_getpeername,
- StatInt_ioctl,
- StatInt_sysinfo,
- StatInt_readdir,
- StatInt_readdir64,
- StatInt_readdir_r,
- StatInt_readdir64_r,
- StatInt_ptrace,
- StatInt_setlocale,
- StatInt_getcwd,
- StatInt_get_current_dir_name,
- StatInt_strtoimax,
- StatInt_strtoumax,
- StatInt_mbstowcs,
- StatInt_mbsrtowcs,
- StatInt_mbsnrtowcs,
- StatInt_wcstombs,
- StatInt_wcsrtombs,
- StatInt_wcsnrtombs,
- StatInt_tcgetattr,
- StatInt_realpath,
- StatInt_canonicalize_file_name,
- StatInt_confstr,
- StatInt_sched_getaffinity,
- StatInt_strerror,
- StatInt_strerror_r,
- StatInt___xpg_strerror_r,
- StatInt_scandir,
- StatInt_scandir64,
- StatInt_getgroups,
- StatInt_wordexp,
- StatInt_sigwait,
- StatInt_sigwaitinfo,
- StatInt_sigtimedwait,
- StatInt_sigemptyset,
- StatInt_sigfillset,
- StatInt_sigpending,
- StatInt_sigprocmask,
- StatInt_backtrace,
- StatInt_backtrace_symbols,
- StatInt_dlopen,
- StatInt_dlclose,
- StatInt_getmntent,
- StatInt_getmntent_r,
- StatInt_statfs,
- StatInt_statfs64,
- StatInt_fstatfs,
- StatInt_fstatfs64,
- StatInt_statvfs,
- StatInt_statvfs64,
- StatInt_fstatvfs,
- StatInt_fstatvfs64,
- StatInt_initgroups,
- StatInt_ether_ntoa,
- StatInt_ether_aton,
- StatInt_ether_ntoa_r,
- StatInt_ether_aton_r,
- StatInt_ether_ntohost,
- StatInt_ether_hostton,
- StatInt_ether_line,
- StatInt_shmctl,
- StatInt_random_r,
- StatInt_tmpnam,
- StatInt_tmpnam_r,
- StatInt_tempnam,
- StatInt_sincos,
- StatInt_sincosf,
- StatInt_sincosl,
- StatInt_remquo,
- StatInt_remquof,
- StatInt_remquol,
- StatInt_lgamma,
- StatInt_lgammaf,
- StatInt_lgammal,
- StatInt_lgamma_r,
- StatInt_lgammaf_r,
- StatInt_lgammal_r,
- StatInt_drand48_r,
- StatInt_lrand48_r,
- StatInt_getline,
- StatInt_getdelim,
- StatInt_iconv,
- StatInt_times,
-
- StatInt_pthread_attr_getdetachstate,
- StatInt_pthread_attr_getguardsize,
- StatInt_pthread_attr_getschedparam,
- StatInt_pthread_attr_getschedpolicy,
- StatInt_pthread_attr_getinheritsched,
- StatInt_pthread_attr_getscope,
- StatInt_pthread_attr_getstacksize,
- StatInt_pthread_attr_getstack,
- StatInt_pthread_attr_getaffinity_np,
-
// Dynamic annotations.
StatAnnotation,
StatAnnotateHappensBefore,
StatMtxAnnotations,
StatMtxAtExit,
StatMtxMBlock,
- StatMtxJavaMBlock,
+ StatMtxDeadlockDetector,
+ StatMtxFired,
+ StatMtxRacy,
StatMtxFD,
// This must be the last.
#include "tsan_mman.h"
#include "tsan_platform.h"
+#ifndef SANITIZER_GO
// Suppressions for true/false positives in standard libraries.
static const char *const std_suppressions =
// Libstdc++ 4.4 has data races in std::string.
"race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n";
// Can be overriden in frontend.
-#ifndef TSAN_GO
extern "C" const char *WEAK __tsan_default_suppressions() {
return 0;
}
namespace __tsan {
-static SuppressionContext* g_ctx;
-
-static char *ReadFile(const char *filename) {
- if (filename == 0 || filename[0] == 0)
- return 0;
- InternalScopedBuffer<char> tmp(4*1024);
- if (filename[0] == '/' || GetPwd() == 0)
- internal_snprintf(tmp.data(), tmp.size(), "%s", filename);
- else
- internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename);
- uptr openrv = OpenFile(tmp.data(), false);
- if (internal_iserror(openrv)) {
- Printf("ThreadSanitizer: failed to open suppressions file '%s'\n",
- tmp.data());
- Die();
- }
- fd_t fd = openrv;
- const uptr fsize = internal_filesize(fd);
- if (fsize == (uptr)-1) {
- Printf("ThreadSanitizer: failed to stat suppressions file '%s'\n",
- tmp.data());
- Die();
- }
- char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1);
- if (fsize != internal_read(fd, buf, fsize)) {
- Printf("ThreadSanitizer: failed to read suppressions file '%s'\n",
- tmp.data());
- Die();
- }
- internal_close(fd);
- buf[fsize] = 0;
- return buf;
-}
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char *kSuppressionTypes[] = {
+ kSuppressionRace, kSuppressionRaceTop, kSuppressionMutex,
+ kSuppressionThread, kSuppressionSignal, kSuppressionLib,
+ kSuppressionDeadlock};
void InitializeSuppressions() {
- ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)];
- g_ctx = new(placeholder_) SuppressionContext;
- const char *supp = ReadFile(flags()->suppressions);
- g_ctx->Parse(supp);
-#ifndef TSAN_GO
- supp = __tsan_default_suppressions();
- g_ctx->Parse(supp);
- g_ctx->Parse(std_suppressions);
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
+#ifndef SANITIZER_GO
+ suppression_ctx->Parse(__tsan_default_suppressions());
+ suppression_ctx->Parse(std_suppressions);
#endif
}
-SuppressionContext *GetSuppressionContext() {
- CHECK_NE(g_ctx, 0);
- return g_ctx;
+SuppressionContext *Suppressions() {
+ CHECK(suppression_ctx);
+ return suppression_ctx;
}
-SuppressionType conv(ReportType typ) {
+static const char *conv(ReportType typ) {
if (typ == ReportTypeRace)
- return SuppressionRace;
+ return kSuppressionRace;
else if (typ == ReportTypeVptrRace)
- return SuppressionRace;
+ return kSuppressionRace;
else if (typ == ReportTypeUseAfterFree)
- return SuppressionRace;
+ return kSuppressionRace;
+ else if (typ == ReportTypeVptrUseAfterFree)
+ return kSuppressionRace;
else if (typ == ReportTypeThreadLeak)
- return SuppressionThread;
+ return kSuppressionThread;
else if (typ == ReportTypeMutexDestroyLocked)
- return SuppressionMutex;
+ return kSuppressionMutex;
+ else if (typ == ReportTypeMutexDoubleLock)
+ return kSuppressionMutex;
+ else if (typ == ReportTypeMutexBadUnlock)
+ return kSuppressionMutex;
+ else if (typ == ReportTypeMutexBadReadLock)
+ return kSuppressionMutex;
+ else if (typ == ReportTypeMutexBadReadUnlock)
+ return kSuppressionMutex;
else if (typ == ReportTypeSignalUnsafe)
- return SuppressionSignal;
+ return kSuppressionSignal;
else if (typ == ReportTypeErrnoInSignal)
- return SuppressionNone;
+ return kSuppressionNone;
+ else if (typ == ReportTypeDeadlock)
+ return kSuppressionDeadlock;
Printf("ThreadSanitizer: unknown report type %d\n", typ),
Die();
}
+static uptr IsSuppressed(const char *stype, const AddressInfo &info,
+ Suppression **sp) {
+ if (suppression_ctx->Match(info.function, stype, sp) ||
+ suppression_ctx->Match(info.file, stype, sp) ||
+ suppression_ctx->Match(info.module, stype, sp)) {
+ VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n", (*sp)->templ);
+ atomic_fetch_add(&(*sp)->hit_count, 1, memory_order_relaxed);
+ return info.address;
+ }
+ return 0;
+}
+
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
- CHECK(g_ctx);
- if (!g_ctx->SuppressionCount() || stack == 0) return 0;
- SuppressionType stype = conv(typ);
- if (stype == SuppressionNone)
+ CHECK(suppression_ctx);
+ if (!suppression_ctx->SuppressionCount() || stack == 0 ||
+ !stack->suppressable)
return 0;
- Suppression *s;
- for (const ReportStack *frame = stack; frame; frame = frame->next) {
- if (g_ctx->Match(frame->func, stype, &s) ||
- g_ctx->Match(frame->file, stype, &s) ||
- g_ctx->Match(frame->module, stype, &s)) {
- DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ);
- s->hit_count++;
- *sp = s;
- return frame->pc;
- }
+ const char *stype = conv(typ);
+ if (0 == internal_strcmp(stype, kSuppressionNone))
+ return 0;
+ for (const SymbolizedStack *frame = stack->frames; frame;
+ frame = frame->next) {
+ uptr pc = IsSuppressed(stype, frame->info, sp);
+ if (pc != 0)
+ return pc;
}
+ if (0 == internal_strcmp(stype, kSuppressionRace) && stack->frames != nullptr)
+ return IsSuppressed(kSuppressionRaceTop, stack->frames->info, sp);
return 0;
}
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
- CHECK(g_ctx);
- if (!g_ctx->SuppressionCount() || loc == 0 ||
- loc->type != ReportLocationGlobal)
+ CHECK(suppression_ctx);
+ if (!suppression_ctx->SuppressionCount() || loc == 0 ||
+ loc->type != ReportLocationGlobal || !loc->suppressable)
return 0;
- SuppressionType stype = conv(typ);
- if (stype == SuppressionNone)
+ const char *stype = conv(typ);
+ if (0 == internal_strcmp(stype, kSuppressionNone))
return 0;
Suppression *s;
- if (g_ctx->Match(loc->name, stype, &s) ||
- g_ctx->Match(loc->file, stype, &s) ||
- g_ctx->Match(loc->module, stype, &s)) {
- DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ);
- s->hit_count++;
+ const DataInfo &global = loc->global;
+ if (suppression_ctx->Match(global.name, stype, &s) ||
+ suppression_ctx->Match(global.module, stype, &s)) {
+ VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n", s->templ);
+ atomic_fetch_add(&s->hit_count, 1, memory_order_relaxed);
*sp = s;
- return loc->addr;
+ return global.start;
}
return 0;
}
void PrintMatchedSuppressions() {
- CHECK(g_ctx);
InternalMmapVector<Suppression *> matched(1);
- g_ctx->GetMatched(&matched);
+ CHECK(suppression_ctx);
+ suppression_ctx->GetMatched(&matched);
if (!matched.size())
return;
int hit_count = 0;
for (uptr i = 0; i < matched.size(); i++)
- hit_count += matched[i]->hit_count;
+ hit_count += atomic_load_relaxed(&matched[i]->hit_count);
Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count,
(int)internal_getpid());
for (uptr i = 0; i < matched.size(); i++) {
- Printf("%d %s:%s\n", matched[i]->hit_count,
- SuppressionTypeString(matched[i]->type), matched[i]->templ);
+ Printf("%d %s:%s\n", matched[i]->hit_count, matched[i]->type,
+ matched[i]->templ);
}
}
} // namespace __tsan
namespace __tsan {
+const char kSuppressionNone[] = "none";
+const char kSuppressionRace[] = "race";
+const char kSuppressionRaceTop[] = "race_top";
+const char kSuppressionMutex[] = "mutex";
+const char kSuppressionThread[] = "thread";
+const char kSuppressionSignal[] = "signal";
+const char kSuppressionLib[] = "called_from_lib";
+const char kSuppressionDeadlock[] = "deadlock";
+
void InitializeSuppressions();
+SuppressionContext *Suppressions();
void PrintMatchedSuppressions();
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp);
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp);
-SuppressionContext *GetSuppressionContext();
} // namespace __tsan
ThreadState *thr = cur_thread();
CHECK(!thr->in_symbolizer);
thr->in_symbolizer = true;
+ thr->ignore_interceptors++;
}
void ExitSymbolizer() {
ThreadState *thr = cur_thread();
CHECK(thr->in_symbolizer);
thr->in_symbolizer = false;
+ thr->ignore_interceptors--;
}
-ReportStack *NewReportStackEntry(uptr addr) {
- ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack,
- sizeof(ReportStack));
- internal_memset(ent, 0, sizeof(*ent));
- ent->pc = addr;
- return ent;
-}
-
-static ReportStack *NewReportStackEntry(const AddressInfo &info) {
- ReportStack *ent = NewReportStackEntry(info.address);
- ent->module = StripModuleName(info.module);
- ent->offset = info.module_offset;
- if (info.function)
- ent->func = internal_strdup(info.function);
- if (info.file)
- ent->file = internal_strdup(info.file);
- ent->line = info.line;
- ent->col = info.column;
- return ent;
-}
-
-
- ReportStack *next;
- char *module;
- uptr offset;
- uptr pc;
- char *func;
- char *file;
- int line;
- int col;
-
-
-// Denotes fake PC values that come from JIT/JAVA/etc.
-// For such PC values __tsan_symbolize_external() will be called.
-const uptr kExternalPCBit = 1ULL << 60;
-
// May be overriden by JIT/JAVA/etc,
// whatever produces PCs marked with kExternalPCBit.
-extern "C" bool __tsan_symbolize_external(uptr pc,
- char *func_buf, uptr func_siz,
- char *file_buf, uptr file_siz,
- int *line, int *col)
- SANITIZER_WEAK_ATTRIBUTE;
-
-bool __tsan_symbolize_external(uptr pc,
- char *func_buf, uptr func_siz,
- char *file_buf, uptr file_siz,
- int *line, int *col) {
+extern "C" bool WEAK __tsan_symbolize_external(uptr pc,
+ char *func_buf, uptr func_siz,
+ char *file_buf, uptr file_siz,
+ int *line, int *col) {
return false;
}
-ReportStack *SymbolizeCode(uptr addr) {
+SymbolizedStack *SymbolizeCode(uptr addr) {
// Check if PC comes from non-native land.
if (addr & kExternalPCBit) {
// Declare static to not consume too much stack space.
static char func_buf[1024];
static char file_buf[1024];
int line, col;
- if (!__tsan_symbolize_external(addr, func_buf, sizeof(func_buf),
- file_buf, sizeof(file_buf), &line, &col))
- return NewReportStackEntry(addr);
- ReportStack *ent = NewReportStackEntry(addr);
- ent->module = 0;
- ent->offset = 0;
- ent->func = internal_strdup(func_buf);
- ent->file = internal_strdup(file_buf);
- ent->line = line;
- ent->col = col;
- return ent;
- }
- if (!Symbolizer::Get()->IsAvailable())
- return SymbolizeCodeAddr2Line(addr);
- static const uptr kMaxAddrFrames = 16;
- InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
- for (uptr i = 0; i < kMaxAddrFrames; i++)
- new(&addr_frames[i]) AddressInfo();
- uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode(
- addr, addr_frames.data(), kMaxAddrFrames);
- if (addr_frames_num == 0)
- return NewReportStackEntry(addr);
- ReportStack *top = 0;
- ReportStack *bottom = 0;
- for (uptr i = 0; i < addr_frames_num; i++) {
- ReportStack *cur_entry = NewReportStackEntry(addr_frames[i]);
- CHECK(cur_entry);
- addr_frames[i].Clear();
- if (i == 0)
- top = cur_entry;
- else
- bottom->next = cur_entry;
- bottom = cur_entry;
+ SymbolizedStack *frame = SymbolizedStack::New(addr);
+ if (__tsan_symbolize_external(addr, func_buf, sizeof(func_buf), file_buf,
+ sizeof(file_buf), &line, &col)) {
+ frame->info.function = internal_strdup(func_buf);
+ frame->info.file = internal_strdup(file_buf);
+ frame->info.line = line;
+ frame->info.column = col;
+ }
+ return frame;
}
- return top;
+ return Symbolizer::GetOrInit()->SymbolizePC(addr);
}
ReportLocation *SymbolizeData(uptr addr) {
- if (!Symbolizer::Get()->IsAvailable())
- return 0;
DataInfo info;
- if (!Symbolizer::Get()->SymbolizeData(addr, &info))
+ if (!Symbolizer::GetOrInit()->SymbolizeData(addr, &info))
return 0;
- ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack,
- sizeof(ReportLocation));
- internal_memset(ent, 0, sizeof(*ent));
- ent->type = ReportLocationGlobal;
- ent->module = StripModuleName(info.module);
- ent->offset = info.module_offset;
- if (info.name)
- ent->name = internal_strdup(info.name);
- ent->addr = info.start;
- ent->size = info.size;
+ ReportLocation *ent = ReportLocation::New(ReportLocationGlobal);
+ ent->global = info;
return ent;
}
void SymbolizeFlush() {
- if (!Symbolizer::Get()->IsAvailable())
- return;
- Symbolizer::Get()->Flush();
+ Symbolizer::GetOrInit()->Flush();
}
} // namespace __tsan
void EnterSymbolizer();
void ExitSymbolizer();
-ReportStack *SymbolizeCode(uptr addr);
+SymbolizedStack *SymbolizeCode(uptr addr);
ReportLocation *SymbolizeData(uptr addr);
void SymbolizeFlush();
-ReportStack *SymbolizeCodeAddr2Line(uptr addr);
-
ReportStack *NewReportStackEntry(uptr addr);
} // namespace __tsan
+++ /dev/null
-//===-- tsan_symbolize_addr2line.cc ---------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "tsan_symbolize.h"
-#include "tsan_mman.h"
-#include "tsan_rtl.h"
-#include "tsan_platform.h"
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <link.h>
-#include <linux/limits.h>
-#include <sys/types.h>
-
-namespace __tsan {
-
-struct ModuleDesc {
- const char *fullname;
- const char *name;
- uptr base;
- int inp_fd;
- int out_fd;
-};
-
-struct SectionDesc {
- SectionDesc *next;
- ModuleDesc *module;
- uptr base;
- uptr end;
-};
-
-struct DlIteratePhdrCtx {
- SectionDesc *sections;
- bool is_first;
-};
-
-static void NOINLINE InitModule(ModuleDesc *m) {
- int outfd[2] = {};
- if (pipe(&outfd[0])) {
- Printf("ThreadSanitizer: outfd pipe() failed (%d)\n", errno);
- Die();
- }
- int infd[2] = {};
- if (pipe(&infd[0])) {
- Printf("ThreadSanitizer: infd pipe() failed (%d)\n", errno);
- Die();
- }
- int pid = fork();
- if (pid == 0) {
- internal_close(STDOUT_FILENO);
- internal_close(STDIN_FILENO);
- internal_dup2(outfd[0], STDIN_FILENO);
- internal_dup2(infd[1], STDOUT_FILENO);
- internal_close(outfd[0]);
- internal_close(outfd[1]);
- internal_close(infd[0]);
- internal_close(infd[1]);
- for (int fd = getdtablesize(); fd > 2; fd--)
- internal_close(fd);
- execl("/usr/bin/addr2line", "/usr/bin/addr2line", "-Cfe", m->fullname, 0);
- _exit(0);
- } else if (pid < 0) {
- Printf("ThreadSanitizer: failed to fork symbolizer\n");
- Die();
- }
- internal_close(outfd[0]);
- internal_close(infd[1]);
- m->inp_fd = infd[0];
- m->out_fd = outfd[1];
-}
-
-static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
- DlIteratePhdrCtx *ctx = (DlIteratePhdrCtx*)arg;
- InternalScopedBuffer<char> tmp(128);
- if (ctx->is_first) {
- internal_snprintf(tmp.data(), tmp.size(), "/proc/%d/exe",
- (int)internal_getpid());
- info->dlpi_name = tmp.data();
- }
- ctx->is_first = false;
- if (info->dlpi_name == 0 || info->dlpi_name[0] == 0)
- return 0;
- ModuleDesc *m = (ModuleDesc*)internal_alloc(MBlockReportStack,
- sizeof(ModuleDesc));
- m->fullname = internal_strdup(info->dlpi_name);
- m->name = internal_strrchr(m->fullname, '/');
- if (m->name)
- m->name += 1;
- else
- m->name = m->fullname;
- m->base = (uptr)info->dlpi_addr;
- m->inp_fd = -1;
- m->out_fd = -1;
- DPrintf2("Module %s %zx\n", m->name, m->base);
- for (int i = 0; i < info->dlpi_phnum; i++) {
- const Elf64_Phdr *s = &info->dlpi_phdr[i];
- DPrintf2(" Section p_type=%zx p_offset=%zx p_vaddr=%zx p_paddr=%zx"
- " p_filesz=%zx p_memsz=%zx p_flags=%zx p_align=%zx\n",
- (uptr)s->p_type, (uptr)s->p_offset, (uptr)s->p_vaddr,
- (uptr)s->p_paddr, (uptr)s->p_filesz, (uptr)s->p_memsz,
- (uptr)s->p_flags, (uptr)s->p_align);
- if (s->p_type != PT_LOAD)
- continue;
- SectionDesc *sec = (SectionDesc*)internal_alloc(MBlockReportStack,
- sizeof(SectionDesc));
- sec->module = m;
- sec->base = info->dlpi_addr + s->p_vaddr;
- sec->end = sec->base + s->p_memsz;
- sec->next = ctx->sections;
- ctx->sections = sec;
- DPrintf2(" Section %zx-%zx\n", sec->base, sec->end);
- }
- return 0;
-}
-
-static SectionDesc *InitSections() {
- DlIteratePhdrCtx ctx = {0, true};
- dl_iterate_phdr(dl_iterate_phdr_cb, &ctx);
- return ctx.sections;
-}
-
-static SectionDesc *GetSectionDesc(uptr addr) {
- static SectionDesc *sections = 0;
- if (sections == 0)
- sections = InitSections();
- for (SectionDesc *s = sections; s; s = s->next) {
- if (addr >= s->base && addr < s->end) {
- if (s->module->inp_fd == -1)
- InitModule(s->module);
- return s;
- }
- }
- return 0;
-}
-
-ReportStack *SymbolizeCodeAddr2Line(uptr addr) {
- SectionDesc *s = GetSectionDesc(addr);
- if (s == 0)
- return NewReportStackEntry(addr);
- ModuleDesc *m = s->module;
- uptr offset = addr - m->base;
- char addrstr[32];
- internal_snprintf(addrstr, sizeof(addrstr), "%p\n", (void*)offset);
- if (0 >= internal_write(m->out_fd, addrstr, internal_strlen(addrstr))) {
- Printf("ThreadSanitizer: can't write from symbolizer (%d, %d)\n",
- m->out_fd, errno);
- Die();
- }
- InternalScopedBuffer<char> func(1024);
- ssize_t len = internal_read(m->inp_fd, func.data(), func.size() - 1);
- if (len <= 0) {
- Printf("ThreadSanitizer: can't read from symbolizer (%d, %d)\n",
- m->inp_fd, errno);
- Die();
- }
- func.data()[len] = 0;
- ReportStack *res = NewReportStackEntry(addr);
- res->module = internal_strdup(m->name);
- res->offset = offset;
- char *pos = (char*)internal_strchr(func.data(), '\n');
- if (pos && func[0] != '?') {
- res->func = (char*)internal_alloc(MBlockReportStack, pos - func.data() + 1);
- internal_memcpy(res->func, func.data(), pos - func.data());
- res->func[pos - func.data()] = 0;
- char *pos2 = (char*)internal_strchr(pos, ':');
- if (pos2) {
- res->file = (char*)internal_alloc(MBlockReportStack, pos2 - pos - 1 + 1);
- internal_memcpy(res->file, pos + 1, pos2 - pos - 1);
- res->file[pos2 - pos - 1] = 0;
- res->line = atoi(pos2 + 1);
- }
- }
- return res;
-}
-
-ReportStack *SymbolizeDataAddr2Line(uptr addr) {
- return 0;
-}
-
-} // namespace __tsan
namespace __tsan {
-SyncVar::SyncVar(uptr addr, u64 uid)
- : mtx(MutexTypeSyncVar, StatMtxSyncVar)
- , addr(addr)
- , uid(uid)
- , owner_tid(kInvalidTid)
- , last_lock()
- , recursion()
- , is_rw()
- , is_recursive()
- , is_broken()
- , is_linker_init() {
-}
+void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s);
-SyncTab::Part::Part()
- : mtx(MutexTypeSyncTab, StatMtxSyncTab)
- , val() {
+SyncVar::SyncVar()
+ : mtx(MutexTypeSyncVar, StatMtxSyncVar) {
+ Reset(0);
}
-SyncTab::SyncTab() {
+void SyncVar::Init(ThreadState *thr, uptr pc, uptr addr, u64 uid) {
+ this->addr = addr;
+ this->uid = uid;
+ this->next = 0;
+
+ creation_stack_id = 0;
+ if (kCppMode) // Go does not use them
+ creation_stack_id = CurrentStackId(thr, pc);
+ if (common_flags()->detect_deadlocks)
+ DDMutexInit(thr, pc, this);
}
-SyncTab::~SyncTab() {
- for (int i = 0; i < kPartCount; i++) {
- while (tab_[i].val) {
- SyncVar *tmp = tab_[i].val;
- tab_[i].val = tmp->next;
- DestroyAndFree(tmp);
- }
+void SyncVar::Reset(ThreadState *thr) {
+ uid = 0;
+ creation_stack_id = 0;
+ owner_tid = kInvalidTid;
+ last_lock = 0;
+ recursion = 0;
+ is_rw = 0;
+ is_recursive = 0;
+ is_broken = 0;
+ is_linker_init = 0;
+
+ if (thr == 0) {
+ CHECK_EQ(clock.size(), 0);
+ CHECK_EQ(read_clock.size(), 0);
+ } else {
+ clock.Reset(&thr->clock_cache);
+ read_clock.Reset(&thr->clock_cache);
}
}
-SyncVar* SyncTab::GetOrCreateAndLock(ThreadState *thr, uptr pc,
- uptr addr, bool write_lock) {
- return GetAndLock(thr, pc, addr, write_lock, true);
+MetaMap::MetaMap() {
+ atomic_store(&uid_gen_, 0, memory_order_relaxed);
}
-SyncVar* SyncTab::GetIfExistsAndLock(uptr addr, bool write_lock) {
- return GetAndLock(0, 0, addr, write_lock, false);
+void MetaMap::AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz) {
+ u32 idx = block_alloc_.Alloc(&thr->block_cache);
+ MBlock *b = block_alloc_.Map(idx);
+ b->siz = sz;
+ b->tid = thr->tid;
+ b->stk = CurrentStackId(thr, pc);
+ u32 *meta = MemToMeta(p);
+ DCHECK_EQ(*meta, 0);
+ *meta = idx | kFlagBlock;
}
-SyncVar* SyncTab::Create(ThreadState *thr, uptr pc, uptr addr) {
- StatInc(thr, StatSyncCreated);
- void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
- const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
- SyncVar *res = new(mem) SyncVar(addr, uid);
-#ifndef TSAN_GO
- res->creation_stack_id = CurrentStackId(thr, pc);
-#endif
- return res;
+uptr MetaMap::FreeBlock(ThreadState *thr, uptr pc, uptr p) {
+ MBlock* b = GetBlock(p);
+ if (b == 0)
+ return 0;
+ uptr sz = RoundUpTo(b->siz, kMetaShadowCell);
+ FreeRange(thr, pc, p, sz);
+ return sz;
}
-SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
- uptr addr, bool write_lock, bool create) {
-#ifndef TSAN_GO
- { // NOLINT
- SyncVar *res = GetJavaSync(thr, pc, addr, write_lock, create);
- if (res)
- return res;
- }
-
- // Here we ask only PrimaryAllocator, because
- // SecondaryAllocator::PointerIsMine() is slow and we have fallback on
- // the hashmap anyway.
- if (PrimaryAllocator::PointerIsMine((void*)addr)) {
- MBlock *b = user_mblock(thr, (void*)addr);
- CHECK_NE(b, 0);
- MBlock::ScopedLock l(b);
- SyncVar *res = 0;
- for (res = b->ListHead(); res; res = res->next) {
- if (res->addr == addr)
- break;
- }
- if (res == 0) {
- if (!create)
- return 0;
- res = Create(thr, pc, addr);
- b->ListPush(res);
- }
- if (write_lock)
- res->mtx.Lock();
- else
- res->mtx.ReadLock();
- return res;
- }
-#endif
-
- Part *p = &tab_[PartIdx(addr)];
- {
- ReadLock l(&p->mtx);
- for (SyncVar *res = p->val; res; res = res->next) {
- if (res->addr == addr) {
- if (write_lock)
- res->mtx.Lock();
- else
- res->mtx.ReadLock();
- return res;
- }
+bool MetaMap::FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz) {
+ bool has_something = false;
+ u32 *meta = MemToMeta(p);
+ u32 *end = MemToMeta(p + sz);
+ if (end == meta)
+ end++;
+ for (; meta < end; meta++) {
+ u32 idx = *meta;
+ if (idx == 0) {
+ // Note: don't write to meta in this case -- the block can be huge.
+ continue;
}
- }
- if (!create)
- return 0;
- {
- Lock l(&p->mtx);
- SyncVar *res = p->val;
- for (; res; res = res->next) {
- if (res->addr == addr)
+ *meta = 0;
+ has_something = true;
+ while (idx != 0) {
+ if (idx & kFlagBlock) {
+ block_alloc_.Free(&thr->block_cache, idx & ~kFlagMask);
break;
+ } else if (idx & kFlagSync) {
+ DCHECK(idx & kFlagSync);
+ SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
+ u32 next = s->next;
+ s->Reset(thr);
+ sync_alloc_.Free(&thr->sync_cache, idx & ~kFlagMask);
+ idx = next;
+ } else {
+ CHECK(0);
+ }
}
- if (res == 0) {
- res = Create(thr, pc, addr);
- res->next = p->val;
- p->val = res;
- }
- if (write_lock)
- res->mtx.Lock();
- else
- res->mtx.ReadLock();
- return res;
}
+ return has_something;
}
-SyncVar* SyncTab::GetAndRemove(ThreadState *thr, uptr pc, uptr addr) {
-#ifndef TSAN_GO
- { // NOLINT
- SyncVar *res = GetAndRemoveJavaSync(thr, pc, addr);
- if (res)
- return res;
+// ResetRange removes all meta objects from the range.
+// It is called for large mmap-ed regions. The function is best-effort wrt
+// freeing of meta objects, because we don't want to page in the whole range
+// which can be huge. The function probes pages one-by-one until it finds a page
+// without meta objects, at this point it stops freeing meta objects. Because
+// thread stacks grow top-down, we do the same starting from end as well.
+void MetaMap::ResetRange(ThreadState *thr, uptr pc, uptr p, uptr sz) {
+ const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
+ const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
+ if (sz <= 4 * kPageSize) {
+ // If the range is small, just do the normal free procedure.
+ FreeRange(thr, pc, p, sz);
+ return;
}
- if (PrimaryAllocator::PointerIsMine((void*)addr)) {
- MBlock *b = user_mblock(thr, (void*)addr);
- CHECK_NE(b, 0);
- SyncVar *res = 0;
- {
- MBlock::ScopedLock l(b);
- res = b->ListHead();
- if (res) {
- if (res->addr == addr) {
- if (res->is_linker_init)
- return 0;
- b->ListPop();
- } else {
- SyncVar **prev = &res->next;
- res = *prev;
- while (res) {
- if (res->addr == addr) {
- if (res->is_linker_init)
- return 0;
- *prev = res->next;
- break;
- }
- prev = &res->next;
- res = *prev;
- }
- }
- if (res) {
- StatInc(thr, StatSyncDestroyed);
- res->mtx.Lock();
- res->mtx.Unlock();
- }
- }
- }
- return res;
+ // First, round both ends of the range to page size.
+ uptr diff = RoundUp(p, kPageSize) - p;
+ if (diff != 0) {
+ FreeRange(thr, pc, p, diff);
+ p += diff;
+ sz -= diff;
}
-#endif
-
- Part *p = &tab_[PartIdx(addr)];
- SyncVar *res = 0;
- {
- Lock l(&p->mtx);
- SyncVar **prev = &p->val;
- res = *prev;
- while (res) {
- if (res->addr == addr) {
- if (res->is_linker_init)
- return 0;
- *prev = res->next;
- break;
- }
- prev = &res->next;
- res = *prev;
- }
+ diff = p + sz - RoundDown(p + sz, kPageSize);
+ if (diff != 0) {
+ FreeRange(thr, pc, p + sz - diff, diff);
+ sz -= diff;
+ }
+ // Now we must have a non-empty page-aligned range.
+ CHECK_GT(sz, 0);
+ CHECK_EQ(p, RoundUp(p, kPageSize));
+ CHECK_EQ(sz, RoundUp(sz, kPageSize));
+ const uptr p0 = p;
+ const uptr sz0 = sz;
+ // Probe start of the range.
+ while (sz > 0) {
+ bool has_something = FreeRange(thr, pc, p, kPageSize);
+ p += kPageSize;
+ sz -= kPageSize;
+ if (!has_something)
+ break;
}
- if (res) {
- StatInc(thr, StatSyncDestroyed);
- res->mtx.Lock();
- res->mtx.Unlock();
+ // Probe end of the range.
+ while (sz > 0) {
+ bool has_something = FreeRange(thr, pc, p - kPageSize, kPageSize);
+ sz -= kPageSize;
+ if (!has_something)
+ break;
}
- return res;
+ // Finally, page out the whole range (including the parts that we've just
+ // freed). Note: we can't simply madvise, because we need to leave a zeroed
+ // range (otherwise __tsan_java_move can crash if it encounters a left-over
+ // meta objects in java heap).
+ uptr metap = (uptr)MemToMeta(p0);
+ uptr metasz = sz0 / kMetaRatio;
+ UnmapOrDie((void*)metap, metasz);
+ MmapFixedNoReserve(metap, metasz);
}
-int SyncTab::PartIdx(uptr addr) {
- return (addr >> 3) % kPartCount;
+MBlock* MetaMap::GetBlock(uptr p) {
+ u32 *meta = MemToMeta(p);
+ u32 idx = *meta;
+ for (;;) {
+ if (idx == 0)
+ return 0;
+ if (idx & kFlagBlock)
+ return block_alloc_.Map(idx & ~kFlagMask);
+ DCHECK(idx & kFlagSync);
+ SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
+ idx = s->next;
+ }
}
-StackTrace::StackTrace()
- : n_()
- , s_()
- , c_() {
+SyncVar* MetaMap::GetOrCreateAndLock(ThreadState *thr, uptr pc,
+ uptr addr, bool write_lock) {
+ return GetAndLock(thr, pc, addr, write_lock, true);
}
-StackTrace::StackTrace(uptr *buf, uptr cnt)
- : n_()
- , s_(buf)
- , c_(cnt) {
- CHECK_NE(buf, 0);
- CHECK_NE(cnt, 0);
+SyncVar* MetaMap::GetIfExistsAndLock(uptr addr) {
+ return GetAndLock(0, 0, addr, true, false);
}
-StackTrace::~StackTrace() {
- Reset();
-}
+SyncVar* MetaMap::GetAndLock(ThreadState *thr, uptr pc,
+ uptr addr, bool write_lock, bool create) {
+ u32 *meta = MemToMeta(addr);
+ u32 idx0 = *meta;
+ u32 myidx = 0;
+ SyncVar *mys = 0;
+ for (;;) {
+ u32 idx = idx0;
+ for (;;) {
+ if (idx == 0)
+ break;
+ if (idx & kFlagBlock)
+ break;
+ DCHECK(idx & kFlagSync);
+ SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
+ if (s->addr == addr) {
+ if (myidx != 0) {
+ mys->Reset(thr);
+ sync_alloc_.Free(&thr->sync_cache, myidx);
+ }
+ if (write_lock)
+ s->mtx.Lock();
+ else
+ s->mtx.ReadLock();
+ return s;
+ }
+ idx = s->next;
+ }
+ if (!create)
+ return 0;
+ if (*meta != idx0) {
+ idx0 = *meta;
+ continue;
+ }
-void StackTrace::Reset() {
- if (s_ && !c_) {
- CHECK_NE(n_, 0);
- internal_free(s_);
- s_ = 0;
+ if (myidx == 0) {
+ const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
+ myidx = sync_alloc_.Alloc(&thr->sync_cache);
+ mys = sync_alloc_.Map(myidx);
+ mys->Init(thr, pc, addr, uid);
+ }
+ mys->next = idx0;
+ if (atomic_compare_exchange_strong((atomic_uint32_t*)meta, &idx0,
+ myidx | kFlagSync, memory_order_release)) {
+ if (write_lock)
+ mys->mtx.Lock();
+ else
+ mys->mtx.ReadLock();
+ return mys;
+ }
}
- n_ = 0;
}
-void StackTrace::Init(const uptr *pcs, uptr cnt) {
- Reset();
- if (cnt == 0)
- return;
- if (c_) {
- CHECK_NE(s_, 0);
- CHECK_LE(cnt, c_);
- } else {
- s_ = (uptr*)internal_alloc(MBlockStackTrace, cnt * sizeof(s_[0]));
+void MetaMap::MoveMemory(uptr src, uptr dst, uptr sz) {
+ // src and dst can overlap,
+ // there are no concurrent accesses to the regions (e.g. stop-the-world).
+ CHECK_NE(src, dst);
+ CHECK_NE(sz, 0);
+ uptr diff = dst - src;
+ u32 *src_meta = MemToMeta(src);
+ u32 *dst_meta = MemToMeta(dst);
+ u32 *src_meta_end = MemToMeta(src + sz);
+ uptr inc = 1;
+ if (dst > src) {
+ src_meta = MemToMeta(src + sz) - 1;
+ dst_meta = MemToMeta(dst + sz) - 1;
+ src_meta_end = MemToMeta(src) - 1;
+ inc = -1;
}
- n_ = cnt;
- internal_memcpy(s_, pcs, cnt * sizeof(s_[0]));
-}
-
-void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) {
- Reset();
- n_ = thr->shadow_stack_pos - thr->shadow_stack;
- if (n_ + !!toppc == 0)
- return;
- uptr start = 0;
- if (c_) {
- CHECK_NE(s_, 0);
- if (n_ + !!toppc > c_) {
- start = n_ - c_ + !!toppc;
- n_ = c_ - !!toppc;
- }
- } else {
- // Cap potentially huge stacks.
- if (n_ + !!toppc > kTraceStackSize) {
- start = n_ - kTraceStackSize + !!toppc;
- n_ = kTraceStackSize - !!toppc;
+ for (; src_meta != src_meta_end; src_meta += inc, dst_meta += inc) {
+ CHECK_EQ(*dst_meta, 0);
+ u32 idx = *src_meta;
+ *src_meta = 0;
+ *dst_meta = idx;
+ // Patch the addresses in sync objects.
+ while (idx != 0) {
+ if (idx & kFlagBlock)
+ break;
+ CHECK(idx & kFlagSync);
+ SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
+ s->addr += diff;
+ idx = s->next;
}
- s_ = (uptr*)internal_alloc(MBlockStackTrace,
- (n_ + !!toppc) * sizeof(s_[0]));
}
- for (uptr i = 0; i < n_; i++)
- s_[i] = thr->shadow_stack[start + i];
- if (toppc) {
- s_[n_] = toppc;
- n_++;
- }
-}
-
-void StackTrace::CopyFrom(const StackTrace& other) {
- Reset();
- Init(other.Begin(), other.Size());
-}
-
-bool StackTrace::IsEmpty() const {
- return n_ == 0;
-}
-
-uptr StackTrace::Size() const {
- return n_;
-}
-
-uptr StackTrace::Get(uptr i) const {
- CHECK_LT(i, n_);
- return s_[i];
}
-const uptr *StackTrace::Begin() const {
- return s_;
+void MetaMap::OnThreadIdle(ThreadState *thr) {
+ block_alloc_.FlushCache(&thr->block_cache);
+ sync_alloc_.FlushCache(&thr->sync_cache);
}
} // namespace __tsan
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
-#include "tsan_clock.h"
+#include "sanitizer_common/sanitizer_deadlock_detector_interface.h"
#include "tsan_defs.h"
+#include "tsan_clock.h"
#include "tsan_mutex.h"
+#include "tsan_dense_alloc.h"
namespace __tsan {
-class SlabCache;
-
-class StackTrace {
- public:
- StackTrace();
- // Initialized the object in "static mode",
- // in this mode it never calls malloc/free but uses the provided buffer.
- StackTrace(uptr *buf, uptr cnt);
- ~StackTrace();
- void Reset();
-
- void Init(const uptr *pcs, uptr cnt);
- void ObtainCurrent(ThreadState *thr, uptr toppc);
- bool IsEmpty() const;
- uptr Size() const;
- uptr Get(uptr i) const;
- const uptr *Begin() const;
- void CopyFrom(const StackTrace& other);
-
- private:
- uptr n_;
- uptr *s_;
- const uptr c_;
-
- StackTrace(const StackTrace&);
- void operator = (const StackTrace&);
-};
-
struct SyncVar {
- explicit SyncVar(uptr addr, u64 uid);
+ SyncVar();
static const int kInvalidTid = -1;
+ uptr addr; // overwritten by DenseSlabAlloc freelist
Mutex mtx;
- uptr addr;
- const u64 uid; // Globally unique id.
- SyncClock clock;
- SyncClock read_clock; // Used for rw mutexes only.
+ u64 uid; // Globally unique id.
u32 creation_stack_id;
int owner_tid; // Set only by exclusive owners.
u64 last_lock;
bool is_recursive;
bool is_broken;
bool is_linker_init;
- SyncVar *next; // In SyncTab hashtable.
+ u32 next; // in MetaMap
+ DDMutex dd;
+ SyncClock read_clock; // Used for rw mutexes only.
+ // The clock is placed last, so that it is situated on a different cache line
+ // with the mtx. This reduces contention for hot sync objects.
+ SyncClock clock;
+
+ void Init(ThreadState *thr, uptr pc, uptr addr, u64 uid);
+ void Reset(ThreadState *thr);
- uptr GetMemoryConsumption();
u64 GetId() const {
// 47 lsb is addr, then 14 bits is low part of uid, then 3 zero bits.
return GetLsb((u64)addr | (uid << 47), 61);
}
};
-class SyncTab {
+/* MetaMap allows to map arbitrary user pointers onto various descriptors.
+ Currently it maps pointers to heap block descriptors and sync var descs.
+ It uses 1/2 direct shadow, see tsan_platform.h.
+*/
+class MetaMap {
public:
- SyncTab();
- ~SyncTab();
+ MetaMap();
+
+ void AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz);
+ uptr FreeBlock(ThreadState *thr, uptr pc, uptr p);
+ bool FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz);
+ void ResetRange(ThreadState *thr, uptr pc, uptr p, uptr sz);
+ MBlock* GetBlock(uptr p);
SyncVar* GetOrCreateAndLock(ThreadState *thr, uptr pc,
uptr addr, bool write_lock);
- SyncVar* GetIfExistsAndLock(uptr addr, bool write_lock);
-
- // If the SyncVar does not exist, returns 0.
- SyncVar* GetAndRemove(ThreadState *thr, uptr pc, uptr addr);
+ SyncVar* GetIfExistsAndLock(uptr addr);
- SyncVar* Create(ThreadState *thr, uptr pc, uptr addr);
+ void MoveMemory(uptr src, uptr dst, uptr sz);
- uptr GetMemoryConsumption(uptr *nsync);
+ void OnThreadIdle(ThreadState *thr);
private:
- struct Part {
- Mutex mtx;
- SyncVar *val;
- char pad[kCacheLineSize - sizeof(Mutex) - sizeof(SyncVar*)]; // NOLINT
- Part();
- };
-
- // FIXME: Implement something more sane.
- static const int kPartCount = 1009;
- Part tab_[kPartCount];
+ static const u32 kFlagMask = 3u << 30;
+ static const u32 kFlagBlock = 1u << 30;
+ static const u32 kFlagSync = 2u << 30;
+ typedef DenseSlabAlloc<MBlock, 1<<16, 1<<12> BlockAlloc;
+ typedef DenseSlabAlloc<SyncVar, 1<<16, 1<<10> SyncAlloc;
+ BlockAlloc block_alloc_;
+ SyncAlloc sync_alloc_;
atomic_uint64_t uid_gen_;
- int PartIdx(uptr addr);
-
- SyncVar* GetAndLock(ThreadState *thr, uptr pc,
- uptr addr, bool write_lock, bool create);
-
- SyncTab(const SyncTab&); // Not implemented.
- void operator = (const SyncTab&); // Not implemented.
+ SyncVar* GetAndLock(ThreadState *thr, uptr pc, uptr addr, bool write_lock,
+ bool create);
};
} // namespace __tsan
#include "tsan_defs.h"
#include "tsan_mutex.h"
-#include "tsan_sync.h"
+#include "tsan_stack_trace.h"
#include "tsan_mutexset.h"
namespace __tsan {
-const int kTracePartSizeBits = 14;
+const int kTracePartSizeBits = 13;
const int kTracePartSize = 1 << kTracePartSizeBits;
-const int kTraceParts = 4 * 1024 * 1024 / kTracePartSize;
+const int kTraceParts = 2 * 1024 * 1024 / kTracePartSize;
const int kTraceSize = kTracePartSize * kTraceParts;
// Must fit into 3 bits.
typedef u64 Event;
struct TraceHeader {
- StackTrace stack0; // Start stack for the trace.
+#ifndef SANITIZER_GO
+ BufferedStackTrace stack0; // Start stack for the trace.
+#else
+ VarSizeStackTrace stack0;
+#endif
u64 epoch0; // Start epoch for the trace.
MutexSet mset0;
-#ifndef TSAN_GO
- uptr stack0buf[kTraceStackSize];
-#endif
- TraceHeader()
-#ifndef TSAN_GO
- : stack0(stack0buf, kTraceStackSize)
-#else
- : stack0()
-#endif
- , epoch0() {
- }
+ TraceHeader() : stack0(), epoch0() {}
};
struct Trace {
- TraceHeader headers[kTraceParts];
Mutex mtx;
-#ifndef TSAN_GO
+#ifndef SANITIZER_GO
// Must be last to catch overflow as paging fault.
// Go shadow stack is dynamically allocated.
uptr shadow_stack[kShadowStackSize];
#endif
+ // Must be the last field, because we unmap the unused part in
+ // CreateThreadContext.
+ TraceHeader headers[kTraceParts];
Trace()
: mtx(MutexTypeTrace, StatMtxTrace) {
do {
StatInc(thr, StatShadowProcessed);
const unsigned kAccessSize = 1 << kAccessSizeLog;
- unsigned off = cur.ComputeSearchOffset();
- u64 *sp = &shadow_mem[(idx + off) % kShadowCnt];
+ u64 *sp = &shadow_mem[idx];
old = LoadShadow(sp);
if (old.IsZero()) {
StatInc(thr, StatShadowZero);
// same thread?
if (Shadow::TidsAreEqual(old, cur)) {
StatInc(thr, StatShadowSameThread);
- if (OldIsInSameSynchEpoch(old, thr)) {
- if (old.IsRWNotWeaker(kAccessIsWrite, kIsAtomic)) {
- // found a slot that holds effectively the same info
- // (that is, same tid, same sync epoch and same size)
- StatInc(thr, StatMopSame);
- return;
- }
- StoreIfNotYetStored(sp, &store_word);
- break;
- }
if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic))
StoreIfNotYetStored(sp, &store_word);
break;
}
StatInc(thr, StatShadowAnotherThread);
if (HappensBefore(old, thr)) {
- StoreIfNotYetStored(sp, &store_word);
+ if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic))
+ StoreIfNotYetStored(sp, &store_word);
break;
}
if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic))
return begin_[i];
}
- T *PushBack(T v = T()) {
+ T *PushBack() {
EnsureSize(Size() + 1);
- end_[-1] = v;
- return &end_[-1];
+ T *p = &end_[-1];
+ internal_memset(p, 0, sizeof(*p));
+ return p;
+ }
+
+ T *PushBack(const T& v) {
+ EnsureSize(Size() + 1);
+ T *p = &end_[-1];
+ internal_memcpy(p, &v, sizeof(*p));
+ return p;
}
void PopBack() {
}
void Resize(uptr size) {
+ if (size == 0) {
+ end_ = begin_;
+ return;
+ }
uptr old_size = Size();
EnsureSize(size);
if (old_size < size) {
for (uptr i = old_size; i < size; i++)
- begin_[i] = T();
+ internal_memset(&begin_[i], 0, sizeof(begin_[i]));
}
}
return;
}
uptr cap0 = last_ - begin_;
- uptr cap = 2 * cap0;
+ uptr cap = cap0 * 5 / 4; // 25% growth
if (cap == 0)
cap = 16;
if (cap < size)
# May be used by toolexeclibdir.
gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
-DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC -DCAN_SANITIZE_UB=1
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+AM_CXXFLAGS += -std=gnu++11
ACLOCAL_AMFLAGS = -I m4
toolexeclib_LTLIBRARIES = libubsan.la
-ubsan_files = \
+ubsan_plugin_files = \
ubsan_diag.cc \
+ ubsan_flags.cc \
ubsan_handlers.cc \
ubsan_handlers_cxx.cc \
+ ubsan_init.cc \
ubsan_type_hash.cc \
+ ubsan_type_hash_itanium.cc \
+ ubsan_type_hash_win.cc \
ubsan_value.cc
+ubsan_files = \
+ $(ubsan_plugin_files) \
+ ubsan_init_standalone.cc
+
libubsan_la_SOURCES = $(ubsan_files)
libubsan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la
if !USING_MAC_INTERPOSE
libubsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libubsan)
# Use special rules for files that require RTTI support.
-ubsan_handlers_cxx.% ubsan_type_hash.% : AM_CXXFLAGS += -frtti
+ubsan_handlers_cxx.% ubsan_type_hash.% ubsan_type_hash_itanium.% : AM_CXXFLAGS += -frtti
# Work around what appears to be a GNU make bug handling MAKEFLAGS
# values defined in terms of make variables, as is the case for CC and
libubsan_la_DEPENDENCIES = \
$(top_builddir)/sanitizer_common/libsanitizer_common.la \
$(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1)
-am__objects_1 = ubsan_diag.lo ubsan_handlers.lo ubsan_handlers_cxx.lo \
- ubsan_type_hash.lo ubsan_value.lo
-am_libubsan_la_OBJECTS = $(am__objects_1)
+am__objects_1 = ubsan_diag.lo ubsan_flags.lo ubsan_handlers.lo \
+ ubsan_handlers_cxx.lo ubsan_init.lo ubsan_type_hash.lo \
+ ubsan_type_hash_itanium.lo ubsan_type_hash_win.lo \
+ ubsan_value.lo
+am__objects_2 = $(am__objects_1) ubsan_init_standalone.lo
+am_libubsan_la_OBJECTS = $(am__objects_2)
libubsan_la_OBJECTS = $(am_libubsan_la_OBJECTS)
libubsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
-DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
+DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC -DCAN_SANITIZE_UB=1
DEPDIR = @DEPDIR@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
+OBSTACK_DEFS = @OBSTACK_DEFS@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
+RPC_DEFS = @RPC_DEFS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
+TSAN_TARGET_DEPENDENT_OBJECTS = @TSAN_TARGET_DEPENDENT_OBJECTS@
VERSION = @VERSION@
VIEW_FILE = @VIEW_FILE@
abs_builddir = @abs_builddir@
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
- -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
+ -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) \
+ -std=gnu++11
ACLOCAL_AMFLAGS = -I m4
toolexeclib_LTLIBRARIES = libubsan.la
-ubsan_files = \
+ubsan_plugin_files = \
ubsan_diag.cc \
+ ubsan_flags.cc \
ubsan_handlers.cc \
ubsan_handlers_cxx.cc \
+ ubsan_init.cc \
ubsan_type_hash.cc \
+ ubsan_type_hash_itanium.cc \
+ ubsan_type_hash_win.cc \
ubsan_value.cc
+ubsan_files = \
+ $(ubsan_plugin_files) \
+ ubsan_init_standalone.cc
+
libubsan_la_SOURCES = $(ubsan_files)
libubsan_la_LIBADD = \
$(top_builddir)/sanitizer_common/libsanitizer_common.la \
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_diag.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_flags.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_handlers.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_handlers_cxx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_init.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_init_standalone.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash_itanium.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash_win.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_value.Plo@am__quote@
.cc.o:
# Use special rules for files that require RTTI support.
-ubsan_handlers_cxx.% ubsan_type_hash.% : AM_CXXFLAGS += -frtti
+ubsan_handlers_cxx.% ubsan_type_hash.% ubsan_type_hash_itanium.% : AM_CXXFLAGS += -frtti
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
--- /dev/null
+//===-- ubsan_checks.inc ----------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// List of checks handled by UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_CHECK
+# error "Define UBSAN_CHECK prior to including this file!"
+#endif
+
+// UBSAN_CHECK(Name, SummaryKind, FlagName)
+// SummaryKind and FlagName should be string literals.
+
+UBSAN_CHECK(GenericUB, "undefined-behavior", "-fsanitize=undefined")
+UBSAN_CHECK(NullPointerUse, "null-pointer-use", "-fsanitize=null")
+UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use",
+ "-fsanitize=alignment")
+UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size",
+ "-fsanitize=object-size")
+UBSAN_CHECK(SignedIntegerOverflow, "signed-integer-overflow",
+ "-fsanitize=signed-integer-overflow")
+UBSAN_CHECK(UnsignedIntegerOverflow, "unsigned-integer-overflow",
+ "-fsanitize=unsigned-integer-overflow")
+UBSAN_CHECK(IntegerDivideByZero, "integer-divide-by-zero",
+ "-fsanitize=integer-divide-by-zero")
+UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero",
+ "-fsanitize=float-divide-by-zero")
+UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "-fsanitize=shift-base")
+UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent",
+ "-fsanitize=shift-exponent")
+UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "-fsanitize=bounds")
+UBSAN_CHECK(UnreachableCall, "unreachable-call", "-fsanitize=unreachable")
+UBSAN_CHECK(MissingReturn, "missing-return", "-fsanitize=return")
+UBSAN_CHECK(NonPositiveVLAIndex, "non-positive-vla-index",
+ "-fsanitize=vla-bound")
+UBSAN_CHECK(FloatCastOverflow, "float-cast-overflow",
+ "-fsanitize=float-cast-overflow")
+UBSAN_CHECK(InvalidBoolLoad, "invalid-bool-load", "-fsanitize=bool")
+UBSAN_CHECK(InvalidEnumLoad, "invalid-enum-load", "-fsanitize=enum")
+UBSAN_CHECK(FunctionTypeMismatch, "function-type-mismatch",
+ "-fsanitize=function")
+UBSAN_CHECK(InvalidNullReturn, "invalid-null-return",
+ "-fsanitize=returns-nonnull-attribute")
+UBSAN_CHECK(InvalidNullArgument, "invalid-null-argument",
+ "-fsanitize=nonnull-attribute")
+UBSAN_CHECK(DynamicTypeMismatch, "dynamic-type-mismatch", "-fsanitize=vptr")
+UBSAN_CHECK(CFIBadType, "cfi-bad-type", "-fsanitize=cfi")
//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_diag.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_libc.h"
+#include "ubsan_init.h"
+#include "ubsan_flags.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_stacktrace_printer.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include <stdio.h>
using namespace __ubsan;
-Location __ubsan::getCallerLocation(uptr CallerLoc) {
- if (!CallerLoc)
- return Location();
-
- uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc);
- return getFunctionLocation(Loc, 0);
+static void MaybePrintStackTrace(uptr pc, uptr bp) {
+ // We assume that flags are already parsed, as UBSan runtime
+ // will definitely be called when we print the first diagnostics message.
+ if (!flags()->print_stacktrace)
+ return;
+ // We can only use slow unwind, as we don't have any information about stack
+ // top/bottom.
+ // FIXME: It's better to respect "fast_unwind_on_fatal" runtime flag and
+ // fetch stack top/bottom information if we have it (e.g. if we're running
+ // under ASan).
+ if (StackTrace::WillUseFastUnwind(false))
+ return;
+ BufferedStackTrace stack;
+ stack.Unwind(kStackTraceMax, pc, bp, 0, 0, 0, false);
+ stack.Print();
}
-Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) {
- if (!Loc)
- return Location();
-
- AddressInfo Info;
- if (!Symbolizer::GetOrInit()->SymbolizeCode(Loc, &Info, 1) ||
- !Info.module || !*Info.module)
- return Location(Loc);
+static const char *ConvertTypeToString(ErrorType Type) {
+ switch (Type) {
+#define UBSAN_CHECK(Name, SummaryKind, FlagName) \
+ case ErrorType::Name: \
+ return SummaryKind;
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+ }
+ UNREACHABLE("unknown ErrorType!");
+}
- if (FName && Info.function)
- *FName = Info.function;
+static void MaybeReportErrorSummary(Location Loc, ErrorType Type) {
+ if (!common_flags()->print_summary)
+ return;
+ if (!flags()->report_error_type)
+ Type = ErrorType::GenericUB;
+ const char *ErrorKind = ConvertTypeToString(Type);
+ if (Loc.isSourceLocation()) {
+ SourceLocation SLoc = Loc.getSourceLocation();
+ if (!SLoc.isInvalid()) {
+ AddressInfo AI;
+ AI.file = internal_strdup(SLoc.getFilename());
+ AI.line = SLoc.getLine();
+ AI.column = SLoc.getColumn();
+ AI.function = internal_strdup(""); // Avoid printing ?? as function name.
+ ReportErrorSummary(ErrorKind, AI);
+ AI.Clear();
+ return;
+ }
+ } else if (Loc.isSymbolizedStack()) {
+ const AddressInfo &AI = Loc.getSymbolizedStack()->info;
+ ReportErrorSummary(ErrorKind, AI);
+ return;
+ }
+ ReportErrorSummary(ErrorKind);
+}
- if (!Info.file)
- return ModuleLocation(Info.module, Info.module_offset);
+namespace {
+class Decorator : public SanitizerCommonDecorator {
+ public:
+ Decorator() : SanitizerCommonDecorator() {}
+ const char *Highlight() const { return Green(); }
+ const char *EndHighlight() const { return Default(); }
+ const char *Note() const { return Black(); }
+ const char *EndNote() const { return Default(); }
+};
+}
- return SourceLocation(Info.file, Info.line, Info.column);
+SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) {
+ InitAsStandaloneIfNecessary();
+ return Symbolizer::GetOrInit()->SymbolizePC(PC);
}
Diag &Diag::operator<<(const TypeDescriptor &V) {
if (SLoc.isInvalid())
LocBuffer.append("<unknown>");
else
- PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
- SLoc.getColumn());
+ RenderSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
+ SLoc.getColumn(), common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
break;
}
- case Location::LK_Module:
- PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(),
- Loc.getModuleLocation().getOffset());
- break;
case Location::LK_Memory:
LocBuffer.append("%p", Loc.getMemoryLocation());
break;
+ case Location::LK_Symbolized: {
+ const AddressInfo &Info = Loc.getSymbolizedStack()->info;
+ if (Info.file) {
+ RenderSourceLocation(&LocBuffer, Info.file, Info.line, Info.column,
+ common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ } else if (Info.module) {
+ RenderModuleLocation(&LocBuffer, Info.module, Info.module_offset,
+ common_flags()->strip_path_prefix);
+ } else {
+ LocBuffer.append("%p", Info.address);
+ }
+ break;
+ }
case Location::LK_Null:
LocBuffer.append("<unknown>");
break;
case Diag::AK_String:
Printf("%s", A.String);
break;
- case Diag::AK_Mangled: {
- Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
+ case Diag::AK_TypeName: {
+ if (SANITIZER_WINDOWS)
+ // The Windows implementation demangles names early.
+ Printf("'%s'", A.String);
+ else
+ Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
break;
}
case Diag::AK_SInt:
// FIXME: Support floating-point formatting in sanitizer_common's
// printf, and stop using snprintf here.
char Buffer[32];
+#if SANITIZER_WINDOWS
+ sprintf_s(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
+#else
snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
+#endif
Printf("%s", Buffer);
break;
}
return Best;
}
+static inline uptr subtractNoOverflow(uptr LHS, uptr RHS) {
+ return (LHS < RHS) ? 0 : LHS - RHS;
+}
+
+static inline uptr addNoOverflow(uptr LHS, uptr RHS) {
+ const uptr Limit = (uptr)-1;
+ return (LHS > Limit - RHS) ? Limit : LHS + RHS;
+}
+
/// Render a snippet of the address space near a location.
-static void renderMemorySnippet(const __sanitizer::AnsiColorDecorator &Decor,
- MemoryLocation Loc,
+static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
Range *Ranges, unsigned NumRanges,
const Diag::Arg *Args) {
- const unsigned BytesToShow = 32;
- const unsigned MinBytesNearLoc = 4;
-
// Show at least the 8 bytes surrounding Loc.
- MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc;
+ const unsigned MinBytesNearLoc = 4;
+ MemoryLocation Min = subtractNoOverflow(Loc, MinBytesNearLoc);
+ MemoryLocation Max = addNoOverflow(Loc, MinBytesNearLoc);
+ MemoryLocation OrigMin = Min;
for (unsigned I = 0; I < NumRanges; ++I) {
Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
}
// If we have too many interesting bytes, prefer to show bytes after Loc.
+ const unsigned BytesToShow = 32;
if (Max - Min > BytesToShow)
- Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc);
- Max = Min + BytesToShow;
+ Min = __sanitizer::Min(Max - BytesToShow, OrigMin);
+ Max = addNoOverflow(Min, BytesToShow);
+
+ if (!IsAccessibleMemoryRange(Min, Max - Min)) {
+ Printf("<memory cannot be printed>\n");
+ return;
+ }
// Emit data.
for (uptr P = Min; P != Max; ++P) {
- // FIXME: Check that the address is readable before printing it.
unsigned char C = *reinterpret_cast<const unsigned char*>(P);
Printf("%s%02x", (P % 8 == 0) ? " " : " ", C);
}
Printf("\n");
// Emit highlights.
- Printf(Decor.Green());
+ Printf(Decor.Highlight());
Range *InRange = upperBound(Min, Ranges, NumRanges);
for (uptr P = Min; P != Max; ++P) {
char Pad = ' ', Byte = ' ';
char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 };
Printf((P % 8 == 0) ? Buffer : &Buffer[1]);
}
- Printf("%s\n", Decor.Default());
+ Printf("%s\n", Decor.EndHighlight());
// Go over the line again, and print names for the ranges.
InRange = 0;
}
Diag::~Diag() {
- __sanitizer::AnsiColorDecorator Decor(PrintsToTty());
- SpinMutexLock l(&CommonSanitizerReportMutex);
+ // All diagnostics should be printed under report mutex.
+ CommonSanitizerReportMutex.CheckLocked();
+ Decorator Decor;
Printf(Decor.Bold());
renderLocation(Loc);
switch (Level) {
case DL_Error:
Printf("%s runtime error: %s%s",
- Decor.Red(), Decor.Default(), Decor.Bold());
+ Decor.Warning(), Decor.EndWarning(), Decor.Bold());
break;
case DL_Note:
- Printf("%s note: %s", Decor.Black(), Decor.Default());
+ Printf("%s note: %s", Decor.Note(), Decor.EndNote());
break;
}
renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges,
NumRanges, Args);
}
+
+ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc,
+ ErrorType Type)
+ : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) {
+ InitAsStandaloneIfNecessary();
+ CommonSanitizerReportMutex.Lock();
+}
+
+ScopedReport::~ScopedReport() {
+ MaybePrintStackTrace(Opts.pc, Opts.bp);
+ MaybeReportErrorSummary(SummaryLoc, Type);
+ CommonSanitizerReportMutex.Unlock();
+ if (Opts.DieAfterReport || flags()->halt_on_error)
+ Die();
+}
+
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char kVptrCheck[] = "vptr_check";
+static const char *kSuppressionTypes[] = { kVptrCheck };
+
+void __ubsan::InitializeSuppressions() {
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
+}
+
+bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
+ InitAsStandaloneIfNecessary();
+ CHECK(suppression_ctx);
+ Suppression *s;
+ return suppression_ctx->Match(TypeName, kVptrCheck, &s);
+}
+
+#endif // CAN_SANITIZE_UB
#define UBSAN_DIAG_H
#include "ubsan_value.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
namespace __ubsan {
-/// \brief A location within a loaded module in the program. These are used when
-/// the location can't be resolved to a SourceLocation.
-class ModuleLocation {
- const char *ModuleName;
- uptr Offset;
+class SymbolizedStackHolder {
+ SymbolizedStack *Stack;
+
+ void clear() {
+ if (Stack)
+ Stack->ClearAll();
+ }
public:
- ModuleLocation() : ModuleName(0), Offset(0) {}
- ModuleLocation(const char *ModuleName, uptr Offset)
- : ModuleName(ModuleName), Offset(Offset) {}
- const char *getModuleName() const { return ModuleName; }
- uptr getOffset() const { return Offset; }
+ explicit SymbolizedStackHolder(SymbolizedStack *Stack = nullptr)
+ : Stack(Stack) {}
+ ~SymbolizedStackHolder() { clear(); }
+ void reset(SymbolizedStack *S) {
+ if (Stack != S)
+ clear();
+ Stack = S;
+ }
+ const SymbolizedStack *get() const { return Stack; }
};
+SymbolizedStack *getSymbolizedLocation(uptr PC);
+
+inline SymbolizedStack *getCallerLocation(uptr CallerPC) {
+ CHECK(CallerPC);
+ uptr PC = StackTrace::GetPreviousInstructionPc(CallerPC);
+ return getSymbolizedLocation(PC);
+}
+
/// A location of some data within the program's address space.
typedef uptr MemoryLocation;
/// \brief Location at which a diagnostic can be emitted. Either a
-/// SourceLocation, a ModuleLocation, or a MemoryLocation.
+/// SourceLocation, a MemoryLocation, or a SymbolizedStack.
class Location {
public:
- enum LocationKind { LK_Null, LK_Source, LK_Module, LK_Memory };
+ enum LocationKind { LK_Null, LK_Source, LK_Memory, LK_Symbolized };
private:
LocationKind Kind;
// FIXME: In C++11, wrap these in an anonymous union.
SourceLocation SourceLoc;
- ModuleLocation ModuleLoc;
MemoryLocation MemoryLoc;
+ const SymbolizedStack *SymbolizedLoc; // Not owned.
public:
Location() : Kind(LK_Null) {}
Location(SourceLocation Loc) :
Kind(LK_Source), SourceLoc(Loc) {}
- Location(ModuleLocation Loc) :
- Kind(LK_Module), ModuleLoc(Loc) {}
Location(MemoryLocation Loc) :
Kind(LK_Memory), MemoryLoc(Loc) {}
+ // SymbolizedStackHolder must outlive Location object.
+ Location(const SymbolizedStackHolder &Stack) :
+ Kind(LK_Symbolized), SymbolizedLoc(Stack.get()) {}
LocationKind getKind() const { return Kind; }
bool isSourceLocation() const { return Kind == LK_Source; }
- bool isModuleLocation() const { return Kind == LK_Module; }
bool isMemoryLocation() const { return Kind == LK_Memory; }
+ bool isSymbolizedStack() const { return Kind == LK_Symbolized; }
SourceLocation getSourceLocation() const {
CHECK(isSourceLocation());
return SourceLoc;
}
- ModuleLocation getModuleLocation() const {
- CHECK(isModuleLocation());
- return ModuleLoc;
- }
MemoryLocation getMemoryLocation() const {
CHECK(isMemoryLocation());
return MemoryLoc;
}
+ const SymbolizedStack *getSymbolizedStack() const {
+ CHECK(isSymbolizedStack());
+ return SymbolizedLoc;
+ }
};
-/// Try to obtain a location for the caller. This might fail, and produce either
-/// an invalid location or a module location for the caller.
-Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC());
-
-/// Try to obtain a location for the given function pointer. This might fail,
-/// and produce either an invalid location or a module location for the caller.
-/// If FName is non-null and the name of the function is known, set *FName to
-/// the function name, otherwise *FName is unchanged.
-Location getFunctionLocation(uptr Loc, const char **FName);
-
/// A diagnostic severity level.
enum DiagLevel {
DL_Error, ///< An error.
const char *getText() const { return Text; }
};
-/// \brief A mangled C++ name. Really just a strong typedef for 'const char*'.
-class MangledName {
+/// \brief A C++ type name. Really just a strong typedef for 'const char*'.
+class TypeName {
const char *Name;
public:
- MangledName(const char *Name) : Name(Name) {}
+ TypeName(const char *Name) : Name(Name) {}
const char *getName() const { return Name; }
};
/// Kinds of arguments, corresponding to members of \c Arg's union.
enum ArgKind {
AK_String, ///< A string argument, displayed as-is.
- AK_Mangled,///< A C++ mangled name, demangled before display.
+ AK_TypeName,///< A C++ type name, possibly demangled before display.
AK_UInt, ///< An unsigned integer argument.
AK_SInt, ///< A signed integer argument.
AK_Float, ///< A floating-point argument.
struct Arg {
Arg() {}
Arg(const char *String) : Kind(AK_String), String(String) {}
- Arg(MangledName MN) : Kind(AK_Mangled), String(MN.getName()) {}
+ Arg(TypeName TN) : Kind(AK_TypeName), String(TN.getName()) {}
Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {}
Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {}
Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {}
~Diag();
Diag &operator<<(const char *Str) { return AddArg(Str); }
- Diag &operator<<(MangledName MN) { return AddArg(MN); }
+ Diag &operator<<(TypeName TN) { return AddArg(TN); }
Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); }
Diag &operator<<(const void *V) { return AddArg(V); }
Diag &operator<<(const TypeDescriptor &V);
Diag &operator<<(const Range &R) { return AddRange(R); }
};
+struct ReportOptions {
+ /// If DieAfterReport is specified, UBSan will terminate the program after the
+ /// report is printed.
+ bool DieAfterReport;
+ /// pc/bp are used to unwind the stack trace.
+ uptr pc;
+ uptr bp;
+};
+
+enum class ErrorType {
+#define UBSAN_CHECK(Name, SummaryKind, FlagName) Name,
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+};
+
+#define GET_REPORT_OPTIONS(die_after_report) \
+ GET_CALLER_PC_BP; \
+ ReportOptions Opts = {die_after_report, pc, bp}
+
+/// \brief Instantiate this class before printing diagnostics in the error
+/// report. This class ensures that reports from different threads and from
+/// different sanitizers won't be mixed.
+class ScopedReport {
+ ReportOptions Opts;
+ Location SummaryLoc;
+ ErrorType Type;
+
+public:
+ ScopedReport(ReportOptions Opts, Location SummaryLoc,
+ ErrorType Type = ErrorType::GenericUB);
+ void setErrorType(ErrorType T) { Type = T; }
+ ~ScopedReport();
+};
+
+void InitializeSuppressions();
+bool IsVptrCheckSuppressed(const char *TypeName);
+
} // namespace __ubsan
#endif // UBSAN_DIAG_H
--- /dev/null
+//===-- ubsan_flags.cc ----------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Runtime flags for UndefinedBehaviorSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
+#include "ubsan_flags.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+
+namespace __ubsan {
+
+const char *MaybeCallUbsanDefaultOptions() {
+ return (&__ubsan_default_options) ? __ubsan_default_options() : "";
+}
+
+Flags ubsan_flags;
+
+void Flags::SetDefaults() {
+#define UBSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "ubsan_flags.inc"
+#undef UBSAN_FLAG
+}
+
+void RegisterUbsanFlags(FlagParser *parser, Flags *f) {
+#define UBSAN_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &f->Name);
+#include "ubsan_flags.inc"
+#undef UBSAN_FLAG
+}
+
+void InitializeFlags() {
+ SetCommonFlagsDefaults();
+ {
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.print_summary = false;
+ OverrideCommonFlags(cf);
+ }
+
+ Flags *f = flags();
+ f->SetDefaults();
+
+ FlagParser parser;
+ RegisterCommonFlags(&parser);
+ RegisterUbsanFlags(&parser, f);
+
+ // Override from user-specified string.
+ parser.ParseString(MaybeCallUbsanDefaultOptions());
+ // Override from predefined file
+ char *ubsanFlags;
+ uptr len = 0;
+ ReadFileToBuffer("/UBSAN_OPTIONS", &ubsanFlags, &len, GetPageSizeCached());
+ ParseFlagsFromString(f, ubsanFlags);
+ UnmapOrDie(ubsanFlags, len);
+ // Override from environment variable.
+ parser.ParseString(GetEnv("UBSAN_OPTIONS"));
+ SetVerbosity(common_flags()->verbosity);
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (common_flags()->help) parser.PrintFlagDescriptions();
+}
+
+} // namespace __ubsan
+
+extern "C" {
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char *__ubsan_default_options() { return ""; }
+#endif
+
+#if SANITIZER_WINDOWS
+const char *__ubsan_default_default_options() { return ""; }
+# ifdef _WIN64
+# pragma comment(linker, "/alternatename:__ubsan_default_options=__ubsan_default_default_options")
+# else
+# pragma comment(linker, "/alternatename:___ubsan_default_options=___ubsan_default_default_options")
+# endif
+#endif
+
+} // extern "C"
+
+#endif // CAN_SANITIZE_UB
--- /dev/null
+//===-- ubsan_flags.h -------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Runtime flags for UndefinedBehaviorSanitizer.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_FLAGS_H
+#define UBSAN_FLAGS_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+class FlagParser;
+}
+
+namespace __ubsan {
+
+struct Flags {
+#define UBSAN_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "ubsan_flags.inc"
+#undef UBSAN_FLAG
+
+ void SetDefaults();
+};
+
+extern Flags ubsan_flags;
+inline Flags *flags() { return &ubsan_flags; }
+
+void InitializeFlags();
+void RegisterUbsanFlags(FlagParser *parser, Flags *f);
+
+const char *MaybeCallUbsanDefaultOptions();
+
+} // namespace __ubsan
+
+extern "C" {
+// Users may provide their own implementation of __ubsan_default_options to
+// override the default flag values.
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char *__ubsan_default_options();
+} // extern "C"
+
+#endif // UBSAN_FLAGS_H
--- /dev/null
+//===-- ubsan_flags.inc -----------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// UBSan runtime flags.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_FLAG
+# error "Define UBSAN_FLAG prior to including this file!"
+#endif
+
+// UBSAN_FLAG(Type, Name, DefaultValue, Description)
+// See COMMON_FLAG in sanitizer_flags.inc for more details.
+
+UBSAN_FLAG(bool, halt_on_error, false,
+ "Crash the program after printing the first error report")
+UBSAN_FLAG(bool, print_stacktrace, false,
+ "Include full stacktrace into an error report")
+UBSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
+UBSAN_FLAG(bool, report_error_type, false,
+ "Print specific error type instead of 'undefined-behavior' in summary.")
//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_handlers.h"
#include "ubsan_diag.h"
using namespace __sanitizer;
using namespace __ubsan;
+static bool ignoreReport(SourceLocation SLoc, ReportOptions Opts) {
+ // If source location is already acquired, we don't need to print an error
+ // report for the second time. However, if we're in an unrecoverable handler,
+ // it's possible that location was required by concurrently running thread.
+ // In this case, we should continue the execution to ensure that any of
+ // threads will grab the report mutex and print the report before
+ // crashing the program.
+ return SLoc.isDisabled() && !Opts.DieAfterReport;
+}
+
namespace __ubsan {
- const char *TypeCheckKinds[] = {
+const char *TypeCheckKinds[] = {
"load of", "store to", "reference binding to", "member access within",
- "member call on", "constructor call on", "downcast of", "downcast of"
- };
+ "member call on", "constructor call on", "downcast of", "downcast of",
+ "upcast of", "cast to virtual base of"};
}
static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
- Location FallbackLoc) {
+ ReportOptions Opts) {
Location Loc = Data->Loc.acquire();
-
// Use the SourceLocation from Data to track deduplication, even if 'invalid'
- if (Loc.getSourceLocation().isDisabled())
+ if (ignoreReport(Loc.getSourceLocation(), Opts))
return;
- if (Data->Loc.isInvalid())
+
+ SymbolizedStackHolder FallbackLoc;
+ if (Data->Loc.isInvalid()) {
+ FallbackLoc.reset(getCallerLocation(Opts.pc));
Loc = FallbackLoc;
+ }
- if (!Pointer)
+ ScopedReport R(Opts, Loc);
+
+ if (!Pointer) {
+ R.setErrorType(ErrorType::NullPointerUse);
Diag(Loc, DL_Error, "%0 null pointer of type %1")
<< TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
- else if (Data->Alignment && (Pointer & (Data->Alignment - 1)))
+ } else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) {
+ R.setErrorType(ErrorType::MisalignedPointerUse);
Diag(Loc, DL_Error, "%0 misaligned address %1 for type %3, "
"which requires %2 byte alignment")
<< TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer
<< Data->Alignment << Data->Type;
- else
+ } else {
+ R.setErrorType(ErrorType::InsufficientObjectSize);
Diag(Loc, DL_Error, "%0 address %1 with insufficient space "
"for an object of type %2")
<< TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
+ }
if (Pointer)
Diag(Pointer, DL_Note, "pointer points here");
}
+
void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data,
ValueHandle Pointer) {
- handleTypeMismatchImpl(Data, Pointer, getCallerLocation());
+ GET_REPORT_OPTIONS(false);
+ handleTypeMismatchImpl(Data, Pointer, Opts);
}
void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData *Data,
ValueHandle Pointer) {
- handleTypeMismatchImpl(Data, Pointer, getCallerLocation());
+ GET_REPORT_OPTIONS(true);
+ handleTypeMismatchImpl(Data, Pointer, Opts);
Die();
}
/// \brief Common diagnostic emission for various forms of integer overflow.
-template<typename T> static void HandleIntegerOverflow(OverflowData *Data,
- ValueHandle LHS,
- const char *Operator,
- T RHS) {
+template <typename T>
+static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
+ const char *Operator, T RHS,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ bool IsSigned = Data->Type.isSignedIntegerTy();
+ ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow
+ : ErrorType::UnsignedIntegerOverflow);
+
Diag(Loc, DL_Error, "%0 integer overflow: "
"%1 %2 %3 cannot be represented in type %4")
- << (Data->Type.isSignedIntegerTy() ? "signed" : "unsigned")
+ << (IsSigned ? "signed" : "unsigned")
<< Value(Data->Type, LHS) << Operator << RHS << Data->Type;
}
-void __ubsan::__ubsan_handle_add_overflow(OverflowData *Data,
- ValueHandle LHS, ValueHandle RHS) {
- HandleIntegerOverflow(Data, LHS, "+", Value(Data->Type, RHS));
-}
-void __ubsan::__ubsan_handle_add_overflow_abort(OverflowData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
- __ubsan_handle_add_overflow(Data, LHS, RHS);
- Die();
-}
-
-void __ubsan::__ubsan_handle_sub_overflow(OverflowData *Data,
- ValueHandle LHS, ValueHandle RHS) {
- HandleIntegerOverflow(Data, LHS, "-", Value(Data->Type, RHS));
-}
-void __ubsan::__ubsan_handle_sub_overflow_abort(OverflowData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
- __ubsan_handle_sub_overflow(Data, LHS, RHS);
- Die();
-}
-
-void __ubsan::__ubsan_handle_mul_overflow(OverflowData *Data,
- ValueHandle LHS, ValueHandle RHS) {
- HandleIntegerOverflow(Data, LHS, "*", Value(Data->Type, RHS));
-}
-void __ubsan::__ubsan_handle_mul_overflow_abort(OverflowData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
- __ubsan_handle_mul_overflow(Data, LHS, RHS);
- Die();
-}
-
-void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data,
- ValueHandle OldVal) {
+#define UBSAN_OVERFLOW_HANDLER(handler_name, op, abort) \
+ void __ubsan::handler_name(OverflowData *Data, ValueHandle LHS, \
+ ValueHandle RHS) { \
+ GET_REPORT_OPTIONS(abort); \
+ handleIntegerOverflowImpl(Data, LHS, op, Value(Data->Type, RHS), Opts); \
+ if (abort) Die(); \
+ }
+
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow, "+", false)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow_abort, "+", true)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow, "-", false)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow_abort, "-", true)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow, "*", false)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort, "*", true)
+
+static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
- if (Data->Type.isSignedIntegerTy())
+ bool IsSigned = Data->Type.isSignedIntegerTy();
+ ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow
+ : ErrorType::UnsignedIntegerOverflow);
+
+ if (IsSigned)
Diag(Loc, DL_Error,
"negation of %0 cannot be represented in type %1; "
"cast to an unsigned type to negate this value to itself")
- << Value(Data->Type, OldVal) << Data->Type;
+ << Value(Data->Type, OldVal) << Data->Type;
else
- Diag(Loc, DL_Error,
- "negation of %0 cannot be represented in type %1")
- << Value(Data->Type, OldVal) << Data->Type;
+ Diag(Loc, DL_Error, "negation of %0 cannot be represented in type %1")
+ << Value(Data->Type, OldVal) << Data->Type;
+}
+
+void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data,
+ ValueHandle OldVal) {
+ GET_REPORT_OPTIONS(false);
+ handleNegateOverflowImpl(Data, OldVal, Opts);
}
void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data,
ValueHandle OldVal) {
- __ubsan_handle_negate_overflow(Data, OldVal);
+ GET_REPORT_OPTIONS(true);
+ handleNegateOverflowImpl(Data, OldVal, Opts);
Die();
}
-void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data,
- ValueHandle LHS, ValueHandle RHS) {
+static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
+ ValueHandle RHS, ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc);
+
Value LHSVal(Data->Type, LHS);
Value RHSVal(Data->Type, RHS);
- if (RHSVal.isMinusOne())
+ if (RHSVal.isMinusOne()) {
+ R.setErrorType(ErrorType::SignedIntegerOverflow);
Diag(Loc, DL_Error,
"division of %0 by -1 cannot be represented in type %1")
<< LHSVal << Data->Type;
- else
+ } else {
+ R.setErrorType(Data->Type.isIntegerTy() ? ErrorType::IntegerDivideByZero
+ : ErrorType::FloatDivideByZero);
Diag(Loc, DL_Error, "division by zero");
+ }
+}
+
+void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data,
+ ValueHandle LHS, ValueHandle RHS) {
+ GET_REPORT_OPTIONS(false);
+ handleDivremOverflowImpl(Data, LHS, RHS, Opts);
}
void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData *Data,
ValueHandle LHS,
ValueHandle RHS) {
- __ubsan_handle_divrem_overflow(Data, LHS, RHS);
+ GET_REPORT_OPTIONS(true);
+ handleDivremOverflowImpl(Data, LHS, RHS, Opts);
Die();
}
-void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
+static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
+ ValueHandle LHS, ValueHandle RHS,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc);
+
Value LHSVal(Data->LHSType, LHS);
Value RHSVal(Data->RHSType, RHS);
- if (RHSVal.isNegative())
+ if (RHSVal.isNegative()) {
+ R.setErrorType(ErrorType::InvalidShiftExponent);
Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal;
- else if (RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth())
- Diag(Loc, DL_Error,
- "shift exponent %0 is too large for %1-bit type %2")
- << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
- else if (LHSVal.isNegative())
+ } else if (RHSVal.getPositiveIntValue() >=
+ Data->LHSType.getIntegerBitWidth()) {
+ R.setErrorType(ErrorType::InvalidShiftExponent);
+ Diag(Loc, DL_Error, "shift exponent %0 is too large for %1-bit type %2")
+ << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
+ } else if (LHSVal.isNegative()) {
+ R.setErrorType(ErrorType::InvalidShiftBase);
Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal;
- else
+ } else {
+ R.setErrorType(ErrorType::InvalidShiftBase);
Diag(Loc, DL_Error,
"left shift of %0 by %1 places cannot be represented in type %2")
- << LHSVal << RHSVal << Data->LHSType;
+ << LHSVal << RHSVal << Data->LHSType;
+ }
+}
+
+void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ GET_REPORT_OPTIONS(false);
+ handleShiftOutOfBoundsImpl(Data, LHS, RHS, Opts);
}
void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
ShiftOutOfBoundsData *Data,
ValueHandle LHS,
ValueHandle RHS) {
- __ubsan_handle_shift_out_of_bounds(Data, LHS, RHS);
+ GET_REPORT_OPTIONS(true);
+ handleShiftOutOfBoundsImpl(Data, LHS, RHS, Opts);
Die();
}
-void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData *Data,
- ValueHandle Index) {
+static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc, ErrorType::OutOfBoundsIndex);
+
Value IndexVal(Data->IndexType, Index);
Diag(Loc, DL_Error, "index %0 out of bounds for type %1")
<< IndexVal << Data->ArrayType;
}
+
+void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData *Data,
+ ValueHandle Index) {
+ GET_REPORT_OPTIONS(false);
+ handleOutOfBoundsImpl(Data, Index, Opts);
+}
void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data,
ValueHandle Index) {
- __ubsan_handle_out_of_bounds(Data, Index);
+ GET_REPORT_OPTIONS(true);
+ handleOutOfBoundsImpl(Data, Index, Opts);
Die();
}
-void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) {
+static void handleBuiltinUnreachableImpl(UnreachableData *Data,
+ ReportOptions Opts) {
+ ScopedReport R(Opts, Data->Loc, ErrorType::UnreachableCall);
Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call");
+}
+
+void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleBuiltinUnreachableImpl(Data, Opts);
Die();
}
-void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
+static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) {
+ ScopedReport R(Opts, Data->Loc, ErrorType::MissingReturn);
Diag(Data->Loc, DL_Error,
"execution reached the end of a value-returning function "
"without returning a value");
+}
+
+void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleMissingReturnImpl(Data, Opts);
Die();
}
-void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data,
- ValueHandle Bound) {
+static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ ScopedReport R(Opts, Loc, ErrorType::NonPositiveVLAIndex);
+
Diag(Loc, DL_Error, "variable length array bound evaluates to "
"non-positive value %0")
<< Value(Data->Type, Bound);
}
+
+void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data,
+ ValueHandle Bound) {
+ GET_REPORT_OPTIONS(false);
+ handleVLABoundNotPositive(Data, Bound, Opts);
+}
void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data,
- ValueHandle Bound) {
- __ubsan_handle_vla_bound_not_positive(Data, Bound);
+ ValueHandle Bound) {
+ GET_REPORT_OPTIONS(true);
+ handleVLABoundNotPositive(Data, Bound, Opts);
Die();
}
+static bool looksLikeFloatCastOverflowDataV1(void *Data) {
+ // First field is either a pointer to filename or a pointer to a
+ // TypeDescriptor.
+ u8 *FilenameOrTypeDescriptor;
+ internal_memcpy(&FilenameOrTypeDescriptor, Data,
+ sizeof(FilenameOrTypeDescriptor));
+
+ // Heuristic: For float_cast_overflow, the TypeKind will be either TK_Integer
+ // (0x0), TK_Float (0x1) or TK_Unknown (0xff). If both types are known,
+ // adding both bytes will be 0 or 1 (for BE or LE). If it were a filename,
+ // adding two printable characters will not yield such a value. Otherwise,
+ // if one of them is 0xff, this is most likely TK_Unknown type descriptor.
+ u16 MaybeFromTypeKind =
+ FilenameOrTypeDescriptor[0] + FilenameOrTypeDescriptor[1];
+ return MaybeFromTypeKind < 2 || FilenameOrTypeDescriptor[0] == 0xff ||
+ FilenameOrTypeDescriptor[1] == 0xff;
+}
+
+static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
+ ReportOptions Opts) {
+ SymbolizedStackHolder CallerLoc;
+ Location Loc;
+ const TypeDescriptor *FromType, *ToType;
+
+ if (looksLikeFloatCastOverflowDataV1(DataPtr)) {
+ auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr);
+ CallerLoc.reset(getCallerLocation(Opts.pc));
+ Loc = CallerLoc;
+ FromType = &Data->FromType;
+ ToType = &Data->ToType;
+ } else {
+ auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr);
+ SourceLocation SLoc = Data->Loc.acquire();
+ if (ignoreReport(SLoc, Opts))
+ return;
+ Loc = SLoc;
+ FromType = &Data->FromType;
+ ToType = &Data->ToType;
+ }
+
+ ScopedReport R(Opts, Loc, ErrorType::FloatCastOverflow);
-void __ubsan::__ubsan_handle_float_cast_overflow(FloatCastOverflowData *Data,
- ValueHandle From) {
- // TODO: Add deduplication once a SourceLocation is generated for this check.
- Diag(getCallerLocation(), DL_Error,
+ Diag(Loc, DL_Error,
"value %0 is outside the range of representable values of type %2")
- << Value(Data->FromType, From) << Data->FromType << Data->ToType;
+ << Value(*FromType, From) << *FromType << *ToType;
}
-void __ubsan::__ubsan_handle_float_cast_overflow_abort(
- FloatCastOverflowData *Data,
- ValueHandle From) {
- Diag(getCallerLocation(), DL_Error,
- "value %0 is outside the range of representable values of type %2")
- << Value(Data->FromType, From) << Data->FromType << Data->ToType;
+
+void __ubsan::__ubsan_handle_float_cast_overflow(void *Data, ValueHandle From) {
+ GET_REPORT_OPTIONS(false);
+ handleFloatCastOverflow(Data, From, Opts);
+}
+void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data,
+ ValueHandle From) {
+ GET_REPORT_OPTIONS(true);
+ handleFloatCastOverflow(Data, From, Opts);
Die();
}
-void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data,
- ValueHandle Val) {
+static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val,
+ ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
+ if (ignoreReport(Loc, Opts))
return;
+ // This check could be more precise if we used different handlers for
+ // -fsanitize=bool and -fsanitize=enum.
+ bool IsBool = (0 == internal_strcmp(Data->Type.getTypeName(), "'bool'"));
+ ScopedReport R(Opts, Loc, IsBool ? ErrorType::InvalidBoolLoad
+ : ErrorType::InvalidEnumLoad);
+
Diag(Loc, DL_Error,
"load of value %0, which is not a valid value for type %1")
<< Value(Data->Type, Val) << Data->Type;
}
+
+void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data,
+ ValueHandle Val) {
+ GET_REPORT_OPTIONS(false);
+ handleLoadInvalidValue(Data, Val, Opts);
+}
void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data,
ValueHandle Val) {
- __ubsan_handle_load_invalid_value(Data, Val);
+ GET_REPORT_OPTIONS(true);
+ handleLoadInvalidValue(Data, Val, Opts);
Die();
}
-void __ubsan::__ubsan_handle_function_type_mismatch(
- FunctionTypeMismatchData *Data,
- ValueHandle Function) {
- const char *FName = "(unknown)";
+static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
+ ValueHandle Function,
+ ReportOptions Opts) {
+ SourceLocation CallLoc = Data->Loc.acquire();
+ if (ignoreReport(CallLoc, Opts))
+ return;
- Location Loc = getFunctionLocation(Function, &FName);
+ ScopedReport R(Opts, CallLoc, ErrorType::FunctionTypeMismatch);
- Diag(Data->Loc, DL_Error,
+ SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
+ const char *FName = FLoc.get()->info.function;
+ if (!FName)
+ FName = "(unknown)";
+
+ Diag(CallLoc, DL_Error,
"call to function %0 through pointer to incorrect function type %1")
- << FName << Data->Type;
- Diag(Loc, DL_Note, "%0 defined here") << FName;
+ << FName << Data->Type;
+ Diag(FLoc, DL_Note, "%0 defined here") << FName;
+}
+
+void
+__ubsan::__ubsan_handle_function_type_mismatch(FunctionTypeMismatchData *Data,
+ ValueHandle Function) {
+ GET_REPORT_OPTIONS(false);
+ handleFunctionTypeMismatch(Data, Function, Opts);
}
void __ubsan::__ubsan_handle_function_type_mismatch_abort(
- FunctionTypeMismatchData *Data,
- ValueHandle Function) {
- __ubsan_handle_function_type_mismatch(Data, Function);
+ FunctionTypeMismatchData *Data, ValueHandle Function) {
+ GET_REPORT_OPTIONS(true);
+ handleFunctionTypeMismatch(Data, Function, Opts);
+ Die();
+}
+
+static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (ignoreReport(Loc, Opts))
+ return;
+
+ ScopedReport R(Opts, Loc, ErrorType::InvalidNullReturn);
+
+ Diag(Loc, DL_Error, "null pointer returned from function declared to never "
+ "return null");
+ if (!Data->AttrLoc.isInvalid())
+ Diag(Data->AttrLoc, DL_Note, "returns_nonnull attribute specified here");
+}
+
+void __ubsan::__ubsan_handle_nonnull_return(NonNullReturnData *Data) {
+ GET_REPORT_OPTIONS(false);
+ handleNonNullReturn(Data, Opts);
+}
+
+void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleNonNullReturn(Data, Opts);
+ Die();
+}
+
+static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (ignoreReport(Loc, Opts))
+ return;
+
+ ScopedReport R(Opts, Loc, ErrorType::InvalidNullArgument);
+
+ Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to "
+ "never be null") << Data->ArgIndex;
+ if (!Data->AttrLoc.isInvalid())
+ Diag(Data->AttrLoc, DL_Note, "nonnull attribute specified here");
+}
+
+void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data) {
+ GET_REPORT_OPTIONS(false);
+ handleNonNullArg(Data, Opts);
+}
+
+void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleNonNullArg(Data, Opts);
+ Die();
+}
+
+static void handleCFIBadIcall(CFIBadIcallData *Data, ValueHandle Function,
+ ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (ignoreReport(Loc, Opts))
+ return;
+
+ ScopedReport R(Opts, Loc);
+
+ Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
+ "indirect function call")
+ << Data->Type;
+
+ SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
+ const char *FName = FLoc.get()->info.function;
+ if (!FName)
+ FName = "(unknown)";
+ Diag(FLoc, DL_Note, "%0 defined here") << FName;
+}
+
+void __ubsan::__ubsan_handle_cfi_bad_icall(CFIBadIcallData *Data,
+ ValueHandle Function) {
+ GET_REPORT_OPTIONS(false);
+ handleCFIBadIcall(Data, Function, Opts);
+}
+
+void __ubsan::__ubsan_handle_cfi_bad_icall_abort(CFIBadIcallData *Data,
+ ValueHandle Function) {
+ GET_REPORT_OPTIONS(true);
+ handleCFIBadIcall(Data, Function, Opts);
Die();
}
+
+#endif // CAN_SANITIZE_UB
unsigned char TypeCheckKind;
};
+#define UNRECOVERABLE(checkname, ...) \
+ extern "C" SANITIZER_INTERFACE_ATTRIBUTE NORETURN \
+ void __ubsan_handle_ ## checkname( __VA_ARGS__ );
+
#define RECOVERABLE(checkname, ...) \
extern "C" SANITIZER_INTERFACE_ATTRIBUTE \
void __ubsan_handle_ ## checkname( __VA_ARGS__ ); \
- extern "C" SANITIZER_INTERFACE_ATTRIBUTE \
+ extern "C" SANITIZER_INTERFACE_ATTRIBUTE NORETURN \
void __ubsan_handle_ ## checkname ## _abort( __VA_ARGS__ );
/// \brief Handle a runtime type check failure, caused by either a misaligned
};
/// \brief Handle a __builtin_unreachable which is reached.
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __ubsan_handle_builtin_unreachable(UnreachableData *Data);
+UNRECOVERABLE(builtin_unreachable, UnreachableData *Data)
/// \brief Handle reaching the end of a value-returning function.
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __ubsan_handle_missing_return(UnreachableData *Data);
+UNRECOVERABLE(missing_return, UnreachableData *Data)
struct VLABoundData {
SourceLocation Loc;
/// \brief Handle a VLA with a non-positive bound.
RECOVERABLE(vla_bound_not_positive, VLABoundData *Data, ValueHandle Bound)
+// Keeping this around for binary compatibility with (sanitized) programs
+// compiled with older compilers.
struct FloatCastOverflowData {
- // FIXME: SourceLocation Loc;
const TypeDescriptor &FromType;
const TypeDescriptor &ToType;
};
-/// \brief Handle overflow in a conversion to or from a floating-point type.
-RECOVERABLE(float_cast_overflow, FloatCastOverflowData *Data, ValueHandle From)
+struct FloatCastOverflowDataV2 {
+ SourceLocation Loc;
+ const TypeDescriptor &FromType;
+ const TypeDescriptor &ToType;
+};
+
+/// Handle overflow in a conversion to or from a floating-point type.
+/// void *Data is one of FloatCastOverflowData* or FloatCastOverflowDataV2*
+RECOVERABLE(float_cast_overflow, void *Data, ValueHandle From)
struct InvalidValueData {
SourceLocation Loc;
FunctionTypeMismatchData *Data,
ValueHandle Val)
+struct NonNullReturnData {
+ SourceLocation Loc;
+ SourceLocation AttrLoc;
+};
+
+/// \brief Handle returning null from function with returns_nonnull attribute.
+RECOVERABLE(nonnull_return, NonNullReturnData *Data)
+
+struct NonNullArgData {
+ SourceLocation Loc;
+ SourceLocation AttrLoc;
+ int ArgIndex;
+};
+
+/// \brief Handle passing null pointer to function with nonnull attribute.
+RECOVERABLE(nonnull_arg, NonNullArgData *Data)
+
+struct CFIBadIcallData {
+ SourceLocation Loc;
+ const TypeDescriptor &Type;
+};
+
+/// \brief Handle control flow integrity failure for indirect function calls.
+RECOVERABLE(cfi_bad_icall, CFIBadIcallData *Data, ValueHandle Function)
+
}
#endif // UBSAN_HANDLERS_H
//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_handlers_cxx.h"
#include "ubsan_diag.h"
#include "ubsan_type_hash.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
using namespace __sanitizer;
using namespace __ubsan;
static void HandleDynamicTypeCacheMiss(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash,
- bool Abort) {
+ ReportOptions Opts) {
if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash))
// Just a cache miss. The type matches after all.
return;
+ // Check if error report should be suppressed.
+ DynamicTypeInfo DTI = getDynamicTypeInfoFromObject((void*)Pointer);
+ if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName()))
+ return;
+
SourceLocation Loc = Data->Loc.acquire();
if (Loc.isDisabled())
return;
+ ScopedReport R(Opts, Loc, ErrorType::DynamicTypeMismatch);
+
Diag(Loc, DL_Error,
"%0 address %1 which does not point to an object of type %2")
<< TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
// If possible, say what type it actually points to.
- DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer);
if (!DTI.isValid())
Diag(Pointer, DL_Note, "object has invalid vptr")
- << MangledName(DTI.getMostDerivedTypeName())
- << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
+ << TypeName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
else if (!DTI.getOffset())
Diag(Pointer, DL_Note, "object is of type %0")
- << MangledName(DTI.getMostDerivedTypeName())
- << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
+ << TypeName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
else
// FIXME: Find the type at the specified offset, and include that
// in the note.
Diag(Pointer - DTI.getOffset(), DL_Note,
"object is base class subobject at offset %0 within object of type %1")
- << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName())
- << MangledName(DTI.getSubobjectTypeName())
- << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1");
-
- if (Abort)
- Die();
+ << DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName())
+ << TypeName(DTI.getSubobjectTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr),
+ "vptr for %2 base class of %1");
}
void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
- HandleDynamicTypeCacheMiss(Data, Pointer, Hash, false);
+ GET_REPORT_OPTIONS(false);
+ HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts);
}
void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
- HandleDynamicTypeCacheMiss(Data, Pointer, Hash, true);
+ GET_REPORT_OPTIONS(true);
+ HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts);
+}
+
+static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable,
+ ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ ScopedReport R(Opts, Loc, ErrorType::CFIBadType);
+ DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void*)Vtable);
+
+ static const char *TypeCheckKinds[] = {
+ "virtual call",
+ "non-virtual call",
+ "base-to-derived cast",
+ "cast to unrelated type",
+ };
+
+ Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
+ "%1 (vtable address %2)")
+ << Data->Type << TypeCheckKinds[Data->TypeCheckKind] << (void *)Vtable;
+
+ // If possible, say what type it actually points to.
+ if (!DTI.isValid())
+ Diag(Vtable, DL_Note, "invalid vtable");
+ else
+ Diag(Vtable, DL_Note, "vtable is of type %0")
+ << TypeName(DTI.getMostDerivedTypeName());
}
+
+void __ubsan::__ubsan_handle_cfi_bad_type(CFIBadTypeData *Data,
+ ValueHandle Vtable) {
+ GET_REPORT_OPTIONS(false);
+ HandleCFIBadType(Data, Vtable, Opts);
+}
+
+void __ubsan::__ubsan_handle_cfi_bad_type_abort(CFIBadTypeData *Data,
+ ValueHandle Vtable) {
+ GET_REPORT_OPTIONS(true);
+ HandleCFIBadType(Data, Vtable, Opts);
+}
+
+#endif // CAN_SANITIZE_UB
unsigned char TypeCheckKind;
};
+struct CFIBadTypeData {
+ SourceLocation Loc;
+ const TypeDescriptor &Type;
+ unsigned char TypeCheckKind;
+};
+
/// \brief Handle a runtime type check failure, caused by an incorrect vptr.
/// When this handler is called, all we know is that the type was not in the
/// cache; this does not necessarily imply the existence of a bug.
void __ubsan_handle_dynamic_type_cache_miss_abort(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash);
+/// \brief Handle a control flow integrity check failure by printing a
+/// diagnostic.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
+__ubsan_handle_cfi_bad_type(CFIBadTypeData *Data, ValueHandle Vtable);
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
+__ubsan_handle_cfi_bad_type_abort(CFIBadTypeData *Data, ValueHandle Vtable);
+
}
#endif // UBSAN_HANDLERS_H
--- /dev/null
+//===-- ubsan_init.cc -----------------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Initialization of UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
+#include "ubsan_diag.h"
+#include "ubsan_init.h"
+#include "ubsan_flags.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+using namespace __ubsan;
+
+static enum {
+ UBSAN_MODE_UNKNOWN = 0,
+ UBSAN_MODE_STANDALONE,
+ UBSAN_MODE_PLUGIN
+} ubsan_mode;
+static StaticSpinMutex ubsan_init_mu;
+
+static void CommonInit() {
+ InitializeSuppressions();
+}
+
+static void CommonStandaloneInit() {
+ SanitizerToolName = "UndefinedBehaviorSanitizer";
+ InitializeFlags();
+ CacheBinaryName();
+ __sanitizer_set_report_path(common_flags()->log_path);
+ InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
+ CommonInit();
+ ubsan_mode = UBSAN_MODE_STANDALONE;
+}
+
+void __ubsan::InitAsStandalone() {
+ if (SANITIZER_CAN_USE_PREINIT_ARRAY) {
+ CHECK_EQ(UBSAN_MODE_UNKNOWN, ubsan_mode);
+ CommonStandaloneInit();
+ return;
+ }
+ SpinMutexLock l(&ubsan_init_mu);
+ CHECK_NE(UBSAN_MODE_PLUGIN, ubsan_mode);
+ if (ubsan_mode == UBSAN_MODE_UNKNOWN)
+ CommonStandaloneInit();
+}
+
+void __ubsan::InitAsStandaloneIfNecessary() {
+ if (SANITIZER_CAN_USE_PREINIT_ARRAY) {
+ CHECK_NE(UBSAN_MODE_UNKNOWN, ubsan_mode);
+ return;
+ }
+ SpinMutexLock l(&ubsan_init_mu);
+ if (ubsan_mode == UBSAN_MODE_UNKNOWN)
+ CommonStandaloneInit();
+}
+
+void __ubsan::InitAsPlugin() {
+#if !SANITIZER_CAN_USE_PREINIT_ARRAY
+ SpinMutexLock l(&ubsan_init_mu);
+#endif
+ CHECK_EQ(UBSAN_MODE_UNKNOWN, ubsan_mode);
+ CommonInit();
+ ubsan_mode = UBSAN_MODE_PLUGIN;
+}
+
+#endif // CAN_SANITIZE_UB
--- /dev/null
+//===-- ubsan_init.h --------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Initialization function for UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_INIT_H
+#define UBSAN_INIT_H
+
+namespace __ubsan {
+
+// Initialize UBSan as a standalone tool. Typically should be called early
+// during initialization.
+void InitAsStandalone();
+
+// Initialize UBSan as a standalone tool, if it hasn't been initialized before.
+void InitAsStandaloneIfNecessary();
+
+// Initializes UBSan as a plugin tool. This function should be called once
+// from "parent tool" (e.g. ASan) initialization.
+void InitAsPlugin();
+
+} // namespace __ubsan
+
+#endif // UBSAN_INIT_H
--- /dev/null
+//===-- ubsan_init_standalone.cc ------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Initialization of standalone UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if !CAN_SANITIZE_UB
+# error "UBSan is not supported on this platform!"
+#endif
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "ubsan_init.h"
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+__attribute__((section(".preinit_array"), used))
+void (*__local_ubsan_preinit)(void) = __ubsan::InitAsStandalone;
+#else
+// Use a dynamic initializer.
+class UbsanStandaloneInitializer {
+ public:
+ UbsanStandaloneInitializer() {
+ __ubsan::InitAsStandalone();
+ }
+};
+static UbsanStandaloneInitializer ubsan_standalone_initializer;
+#endif // SANITIZER_CAN_USE_PREINIT_ARRAY
--- /dev/null
+//===-- ubsan_platform.h ----------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the platforms which UBSan is supported at.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_PLATFORM_H
+#define UBSAN_PLATFORM_H
+
+#ifndef CAN_SANITIZE_UB
+// Other platforms should be easy to add, and probably work as-is.
+#if (defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)) && \
+ (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || \
+ defined(__aarch64__) || defined(__mips__) || defined(__powerpc64__))
+# define CAN_SANITIZE_UB 1
+#elif defined(_WIN32)
+# define CAN_SANITIZE_UB 1
+#else
+# define CAN_SANITIZE_UB 0
+#endif
+#endif //CAN_SANITIZE_UB
+
+#endif
// relationships. This file is only linked into C++ compilations, and is
// permitted to use language features which require a C++ ABI library.
//
+// Most of the implementation lives in an ABI-specific source file
+// (ubsan_type_hash_{itanium,win}.cc).
+//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_type_hash.h"
#include "sanitizer_common/sanitizer_common.h"
-// The following are intended to be binary compatible with the definitions
-// given in the Itanium ABI. We make no attempt to be ODR-compatible with
-// those definitions, since existing ABI implementations aren't.
-
-namespace std {
- class type_info {
- public:
- virtual ~type_info();
-
- const char *__type_name;
- };
-}
-
-namespace __cxxabiv1 {
-
-/// Type info for classes with no bases, and base class for type info for
-/// classes with bases.
-class __class_type_info : public std::type_info {
- virtual ~__class_type_info();
-};
-
-/// Type info for classes with simple single public inheritance.
-class __si_class_type_info : public __class_type_info {
-public:
- virtual ~__si_class_type_info();
-
- const __class_type_info *__base_type;
-};
-
-class __base_class_type_info {
-public:
- const __class_type_info *__base_type;
- long __offset_flags;
-
- enum __offset_flags_masks {
- __virtual_mask = 0x1,
- __public_mask = 0x2,
- __offset_shift = 8
- };
-};
-
-/// Type info for classes with multiple, virtual, or non-public inheritance.
-class __vmi_class_type_info : public __class_type_info {
-public:
- virtual ~__vmi_class_type_info();
-
- unsigned int flags;
- unsigned int base_count;
- __base_class_type_info base_info[1];
-};
-
-}
-
-namespace abi = __cxxabiv1;
-
-// We implement a simple two-level cache for type-checking results. For each
-// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
-// unique; if it collides, we will get false negatives, but:
-// * such a collision would have to occur on the *first* bad access,
-// * the probability of such a collision is low (and for a 64-bit target, is
-// negligible), and
-// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
-// give better coverage.
-//
-// The first caching layer is a small hash table with no chaining; buckets are
-// reused as needed. The second caching layer is a large hash table with open
-// chaining. We can freely evict from either layer since this is just a cache.
-//
-// FIXME: Make these hash table accesses thread-safe. The races here are benign:
-// assuming the unsequenced loads and stores don't misbehave too badly,
-// the worst case is false negatives or poor cache behavior, not false
-// positives or crashes.
-
-/// Find a bucket to store the given hash value in.
-static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
- static const unsigned HashTableSize = 65537;
- static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize];
-
- unsigned First = (V & 65535) ^ 1;
- unsigned Probe = First;
- for (int Tries = 5; Tries; --Tries) {
- if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
- return &__ubsan_vptr_hash_set[Probe];
- Probe += ((V >> 16) & 65535) + 1;
- if (Probe >= HashTableSize)
- Probe -= HashTableSize;
- }
- // FIXME: Pick a random entry from the probe sequence to evict rather than
- // just taking the first.
- return &__ubsan_vptr_hash_set[First];
-}
-
/// A cache of recently-checked hashes. Mini hash table with "random" evictions.
__ubsan::HashValue
__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize];
-/// \brief Determine whether \p Derived has a \p Base base class subobject at
-/// offset \p Offset.
-static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
- const abi::__class_type_info *Base,
- sptr Offset) {
- if (Derived->__type_name == Base->__type_name)
- return Offset == 0;
-
- if (const abi::__si_class_type_info *SI =
- dynamic_cast<const abi::__si_class_type_info*>(Derived))
- return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
-
- const abi::__vmi_class_type_info *VTI =
- dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
- if (!VTI)
- // No base class subobjects.
- return false;
-
- // Look for a base class which is derived from \p Base at the right offset.
- for (unsigned int base = 0; base != VTI->base_count; ++base) {
- // FIXME: Curtail the recursion if this base can't possibly contain the
- // given offset.
- sptr OffsetHere = VTI->base_info[base].__offset_flags >>
- abi::__base_class_type_info::__offset_shift;
- if (VTI->base_info[base].__offset_flags &
- abi::__base_class_type_info::__virtual_mask)
- // For now, just punt on virtual bases and say 'yes'.
- // FIXME: OffsetHere is the offset in the vtable of the virtual base
- // offset. Read the vbase offset out of the vtable and use it.
- return true;
- if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
- Base, Offset - OffsetHere))
- return true;
- }
-
- return false;
-}
-
-/// \brief Find the derived-most dynamic base class of \p Derived at offset
-/// \p Offset.
-static const abi::__class_type_info *findBaseAtOffset(
- const abi::__class_type_info *Derived, sptr Offset) {
- if (!Offset)
- return Derived;
-
- if (const abi::__si_class_type_info *SI =
- dynamic_cast<const abi::__si_class_type_info*>(Derived))
- return findBaseAtOffset(SI->__base_type, Offset);
-
- const abi::__vmi_class_type_info *VTI =
- dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
- if (!VTI)
- // No base class subobjects.
- return 0;
-
- for (unsigned int base = 0; base != VTI->base_count; ++base) {
- sptr OffsetHere = VTI->base_info[base].__offset_flags >>
- abi::__base_class_type_info::__offset_shift;
- if (VTI->base_info[base].__offset_flags &
- abi::__base_class_type_info::__virtual_mask)
- // FIXME: Can't handle virtual bases yet.
- continue;
- if (const abi::__class_type_info *Base =
- findBaseAtOffset(VTI->base_info[base].__base_type,
- Offset - OffsetHere))
- return Base;
- }
-
- return 0;
-}
-
-namespace {
-
-struct VtablePrefix {
- /// The offset from the vptr to the start of the most-derived object.
- /// This should never be greater than zero, and will usually be exactly
- /// zero.
- sptr Offset;
- /// The type_info object describing the most-derived class type.
- std::type_info *TypeInfo;
-};
-VtablePrefix *getVtablePrefix(void *Object) {
- VtablePrefix **VptrPtr = reinterpret_cast<VtablePrefix**>(Object);
- if (!*VptrPtr)
- return 0;
- VtablePrefix *Prefix = *VptrPtr - 1;
- if (Prefix->Offset > 0 || !Prefix->TypeInfo)
- // This can't possibly be a valid vtable.
- return 0;
- return Prefix;
-}
-
+__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfoFromObject(void *Object) {
+ void *VtablePtr = *reinterpret_cast<void **>(Object);
+ return getDynamicTypeInfoFromVtable(VtablePtr);
}
-bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
- // A crash anywhere within this function probably means the vptr is corrupted.
- // FIXME: Perform these checks more cautiously.
-
- // Check whether this is something we've evicted from the cache.
- HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
- if (*Bucket == Hash) {
- __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
- return true;
- }
-
- VtablePrefix *Vtable = getVtablePrefix(Object);
- if (!Vtable)
- return false;
-
- // Check that this is actually a type_info object for a class type.
- abi::__class_type_info *Derived =
- dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
- if (!Derived)
- return false;
-
- abi::__class_type_info *Base = (abi::__class_type_info*)Type;
- if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
- return false;
-
- // Success. Cache this result.
- __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
- *Bucket = Hash;
- return true;
-}
-
-__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) {
- VtablePrefix *Vtable = getVtablePrefix(Object);
- if (!Vtable)
- return DynamicTypeInfo(0, 0, 0);
- const abi::__class_type_info *ObjectType = findBaseAtOffset(
- static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
- -Vtable->Offset);
- return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
- ObjectType ? ObjectType->__type_name : "<unknown>");
-}
+#endif // CAN_SANITIZE_UB
};
/// \brief Get information about the dynamic type of an object.
-DynamicTypeInfo getDynamicTypeInfo(void *Object);
+DynamicTypeInfo getDynamicTypeInfoFromObject(void *Object);
+
+/// \brief Get information about the dynamic type of an object from its vtable.
+DynamicTypeInfo getDynamicTypeInfoFromVtable(void *Vtable);
/// \brief Check whether the dynamic type of \p Object has a \p Type subobject
/// at offset 0.
--- /dev/null
+//===-- ubsan_type_hash_itanium.cc ----------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of type hashing/lookup for Itanium C++ ABI.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB && !SANITIZER_WINDOWS
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+// The following are intended to be binary compatible with the definitions
+// given in the Itanium ABI. We make no attempt to be ODR-compatible with
+// those definitions, since existing ABI implementations aren't.
+
+namespace std {
+ class type_info {
+ public:
+ virtual ~type_info();
+
+ const char *__type_name;
+ };
+}
+
+namespace __cxxabiv1 {
+
+/// Type info for classes with no bases, and base class for type info for
+/// classes with bases.
+class __class_type_info : public std::type_info {
+ ~__class_type_info() override;
+};
+
+/// Type info for classes with simple single public inheritance.
+class __si_class_type_info : public __class_type_info {
+public:
+ ~__si_class_type_info() override;
+
+ const __class_type_info *__base_type;
+};
+
+class __base_class_type_info {
+public:
+ const __class_type_info *__base_type;
+ long __offset_flags;
+
+ enum __offset_flags_masks {
+ __virtual_mask = 0x1,
+ __public_mask = 0x2,
+ __offset_shift = 8
+ };
+};
+
+/// Type info for classes with multiple, virtual, or non-public inheritance.
+class __vmi_class_type_info : public __class_type_info {
+public:
+ ~__vmi_class_type_info() override;
+
+ unsigned int flags;
+ unsigned int base_count;
+ __base_class_type_info base_info[1];
+};
+
+}
+
+namespace abi = __cxxabiv1;
+
+// We implement a simple two-level cache for type-checking results. For each
+// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
+// unique; if it collides, we will get false negatives, but:
+// * such a collision would have to occur on the *first* bad access,
+// * the probability of such a collision is low (and for a 64-bit target, is
+// negligible), and
+// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
+// give better coverage.
+//
+// The first caching layer is a small hash table with no chaining; buckets are
+// reused as needed. The second caching layer is a large hash table with open
+// chaining. We can freely evict from either layer since this is just a cache.
+//
+// FIXME: Make these hash table accesses thread-safe. The races here are benign:
+// assuming the unsequenced loads and stores don't misbehave too badly,
+// the worst case is false negatives or poor cache behavior, not false
+// positives or crashes.
+
+/// Find a bucket to store the given hash value in.
+static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
+ static const unsigned HashTableSize = 65537;
+ static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize];
+
+ unsigned First = (V & 65535) ^ 1;
+ unsigned Probe = First;
+ for (int Tries = 5; Tries; --Tries) {
+ if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
+ return &__ubsan_vptr_hash_set[Probe];
+ Probe += ((V >> 16) & 65535) + 1;
+ if (Probe >= HashTableSize)
+ Probe -= HashTableSize;
+ }
+ // FIXME: Pick a random entry from the probe sequence to evict rather than
+ // just taking the first.
+ return &__ubsan_vptr_hash_set[First];
+}
+
+/// \brief Determine whether \p Derived has a \p Base base class subobject at
+/// offset \p Offset.
+static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
+ const abi::__class_type_info *Base,
+ sptr Offset) {
+ if (Derived->__type_name == Base->__type_name)
+ return Offset == 0;
+
+ if (const abi::__si_class_type_info *SI =
+ dynamic_cast<const abi::__si_class_type_info*>(Derived))
+ return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
+
+ const abi::__vmi_class_type_info *VTI =
+ dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+ if (!VTI)
+ // No base class subobjects.
+ return false;
+
+ // Look for a base class which is derived from \p Base at the right offset.
+ for (unsigned int base = 0; base != VTI->base_count; ++base) {
+ // FIXME: Curtail the recursion if this base can't possibly contain the
+ // given offset.
+ sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+ abi::__base_class_type_info::__offset_shift;
+ if (VTI->base_info[base].__offset_flags &
+ abi::__base_class_type_info::__virtual_mask)
+ // For now, just punt on virtual bases and say 'yes'.
+ // FIXME: OffsetHere is the offset in the vtable of the virtual base
+ // offset. Read the vbase offset out of the vtable and use it.
+ return true;
+ if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
+ Base, Offset - OffsetHere))
+ return true;
+ }
+
+ return false;
+}
+
+/// \brief Find the derived-most dynamic base class of \p Derived at offset
+/// \p Offset.
+static const abi::__class_type_info *findBaseAtOffset(
+ const abi::__class_type_info *Derived, sptr Offset) {
+ if (!Offset)
+ return Derived;
+
+ if (const abi::__si_class_type_info *SI =
+ dynamic_cast<const abi::__si_class_type_info*>(Derived))
+ return findBaseAtOffset(SI->__base_type, Offset);
+
+ const abi::__vmi_class_type_info *VTI =
+ dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+ if (!VTI)
+ // No base class subobjects.
+ return 0;
+
+ for (unsigned int base = 0; base != VTI->base_count; ++base) {
+ sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+ abi::__base_class_type_info::__offset_shift;
+ if (VTI->base_info[base].__offset_flags &
+ abi::__base_class_type_info::__virtual_mask)
+ // FIXME: Can't handle virtual bases yet.
+ continue;
+ if (const abi::__class_type_info *Base =
+ findBaseAtOffset(VTI->base_info[base].__base_type,
+ Offset - OffsetHere))
+ return Base;
+ }
+
+ return 0;
+}
+
+namespace {
+
+struct VtablePrefix {
+ /// The offset from the vptr to the start of the most-derived object.
+ /// This will only be greater than zero in some virtual base class vtables
+ /// used during object con-/destruction, and will usually be exactly zero.
+ sptr Offset;
+ /// The type_info object describing the most-derived class type.
+ std::type_info *TypeInfo;
+};
+VtablePrefix *getVtablePrefix(void *Vtable) {
+ VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable);
+ if (!Vptr)
+ return 0;
+ VtablePrefix *Prefix = Vptr - 1;
+ if (!Prefix->TypeInfo)
+ // This can't possibly be a valid vtable.
+ return 0;
+ return Prefix;
+}
+
+}
+
+bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
+ // A crash anywhere within this function probably means the vptr is corrupted.
+ // FIXME: Perform these checks more cautiously.
+
+ // Check whether this is something we've evicted from the cache.
+ HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
+ if (*Bucket == Hash) {
+ __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
+ return true;
+ }
+
+ void *VtablePtr = *reinterpret_cast<void **>(Object);
+ VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
+ if (!Vtable)
+ return false;
+
+ // Check that this is actually a type_info object for a class type.
+ abi::__class_type_info *Derived =
+ dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
+ if (!Derived)
+ return false;
+
+ abi::__class_type_info *Base = (abi::__class_type_info*)Type;
+ if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
+ return false;
+
+ // Success. Cache this result.
+ __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
+ *Bucket = Hash;
+ return true;
+}
+
+__ubsan::DynamicTypeInfo
+__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
+ VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
+ if (!Vtable)
+ return DynamicTypeInfo(0, 0, 0);
+ const abi::__class_type_info *ObjectType = findBaseAtOffset(
+ static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
+ -Vtable->Offset);
+ return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
+ ObjectType ? ObjectType->__type_name : "<unknown>");
+}
+
+#endif // CAN_SANITIZE_UB && !SANITIZER_WINDOWS
--- /dev/null
+//===-- ubsan_type_hash_win.cc --------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of type hashing/lookup for Microsoft C++ ABI.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB && SANITIZER_WINDOWS
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+#include <typeinfo>
+
+struct CompleteObjectLocator {
+ int is_image_relative;
+ int offset_to_top;
+ int vfptr_offset;
+ int rtti_addr;
+ int chd_addr;
+ int obj_locator_addr;
+};
+
+struct CompleteObjectLocatorAbs {
+ int is_image_relative;
+ int offset_to_top;
+ int vfptr_offset;
+ std::type_info *rtti_addr;
+ void *chd_addr;
+ CompleteObjectLocator *obj_locator_addr;
+};
+
+bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
+ // FIXME: Implement.
+ return false;
+}
+
+__ubsan::DynamicTypeInfo
+__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
+ // The virtual table may not have a complete object locator if the object
+ // was compiled without RTTI (i.e. we might be reading from some other global
+ // laid out before the virtual table), so we need to carefully validate each
+ // pointer dereference and perform sanity checks.
+ CompleteObjectLocator **obj_locator_ptr =
+ ((CompleteObjectLocator**)VtablePtr)-1;
+ if (!IsAccessibleMemoryRange((uptr)obj_locator_ptr, sizeof(void*)))
+ return DynamicTypeInfo(0, 0, 0);
+
+ CompleteObjectLocator *obj_locator = *obj_locator_ptr;
+ if (!IsAccessibleMemoryRange((uptr)obj_locator,
+ sizeof(CompleteObjectLocator)))
+ return DynamicTypeInfo(0, 0, 0);
+
+ std::type_info *tinfo;
+ if (obj_locator->is_image_relative == 1) {
+ char *image_base = ((char *)obj_locator) - obj_locator->obj_locator_addr;
+ tinfo = (std::type_info *)(image_base + obj_locator->rtti_addr);
+ } else if (obj_locator->is_image_relative == 0)
+ tinfo = ((CompleteObjectLocatorAbs *)obj_locator)->rtti_addr;
+ else
+ // Probably not a complete object locator.
+ return DynamicTypeInfo(0, 0, 0);
+
+ if (!IsAccessibleMemoryRange((uptr)tinfo, sizeof(std::type_info)))
+ return DynamicTypeInfo(0, 0, 0);
+
+ // Okay, this is probably a std::type_info. Request its name.
+ // FIXME: Implement a base class search like we do for Itanium.
+ return DynamicTypeInfo(tinfo->name(), obj_locator->offset_to_top,
+ "<unknown>");
+}
+
+#endif // CAN_SANITIZE_UB && SANITIZER_WINDOWS
//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_value.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#endif
case 32: {
float Value;
- internal_memcpy(&Value, &Val, 4);
+#if defined(__BIG_ENDIAN__)
+ // For big endian the float value is in the last 4 bytes.
+ // On some targets we may only have 4 bytes so we count backwards from
+ // the end of Val to account for both the 32-bit and 64-bit cases.
+ internal_memcpy(&Value, ((const char*)(&Val + 1)) - 4, 4);
+#else
+ internal_memcpy(&Value, &Val, 4);
+#endif
return Value;
}
case 64: {
switch (getType().getFloatBitWidth()) {
case 64: return *reinterpret_cast<double*>(Val);
case 80: return *reinterpret_cast<long double*>(Val);
+ case 96: return *reinterpret_cast<long double*>(Val);
case 128: return *reinterpret_cast<long double*>(Val);
}
}
UNREACHABLE("unexpected floating point bit width");
}
+
+#endif // CAN_SANITIZE_UB
#ifndef UBSAN_VALUE_H
#define UBSAN_VALUE_H
-// For now, only support linux and darwin. Other platforms should be easy to
-// add, and probably work as-is.
-#if !defined(__linux__) && !defined(__APPLE__)
-#error "UBSan not supported for this platform!"
-#endif
-
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#define HAVE_INT128_T 0
#endif
-
namespace __ubsan {
/// \brief Largest integer types we support.
private:
typedef typename is_same<_CharT, char>::type _IsChar;
struct _Dummy { };
+
typedef typename conditional<_IsChar::value,
- std::bitset<1 << (8 * sizeof(_CharT))>,
+ std::bitset<1 << (sizeof(_CharT) * __CHAR_BIT__ * int(_IsChar::value))>,
_Dummy>::type _CacheT;
typedef typename make_unsigned<_CharT>::type _UnsignedCharT;