From ef1b3fda32c08e9bd6977b96e1e619aae3e3726a Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Mon, 4 Nov 2013 21:33:31 +0000 Subject: [PATCH] libsanitizer merge from upstream r191666 This may break gcc-asan on Mac, will follow up separately. From-SVN: r204368 --- gcc/ChangeLog | 14 + gcc/asan.c | 70 +- gcc/asan.h | 1 + gcc/final.c | 4 + gcc/sanitizer.def | 2 +- gcc/testsuite/ChangeLog | 6 + .../c-c++-common/asan/stack-overflow-1.c | 3 +- gcc/testsuite/g++.dg/asan/asan_test.cc | 46 - libsanitizer/ChangeLog | 16 + libsanitizer/MERGE | 2 +- libsanitizer/Makefile.am | 6 +- libsanitizer/Makefile.in | 8 +- libsanitizer/asan/Makefile.am | 21 +- libsanitizer/asan/Makefile.in | 37 +- libsanitizer/asan/asan_allocator.cc | 811 ----- libsanitizer/asan/asan_allocator.h | 106 +- libsanitizer/asan/asan_allocator2.cc | 317 +- libsanitizer/asan/asan_dll_thunk.cc | 194 ++ libsanitizer/asan/asan_fake_stack.cc | 293 +- libsanitizer/asan/asan_fake_stack.h | 167 + libsanitizer/asan/asan_flags.h | 37 +- libsanitizer/asan/asan_globals.cc | 143 +- .../asan/asan_intercepted_functions.h | 269 +- libsanitizer/asan/asan_interceptors.cc | 326 +- libsanitizer/asan/asan_interface_internal.h | 135 +- libsanitizer/asan/asan_internal.h | 50 +- libsanitizer/asan/asan_linux.cc | 36 +- libsanitizer/asan/asan_mac.cc | 76 +- libsanitizer/asan/asan_mac.h | 10 +- libsanitizer/asan/asan_malloc_linux.cc | 9 +- libsanitizer/asan/asan_malloc_mac.cc | 20 +- libsanitizer/asan/asan_malloc_win.cc | 15 +- libsanitizer/asan/asan_mapping.h | 36 +- libsanitizer/asan/asan_new_delete.cc | 41 +- libsanitizer/asan/asan_poisoning.cc | 75 +- libsanitizer/asan/asan_poisoning.h | 57 + libsanitizer/asan/asan_posix.cc | 19 +- libsanitizer/asan/asan_preinit.cc | 6 +- libsanitizer/asan/asan_report.cc | 308 +- libsanitizer/asan/asan_report.h | 2 +- libsanitizer/asan/asan_rtl.cc | 187 +- libsanitizer/asan/asan_stack.cc | 9 +- libsanitizer/asan/asan_stack.h | 43 +- libsanitizer/asan/asan_stats.cc | 97 +- libsanitizer/asan/asan_stats.h | 13 +- libsanitizer/asan/asan_thread.cc | 245 +- libsanitizer/asan/asan_thread.h | 143 +- libsanitizer/asan/asan_thread_registry.cc | 196 -- libsanitizer/asan/asan_thread_registry.h | 83 - libsanitizer/asan/asan_win.cc | 36 +- libsanitizer/asan/libtool-version | 4 +- libsanitizer/configure | 14 +- libsanitizer/configure.ac | 2 +- .../include/sanitizer/common_interface_defs.h | 10 + .../include/sanitizer/dfsan_interface.h | 85 + .../include/sanitizer/linux_syscall_hooks.h | 3067 +++++++++++++++++ .../include/sanitizer/lsan_interface.h | 50 + .../include/sanitizer/msan_interface.h | 160 + libsanitizer/interception/interception.h | 99 +- .../interception/interception_linux.cc | 8 +- .../interception/interception_linux.h | 7 + libsanitizer/lsan/Makefile.am | 60 + libsanitizer/lsan/Makefile.in | 514 +++ libsanitizer/lsan/libtool-version | 6 + libsanitizer/lsan/lsan.cc | 63 + libsanitizer/lsan/lsan.h | 21 + libsanitizer/lsan/lsan_allocator.cc | 191 + libsanitizer/lsan/lsan_allocator.h | 37 + libsanitizer/lsan/lsan_common.cc | 577 ++++ libsanitizer/lsan/lsan_common.h | 174 + libsanitizer/lsan/lsan_common_linux.cc | 139 + libsanitizer/lsan/lsan_interceptors.cc | 279 ++ libsanitizer/lsan/lsan_thread.cc | 154 + libsanitizer/lsan/lsan_thread.h | 51 + libsanitizer/merge.sh | 1 + libsanitizer/sanitizer_common/Makefile.am | 36 +- libsanitizer/sanitizer_common/Makefile.in | 65 +- .../sanitizer_common/sanitizer_allocator.cc | 107 +- .../sanitizer_common/sanitizer_allocator.h | 323 +- .../sanitizer_allocator_internal.h | 62 + .../sanitizer_common/sanitizer_atomic_clang.h | 21 +- .../sanitizer_common/sanitizer_atomic_msvc.h | 27 +- .../sanitizer_common/sanitizer_common.cc | 127 +- .../sanitizer_common/sanitizer_common.h | 195 +- .../sanitizer_common_interceptors.inc | 1934 ++++++++++- .../sanitizer_common_interceptors_ioctl.inc | 566 +++ .../sanitizer_common_interceptors_scanf.inc | 4 +- .../sanitizer_common_libcdep.cc | 35 + .../sanitizer_common_syscalls.inc | 2732 +++++++++++++++ .../sanitizer_common/sanitizer_flags.cc | 35 +- .../sanitizer_common/sanitizer_flags.h | 35 + .../sanitizer_internal_defs.h | 68 +- .../sanitizer_common/sanitizer_libc.cc | 11 +- .../sanitizer_common/sanitizer_libc.h | 33 +- .../sanitizer_common/sanitizer_linux.cc | 683 ++-- .../sanitizer_common/sanitizer_linux.h | 81 + .../sanitizer_linux_libcdep.cc | 351 ++ .../sanitizer_common/sanitizer_mac.cc | 134 +- .../sanitizer_common/sanitizer_mutex.h | 2 + .../sanitizer_placement_new.h | 2 +- .../sanitizer_common/sanitizer_platform.h | 44 + .../sanitizer_platform_interceptors.h | 94 +- .../sanitizer_platform_limits_linux.cc | 45 + .../sanitizer_platform_limits_posix.cc | 863 ++++- .../sanitizer_platform_limits_posix.h | 788 ++++- .../sanitizer_common/sanitizer_posix.cc | 211 +- .../sanitizer_posix_libcdep.cc | 114 + .../sanitizer_common/sanitizer_printf.cc | 175 +- .../sanitizer_common/sanitizer_procmaps.h | 54 +- .../sanitizer_common/sanitizer_quarantine.h | 4 +- .../sanitizer_report_decorator.h | 2 + .../sanitizer_common/sanitizer_stackdepot.cc | 34 + .../sanitizer_common/sanitizer_stackdepot.h | 26 + .../sanitizer_common/sanitizer_stacktrace.cc | 18 +- .../sanitizer_common/sanitizer_stacktrace.h | 14 + .../sanitizer_common/sanitizer_stoptheworld.h | 66 + .../sanitizer_stoptheworld_linux_libcdep.cc | 450 +++ .../sanitizer_suppressions.cc | 146 + .../sanitizer_common/sanitizer_suppressions.h | 58 + .../sanitizer_common/sanitizer_symbolizer.h | 82 +- .../sanitizer_symbolizer_itanium.cc | 40 - .../sanitizer_symbolizer_linux.cc | 180 - .../sanitizer_symbolizer_mac.cc | 29 - ... => sanitizer_symbolizer_posix_libcdep.cc} | 285 +- .../sanitizer_symbolizer_win.cc | 17 +- .../sanitizer_syscall_generic.inc | 22 + .../sanitizer_syscall_linux_x86_64.inc | 85 + .../sanitizer_thread_registry.cc | 277 ++ .../sanitizer_thread_registry.h | 142 + .../sanitizer_common/sanitizer_win.cc | 186 +- libsanitizer/tsan/tsan_defs.h | 1 - libsanitizer/tsan/tsan_fd.cc | 7 +- libsanitizer/tsan/tsan_flags.cc | 12 + libsanitizer/tsan/tsan_flags.h | 10 + libsanitizer/tsan/tsan_interceptors.cc | 578 ++-- libsanitizer/tsan/tsan_interface.cc | 56 + libsanitizer/tsan/tsan_interface.h | 46 +- libsanitizer/tsan/tsan_interface_ann.cc | 99 +- libsanitizer/tsan/tsan_interface_ann.h | 4 +- libsanitizer/tsan/tsan_interface_atomic.cc | 90 +- libsanitizer/tsan/tsan_interface_inl.h | 16 +- libsanitizer/tsan/tsan_interface_java.cc | 41 +- libsanitizer/tsan/tsan_interface_java.h | 19 +- libsanitizer/tsan/tsan_mman.cc | 95 +- libsanitizer/tsan/tsan_mman.h | 2 + libsanitizer/tsan/tsan_mutex.cc | 4 +- libsanitizer/tsan/tsan_mutexset.h | 2 +- libsanitizer/tsan/tsan_platform.h | 33 +- libsanitizer/tsan/tsan_platform_linux.cc | 179 +- libsanitizer/tsan/tsan_platform_mac.cc | 17 +- libsanitizer/tsan/tsan_platform_windows.cc | 17 +- libsanitizer/tsan/tsan_report.cc | 80 +- libsanitizer/tsan/tsan_report.h | 2 + libsanitizer/tsan/tsan_rtl.cc | 322 +- libsanitizer/tsan/tsan_rtl.h | 196 +- libsanitizer/tsan/tsan_rtl_mutex.cc | 81 +- libsanitizer/tsan/tsan_rtl_report.cc | 221 +- libsanitizer/tsan/tsan_rtl_thread.cc | 472 ++- libsanitizer/tsan/tsan_stat.cc | 99 + libsanitizer/tsan/tsan_stat.h | 94 + libsanitizer/tsan/tsan_suppressions.cc | 183 +- libsanitizer/tsan/tsan_suppressions.h | 23 +- libsanitizer/tsan/tsan_symbolize.cc | 66 +- libsanitizer/tsan/tsan_symbolize.h | 1 + .../tsan/tsan_symbolize_addr2line_linux.cc | 3 +- libsanitizer/tsan/tsan_sync.cc | 66 +- libsanitizer/tsan/tsan_sync.h | 2 +- .../tsan/tsan_update_shadow_word_inl.h | 3 +- libsanitizer/tsan/tsan_vector.h | 5 + libsanitizer/ubsan/ubsan_diag.cc | 5 +- libsanitizer/ubsan/ubsan_value.h | 4 +- 171 files changed, 21186 insertions(+), 4909 deletions(-) delete mode 100644 libsanitizer/asan/asan_allocator.cc create mode 100644 libsanitizer/asan/asan_dll_thunk.cc create mode 100644 libsanitizer/asan/asan_fake_stack.h create mode 100644 libsanitizer/asan/asan_poisoning.h delete mode 100644 libsanitizer/asan/asan_thread_registry.cc delete mode 100644 libsanitizer/asan/asan_thread_registry.h create mode 100644 libsanitizer/include/sanitizer/dfsan_interface.h create mode 100644 libsanitizer/include/sanitizer/linux_syscall_hooks.h create mode 100644 libsanitizer/include/sanitizer/lsan_interface.h create mode 100644 libsanitizer/include/sanitizer/msan_interface.h create mode 100644 libsanitizer/lsan/Makefile.am create mode 100644 libsanitizer/lsan/Makefile.in create mode 100644 libsanitizer/lsan/libtool-version create mode 100644 libsanitizer/lsan/lsan.cc create mode 100644 libsanitizer/lsan/lsan.h create mode 100644 libsanitizer/lsan/lsan_allocator.cc create mode 100644 libsanitizer/lsan/lsan_allocator.h create mode 100644 libsanitizer/lsan/lsan_common.cc create mode 100644 libsanitizer/lsan/lsan_common.h create mode 100644 libsanitizer/lsan/lsan_common_linux.cc create mode 100644 libsanitizer/lsan/lsan_interceptors.cc create mode 100644 libsanitizer/lsan/lsan_thread.cc create mode 100644 libsanitizer/lsan/lsan_thread.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_allocator_internal.h create mode 100755 libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc create mode 100644 libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc create mode 100644 libsanitizer/sanitizer_common/sanitizer_linux.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_platform.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_stoptheworld.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_suppressions.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_suppressions.h delete mode 100644 libsanitizer/sanitizer_common/sanitizer_symbolizer_itanium.cc delete mode 100644 libsanitizer/sanitizer_common/sanitizer_symbolizer_linux.cc delete mode 100644 libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc rename libsanitizer/sanitizer_common/{sanitizer_symbolizer.cc => sanitizer_symbolizer_posix_libcdep.cc} (58%) create mode 100644 libsanitizer/sanitizer_common/sanitizer_syscall_generic.inc create mode 100644 libsanitizer/sanitizer_common/sanitizer_syscall_linux_x86_64.inc create mode 100644 libsanitizer/sanitizer_common/sanitizer_thread_registry.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_thread_registry.h diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f8584062cef..e89177880ee 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,17 @@ +2013-11-04 Kostya Serebryany + + Update to match the changed asan API. + * asan.c (asan_function_start): New function. + (asan_emit_stack_protection): Update the string stored in the + stack red zone to match new API. Store the PC of the current + function in the red zone. + (asan_global_struct): Update the __asan_global definition to match + the new API. + (asan_add_global): Ditto. + * asan.h (asan_function_start): New prototype. + * final.c (final_start_function): Call asan_function_start. + * sanitizer.def (BUILT_IN_ASAN_INIT): Rename __asan_init_v1 to __asan_init_v3. + 2013-11-04 Wei Mi * gcc/config/i386/i386-c.c (ix86_target_macros_internal): Separate diff --git a/gcc/asan.c b/gcc/asan.c index 763c59aaed4..fdca37755a9 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -59,11 +59,13 @@ along with GCC; see the file COPYING3. If not see if ((X & 7) + N - 1 > ShadowValue) __asan_report_loadN(X); Stores are instrumented similarly, but using __asan_report_storeN functions. - A call too __asan_init() is inserted to the list of module CTORs. + A call too __asan_init_vN() is inserted to the list of module CTORs. + N is the version number of the AddressSanitizer API. The changes between the + API versions are listed in libsanitizer/asan/asan_interface_internal.h. The run-time library redefines malloc (so that redzone are inserted around the allocated memory) and free (so that reuse of free-ed memory is delayed), - provides __asan_report* and __asan_init functions. + provides __asan_report* and __asan_init_vN functions. Read more: http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm @@ -125,9 +127,11 @@ along with GCC; see the file COPYING3. If not see where '(...){n}' means the content inside the parenthesis occurs 'n' times, with 'n' being the number of variables on the stack. + + 3/ The following 8 bytes contain the PC of the current function which + will be used by the run-time library to print an error message. - 3/ The following 16 bytes of the red zone have no particular - format. + 4/ The following 8 bytes are reserved for internal use by the run-time. The shadow memory for that stack layout is going to look like this: @@ -205,6 +209,9 @@ along with GCC; see the file COPYING3. If not see // Name of the global variable. const void *__name; + // Name of the module where the global variable is declared. + const void *__module_name; + // This is always set to NULL for now. uptr __has_dynamic_init; } @@ -914,6 +921,15 @@ asan_clear_shadow (rtx shadow_mem, HOST_WIDE_INT len) add_int_reg_note (jump, REG_BR_PROB, REG_BR_PROB_BASE * 80 / 100); } +void +asan_function_start (void) +{ + section *fnsec = function_section (current_function_decl); + switch_to_section (fnsec); + ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LASANPC", + current_function_funcdef_no); +} + /* Insert code to protect stack vars. The prologue sequence should be emitted directly, epilogue sequence returned. BASE is the register holding the stack base, against which OFFSETS array offsets are relative to, OFFSETS @@ -929,12 +945,13 @@ asan_emit_stack_protection (rtx base, HOST_WIDE_INT *offsets, tree *decls, int length) { rtx shadow_base, shadow_mem, ret, mem; + char buf[30]; unsigned char shadow_bytes[4]; HOST_WIDE_INT base_offset = offsets[length - 1], offset, prev_offset; HOST_WIDE_INT last_offset, last_size; int l; unsigned char cur_shadow_byte = ASAN_STACK_MAGIC_LEFT; - tree str_cst; + tree str_cst, decl, id; if (shadow_ptr_types[0] == NULL_TREE) asan_init_shadow_ptr_types (); @@ -942,11 +959,6 @@ asan_emit_stack_protection (rtx base, HOST_WIDE_INT *offsets, tree *decls, /* First of all, prepare the description string. */ pretty_printer asan_pp; - if (DECL_NAME (current_function_decl)) - pp_tree_identifier (&asan_pp, DECL_NAME (current_function_decl)); - else - pp_string (&asan_pp, ""); - pp_space (&asan_pp); pp_decimal_int (&asan_pp, length / 2 - 1); pp_space (&asan_pp); for (l = length - 2; l; l -= 2) @@ -976,6 +988,20 @@ asan_emit_stack_protection (rtx base, HOST_WIDE_INT *offsets, tree *decls, emit_move_insn (mem, gen_int_mode (ASAN_STACK_FRAME_MAGIC, ptr_mode)); mem = adjust_address (mem, VOIDmode, GET_MODE_SIZE (ptr_mode)); emit_move_insn (mem, expand_normal (str_cst)); + mem = adjust_address (mem, VOIDmode, GET_MODE_SIZE (ptr_mode)); + ASM_GENERATE_INTERNAL_LABEL (buf, "LASANPC", current_function_funcdef_no); + id = get_identifier (buf); + decl = build_decl (DECL_SOURCE_LOCATION (current_function_decl), + VAR_DECL, id, char_type_node); + SET_DECL_ASSEMBLER_NAME (decl, id); + TREE_ADDRESSABLE (decl) = 1; + TREE_READONLY (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 1; + TREE_STATIC (decl) = 1; + TREE_PUBLIC (decl) = 0; + TREE_USED (decl) = 1; + emit_move_insn (mem, expand_normal (build_fold_addr_expr (decl))); shadow_base = expand_binop (Pmode, lshr_optab, base, GEN_INT (ASAN_SHADOW_SHIFT), NULL_RTX, 1, OPTAB_DIRECT); @@ -1924,20 +1950,21 @@ transform_statements (void) uptr __size; uptr __size_with_redzone; const void *__name; + const void *__module_name; uptr __has_dynamic_init; } type. */ static tree asan_global_struct (void) { - static const char *field_names[5] + static const char *field_names[6] = { "__beg", "__size", "__size_with_redzone", - "__name", "__has_dynamic_init" }; - tree fields[5], ret; + "__name", "__module_name", "__has_dynamic_init" }; + tree fields[6], ret; int i; ret = make_node (RECORD_TYPE); - for (i = 0; i < 5; i++) + for (i = 0; i < 6; i++) { fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, @@ -1962,21 +1989,20 @@ asan_add_global (tree decl, tree type, vec *v) { tree init, uptr = TREE_TYPE (DECL_CHAIN (TYPE_FIELDS (type))); unsigned HOST_WIDE_INT size; - tree str_cst, refdecl = decl; + tree str_cst, module_name_cst, refdecl = decl; vec *vinner = NULL; - pretty_printer asan_pp; + pretty_printer asan_pp, module_name_pp; if (DECL_NAME (decl)) pp_tree_identifier (&asan_pp, DECL_NAME (decl)); else pp_string (&asan_pp, ""); - pp_space (&asan_pp); - pp_left_paren (&asan_pp); - pp_string (&asan_pp, main_input_filename); - pp_right_paren (&asan_pp); str_cst = asan_pp_string (&asan_pp); + pp_string (&module_name_pp, main_input_filename); + module_name_cst = asan_pp_string (&module_name_pp); + if (asan_needs_local_alias (decl)) { char buf[20]; @@ -2004,6 +2030,8 @@ asan_add_global (tree decl, tree type, vec *v) CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, build_int_cst (uptr, size)); CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, fold_convert (const_ptr_type_node, str_cst)); + CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, + fold_convert (const_ptr_type_node, module_name_cst)); CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, build_int_cst (uptr, 0)); init = build_constructor (type, vinner); CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init); @@ -2158,7 +2186,7 @@ add_string_csts (void **slot, void *data) static GTY(()) tree asan_ctor_statements; /* Module-level instrumentation. - - Insert __asan_init() into the list of CTORs. + - Insert __asan_init_vN() into the list of CTORs. - TODO: insert redzones around globals. */ diff --git a/gcc/asan.h b/gcc/asan.h index 62dbe984967..e56468424c9 100644 --- a/gcc/asan.h +++ b/gcc/asan.h @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #ifndef TREE_ASAN #define TREE_ASAN +extern void asan_function_start (void); extern void asan_finish_file (void); extern rtx asan_emit_stack_protection (rtx, HOST_WIDE_INT *, tree *, int); extern bool asan_protect_global (tree); diff --git a/gcc/final.c b/gcc/final.c index efefe1a8ee5..39947329650 100644 --- a/gcc/final.c +++ b/gcc/final.c @@ -78,6 +78,7 @@ along with GCC; see the file COPYING3. If not see #include "cfgloop.h" #include "params.h" #include "tree-pretty-print.h" /* for dump_function_header */ +#include "asan.h" #ifdef XCOFF_DEBUGGING_INFO #include "xcoffout.h" /* Needed for external data @@ -1738,6 +1739,9 @@ final_start_function (rtx first, FILE *file, high_block_linenum = high_function_linenum = last_linenum; + if (flag_sanitize & SANITIZE_ADDRESS) + asan_function_start (); + if (!DECL_IGNORED_P (current_function_decl)) debug_hooks->begin_prologue (last_linenum, last_filename); diff --git a/gcc/sanitizer.def b/gcc/sanitizer.def index c7c780929b8..0f45e9eead4 100644 --- a/gcc/sanitizer.def +++ b/gcc/sanitizer.def @@ -27,7 +27,7 @@ along with GCC; see the file COPYING3. If not see for other FEs by asan.c. */ /* Address Sanitizer */ -DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init_v1", +DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init_v3", BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) /* Do not reorder the BUILT_IN_ASAN_REPORT* builtins, e.g. cfgcleanup.c relies on this order. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d007b0202b2..8483842402f 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2013-11-04 Kostya Serebryany + + * g++.dg/asan/asan_test.cc: Update the test + to match the fresh asan run-time. + * c-c++-common/asan/stack-overflow-1.c: Ditto. + 2013-11-04 Ian Lance Taylor * g++.dg/ext/sync-4.C: New test. diff --git a/gcc/testsuite/c-c++-common/asan/stack-overflow-1.c b/gcc/testsuite/c-c++-common/asan/stack-overflow-1.c index 5f563561935..c7176509710 100644 --- a/gcc/testsuite/c-c++-common/asan/stack-overflow-1.c +++ b/gcc/testsuite/c-c++-common/asan/stack-overflow-1.c @@ -19,4 +19,5 @@ int main() { /* { 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 "\[^\n\r]*Address 0x\[0-9a-f\]+ is\[^\n\r]*frame
" } */ +/* { 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)" */ diff --git a/gcc/testsuite/g++.dg/asan/asan_test.cc b/gcc/testsuite/g++.dg/asan/asan_test.cc index 76b6e693498..2df8c62cbb1 100644 --- a/gcc/testsuite/g++.dg/asan/asan_test.cc +++ b/gcc/testsuite/g++.dg/asan/asan_test.cc @@ -204,16 +204,6 @@ TEST(AddressSanitizer, BitFieldNegativeTest) { delete Ident(x); } -TEST(AddressSanitizer, OutOfMemoryTest) { - size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 48) : (0xf0000000); - EXPECT_EQ(0, realloc(0, size)); - EXPECT_EQ(0, realloc(0, ~Ident(0))); - EXPECT_EQ(0, malloc(size)); - EXPECT_EQ(0, malloc(~Ident(0))); - EXPECT_EQ(0, calloc(1, size)); - EXPECT_EQ(0, calloc(1, ~Ident(0))); -} - #if ASAN_NEEDS_SEGV namespace { @@ -497,42 +487,6 @@ TEST(AddressSanitizer, ManyStackObjectsTest) { EXPECT_DEATH(Ident(ZZZ)[-1] = 0, ASAN_PCRE_DOTALL "XXX.*YYY.*ZZZ"); } -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"); -} - NOINLINE void LongJmpFunc1(jmp_buf buf) { // create three red zones for these two stack objects. int a; diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog index 9d33782441e..c7871e3295f 100644 --- a/libsanitizer/ChangeLog +++ b/libsanitizer/ChangeLog @@ -1,3 +1,19 @@ +2013-11-04 Kostya Serebryany + + * All source files: Merge from upstream r191666. + * merge.sh: Added lsan. + * configure.ac (AC_CONFIG_FILES): Added lsan. + * Makefile.am (SUBDIRS): Added lsan. + * sanitizer_common/Makefile.am (sanitizer_common_files): Added new fles. + * asan/Makefile.am (asan_files): Added new files. + (libasan_la_LIBADD): Added a dependency on lsan. + * lsan/Makefile.am: New file. + * asan/Makefile.in: Regenerate. + * lsan/Makefile.in: Regenerate. + * Makefile.in: Regenerate. + * configure: Regenerate. + * sanitizer_common/Makefile.in: Regenerate. + 2013-09-20 Alan Modra * configure: Regenerate. diff --git a/libsanitizer/MERGE b/libsanitizer/MERGE index 28d1e49ab77..0431b147a16 100644 --- a/libsanitizer/MERGE +++ b/libsanitizer/MERGE @@ -1,4 +1,4 @@ -175733 +191666 The first line of this file holds the svn revision number of the last merge done from the master library sources. diff --git a/libsanitizer/Makefile.am b/libsanitizer/Makefile.am index 739c33babbe..15c11ecb2d7 100644 --- a/libsanitizer/Makefile.am +++ b/libsanitizer/Makefile.am @@ -1,13 +1,13 @@ ACLOCAL_AMFLAGS = -I .. -I ../config if TSAN_SUPPORTED -SUBDIRS = interception sanitizer_common asan tsan ubsan +SUBDIRS = interception sanitizer_common lsan asan tsan ubsan else -SUBDIRS = interception sanitizer_common asan ubsan +SUBDIRS = interception sanitizer_common lsan asan ubsan endif if USING_MAC_INTERPOSE -SUBDIRS = sanitizer_common asan ubsan +SUBDIRS = sanitizer_common lsan asan ubsan endif # Work around what appears to be a GNU make bug handling MAKEFLAGS diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in index 286cea61138..47a8771c783 100644 --- a/libsanitizer/Makefile.in +++ b/libsanitizer/Makefile.in @@ -76,7 +76,7 @@ AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS ETAGS = etags CTAGS = ctags -DIST_SUBDIRS = interception sanitizer_common asan ubsan tsan +DIST_SUBDIRS = interception sanitizer_common lsan asan ubsan tsan ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AR = @AR@ @@ -209,9 +209,9 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ ACLOCAL_AMFLAGS = -I .. -I ../config -@TSAN_SUPPORTED_FALSE@SUBDIRS = interception sanitizer_common asan ubsan -@TSAN_SUPPORTED_TRUE@SUBDIRS = interception sanitizer_common asan tsan ubsan -@USING_MAC_INTERPOSE_TRUE@SUBDIRS = sanitizer_common asan ubsan +@TSAN_SUPPORTED_FALSE@SUBDIRS = interception sanitizer_common lsan asan ubsan +@TSAN_SUPPORTED_TRUE@SUBDIRS = interception sanitizer_common lsan asan tsan ubsan +@USING_MAC_INTERPOSE_TRUE@SUBDIRS = sanitizer_common lsan asan ubsan # 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 diff --git a/libsanitizer/asan/Makefile.am b/libsanitizer/asan/Makefile.am index f7847db3647..8764007ca45 100644 --- a/libsanitizer/asan/Makefile.am +++ b/libsanitizer/asan/Makefile.am @@ -15,32 +15,31 @@ toolexeclib_LTLIBRARIES = libasan.la nodist_toolexeclib_HEADERS = libasan_preinit.o asan_files = \ - asan_allocator.cc \ asan_allocator2.cc \ - asan_interceptors.cc \ - asan_mac.cc \ - asan_malloc_mac.cc \ - asan_new_delete.cc \ - asan_posix.cc \ - asan_rtl.cc \ - asan_stats.cc \ - asan_thread_registry.cc \ + asan_dll_thunk.cc \ asan_fake_stack.cc \ asan_globals.cc \ + asan_interceptors.cc \ asan_linux.cc \ + asan_mac.cc \ asan_malloc_linux.cc \ + asan_malloc_mac.cc \ asan_malloc_win.cc \ + asan_new_delete.cc \ asan_poisoning.cc \ + asan_posix.cc \ asan_report.cc \ + asan_rtl.cc \ asan_stack.cc \ + asan_stats.cc \ asan_thread.cc \ asan_win.cc libasan_la_SOURCES = $(asan_files) if USING_MAC_INTERPOSE -libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la +libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la else -libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la +libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la $(top_builddir)/interception/libinterception.la endif libasan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS) diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in index 1db6fa3da71..be8b879b549 100644 --- a/libsanitizer/asan/Makefile.in +++ b/libsanitizer/asan/Makefile.in @@ -81,17 +81,18 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" \ LTLIBRARIES = $(toolexeclib_LTLIBRARIES) am__DEPENDENCIES_1 = @USING_MAC_INTERPOSE_FALSE@libasan_la_DEPENDENCIES = $(top_builddir)/sanitizer_common/libsanitizer_common.la \ +@USING_MAC_INTERPOSE_FALSE@ $(top_builddir)/lsan/libsanitizer_lsan.la \ @USING_MAC_INTERPOSE_FALSE@ $(top_builddir)/interception/libinterception.la \ @USING_MAC_INTERPOSE_FALSE@ $(am__DEPENDENCIES_1) @USING_MAC_INTERPOSE_TRUE@libasan_la_DEPENDENCIES = $(top_builddir)/sanitizer_common/libsanitizer_common.la \ +@USING_MAC_INTERPOSE_TRUE@ $(top_builddir)/lsan/libsanitizer_lsan.la \ @USING_MAC_INTERPOSE_TRUE@ $(am__DEPENDENCIES_1) -am__objects_1 = asan_allocator.lo asan_allocator2.lo \ - asan_interceptors.lo asan_mac.lo asan_malloc_mac.lo \ - asan_new_delete.lo asan_posix.lo asan_rtl.lo asan_stats.lo \ - asan_thread_registry.lo asan_fake_stack.lo asan_globals.lo \ - asan_linux.lo asan_malloc_linux.lo asan_malloc_win.lo \ - asan_poisoning.lo asan_report.lo asan_stack.lo asan_thread.lo \ - asan_win.lo +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_libasan_la_OBJECTS = $(am__objects_1) libasan_la_OBJECTS = $(am_libasan_la_OBJECTS) libasan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ @@ -260,32 +261,33 @@ ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config toolexeclib_LTLIBRARIES = libasan.la nodist_toolexeclib_HEADERS = libasan_preinit.o asan_files = \ - asan_allocator.cc \ asan_allocator2.cc \ - asan_interceptors.cc \ - asan_mac.cc \ - asan_malloc_mac.cc \ - asan_new_delete.cc \ - asan_posix.cc \ - asan_rtl.cc \ - asan_stats.cc \ - asan_thread_registry.cc \ + asan_dll_thunk.cc \ asan_fake_stack.cc \ asan_globals.cc \ + asan_interceptors.cc \ asan_linux.cc \ + asan_mac.cc \ asan_malloc_linux.cc \ + asan_malloc_mac.cc \ asan_malloc_win.cc \ + asan_new_delete.cc \ asan_poisoning.cc \ + asan_posix.cc \ asan_report.cc \ + asan_rtl.cc \ asan_stack.cc \ + asan_stats.cc \ asan_thread.cc \ asan_win.cc libasan_la_SOURCES = $(asan_files) @USING_MAC_INTERPOSE_FALSE@libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la \ +@USING_MAC_INTERPOSE_FALSE@ $(top_builddir)/lsan/libsanitizer_lsan.la \ @USING_MAC_INTERPOSE_FALSE@ $(top_builddir)/interception/libinterception.la \ @USING_MAC_INTERPOSE_FALSE@ $(LIBSTDCXX_RAW_CXX_LDFLAGS) @USING_MAC_INTERPOSE_TRUE@libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la \ +@USING_MAC_INTERPOSE_TRUE@ $(top_builddir)/lsan/libsanitizer_lsan.la \ @USING_MAC_INTERPOSE_TRUE@ $(LIBSTDCXX_RAW_CXX_LDFLAGS) libasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` -lpthread -ldl @@ -402,8 +404,8 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@ @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_fake_stack.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@ @@ -420,7 +422,6 @@ distclean-compile: @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_thread.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread_registry.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@ .cc.o: diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc deleted file mode 100644 index 4e97ff57530..00000000000 --- a/libsanitizer/asan/asan_allocator.cc +++ /dev/null @@ -1,811 +0,0 @@ -//===-- 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. -// Evey piece of memory (AsanChunk) allocated by the allocator -// has a left redzone of REDZONE bytes and -// a right redzone such that the end of the chunk is aligned by REDZONE -// (i.e. the right redzone is between 0 and REDZONE-1). -// The left redzone is always poisoned. -// The right redzone is poisoned on malloc, the body is poisoned on free. -// Once freed, a chunk is moved to a quarantine (fifo list). -// After quarantine, a chunk is returned to freelists. -// -// The left redzone contains ASan's internal data and the stack trace of -// the malloc call. -// Once freed, the body of the chunk contains the stack trace of the free call. -// -//===----------------------------------------------------------------------===// -#include "asan_allocator.h" - -#if ASAN_ALLOCATOR_VERSION == 1 -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "asan_stats.h" -#include "asan_report.h" -#include "asan_thread.h" -#include "asan_thread_registry.h" -#include "sanitizer_common/sanitizer_allocator.h" -#include "sanitizer_common/sanitizer_atomic.h" -#include "sanitizer_common/sanitizer_mutex.h" - -namespace __asan { - -#define REDZONE ((uptr)(flags()->redzone)) -static const uptr kMinAllocSize = REDZONE * 2; -static const u64 kMaxAvailableRam = 128ULL << 30; // 128G -static const uptr kMaxThreadLocalQuarantine = 1 << 20; // 1M - -static const uptr kMinMmapSize = (ASAN_LOW_MEMORY) ? 4UL << 17 : 4UL << 20; -static const uptr kMaxSizeForThreadLocalFreeList = - (ASAN_LOW_MEMORY) ? 1 << 15 : 1 << 17; - -// Size classes less than kMallocSizeClassStep are powers of two. -// All other size classes are multiples of kMallocSizeClassStep. -static const uptr kMallocSizeClassStepLog = 26; -static const uptr kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog; - -static const uptr kMaxAllowedMallocSize = - (SANITIZER_WORDSIZE == 32) ? 3UL << 30 : 8UL << 30; - -static inline uptr SizeClassToSize(u8 size_class) { - CHECK(size_class < kNumberOfSizeClasses); - if (size_class <= kMallocSizeClassStepLog) { - return 1UL << size_class; - } else { - return (size_class - kMallocSizeClassStepLog) * kMallocSizeClassStep; - } -} - -static inline u8 SizeToSizeClass(uptr size) { - u8 res = 0; - if (size <= kMallocSizeClassStep) { - uptr rounded = RoundUpToPowerOfTwo(size); - res = Log2(rounded); - } else { - res = ((size + kMallocSizeClassStep - 1) / kMallocSizeClassStep) - + kMallocSizeClassStepLog; - } - CHECK(res < kNumberOfSizeClasses); - CHECK(size <= SizeClassToSize(res)); - return res; -} - -// Given REDZONE bytes, we need to mark first size bytes -// as addressable and the rest REDZONE-size bytes as unaddressable. -static void PoisonHeapPartialRightRedzone(uptr mem, uptr size) { - CHECK(size <= REDZONE); - CHECK(IsAligned(mem, REDZONE)); - CHECK(IsPowerOfTwo(SHADOW_GRANULARITY)); - CHECK(IsPowerOfTwo(REDZONE)); - CHECK(REDZONE >= SHADOW_GRANULARITY); - PoisonShadowPartialRightRedzone(mem, size, REDZONE, - kAsanHeapRightRedzoneMagic); -} - -static u8 *MmapNewPagesAndPoisonShadow(uptr size) { - CHECK(IsAligned(size, GetPageSizeCached())); - u8 *res = (u8*)MmapOrDie(size, __FUNCTION__); - PoisonShadow((uptr)res, size, kAsanHeapLeftRedzoneMagic); - if (flags()->debug) { - Printf("ASAN_MMAP: [%p, %p)\n", res, res + size); - } - return res; -} - -// 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. -// -// The pseudo state CHUNK_MEMALIGN is used to mark that the address is not -// the beginning of a AsanChunk (in which the actual chunk resides at -// this - this->used_size). -// -// The magic numbers for the enum values are taken randomly. -enum { - CHUNK_AVAILABLE = 0x57, - CHUNK_ALLOCATED = 0x32, - CHUNK_QUARANTINE = 0x19, - CHUNK_MEMALIGN = 0xDC -}; - -struct ChunkBase { - // First 8 bytes. - uptr chunk_state : 8; - uptr alloc_tid : 24; - uptr size_class : 8; - uptr free_tid : 24; - - // Second 8 bytes. - uptr alignment_log : 8; - uptr alloc_type : 2; - uptr used_size : FIRST_32_SECOND_64(32, 54); // Size requested by the user. - - // This field may overlap with the user area and thus should not - // be used while the chunk is in CHUNK_ALLOCATED state. - AsanChunk *next; - - // Typically the beginning of the user-accessible memory is 'this'+REDZONE - // and is also aligned by REDZONE. However, if the memory is allocated - // by memalign, the alignment might be higher and the user-accessible memory - // starts at the first properly aligned address after 'this'. - uptr Beg() { return RoundUpTo((uptr)this + 1, 1 << alignment_log); } - uptr Size() { return SizeClassToSize(size_class); } - u8 SizeClass() { return size_class; } -}; - -struct AsanChunk: public ChunkBase { - u32 *compressed_alloc_stack() { - return (u32*)((uptr)this + sizeof(ChunkBase)); - } - u32 *compressed_free_stack() { - return (u32*)((uptr)this + Max((uptr)REDZONE, (uptr)sizeof(ChunkBase))); - } - - // The left redzone after the ChunkBase is given to the alloc stack trace. - uptr compressed_alloc_stack_size() { - if (REDZONE < sizeof(ChunkBase)) return 0; - return (REDZONE - sizeof(ChunkBase)) / sizeof(u32); - } - uptr compressed_free_stack_size() { - if (REDZONE < sizeof(ChunkBase)) return 0; - return (REDZONE) / sizeof(u32); - } -}; - -uptr AsanChunkView::Beg() { return chunk_->Beg(); } -uptr AsanChunkView::End() { return Beg() + UsedSize(); } -uptr AsanChunkView::UsedSize() { return chunk_->used_size; } -uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; } -uptr AsanChunkView::FreeTid() { return chunk_->free_tid; } - -void AsanChunkView::GetAllocStack(StackTrace *stack) { - StackTrace::UncompressStack(stack, chunk_->compressed_alloc_stack(), - chunk_->compressed_alloc_stack_size()); -} - -void AsanChunkView::GetFreeStack(StackTrace *stack) { - StackTrace::UncompressStack(stack, chunk_->compressed_free_stack(), - chunk_->compressed_free_stack_size()); -} - -static AsanChunk *PtrToChunk(uptr ptr) { - AsanChunk *m = (AsanChunk*)(ptr - REDZONE); - if (m->chunk_state == CHUNK_MEMALIGN) { - m = (AsanChunk*)((uptr)m - m->used_size); - } - return m; -} - -void AsanChunkFifoList::PushList(AsanChunkFifoList *q) { - CHECK(q->size() > 0); - size_ += q->size(); - append_back(q); - q->clear(); -} - -void AsanChunkFifoList::Push(AsanChunk *n) { - push_back(n); - size_ += n->Size(); -} - -// Interesting performance observation: this function takes up to 15% of overal -// allocator time. That's because *first_ has been evicted from cache long time -// ago. Not sure if we can or want to do anything with this. -AsanChunk *AsanChunkFifoList::Pop() { - CHECK(first_); - AsanChunk *res = front(); - size_ -= res->Size(); - pop_front(); - return res; -} - -// All pages we ever allocated. -struct PageGroup { - uptr beg; - uptr end; - uptr size_of_chunk; - uptr last_chunk; - bool InRange(uptr addr) { - return addr >= beg && addr < end; - } -}; - -class MallocInfo { - public: - explicit MallocInfo(LinkerInitialized x) : mu_(x) { } - - AsanChunk *AllocateChunks(u8 size_class, uptr n_chunks) { - AsanChunk *m = 0; - AsanChunk **fl = &free_lists_[size_class]; - { - BlockingMutexLock lock(&mu_); - for (uptr i = 0; i < n_chunks; i++) { - if (!(*fl)) { - *fl = GetNewChunks(size_class); - } - AsanChunk *t = *fl; - *fl = t->next; - t->next = m; - CHECK(t->chunk_state == CHUNK_AVAILABLE); - m = t; - } - } - return m; - } - - void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x, - bool eat_free_lists) { - CHECK(flags()->quarantine_size > 0); - BlockingMutexLock lock(&mu_); - AsanChunkFifoList *q = &x->quarantine_; - if (q->size() > 0) { - quarantine_.PushList(q); - while (quarantine_.size() > (uptr)flags()->quarantine_size) { - QuarantinePop(); - } - } - if (eat_free_lists) { - for (uptr size_class = 0; size_class < kNumberOfSizeClasses; - size_class++) { - AsanChunk *m = x->free_lists_[size_class]; - while (m) { - AsanChunk *t = m->next; - m->next = free_lists_[size_class]; - free_lists_[size_class] = m; - m = t; - } - x->free_lists_[size_class] = 0; - } - } - } - - void BypassThreadLocalQuarantine(AsanChunk *chunk) { - BlockingMutexLock lock(&mu_); - quarantine_.Push(chunk); - } - - AsanChunk *FindChunkByAddr(uptr addr) { - BlockingMutexLock lock(&mu_); - return FindChunkByAddrUnlocked(addr); - } - - uptr AllocationSize(uptr ptr) { - if (!ptr) return 0; - BlockingMutexLock lock(&mu_); - - // Make sure this is our chunk and |ptr| actually points to the beginning - // of the allocated memory. - AsanChunk *m = FindChunkByAddrUnlocked(ptr); - if (!m || m->Beg() != ptr) return 0; - - if (m->chunk_state == CHUNK_ALLOCATED) { - return m->used_size; - } else { - return 0; - } - } - - void ForceLock() { - mu_.Lock(); - } - - void ForceUnlock() { - mu_.Unlock(); - } - - void PrintStatus() { - BlockingMutexLock lock(&mu_); - uptr malloced = 0; - - Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ", - quarantine_.size() >> 20, malloced >> 20); - for (uptr j = 1; j < kNumberOfSizeClasses; j++) { - AsanChunk *i = free_lists_[j]; - if (!i) continue; - uptr t = 0; - for (; i; i = i->next) { - t += i->Size(); - } - Printf("%zu:%zu ", j, t >> 20); - } - Printf("\n"); - } - - PageGroup *FindPageGroup(uptr addr) { - BlockingMutexLock lock(&mu_); - return FindPageGroupUnlocked(addr); - } - - private: - PageGroup *FindPageGroupUnlocked(uptr addr) { - int n = atomic_load(&n_page_groups_, memory_order_relaxed); - // If the page groups are not sorted yet, sort them. - if (n_sorted_page_groups_ < n) { - SortArray((uptr*)page_groups_, n); - n_sorted_page_groups_ = n; - } - // Binary search over the page groups. - int beg = 0, end = n; - while (beg < end) { - int med = (beg + end) / 2; - uptr g = (uptr)page_groups_[med]; - if (addr > g) { - // 'g' points to the end of the group, so 'addr' - // may not belong to page_groups_[med] or any previous group. - beg = med + 1; - } else { - // 'addr' may belong to page_groups_[med] or a previous group. - end = med; - } - } - if (beg >= n) - return 0; - PageGroup *g = page_groups_[beg]; - CHECK(g); - if (g->InRange(addr)) - return g; - return 0; - } - - // 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 or a chunk from quarantine. - if (left_chunk->chunk_state == CHUNK_AVAILABLE && - right_chunk->chunk_state != CHUNK_AVAILABLE) - return right_chunk; - if (right_chunk->chunk_state == CHUNK_AVAILABLE && - left_chunk->chunk_state != CHUNK_AVAILABLE) - return left_chunk; - // 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; - } - - AsanChunk *FindChunkByAddrUnlocked(uptr addr) { - PageGroup *g = FindPageGroupUnlocked(addr); - if (!g) return 0; - CHECK(g->size_of_chunk); - uptr offset_from_beg = addr - g->beg; - uptr this_chunk_addr = g->beg + - (offset_from_beg / g->size_of_chunk) * g->size_of_chunk; - CHECK(g->InRange(this_chunk_addr)); - AsanChunk *m = (AsanChunk*)this_chunk_addr; - CHECK(m->chunk_state == CHUNK_ALLOCATED || - m->chunk_state == CHUNK_AVAILABLE || - m->chunk_state == CHUNK_QUARANTINE); - sptr offset = 0; - AsanChunkView m_view(m); - if (m_view.AddrIsInside(addr, 1, &offset)) - return m; - - if (m_view.AddrIsAtRight(addr, 1, &offset)) { - if (this_chunk_addr == g->last_chunk) // rightmost chunk - return m; - uptr right_chunk_addr = this_chunk_addr + g->size_of_chunk; - CHECK(g->InRange(right_chunk_addr)); - return ChooseChunk(addr, m, (AsanChunk*)right_chunk_addr); - } else { - CHECK(m_view.AddrIsAtLeft(addr, 1, &offset)); - if (this_chunk_addr == g->beg) // leftmost chunk - return m; - uptr left_chunk_addr = this_chunk_addr - g->size_of_chunk; - CHECK(g->InRange(left_chunk_addr)); - return ChooseChunk(addr, (AsanChunk*)left_chunk_addr, m); - } - } - - void QuarantinePop() { - CHECK(quarantine_.size() > 0); - AsanChunk *m = quarantine_.Pop(); - CHECK(m); - // if (F_v >= 2) Printf("MallocInfo::pop %p\n", m); - - CHECK(m->chunk_state == CHUNK_QUARANTINE); - m->chunk_state = CHUNK_AVAILABLE; - PoisonShadow((uptr)m, m->Size(), kAsanHeapLeftRedzoneMagic); - CHECK(m->alloc_tid >= 0); - CHECK(m->free_tid >= 0); - - uptr size_class = m->SizeClass(); - m->next = free_lists_[size_class]; - free_lists_[size_class] = m; - - // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); - thread_stats.real_frees++; - thread_stats.really_freed += m->used_size; - thread_stats.really_freed_redzones += m->Size() - m->used_size; - thread_stats.really_freed_by_size[m->SizeClass()]++; - } - - // Get a list of newly allocated chunks. - AsanChunk *GetNewChunks(u8 size_class) { - uptr size = SizeClassToSize(size_class); - CHECK(IsPowerOfTwo(kMinMmapSize)); - CHECK(size < kMinMmapSize || (size % kMinMmapSize) == 0); - uptr mmap_size = Max(size, kMinMmapSize); - uptr n_chunks = mmap_size / size; - CHECK(n_chunks * size == mmap_size); - uptr PageSize = GetPageSizeCached(); - if (size < PageSize) { - // Size is small, just poison the last chunk. - n_chunks--; - } else { - // Size is large, allocate an extra page at right and poison it. - mmap_size += PageSize; - } - CHECK(n_chunks > 0); - u8 *mem = MmapNewPagesAndPoisonShadow(mmap_size); - - // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); - thread_stats.mmaps++; - thread_stats.mmaped += mmap_size; - thread_stats.mmaped_by_size[size_class] += n_chunks; - - AsanChunk *res = 0; - for (uptr i = 0; i < n_chunks; i++) { - AsanChunk *m = (AsanChunk*)(mem + i * size); - m->chunk_state = CHUNK_AVAILABLE; - m->size_class = size_class; - m->next = res; - res = m; - } - PageGroup *pg = (PageGroup*)(mem + n_chunks * size); - // This memory is already poisoned, no need to poison it again. - pg->beg = (uptr)mem; - pg->end = pg->beg + mmap_size; - pg->size_of_chunk = size; - pg->last_chunk = (uptr)(mem + size * (n_chunks - 1)); - int idx = atomic_fetch_add(&n_page_groups_, 1, memory_order_relaxed); - CHECK(idx < (int)ARRAY_SIZE(page_groups_)); - page_groups_[idx] = pg; - return res; - } - - AsanChunk *free_lists_[kNumberOfSizeClasses]; - AsanChunkFifoList quarantine_; - BlockingMutex mu_; - - PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize]; - atomic_uint32_t n_page_groups_; - int n_sorted_page_groups_; -}; - -static MallocInfo malloc_info(LINKER_INITIALIZED); - -void AsanThreadLocalMallocStorage::CommitBack() { - malloc_info.SwallowThreadLocalMallocStorage(this, true); -} - -AsanChunkView FindHeapChunkByAddress(uptr address) { - return AsanChunkView(malloc_info.FindChunkByAddr(address)); -} - -static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack, - AllocType alloc_type) { - __asan_init(); - CHECK(stack); - if (size == 0) { - size = 1; // TODO(kcc): do something smarter - } - CHECK(IsPowerOfTwo(alignment)); - uptr rounded_size = RoundUpTo(size, REDZONE); - uptr needed_size = rounded_size + REDZONE; - if (alignment > REDZONE) { - needed_size += alignment; - } - CHECK(IsAligned(needed_size, REDZONE)); - if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { - Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", - (void*)size); - return 0; - } - - u8 size_class = SizeToSizeClass(needed_size); - uptr size_to_allocate = SizeClassToSize(size_class); - CHECK(size_to_allocate >= kMinAllocSize); - CHECK(size_to_allocate >= needed_size); - CHECK(IsAligned(size_to_allocate, REDZONE)); - - if (flags()->verbosity >= 3) { - Printf("Allocate align: %zu size: %zu class: %u real: %zu\n", - alignment, size, size_class, size_to_allocate); - } - - AsanThread *t = asanThreadRegistry().GetCurrent(); - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); - // Statistics - thread_stats.mallocs++; - thread_stats.malloced += size; - thread_stats.malloced_redzones += size_to_allocate - size; - thread_stats.malloced_by_size[size_class]++; - - AsanChunk *m = 0; - if (!t || size_to_allocate >= kMaxSizeForThreadLocalFreeList) { - // get directly from global storage. - m = malloc_info.AllocateChunks(size_class, 1); - thread_stats.malloc_large++; - } else { - // get from the thread-local storage. - AsanChunk **fl = &t->malloc_storage().free_lists_[size_class]; - if (!*fl) { - uptr n_new_chunks = kMaxSizeForThreadLocalFreeList / size_to_allocate; - *fl = malloc_info.AllocateChunks(size_class, n_new_chunks); - thread_stats.malloc_small_slow++; - } - m = *fl; - *fl = (*fl)->next; - } - CHECK(m); - CHECK(m->chunk_state == CHUNK_AVAILABLE); - m->chunk_state = CHUNK_ALLOCATED; - m->alloc_type = alloc_type; - m->next = 0; - CHECK(m->Size() == size_to_allocate); - uptr addr = (uptr)m + REDZONE; - CHECK(addr <= (uptr)m->compressed_free_stack()); - - if (alignment > REDZONE && (addr & (alignment - 1))) { - addr = RoundUpTo(addr, alignment); - CHECK((addr & (alignment - 1)) == 0); - AsanChunk *p = (AsanChunk*)(addr - REDZONE); - p->chunk_state = CHUNK_MEMALIGN; - p->used_size = (uptr)p - (uptr)m; - m->alignment_log = Log2(alignment); - CHECK(m->Beg() == addr); - } else { - m->alignment_log = Log2(REDZONE); - } - CHECK(m == PtrToChunk(addr)); - m->used_size = size; - CHECK(m->Beg() == addr); - m->alloc_tid = t ? t->tid() : 0; - m->free_tid = kInvalidTid; - StackTrace::CompressStack(stack, m->compressed_alloc_stack(), - m->compressed_alloc_stack_size()); - PoisonShadow(addr, rounded_size, 0); - if (size < rounded_size) { - PoisonHeapPartialRightRedzone(addr + rounded_size - REDZONE, - size & (REDZONE - 1)); - } - if (size <= (uptr)(flags()->max_malloc_fill_size)) { - REAL(memset)((void*)addr, 0, rounded_size); - } - return (u8*)addr; -} - -static void Deallocate(u8 *ptr, StackTrace *stack, AllocType alloc_type) { - if (!ptr) return; - CHECK(stack); - - if (flags()->debug) { - CHECK(malloc_info.FindPageGroup((uptr)ptr)); - } - - // Printf("Deallocate %p\n", ptr); - AsanChunk *m = PtrToChunk((uptr)ptr); - - // Flip the chunk_state atomically to avoid race on double-free. - u8 old_chunk_state = atomic_exchange((atomic_uint8_t*)m, CHUNK_QUARANTINE, - memory_order_acq_rel); - - if (old_chunk_state == CHUNK_QUARANTINE) { - ReportDoubleFree((uptr)ptr, stack); - } else if (old_chunk_state != CHUNK_ALLOCATED) { - ReportFreeNotMalloced((uptr)ptr, stack); - } - CHECK(old_chunk_state == CHUNK_ALLOCATED); - if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch) - ReportAllocTypeMismatch((uptr)ptr, stack, - (AllocType)m->alloc_type, (AllocType)alloc_type); - // With REDZONE==16 m->next is in the user area, otherwise it should be 0. - CHECK(REDZONE <= 16 || !m->next); - CHECK(m->free_tid == kInvalidTid); - CHECK(m->alloc_tid >= 0); - AsanThread *t = asanThreadRegistry().GetCurrent(); - m->free_tid = t ? t->tid() : 0; - StackTrace::CompressStack(stack, m->compressed_free_stack(), - m->compressed_free_stack_size()); - uptr rounded_size = RoundUpTo(m->used_size, REDZONE); - PoisonShadow((uptr)ptr, rounded_size, kAsanHeapFreeMagic); - - // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); - thread_stats.frees++; - thread_stats.freed += m->used_size; - thread_stats.freed_by_size[m->SizeClass()]++; - - CHECK(m->chunk_state == CHUNK_QUARANTINE); - - if (t) { - AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); - ms->quarantine_.Push(m); - - if (ms->quarantine_.size() > kMaxThreadLocalQuarantine) { - malloc_info.SwallowThreadLocalMallocStorage(ms, false); - } - } else { - malloc_info.BypassThreadLocalQuarantine(m); - } -} - -static u8 *Reallocate(u8 *old_ptr, uptr new_size, - StackTrace *stack) { - CHECK(old_ptr && new_size); - - // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); - thread_stats.reallocs++; - thread_stats.realloced += new_size; - - AsanChunk *m = PtrToChunk((uptr)old_ptr); - CHECK(m->chunk_state == CHUNK_ALLOCATED); - uptr old_size = m->used_size; - uptr memcpy_size = Min(new_size, old_size); - u8 *new_ptr = Allocate(0, new_size, stack, FROM_MALLOC); - if (new_ptr) { - CHECK(REAL(memcpy) != 0); - REAL(memcpy)(new_ptr, old_ptr, memcpy_size); - Deallocate(old_ptr, stack, FROM_MALLOC); - } - return new_ptr; -} - -} // namespace __asan - -#if !SANITIZER_SUPPORTS_WEAK_HOOKS -// Provide default (no-op) implementation of malloc hooks. -extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE -void __asan_malloc_hook(void *ptr, uptr size) { - (void)ptr; - (void)size; -} -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE -void __asan_free_hook(void *ptr) { - (void)ptr; -} -} // extern "C" -#endif - -namespace __asan { - -void InitializeAllocator() { } - -void PrintInternalAllocatorStats() { -} - -SANITIZER_INTERFACE_ATTRIBUTE -void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, - AllocType alloc_type) { - void *ptr = (void*)Allocate(alignment, size, stack, alloc_type); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; -} - -SANITIZER_INTERFACE_ATTRIBUTE -void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { - ASAN_FREE_HOOK(ptr); - Deallocate((u8*)ptr, stack, alloc_type); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void *asan_malloc(uptr size, StackTrace *stack) { - void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; -} - -void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { - if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; - void *ptr = (void*)Allocate(0, nmemb * size, stack, FROM_MALLOC); - if (ptr) - REAL(memset)(ptr, 0, nmemb * size); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; -} - -void *asan_realloc(void *p, uptr size, StackTrace *stack) { - if (p == 0) { - void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; - } else if (size == 0) { - ASAN_FREE_HOOK(p); - Deallocate((u8*)p, stack, FROM_MALLOC); - return 0; - } - return Reallocate((u8*)p, size, stack); -} - -void *asan_valloc(uptr size, StackTrace *stack) { - void *ptr = (void*)Allocate(GetPageSizeCached(), size, stack, FROM_MALLOC); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; -} - -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; - } - void *ptr = (void*)Allocate(PageSize, size, stack, FROM_MALLOC); - ASAN_MALLOC_HOOK(ptr, size); - return ptr; -} - -int asan_posix_memalign(void **memptr, uptr alignment, uptr size, - StackTrace *stack) { - void *ptr = Allocate(alignment, size, stack, FROM_MALLOC); - CHECK(IsAligned((uptr)ptr, alignment)); - ASAN_MALLOC_HOOK(ptr, size); - *memptr = ptr; - return 0; -} - -uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) { - CHECK(stack); - if (ptr == 0) return 0; - uptr usable_size = malloc_info.AllocationSize((uptr)ptr); - if (flags()->check_malloc_usable_size && (usable_size == 0)) { - ReportMallocUsableSizeNotOwned((uptr)ptr, stack); - } - return usable_size; -} - -uptr asan_mz_size(const void *ptr) { - return malloc_info.AllocationSize((uptr)ptr); -} - -void asan_mz_force_lock() { - malloc_info.ForceLock(); -} - -void asan_mz_force_unlock() { - malloc_info.ForceUnlock(); -} - -} // namespace __asan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -// ASan allocator doesn't reserve extra bytes, so normally we would -// just return "size". -uptr __asan_get_estimated_allocated_size(uptr size) { - if (size == 0) return 1; - return Min(size, kMaxAllowedMallocSize); -} - -bool __asan_get_ownership(const void *p) { - return malloc_info.AllocationSize((uptr)p) > 0; -} - -uptr __asan_get_allocated_size(const void *p) { - if (p == 0) return 0; - uptr allocated_size = malloc_info.AllocationSize((uptr)p); - // Die if p is not malloced or if it is already freed. - if (allocated_size == 0) { - GET_STACK_TRACE_FATAL_HERE; - ReportAsanGetAllocatedSizeNotOwned((uptr)p, &stack); - } - return allocated_size; -} -#endif // ASAN_ALLOCATOR_VERSION diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h index df2f520c41a..1f83dcd6780 100644 --- a/libsanitizer/asan/asan_allocator.h +++ b/libsanitizer/asan/asan_allocator.h @@ -7,7 +7,7 @@ // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_allocator.cc. +// ASan-private header for asan_allocator2.cc. //===----------------------------------------------------------------------===// #ifndef ASAN_ALLOCATOR_H @@ -17,18 +17,6 @@ #include "asan_interceptors.h" #include "sanitizer_common/sanitizer_list.h" -// We are in the process of transitioning from the old allocator (version 1) -// to a new one (version 2). The change is quite intrusive so both allocators -// will co-exist in the source base for a while. The actual allocator is chosen -// at build time by redefining this macro. -#ifndef ASAN_ALLOCATOR_VERSION -# if (ASAN_LINUX && !ASAN_ANDROID) || ASAN_MAC || ASAN_WINDOWS -# define ASAN_ALLOCATOR_VERSION 2 -# else -# define ASAN_ALLOCATOR_VERSION 1 -# endif -#endif // ASAN_ALLOCATOR_VERSION - namespace __asan { enum AllocType { @@ -101,109 +89,17 @@ class AsanChunkFifoList: public IntrusiveList { struct AsanThreadLocalMallocStorage { explicit AsanThreadLocalMallocStorage(LinkerInitialized x) -#if ASAN_ALLOCATOR_VERSION == 1 - : quarantine_(x) -#endif { } AsanThreadLocalMallocStorage() { CHECK(REAL(memset)); REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage)); } -#if ASAN_ALLOCATOR_VERSION == 1 - AsanChunkFifoList quarantine_; - AsanChunk *free_lists_[kNumberOfSizeClasses]; -#else uptr quarantine_cache[16]; uptr allocator2_cache[96 * (512 * 8 + 16)]; // Opaque. -#endif void CommitBack(); }; -// Fake stack frame contains local variables of one function. -// This struct should fit into a stack redzone (32 bytes). -struct FakeFrame { - uptr magic; // Modified by the instrumented code. - uptr descr; // Modified by the instrumented code. - FakeFrame *next; - u64 real_stack : 48; - u64 size_minus_one : 16; -}; - -struct FakeFrameFifo { - public: - void FifoPush(FakeFrame *node); - FakeFrame *FifoPop(); - private: - FakeFrame *first_, *last_; -}; - -class FakeFrameLifo { - public: - void LifoPush(FakeFrame *node) { - node->next = top_; - top_ = node; - } - void LifoPop() { - CHECK(top_); - top_ = top_->next; - } - FakeFrame *top() { return top_; } - private: - FakeFrame *top_; -}; - -// For each thread we create a fake stack and place stack objects on this fake -// stack instead of the real stack. The fake stack is not really a stack but -// a fast malloc-like allocator so that when a function exits the fake stack -// is not poped but remains there for quite some time until gets used again. -// So, we poison the objects on the fake stack when function returns. -// It helps us find use-after-return bugs. -// We can not rely on __asan_stack_free being called on every function exit, -// so we maintain a lifo list of all current fake frames and update it on every -// call to __asan_stack_malloc. -class FakeStack { - public: - FakeStack(); - explicit FakeStack(LinkerInitialized) {} - void Init(uptr stack_size); - void StopUsingFakeStack() { alive_ = false; } - void Cleanup(); - uptr AllocateStack(uptr size, uptr real_stack); - static void OnFree(uptr ptr, uptr size, uptr real_stack); - // Return the bottom of the maped region. - uptr AddrIsInFakeStack(uptr addr); - bool StackSize() { return stack_size_; } - - private: - static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B. - static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K. - static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; - static const uptr kNumberOfSizeClasses = - kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1; - - bool AddrIsInSizeClass(uptr addr, uptr size_class); - - // Each size class should be large enough to hold all frames. - uptr ClassMmapSize(uptr size_class); - - uptr ClassSize(uptr size_class) { - return 1UL << (size_class + kMinStackFrameSizeLog); - } - - void DeallocateFrame(FakeFrame *fake_frame); - - uptr ComputeSizeClass(uptr alloc_size); - void AllocateOneSizeClass(uptr size_class); - - uptr stack_size_; - bool alive_; - - uptr allocated_size_classes_[kNumberOfSizeClasses]; - FakeFrameFifo size_classes_[kNumberOfSizeClasses]; - FakeFrameLifo call_stack_; -}; - void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, AllocType alloc_type); void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type); diff --git a/libsanitizer/asan/asan_allocator2.cc b/libsanitizer/asan/asan_allocator2.cc index 1ff120e555c..34aad11ed75 100644 --- a/libsanitizer/asan/asan_allocator2.cc +++ b/libsanitizer/asan/asan_allocator2.cc @@ -11,20 +11,20 @@ // This variant uses the allocator from sanitizer_common, i.e. the one shared // with ThreadSanitizer and MemorySanitizer. // -// Status: under development, not enabled by default yet. //===----------------------------------------------------------------------===// #include "asan_allocator.h" -#if ASAN_ALLOCATOR_VERSION == 2 #include "asan_mapping.h" +#include "asan_poisoning.h" #include "asan_report.h" #include "asan_thread.h" -#include "asan_thread_registry.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 { @@ -32,7 +32,7 @@ struct AsanMapUnmapCallback { void OnMap(uptr p, uptr size) const { PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.mmaps++; thread_stats.mmaped += size; } @@ -47,7 +47,7 @@ struct AsanMapUnmapCallback { uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size); FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.munmaps++; thread_stats.munmaped += size; } @@ -56,18 +56,23 @@ struct AsanMapUnmapCallback { #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 -const uptr kAllocatorSize = 0x10000000000ULL; // 1T. typedef DefaultSizeClassMap SizeClassMap; typedef SizeClassAllocator64 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, AsanMapUnmapCallback> PrimaryAllocator; + SizeClassMap, kRegionSizeLog, + FlatByteMap, + AsanMapUnmapCallback> PrimaryAllocator; #endif typedef SizeClassAllocatorLocalCache AllocatorCache; @@ -139,14 +144,15 @@ static uptr ComputeRZLog(uptr user_requested_size) { // ChunkBase consists of ChunkHeader and other bytes that overlap with user // memory. -// If a memory chunk is allocated by memalign and we had to increase the -// allocation size to achieve the proper alignment, then we store this magic +// 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 H H U U U U U U -// M -- magic value kMemalignMagic +// 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 kMemalignMagic = 0xCC6E96B9; +static const uptr kAllocBegMagic = 0xCC6E96B9; struct ChunkHeader { // 1-st 8 bytes. @@ -157,6 +163,7 @@ struct ChunkHeader { 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 @@ -167,7 +174,6 @@ struct ChunkHeader { struct ChunkBase : ChunkHeader { // Header2, intersects with user memory. - AsanChunk *next; u32 free_context_id; }; @@ -188,7 +194,8 @@ struct AsanChunk: ChunkBase { return allocator.GetBlockBegin(reinterpret_cast(this)); return reinterpret_cast(Beg() - RZLog2Size(rz_log)); } - // We store the alloc/free stack traces in the chunk itself. + // 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)); } @@ -204,6 +211,9 @@ struct AsanChunk: ChunkBase { uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY); return (available - kChunkHeader2Size) / sizeof(u32); } + bool AddrIsInside(uptr addr) { + return (addr >= Beg()) && (addr < Beg() + UsedSize()); + } }; uptr AsanChunkView::Beg() { return chunk_->Beg(); } @@ -257,22 +267,25 @@ struct QuarantineCallback { } void Recycle(AsanChunk *m) { - CHECK(m->chunk_state == CHUNK_QUARANTINE); - m->chunk_state = CHUNK_AVAILABLE; + 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(m->AllocBeg()); - if (m->from_memalign) { - uptr *memalign_magic = reinterpret_cast(p); - CHECK_EQ(memalign_magic[0], kMemalignMagic); - CHECK_EQ(memalign_magic[1], reinterpret_cast(m)); + if (p != m) { + uptr *alloc_magic = reinterpret_cast(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(m)); } // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.real_frees++; thread_stats.really_freed += m->UsedSize(); @@ -296,9 +309,10 @@ void InitializeAllocator() { } static void *Allocate(uptr size, uptr alignment, StackTrace *stack, - AllocType alloc_type) { + 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) @@ -314,9 +328,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, CHECK(IsPowerOfTwo(alignment)); uptr rz_log = ComputeRZLog(size); uptr rz_size = RZLog2Size(rz_log); - uptr rounded_size = RoundUpTo(size, alignment); - if (rounded_size < kChunkHeader2Size) - rounded_size = kChunkHeader2Size; + uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); uptr needed_size = rounded_size + rz_size; if (alignment > min_alignment) needed_size += alignment; @@ -331,10 +343,10 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", (void*)size); - return 0; + return AllocatorReturnNull(); } - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); void *allocated; if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); @@ -345,8 +357,6 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, allocated = allocator.Allocate(cache, needed_size, 8, false); } uptr alloc_beg = reinterpret_cast(allocated); - // Clear the first allocated word (an old kMemalignMagic may still be there). - reinterpret_cast(alloc_beg)[0] = 0; uptr alloc_end = alloc_beg + needed_size; uptr beg_plus_redzone = alloc_beg + rz_size; uptr user_beg = beg_plus_redzone; @@ -356,7 +366,6 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, CHECK_LE(user_end, alloc_end); uptr chunk_beg = user_beg - kChunkHeaderSize; AsanChunk *m = reinterpret_cast(chunk_beg); - m->chunk_state = CHUNK_ALLOCATED; m->alloc_type = alloc_type; m->rz_log = rz_log; u32 alloc_tid = t ? t->tid() : 0; @@ -364,11 +373,10 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, 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 (m->from_memalign) { - CHECK_LE(beg_plus_redzone + 2 * sizeof(uptr), user_beg); - uptr *memalign_magic = reinterpret_cast(alloc_beg); - memalign_magic[0] = kMemalignMagic; - memalign_magic[1] = chunk_beg; + if (alloc_beg != chunk_beg) { + CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); + reinterpret_cast(alloc_beg)[0] = kAllocBegMagic; + reinterpret_cast(alloc_beg)[1] = chunk_beg; } if (using_primary_allocator) { CHECK(size); @@ -382,7 +390,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, meta[1] = chunk_beg; } - if (flags()->use_stack_depot) { + if (fl.use_stack_depot) { m->alloc_context_id = StackDepotPut(stack->trace, stack->size); } else { m->alloc_context_id = 0; @@ -394,12 +402,12 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, 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 && flags()->poison_heap) { + if (size != size_rounded_down_to_granularity && fl.poison_heap) { u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity); *shadow = size & (SHADOW_GRANULARITY - 1); } - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.mallocs++; thread_stats.malloced += size; thread_stats.malloced_redzones += needed_size - size; @@ -409,26 +417,43 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, thread_stats.malloc_large++; void *res = reinterpret_cast(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 Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { - uptr p = reinterpret_cast(ptr); - if (p == 0) return; - ASAN_FREE_HOOK(ptr); - uptr chunk_beg = p - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast(chunk_beg); - - // Flip the chunk_state atomically to avoid race on double-free. - u8 old_chunk_state = atomic_exchange((atomic_uint8_t*)m, CHUNK_QUARANTINE, - memory_order_relaxed); - - if (old_chunk_state == CHUNK_QUARANTINE) +static void ReportInvalidFree(void *ptr, u8 chunk_state, StackTrace *stack) { + if (chunk_state == CHUNK_QUARANTINE) ReportDoubleFree((uptr)ptr, stack); - else if (old_chunk_state != CHUNK_ALLOCATED) + else ReportFreeNotMalloced((uptr)ptr, stack); - CHECK(old_chunk_state == CHUNK_ALLOCATED); +} + +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); @@ -436,7 +461,7 @@ static void Deallocate(void *ptr, StackTrace *stack, 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 = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); m->free_tid = t ? t->tid() : 0; if (flags()->use_stack_depot) { m->free_context_id = StackDepotPut(stack->trace, stack->size); @@ -444,13 +469,12 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { m->free_context_id = 0; StackTrace::CompressStack(stack, m->FreeStackBeg(), m->FreeStackSize()); } - CHECK(m->chunk_state == CHUNK_QUARANTINE); // Poison the region. PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), kAsanHeapFreeMagic); - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.frees++; thread_stats.freed += m->UsedSize(); @@ -468,57 +492,67 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { } } +static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { + uptr p = reinterpret_cast(ptr); + if (p == 0) return; + + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(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(old_ptr); uptr chunk_beg = p - kChunkHeaderSize; AsanChunk *m = reinterpret_cast(chunk_beg); - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.reallocs++; thread_stats.realloced += new_size; - CHECK(m->chunk_state == CHUNK_ALLOCATED); - uptr old_size = m->UsedSize(); - uptr memcpy_size = Min(new_size, old_size); - void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC); + void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); if (new_ptr) { - CHECK(REAL(memcpy) != 0); + 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; } -static AsanChunk *GetAsanChunkByAddr(uptr p) { - void *ptr = reinterpret_cast(p); - uptr alloc_beg = reinterpret_cast(allocator.GetBlockBegin(ptr)); +// Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). +static AsanChunk *GetAsanChunk(void *alloc_beg) { if (!alloc_beg) return 0; - uptr *memalign_magic = reinterpret_cast(alloc_beg); - if (memalign_magic[0] == kMemalignMagic) { - AsanChunk *m = reinterpret_cast(memalign_magic[1]); - CHECK(m->from_memalign); - return m; - } - if (!allocator.FromPrimary(ptr)) { - uptr *meta = reinterpret_cast( - allocator.GetMetaData(reinterpret_cast(alloc_beg))); + if (!allocator.FromPrimary(alloc_beg)) { + uptr *meta = reinterpret_cast(allocator.GetMetaData(alloc_beg)); AsanChunk *m = reinterpret_cast(meta[1]); return m; } - uptr actual_size = allocator.GetActuallyAllocatedSize(ptr); - CHECK_LE(actual_size, SizeClassMap::kMaxSize); - // We know the actually allocted size, but we don't know the redzone size. - // Just try all possible redzone sizes. - for (u32 rz_log = 0; rz_log < 8; rz_log++) { - u32 rz_size = RZLog2Size(rz_log); - uptr max_possible_size = actual_size - rz_size; - if (ComputeRZLog(max_possible_size) != rz_log) - continue; - return reinterpret_cast( - alloc_beg + rz_size - kChunkHeaderSize); - } - return 0; + uptr *alloc_magic = reinterpret_cast(alloc_beg); + if (alloc_magic[0] == kAllocBegMagic) + return reinterpret_cast(alloc_magic[1]); + return reinterpret_cast(alloc_beg); +} + +static AsanChunk *GetAsanChunkByAddr(uptr p) { + void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast(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(p)); + return GetAsanChunk(alloc_beg); } static uptr AllocationSize(uptr p) { @@ -583,33 +617,33 @@ void PrintInternalAllocatorStats() { allocator.PrintStats(); } -SANITIZER_INTERFACE_ATTRIBUTE void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, AllocType alloc_type) { - return Allocate(size, alignment, stack, alloc_type); + return Allocate(size, alignment, stack, alloc_type, true); } -SANITIZER_INTERFACE_ATTRIBUTE void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { Deallocate(ptr, stack, alloc_type); } -SANITIZER_INTERFACE_ATTRIBUTE void *asan_malloc(uptr size, StackTrace *stack) { - return Allocate(size, 8, stack, FROM_MALLOC); + return Allocate(size, 8, stack, FROM_MALLOC, true); } void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; - void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC); - if (ptr) + 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); + return Allocate(size, 8, stack, FROM_MALLOC, true); if (size == 0) { Deallocate(p, stack, FROM_MALLOC); return 0; @@ -618,7 +652,7 @@ void *asan_realloc(void *p, uptr size, StackTrace *stack) { } void *asan_valloc(uptr size, StackTrace *stack) { - return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC); + return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true); } void *asan_pvalloc(uptr size, StackTrace *stack) { @@ -628,12 +662,12 @@ void *asan_pvalloc(uptr size, StackTrace *stack) { // pvalloc(0) should allocate one page. size = PageSize; } - return Allocate(size, PageSize, stack, FROM_MALLOC); + 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); + void *ptr = Allocate(size, alignment, stack, FROM_MALLOC, true); CHECK(IsAligned((uptr)ptr, alignment)); *memptr = ptr; return 0; @@ -664,6 +698,86 @@ void asan_mz_force_unlock() { } // 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(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)) + 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(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(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(); +} + +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(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 @@ -693,17 +807,14 @@ uptr __asan_get_allocated_size(const void *p) { #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default (no-op) implementation of malloc hooks. extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __asan_malloc_hook(void *ptr, uptr size) { (void)ptr; (void)size; } -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __asan_free_hook(void *ptr) { (void)ptr; } } // extern "C" #endif - - -#endif // ASAN_ALLOCATOR_VERSION diff --git a/libsanitizer/asan/asan_dll_thunk.cc b/libsanitizer/asan/asan_dll_thunk.cc new file mode 100644 index 00000000000..26e19441523 --- /dev/null +++ b/libsanitizer/asan/asan_dll_thunk.cc @@ -0,0 +1,194 @@ +//===-- 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_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 diff --git a/libsanitizer/asan/asan_fake_stack.cc b/libsanitizer/asan/asan_fake_stack.cc index 1fc0415750b..b9cce88f34f 100644 --- a/libsanitizer/asan/asan_fake_stack.cc +++ b/libsanitizer/asan/asan_fake_stack.cc @@ -10,170 +10,195 @@ // FakeStack is used to detect use-after-return bugs. //===----------------------------------------------------------------------===// #include "asan_allocator.h" +#include "asan_poisoning.h" #include "asan_thread.h" -#include "asan_thread_registry.h" namespace __asan { -FakeStack::FakeStack() { - CHECK(REAL(memset) != 0); - REAL(memset)(this, 0, sizeof(*this)); -} +static const u64 kMagic1 = kAsanStackAfterReturnMagic; +static const u64 kMagic2 = (kMagic1 << 8) | kMagic1; +static const u64 kMagic4 = (kMagic2 << 16) | kMagic2; +static const u64 kMagic8 = (kMagic4 << 32) | kMagic4; -bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) { - uptr mem = allocated_size_classes_[size_class]; - uptr size = ClassMmapSize(size_class); - bool res = mem && addr >= mem && addr < mem + size; - return res; -} - -uptr FakeStack::AddrIsInFakeStack(uptr addr) { - for (uptr i = 0; i < kNumberOfSizeClasses; i++) { - if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i]; - } - return 0; -} - -// We may want to compute this during compilation. -inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) { - uptr rounded_size = RoundUpToPowerOfTwo(alloc_size); - uptr log = Log2(rounded_size); - CHECK(alloc_size <= (1UL << log)); - if (!(alloc_size > (1UL << (log-1)))) { - Printf("alloc_size %zu log %zu\n", alloc_size, log); - } - CHECK(alloc_size > (1UL << (log-1))); - uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog; - CHECK(res < kNumberOfSizeClasses); - CHECK(ClassSize(res) >= rounded_size); - return res; -} - -void FakeFrameFifo::FifoPush(FakeFrame *node) { - CHECK(node); - node->next = 0; - if (first_ == 0 && last_ == 0) { - first_ = last_ = node; +// 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(MemToShadow(ptr)); + if (class_id <= 6) { + for (uptr i = 0; i < (1U << class_id); i++) + shadow[i] = magic; } else { - CHECK(first_); - CHECK(last_); - last_->next = node; - last_ = node; + // The size class is too big, it's cheaper to poison only size bytes. + PoisonShadow(ptr, size, static_cast(magic)); } } -FakeFrame *FakeFrameFifo::FifoPop() { - CHECK(first_ && last_ && "Exhausted fake stack"); - FakeFrame *res = 0; - if (first_ == last_) { - res = first_; - first_ = last_ = 0; - } else { - res = first_; - first_ = first_->next; +FakeStack *FakeStack::Create(uptr stack_size_log) { + static uptr kMinStackSizeLog = 16; + static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28); + if (stack_size_log < kMinStackSizeLog) + stack_size_log = kMinStackSizeLog; + if (stack_size_log > kMaxStackSizeLog) + stack_size_log = kMaxStackSizeLog; + FakeStack *res = reinterpret_cast( + MmapOrDie(RequiredSize(stack_size_log), "FakeStack")); + res->stack_size_log_ = stack_size_log; + if (flags()->verbosity) { + u8 *p = reinterpret_cast(res); + Report("T%d: FakeStack created: %p -- %p stack_size_log: %zd \n", + GetCurrentTidOrInvalid(), p, + p + FakeStack::RequiredSize(stack_size_log), stack_size_log); } return res; } -void FakeStack::Init(uptr stack_size) { - stack_size_ = stack_size; - alive_ = true; +void FakeStack::Destroy() { + PoisonAll(0); + UnmapOrDie(this, RequiredSize(stack_size_log_)); } -void FakeStack::Cleanup() { - alive_ = false; - for (uptr i = 0; i < kNumberOfSizeClasses; i++) { - uptr mem = allocated_size_classes_[i]; - if (mem) { - PoisonShadow(mem, ClassMmapSize(i), 0); - allocated_size_classes_[i] = 0; - UnmapOrDie((void*)mem, ClassMmapSize(i)); +void FakeStack::PoisonAll(u8 magic) { + PoisonShadow(reinterpret_cast(this), RequiredSize(stack_size_log()), + magic); +} + +ALWAYS_INLINE USED +FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, + uptr real_stack) { + CHECK_LT(class_id, kNumberOfSizeClasses); + if (needs_gc_) + GC(real_stack); + uptr &hint_position = hint_position_[class_id]; + const int num_iter = NumberOfFrames(stack_size_log, class_id); + u8 *flags = GetFlags(stack_size_log, class_id); + for (int i = 0; i < num_iter; i++) { + uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++); + // This part is tricky. On one hand, checking and setting flags[pos] + // should be atomic to ensure async-signal safety. But on the other hand, + // if the signal arrives between checking and setting flags[pos], the + // signal handler's fake stack will start from a different hint_position + // and so will not touch this particular byte. So, it is safe to do this + // with regular non-atimic load and store (at least I was not able to make + // this code crash). + if (flags[pos]) continue; + flags[pos] = 1; + FakeFrame *res = reinterpret_cast( + GetFrame(stack_size_log, class_id, pos)); + res->real_stack = real_stack; + *SavedFlagPtr(reinterpret_cast(res), class_id) = &flags[pos]; + return res; + } + return 0; // We are out of fake stack. +} + +uptr FakeStack::AddrIsInFakeStack(uptr ptr) { + uptr stack_size_log = this->stack_size_log(); + uptr beg = reinterpret_cast(GetFrame(stack_size_log, 0, 0)); + uptr end = reinterpret_cast(this) + RequiredSize(stack_size_log); + if (ptr < beg || ptr >= end) return 0; + uptr class_id = (ptr - beg) >> stack_size_log; + uptr base = beg + (class_id << 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); +} + +void FakeStack::HandleNoReturn() { + needs_gc_ = true; +} + +// When throw, longjmp or some such happens we don't call OnFree() and +// as the result may leak one or more fake frames, but the good news is that +// we are notified about all such events by HandleNoReturn(). +// If we recently had such no-return event we need to collect garbage frames. +// We do it based on their 'real_stack' values -- everything that is lower +// than the current real_stack is garbage. +NOINLINE void FakeStack::GC(uptr real_stack) { + uptr collected = 0; + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { + u8 *flags = GetFlags(stack_size_log(), class_id); + for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; + i++) { + if (flags[i] == 0) continue; // not allocated. + FakeFrame *ff = reinterpret_cast( + GetFrame(stack_size_log(), class_id, i)); + if (ff->real_stack < real_stack) { + flags[i] = 0; + collected++; + } } } + needs_gc_ = false; } -uptr FakeStack::ClassMmapSize(uptr size_class) { - return RoundUpToPowerOfTwo(stack_size_); +#if SANITIZER_LINUX && !SANITIZER_ANDROID +static THREADLOCAL FakeStack *fake_stack_tls; + +FakeStack *GetTLSFakeStack() { + return fake_stack_tls; +} +void SetTLSFakeStack(FakeStack *fs) { + fake_stack_tls = fs; +} +#else +FakeStack *GetTLSFakeStack() { return 0; } +void SetTLSFakeStack(FakeStack *fs) { } +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID + +static FakeStack *GetFakeStack() { + AsanThread *t = GetCurrentThread(); + if (!t) return 0; + return t->fake_stack(); } -void FakeStack::AllocateOneSizeClass(uptr size_class) { - CHECK(ClassMmapSize(size_class) >= GetPageSizeCached()); - uptr new_mem = (uptr)MmapOrDie( - ClassMmapSize(size_class), __FUNCTION__); - // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n", - // asanThreadRegistry().GetCurrent()->tid(), - // size_class, new_mem, new_mem + ClassMmapSize(size_class), - // ClassMmapSize(size_class)); - uptr i; - for (i = 0; i < ClassMmapSize(size_class); - i += ClassSize(size_class)) { - size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i)); - } - CHECK(i == ClassMmapSize(size_class)); - allocated_size_classes_[size_class] = new_mem; +static FakeStack *GetFakeStackFast() { + if (FakeStack *fs = GetTLSFakeStack()) + return fs; + if (!__asan_option_detect_stack_use_after_return) + return 0; + return GetFakeStack(); } -uptr FakeStack::AllocateStack(uptr size, uptr real_stack) { - if (!alive_) return real_stack; - CHECK(size <= kMaxStackMallocSize && size > 1); - uptr size_class = ComputeSizeClass(size); - if (!allocated_size_classes_[size_class]) { - AllocateOneSizeClass(size_class); - } - FakeFrame *fake_frame = size_classes_[size_class].FifoPop(); - CHECK(fake_frame); - fake_frame->size_minus_one = size - 1; - fake_frame->real_stack = real_stack; - while (FakeFrame *top = call_stack_.top()) { - if (top->real_stack > real_stack) break; - call_stack_.LifoPop(); - DeallocateFrame(top); - } - call_stack_.LifoPush(fake_frame); - uptr ptr = (uptr)fake_frame; - PoisonShadow(ptr, size, 0); +ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) { + FakeStack *fs = GetFakeStackFast(); + if (!fs) return real_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. + uptr ptr = reinterpret_cast(ff); + SetShadow(ptr, size, class_id, 0); return ptr; } -void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { - CHECK(alive_); - uptr size = fake_frame->size_minus_one + 1; - uptr size_class = ComputeSizeClass(size); - CHECK(allocated_size_classes_[size_class]); - uptr ptr = (uptr)fake_frame; - CHECK(AddrIsInSizeClass(ptr, size_class)); - CHECK(AddrIsInSizeClass(ptr + size - 1, size_class)); - size_classes_[size_class].FifoPush(fake_frame); -} - -void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) { - FakeFrame *fake_frame = (FakeFrame*)ptr; - CHECK(fake_frame->magic = kRetiredStackFrameMagic); - CHECK(fake_frame->descr != 0); - CHECK(fake_frame->size_minus_one == size - 1); - PoisonShadow(ptr, size, kAsanStackAfterReturnMagic); +ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) { + if (ptr == real_stack) + return; + FakeStack::Deallocate(ptr, class_id); + SetShadow(ptr, size, class_id, kMagic8); } } // namespace __asan // ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -uptr __asan_stack_malloc(uptr size, uptr real_stack) { - if (!flags()->use_fake_stack) return real_stack; - AsanThread *t = asanThreadRegistry().GetCurrent(); - if (!t) { - // TSD is gone, use the real stack. - return real_stack; +#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); \ + } \ + 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 = t->fake_stack().AllocateStack(size, real_stack); - // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack); - return ptr; -} -void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) { - if (!flags()->use_fake_stack) return; - if (ptr != real_stack) { - FakeStack::OnFree(ptr, size, real_stack); - } -} +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10) diff --git a/libsanitizer/asan/asan_fake_stack.h b/libsanitizer/asan/asan_fake_stack.h new file mode 100644 index 00000000000..4287497fd5d --- /dev/null +++ b/libsanitizer/asan/asan_fake_stack.h @@ -0,0 +1,167 @@ +//===-- asan_fake_stack.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_fake_stack.cc, implements FakeStack. +//===----------------------------------------------------------------------===// + +#ifndef ASAN_FAKE_STACK_H +#define ASAN_FAKE_STACK_H + +#include "sanitizer_common/sanitizer_common.h" + +namespace __asan { + +// Fake stack frame contains local variables of one function. +struct FakeFrame { + uptr magic; // Modified by the instrumented code. + uptr descr; // Modified by the instrumented code. + uptr pc; // Modified by the instrumented code. + uptr real_stack; +}; + +// For each thread we create a fake stack and place stack objects on this fake +// stack instead of the real stack. The fake stack is not really a stack but +// a fast malloc-like allocator so that when a function exits the fake stack +// is not popped but remains there for quite some time until gets used again. +// So, we poison the objects on the fake stack when function returns. +// It helps us find use-after-return bugs. +// +// The FakeStack objects is allocated by a single mmap call and has no other +// pointers. The size of the fake stack depends on the actual thread stack size +// and thus can not be a constant. +// stack_size is a power of two greater or equal to the thread's stack size; +// we store it as its logarithm (stack_size_log). +// FakeStack has kNumberOfSizeClasses (11) size classes, each size class +// is a power of two, starting from 64 bytes. Each size class occupies +// stack_size bytes and thus can allocate +// NumberOfFrames=(stack_size/BytesInSizeClass) fake frames (also a power of 2). +// For each size class we have NumberOfFrames allocation flags, +// each flag indicates whether the given frame is currently allocated. +// All flags for size classes 0 .. 10 are stored in a single contiguous region +// followed by another contiguous region which contains the actual memory for +// size classes. The addresses are computed by GetFlags and GetFrame without +// any memory accesses solely based on 'this' and stack_size_log. +// Allocate() flips the appropriate allocation flag atomically, thus achieving +// async-signal safety. +// This allocator does not have quarantine per se, but it tries to allocate the +// frames in round robin fasion to maximize the delay between a deallocation +// and the next allocation. +class FakeStack { + static const uptr kMinStackFrameSizeLog = 6; // Min frame is 64B. + static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K. + + public: + static const uptr kNumberOfSizeClasses = + kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1; + + // CTOR: create the FakeStack as a single mmap-ed object. + static FakeStack *Create(uptr stack_size_log); + + void Destroy(); + + // stack_size_log is at least 15 (stack_size >= 32K). + static uptr SizeRequiredForFlags(uptr stack_size_log) { + return 1UL << (stack_size_log + 1 - kMinStackFrameSizeLog); + } + + // Each size class occupies stack_size bytes. + static uptr SizeRequiredForFrames(uptr stack_size_log) { + return (1ULL << stack_size_log) * kNumberOfSizeClasses; + } + + // Number of bytes requires for the whole object. + static uptr RequiredSize(uptr stack_size_log) { + return kFlagsOffset + SizeRequiredForFlags(stack_size_log) + + SizeRequiredForFrames(stack_size_log); + } + + // Offset of the given flag from the first flag. + // The flags for class 0 begin at offset 000000000 + // The flags for class 1 begin at offset 100000000 + // ....................2................ 110000000 + // ....................3................ 111000000 + // and so on. + static uptr FlagsOffset(uptr stack_size_log, uptr class_id) { + uptr t = kNumberOfSizeClasses - 1 - class_id; + const uptr all_ones = (1 << (kNumberOfSizeClasses - 1)) - 1; + return ((all_ones >> t) << t) << (stack_size_log - 15); + } + + static uptr NumberOfFrames(uptr stack_size_log, uptr class_id) { + return 1UL << (stack_size_log - kMinStackFrameSizeLog - class_id); + } + + // Divide n by the numbe of frames in size class. + static uptr ModuloNumberOfFrames(uptr stack_size_log, uptr class_id, uptr n) { + return n & (NumberOfFrames(stack_size_log, class_id) - 1); + } + + // The the pointer to the flags of the given class_id. + u8 *GetFlags(uptr stack_size_log, uptr class_id) { + return reinterpret_cast(this) + kFlagsOffset + + FlagsOffset(stack_size_log, class_id); + } + + // Get frame by class_id and pos. + u8 *GetFrame(uptr stack_size_log, uptr class_id, uptr pos) { + return reinterpret_cast(this) + kFlagsOffset + + SizeRequiredForFlags(stack_size_log) + + (1 << stack_size_log) * class_id + BytesInSizeClass(class_id) * pos; + } + + // Allocate the fake frame. + FakeFrame *Allocate(uptr stack_size_log, uptr class_id, uptr real_stack); + + // Deallocate the fake frame: read the saved flag address and write 0 there. + static void Deallocate(uptr x, uptr class_id) { + **SavedFlagPtr(x, class_id) = 0; + } + + // Poison the entire FakeStack's shadow with the magic value. + void PoisonAll(u8 magic); + + // Return the beginning of the FakeFrame or 0 if the address is not ours. + uptr AddrIsInFakeStack(uptr addr); + + // Number of bytes in a fake frame of this size class. + static uptr BytesInSizeClass(uptr class_id) { + return 1UL << (class_id + kMinStackFrameSizeLog); + } + + // The fake frame is guaranteed to have a right redzone. + // We use the last word of that redzone to store the address of the flag + // that corresponds to the current frame to make faster deallocation. + static u8 **SavedFlagPtr(uptr x, uptr class_id) { + return reinterpret_cast(x + BytesInSizeClass(class_id) - sizeof(x)); + } + + uptr stack_size_log() const { return stack_size_log_; } + + void HandleNoReturn(); + void GC(uptr real_stack); + + private: + FakeStack() { } + static const uptr kFlagsOffset = 4096; // This is were the flags begin. + // Must match the number of uses of DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID + COMPILER_CHECK(kNumberOfSizeClasses == 11); + static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; + + uptr hint_position_[kNumberOfSizeClasses]; + uptr stack_size_log_; + // a bit is set if something was allocated from the corresponding size class. + bool needs_gc_; +}; + +FakeStack *GetTLSFakeStack(); +void SetTLSFakeStack(FakeStack *fs); + +} // namespace __asan + +#endif // ASAN_FAKE_STACK_H diff --git a/libsanitizer/asan/asan_flags.h b/libsanitizer/asan/asan_flags.h index b880896c7a3..c115997ff29 100644 --- a/libsanitizer/asan/asan_flags.h +++ b/libsanitizer/asan/asan_flags.h @@ -30,8 +30,6 @@ struct Flags { // Lower value may reduce memory usage but increase the chance of // false negatives. int quarantine_size; - // If set, uses in-process symbolizer from common sanitizer runtime. - bool symbolize; // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). int verbosity; // Size (in bytes) of redzones around heap objects. @@ -45,8 +43,6 @@ struct Flags { int report_globals; // If set, attempts to catch initialization order issues. bool check_initialization_order; - // Max number of stack frames kept for each allocation/deallocation. - int malloc_context_size; // If set, uses custom wrappers and replacements for libc string functions // to find more errors. bool replace_str; @@ -54,11 +50,13 @@ struct Flags { bool replace_intrin; // Used on Mac only. bool mac_ignore_invalid_free; - // ASan allocator flag. See asan_allocator.cc. - bool use_fake_stack; - // ASan allocator flag. Sets the maximal size of allocation request - // that would return memory filled with zero bytes. - int max_malloc_fill_size; + // 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. @@ -69,6 +67,8 @@ struct Flags { 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.*. @@ -89,18 +89,10 @@ struct Flags { // Allow the tool to re-exec the program. This may interfere badly with the // debugger. bool allow_reexec; - // Strips this prefix from file paths in error reports. - const char *strip_path_prefix; // 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; - // ASan will write logs to "log_path.pid" instead of stderr. - const char *log_path; - // 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; // Poison (or not) the heap memory on [de]allocation. Zero value is useful // for benchmarking the allocator or instrumentator. bool poison_heap; @@ -108,9 +100,18 @@ struct Flags { bool alloc_dealloc_mismatch; // Use stack depot instead of storing stacks in the redzones. bool use_stack_depot; + // 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; }; -Flags *flags(); +extern Flags asan_flags_dont_use_directly; +inline Flags *flags() { + return &asan_flags_dont_use_directly; +} void InitializeFlags(Flags *f, const char *env); } // namespace __asan diff --git a/libsanitizer/asan/asan_globals.cc b/libsanitizer/asan/asan_globals.cc index 7093c445588..96985af71a9 100644 --- a/libsanitizer/asan/asan_globals.cc +++ b/libsanitizer/asan/asan_globals.cc @@ -12,11 +12,14 @@ #include "asan_interceptors.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 "asan_thread.h" +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_placement_new.h" namespace __asan { @@ -30,15 +33,26 @@ struct ListOfGlobals { static BlockingMutex mu_for_globals(LINKER_INITIALIZED); static LowLevelAllocator allocator_for_globals; static ListOfGlobals *list_of_all_globals; -static ListOfGlobals *list_of_dynamic_init_globals; -void PoisonRedZones(const Global &g) { +static const int kDynamicInitGlobalsInitialCapacity = 512; +struct DynInitGlobal { + Global g; + bool initialized; +}; +typedef InternalMmapVector VectorOfGlobals; +// Lazy-initialized and never deleted. +static VectorOfGlobals *dynamic_init_globals; + +ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { + FastPoisonShadow(g->beg, g->size_with_redzone, value); +} + +ALWAYS_INLINE void PoisonRedZones(const Global &g) { uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); - PoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, - kAsanGlobalRedzoneMagic); + FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, + kAsanGlobalRedzoneMagic); if (g.size != aligned_size) { - // partial right redzone - PoisonShadowPartialRightRedzone( + FastPoisonShadowPartialRightRedzone( g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), g.size % SHADOW_GRANULARITY, SHADOW_GRANULARITY, @@ -46,6 +60,12 @@ void PoisonRedZones(const Global &g) { } } +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, + g.module_name, g.has_dynamic_init); +} + bool DescribeAddressIfGlobal(uptr addr, uptr size) { if (!flags()->report_globals) return false; BlockingMutexLock lock(&mu_for_globals); @@ -53,8 +73,7 @@ bool DescribeAddressIfGlobal(uptr addr, uptr size) { for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { const Global &g = *l->g; if (flags()->report_globals >= 2) - Report("Search Global: beg=%p size=%zu name=%s\n", - (void*)g.beg, g.size, (char*)g.name); + ReportGlobal(g, "Search"); res |= DescribeAddressRelativeToGlobal(addr, size, g); } return res; @@ -66,24 +85,26 @@ bool DescribeAddressIfGlobal(uptr addr, uptr size) { static void RegisterGlobal(const Global *g) { CHECK(asan_inited); if (flags()->report_globals >= 2) - Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n", - (void*)g->beg, g->size, g->size_with_redzone, g->name, - g->has_dynamic_init); + ReportGlobal(*g, "Added"); CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - PoisonRedZones(*g); + if (flags()->poison_heap) + PoisonRedZones(*g); ListOfGlobals *l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); l->g = g; l->next = list_of_all_globals; list_of_all_globals = l; if (g->has_dynamic_init) { - l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); - l->g = g; - l->next = list_of_dynamic_init_globals; - list_of_dynamic_init_globals = l; + if (dynamic_init_globals == 0) { + void *mem = allocator_for_globals.Allocate(sizeof(VectorOfGlobals)); + dynamic_init_globals = new(mem) + VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); + } + DynInitGlobal dyn_global = { *g, false }; + dynamic_init_globals->push_back(dyn_global); } } @@ -93,34 +114,26 @@ static void UnregisterGlobal(const Global *g) { CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - PoisonShadow(g->beg, g->size_with_redzone, 0); + if (flags()->poison_heap) + 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 // implementation. It might not be worth doing anyway. } -// Poison all shadow memory for a single global. -static void PoisonGlobalAndRedzones(const Global *g) { - CHECK(asan_inited); - CHECK(flags()->check_initialization_order); - CHECK(AddrIsInMem(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - if (flags()->report_globals >= 3) - Printf("DynInitPoison : %s\n", g->name); - PoisonShadow(g->beg, g->size_with_redzone, kAsanInitializationOrderMagic); -} - -static void UnpoisonGlobal(const Global *g) { - CHECK(asan_inited); - CHECK(flags()->check_initialization_order); - CHECK(AddrIsInMem(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - if (flags()->report_globals >= 3) - Printf("DynInitUnpoison: %s\n", g->name); - PoisonShadow(g->beg, g->size_with_redzone, 0); - PoisonRedZones(*g); +void StopInitOrderChecking() { + BlockingMutexLock lock(&mu_for_globals); + if (!flags()->check_initialization_order || !dynamic_init_globals) + return; + flags()->check_initialization_order = false; + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } } } // namespace __asan @@ -151,31 +164,47 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) { // when all dynamically initialized globals are unpoisoned. This method // poisons all global variables not defined in this TU, so that a dynamic // initializer can only touch global variables in the same TU. -void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) { - if (!flags()->check_initialization_order) return; - CHECK(list_of_dynamic_init_globals); +void __asan_before_dynamic_init(const char *module_name) { + if (!flags()->check_initialization_order || + !flags()->poison_heap) + return; + bool strict_init_order = flags()->strict_init_order; + CHECK(dynamic_init_globals); + CHECK(module_name); + CHECK(asan_inited); BlockingMutexLock lock(&mu_for_globals); - bool from_current_tu = false; - // The list looks like: - // a => ... => b => last_addr => ... => first_addr => c => ... - // The globals of the current TU reside between last_addr and first_addr. - for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) { - if (l->g->beg == last_addr) - from_current_tu = true; - if (!from_current_tu) - PoisonGlobalAndRedzones(l->g); - if (l->g->beg == first_addr) - from_current_tu = false; + if (flags()->report_globals >= 3) + Printf("DynInitPoison module: %s\n", module_name); + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (dyn_g.initialized) + continue; + if (g->module_name != module_name) + PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); + else if (!strict_init_order) + dyn_g.initialized = true; } - CHECK(!from_current_tu); } // This method runs immediately after dynamic initialization in each TU, when // all dynamically initialized globals except for those defined in the current // TU are poisoned. It simply unpoisons all dynamically initialized globals. void __asan_after_dynamic_init() { - if (!flags()->check_initialization_order) return; + if (!flags()->check_initialization_order || + !flags()->poison_heap) + return; + CHECK(asan_inited); BlockingMutexLock lock(&mu_for_globals); - for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) - UnpoisonGlobal(l->g); + // FIXME: Optionally report that we're unpoisoning globals from a module. + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (!dyn_g.initialized) { + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } + } } diff --git a/libsanitizer/asan/asan_intercepted_functions.h b/libsanitizer/asan/asan_intercepted_functions.h index ed75c428439..19b53363a5b 100644 --- a/libsanitizer/asan/asan_intercepted_functions.h +++ b/libsanitizer/asan/asan_intercepted_functions.h @@ -12,22 +12,14 @@ #ifndef ASAN_INTERCEPTED_FUNCTIONS_H #define ASAN_INTERCEPTED_FUNCTIONS_H -#include "asan_internal.h" -#include "interception/interception.h" #include "sanitizer_common/sanitizer_platform_interceptors.h" -#include -#include - -using __sanitizer::uptr; - // Use macro to describe if specific function should be // intercepted on a given platform. -#if !defined(_WIN32) +#if !SANITIZER_WINDOWS # define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1 # define ASAN_INTERCEPT__LONGJMP 1 # define ASAN_INTERCEPT_STRDUP 1 -# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 1 # define ASAN_INTERCEPT_INDEX 1 # define ASAN_INTERCEPT_PTHREAD_CREATE 1 # define ASAN_INTERCEPT_MLOCKX 1 @@ -35,290 +27,51 @@ using __sanitizer::uptr; # define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0 # define ASAN_INTERCEPT__LONGJMP 0 # define ASAN_INTERCEPT_STRDUP 0 -# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 0 # define ASAN_INTERCEPT_INDEX 0 # define ASAN_INTERCEPT_PTHREAD_CREATE 0 # define ASAN_INTERCEPT_MLOCKX 0 #endif -#if defined(__linux__) +#if SANITIZER_LINUX # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1 #else # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0 #endif -#if !defined(__APPLE__) +#if !SANITIZER_MAC # define ASAN_INTERCEPT_STRNLEN 1 #else # define ASAN_INTERCEPT_STRNLEN 0 #endif -#if defined(__linux__) && !defined(ANDROID) +#if SANITIZER_LINUX && !SANITIZER_ANDROID # define ASAN_INTERCEPT_SWAPCONTEXT 1 #else # define ASAN_INTERCEPT_SWAPCONTEXT 0 #endif -#if !defined(ANDROID) && !defined(_WIN32) +#if !SANITIZER_ANDROID && !SANITIZER_WINDOWS # define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1 #else # define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0 #endif -#if !defined(_WIN32) +#if !SANITIZER_WINDOWS # define ASAN_INTERCEPT_SIGLONGJMP 1 #else # define ASAN_INTERCEPT_SIGLONGJMP 0 #endif -#if ASAN_HAS_EXCEPTIONS && !defined(_WIN32) +#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS # define ASAN_INTERCEPT___CXA_THROW 1 #else # define ASAN_INTERCEPT___CXA_THROW 0 #endif -#define INTERPOSE_FUNCTION(function) \ - { reinterpret_cast(WRAP(function)), \ - reinterpret_cast(function) } - -#define INTERPOSE_FUNCTION_2(function, wrapper) \ - { reinterpret_cast(wrapper), \ - reinterpret_cast(function) } - -struct interpose_substitution { - const uptr replacement; - const uptr original; -}; - -#define INTERPOSER(func) __attribute__((used)) \ -const interpose_substitution substitution_##func[] \ - __attribute__((section("__DATA, __interpose"))) = { \ - INTERPOSE_FUNCTION(func), \ -} - -#define INTERPOSER_2(func, wrapper) __attribute__((used)) \ -const interpose_substitution substitution_##func[] \ - __attribute__((section("__DATA, __interpose"))) = { \ - INTERPOSE_FUNCTION_2(func, wrapper), \ -} - - -#define DECLARE_FUNCTION_AND_WRAPPER(ret_type, func, ...) \ - ret_type func(__VA_ARGS__); \ - ret_type WRAP(func)(__VA_ARGS__); \ - INTERPOSER(func) - -// Use extern declarations of intercepted functions on Mac and Windows -// to avoid including system headers. -#if defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL)) -extern "C" { -// signal.h -# if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION -struct sigaction; -DECLARE_FUNCTION_AND_WRAPPER(int, sigaction, int sig, - const struct sigaction *act, - struct sigaction *oldact); -DECLARE_FUNCTION_AND_WRAPPER(void*, signal, int signum, void *handler); -# endif - -// setjmp.h -DECLARE_FUNCTION_AND_WRAPPER(void, longjmp, void *env, int value); -# if ASAN_INTERCEPT__LONGJMP -DECLARE_FUNCTION_AND_WRAPPER(void, _longjmp, void *env, int value); -# endif -# if ASAN_INTERCEPT_SIGLONGJMP -DECLARE_FUNCTION_AND_WRAPPER(void, siglongjmp, void *env, int value); -# endif -# if ASAN_INTERCEPT___CXA_THROW -DECLARE_FUNCTION_AND_WRAPPER(void, __cxa_throw, void *a, void *b, void *c); -# endif - -// string.h / strings.h -DECLARE_FUNCTION_AND_WRAPPER(int, memcmp, - const void *a1, const void *a2, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(void*, memmove, - void *to, const void *from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(void*, memcpy, - void *to, const void *from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(void*, memset, void *block, int c, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(char*, strchr, const char *str, int c); -DECLARE_FUNCTION_AND_WRAPPER(char*, strcat, /* NOLINT */ - char *to, const char* from); -DECLARE_FUNCTION_AND_WRAPPER(char*, strncat, - char *to, const char* from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(char*, strcpy, /* NOLINT */ - char *to, const char* from); -DECLARE_FUNCTION_AND_WRAPPER(char*, strncpy, - char *to, const char* from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(int, strcmp, const char *s1, const char* s2); -DECLARE_FUNCTION_AND_WRAPPER(int, strncmp, - const char *s1, const char* s2, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(uptr, strlen, const char *s); -# if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP -DECLARE_FUNCTION_AND_WRAPPER(int, strcasecmp, const char *s1, const char *s2); -DECLARE_FUNCTION_AND_WRAPPER(int, strncasecmp, - const char *s1, const char *s2, uptr n); -# endif -# if ASAN_INTERCEPT_STRDUP -DECLARE_FUNCTION_AND_WRAPPER(char*, strdup, const char *s); -# endif -# if ASAN_INTERCEPT_STRNLEN -DECLARE_FUNCTION_AND_WRAPPER(uptr, strnlen, const char *s, uptr maxlen); -# endif -# if ASAN_INTERCEPT_INDEX -char* index(const char *string, int c); -INTERPOSER_2(index, WRAP(strchr)); -# endif - -// stdlib.h -DECLARE_FUNCTION_AND_WRAPPER(int, atoi, const char *nptr); -DECLARE_FUNCTION_AND_WRAPPER(long, atol, const char *nptr); // NOLINT -DECLARE_FUNCTION_AND_WRAPPER(long, strtol, const char *nptr, char **endptr, int base); // NOLINT -# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL -DECLARE_FUNCTION_AND_WRAPPER(long long, atoll, const char *nptr); // NOLINT -DECLARE_FUNCTION_AND_WRAPPER(long long, strtoll, const char *nptr, char **endptr, int base); // NOLINT -# endif - -// unistd.h -# if SANITIZER_INTERCEPT_READ -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, read, int fd, void *buf, SIZE_T count); -# endif -# if SANITIZER_INTERCEPT_PREAD -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread, int fd, void *buf, - SIZE_T count, OFF_T offset); -# endif -# if SANITIZER_INTERCEPT_PREAD64 -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread64, int fd, void *buf, - SIZE_T count, OFF64_T offset); -# endif - -# if SANITIZER_INTERCEPT_WRITE -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, write, int fd, void *ptr, SIZE_T count); -# endif -# if SANITIZER_INTERCEPT_PWRITE -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pwrite, - int fd, void *ptr, SIZE_T count, OFF_T offset); -# endif - -# if ASAN_INTERCEPT_MLOCKX -// mlock/munlock -DECLARE_FUNCTION_AND_WRAPPER(int, mlock, const void *addr, SIZE_T len); -DECLARE_FUNCTION_AND_WRAPPER(int, munlock, const void *addr, SIZE_T len); -DECLARE_FUNCTION_AND_WRAPPER(int, mlockall, int flags); -DECLARE_FUNCTION_AND_WRAPPER(int, munlockall, void); -# endif - -// Windows threads. -# if defined(_WIN32) -__declspec(dllimport) -void* __stdcall CreateThread(void *sec, uptr st, void* start, - void *arg, DWORD fl, DWORD *id); -# endif -// Posix threads. -# if ASAN_INTERCEPT_PTHREAD_CREATE -DECLARE_FUNCTION_AND_WRAPPER(int, pthread_create, - void *thread, void *attr, - void *(*start_routine)(void*), void *arg); -# endif - -# if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS -DECLARE_FUNCTION_AND_WRAPPER(void *, localtime, unsigned long *timep); -DECLARE_FUNCTION_AND_WRAPPER(void *, localtime_r, unsigned long *timep, - void *result); -DECLARE_FUNCTION_AND_WRAPPER(void *, gmtime, unsigned long *timep); -DECLARE_FUNCTION_AND_WRAPPER(void *, gmtime_r, unsigned long *timep, - void *result); -DECLARE_FUNCTION_AND_WRAPPER(char *, ctime, unsigned long *timep); -DECLARE_FUNCTION_AND_WRAPPER(char *, ctime_r, unsigned long *timep, - char *result); -DECLARE_FUNCTION_AND_WRAPPER(char *, asctime, void *tm); -DECLARE_FUNCTION_AND_WRAPPER(char *, asctime_r, void *tm, char *result); -# endif - -// stdio.h -# if SANITIZER_INTERCEPT_SCANF -DECLARE_FUNCTION_AND_WRAPPER(int, vscanf, const char *format, va_list ap); -DECLARE_FUNCTION_AND_WRAPPER(int, vsscanf, const char *str, const char *format, - va_list ap); -DECLARE_FUNCTION_AND_WRAPPER(int, vfscanf, void *stream, const char *format, - va_list ap); -DECLARE_FUNCTION_AND_WRAPPER(int, scanf, const char *format, ...); -DECLARE_FUNCTION_AND_WRAPPER(int, fscanf, - void* stream, const char *format, ...); -DECLARE_FUNCTION_AND_WRAPPER(int, sscanf, // NOLINT - const char *str, const char *format, ...); -# endif - -# if defined(__APPLE__) -typedef void* pthread_workqueue_t; -typedef void* pthread_workitem_handle_t; - -typedef void* dispatch_group_t; -typedef void* dispatch_queue_t; -typedef void* dispatch_source_t; -typedef u64 dispatch_time_t; -typedef void (*dispatch_function_t)(void *block); -typedef void* (*worker_t)(void *block); - -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async_f, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_sync_f, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after_f, - dispatch_time_t when, dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_barrier_async_f, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async_f, - dispatch_group_t group, dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); - -# if !defined(MISSING_BLOCKS_SUPPORT) -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async, - dispatch_group_t dg, - dispatch_queue_t dq, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async, - dispatch_queue_t dq, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after, - dispatch_queue_t dq, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_event_handler, - dispatch_source_t ds, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_cancel_handler, - dispatch_source_t ds, void (^work)(void)); -# endif // MISSING_BLOCKS_SUPPORT - -typedef void malloc_zone_t; -typedef size_t vm_size_t; -DECLARE_FUNCTION_AND_WRAPPER(malloc_zone_t *, malloc_create_zone, - vm_size_t start_size, unsigned flags); -DECLARE_FUNCTION_AND_WRAPPER(malloc_zone_t *, malloc_default_zone, void); -DECLARE_FUNCTION_AND_WRAPPER( - malloc_zone_t *, malloc_default_purgeable_zone, void); -DECLARE_FUNCTION_AND_WRAPPER(void, malloc_make_purgeable, void *ptr); -DECLARE_FUNCTION_AND_WRAPPER(int, malloc_make_nonpurgeable, void *ptr); -DECLARE_FUNCTION_AND_WRAPPER(void, malloc_set_zone_name, - malloc_zone_t *zone, const char *name); -DECLARE_FUNCTION_AND_WRAPPER(void *, malloc, size_t size); -DECLARE_FUNCTION_AND_WRAPPER(void, free, void *ptr); -DECLARE_FUNCTION_AND_WRAPPER(void *, realloc, void *ptr, size_t size); -DECLARE_FUNCTION_AND_WRAPPER(void *, calloc, size_t nmemb, size_t size); -DECLARE_FUNCTION_AND_WRAPPER(void *, valloc, size_t size); -DECLARE_FUNCTION_AND_WRAPPER(size_t, malloc_good_size, size_t size); -DECLARE_FUNCTION_AND_WRAPPER(int, posix_memalign, - void **memptr, size_t alignment, size_t size); -#if 0 -DECLARE_FUNCTION_AND_WRAPPER(void, _malloc_fork_prepare, void); -DECLARE_FUNCTION_AND_WRAPPER(void, _malloc_fork_parent, void); -DECLARE_FUNCTION_AND_WRAPPER(void, _malloc_fork_child, void); +#if !SANITIZER_WINDOWS +# define ASAN_INTERCEPT___CXA_ATEXIT 1 +#else +# define ASAN_INTERCEPT___CXA_ATEXIT 0 #endif - - -# endif // __APPLE__ -} // extern "C" -#endif // defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL)) - #endif // ASAN_INTERCEPTED_FUNCTIONS_H diff --git a/libsanitizer/asan/asan_interceptors.cc b/libsanitizer/asan/asan_interceptors.cc index 064fc6261b0..6fa968da0a3 100644 --- a/libsanitizer/asan/asan_interceptors.cc +++ b/libsanitizer/asan/asan_interceptors.cc @@ -15,10 +15,10 @@ #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 "asan_thread_registry.h" #include "interception/interception.h" #include "sanitizer_common/sanitizer_libc.h" @@ -42,15 +42,16 @@ static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) { #define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ uptr __offset = (uptr)(offset); \ uptr __size = (uptr)(size); \ + uptr __bad = 0; \ if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \ - __asan_region_is_poisoned(__offset, __size)) { \ + (__bad = __asan_region_is_poisoned(__offset, __size))) { \ GET_CURRENT_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, __offset, isWrite, __size); \ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \ } \ } 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_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true) // Behavior of functions like "memcpy" or "strcpy" is undefined // if memory intervals overlap. We report error in this case. @@ -86,9 +87,9 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { } void SetThreadName(const char *name) { - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); if (t) - t->summary()->set_name(name); + asanThreadRegistry().SetThreadName(t->tid(), name); } } // namespace __asan @@ -96,40 +97,76 @@ void SetThreadName(const char *name) { // ---------------------- Wrappers ---------------- {{{1 using namespace __asan; // NOLINT +DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr) +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) + +#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 { \ - ctx = 0; \ - (void)ctx; \ - ENSURE_ASAN_INITED(); \ +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + do { \ + if (asan_init_is_running) return REAL(func)(__VA_ARGS__); \ + ctx = 0; \ + (void) ctx; \ + ENSURE_ASAN_INITED(); \ + } while (false) +#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + do { \ } while (false) -#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) do { } while (false) -#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name) +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #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) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ + do { \ + } while (false) +#include "sanitizer_common/sanitizer_common_syscalls.inc" + static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { AsanThread *t = (AsanThread*)arg; - asanThreadRegistry().SetCurrent(t); - return t->ThreadStart(); + SetCurrentThread(t); + return t->ThreadStart(GetTid()); } #if ASAN_INTERCEPT_PTHREAD_CREATE +extern "C" int pthread_attr_getdetachstate(void *attr, int *v); + INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*start_routine)(void*), void *arg) { + EnsureMainThreadIDIsCorrect(); + // Strict init-order checking in thread-hostile. + if (flags()->strict_init_order) + StopInitOrderChecking(); GET_STACK_TRACE_THREAD; - u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); - AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); - asanThreadRegistry().RegisterThread(t); + int detached = 0; + if (attr != 0) + pthread_attr_getdetachstate(attr, &detached); + + 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); } #endif // ASAN_INTERCEPT_PTHREAD_CREATE #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION INTERCEPTOR(void*, signal, int signum, void *handler) { - if (!AsanInterceptsSignal(signum)) { + if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) { return REAL(signal)(signum, handler); } return 0; @@ -137,15 +174,15 @@ INTERCEPTOR(void*, signal, int signum, void *handler) { INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) { - if (!AsanInterceptsSignal(signum)) { + if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) { return REAL(sigaction)(signum, act, oldact); } return 0; } -#elif ASAN_POSIX +#elif SANITIZER_POSIX // We need to have defined REAL(sigaction) on posix systems. DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, - struct sigaction *oldact); + struct sigaction *oldact) #endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION #if ASAN_INTERCEPT_SWAPCONTEXT @@ -215,13 +252,15 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { // Since asan maps 16T of RAM, mlock is completely unfriendly to asan. // All functions return 0 (success). static void MlockIsUnsupported() { - static bool printed = 0; + static bool printed = false; if (printed) return; printed = true; - Printf("INFO: AddressSanitizer ignores mlock/mlockall/munlock/munlockall\n"); + if (flags()->verbosity > 0) { + Printf("INFO: AddressSanitizer ignores " + "mlock/mlockall/munlock/munlockall\n"); + } } -extern "C" { INTERCEPTOR(int, mlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; @@ -241,36 +280,56 @@ INTERCEPTOR(int, munlockall, void) { MlockIsUnsupported(); return 0; } -} // extern "C" static inline int CharCmp(unsigned char c1, unsigned char c2) { return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; } -static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { - int c1_low = ToLower(c1); - int c2_low = ToLower(c2); - return c1_low - c2_low; -} - INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { if (!asan_inited) return internal_memcmp(a1, a2, size); ENSURE_ASAN_INITED(); - 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; + 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); + } } - 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)); } +#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) MEMMOVE_BODY + INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { +#if !SANITIZER_MAC if (!asan_inited) return internal_memcpy(to, from, size); // memcpy is called during __asan_init() from the internals // of printf(...). @@ -287,24 +346,19 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr 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. + // 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); -} - -INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { - 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); - } - // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8. - // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116. - return internal_memmove(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() + // calls. If we just disable error reporting with + // 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 +#endif // !SANITIZER_MAC } INTERCEPTOR(void*, memset, void *block, int c, uptr size) { @@ -341,7 +395,12 @@ INTERCEPTOR(char*, strchr, const char *str, int c) { INTERCEPTOR(char*, index, const char *string, int c) ALIAS(WRAPPER_NAME(strchr)); # else +# if SANITIZER_MAC +DECLARE_REAL(char*, index, const char *string, int c) +OVERRIDE_FUNCTION(index, strchr); +# else DEFINE_REAL(char*, index, const char *string, int c) +# endif # endif #endif // ASAN_INTERCEPT_INDEX @@ -383,26 +442,8 @@ INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { return REAL(strncat)(to, from, size); } -INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { - if (!asan_inited) return internal_strcmp(s1, s2); - if (asan_init_is_running) { - return REAL(strcmp)(s1, s2); - } - ENSURE_ASAN_INITED(); - unsigned char c1, c2; - uptr i; - for (i = 0; ; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (c1 != c2 || c1 == '\0') break; - } - ASAN_READ_RANGE(s1, i + 1); - ASAN_READ_RANGE(s2, i + 1); - return CharCmp(c1, c2); -} - INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT -#if defined(__APPLE__) +#if SANITIZER_MAC if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT #endif // strcpy is called from malloc_default_purgeable_zone() @@ -422,21 +463,16 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT #if ASAN_INTERCEPT_STRDUP INTERCEPTOR(char*, strdup, const char *s) { -#if defined(__APPLE__) - // FIXME: because internal_strdup() uses InternalAlloc(), which currently - // just calls malloc() on Mac, we can't use internal_strdup() with the - // dynamic runtime. We can remove the call to REAL(strdup) once InternalAlloc - // starts using mmap() instead. - // See also http://code.google.com/p/address-sanitizer/issues/detail?id=123. - if (!asan_inited) return REAL(strdup)(s); -#endif if (!asan_inited) return internal_strdup(s); ENSURE_ASAN_INITED(); + uptr length = REAL(strlen)(s); if (flags()->replace_str) { - uptr length = REAL(strlen)(s); ASAN_READ_RANGE(s, length + 1); } - return REAL(strdup)(s); + GET_STACK_TRACE_MALLOC; + void *new_mem = asan_malloc(length + 1, &stack); + REAL(memcpy)(new_mem, s, length + 1); + return reinterpret_cast(new_mem); } #endif @@ -455,54 +491,13 @@ INTERCEPTOR(uptr, strlen, const char *s) { return length; } -#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP -INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { - ENSURE_ASAN_INITED(); - unsigned char c1, c2; - uptr i; - for (i = 0; ; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; +INTERCEPTOR(uptr, wcslen, const wchar_t *s) { + uptr length = REAL(wcslen)(s); + if (!asan_init_is_running) { + ENSURE_ASAN_INITED(); + ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t)); } - ASAN_READ_RANGE(s1, i + 1); - ASAN_READ_RANGE(s2, i + 1); - return CharCaseCmp(c1, c2); -} - -INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr n) { - ENSURE_ASAN_INITED(); - unsigned char c1 = 0, c2 = 0; - uptr i; - for (i = 0; i < n; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; - } - ASAN_READ_RANGE(s1, Min(i + 1, n)); - ASAN_READ_RANGE(s2, Min(i + 1, n)); - return CharCaseCmp(c1, c2); -} -#endif // ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP - -INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { - if (!asan_inited) return internal_strncmp(s1, s2, size); - // strncmp is called from malloc_default_purgeable_zone() - // in __asan::ReplaceSystemAlloc() on Mac. - if (asan_init_is_running) { - return REAL(strncmp)(s1, s2, size); - } - ENSURE_ASAN_INITED(); - unsigned char c1 = 0, c2 = 0; - uptr i; - for (i = 0; i < size; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (c1 != c2 || c1 == '\0') break; - } - ASAN_READ_RANGE(s1, Min(i + 1, size)); - ASAN_READ_RANGE(s2, Min(i + 1, size)); - return CharCmp(c1, c2); + return length; } INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { @@ -532,7 +527,7 @@ static inline bool IsValidStrtolBase(int base) { } static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { - CHECK(endptr != 0); + 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. @@ -563,7 +558,7 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT } INTERCEPTOR(int, atoi, const char *nptr) { -#if defined(__APPLE__) +#if SANITIZER_MAC if (!asan_inited) return REAL(atoi)(nptr); #endif ENSURE_ASAN_INITED(); @@ -582,7 +577,7 @@ INTERCEPTOR(int, atoi, const char *nptr) { } INTERCEPTOR(long, atol, const char *nptr) { // NOLINT -#if defined(__APPLE__) +#if SANITIZER_MAC if (!asan_inited) return REAL(atol)(nptr); #endif ENSURE_ASAN_INITED(); @@ -631,22 +626,47 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL +static void AtCxaAtexit(void *unused) { + (void)unused; + StopInitOrderChecking(); +} + +#if ASAN_INTERCEPT___CXA_ATEXIT +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { + ENSURE_ASAN_INITED(); + int res = REAL(__cxa_atexit)(func, arg, dso_handle); + REAL(__cxa_atexit)(AtCxaAtexit, 0, 0); + return res; +} +#endif // ASAN_INTERCEPT___CXA_ATEXIT + +#if !SANITIZER_MAC #define ASAN_INTERCEPT_FUNC(name) do { \ if (!INTERCEPT_FUNCTION(name) && 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 -#if defined(_WIN32) +#if SANITIZER_WINDOWS INTERCEPTOR_WINAPI(DWORD, CreateThread, void* security, uptr stack_size, DWORD (__stdcall *start_routine)(void*), void* arg, - DWORD flags, void* tid) { + 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 = asanThreadRegistry().GetCurrentTidOrInvalid(); - AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); - asanThreadRegistry().RegisterThread(t); + 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, flags, tid); + asan_thread_start, t, thr_flags, tid); } namespace __asan { @@ -663,9 +683,6 @@ void InitializeAsanInterceptors() { static bool was_called_once; CHECK(was_called_once == false); was_called_once = true; -#if defined(__APPLE__) - return; -#else SANITIZER_COMMON_INTERCEPTORS_INIT; // Intercept mem* functions. @@ -679,16 +696,11 @@ void InitializeAsanInterceptors() { // Intercept str* functions. ASAN_INTERCEPT_FUNC(strcat); // NOLINT ASAN_INTERCEPT_FUNC(strchr); - ASAN_INTERCEPT_FUNC(strcmp); ASAN_INTERCEPT_FUNC(strcpy); // NOLINT ASAN_INTERCEPT_FUNC(strlen); + ASAN_INTERCEPT_FUNC(wcslen); ASAN_INTERCEPT_FUNC(strncat); - ASAN_INTERCEPT_FUNC(strncmp); ASAN_INTERCEPT_FUNC(strncpy); -#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP - ASAN_INTERCEPT_FUNC(strcasecmp); - ASAN_INTERCEPT_FUNC(strncasecmp); -#endif #if ASAN_INTERCEPT_STRDUP ASAN_INTERCEPT_FUNC(strdup); #endif @@ -741,15 +753,19 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(pthread_create); #endif + // Intercept atexit function. +#if ASAN_INTERCEPT___CXA_ATEXIT + ASAN_INTERCEPT_FUNC(__cxa_atexit); +#endif + // Some Windows-specific interceptors. -#if defined(_WIN32) +#if SANITIZER_WINDOWS InitializeWindowsInterceptors(); #endif if (flags()->verbosity > 0) { Report("AddressSanitizer: libc interceptors initialized\n"); } -#endif // __APPLE__ } } // namespace __asan diff --git a/libsanitizer/asan/asan_interface_internal.h b/libsanitizer/asan/asan_interface_internal.h index 2fd58b856bc..7deed9f4607 100644 --- a/libsanitizer/asan/asan_interface_internal.h +++ b/libsanitizer/asan/asan_interface_internal.h @@ -23,8 +23,13 @@ extern "C" { // 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. - void __asan_init_v1() SANITIZER_INTERFACE_ATTRIBUTE; - #define __asan_init __asan_init_v1 + // 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 // This structure describes an instrumented global variable. struct __asan_global { @@ -32,102 +37,92 @@ extern "C" { uptr size; // The original size of the global. uptr size_with_redzone; // The size with the redzone. const char *name; // Name as a C string. + 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. }; // These two functions should be called by the instrumented code. // 'globals' is an array of structures describing 'n' globals. - void __asan_register_globals(__asan_global *globals, uptr n) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unregister_globals(__asan_global *globals, uptr n) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_register_globals(__asan_global *globals, uptr n); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unregister_globals(__asan_global *globals, uptr n); // These two functions should be called before and after dynamic initializers - // run, respectively. They should be called with parameters describing all - // dynamically initialized globals defined in the calling TU. - void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_after_dynamic_init() - SANITIZER_INTERFACE_ATTRIBUTE; - - // These two functions are used by the instrumented code in the - // use-after-return mode. __asan_stack_malloc allocates size bytes of - // fake stack and __asan_stack_free poisons it. real_stack is a pointer to - // the real stack region. - uptr __asan_stack_malloc(uptr size, uptr real_stack) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) - SANITIZER_INTERFACE_ATTRIBUTE; + // of a single module run, respectively. + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_before_dynamic_init(const char *module_name); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_after_dynamic_init(); // These two functions are used by instrumented code in the // use-after-scope mode. They mark memory for local variables as // unaddressable when they leave scope and addressable before the // function exits. - void __asan_poison_stack_memory(uptr addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unpoison_stack_memory(uptr addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_poison_stack_memory(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unpoison_stack_memory(uptr addr, uptr size); // Performs cleanup before a NoReturn function. Must be called before things // like _exit and execl to avoid false positives on stack. - void __asan_handle_no_return() SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE void __asan_handle_no_return(); - void __asan_poison_memory_region(void const volatile *addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unpoison_memory_region(void const volatile *addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_poison_memory_region(void const volatile *addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unpoison_memory_region(void const volatile *addr, uptr size); - bool __asan_address_is_poisoned(void const volatile *addr) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + bool __asan_address_is_poisoned(void const volatile *addr); - uptr __asan_region_is_poisoned(uptr beg, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_region_is_poisoned(uptr beg, uptr size); - void __asan_describe_address(uptr addr) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + 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) - SANITIZER_INTERFACE_ATTRIBUTE; + uptr addr, bool is_write, uptr access_size); - int __asan_set_error_exit_code(int exit_code) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_set_death_callback(void (*callback)(void)) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_set_error_report_callback(void (*callback)(const char*)) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_set_error_exit_code(int exit_code); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_death_callback(void (*callback)(void)); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_error_report_callback(void (*callback)(const char*)); - /* OPTIONAL */ void __asan_on_error() - SANITIZER_WEAK_ATTRIBUTE 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_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + int out_size); - 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_INTERFACE_ATTRIBUTE + uptr __asan_get_estimated_allocated_size(uptr size); - /* OPTIONAL */ const char* __asan_default_options() - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + 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(); - /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; - /* OPTIONAL */ void __asan_free_hook(void *ptr) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + 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; } // extern "C" #endif // ASAN_INTERFACE_INTERNAL_H diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h index 1ccbf108647..b5b48708090 100644 --- a/libsanitizer/asan/asan_internal.h +++ b/libsanitizer/asan/asan_internal.h @@ -19,39 +19,8 @@ #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_libc.h" -#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) -# error "This operating system is not supported by AddressSanitizer" -#endif - #define ASAN_DEFAULT_FAILURE_EXITCODE 1 -#if defined(__linux__) -# define ASAN_LINUX 1 -#else -# define ASAN_LINUX 0 -#endif - -#if defined(__APPLE__) -# define ASAN_MAC 1 -#else -# define ASAN_MAC 0 -#endif - -#if defined(_WIN32) -# define ASAN_WINDOWS 1 -#else -# define ASAN_WINDOWS 0 -#endif - -#if defined(__ANDROID__) || defined(ANDROID) -# define ASAN_ANDROID 1 -#else -# define ASAN_ANDROID 0 -#endif - - -#define ASAN_POSIX (ASAN_LINUX || ASAN_MAC) - #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) # error "The AddressSanitizer run-time should not be" " instrumented by AddressSanitizer" @@ -61,7 +30,7 @@ // If set, asan will install its own SEGV signal handler. #ifndef ASAN_NEEDS_SEGV -# if ASAN_ANDROID == 1 +# if SANITIZER_ANDROID == 1 # define ASAN_NEEDS_SEGV 0 # else # define ASAN_NEEDS_SEGV 1 @@ -90,7 +59,7 @@ #endif #ifndef ASAN_USE_PREINIT_ARRAY -# define ASAN_USE_PREINIT_ARRAY (ASAN_LINUX && !ASAN_ANDROID) +# define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID) #endif // All internal functions in asan reside inside the __asan namespace @@ -121,6 +90,7 @@ void UnsetAlternateSignalStack(); void InstallSignalHandlers(); void ReadContextStack(void *context, uptr *stack, uptr *ssize); void AsanPlatformThreadInit(); +void StopInitOrderChecking(); // Wrapper for TLS/TSD. void AsanTSDInit(void (*destructor)(void *tsd)); @@ -129,24 +99,14 @@ void AsanTSDSet(void *tsd); void AppendToErrorMessageBuffer(const char *buffer); -// asan_poisoning.cc -// Poisons the shadow memory for "size" bytes starting from "addr". -void PoisonShadow(uptr addr, uptr size, u8 value); -// Poisons the shadow memory for "redzone_size" bytes starting from -// "addr + size". -void PoisonShadowPartialRightRedzone(uptr addr, - uptr size, - uptr redzone_size, - u8 value); - // Platfrom-specific options. -#ifdef __APPLE__ +#if SANITIZER_MAC bool PlatformHasDifferentMemcpyAndMemmove(); # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \ (PlatformHasDifferentMemcpyAndMemmove()) #else # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true -#endif // __APPLE__ +#endif // SANITIZER_MAC // Add convenient macro for interface functions that may be represented as // weak hooks. diff --git a/libsanitizer/asan/asan_linux.cc b/libsanitizer/asan/asan_linux.cc index a030fcd3972..10c6175092b 100644 --- a/libsanitizer/asan/asan_linux.cc +++ b/libsanitizer/asan/asan_linux.cc @@ -9,12 +9,13 @@ // // Linux-specific details. //===----------------------------------------------------------------------===// -#ifdef __linux__ + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" @@ -29,7 +30,7 @@ #include #include -#if !ASAN_ANDROID +#if !SANITIZER_ANDROID // FIXME: where to get ucontext on Android? #include #endif @@ -48,7 +49,7 @@ void *AsanDoesNotSupportStaticLinkage() { } void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { -#if ASAN_ANDROID +#if SANITIZER_ANDROID *pc = *sp = *bp = 0; #elif defined(__arm__) ucontext_t *ucontext = (ucontext_t*)context; @@ -86,6 +87,11 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { 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]; #else # error "Unsupported arch" #endif @@ -99,25 +105,7 @@ void AsanPlatformThreadInit() { // Nothing here for now. } -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) { -#if defined(__arm__) || \ - defined(__powerpc__) || defined(__powerpc64__) || \ - defined(__sparc__) - fast = false; -#endif - if (!fast) - return stack->SlowUnwindStack(pc, max_s); - stack->size = 0; - stack->trace[0] = pc; - if (max_s > 1) { - stack->max_size = max_s; - if (!asan_inited) return; - if (AsanThread *t = asanThreadRegistry().GetCurrent()) - stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom()); - } -} - -#if !ASAN_ANDROID +#if !SANITIZER_ANDROID void ReadContextStack(void *context, uptr *stack, uptr *ssize) { ucontext_t *ucp = (ucontext_t*)context; *stack = (uptr)ucp->uc_stack.ss_sp; @@ -131,4 +119,4 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) { } // namespace __asan -#endif // __linux__ +#endif // SANITIZER_LINUX diff --git a/libsanitizer/asan/asan_mac.cc b/libsanitizer/asan/asan_mac.cc index dd2657df1e2..4b28c1422cd 100644 --- a/libsanitizer/asan/asan_mac.cc +++ b/libsanitizer/asan/asan_mac.cc @@ -10,7 +10,8 @@ // Mac-specific details. //===----------------------------------------------------------------------===// -#ifdef __APPLE__ +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC #include "asan_interceptors.h" #include "asan_internal.h" @@ -18,7 +19,7 @@ #include "asan_mapping.h" #include "asan_stack.h" #include "asan_thread.h" -#include "asan_thread_registry.h" +#include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_libc.h" #include // for _NSGetArgv @@ -50,15 +51,17 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { # endif // SANITIZER_WORDSIZE } -int GetMacosVersion() { +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(sysctl(mib, 2, 0, &len, 0, 0) != -1); - CHECK(len < maxlen); - CHECK(sysctl(mib, 2, version, &len, 0, 0) != -1); + 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': { @@ -66,6 +69,7 @@ int GetMacosVersion() { 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; } } @@ -73,6 +77,18 @@ int GetMacosVersion() { } } +MacosVersion GetMacosVersion() { + atomic_uint32_t *cache = + reinterpret_cast(&cached_macos_version); + MacosVersion result = + static_cast(atomic_load(cache, memory_order_acquire)); + if (result == MACOS_VERSION_UNINITIALIZED) { + result = GetMacosVersionInternal(); + atomic_store(cache, result, memory_order_release); + } + return result; +} + bool PlatformHasDifferentMemcpyAndMemmove() { // On OS X 10.7 memcpy() and memmove() are both resolved // into memmove$VARIANT$sse42. @@ -227,18 +243,6 @@ bool AsanInterceptsSignal(int signum) { void AsanPlatformThreadInit() { } -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) { - (void)fast; - stack->size = 0; - stack->trace[0] = pc; - if ((max_s) > 1) { - stack->max_size = max_s; - if (!asan_inited) return; - if (AsanThread *t = asanThreadRegistry().GetCurrent()) - stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom()); - } -} - void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } @@ -286,32 +290,16 @@ typedef struct { u32 parent_tid; } asan_block_context_t; -// We use extern declarations of libdispatch functions here instead -// of including . This header is not present on -// Mac OS X Leopard and eariler, and although we don't expect ASan to -// work on legacy systems, it's bad to break the build of -// LLVM compiler-rt there. -extern "C" { -void dispatch_async_f(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_sync_f(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_after_f(dispatch_time_t when, dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -} // extern "C" - -static ALWAYS_INLINE +ALWAYS_INLINE void asan_register_worker_thread(int parent_tid, StackTrace *stack) { - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); if (!t) { - t = AsanThread::Create(parent_tid, 0, 0, stack); - asanThreadRegistry().RegisterThread(t); + t = AsanThread::Create(0, 0); + CreateThreadContextArgs args = { t, stack }; + asanThreadRegistry().CreateThread(*(uptr*)t, true, parent_tid, &args); t->Init(); - asanThreadRegistry().SetCurrent(t); + asanThreadRegistry().StartThread(t->tid(), 0, 0); + SetCurrentThread(t); } } @@ -345,7 +333,7 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); asan_ctxt->block = ctxt; asan_ctxt->func = func; - asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + asan_ctxt->parent_tid = GetCurrentTidOrInvalid(); return asan_ctxt; } @@ -411,7 +399,7 @@ void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); #define GET_ASAN_BLOCK(work) \ void (^asan_block)(void); \ - int parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); \ + int parent_tid = GetCurrentTidOrInvalid(); \ asan_block = ^(void) { \ GET_STACK_TRACE_THREAD; \ asan_register_worker_thread(parent_tid, &stack); \ @@ -449,4 +437,4 @@ INTERCEPTOR(void, dispatch_source_set_event_handler, } #endif -#endif // __APPLE__ +#endif // SANITIZER_MAC diff --git a/libsanitizer/asan/asan_mac.h b/libsanitizer/asan/asan_mac.h index 2c162fb0c39..2d1d4b0bfb3 100644 --- a/libsanitizer/asan/asan_mac.h +++ b/libsanitizer/asan/asan_mac.h @@ -34,12 +34,14 @@ typedef struct __CFRuntimeBase { #endif } CFRuntimeBase; -enum { - MACOS_VERSION_UNKNOWN = 0, +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_MOUNTAIN_LION, + MACOS_VERSION_MAVERICKS }; // Used by asan_malloc_mac.cc and asan_mac.cc @@ -47,7 +49,7 @@ extern "C" void __CFInitialize(); namespace __asan { -int GetMacosVersion(); +MacosVersion GetMacosVersion(); void MaybeReplaceCFAllocator(); } // namespace __asan diff --git a/libsanitizer/asan/asan_malloc_linux.cc b/libsanitizer/asan/asan_malloc_linux.cc index 18e6a3be865..97691fcd361 100644 --- a/libsanitizer/asan/asan_malloc_linux.cc +++ b/libsanitizer/asan/asan_malloc_linux.cc @@ -11,15 +11,16 @@ // We simply define functions like malloc, free, realloc, etc. // They will replace the corresponding libc functions automagically. //===----------------------------------------------------------------------===// -#ifdef __linux__ + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_stack.h" -#include "asan_thread_registry.h" -#if ASAN_ANDROID +#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) @@ -144,4 +145,4 @@ INTERCEPTOR(void, malloc_stats, void) { __asan_print_accumulated_stats(); } -#endif // __linux__ +#endif // SANITIZER_LINUX diff --git a/libsanitizer/asan/asan_malloc_mac.cc b/libsanitizer/asan/asan_malloc_mac.cc index 3ae6c594650..342e806e3b6 100644 --- a/libsanitizer/asan/asan_malloc_mac.cc +++ b/libsanitizer/asan/asan_malloc_mac.cc @@ -10,12 +10,14 @@ // Mac-specific malloc interception. //===----------------------------------------------------------------------===// -#ifdef __APPLE__ +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC #include #include #include #include +#include #include "asan_allocator.h" #include "asan_interceptors.h" @@ -24,7 +26,6 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" -#include "asan_thread_registry.h" // Similar code is used in Google Perftools, // http://code.google.com/p/google-perftools. @@ -40,10 +41,19 @@ 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_malloc(sizeof(asan_zone), &stack); + (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; } @@ -282,7 +292,7 @@ void mi_force_unlock(malloc_zone_t *zone) { void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { AsanMallocStats malloc_stats; - asanThreadRegistry().FillMallocStatistics(&malloc_stats); + FillMallocStatistics(&malloc_stats); CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); } @@ -344,4 +354,4 @@ void ReplaceSystemMalloc() { } } // namespace __asan -#endif // __APPLE__ +#endif // SANITIZER_MAC diff --git a/libsanitizer/asan/asan_malloc_win.cc b/libsanitizer/asan/asan_malloc_win.cc index 437079f5d1d..cabf8cd254c 100644 --- a/libsanitizer/asan/asan_malloc_win.cc +++ b/libsanitizer/asan/asan_malloc_win.cc @@ -9,7 +9,9 @@ // // Windows-specific malloc interception. //===----------------------------------------------------------------------===// -#ifdef _WIN32 + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS #include "asan_allocator.h" #include "asan_interceptors.h" @@ -28,11 +30,13 @@ using namespace __asan; // NOLINT // revisited in the future. extern "C" { +SANITIZER_INTERFACE_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) { free(ptr); } @@ -41,38 +45,46 @@ void cfree(void *ptr) { CHECK(!"cfree() should not be used on Windows?"); } +SANITIZER_INTERFACE_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) { return malloc(size); } +SANITIZER_INTERFACE_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); } +SANITIZER_INTERFACE_ATTRIBUTE void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { return calloc(nmemb, size); } +SANITIZER_INTERFACE_ATTRIBUTE void *realloc(void *ptr, size_t size) { GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } +SANITIZER_INTERFACE_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) { if (!p) return calloc(n, elem_size); @@ -82,6 +94,7 @@ void* _recalloc(void* p, size_t n, size_t elem_size) { return realloc(p, size); } +SANITIZER_INTERFACE_ATTRIBUTE size_t _msize(void *ptr) { GET_STACK_TRACE_MALLOC; return asan_malloc_usable_size(ptr, &stack); diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h index 9b4dd35f1eb..fd5c2039bca 100644 --- a/libsanitizer/asan/asan_mapping.h +++ b/libsanitizer/asan/asan_mapping.h @@ -47,6 +47,20 @@ // || `[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 || + +static const u64 kDefaultShadowScale = 3; +static const u64 kDefaultShadowOffset32 = 1ULL << 29; +static const u64 kDefaultShadowOffset64 = 1ULL << 44; +static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G. +static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; +static const u64 kMIPS32_ShadowOffset32 = 0x0aaa8000; #if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale; @@ -54,22 +68,23 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset; # define SHADOW_SCALE (__asan_mapping_scale) # define SHADOW_OFFSET (__asan_mapping_offset) #else -# if ASAN_ANDROID -# define SHADOW_SCALE (3) +# define SHADOW_SCALE kDefaultShadowScale +# if SANITIZER_ANDROID # define SHADOW_OFFSET (0) # else -# define SHADOW_SCALE (3) # if SANITIZER_WORDSIZE == 32 -# define SHADOW_OFFSET (1 << 29) +# if defined(__mips__) +# define SHADOW_OFFSET kMIPS32_ShadowOffset32 +# else +# define SHADOW_OFFSET kDefaultShadowOffset32 +# endif # else # if defined(__powerpc64__) -# define SHADOW_OFFSET (1ULL << 41) +# define SHADOW_OFFSET kPPC64_ShadowOffset64 +# elif SANITIZER_MAC +# define SHADOW_OFFSET kDefaultShadowOffset64 # else -# if ASAN_MAC -# define SHADOW_OFFSET (1ULL << 44) -# else -# define SHADOW_OFFSET 0x7fff8000ULL -# endif +# define SHADOW_OFFSET kDefaultShort64bitShadowOffset # endif # endif # endif @@ -131,7 +146,6 @@ static uptr kHighMemEnd = 0x7fffffffffffULL; static uptr kMidMemBeg = 0x3000000000ULL; static uptr kMidMemEnd = 0x4fffffffffULL; #else -SANITIZER_INTERFACE_ATTRIBUTE extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init. #endif diff --git a/libsanitizer/asan/asan_new_delete.cc b/libsanitizer/asan/asan_new_delete.cc index fd47eee4205..beac8cdbdd5 100644 --- a/libsanitizer/asan/asan_new_delete.cc +++ b/libsanitizer/asan/asan_new_delete.cc @@ -27,7 +27,7 @@ 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 !ASAN_ANDROID +#if !SANITIZER_ANDROID // Fake std::nothrow_t to avoid including . namespace std { @@ -38,6 +38,14 @@ struct nothrow_t {}; GET_STACK_TRACE_MALLOC;\ return asan_memalign(0, size, &stack, type); +// On OS X it's not enough to just provide our own 'operator new' and +// 'operator delete' implementations, because they're going to be in the +// runtime dylib, and the main executable will depend on both the runtime +// dylib and libstdc++, each of those'll have its implementation of new and +// delete. +// 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 void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); } INTERCEPTOR_ATTRIBUTE @@ -49,10 +57,26 @@ INTERCEPTOR_ATTRIBUTE void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY(FROM_NEW_BR); } +#else // SANITIZER_MAC +INTERCEPTOR(void *, _Znwm, size_t size) { + OPERATOR_NEW_BODY(FROM_NEW); +} +INTERCEPTOR(void *, _Znam, size_t size) { + OPERATOR_NEW_BODY(FROM_NEW_BR); +} +INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(FROM_NEW); +} +INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(FROM_NEW_BR); +} +#endif + #define OPERATOR_DELETE_BODY(type) \ GET_STACK_TRACE_FREE;\ asan_free(ptr, &stack, type); +#if !SANITIZER_MAC INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); } INTERCEPTOR_ATTRIBUTE @@ -64,4 +88,19 @@ INTERCEPTOR_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } +#else // SANITIZER_MAC +INTERCEPTOR(void, _ZdlPv, void *ptr) { + OPERATOR_DELETE_BODY(FROM_NEW); +} +INTERCEPTOR(void, _ZdaPv, void *ptr) { + OPERATOR_DELETE_BODY(FROM_NEW_BR); +} +INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(FROM_NEW); +} +INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(FROM_NEW_BR); +} +#endif + #endif diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc index 7e930034bef..b967acded63 100644 --- a/libsanitizer/asan/asan_poisoning.cc +++ b/libsanitizer/asan/asan_poisoning.cc @@ -10,9 +10,7 @@ // Shadow memory poisoning by ASan RTL and by user application. //===----------------------------------------------------------------------===// -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_mapping.h" +#include "asan_poisoning.h" #include "sanitizer_common/sanitizer_libc.h" namespace __asan { @@ -20,11 +18,11 @@ namespace __asan { void PoisonShadow(uptr addr, uptr size, u8 value) { if (!flags()->poison_heap) return; CHECK(AddrIsAlignedByGranularity(addr)); + CHECK(AddrIsInMem(addr)); CHECK(AddrIsAlignedByGranularity(addr + size)); - uptr shadow_beg = MemToShadow(addr); - uptr shadow_end = MemToShadow(addr + size - SHADOW_GRANULARITY) + 1; - CHECK(REAL(memset) != 0); - REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); + CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY)); + CHECK(REAL(memset)); + FastPoisonShadow(addr, size, value); } void PoisonShadowPartialRightRedzone(uptr addr, @@ -33,20 +31,10 @@ void PoisonShadowPartialRightRedzone(uptr addr, u8 value) { if (!flags()->poison_heap) return; CHECK(AddrIsAlignedByGranularity(addr)); - u8 *shadow = (u8*)MemToShadow(addr); - for (uptr i = 0; i < redzone_size; - i += SHADOW_GRANULARITY, shadow++) { - if (i + SHADOW_GRANULARITY <= size) { - *shadow = 0; // fully addressable - } else if (i >= size) { - *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable - } else { - *shadow = size - i; // first size-i bytes are addressable - } - } + CHECK(AddrIsInMem(addr)); + FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value); } - struct ShadowSegmentEndpoint { u8 *chunk; s8 offset; // in [0, SHADOW_GRANULARITY) @@ -179,6 +167,55 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) { return 0; } +#define CHECK_SMALL_REGION(p, size, isWrite) \ + do { \ + uptr __p = reinterpret_cast(p); \ + uptr __size = size; \ + if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \ + __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);\ + } \ + } while (false); \ + + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u16 __sanitizer_unaligned_load16(const uu16 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u32 __sanitizer_unaligned_load32(const uu32 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u64 __sanitizer_unaligned_load64(const uu64 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(uu16 *p, u16 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(uu32 *p, u32 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store64(uu64 *p, u64 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + // 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) { diff --git a/libsanitizer/asan/asan_poisoning.h b/libsanitizer/asan/asan_poisoning.h new file mode 100644 index 00000000000..866c0a57c7e --- /dev/null +++ b/libsanitizer/asan/asan_poisoning.h @@ -0,0 +1,57 @@ +//===-- asan_poisoning.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. +// +// Shadow memory poisoning by ASan RTL and by user application. +//===----------------------------------------------------------------------===// + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mapping.h" + +namespace __asan { + +// Poisons the shadow memory for "size" bytes starting from "addr". +void PoisonShadow(uptr addr, uptr size, u8 value); + +// Poisons the shadow memory for "redzone_size" bytes starting from +// "addr + size". +void PoisonShadowPartialRightRedzone(uptr addr, + uptr size, + uptr redzone_size, + u8 value); + +// Fast versions of PoisonShadow and PoisonShadowPartialRightRedzone that +// assume that memory addresses are properly aligned. Use in +// performance-critical code with care. +ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, + u8 value) { + DCHECK(flags()->poison_heap); + 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); +} + +ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( + uptr aligned_addr, uptr size, uptr redzone_size, u8 value) { + DCHECK(flags()->poison_heap); + u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr); + for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) { + if (i + SHADOW_GRANULARITY <= size) { + *shadow = 0; // fully addressable + } else if (i >= size) { + *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable + } else { + // first size-i bytes are addressable + *shadow = static_cast(size - i); + } + } +} + +} // namespace __asan diff --git a/libsanitizer/asan/asan_posix.cc b/libsanitizer/asan/asan_posix.cc index 177b84ae67f..a210a810036 100644 --- a/libsanitizer/asan/asan_posix.cc +++ b/libsanitizer/asan/asan_posix.cc @@ -9,14 +9,15 @@ // // Posix-specific details. //===----------------------------------------------------------------------===// -#if defined(__linux__) || defined(__APPLE__) + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX || SANITIZER_MAC #include "asan_internal.h" #include "asan_interceptors.h" #include "asan_mapping.h" #include "asan_report.h" #include "asan_stack.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" @@ -40,7 +41,7 @@ static void MaybeInstallSigaction(int signum, sigact.sa_sigaction = handler; sigact.sa_flags = SA_SIGINFO; if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; - CHECK(0 == REAL(sigaction)(signum, &sigact, 0)); + CHECK_EQ(0, REAL(sigaction)(signum, &sigact, 0)); if (flags()->verbosity >= 1) { Report("Installed the sigaction for signal %d\n", signum); } @@ -57,7 +58,7 @@ static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) { void SetAlternateSignalStack() { stack_t altstack, oldstack; - CHECK(0 == sigaltstack(0, &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 @@ -67,10 +68,10 @@ void SetAlternateSignalStack() { altstack.ss_sp = base; altstack.ss_flags = 0; altstack.ss_size = kAltStackSize; - CHECK(0 == sigaltstack(&altstack, 0)); + CHECK_EQ(0, sigaltstack(&altstack, 0)); if (flags()->verbosity > 0) { Report("Alternative stack for T%d set: [%p,%p)\n", - asanThreadRegistry().GetCurrentTidOrInvalid(), + GetCurrentTidOrInvalid(), altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size); } } @@ -80,7 +81,7 @@ void UnsetAlternateSignalStack() { altstack.ss_sp = 0; altstack.ss_flags = SS_DISABLE; altstack.ss_size = 0; - CHECK(0 == sigaltstack(&altstack, &oldstack)); + CHECK_EQ(0, sigaltstack(&altstack, &oldstack)); UnmapOrDie(oldstack.ss_sp, oldstack.ss_size); } @@ -100,7 +101,7 @@ static bool tsd_key_inited = false; void AsanTSDInit(void (*destructor)(void *tsd)) { CHECK(!tsd_key_inited); tsd_key_inited = true; - CHECK(0 == pthread_key_create(&tsd_key, destructor)); + CHECK_EQ(0, pthread_key_create(&tsd_key, destructor)); } void *AsanTSDGet() { @@ -115,4 +116,4 @@ void AsanTSDSet(void *tsd) { } // namespace __asan -#endif // __linux__ || __APPLE_ +#endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/libsanitizer/asan/asan_preinit.cc b/libsanitizer/asan/asan_preinit.cc index 40309fa389d..31042401536 100644 --- a/libsanitizer/asan/asan_preinit.cc +++ b/libsanitizer/asan/asan_preinit.cc @@ -16,9 +16,11 @@ // 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? + // The symbol is called __local_asan_preinit, because it's not intended to be + // exported. __attribute__((section(".preinit_array"), used)) - void (*__asan_preinit)(void) =__asan_init; -#elif defined(_WIN32) && defined(_DLL) + 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. diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc index 13e94c421b5..8f11ff4eac3 100644 --- a/libsanitizer/asan/asan_report.cc +++ b/libsanitizer/asan/asan_report.cc @@ -15,8 +15,8 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_symbolizer.h" @@ -42,15 +42,6 @@ void AppendToErrorMessageBuffer(const char *buffer) { } // ---------------------- Decorator ------------------------------ {{{1 -bool PrintsToTtyCached() { - static int cached = 0; - static bool prints_to_tty; - if (!cached) { // Ok wrt threads since we are printing only from one thread. - prints_to_tty = PrintsToTty(); - cached = 1; - } - return prints_to_tty; -} class Decorator: private __sanitizer::AnsiColorDecorator { public: Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } @@ -111,7 +102,7 @@ static void PrintShadowBytes(const char *before, u8 *bytes, for (uptr i = 0; i < n; i++) { u8 *p = bytes + i; const char *before = p == guilty ? "[" : - p - 1 == guilty ? "" : " "; + (p - 1 == guilty && i != 0) ? "" : " "; const char *after = p == guilty ? "]" : ""; PrintShadowByte(before, *p, after); } @@ -123,12 +114,12 @@ static void PrintLegend() { "application bytes):\n", (int)SHADOW_GRANULARITY); PrintShadowByte(" Addressable: ", 0); Printf(" Partially addressable: "); - for (uptr i = 1; i < SHADOW_GRANULARITY; i++) + for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte("", i, " "); Printf("\n"); PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic); - PrintShadowByte(" Heap righ redzone: ", kAsanHeapRightRedzoneMagic); - PrintShadowByte(" Freed Heap region: ", kAsanHeapFreeMagic); + 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); @@ -173,19 +164,34 @@ static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, } } +static void DescribeThread(AsanThread *t) { + if (t) + DescribeThread(t->context()); +} + // ---------------------- Address Descriptions ------------------- {{{1 static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; } +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' && &getSymbolizer) + ? getSymbolizer()->Demangle(name) + : name; +} + // Check if the global is a zero-terminated ASCII string. If so, print it. static void PrintGlobalNameIfASCII(const __asan_global &g) { for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { - if (!IsASCII(*(unsigned char*)p)) return; + 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", g.name, (char*)g.beg); + if (*(char*)(g.beg + g.size - 1) != '\0') return; + Printf(" '%s' is ascii string '%s'\n", + MaybeDemangleGlobalName(g.name), (char*)g.beg); } bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, @@ -206,8 +212,8 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, // Can it happen? Printf("%p is located %zd bytes inside", (void*)addr, addr - g.beg); } - Printf(" of global variable '%s' (0x%zx) of size %zu\n", - g.name, g.beg, g.size); + 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; @@ -234,57 +240,149 @@ bool DescribeAddressIfShadow(uptr addr) { return false; } +// Return " (thread_name) " or an empty string if the name is empty. +const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], + uptr buff_len) { + const char *name = t->name; + if (name[0] == '\0') return ""; + buff[0] = 0; + internal_strncat(buff, " (", 3); + internal_strncat(buff, name, buff_len - 4); + internal_strncat(buff, ")", 2); + return buff; +} + +const char *ThreadNameWithParenthesis(u32 tid, char buff[], + uptr buff_len) { + if (tid == kInvalidTid) return ""; + asanThreadRegistry().CheckLocked(); + AsanThreadContext *t = GetThreadContextByTidLocked(tid); + 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; + uptr addr_end = addr + access_size; + const char *pos_descr = 0; + // 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_end <= var_end) + pos_descr = "is inside"; // May happen if this is a use-after-return. + else if (addr < var_end) + pos_descr = "partially overflows"; + else if (addr_end <= next_var_beg && + next_var_beg - addr_end >= addr - var_end) + pos_descr = "overflows"; + } else { + if (addr_end > var_beg) + pos_descr = "partially underflows"; + else if (addr >= prev_var_end && + addr - prev_var_end >= var_beg - addr_end) + pos_descr = "underflows"; + } + Printf(" [%zd, %zd) '%s'", var_beg, var_beg + var_size, var_name); + 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()); + } else { + Printf("\n"); + } +} + +struct StackVarDescr { + uptr beg; + uptr size; + const char *name_pos; + uptr name_len; +}; + bool DescribeAddressIfStack(uptr addr, uptr access_size) { - AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr); + AsanThread *t = FindThreadByStackAddress(addr); if (!t) return false; - const sptr kBufSize = 4095; + const uptr kBufSize = 4095; char buf[kBufSize]; uptr offset = 0; - const char *frame_descr = t->GetFrameNameByAddr(addr, &offset); + 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(frame_pc); +#endif + // This string is created by the compiler and has the following form: - // "FunctioName n alloc_1 alloc_2 ... alloc_n" + // "n alloc_1 alloc_2 ... alloc_n" // where alloc_i looks like "offset size len ObjectName ". CHECK(frame_descr); - // Report the function name and the offset. - const char *name_end = internal_strchr(frame_descr, ' '); - CHECK(name_end); - buf[0] = 0; - internal_strncat(buf, frame_descr, - Min(kBufSize, - static_cast(name_end - frame_descr))); Decorator d; Printf("%s", d.Location()); - Printf("Address %p is located at offset %zu " - "in frame <%s> of T%d's stack:\n", - (void*)addr, offset, Demangle(buf), t->tid()); + 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); + // 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. + // The frame numbers may be different than those in the stack trace printed + // 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; Printf("%s", d.EndLocation()); + PrintStack(&alloca_stack); // Report the number of stack objects. char *p; - uptr n_objects = internal_simple_strtoll(name_end, &p, 10); - CHECK(n_objects > 0); + 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 vars(n_objects); for (uptr i = 0; i < n_objects; i++) { uptr beg, size; - sptr len; - beg = internal_simple_strtoll(p, &p, 10); - size = internal_simple_strtoll(p, &p, 10); - len = internal_simple_strtoll(p, &p, 10); - if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') { + 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++; - buf[0] = 0; - internal_strncat(buf, p, Min(kBufSize, len)); + vars[i].beg = beg; + vars[i].size = size; + vars[i].name_pos = p; + vars[i].name_len = len; p += len; - Printf(" [%zu, %zu) '%s'\n", beg, beg + size, buf); + } + for (uptr i = 0; i < n_objects; i++) { + buf[0] = 0; + internal_strncat(buf, vars[i].name_pos, + static_cast(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, + 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"); - DescribeThread(t->summary()); + DescribeThread(t); return true; } @@ -312,65 +410,43 @@ static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, Printf("%s", d.EndLocation()); } -// Return " (thread_name) " or an empty string if the name is empty. -const char *ThreadNameWithParenthesis(AsanThreadSummary *t, char buff[], - uptr buff_len) { - const char *name = t->name(); - if (*name == 0) return ""; - buff[0] = 0; - internal_strncat(buff, " (", 3); - internal_strncat(buff, name, buff_len - 4); - internal_strncat(buff, ")", 2); - return buff; -} - -const char *ThreadNameWithParenthesis(u32 tid, char buff[], - uptr buff_len) { - if (tid == kInvalidTid) return ""; - AsanThreadSummary *t = asanThreadRegistry().FindByTid(tid); - return ThreadNameWithParenthesis(t, buff, buff_len); -} - void DescribeHeapAddress(uptr addr, uptr access_size) { AsanChunkView chunk = FindHeapChunkByAddress(addr); if (!chunk.IsValid()) return; DescribeAccessToHeapChunk(chunk, addr, access_size); CHECK(chunk.AllocTid() != kInvalidTid); - AsanThreadSummary *alloc_thread = - asanThreadRegistry().FindByTid(chunk.AllocTid()); + asanThreadRegistry().CheckLocked(); + AsanThreadContext *alloc_thread = + GetThreadContextByTidLocked(chunk.AllocTid()); StackTrace alloc_stack; chunk.GetAllocStack(&alloc_stack); - AsanThread *t = asanThreadRegistry().GetCurrent(); - CHECK(t); char tname[128]; Decorator d; + AsanThreadContext *free_thread = 0; if (chunk.FreeTid() != kInvalidTid) { - AsanThreadSummary *free_thread = - asanThreadRegistry().FindByTid(chunk.FreeTid()); + free_thread = GetThreadContextByTidLocked(chunk.FreeTid()); Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), - free_thread->tid(), + free_thread->tid, ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), d.EndAllocation()); StackTrace free_stack; chunk.GetFreeStack(&free_stack); PrintStack(&free_stack); Printf("%spreviously allocated by thread T%d%s here:%s\n", - d.Allocation(), alloc_thread->tid(), + d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); - PrintStack(&alloc_stack); - DescribeThread(t->summary()); - DescribeThread(free_thread); - DescribeThread(alloc_thread); } else { Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), - alloc_thread->tid(), + alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); - PrintStack(&alloc_stack); - DescribeThread(t->summary()); - DescribeThread(alloc_thread); } + PrintStack(&alloc_stack); + DescribeThread(GetCurrentThread()); + if (free_thread) + DescribeThread(free_thread); + DescribeThread(alloc_thread); } void DescribeAddress(uptr addr, uptr access_size) { @@ -388,26 +464,27 @@ void DescribeAddress(uptr addr, uptr access_size) { // ------------------- Thread description -------------------- {{{1 -void DescribeThread(AsanThreadSummary *summary) { - CHECK(summary); +void DescribeThread(AsanThreadContext *context) { + CHECK(context); + asanThreadRegistry().CheckLocked(); // No need to announce the main thread. - if (summary->tid() == 0 || summary->announced()) { + if (context->tid == 0 || context->announced) { return; } - summary->set_announced(true); + context->announced = true; char tname[128]; - Printf("Thread T%d%s", summary->tid(), - ThreadNameWithParenthesis(summary->tid(), tname, sizeof(tname))); + Printf("Thread T%d%s", context->tid, + ThreadNameWithParenthesis(context->tid, tname, sizeof(tname))); Printf(" created by T%d%s here:\n", - summary->parent_tid(), - ThreadNameWithParenthesis(summary->parent_tid(), + context->parent_tid, + ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); - PrintStack(summary->stack()); + PrintStack(&context->stack); // Recursively described parent thread if needed. if (flags()->print_full_thread_history) { - AsanThreadSummary *parent_summary = - asanThreadRegistry().FindByTid(summary->parent_tid()); - DescribeThread(parent_summary); + AsanThreadContext *parent_context = + GetThreadContextByTidLocked(context->parent_tid); + DescribeThread(parent_context); } } @@ -426,7 +503,7 @@ class ScopedInErrorReport { // they are defined as no-return. Report("AddressSanitizer: while reporting a bug found another one." "Ignoring.\n"); - u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + 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 @@ -438,24 +515,20 @@ class ScopedInErrorReport { internal__exit(flags()->exitcode); } ASAN_ON_ERROR(); - reporting_thread_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + // 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"); - if (reporting_thread_tid != kInvalidTid) { - // We started reporting an error message. Stop using the fake stack - // in case we call an instrumented function from a symbolizer. - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); - CHECK(curr_thread); - curr_thread->fake_stack().StopUsingFakeStack(); - } } // Destructor is NORETURN, as functions that report errors are. NORETURN ~ScopedInErrorReport() { // Make sure the current thread is announced. - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); - if (curr_thread) { - DescribeThread(curr_thread->summary()); - } + DescribeThread(GetCurrentThread()); // Print memory stats. if (flags()->print_stats) __asan_print_accumulated_stats(); @@ -469,13 +542,15 @@ class ScopedInErrorReport { static void ReportSummary(const char *error_type, StackTrace *stack) { if (!stack->size) return; - if (IsSymbolizerAvailable()) { + if (&getSymbolizer && getSymbolizer()->IsAvailable()) { AddressInfo ai; // Currently, we include the first stack frame into the report summary. // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). - SymbolizeCode(stack->trace[0], &ai, 1); + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); + getSymbolizer()->SymbolizeCode(pc, &ai, 1); ReportErrorSummary(error_type, - StripPathPrefix(ai.file, flags()->strip_path_prefix), + StripPathPrefix(ai.file, + common_flags()->strip_path_prefix), ai.line, ai.function); } // FIXME: do we need to print anything at all if there is no symbolizer? @@ -488,7 +563,7 @@ void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { 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, - asanThreadRegistry().GetCurrentTidOrInvalid()); + GetCurrentTidOrInvalid()); Printf("%s", d.EndWarning()); Printf("AddressSanitizer can not provide additional info.\n"); GET_STACK_TRACE_FATAL(pc, bp); @@ -500,7 +575,13 @@ void ReportDoubleFree(uptr addr, StackTrace *stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: attempting double-free on %p:\n", addr); + char tname[128]; + u32 curr_tid = GetCurrentTidOrInvalid(); + Report("ERROR: AddressSanitizer: attempting double-free on %p in " + "thread T%d%s:\n", + addr, curr_tid, + ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); + Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); @@ -511,8 +592,11 @@ void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); + char tname[128]; + u32 curr_tid = GetCurrentTidOrInvalid(); Report("ERROR: AddressSanitizer: attempting free on address " - "which was not malloc()-ed: %p\n", addr); + "which was not malloc()-ed: %p in thread T%d%s\n", addr, + curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); @@ -678,7 +762,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, bug_descr, (void*)addr, pc, bp, sp); Printf("%s", d.EndWarning()); - u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + u32 curr_tid = GetCurrentTidOrInvalid(); char tname[128]; Printf("%s%s of size %zu at %p thread T%d%s%s\n", d.Access(), @@ -712,6 +796,6 @@ void __asan_describe_address(uptr addr) { #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default implementation of __asan_on_error that does nothing // and may be overriden by user. -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE void __asan_on_error() {} #endif diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h index 13724dab9ee..afe7673304c 100644 --- a/libsanitizer/asan/asan_report.h +++ b/libsanitizer/asan/asan_report.h @@ -27,7 +27,7 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size); // Determines memory type on its own. void DescribeAddress(uptr addr, uptr access_size); -void DescribeThread(AsanThreadSummary *summary); +void DescribeThread(AsanThreadContext *context); // Different kinds of error reports. void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr); diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc index 6ddb01329ad..67327611e84 100644 --- a/libsanitizer/asan/asan_rtl.cc +++ b/libsanitizer/asan/asan_rtl.cc @@ -11,17 +11,21 @@ //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_interceptors.h" +#include "asan_interface_internal.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 "asan_thread.h" -#include "asan_thread_registry.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" + +int __asan_option_detect_stack_use_after_return; // Global interface symbol. namespace __asan { @@ -62,13 +66,9 @@ static void AsanCheckFailed(const char *file, int line, const char *cond, } // -------------------------- Flags ------------------------- {{{1 -static const int kDeafultMallocContextSize = 30; +static const int kDefaultMallocContextSize = 30; -static Flags asan_flags; - -Flags *flags() { - return &asan_flags; -} +Flags asan_flags_dont_use_directly; // use via flags(). static const char *MaybeCallAsanDefaultOptions() { return (&__asan_default_options) ? __asan_default_options() : ""; @@ -86,28 +86,32 @@ static const char *MaybeUseAsanDefaultOptionsCompileDefiniton() { } static void ParseFlagsFromString(Flags *f, const char *str) { + ParseCommonFlagsFromString(str); + CHECK((uptr)common_flags()->malloc_context_size <= kStackTraceMax); + ParseFlag(str, &f->quarantine_size, "quarantine_size"); - ParseFlag(str, &f->symbolize, "symbolize"); ParseFlag(str, &f->verbosity, "verbosity"); ParseFlag(str, &f->redzone, "redzone"); - CHECK(f->redzone >= 16); + 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, "initialization_order"); - ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); - CHECK((uptr)f->malloc_context_size <= kStackTraceMax); + 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->use_fake_stack, "use_fake_stack"); + 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"); @@ -116,37 +120,47 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->print_legend, "print_legend"); ParseFlag(str, &f->atexit, "atexit"); ParseFlag(str, &f->disable_core, "disable_core"); - ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix"); ParseFlag(str, &f->allow_reexec, "allow_reexec"); ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history"); - ParseFlag(str, &f->log_path, "log_path"); - 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->poison_heap, "poison_heap"); ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch"); ParseFlag(str, &f->use_stack_depot, "use_stack_depot"); + ParseFlag(str, &f->strict_memcmp, "strict_memcmp"); + ParseFlag(str, &f->strict_init_order, "strict_init_order"); } void InitializeFlags(Flags *f, const char *env) { - internal_memset(f, 0, sizeof(*f)); + CommonFlags *cf = common_flags(); + cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); + cf->symbolize = true; + cf->malloc_context_size = kDefaultMallocContextSize; + cf->fast_unwind_on_fatal = false; + cf->fast_unwind_on_malloc = true; + cf->strip_path_prefix = ""; + cf->handle_ioctl = false; + cf->log_path = 0; + cf->detect_leaks = false; + cf->leak_check_at_exit = true; + internal_memset(f, 0, sizeof(*f)); f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; - f->symbolize = false; f->verbosity = 0; - f->redzone = ASAN_ALLOCATOR_VERSION == 2 ? 16 : (ASAN_LOW_MEMORY) ? 64 : 128; + f->redzone = 16; f->debug = false; f->report_globals = 1; - f->check_initialization_order = true; - f->malloc_context_size = kDeafultMallocContextSize; + f->check_initialization_order = false; f->replace_str = true; f->replace_intrin = true; f->mac_ignore_invalid_free = false; - f->use_fake_stack = true; - f->max_malloc_fill_size = 0; + 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; @@ -155,15 +169,15 @@ void InitializeFlags(Flags *f, const char *env) { f->print_legend = true; f->atexit = false; f->disable_core = (SANITIZER_WORDSIZE == 64); - f->strip_path_prefix = ""; f->allow_reexec = true; f->print_full_thread_history = true; - f->log_path = 0; - f->fast_unwind_on_fatal = false; - f->fast_unwind_on_malloc = true; f->poison_heap = true; - f->alloc_dealloc_mismatch = true; - f->use_stack_depot = true; // Only affects allocator2. + // 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->use_stack_depot = true; + f->strict_memcmp = true; + f->strict_init_order = false; // Override from compile definition. ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton()); @@ -177,6 +191,20 @@ void InitializeFlags(Flags *f, const char *env) { // 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 + + if (cf->detect_leaks && !f->use_stack_depot) { + Report("%s: detect_leaks is ignored (requires use_stack_depot).\n", + SanitizerToolName); + cf->detect_leaks = false; + } } // -------------------------- Globals --------------------- {{{1 @@ -197,8 +225,8 @@ void ShowStatsAndAbort() { // ---------------------- mmap -------------------- {{{1 // Reserve memory range [beg, end]. static void ReserveShadowMemoryRange(uptr beg, uptr end) { - CHECK((beg % GetPageSizeCached()) == 0); - CHECK(((end + 1) % GetPageSizeCached()) == 0); + CHECK_EQ((beg % GetPageSizeCached()), 0); + CHECK_EQ(((end + 1) % GetPageSizeCached()), 0); uptr size = end - beg + 1; void *res = MmapFixedNoReserve(beg, size); if (res != (void*)beg) { @@ -281,9 +309,7 @@ static NOINLINE void force_interface_symbols() { 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 28: __asan_stack_free(0, 0, 0); break; - case 29: __asan_stack_malloc(0, 0); break; - case 30: __asan_before_dynamic_init(0, 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; @@ -304,22 +330,12 @@ static void asan_atexit() { static void InitializeHighMemEnd() { #if !ASAN_FIXED_MAPPING -#if SANITIZER_WORDSIZE == 64 -# if defined(__powerpc64__) - // FIXME: - // 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 - // 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. - kHighMemEnd = (1ULL << 44) - 1; // 0x00000fffffffffffUL -# else - kHighMemEnd = (1ULL << 47) - 1; // 0x00007fffffffffffUL; -# endif -#else // SANITIZER_WORDSIZE == 32 - kHighMemEnd = (1ULL << 32) - 1; // 0xffffffff; -#endif // SANITIZER_WORDSIZE + kHighMemEnd = GetMaxVirtualAddress(); + // Increase kHighMemEnd to make sure it's properly + // aligned together with kHighMemBeg: + kHighMemEnd |= SHADOW_GRANULARITY * GetPageSizeCached() - 1; #endif // !ASAN_FIXED_MAPPING + CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0); } static void ProtectGap(uptr a, uptr size) { @@ -361,7 +377,9 @@ static void PrintAddressSpaceLayout() { } Printf("\n"); Printf("red_zone=%zu\n", (uptr)flags()->redzone); - Printf("malloc_context_size=%zu\n", (uptr)flags()->malloc_context_size); + Printf("quarantine_size=%zuM\n", (uptr)flags()->quarantine_size >> 20); + 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); @@ -380,7 +398,7 @@ using namespace __asan; // NOLINT #if !SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char* __asan_default_options() { return ""; } } // extern "C" #endif @@ -393,12 +411,28 @@ int NOINLINE __asan_set_error_exit_code(int exit_code) { void NOINLINE __asan_handle_no_return() { int local_stack; - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); + 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(); } void NOINLINE __asan_set_death_callback(void (*callback)(void)) { @@ -424,7 +458,9 @@ void __asan_init() { // initialization steps look at flags(). const char *options = GetEnv("ASAN_OPTIONS"); InitializeFlags(flags(), options); - __sanitizer_set_report_path(flags()->log_path); + __sanitizer_set_report_path(common_flags()->log_path); + __asan_option_detect_stack_use_after_return = + flags()->detect_stack_use_after_return; if (flags()->verbosity && options) { Report("Parsed ASAN_OPTIONS: %s\n", options); @@ -447,12 +483,12 @@ void __asan_init() { ReplaceOperatorsNewAndDelete(); uptr shadow_start = kLowShadowBeg; - if (kLowShadowBeg) shadow_start -= GetMmapGranularity(); - uptr shadow_end = kHighShadowEnd; + if (kLowShadowBeg) + shadow_start -= GetMmapGranularity(); bool full_shadow_is_available = - MemoryRangeIsAvailable(shadow_start, shadow_end); + MemoryRangeIsAvailable(shadow_start, kHighShadowEnd); -#if ASAN_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING +#if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING if (!full_shadow_is_available) { kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0; kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0; @@ -476,7 +512,7 @@ void __asan_init() { ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); } else if (kMidMemBeg && MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && - MemoryRangeIsAvailable(kMidMemEnd + 1, shadow_end)) { + MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { CHECK(kLowShadowBeg != kLowShadowEnd); // mmap the low shadow plus at least one page at the left. ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); @@ -496,12 +532,16 @@ void __asan_init() { } InstallSignalHandlers(); + + AsanTSDInit(AsanThread::TSDDtor); + // Allocator should be initialized before starting external symbolizer, as + // fork() on Mac locks the allocator. + InitializeAllocator(); + // Start symbolizer process if necessary. - if (flags()->symbolize) { - const char *external_symbolizer = GetEnv("ASAN_SYMBOLIZER_PATH"); - if (external_symbolizer) { - InitializeExternalSymbolizer(external_symbolizer); - } + if (common_flags()->symbolize && &getSymbolizer) { + getSymbolizer() + ->InitializeExternal(common_flags()->external_symbolizer_path); } // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited @@ -509,11 +549,24 @@ void __asan_init() { asan_inited = 1; asan_init_is_running = false; - asanThreadRegistry().Init(); - asanThreadRegistry().GetMain()->ThreadStart(); + 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); + SetCurrentThread(main_thread); + main_thread->ThreadStart(internal_getpid()); force_interface_symbols(); // no-op. - InitializeAllocator(); +#if CAN_SANITIZE_LEAKS + __lsan::InitCommonLsan(); + if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { + Atexit(__lsan::DoLeakCheck); + } +#endif // CAN_SANITIZE_LEAKS if (flags()->verbosity) { Report("AddressSanitizer Init done\n"); diff --git a/libsanitizer/asan/asan_stack.cc b/libsanitizer/asan/asan_stack.cc index 999cbfba757..74952518642 100644 --- a/libsanitizer/asan/asan_stack.cc +++ b/libsanitizer/asan/asan_stack.cc @@ -12,6 +12,7 @@ #include "asan_internal.h" #include "asan_flags.h" #include "asan_stack.h" +#include "sanitizer_common/sanitizer_flags.h" namespace __asan { @@ -22,8 +23,8 @@ static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer, } void PrintStack(StackTrace *stack) { - stack->PrintStack(stack->trace, stack->size, flags()->symbolize, - flags()->strip_path_prefix, MaybeCallAsanSymbolize); + stack->PrintStack(stack->trace, stack->size, common_flags()->symbolize, + common_flags()->strip_path_prefix, MaybeCallAsanSymbolize); } } // namespace __asan @@ -33,8 +34,8 @@ void PrintStack(StackTrace *stack) { // 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 !defined(_WIN32) && !SANITIZER_SUPPORTS_WEAK_HOOKS -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +#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; } diff --git a/libsanitizer/asan/asan_stack.h b/libsanitizer/asan/asan_stack.h index 6a5ffc934cc..3c0ac31f6c6 100644 --- a/libsanitizer/asan/asan_stack.h +++ b/libsanitizer/asan/asan_stack.h @@ -12,12 +12,13 @@ #ifndef ASAN_STACK_H #define ASAN_STACK_H -#include "sanitizer_common/sanitizer_stacktrace.h" #include "asan_flags.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" namespace __asan { -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast); void PrintStack(StackTrace *stack); } // namespace __asan @@ -25,10 +26,24 @@ void PrintStack(StackTrace *stack); // 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. -// fast_unwind is currently unused. -#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ - StackTrace stack; \ - GetStackTrace(&stack, max_s, pc, bp, fast) +#if SANITIZER_WINDOWS +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ + StackTrace stack; \ + GetStackTrace(&stack, max_s, pc, bp, 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 && (t = GetCurrentThread()) && !t->isUnwinding()) { \ + uptr stack_top = t->stack_top(); \ + uptr stack_bottom = t->stack_bottom(); \ + ScopedUnwinding unwind_scope(t); \ + GetStackTrace(&stack, max_s, pc, bp, stack_top, stack_bottom, fast); \ + } \ + } +#endif // SANITIZER_WINDOWS // 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 @@ -40,24 +55,24 @@ void PrintStack(StackTrace *stack); #define GET_STACK_TRACE_FATAL(pc, bp) \ GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp, \ - flags()->fast_unwind_on_fatal) + common_flags()->fast_unwind_on_fatal) -#define GET_STACK_TRACE_FATAL_HERE \ - GET_STACK_TRACE(kStackTraceMax, 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_THREAD \ +#define GET_STACK_TRACE_THREAD \ GET_STACK_TRACE(kStackTraceMax, true) -#define GET_STACK_TRACE_MALLOC \ - GET_STACK_TRACE(flags()->malloc_context_size, \ - flags()->fast_unwind_on_malloc) +#define GET_STACK_TRACE_MALLOC \ + GET_STACK_TRACE(common_flags()->malloc_context_size, \ + common_flags()->fast_unwind_on_malloc) #define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC #define PRINT_CURRENT_STACK() \ { \ GET_STACK_TRACE(kStackTraceMax, \ - flags()->fast_unwind_on_fatal); \ + common_flags()->fast_unwind_on_fatal); \ PrintStack(&stack); \ } diff --git a/libsanitizer/asan/asan_stats.cc b/libsanitizer/asan/asan_stats.cc index 935b33e20ac..71c8582e81c 100644 --- a/libsanitizer/asan/asan_stats.cc +++ b/libsanitizer/asan/asan_stats.cc @@ -12,13 +12,18 @@ #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_stats.h" -#include "asan_thread_registry.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_stackdepot.h" namespace __asan { AsanStats::AsanStats() { - CHECK(REAL(memset) != 0); + Clear(); +} + +void AsanStats::Clear() { + CHECK(REAL(memset)); REAL(memset)(this, 0, sizeof(AsanStats)); } @@ -51,11 +56,73 @@ void AsanStats::Print() { malloc_large, malloc_small_slow); } +void AsanStats::MergeFrom(const AsanStats *stats) { + uptr *dst_ptr = reinterpret_cast(this); + const uptr *src_ptr = reinterpret_cast(stats); + uptr num_fields = sizeof(*this) / sizeof(uptr); + for (uptr i = 0; i < num_fields; i++) + dst_ptr[i] += src_ptr[i]; +} + static BlockingMutex print_lock(LINKER_INITIALIZED); +static AsanStats unknown_thread_stats(LINKER_INITIALIZED); +static AsanStats dead_threads_stats(LINKER_INITIALIZED); +static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED); +// Required for malloc_zone_statistics() on OS X. This can't be stored in +// per-thread AsanStats. +static uptr max_malloced_memory; + +static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) { + AsanStats *accumulated_stats = reinterpret_cast(arg); + AsanThreadContext *tctx = static_cast(tctx_base); + if (AsanThread *t = tctx->thread) + accumulated_stats->MergeFrom(&t->stats()); +} + +static void GetAccumulatedStats(AsanStats *stats) { + stats->Clear(); + { + ThreadRegistryLock l(&asanThreadRegistry()); + asanThreadRegistry() + .RunCallbackForEachThreadLocked(MergeThreadStats, stats); + } + stats->MergeFrom(&unknown_thread_stats); + { + BlockingMutexLock lock(&dead_threads_stats_lock); + stats->MergeFrom(&dead_threads_stats); + } + // This is not very accurate: we may miss allocation peaks that happen + // between two updates of accumulated_stats_. For more accurate bookkeeping + // the maximum should be updated on every malloc(), which is unacceptable. + if (max_malloced_memory < stats->malloced) { + max_malloced_memory = stats->malloced; + } +} + +void FlushToDeadThreadStats(AsanStats *stats) { + BlockingMutexLock lock(&dead_threads_stats_lock); + dead_threads_stats.MergeFrom(stats); + stats->Clear(); +} + +void FillMallocStatistics(AsanMallocStats *malloc_stats) { + AsanStats stats; + GetAccumulatedStats(&stats); + malloc_stats->blocks_in_use = stats.mallocs; + malloc_stats->size_in_use = stats.malloced; + malloc_stats->max_size_in_use = max_malloced_memory; + malloc_stats->size_allocated = stats.mmaped; +} + +AsanStats &GetCurrentThreadStats() { + AsanThread *t = GetCurrentThread(); + return (t) ? t->stats() : unknown_thread_stats; +} + static void PrintAccumulatedStats() { AsanStats stats; - asanThreadRegistry().GetAccumulatedStats(&stats); + GetAccumulatedStats(&stats); // Use lock to keep reports from mixing up. BlockingMutexLock lock(&print_lock); stats.Print(); @@ -71,15 +138,33 @@ static void PrintAccumulatedStats() { using namespace __asan; // NOLINT uptr __asan_get_current_allocated_bytes() { - return asanThreadRegistry().GetCurrentAllocatedBytes(); + AsanStats stats; + GetAccumulatedStats(&stats); + uptr malloced = stats.malloced; + uptr freed = stats.freed; + // Return sane value if malloced < freed due to racy + // way we update accumulated stats. + return (malloced > freed) ? malloced - freed : 1; } uptr __asan_get_heap_size() { - return asanThreadRegistry().GetHeapSize(); + AsanStats stats; + GetAccumulatedStats(&stats); + return stats.mmaped - stats.munmaped; } uptr __asan_get_free_bytes() { - return asanThreadRegistry().GetFreeBytes(); + AsanStats stats; + GetAccumulatedStats(&stats); + uptr total_free = stats.mmaped + - stats.munmaped + + stats.really_freed + + stats.really_freed_redzones; + uptr total_used = stats.malloced + + stats.malloced_redzones; + // Return sane value if total_free < total_used due to racy + // way we update accumulated stats. + return (total_free > total_used) ? total_free - total_used : 1; } uptr __asan_get_unmapped_bytes() { diff --git a/libsanitizer/asan/asan_stats.h b/libsanitizer/asan/asan_stats.h index fd27451aef2..2f964f8d052 100644 --- a/libsanitizer/asan/asan_stats.h +++ b/libsanitizer/asan/asan_stats.h @@ -50,10 +50,17 @@ struct AsanStats { // Default ctor for thread-local stats. AsanStats(); - // Prints formatted stats to stderr. - void Print(); + void Print(); // Prints formatted stats to stderr. + void Clear(); + void MergeFrom(const AsanStats *stats); }; +// Returns stats for GetCurrentThread(), or stats for fake "unknown thread" +// if GetCurrentThread() returns 0. +AsanStats &GetCurrentThreadStats(); +// Flushes a given stats into accumulated stats of dead threads. +void FlushToDeadThreadStats(AsanStats *stats); + // A cross-platform equivalent of malloc_statistics_t on Mac OS. struct AsanMallocStats { uptr blocks_in_use; @@ -62,6 +69,8 @@ struct AsanMallocStats { uptr size_allocated; }; +void FillMallocStatistics(AsanMallocStats *malloc_stats); + } // namespace __asan #endif // ASAN_STATS_H diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc index 02f49dd59ef..1da714c6013 100644 --- a/libsanitizer/asan/asan_thread.cc +++ b/libsanitizer/asan/asan_thread.cc @@ -11,46 +11,82 @@ //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_interceptors.h" +#include "asan_poisoning.h" #include "asan_stack.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "asan_mapping.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "lsan/lsan_common.h" namespace __asan { -AsanThread::AsanThread(LinkerInitialized x) - : fake_stack_(x), - malloc_storage_(x), - stats_(x) { } +// AsanThreadContext implementation. -AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine, - void *arg, StackTrace *stack) { +void AsanThreadContext::OnCreated(void *arg) { + CreateThreadContextArgs *args = static_cast(arg); + if (args->stack) { + internal_memcpy(&stack, args->stack, sizeof(stack)); + } + thread = args->thread; + thread->set_context(this); +} + +void AsanThreadContext::OnFinished() { + // Drop the link to the AsanThread object. + thread = 0; +} + +// MIPS requires aligned address +static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)]; +static ThreadRegistry *asan_thread_registry; + +static ThreadContextBase *GetAsanThreadContext(u32 tid) { + void *mem = MmapOrDie(sizeof(AsanThreadContext), "AsanThreadContext"); + return new(mem) AsanThreadContext(tid); +} + +ThreadRegistry &asanThreadRegistry() { + static bool initialized; + // Don't worry about thread_safety - this should be called when there is + // a single thread. + if (!initialized) { + // Never reuse ASan threads: we store pointer to AsanThreadContext + // in TSD and can't reliably tell when no more TSD destructors will + // be called. It would be wrong to reuse AsanThreadContext for another + // thread before all TSD destructors will be called for it. + asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry( + GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads); + initialized = true; + } + return *asan_thread_registry; +} + +AsanThreadContext *GetThreadContextByTidLocked(u32 tid) { + return static_cast( + asanThreadRegistry().GetThreadLocked(tid)); +} + +// AsanThread implementation. + +AsanThread *AsanThread::Create(thread_callback_t start_routine, + void *arg) { uptr PageSize = GetPageSizeCached(); uptr size = RoundUpTo(sizeof(AsanThread), PageSize); AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__); thread->start_routine_ = start_routine; thread->arg_ = arg; - - const uptr kSummaryAllocSize = PageSize; - CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize); - AsanThreadSummary *summary = - (AsanThreadSummary*)MmapOrDie(PageSize, "AsanThreadSummary"); - summary->Init(parent_tid, stack); - summary->set_thread(thread); - thread->set_summary(summary); + thread->context_ = 0; return thread; } -void AsanThreadSummary::TSDDtor(void *tsd) { - AsanThreadSummary *summary = (AsanThreadSummary*)tsd; - if (flags()->verbosity >= 1) { - Report("T%d TSDDtor\n", summary->tid()); - } - if (summary->thread()) { - summary->thread()->Destroy(); - } +void AsanThread::TSDDtor(void *tsd) { + AsanThreadContext *context = (AsanThreadContext*)tsd; + if (flags()->verbosity >= 1) + Report("T%d TSDDtor\n", context->tid); + if (context->thread) + context->thread->Destroy(); } void AsanThread::Destroy() { @@ -58,41 +94,68 @@ void AsanThread::Destroy() { Report("T%d exited\n", tid()); } - asanThreadRegistry().UnregisterThread(this); - CHECK(summary()->thread() == 0); + 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. - ClearShadowForThreadStack(); - fake_stack().Cleanup(); + ClearShadowForThreadStackAndTLS(); + DeleteFakeStack(); uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached()); UnmapOrDie(this, size); } +// We want to create the FakeStack lazyly on the first use, but not eralier +// than the stack size is known and the procedure has to be async-signal safe. +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; + 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 (atomic_compare_exchange_strong( + reinterpret_cast(&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(flags()->uar_stack_size_log); + fake_stack_ = FakeStack::Create(stack_size_log); + SetTLSFakeStack(fake_stack_); + return fake_stack_; + } + return 0; +} + void AsanThread::Init() { - SetThreadStackTopAndBottom(); + SetThreadStackAndTls(); CHECK(AddrIsInMem(stack_bottom_)); CHECK(AddrIsInMem(stack_top_ - 1)); - ClearShadowForThreadStack(); + ClearShadowForThreadStackAndTLS(); if (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_.Init(stack_size()); + fake_stack_ = 0; // Will be initialized lazily if needed. AsanPlatformThreadInit(); } -thread_return_t AsanThread::ThreadStart() { +thread_return_t AsanThread::ThreadStart(uptr os_id) { Init(); + asanThreadRegistry().StartThread(tid(), os_id, 0); if (flags()->use_sigaltstack) SetAlternateSignalStack(); if (!start_routine_) { // start_routine_ == 0 if we're on the main thread or on one of the // OS X libdispatch worker threads. But nobody is supposed to call // ThreadStart() for the worker threads. - CHECK(tid() == 0); + CHECK_EQ(tid(), 0); return 0; } @@ -105,24 +168,33 @@ thread_return_t AsanThread::ThreadStart() { return res; } -void AsanThread::SetThreadStackTopAndBottom() { - GetThreadStackTopAndBottom(tid() == 0, &stack_top_, &stack_bottom_); +void AsanThread::SetThreadStackAndTls() { + uptr tls_size = 0; + GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size_, &tls_begin_, + &tls_size); + stack_top_ = stack_bottom_ + stack_size_; + tls_end_ = tls_begin_ + tls_size; + int local; CHECK(AddrIsInStack((uptr)&local)); } -void AsanThread::ClearShadowForThreadStack() { +void AsanThread::ClearShadowForThreadStackAndTLS() { PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); + if (tls_begin_ != tls_end_) + PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0); } -const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { +const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset, + uptr *frame_pc) { uptr bottom = 0; if (AddrIsInStack(addr)) { bottom = stack_bottom(); - } else { - bottom = fake_stack().AddrIsInFakeStack(addr); + } 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]; } uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr. @@ -147,7 +219,104 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { 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]; } +static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base, + void *addr) { + AsanThreadContext *tctx = static_cast(tctx_base); + AsanThread *t = tctx->thread; + if (!t) return false; + if (t->AddrIsInStack((uptr)addr)) return true; + if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr)) + return true; + return false; +} + +AsanThread *GetCurrentThread() { + AsanThreadContext *context = + reinterpret_cast(AsanTSDGet()); + if (!context) { + if (SANITIZER_ANDROID) { + // On Android, libc constructor is called _after_ asan_init, and cleans up + // TSD. Try to figure out if this is still the main thread by the stack + // address. We are not entirely sure that we have correct main thread + // limits, so only do this magic on Android, and only if the found thread + // is the main thread. + AsanThreadContext *tctx = GetThreadContextByTidLocked(0); + if (ThreadStackContainsAddress(tctx, &context)) { + SetCurrentThread(tctx->thread); + return tctx->thread; + } + } + return 0; + } + return context->thread; +} + +void SetCurrentThread(AsanThread *t) { + CHECK(t->context()); + if (flags()->verbosity >= 2) { + Report("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()); + CHECK_EQ(t->context(), AsanTSDGet()); +} + +u32 GetCurrentTidOrInvalid() { + AsanThread *t = GetCurrentThread(); + return t ? t->tid() : kInvalidTid; +} + +AsanThread *FindThreadByStackAddress(uptr addr) { + asanThreadRegistry().CheckLocked(); + AsanThreadContext *tctx = static_cast( + asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress, + (void *)addr)); + return tctx ? tctx->thread : 0; +} + +void EnsureMainThreadIDIsCorrect() { + AsanThreadContext *context = + reinterpret_cast(AsanTSDGet()); + if (context && (context->tid == 0)) + context->os_id = GetTid(); +} } // namespace __asan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, + uptr *cache_begin, uptr *cache_end) { + __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>( + __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id)); + if (!context) return false; + __asan::AsanThread *t = context->thread; + if (!t) return false; + *stack_begin = t->stack_bottom(); + *stack_end = t->stack_top(); + *tls_begin = t->tls_begin(); + *tls_end = t->tls_end(); + // ASan doesn't keep allocator caches in TLS, so these are unused. + *cache_begin = 0; + *cache_end = 0; + return true; +} + +void LockThreadRegistry() { + __asan::asanThreadRegistry().Lock(); +} + +void UnlockThreadRegistry() { + __asan::asanThreadRegistry().Unlock(); +} + +void EnsureMainThreadIDIsCorrect() { + __asan::EnsureMainThreadIDIsCorrect(); +} +} // namespace __lsan diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h index f385ec35fcd..f21971ff430 100644 --- a/libsanitizer/asan/asan_thread.h +++ b/libsanitizer/asan/asan_thread.h @@ -14,99 +14,148 @@ #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_libc.h" +#include "sanitizer_common/sanitizer_thread_registry.h" namespace __asan { const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits. +const u32 kMaxNumberOfThreads = (1 << 22); // 4M class AsanThread; // These objects are created for every thread and are never deleted, // so we can find them by tid even if the thread is long dead. -class AsanThreadSummary { +class AsanThreadContext : public ThreadContextBase { public: - explicit AsanThreadSummary(LinkerInitialized) { } // for T0. - void Init(u32 parent_tid, StackTrace *stack) { - parent_tid_ = parent_tid; - announced_ = false; - tid_ = kInvalidTid; - if (stack) { - internal_memcpy(&stack_, stack, sizeof(*stack)); - } - thread_ = 0; - name_[0] = 0; + explicit AsanThreadContext(int tid) + : ThreadContextBase(tid), + announced(false), + thread(0) { + internal_memset(&stack, 0, sizeof(stack)); } - u32 tid() { return tid_; } - void set_tid(u32 tid) { tid_ = tid; } - u32 parent_tid() { return parent_tid_; } - bool announced() { return announced_; } - void set_announced(bool announced) { announced_ = announced; } - StackTrace *stack() { return &stack_; } - AsanThread *thread() { return thread_; } - void set_thread(AsanThread *thread) { thread_ = thread; } - static void TSDDtor(void *tsd); - void set_name(const char *name) { - internal_strncpy(name_, name, sizeof(name_) - 1); - } - const char *name() { return name_; } + bool announced; + StackTrace stack; + AsanThread *thread; - private: - u32 tid_; - u32 parent_tid_; - bool announced_; - StackTrace stack_; - AsanThread *thread_; - char name_[128]; + void OnCreated(void *arg); + void OnFinished(); }; -// AsanThreadSummary objects are never freed, so we need many of them. -COMPILER_CHECK(sizeof(AsanThreadSummary) <= 4094); +// AsanThreadContext objects are never freed, so we need many of them. +COMPILER_CHECK(sizeof(AsanThreadContext) <= 4096); // AsanThread are stored in TSD and destroyed when the thread dies. class AsanThread { public: - explicit AsanThread(LinkerInitialized); // for T0. - static AsanThread *Create(u32 parent_tid, thread_callback_t start_routine, - void *arg, StackTrace *stack); + static AsanThread *Create(thread_callback_t start_routine, void *arg); + static void TSDDtor(void *tsd); void Destroy(); void Init(); // Should be called from the thread itself. - thread_return_t ThreadStart(); + thread_return_t ThreadStart(uptr os_id); uptr stack_top() { return stack_top_; } uptr stack_bottom() { return stack_bottom_; } - uptr stack_size() { return stack_top_ - stack_bottom_; } - u32 tid() { return summary_->tid(); } - AsanThreadSummary *summary() { return summary_; } - void set_summary(AsanThreadSummary *summary) { summary_ = summary; } + uptr stack_size() { return stack_size_; } + uptr tls_begin() { return tls_begin_; } + uptr tls_end() { return tls_end_; } + u32 tid() { return context_->tid; } + AsanThreadContext *context() { return context_; } + void set_context(AsanThreadContext *context) { context_ = context; } - const char *GetFrameNameByAddr(uptr addr, uptr *offset); + const char *GetFrameNameByAddr(uptr addr, uptr *offset, uptr *frame_pc); bool AddrIsInStack(uptr addr) { return addr >= stack_bottom_ && addr < stack_top_; } - FakeStack &fake_stack() { return fake_stack_; } + void DeleteFakeStack() { + if (!fake_stack_) return; + FakeStack *t = fake_stack_; + fake_stack_ = 0; + SetTLSFakeStack(0); + t->Destroy(); + } + + bool has_fake_stack() { + return (reinterpret_cast(fake_stack_) > 1); + } + + FakeStack *fake_stack() { + if (!__asan_option_detect_stack_use_after_return) + return 0; + if (!has_fake_stack()) + return AsyncSignalSafeLazyInitFakeStack(); + return fake_stack_; + } + + // True is this thread is currently unwinding stack (i.e. collecting a stack + // trace). Used to prevent deadlocks on platforms where libc unwinder calls + // malloc internally. See PR17116 for more details. + bool isUnwinding() const { return unwinding; } + void setUnwinding(bool b) { unwinding = b; } + AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } AsanStats &stats() { return stats_; } private: - void SetThreadStackTopAndBottom(); - void ClearShadowForThreadStack(); - AsanThreadSummary *summary_; + AsanThread() : unwinding(false) {} + void SetThreadStackAndTls(); + void ClearShadowForThreadStackAndTLS(); + FakeStack *AsyncSignalSafeLazyInitFakeStack(); + + AsanThreadContext *context_; thread_callback_t start_routine_; void *arg_; uptr stack_top_; uptr stack_bottom_; + // stack_size_ == stack_top_ - stack_bottom_; + // It needs to be set in a async-signal-safe manner. + uptr stack_size_; + uptr tls_begin_; + uptr tls_end_; - FakeStack fake_stack_; + FakeStack *fake_stack_; AsanThreadLocalMallocStorage malloc_storage_; AsanStats stats_; + bool unwinding; }; +// ScopedUnwinding is a scope for stacktracing member of a context +class ScopedUnwinding { + public: + explicit ScopedUnwinding(AsanThread *t) : thread(t) { + t->setUnwinding(true); + } + ~ScopedUnwinding() { thread->setUnwinding(false); } + + private: + AsanThread *thread; +}; + +struct CreateThreadContextArgs { + AsanThread *thread; + StackTrace *stack; +}; + +// Returns a single instance of registry. +ThreadRegistry &asanThreadRegistry(); + +// Must be called under ThreadRegistryLock. +AsanThreadContext *GetThreadContextByTidLocked(u32 tid); + +// Get the current thread. May return 0. +AsanThread *GetCurrentThread(); +void SetCurrentThread(AsanThread *t); +u32 GetCurrentTidOrInvalid(); +AsanThread *FindThreadByStackAddress(uptr addr); + +// Used to handle fork(). +void EnsureMainThreadIDIsCorrect(); } // namespace __asan #endif // ASAN_THREAD_H diff --git a/libsanitizer/asan/asan_thread_registry.cc b/libsanitizer/asan/asan_thread_registry.cc deleted file mode 100644 index 8fda9b6ea0a..00000000000 --- a/libsanitizer/asan/asan_thread_registry.cc +++ /dev/null @@ -1,196 +0,0 @@ -//===-- asan_thread_registry.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. -// -// AsanThreadRegistry-related code. AsanThreadRegistry is a container -// for summaries of all created threads. -//===----------------------------------------------------------------------===// - -#include "asan_stack.h" -#include "asan_thread.h" -#include "asan_thread_registry.h" -#include "sanitizer_common/sanitizer_common.h" - -namespace __asan { - -static AsanThreadRegistry asan_thread_registry(LINKER_INITIALIZED); - -AsanThreadRegistry &asanThreadRegistry() { - return asan_thread_registry; -} - -AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x) - : main_thread_(x), - main_thread_summary_(x), - accumulated_stats_(x), - max_malloced_memory_(x), - mu_(x) { } - -void AsanThreadRegistry::Init() { - AsanTSDInit(AsanThreadSummary::TSDDtor); - main_thread_.set_summary(&main_thread_summary_); - main_thread_summary_.set_thread(&main_thread_); - RegisterThread(&main_thread_); - SetCurrent(&main_thread_); - // At this point only one thread exists. - inited_ = true; -} - -void AsanThreadRegistry::RegisterThread(AsanThread *thread) { - BlockingMutexLock lock(&mu_); - u32 tid = n_threads_; - n_threads_++; - CHECK(n_threads_ < kMaxNumberOfThreads); - - AsanThreadSummary *summary = thread->summary(); - CHECK(summary != 0); - summary->set_tid(tid); - thread_summaries_[tid] = summary; -} - -void AsanThreadRegistry::UnregisterThread(AsanThread *thread) { - BlockingMutexLock lock(&mu_); - FlushToAccumulatedStatsUnlocked(&thread->stats()); - AsanThreadSummary *summary = thread->summary(); - CHECK(summary); - summary->set_thread(0); -} - -AsanThread *AsanThreadRegistry::GetMain() { - return &main_thread_; -} - -AsanThread *AsanThreadRegistry::GetCurrent() { - AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet(); - if (!summary) { -#if ASAN_ANDROID - // On Android, libc constructor is called _after_ asan_init, and cleans up - // TSD. Try to figure out if this is still the main thread by the stack - // address. We are not entirely sure that we have correct main thread - // limits, so only do this magic on Android, and only if the found thread is - // the main thread. - AsanThread* thread = FindThreadByStackAddress((uptr)&summary); - if (thread && thread->tid() == 0) { - SetCurrent(thread); - return thread; - } -#endif - return 0; - } - return summary->thread(); -} - -void AsanThreadRegistry::SetCurrent(AsanThread *t) { - CHECK(t->summary()); - if (flags()->verbosity >= 2) { - Report("SetCurrent: %p for thread %p\n", - t->summary(), (void*)GetThreadSelf()); - } - // Make sure we do not reset the current AsanThread. - CHECK(AsanTSDGet() == 0); - AsanTSDSet(t->summary()); - CHECK(AsanTSDGet() == t->summary()); -} - -AsanStats &AsanThreadRegistry::GetCurrentThreadStats() { - AsanThread *t = GetCurrent(); - return (t) ? t->stats() : main_thread_.stats(); -} - -void AsanThreadRegistry::GetAccumulatedStats(AsanStats *stats) { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - internal_memcpy(stats, &accumulated_stats_, sizeof(accumulated_stats_)); -} - -uptr AsanThreadRegistry::GetCurrentAllocatedBytes() { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - uptr malloced = accumulated_stats_.malloced; - uptr freed = accumulated_stats_.freed; - // Return sane value if malloced < freed due to racy - // way we update accumulated stats. - return (malloced > freed) ? malloced - freed : 1; -} - -uptr AsanThreadRegistry::GetHeapSize() { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - return accumulated_stats_.mmaped - accumulated_stats_.munmaped; -} - -uptr AsanThreadRegistry::GetFreeBytes() { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - uptr total_free = accumulated_stats_.mmaped - - accumulated_stats_.munmaped - + accumulated_stats_.really_freed - + accumulated_stats_.really_freed_redzones; - uptr total_used = accumulated_stats_.malloced - + accumulated_stats_.malloced_redzones; - // Return sane value if total_free < total_used due to racy - // way we update accumulated stats. - return (total_free > total_used) ? total_free - total_used : 1; -} - -// Return several stats counters with a single call to -// UpdateAccumulatedStatsUnlocked(). -void AsanThreadRegistry::FillMallocStatistics(AsanMallocStats *malloc_stats) { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - malloc_stats->blocks_in_use = accumulated_stats_.mallocs; - malloc_stats->size_in_use = accumulated_stats_.malloced; - malloc_stats->max_size_in_use = max_malloced_memory_; - malloc_stats->size_allocated = accumulated_stats_.mmaped; -} - -AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) { - CHECK(tid < n_threads_); - CHECK(thread_summaries_[tid]); - return thread_summaries_[tid]; -} - -AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) { - BlockingMutexLock lock(&mu_); - for (u32 tid = 0; tid < n_threads_; tid++) { - AsanThread *t = thread_summaries_[tid]->thread(); - if (!t || !(t->fake_stack().StackSize())) continue; - if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) { - return t; - } - } - return 0; -} - -void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() { - for (u32 tid = 0; tid < n_threads_; tid++) { - AsanThread *t = thread_summaries_[tid]->thread(); - if (t != 0) { - FlushToAccumulatedStatsUnlocked(&t->stats()); - } - } - // This is not very accurate: we may miss allocation peaks that happen - // between two updates of accumulated_stats_. For more accurate bookkeeping - // the maximum should be updated on every malloc(), which is unacceptable. - if (max_malloced_memory_ < accumulated_stats_.malloced) { - max_malloced_memory_ = accumulated_stats_.malloced; - } -} - -void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) { - // AsanStats consists of variables of type uptr only. - uptr *dst = (uptr*)&accumulated_stats_; - uptr *src = (uptr*)stats; - uptr num_fields = sizeof(AsanStats) / sizeof(uptr); - for (uptr i = 0; i < num_fields; i++) { - dst[i] += src[i]; - src[i] = 0; - } -} - -} // namespace __asan diff --git a/libsanitizer/asan/asan_thread_registry.h b/libsanitizer/asan/asan_thread_registry.h deleted file mode 100644 index 8c3d0c886e0..00000000000 --- a/libsanitizer/asan/asan_thread_registry.h +++ /dev/null @@ -1,83 +0,0 @@ -//===-- asan_thread_registry.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_thread_registry.cc -//===----------------------------------------------------------------------===// - -#ifndef ASAN_THREAD_REGISTRY_H -#define ASAN_THREAD_REGISTRY_H - -#include "asan_stack.h" -#include "asan_stats.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_mutex.h" - -namespace __asan { - -// Stores summaries of all created threads, returns current thread, -// thread by tid, thread by stack address. There is a single instance -// of AsanThreadRegistry for the whole program. -// AsanThreadRegistry is thread-safe. -class AsanThreadRegistry { - public: - explicit AsanThreadRegistry(LinkerInitialized); - void Init(); - void RegisterThread(AsanThread *thread); - void UnregisterThread(AsanThread *thread); - - AsanThread *GetMain(); - // Get the current thread. May return 0. - AsanThread *GetCurrent(); - void SetCurrent(AsanThread *t); - - u32 GetCurrentTidOrInvalid() { - if (!inited_) return 0; - AsanThread *t = GetCurrent(); - return t ? t->tid() : kInvalidTid; - } - - // Returns stats for GetCurrent(), or stats for - // T0 if GetCurrent() returns 0. - AsanStats &GetCurrentThreadStats(); - // Flushes all thread-local stats to accumulated stats, and makes - // a copy of accumulated stats. - void GetAccumulatedStats(AsanStats *stats); - uptr GetCurrentAllocatedBytes(); - uptr GetHeapSize(); - uptr GetFreeBytes(); - void FillMallocStatistics(AsanMallocStats *malloc_stats); - - AsanThreadSummary *FindByTid(u32 tid); - AsanThread *FindThreadByStackAddress(uptr addr); - - private: - void UpdateAccumulatedStatsUnlocked(); - // Adds values of all counters in "stats" to accumulated stats, - // and fills "stats" with zeroes. - void FlushToAccumulatedStatsUnlocked(AsanStats *stats); - - static const u32 kMaxNumberOfThreads = (1 << 22); // 4M - AsanThreadSummary *thread_summaries_[kMaxNumberOfThreads]; - AsanThread main_thread_; - AsanThreadSummary main_thread_summary_; - AsanStats accumulated_stats_; - // Required for malloc_zone_statistics() on OS X. This can't be stored in - // per-thread AsanStats. - uptr max_malloced_memory_; - u32 n_threads_; - BlockingMutex mu_; - bool inited_; -}; - -// Returns a single instance of registry. -AsanThreadRegistry &asanThreadRegistry(); - -} // namespace __asan - -#endif // ASAN_THREAD_REGISTRY_H diff --git a/libsanitizer/asan/asan_win.cc b/libsanitizer/asan/asan_win.cc index 6acfeebc8bf..ed785b69281 100644 --- a/libsanitizer/asan/asan_win.cc +++ b/libsanitizer/asan/asan_win.cc @@ -9,7 +9,9 @@ // // Windows-specific details. //===----------------------------------------------------------------------===// -#ifdef _WIN32 + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS #include #include @@ -21,6 +23,14 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" +extern "C" { + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_should_detect_stack_use_after_return() { + __asan_init(); + return __asan_option_detect_stack_use_after_return; + } +} + namespace __asan { // ---------------------- Stacktraces, symbols, etc. ---------------- {{{1 @@ -28,30 +38,6 @@ static BlockingMutex dbghelp_lock(LINKER_INITIALIZED); static bool dbghelp_initialized = false; #pragma comment(lib, "dbghelp.lib") -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) { - (void)fast; - stack->max_size = max_s; - void *tmp[kStackTraceMax]; - - // FIXME: CaptureStackBackTrace might be too slow for us. - // FIXME: Compare with StackWalk64. - // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc - uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0); - uptr offset = 0; - // Skip the RTL frames by searching for the PC in the stacktrace. - // FIXME: this doesn't work well for the malloc/free stacks yet. - for (uptr i = 0; i < cs_ret; i++) { - if (pc != (uptr)tmp[i]) - continue; - offset = i; - break; - } - - stack->size = cs_ret - offset; - for (uptr i = 0; i < stack->size; i++) - stack->trace[i] = (uptr)tmp[i + offset]; -} - // ---------------------- TSD ---------------- {{{1 static bool tsd_key_inited = false; diff --git a/libsanitizer/asan/libtool-version b/libsanitizer/asan/libtool-version index 204fdd2d8e5..9a16cf57844 100644 --- a/libsanitizer/asan/libtool-version +++ b/libsanitizer/asan/libtool-version @@ -1,6 +1,6 @@ -# This file is used to maintain libtool version info for libmudflap. See +# This file is used to maintain libtool version info for libasan. See # the libtool manual to understand the meaning of the fields. This is # a separate file so that version updates don't involve re-running # automake. # CURRENT:REVISION:AGE -0:0:0 +1:0:0 diff --git a/libsanitizer/configure b/libsanitizer/configure index 5d393ca42f3..5e425d1d9e3 100755 --- a/libsanitizer/configure +++ b/libsanitizer/configure @@ -14549,7 +14549,7 @@ fi ac_config_files="$ac_config_files Makefile" -ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile asan/Makefile ubsan/Makefile" +ac_config_files="$ac_config_files interception/Makefile sanitizer_common/Makefile lsan/Makefile asan/Makefile ubsan/Makefile" if test "x$TSAN_SUPPORTED" = "xyes"; then @@ -15679,6 +15679,7 @@ do "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "interception/Makefile") CONFIG_FILES="$CONFIG_FILES interception/Makefile" ;; "sanitizer_common/Makefile") CONFIG_FILES="$CONFIG_FILES sanitizer_common/Makefile" ;; + "lsan/Makefile") CONFIG_FILES="$CONFIG_FILES lsan/Makefile" ;; "asan/Makefile") CONFIG_FILES="$CONFIG_FILES asan/Makefile" ;; "ubsan/Makefile") CONFIG_FILES="$CONFIG_FILES ubsan/Makefile" ;; "tsan/Makefile") CONFIG_FILES="$CONFIG_FILES tsan/Makefile" ;; @@ -17026,6 +17027,17 @@ _EOF ;; "sanitizer_common/Makefile":F) cat > vpsed$$ << \_EOF s!`test -f '$<' || echo '$(srcdir)/'`!! +_EOF + sed -f vpsed$$ $ac_file > tmp$$ + mv tmp$$ $ac_file + rm vpsed$$ + echo 'MULTISUBDIR =' >> $ac_file + ml_norecursion=yes + . ${multi_basedir}/config-ml.in + { ml_norecursion=; unset ml_norecursion;} + ;; + "lsan/Makefile":F) cat > vpsed$$ << \_EOF +s!`test -f '$<' || echo '$(srcdir)/'`!! _EOF sed -f vpsed$$ $ac_file > tmp$$ mv tmp$$ $ac_file diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac index 5919da61304..0b2d8132fb6 100644 --- a/libsanitizer/configure.ac +++ b/libsanitizer/configure.ac @@ -89,7 +89,7 @@ AM_CONDITIONAL(USING_MAC_INTERPOSE, $MAC_INTERPOSE) AC_CONFIG_FILES([Makefile]) -AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common asan ubsan], [DIR/Makefile ]), +AC_CONFIG_FILES(AC_FOREACH([DIR], [interception sanitizer_common lsan asan ubsan], [DIR/Makefile ]), [cat > vpsed$$ << \_EOF s!`test -f '$<' || echo '$(srcdir)/'`!! _EOF diff --git a/libsanitizer/include/sanitizer/common_interface_defs.h b/libsanitizer/include/sanitizer/common_interface_defs.h index c218b5b5654..a7db2ff1c12 100644 --- a/libsanitizer/include/sanitizer/common_interface_defs.h +++ b/libsanitizer/include/sanitizer/common_interface_defs.h @@ -39,6 +39,16 @@ extern "C" { // the error message. This function can be overridden by the client. void __sanitizer_report_error_summary(const char *error_summary); + // Some of the sanitizers (e.g. asan/tsan) may miss bugs that happen + // in unaligned loads/stores. In order to find such bugs reliably one needs + // to replace plain unaligned loads/stores with these calls. + uint16_t __sanitizer_unaligned_load16(const void *p); + uint32_t __sanitizer_unaligned_load32(const void *p); + uint64_t __sanitizer_unaligned_load64(const void *p); + void __sanitizer_unaligned_store16(void *p, uint16_t x); + void __sanitizer_unaligned_store32(void *p, uint32_t x); + void __sanitizer_unaligned_store64(void *p, uint64_t x); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/include/sanitizer/dfsan_interface.h b/libsanitizer/include/sanitizer/dfsan_interface.h new file mode 100644 index 00000000000..dd938483d18 --- /dev/null +++ b/libsanitizer/include/sanitizer/dfsan_interface.h @@ -0,0 +1,85 @@ +//===-- dfsan_interface.h -------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of DataFlowSanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef DFSAN_INTERFACE_H +#define DFSAN_INTERFACE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint16_t dfsan_label; + +/// Stores information associated with a specific label identifier. A label +/// may be a base label created using dfsan_create_label, with associated +/// text description and user data, or an automatically created union label, +/// which represents the union of two label identifiers (which may themselves +/// be base or union labels). +struct dfsan_label_info { + // Fields for union labels, set to 0 for base labels. + dfsan_label l1; + dfsan_label l2; + + // Fields for base labels. + const char *desc; + void *userdata; +}; + +/// 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); + +/// Creates and returns a base label with the given description and user data. +dfsan_label dfsan_create_label(const char *desc, void *userdata); + +/// Sets the label for each address in [addr,addr+size) to \c label. +void dfsan_set_label(dfsan_label label, void *addr, size_t size); + +/// Sets the label for each address in [addr,addr+size) to the union of the +/// current label for that address and \c label. +void dfsan_add_label(dfsan_label label, void *addr, size_t size); + +/// Retrieves the label associated with the given data. +/// +/// The type of 'data' is arbitrary. The function accepts a value of any type, +/// which can be truncated or extended (implicitly or explicitly) as necessary. +/// The truncation/extension operations will preserve the label of the original +/// value. +dfsan_label dfsan_get_label(long data); + +/// Retrieves the label associated with the data at the given address. +dfsan_label dfsan_read_label(const void *addr, size_t size); + +/// Retrieves a pointer to the dfsan_label_info struct for the given label. +const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label); + +/// Returns whether the given label label contains the label elem. +int dfsan_has_label(dfsan_label label, dfsan_label elem); + +/// If the given label label contains a label with the description desc, returns +/// that label, else returns 0. +dfsan_label dfsan_has_label_with_desc(dfsan_label label, const char *desc); + +#ifdef __cplusplus +} // extern "C" + +template +void dfsan_set_label(dfsan_label label, T &data) { // NOLINT + dfsan_set_label(label, (void *)&data, sizeof(T)); +} + +#endif + +#endif // DFSAN_INTERFACE_H diff --git a/libsanitizer/include/sanitizer/linux_syscall_hooks.h b/libsanitizer/include/sanitizer/linux_syscall_hooks.h new file mode 100644 index 00000000000..6cd7d7626b8 --- /dev/null +++ b/libsanitizer/include/sanitizer/linux_syscall_hooks.h @@ -0,0 +1,3067 @@ +//===-- linux_syscall_hooks.h ---------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of public sanitizer interface. +// +// System call handlers. +// +// Interface methods declared in this header implement pre- and post- syscall +// actions for the active sanitizer. +// Usage: +// __sanitizer_syscall_pre_getfoo(...args...); +// long res = syscall(__NR_getfoo, ...args...); +// __sanitizer_syscall_post_getfoo(res, ...args...); +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LINUX_SYSCALL_HOOKS_H +#define SANITIZER_LINUX_SYSCALL_HOOKS_H + +#define __sanitizer_syscall_pre_time(tloc) \ + __sanitizer_syscall_pre_impl_time((long)(tloc)) +#define __sanitizer_syscall_post_time(res, tloc) \ + __sanitizer_syscall_post_impl_time(res, (long)(tloc)) +#define __sanitizer_syscall_pre_stime(tptr) \ + __sanitizer_syscall_pre_impl_stime((long)(tptr)) +#define __sanitizer_syscall_post_stime(res, tptr) \ + __sanitizer_syscall_post_impl_stime(res, (long)(tptr)) +#define __sanitizer_syscall_pre_gettimeofday(tv, tz) \ + __sanitizer_syscall_pre_impl_gettimeofday((long)(tv), (long)(tz)) +#define __sanitizer_syscall_post_gettimeofday(res, tv, tz) \ + __sanitizer_syscall_post_impl_gettimeofday(res, (long)(tv), (long)(tz)) +#define __sanitizer_syscall_pre_settimeofday(tv, tz) \ + __sanitizer_syscall_pre_impl_settimeofday((long)(tv), (long)(tz)) +#define __sanitizer_syscall_post_settimeofday(res, tv, tz) \ + __sanitizer_syscall_post_impl_settimeofday(res, (long)(tv), (long)(tz)) +#define __sanitizer_syscall_pre_adjtimex(txc_p) \ + __sanitizer_syscall_pre_impl_adjtimex((long)(txc_p)) +#define __sanitizer_syscall_post_adjtimex(res, txc_p) \ + __sanitizer_syscall_post_impl_adjtimex(res, (long)(txc_p)) +#define __sanitizer_syscall_pre_times(tbuf) \ + __sanitizer_syscall_pre_impl_times((long)(tbuf)) +#define __sanitizer_syscall_post_times(res, tbuf) \ + __sanitizer_syscall_post_impl_times(res, (long)(tbuf)) +#define __sanitizer_syscall_pre_gettid() __sanitizer_syscall_pre_impl_gettid() +#define __sanitizer_syscall_post_gettid(res) \ + __sanitizer_syscall_post_impl_gettid(res) +#define __sanitizer_syscall_pre_nanosleep(rqtp, rmtp) \ + __sanitizer_syscall_pre_impl_nanosleep((long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_post_nanosleep(res, rqtp, rmtp) \ + __sanitizer_syscall_post_impl_nanosleep(res, (long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_pre_alarm(seconds) \ + __sanitizer_syscall_pre_impl_alarm((long)(seconds)) +#define __sanitizer_syscall_post_alarm(res, seconds) \ + __sanitizer_syscall_post_impl_alarm(res, (long)(seconds)) +#define __sanitizer_syscall_pre_getpid() __sanitizer_syscall_pre_impl_getpid() +#define __sanitizer_syscall_post_getpid(res) \ + __sanitizer_syscall_post_impl_getpid(res) +#define __sanitizer_syscall_pre_getppid() __sanitizer_syscall_pre_impl_getppid() +#define __sanitizer_syscall_post_getppid(res) \ + __sanitizer_syscall_post_impl_getppid(res) +#define __sanitizer_syscall_pre_getuid() __sanitizer_syscall_pre_impl_getuid() +#define __sanitizer_syscall_post_getuid(res) \ + __sanitizer_syscall_post_impl_getuid(res) +#define __sanitizer_syscall_pre_geteuid() __sanitizer_syscall_pre_impl_geteuid() +#define __sanitizer_syscall_post_geteuid(res) \ + __sanitizer_syscall_post_impl_geteuid(res) +#define __sanitizer_syscall_pre_getgid() __sanitizer_syscall_pre_impl_getgid() +#define __sanitizer_syscall_post_getgid(res) \ + __sanitizer_syscall_post_impl_getgid(res) +#define __sanitizer_syscall_pre_getegid() __sanitizer_syscall_pre_impl_getegid() +#define __sanitizer_syscall_post_getegid(res) \ + __sanitizer_syscall_post_impl_getegid(res) +#define __sanitizer_syscall_pre_getresuid(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_getresuid((long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_post_getresuid(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_getresuid(res, (long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_pre_getresgid(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_getresgid((long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_post_getresgid(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_getresgid(res, (long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_pre_getpgid(pid) \ + __sanitizer_syscall_pre_impl_getpgid((long)(pid)) +#define __sanitizer_syscall_post_getpgid(res, pid) \ + __sanitizer_syscall_post_impl_getpgid(res, (long)(pid)) +#define __sanitizer_syscall_pre_getpgrp() __sanitizer_syscall_pre_impl_getpgrp() +#define __sanitizer_syscall_post_getpgrp(res) \ + __sanitizer_syscall_post_impl_getpgrp(res) +#define __sanitizer_syscall_pre_getsid(pid) \ + __sanitizer_syscall_pre_impl_getsid((long)(pid)) +#define __sanitizer_syscall_post_getsid(res, pid) \ + __sanitizer_syscall_post_impl_getsid(res, (long)(pid)) +#define __sanitizer_syscall_pre_getgroups(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_getgroups((long)(gidsetsize), (long)(grouplist)) +#define __sanitizer_syscall_post_getgroups(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_getgroups(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_setregid(rgid, egid) \ + __sanitizer_syscall_pre_impl_setregid((long)(rgid), (long)(egid)) +#define __sanitizer_syscall_post_setregid(res, rgid, egid) \ + __sanitizer_syscall_post_impl_setregid(res, (long)(rgid), (long)(egid)) +#define __sanitizer_syscall_pre_setgid(gid) \ + __sanitizer_syscall_pre_impl_setgid((long)(gid)) +#define __sanitizer_syscall_post_setgid(res, gid) \ + __sanitizer_syscall_post_impl_setgid(res, (long)(gid)) +#define __sanitizer_syscall_pre_setreuid(ruid, euid) \ + __sanitizer_syscall_pre_impl_setreuid((long)(ruid), (long)(euid)) +#define __sanitizer_syscall_post_setreuid(res, ruid, euid) \ + __sanitizer_syscall_post_impl_setreuid(res, (long)(ruid), (long)(euid)) +#define __sanitizer_syscall_pre_setuid(uid) \ + __sanitizer_syscall_pre_impl_setuid((long)(uid)) +#define __sanitizer_syscall_post_setuid(res, uid) \ + __sanitizer_syscall_post_impl_setuid(res, (long)(uid)) +#define __sanitizer_syscall_pre_setresuid(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_setresuid((long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_post_setresuid(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_setresuid(res, (long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_pre_setresgid(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_setresgid((long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_post_setresgid(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_setresgid(res, (long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_pre_setfsuid(uid) \ + __sanitizer_syscall_pre_impl_setfsuid((long)(uid)) +#define __sanitizer_syscall_post_setfsuid(res, uid) \ + __sanitizer_syscall_post_impl_setfsuid(res, (long)(uid)) +#define __sanitizer_syscall_pre_setfsgid(gid) \ + __sanitizer_syscall_pre_impl_setfsgid((long)(gid)) +#define __sanitizer_syscall_post_setfsgid(res, gid) \ + __sanitizer_syscall_post_impl_setfsgid(res, (long)(gid)) +#define __sanitizer_syscall_pre_setpgid(pid, pgid) \ + __sanitizer_syscall_pre_impl_setpgid((long)(pid), (long)(pgid)) +#define __sanitizer_syscall_post_setpgid(res, pid, pgid) \ + __sanitizer_syscall_post_impl_setpgid(res, (long)(pid), (long)(pgid)) +#define __sanitizer_syscall_pre_setsid() __sanitizer_syscall_pre_impl_setsid() +#define __sanitizer_syscall_post_setsid(res) \ + __sanitizer_syscall_post_impl_setsid(res) +#define __sanitizer_syscall_pre_setgroups(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_setgroups((long)(gidsetsize), (long)(grouplist)) +#define __sanitizer_syscall_post_setgroups(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_setgroups(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_acct(name) \ + __sanitizer_syscall_pre_impl_acct((long)(name)) +#define __sanitizer_syscall_post_acct(res, name) \ + __sanitizer_syscall_post_impl_acct(res, (long)(name)) +#define __sanitizer_syscall_pre_capget(header, dataptr) \ + __sanitizer_syscall_pre_impl_capget((long)(header), (long)(dataptr)) +#define __sanitizer_syscall_post_capget(res, header, dataptr) \ + __sanitizer_syscall_post_impl_capget(res, (long)(header), (long)(dataptr)) +#define __sanitizer_syscall_pre_capset(header, data) \ + __sanitizer_syscall_pre_impl_capset((long)(header), (long)(data)) +#define __sanitizer_syscall_post_capset(res, header, data) \ + __sanitizer_syscall_post_impl_capset(res, (long)(header), (long)(data)) +#define __sanitizer_syscall_pre_personality(personality) \ + __sanitizer_syscall_pre_impl_personality((long)(personality)) +#define __sanitizer_syscall_post_personality(res, personality) \ + __sanitizer_syscall_post_impl_personality(res, (long)(personality)) +#define __sanitizer_syscall_pre_sigpending(set) \ + __sanitizer_syscall_pre_impl_sigpending((long)(set)) +#define __sanitizer_syscall_post_sigpending(res, set) \ + __sanitizer_syscall_post_impl_sigpending(res, (long)(set)) +#define __sanitizer_syscall_pre_sigprocmask(how, set, oset) \ + __sanitizer_syscall_pre_impl_sigprocmask((long)(how), (long)(set), \ + (long)(oset)) +#define __sanitizer_syscall_post_sigprocmask(res, how, set, oset) \ + __sanitizer_syscall_post_impl_sigprocmask(res, (long)(how), (long)(set), \ + (long)(oset)) +#define __sanitizer_syscall_pre_getitimer(which, value) \ + __sanitizer_syscall_pre_impl_getitimer((long)(which), (long)(value)) +#define __sanitizer_syscall_post_getitimer(res, which, value) \ + __sanitizer_syscall_post_impl_getitimer(res, (long)(which), (long)(value)) +#define __sanitizer_syscall_pre_setitimer(which, value, ovalue) \ + __sanitizer_syscall_pre_impl_setitimer((long)(which), (long)(value), \ + (long)(ovalue)) +#define __sanitizer_syscall_post_setitimer(res, which, value, ovalue) \ + __sanitizer_syscall_post_impl_setitimer(res, (long)(which), (long)(value), \ + (long)(ovalue)) +#define __sanitizer_syscall_pre_timer_create(which_clock, timer_event_spec, \ + created_timer_id) \ + __sanitizer_syscall_pre_impl_timer_create( \ + (long)(which_clock), (long)(timer_event_spec), (long)(created_timer_id)) +#define __sanitizer_syscall_post_timer_create( \ + res, which_clock, timer_event_spec, created_timer_id) \ + __sanitizer_syscall_post_impl_timer_create(res, (long)(which_clock), \ + (long)(timer_event_spec), \ + (long)(created_timer_id)) +#define __sanitizer_syscall_pre_timer_gettime(timer_id, setting) \ + __sanitizer_syscall_pre_impl_timer_gettime((long)(timer_id), (long)(setting)) +#define __sanitizer_syscall_post_timer_gettime(res, timer_id, setting) \ + __sanitizer_syscall_post_impl_timer_gettime(res, (long)(timer_id), \ + (long)(setting)) +#define __sanitizer_syscall_pre_timer_getoverrun(timer_id) \ + __sanitizer_syscall_pre_impl_timer_getoverrun((long)(timer_id)) +#define __sanitizer_syscall_post_timer_getoverrun(res, timer_id) \ + __sanitizer_syscall_post_impl_timer_getoverrun(res, (long)(timer_id)) +#define __sanitizer_syscall_pre_timer_settime(timer_id, flags, new_setting, \ + old_setting) \ + __sanitizer_syscall_pre_impl_timer_settime((long)(timer_id), (long)(flags), \ + (long)(new_setting), \ + (long)(old_setting)) +#define __sanitizer_syscall_post_timer_settime(res, timer_id, flags, \ + new_setting, old_setting) \ + __sanitizer_syscall_post_impl_timer_settime( \ + res, (long)(timer_id), (long)(flags), (long)(new_setting), \ + (long)(old_setting)) +#define __sanitizer_syscall_pre_timer_delete(timer_id) \ + __sanitizer_syscall_pre_impl_timer_delete((long)(timer_id)) +#define __sanitizer_syscall_post_timer_delete(res, timer_id) \ + __sanitizer_syscall_post_impl_timer_delete(res, (long)(timer_id)) +#define __sanitizer_syscall_pre_clock_settime(which_clock, tp) \ + __sanitizer_syscall_pre_impl_clock_settime((long)(which_clock), (long)(tp)) +#define __sanitizer_syscall_post_clock_settime(res, which_clock, tp) \ + __sanitizer_syscall_post_impl_clock_settime(res, (long)(which_clock), \ + (long)(tp)) +#define __sanitizer_syscall_pre_clock_gettime(which_clock, tp) \ + __sanitizer_syscall_pre_impl_clock_gettime((long)(which_clock), (long)(tp)) +#define __sanitizer_syscall_post_clock_gettime(res, which_clock, tp) \ + __sanitizer_syscall_post_impl_clock_gettime(res, (long)(which_clock), \ + (long)(tp)) +#define __sanitizer_syscall_pre_clock_adjtime(which_clock, tx) \ + __sanitizer_syscall_pre_impl_clock_adjtime((long)(which_clock), (long)(tx)) +#define __sanitizer_syscall_post_clock_adjtime(res, which_clock, tx) \ + __sanitizer_syscall_post_impl_clock_adjtime(res, (long)(which_clock), \ + (long)(tx)) +#define __sanitizer_syscall_pre_clock_getres(which_clock, tp) \ + __sanitizer_syscall_pre_impl_clock_getres((long)(which_clock), (long)(tp)) +#define __sanitizer_syscall_post_clock_getres(res, which_clock, tp) \ + __sanitizer_syscall_post_impl_clock_getres(res, (long)(which_clock), \ + (long)(tp)) +#define __sanitizer_syscall_pre_clock_nanosleep(which_clock, flags, rqtp, \ + rmtp) \ + __sanitizer_syscall_pre_impl_clock_nanosleep( \ + (long)(which_clock), (long)(flags), (long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_post_clock_nanosleep(res, which_clock, flags, \ + rqtp, rmtp) \ + __sanitizer_syscall_post_impl_clock_nanosleep( \ + res, (long)(which_clock), (long)(flags), (long)(rqtp), (long)(rmtp)) +#define __sanitizer_syscall_pre_nice(increment) \ + __sanitizer_syscall_pre_impl_nice((long)(increment)) +#define __sanitizer_syscall_post_nice(res, increment) \ + __sanitizer_syscall_post_impl_nice(res, (long)(increment)) +#define __sanitizer_syscall_pre_sched_setscheduler(pid, policy, param) \ + __sanitizer_syscall_pre_impl_sched_setscheduler((long)(pid), (long)(policy), \ + (long)(param)) +#define __sanitizer_syscall_post_sched_setscheduler(res, pid, policy, param) \ + __sanitizer_syscall_post_impl_sched_setscheduler( \ + res, (long)(pid), (long)(policy), (long)(param)) +#define __sanitizer_syscall_pre_sched_setparam(pid, param) \ + __sanitizer_syscall_pre_impl_sched_setparam((long)(pid), (long)(param)) +#define __sanitizer_syscall_post_sched_setparam(res, pid, param) \ + __sanitizer_syscall_post_impl_sched_setparam(res, (long)(pid), (long)(param)) +#define __sanitizer_syscall_pre_sched_getscheduler(pid) \ + __sanitizer_syscall_pre_impl_sched_getscheduler((long)(pid)) +#define __sanitizer_syscall_post_sched_getscheduler(res, pid) \ + __sanitizer_syscall_post_impl_sched_getscheduler(res, (long)(pid)) +#define __sanitizer_syscall_pre_sched_getparam(pid, param) \ + __sanitizer_syscall_pre_impl_sched_getparam((long)(pid), (long)(param)) +#define __sanitizer_syscall_post_sched_getparam(res, pid, param) \ + __sanitizer_syscall_post_impl_sched_getparam(res, (long)(pid), (long)(param)) +#define __sanitizer_syscall_pre_sched_setaffinity(pid, len, user_mask_ptr) \ + __sanitizer_syscall_pre_impl_sched_setaffinity((long)(pid), (long)(len), \ + (long)(user_mask_ptr)) +#define __sanitizer_syscall_post_sched_setaffinity(res, pid, len, \ + user_mask_ptr) \ + __sanitizer_syscall_post_impl_sched_setaffinity( \ + res, (long)(pid), (long)(len), (long)(user_mask_ptr)) +#define __sanitizer_syscall_pre_sched_getaffinity(pid, len, user_mask_ptr) \ + __sanitizer_syscall_pre_impl_sched_getaffinity((long)(pid), (long)(len), \ + (long)(user_mask_ptr)) +#define __sanitizer_syscall_post_sched_getaffinity(res, pid, len, \ + user_mask_ptr) \ + __sanitizer_syscall_post_impl_sched_getaffinity( \ + res, (long)(pid), (long)(len), (long)(user_mask_ptr)) +#define __sanitizer_syscall_pre_sched_yield() \ + __sanitizer_syscall_pre_impl_sched_yield() +#define __sanitizer_syscall_post_sched_yield(res) \ + __sanitizer_syscall_post_impl_sched_yield(res) +#define __sanitizer_syscall_pre_sched_get_priority_max(policy) \ + __sanitizer_syscall_pre_impl_sched_get_priority_max((long)(policy)) +#define __sanitizer_syscall_post_sched_get_priority_max(res, policy) \ + __sanitizer_syscall_post_impl_sched_get_priority_max(res, (long)(policy)) +#define __sanitizer_syscall_pre_sched_get_priority_min(policy) \ + __sanitizer_syscall_pre_impl_sched_get_priority_min((long)(policy)) +#define __sanitizer_syscall_post_sched_get_priority_min(res, policy) \ + __sanitizer_syscall_post_impl_sched_get_priority_min(res, (long)(policy)) +#define __sanitizer_syscall_pre_sched_rr_get_interval(pid, interval) \ + __sanitizer_syscall_pre_impl_sched_rr_get_interval((long)(pid), \ + (long)(interval)) +#define __sanitizer_syscall_post_sched_rr_get_interval(res, pid, interval) \ + __sanitizer_syscall_post_impl_sched_rr_get_interval(res, (long)(pid), \ + (long)(interval)) +#define __sanitizer_syscall_pre_setpriority(which, who, niceval) \ + __sanitizer_syscall_pre_impl_setpriority((long)(which), (long)(who), \ + (long)(niceval)) +#define __sanitizer_syscall_post_setpriority(res, which, who, niceval) \ + __sanitizer_syscall_post_impl_setpriority(res, (long)(which), (long)(who), \ + (long)(niceval)) +#define __sanitizer_syscall_pre_getpriority(which, who) \ + __sanitizer_syscall_pre_impl_getpriority((long)(which), (long)(who)) +#define __sanitizer_syscall_post_getpriority(res, which, who) \ + __sanitizer_syscall_post_impl_getpriority(res, (long)(which), (long)(who)) +#define __sanitizer_syscall_pre_shutdown(arg0, arg1) \ + __sanitizer_syscall_pre_impl_shutdown((long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_post_shutdown(res, arg0, arg1) \ + __sanitizer_syscall_post_impl_shutdown(res, (long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_pre_reboot(magic1, magic2, cmd, arg) \ + __sanitizer_syscall_pre_impl_reboot((long)(magic1), (long)(magic2), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_reboot(res, magic1, magic2, cmd, arg) \ + __sanitizer_syscall_post_impl_reboot(res, (long)(magic1), (long)(magic2), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_restart_syscall() \ + __sanitizer_syscall_pre_impl_restart_syscall() +#define __sanitizer_syscall_post_restart_syscall(res) \ + __sanitizer_syscall_post_impl_restart_syscall(res) +#define __sanitizer_syscall_pre_kexec_load(entry, nr_segments, segments, \ + flags) \ + __sanitizer_syscall_pre_impl_kexec_load((long)(entry), (long)(nr_segments), \ + (long)(segments), (long)(flags)) +#define __sanitizer_syscall_post_kexec_load(res, entry, nr_segments, segments, \ + flags) \ + __sanitizer_syscall_post_impl_kexec_load(res, (long)(entry), \ + (long)(nr_segments), \ + (long)(segments), (long)(flags)) +#define __sanitizer_syscall_pre_exit(error_code) \ + __sanitizer_syscall_pre_impl_exit((long)(error_code)) +#define __sanitizer_syscall_post_exit(res, error_code) \ + __sanitizer_syscall_post_impl_exit(res, (long)(error_code)) +#define __sanitizer_syscall_pre_exit_group(error_code) \ + __sanitizer_syscall_pre_impl_exit_group((long)(error_code)) +#define __sanitizer_syscall_post_exit_group(res, error_code) \ + __sanitizer_syscall_post_impl_exit_group(res, (long)(error_code)) +#define __sanitizer_syscall_pre_wait4(pid, stat_addr, options, ru) \ + __sanitizer_syscall_pre_impl_wait4((long)(pid), (long)(stat_addr), \ + (long)(options), (long)(ru)) +#define __sanitizer_syscall_post_wait4(res, pid, stat_addr, options, ru) \ + __sanitizer_syscall_post_impl_wait4(res, (long)(pid), (long)(stat_addr), \ + (long)(options), (long)(ru)) +#define __sanitizer_syscall_pre_waitid(which, pid, infop, options, ru) \ + __sanitizer_syscall_pre_impl_waitid( \ + (long)(which), (long)(pid), (long)(infop), (long)(options), (long)(ru)) +#define __sanitizer_syscall_post_waitid(res, which, pid, infop, options, ru) \ + __sanitizer_syscall_post_impl_waitid(res, (long)(which), (long)(pid), \ + (long)(infop), (long)(options), \ + (long)(ru)) +#define __sanitizer_syscall_pre_waitpid(pid, stat_addr, options) \ + __sanitizer_syscall_pre_impl_waitpid((long)(pid), (long)(stat_addr), \ + (long)(options)) +#define __sanitizer_syscall_post_waitpid(res, pid, stat_addr, options) \ + __sanitizer_syscall_post_impl_waitpid(res, (long)(pid), (long)(stat_addr), \ + (long)(options)) +#define __sanitizer_syscall_pre_set_tid_address(tidptr) \ + __sanitizer_syscall_pre_impl_set_tid_address((long)(tidptr)) +#define __sanitizer_syscall_post_set_tid_address(res, tidptr) \ + __sanitizer_syscall_post_impl_set_tid_address(res, (long)(tidptr)) +#define __sanitizer_syscall_pre_init_module(umod, len, uargs) \ + __sanitizer_syscall_pre_impl_init_module((long)(umod), (long)(len), \ + (long)(uargs)) +#define __sanitizer_syscall_post_init_module(res, umod, len, uargs) \ + __sanitizer_syscall_post_impl_init_module(res, (long)(umod), (long)(len), \ + (long)(uargs)) +#define __sanitizer_syscall_pre_delete_module(name_user, flags) \ + __sanitizer_syscall_pre_impl_delete_module((long)(name_user), (long)(flags)) +#define __sanitizer_syscall_post_delete_module(res, name_user, flags) \ + __sanitizer_syscall_post_impl_delete_module(res, (long)(name_user), \ + (long)(flags)) +#define __sanitizer_syscall_pre_rt_sigprocmask(how, set, oset, sigsetsize) \ + __sanitizer_syscall_pre_impl_rt_sigprocmask( \ + (long)(how), (long)(set), (long)(oset), (long)(sigsetsize)) +#define __sanitizer_syscall_post_rt_sigprocmask(res, how, set, oset, \ + sigsetsize) \ + __sanitizer_syscall_post_impl_rt_sigprocmask( \ + res, (long)(how), (long)(set), (long)(oset), (long)(sigsetsize)) +#define __sanitizer_syscall_pre_rt_sigpending(set, sigsetsize) \ + __sanitizer_syscall_pre_impl_rt_sigpending((long)(set), (long)(sigsetsize)) +#define __sanitizer_syscall_post_rt_sigpending(res, set, sigsetsize) \ + __sanitizer_syscall_post_impl_rt_sigpending(res, (long)(set), \ + (long)(sigsetsize)) +#define __sanitizer_syscall_pre_rt_sigtimedwait(uthese, uinfo, uts, \ + sigsetsize) \ + __sanitizer_syscall_pre_impl_rt_sigtimedwait( \ + (long)(uthese), (long)(uinfo), (long)(uts), (long)(sigsetsize)) +#define __sanitizer_syscall_post_rt_sigtimedwait(res, uthese, uinfo, uts, \ + sigsetsize) \ + __sanitizer_syscall_post_impl_rt_sigtimedwait( \ + res, (long)(uthese), (long)(uinfo), (long)(uts), (long)(sigsetsize)) +#define __sanitizer_syscall_pre_rt_tgsigqueueinfo(tgid, pid, sig, uinfo) \ + __sanitizer_syscall_pre_impl_rt_tgsigqueueinfo((long)(tgid), (long)(pid), \ + (long)(sig), (long)(uinfo)) +#define __sanitizer_syscall_post_rt_tgsigqueueinfo(res, tgid, pid, sig, uinfo) \ + __sanitizer_syscall_post_impl_rt_tgsigqueueinfo( \ + res, (long)(tgid), (long)(pid), (long)(sig), (long)(uinfo)) +#define __sanitizer_syscall_pre_kill(pid, sig) \ + __sanitizer_syscall_pre_impl_kill((long)(pid), (long)(sig)) +#define __sanitizer_syscall_post_kill(res, pid, sig) \ + __sanitizer_syscall_post_impl_kill(res, (long)(pid), (long)(sig)) +#define __sanitizer_syscall_pre_tgkill(tgid, pid, sig) \ + __sanitizer_syscall_pre_impl_tgkill((long)(tgid), (long)(pid), (long)(sig)) +#define __sanitizer_syscall_post_tgkill(res, tgid, pid, sig) \ + __sanitizer_syscall_post_impl_tgkill(res, (long)(tgid), (long)(pid), \ + (long)(sig)) +#define __sanitizer_syscall_pre_tkill(pid, sig) \ + __sanitizer_syscall_pre_impl_tkill((long)(pid), (long)(sig)) +#define __sanitizer_syscall_post_tkill(res, pid, sig) \ + __sanitizer_syscall_post_impl_tkill(res, (long)(pid), (long)(sig)) +#define __sanitizer_syscall_pre_rt_sigqueueinfo(pid, sig, uinfo) \ + __sanitizer_syscall_pre_impl_rt_sigqueueinfo((long)(pid), (long)(sig), \ + (long)(uinfo)) +#define __sanitizer_syscall_post_rt_sigqueueinfo(res, pid, sig, uinfo) \ + __sanitizer_syscall_post_impl_rt_sigqueueinfo(res, (long)(pid), (long)(sig), \ + (long)(uinfo)) +#define __sanitizer_syscall_pre_sgetmask() \ + __sanitizer_syscall_pre_impl_sgetmask() +#define __sanitizer_syscall_post_sgetmask(res) \ + __sanitizer_syscall_post_impl_sgetmask(res) +#define __sanitizer_syscall_pre_ssetmask(newmask) \ + __sanitizer_syscall_pre_impl_ssetmask((long)(newmask)) +#define __sanitizer_syscall_post_ssetmask(res, newmask) \ + __sanitizer_syscall_post_impl_ssetmask(res, (long)(newmask)) +#define __sanitizer_syscall_pre_signal(sig, handler) \ + __sanitizer_syscall_pre_impl_signal((long)(sig), (long)(handler)) +#define __sanitizer_syscall_post_signal(res, sig, handler) \ + __sanitizer_syscall_post_impl_signal(res, (long)(sig), (long)(handler)) +#define __sanitizer_syscall_pre_pause() __sanitizer_syscall_pre_impl_pause() +#define __sanitizer_syscall_post_pause(res) \ + __sanitizer_syscall_post_impl_pause(res) +#define __sanitizer_syscall_pre_sync() __sanitizer_syscall_pre_impl_sync() +#define __sanitizer_syscall_post_sync(res) \ + __sanitizer_syscall_post_impl_sync(res) +#define __sanitizer_syscall_pre_fsync(fd) \ + __sanitizer_syscall_pre_impl_fsync((long)(fd)) +#define __sanitizer_syscall_post_fsync(res, fd) \ + __sanitizer_syscall_post_impl_fsync(res, (long)(fd)) +#define __sanitizer_syscall_pre_fdatasync(fd) \ + __sanitizer_syscall_pre_impl_fdatasync((long)(fd)) +#define __sanitizer_syscall_post_fdatasync(res, fd) \ + __sanitizer_syscall_post_impl_fdatasync(res, (long)(fd)) +#define __sanitizer_syscall_pre_bdflush(func, data) \ + __sanitizer_syscall_pre_impl_bdflush((long)(func), (long)(data)) +#define __sanitizer_syscall_post_bdflush(res, func, data) \ + __sanitizer_syscall_post_impl_bdflush(res, (long)(func), (long)(data)) +#define __sanitizer_syscall_pre_mount(dev_name, dir_name, type, flags, data) \ + __sanitizer_syscall_pre_impl_mount((long)(dev_name), (long)(dir_name), \ + (long)(type), (long)(flags), \ + (long)(data)) +#define __sanitizer_syscall_post_mount(res, dev_name, dir_name, type, flags, \ + data) \ + __sanitizer_syscall_post_impl_mount(res, (long)(dev_name), (long)(dir_name), \ + (long)(type), (long)(flags), \ + (long)(data)) +#define __sanitizer_syscall_pre_umount(name, flags) \ + __sanitizer_syscall_pre_impl_umount((long)(name), (long)(flags)) +#define __sanitizer_syscall_post_umount(res, name, flags) \ + __sanitizer_syscall_post_impl_umount(res, (long)(name), (long)(flags)) +#define __sanitizer_syscall_pre_oldumount(name) \ + __sanitizer_syscall_pre_impl_oldumount((long)(name)) +#define __sanitizer_syscall_post_oldumount(res, name) \ + __sanitizer_syscall_post_impl_oldumount(res, (long)(name)) +#define __sanitizer_syscall_pre_truncate(path, length) \ + __sanitizer_syscall_pre_impl_truncate((long)(path), (long)(length)) +#define __sanitizer_syscall_post_truncate(res, path, length) \ + __sanitizer_syscall_post_impl_truncate(res, (long)(path), (long)(length)) +#define __sanitizer_syscall_pre_ftruncate(fd, length) \ + __sanitizer_syscall_pre_impl_ftruncate((long)(fd), (long)(length)) +#define __sanitizer_syscall_post_ftruncate(res, fd, length) \ + __sanitizer_syscall_post_impl_ftruncate(res, (long)(fd), (long)(length)) +#define __sanitizer_syscall_pre_stat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_stat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_stat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_stat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_statfs(path, buf) \ + __sanitizer_syscall_pre_impl_statfs((long)(path), (long)(buf)) +#define __sanitizer_syscall_post_statfs(res, path, buf) \ + __sanitizer_syscall_post_impl_statfs(res, (long)(path), (long)(buf)) +#define __sanitizer_syscall_pre_statfs64(path, sz, buf) \ + __sanitizer_syscall_pre_impl_statfs64((long)(path), (long)(sz), (long)(buf)) +#define __sanitizer_syscall_post_statfs64(res, path, sz, buf) \ + __sanitizer_syscall_post_impl_statfs64(res, (long)(path), (long)(sz), \ + (long)(buf)) +#define __sanitizer_syscall_pre_fstatfs(fd, buf) \ + __sanitizer_syscall_pre_impl_fstatfs((long)(fd), (long)(buf)) +#define __sanitizer_syscall_post_fstatfs(res, fd, buf) \ + __sanitizer_syscall_post_impl_fstatfs(res, (long)(fd), (long)(buf)) +#define __sanitizer_syscall_pre_fstatfs64(fd, sz, buf) \ + __sanitizer_syscall_pre_impl_fstatfs64((long)(fd), (long)(sz), (long)(buf)) +#define __sanitizer_syscall_post_fstatfs64(res, fd, sz, buf) \ + __sanitizer_syscall_post_impl_fstatfs64(res, (long)(fd), (long)(sz), \ + (long)(buf)) +#define __sanitizer_syscall_pre_lstat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_lstat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_lstat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_lstat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_fstat(fd, statbuf) \ + __sanitizer_syscall_pre_impl_fstat((long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_post_fstat(res, fd, statbuf) \ + __sanitizer_syscall_post_impl_fstat(res, (long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_pre_newstat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_newstat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_newstat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_newstat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_newlstat(filename, statbuf) \ + __sanitizer_syscall_pre_impl_newlstat((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_newlstat(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_newlstat(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_newfstat(fd, statbuf) \ + __sanitizer_syscall_pre_impl_newfstat((long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_post_newfstat(res, fd, statbuf) \ + __sanitizer_syscall_post_impl_newfstat(res, (long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_pre_ustat(dev, ubuf) \ + __sanitizer_syscall_pre_impl_ustat((long)(dev), (long)(ubuf)) +#define __sanitizer_syscall_post_ustat(res, dev, ubuf) \ + __sanitizer_syscall_post_impl_ustat(res, (long)(dev), (long)(ubuf)) +#define __sanitizer_syscall_pre_stat64(filename, statbuf) \ + __sanitizer_syscall_pre_impl_stat64((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_stat64(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_stat64(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_fstat64(fd, statbuf) \ + __sanitizer_syscall_pre_impl_fstat64((long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_post_fstat64(res, fd, statbuf) \ + __sanitizer_syscall_post_impl_fstat64(res, (long)(fd), (long)(statbuf)) +#define __sanitizer_syscall_pre_lstat64(filename, statbuf) \ + __sanitizer_syscall_pre_impl_lstat64((long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_post_lstat64(res, filename, statbuf) \ + __sanitizer_syscall_post_impl_lstat64(res, (long)(filename), (long)(statbuf)) +#define __sanitizer_syscall_pre_setxattr(path, name, value, size, flags) \ + __sanitizer_syscall_pre_impl_setxattr( \ + (long)(path), (long)(name), (long)(value), (long)(size), (long)(flags)) +#define __sanitizer_syscall_post_setxattr(res, path, name, value, size, flags) \ + __sanitizer_syscall_post_impl_setxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size), \ + (long)(flags)) +#define __sanitizer_syscall_pre_lsetxattr(path, name, value, size, flags) \ + __sanitizer_syscall_pre_impl_lsetxattr( \ + (long)(path), (long)(name), (long)(value), (long)(size), (long)(flags)) +#define __sanitizer_syscall_post_lsetxattr(res, path, name, value, size, \ + flags) \ + __sanitizer_syscall_post_impl_lsetxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size), \ + (long)(flags)) +#define __sanitizer_syscall_pre_fsetxattr(fd, name, value, size, flags) \ + __sanitizer_syscall_pre_impl_fsetxattr( \ + (long)(fd), (long)(name), (long)(value), (long)(size), (long)(flags)) +#define __sanitizer_syscall_post_fsetxattr(res, fd, name, value, size, flags) \ + __sanitizer_syscall_post_impl_fsetxattr(res, (long)(fd), (long)(name), \ + (long)(value), (long)(size), \ + (long)(flags)) +#define __sanitizer_syscall_pre_getxattr(path, name, value, size) \ + __sanitizer_syscall_pre_impl_getxattr((long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_post_getxattr(res, path, name, value, size) \ + __sanitizer_syscall_post_impl_getxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_pre_lgetxattr(path, name, value, size) \ + __sanitizer_syscall_pre_impl_lgetxattr((long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_post_lgetxattr(res, path, name, value, size) \ + __sanitizer_syscall_post_impl_lgetxattr(res, (long)(path), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_pre_fgetxattr(fd, name, value, size) \ + __sanitizer_syscall_pre_impl_fgetxattr((long)(fd), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_post_fgetxattr(res, fd, name, value, size) \ + __sanitizer_syscall_post_impl_fgetxattr(res, (long)(fd), (long)(name), \ + (long)(value), (long)(size)) +#define __sanitizer_syscall_pre_listxattr(path, list, size) \ + __sanitizer_syscall_pre_impl_listxattr((long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_post_listxattr(res, path, list, size) \ + __sanitizer_syscall_post_impl_listxattr(res, (long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_pre_llistxattr(path, list, size) \ + __sanitizer_syscall_pre_impl_llistxattr((long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_post_llistxattr(res, path, list, size) \ + __sanitizer_syscall_post_impl_llistxattr(res, (long)(path), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_pre_flistxattr(fd, list, size) \ + __sanitizer_syscall_pre_impl_flistxattr((long)(fd), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_post_flistxattr(res, fd, list, size) \ + __sanitizer_syscall_post_impl_flistxattr(res, (long)(fd), (long)(list), \ + (long)(size)) +#define __sanitizer_syscall_pre_removexattr(path, name) \ + __sanitizer_syscall_pre_impl_removexattr((long)(path), (long)(name)) +#define __sanitizer_syscall_post_removexattr(res, path, name) \ + __sanitizer_syscall_post_impl_removexattr(res, (long)(path), (long)(name)) +#define __sanitizer_syscall_pre_lremovexattr(path, name) \ + __sanitizer_syscall_pre_impl_lremovexattr((long)(path), (long)(name)) +#define __sanitizer_syscall_post_lremovexattr(res, path, name) \ + __sanitizer_syscall_post_impl_lremovexattr(res, (long)(path), (long)(name)) +#define __sanitizer_syscall_pre_fremovexattr(fd, name) \ + __sanitizer_syscall_pre_impl_fremovexattr((long)(fd), (long)(name)) +#define __sanitizer_syscall_post_fremovexattr(res, fd, name) \ + __sanitizer_syscall_post_impl_fremovexattr(res, (long)(fd), (long)(name)) +#define __sanitizer_syscall_pre_brk(brk) \ + __sanitizer_syscall_pre_impl_brk((long)(brk)) +#define __sanitizer_syscall_post_brk(res, brk) \ + __sanitizer_syscall_post_impl_brk(res, (long)(brk)) +#define __sanitizer_syscall_pre_mprotect(start, len, prot) \ + __sanitizer_syscall_pre_impl_mprotect((long)(start), (long)(len), \ + (long)(prot)) +#define __sanitizer_syscall_post_mprotect(res, start, len, prot) \ + __sanitizer_syscall_post_impl_mprotect(res, (long)(start), (long)(len), \ + (long)(prot)) +#define __sanitizer_syscall_pre_mremap(addr, old_len, new_len, flags, \ + new_addr) \ + __sanitizer_syscall_pre_impl_mremap((long)(addr), (long)(old_len), \ + (long)(new_len), (long)(flags), \ + (long)(new_addr)) +#define __sanitizer_syscall_post_mremap(res, addr, old_len, new_len, flags, \ + new_addr) \ + __sanitizer_syscall_post_impl_mremap(res, (long)(addr), (long)(old_len), \ + (long)(new_len), (long)(flags), \ + (long)(new_addr)) +#define __sanitizer_syscall_pre_remap_file_pages(start, size, prot, pgoff, \ + flags) \ + __sanitizer_syscall_pre_impl_remap_file_pages( \ + (long)(start), (long)(size), (long)(prot), (long)(pgoff), (long)(flags)) +#define __sanitizer_syscall_post_remap_file_pages(res, start, size, prot, \ + pgoff, flags) \ + __sanitizer_syscall_post_impl_remap_file_pages(res, (long)(start), \ + (long)(size), (long)(prot), \ + (long)(pgoff), (long)(flags)) +#define __sanitizer_syscall_pre_msync(start, len, flags) \ + __sanitizer_syscall_pre_impl_msync((long)(start), (long)(len), (long)(flags)) +#define __sanitizer_syscall_post_msync(res, start, len, flags) \ + __sanitizer_syscall_post_impl_msync(res, (long)(start), (long)(len), \ + (long)(flags)) +#define __sanitizer_syscall_pre_munmap(addr, len) \ + __sanitizer_syscall_pre_impl_munmap((long)(addr), (long)(len)) +#define __sanitizer_syscall_post_munmap(res, addr, len) \ + __sanitizer_syscall_post_impl_munmap(res, (long)(addr), (long)(len)) +#define __sanitizer_syscall_pre_mlock(start, len) \ + __sanitizer_syscall_pre_impl_mlock((long)(start), (long)(len)) +#define __sanitizer_syscall_post_mlock(res, start, len) \ + __sanitizer_syscall_post_impl_mlock(res, (long)(start), (long)(len)) +#define __sanitizer_syscall_pre_munlock(start, len) \ + __sanitizer_syscall_pre_impl_munlock((long)(start), (long)(len)) +#define __sanitizer_syscall_post_munlock(res, start, len) \ + __sanitizer_syscall_post_impl_munlock(res, (long)(start), (long)(len)) +#define __sanitizer_syscall_pre_mlockall(flags) \ + __sanitizer_syscall_pre_impl_mlockall((long)(flags)) +#define __sanitizer_syscall_post_mlockall(res, flags) \ + __sanitizer_syscall_post_impl_mlockall(res, (long)(flags)) +#define __sanitizer_syscall_pre_munlockall() \ + __sanitizer_syscall_pre_impl_munlockall() +#define __sanitizer_syscall_post_munlockall(res) \ + __sanitizer_syscall_post_impl_munlockall(res) +#define __sanitizer_syscall_pre_madvise(start, len, behavior) \ + __sanitizer_syscall_pre_impl_madvise((long)(start), (long)(len), \ + (long)(behavior)) +#define __sanitizer_syscall_post_madvise(res, start, len, behavior) \ + __sanitizer_syscall_post_impl_madvise(res, (long)(start), (long)(len), \ + (long)(behavior)) +#define __sanitizer_syscall_pre_mincore(start, len, vec) \ + __sanitizer_syscall_pre_impl_mincore((long)(start), (long)(len), (long)(vec)) +#define __sanitizer_syscall_post_mincore(res, start, len, vec) \ + __sanitizer_syscall_post_impl_mincore(res, (long)(start), (long)(len), \ + (long)(vec)) +#define __sanitizer_syscall_pre_pivot_root(new_root, put_old) \ + __sanitizer_syscall_pre_impl_pivot_root((long)(new_root), (long)(put_old)) +#define __sanitizer_syscall_post_pivot_root(res, new_root, put_old) \ + __sanitizer_syscall_post_impl_pivot_root(res, (long)(new_root), \ + (long)(put_old)) +#define __sanitizer_syscall_pre_chroot(filename) \ + __sanitizer_syscall_pre_impl_chroot((long)(filename)) +#define __sanitizer_syscall_post_chroot(res, filename) \ + __sanitizer_syscall_post_impl_chroot(res, (long)(filename)) +#define __sanitizer_syscall_pre_mknod(filename, mode, dev) \ + __sanitizer_syscall_pre_impl_mknod((long)(filename), (long)(mode), \ + (long)(dev)) +#define __sanitizer_syscall_post_mknod(res, filename, mode, dev) \ + __sanitizer_syscall_post_impl_mknod(res, (long)(filename), (long)(mode), \ + (long)(dev)) +#define __sanitizer_syscall_pre_link(oldname, newname) \ + __sanitizer_syscall_pre_impl_link((long)(oldname), (long)(newname)) +#define __sanitizer_syscall_post_link(res, oldname, newname) \ + __sanitizer_syscall_post_impl_link(res, (long)(oldname), (long)(newname)) +#define __sanitizer_syscall_pre_symlink(old, new_) \ + __sanitizer_syscall_pre_impl_symlink((long)(old), (long)(new_)) +#define __sanitizer_syscall_post_symlink(res, old, new_) \ + __sanitizer_syscall_post_impl_symlink(res, (long)(old), (long)(new_)) +#define __sanitizer_syscall_pre_unlink(pathname) \ + __sanitizer_syscall_pre_impl_unlink((long)(pathname)) +#define __sanitizer_syscall_post_unlink(res, pathname) \ + __sanitizer_syscall_post_impl_unlink(res, (long)(pathname)) +#define __sanitizer_syscall_pre_rename(oldname, newname) \ + __sanitizer_syscall_pre_impl_rename((long)(oldname), (long)(newname)) +#define __sanitizer_syscall_post_rename(res, oldname, newname) \ + __sanitizer_syscall_post_impl_rename(res, (long)(oldname), (long)(newname)) +#define __sanitizer_syscall_pre_chmod(filename, mode) \ + __sanitizer_syscall_pre_impl_chmod((long)(filename), (long)(mode)) +#define __sanitizer_syscall_post_chmod(res, filename, mode) \ + __sanitizer_syscall_post_impl_chmod(res, (long)(filename), (long)(mode)) +#define __sanitizer_syscall_pre_fchmod(fd, mode) \ + __sanitizer_syscall_pre_impl_fchmod((long)(fd), (long)(mode)) +#define __sanitizer_syscall_post_fchmod(res, fd, mode) \ + __sanitizer_syscall_post_impl_fchmod(res, (long)(fd), (long)(mode)) +#define __sanitizer_syscall_pre_fcntl(fd, cmd, arg) \ + __sanitizer_syscall_pre_impl_fcntl((long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_fcntl(res, fd, cmd, arg) \ + __sanitizer_syscall_post_impl_fcntl(res, (long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_fcntl64(fd, cmd, arg) \ + __sanitizer_syscall_pre_impl_fcntl64((long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_fcntl64(res, fd, cmd, arg) \ + __sanitizer_syscall_post_impl_fcntl64(res, (long)(fd), (long)(cmd), \ + (long)(arg)) +#define __sanitizer_syscall_pre_pipe(fildes) \ + __sanitizer_syscall_pre_impl_pipe((long)(fildes)) +#define __sanitizer_syscall_post_pipe(res, fildes) \ + __sanitizer_syscall_post_impl_pipe(res, (long)(fildes)) +#define __sanitizer_syscall_pre_pipe2(fildes, flags) \ + __sanitizer_syscall_pre_impl_pipe2((long)(fildes), (long)(flags)) +#define __sanitizer_syscall_post_pipe2(res, fildes, flags) \ + __sanitizer_syscall_post_impl_pipe2(res, (long)(fildes), (long)(flags)) +#define __sanitizer_syscall_pre_dup(fildes) \ + __sanitizer_syscall_pre_impl_dup((long)(fildes)) +#define __sanitizer_syscall_post_dup(res, fildes) \ + __sanitizer_syscall_post_impl_dup(res, (long)(fildes)) +#define __sanitizer_syscall_pre_dup2(oldfd, newfd) \ + __sanitizer_syscall_pre_impl_dup2((long)(oldfd), (long)(newfd)) +#define __sanitizer_syscall_post_dup2(res, oldfd, newfd) \ + __sanitizer_syscall_post_impl_dup2(res, (long)(oldfd), (long)(newfd)) +#define __sanitizer_syscall_pre_dup3(oldfd, newfd, flags) \ + __sanitizer_syscall_pre_impl_dup3((long)(oldfd), (long)(newfd), (long)(flags)) +#define __sanitizer_syscall_post_dup3(res, oldfd, newfd, flags) \ + __sanitizer_syscall_post_impl_dup3(res, (long)(oldfd), (long)(newfd), \ + (long)(flags)) +#define __sanitizer_syscall_pre_ioperm(from, num, on) \ + __sanitizer_syscall_pre_impl_ioperm((long)(from), (long)(num), (long)(on)) +#define __sanitizer_syscall_post_ioperm(res, from, num, on) \ + __sanitizer_syscall_post_impl_ioperm(res, (long)(from), (long)(num), \ + (long)(on)) +#define __sanitizer_syscall_pre_ioctl(fd, cmd, arg) \ + __sanitizer_syscall_pre_impl_ioctl((long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_ioctl(res, fd, cmd, arg) \ + __sanitizer_syscall_post_impl_ioctl(res, (long)(fd), (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_flock(fd, cmd) \ + __sanitizer_syscall_pre_impl_flock((long)(fd), (long)(cmd)) +#define __sanitizer_syscall_post_flock(res, fd, cmd) \ + __sanitizer_syscall_post_impl_flock(res, (long)(fd), (long)(cmd)) +#define __sanitizer_syscall_pre_io_setup(nr_reqs, ctx) \ + __sanitizer_syscall_pre_impl_io_setup((long)(nr_reqs), (long)(ctx)) +#define __sanitizer_syscall_post_io_setup(res, nr_reqs, ctx) \ + __sanitizer_syscall_post_impl_io_setup(res, (long)(nr_reqs), (long)(ctx)) +#define __sanitizer_syscall_pre_io_destroy(ctx) \ + __sanitizer_syscall_pre_impl_io_destroy((long)(ctx)) +#define __sanitizer_syscall_post_io_destroy(res, ctx) \ + __sanitizer_syscall_post_impl_io_destroy(res, (long)(ctx)) +#define __sanitizer_syscall_pre_io_getevents(ctx_id, min_nr, nr, events, \ + timeout) \ + __sanitizer_syscall_pre_impl_io_getevents((long)(ctx_id), (long)(min_nr), \ + (long)(nr), (long)(events), \ + (long)(timeout)) +#define __sanitizer_syscall_post_io_getevents(res, ctx_id, min_nr, nr, events, \ + timeout) \ + __sanitizer_syscall_post_impl_io_getevents(res, (long)(ctx_id), \ + (long)(min_nr), (long)(nr), \ + (long)(events), (long)(timeout)) +#define __sanitizer_syscall_pre_io_submit(ctx_id, arg1, arg2) \ + __sanitizer_syscall_pre_impl_io_submit((long)(ctx_id), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_io_submit(res, ctx_id, arg1, arg2) \ + __sanitizer_syscall_post_impl_io_submit(res, (long)(ctx_id), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_io_cancel(ctx_id, iocb, result) \ + __sanitizer_syscall_pre_impl_io_cancel((long)(ctx_id), (long)(iocb), \ + (long)(result)) +#define __sanitizer_syscall_post_io_cancel(res, ctx_id, iocb, result) \ + __sanitizer_syscall_post_impl_io_cancel(res, (long)(ctx_id), (long)(iocb), \ + (long)(result)) +#define __sanitizer_syscall_pre_sendfile(out_fd, in_fd, offset, count) \ + __sanitizer_syscall_pre_impl_sendfile((long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_post_sendfile(res, out_fd, in_fd, offset, count) \ + __sanitizer_syscall_post_impl_sendfile(res, (long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_pre_sendfile64(out_fd, in_fd, offset, count) \ + __sanitizer_syscall_pre_impl_sendfile64((long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_post_sendfile64(res, out_fd, in_fd, offset, count) \ + __sanitizer_syscall_post_impl_sendfile64(res, (long)(out_fd), (long)(in_fd), \ + (long)(offset), (long)(count)) +#define __sanitizer_syscall_pre_readlink(path, buf, bufsiz) \ + __sanitizer_syscall_pre_impl_readlink((long)(path), (long)(buf), \ + (long)(bufsiz)) +#define __sanitizer_syscall_post_readlink(res, path, buf, bufsiz) \ + __sanitizer_syscall_post_impl_readlink(res, (long)(path), (long)(buf), \ + (long)(bufsiz)) +#define __sanitizer_syscall_pre_creat(pathname, mode) \ + __sanitizer_syscall_pre_impl_creat((long)(pathname), (long)(mode)) +#define __sanitizer_syscall_post_creat(res, pathname, mode) \ + __sanitizer_syscall_post_impl_creat(res, (long)(pathname), (long)(mode)) +#define __sanitizer_syscall_pre_open(filename, flags, mode) \ + __sanitizer_syscall_pre_impl_open((long)(filename), (long)(flags), \ + (long)(mode)) +#define __sanitizer_syscall_post_open(res, filename, flags, mode) \ + __sanitizer_syscall_post_impl_open(res, (long)(filename), (long)(flags), \ + (long)(mode)) +#define __sanitizer_syscall_pre_close(fd) \ + __sanitizer_syscall_pre_impl_close((long)(fd)) +#define __sanitizer_syscall_post_close(res, fd) \ + __sanitizer_syscall_post_impl_close(res, (long)(fd)) +#define __sanitizer_syscall_pre_access(filename, mode) \ + __sanitizer_syscall_pre_impl_access((long)(filename), (long)(mode)) +#define __sanitizer_syscall_post_access(res, filename, mode) \ + __sanitizer_syscall_post_impl_access(res, (long)(filename), (long)(mode)) +#define __sanitizer_syscall_pre_vhangup() __sanitizer_syscall_pre_impl_vhangup() +#define __sanitizer_syscall_post_vhangup(res) \ + __sanitizer_syscall_post_impl_vhangup(res) +#define __sanitizer_syscall_pre_chown(filename, user, group) \ + __sanitizer_syscall_pre_impl_chown((long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_post_chown(res, filename, user, group) \ + __sanitizer_syscall_post_impl_chown(res, (long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_pre_lchown(filename, user, group) \ + __sanitizer_syscall_pre_impl_lchown((long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_post_lchown(res, filename, user, group) \ + __sanitizer_syscall_post_impl_lchown(res, (long)(filename), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_pre_fchown(fd, user, group) \ + __sanitizer_syscall_pre_impl_fchown((long)(fd), (long)(user), (long)(group)) +#define __sanitizer_syscall_post_fchown(res, fd, user, group) \ + __sanitizer_syscall_post_impl_fchown(res, (long)(fd), (long)(user), \ + (long)(group)) +#define __sanitizer_syscall_pre_chown16(filename, user, group) \ + __sanitizer_syscall_pre_impl_chown16((long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_post_chown16(res, filename, user, group) \ + __sanitizer_syscall_post_impl_chown16(res, (long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_pre_lchown16(filename, user, group) \ + __sanitizer_syscall_pre_impl_lchown16((long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_post_lchown16(res, filename, user, group) \ + __sanitizer_syscall_post_impl_lchown16(res, (long)(filename), (long)user, \ + (long)group) +#define __sanitizer_syscall_pre_fchown16(fd, user, group) \ + __sanitizer_syscall_pre_impl_fchown16((long)(fd), (long)user, (long)group) +#define __sanitizer_syscall_post_fchown16(res, fd, user, group) \ + __sanitizer_syscall_post_impl_fchown16(res, (long)(fd), (long)user, \ + (long)group) +#define __sanitizer_syscall_pre_setregid16(rgid, egid) \ + __sanitizer_syscall_pre_impl_setregid16((long)rgid, (long)egid) +#define __sanitizer_syscall_post_setregid16(res, rgid, egid) \ + __sanitizer_syscall_post_impl_setregid16(res, (long)rgid, (long)egid) +#define __sanitizer_syscall_pre_setgid16(gid) \ + __sanitizer_syscall_pre_impl_setgid16((long)gid) +#define __sanitizer_syscall_post_setgid16(res, gid) \ + __sanitizer_syscall_post_impl_setgid16(res, (long)gid) +#define __sanitizer_syscall_pre_setreuid16(ruid, euid) \ + __sanitizer_syscall_pre_impl_setreuid16((long)ruid, (long)euid) +#define __sanitizer_syscall_post_setreuid16(res, ruid, euid) \ + __sanitizer_syscall_post_impl_setreuid16(res, (long)ruid, (long)euid) +#define __sanitizer_syscall_pre_setuid16(uid) \ + __sanitizer_syscall_pre_impl_setuid16((long)uid) +#define __sanitizer_syscall_post_setuid16(res, uid) \ + __sanitizer_syscall_post_impl_setuid16(res, (long)uid) +#define __sanitizer_syscall_pre_setresuid16(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_setresuid16((long)ruid, (long)euid, (long)suid) +#define __sanitizer_syscall_post_setresuid16(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_setresuid16(res, (long)ruid, (long)euid, \ + (long)suid) +#define __sanitizer_syscall_pre_getresuid16(ruid, euid, suid) \ + __sanitizer_syscall_pre_impl_getresuid16((long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_post_getresuid16(res, ruid, euid, suid) \ + __sanitizer_syscall_post_impl_getresuid16(res, (long)(ruid), (long)(euid), \ + (long)(suid)) +#define __sanitizer_syscall_pre_setresgid16(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_setresgid16((long)rgid, (long)egid, (long)sgid) +#define __sanitizer_syscall_post_setresgid16(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_setresgid16(res, (long)rgid, (long)egid, \ + (long)sgid) +#define __sanitizer_syscall_pre_getresgid16(rgid, egid, sgid) \ + __sanitizer_syscall_pre_impl_getresgid16((long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_post_getresgid16(res, rgid, egid, sgid) \ + __sanitizer_syscall_post_impl_getresgid16(res, (long)(rgid), (long)(egid), \ + (long)(sgid)) +#define __sanitizer_syscall_pre_setfsuid16(uid) \ + __sanitizer_syscall_pre_impl_setfsuid16((long)uid) +#define __sanitizer_syscall_post_setfsuid16(res, uid) \ + __sanitizer_syscall_post_impl_setfsuid16(res, (long)uid) +#define __sanitizer_syscall_pre_setfsgid16(gid) \ + __sanitizer_syscall_pre_impl_setfsgid16((long)gid) +#define __sanitizer_syscall_post_setfsgid16(res, gid) \ + __sanitizer_syscall_post_impl_setfsgid16(res, (long)gid) +#define __sanitizer_syscall_pre_getgroups16(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_getgroups16((long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_post_getgroups16(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_getgroups16(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_setgroups16(gidsetsize, grouplist) \ + __sanitizer_syscall_pre_impl_setgroups16((long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_post_setgroups16(res, gidsetsize, grouplist) \ + __sanitizer_syscall_post_impl_setgroups16(res, (long)(gidsetsize), \ + (long)(grouplist)) +#define __sanitizer_syscall_pre_getuid16() \ + __sanitizer_syscall_pre_impl_getuid16() +#define __sanitizer_syscall_post_getuid16(res) \ + __sanitizer_syscall_post_impl_getuid16(res) +#define __sanitizer_syscall_pre_geteuid16() \ + __sanitizer_syscall_pre_impl_geteuid16() +#define __sanitizer_syscall_post_geteuid16(res) \ + __sanitizer_syscall_post_impl_geteuid16(res) +#define __sanitizer_syscall_pre_getgid16() \ + __sanitizer_syscall_pre_impl_getgid16() +#define __sanitizer_syscall_post_getgid16(res) \ + __sanitizer_syscall_post_impl_getgid16(res) +#define __sanitizer_syscall_pre_getegid16() \ + __sanitizer_syscall_pre_impl_getegid16() +#define __sanitizer_syscall_post_getegid16(res) \ + __sanitizer_syscall_post_impl_getegid16(res) +#define __sanitizer_syscall_pre_utime(filename, times) \ + __sanitizer_syscall_pre_impl_utime((long)(filename), (long)(times)) +#define __sanitizer_syscall_post_utime(res, filename, times) \ + __sanitizer_syscall_post_impl_utime(res, (long)(filename), (long)(times)) +#define __sanitizer_syscall_pre_utimes(filename, utimes) \ + __sanitizer_syscall_pre_impl_utimes((long)(filename), (long)(utimes)) +#define __sanitizer_syscall_post_utimes(res, filename, utimes) \ + __sanitizer_syscall_post_impl_utimes(res, (long)(filename), (long)(utimes)) +#define __sanitizer_syscall_pre_lseek(fd, offset, origin) \ + __sanitizer_syscall_pre_impl_lseek((long)(fd), (long)(offset), (long)(origin)) +#define __sanitizer_syscall_post_lseek(res, fd, offset, origin) \ + __sanitizer_syscall_post_impl_lseek(res, (long)(fd), (long)(offset), \ + (long)(origin)) +#define __sanitizer_syscall_pre_llseek(fd, offset_high, offset_low, result, \ + origin) \ + __sanitizer_syscall_pre_impl_llseek((long)(fd), (long)(offset_high), \ + (long)(offset_low), (long)(result), \ + (long)(origin)) +#define __sanitizer_syscall_post_llseek(res, fd, offset_high, offset_low, \ + result, origin) \ + __sanitizer_syscall_post_impl_llseek(res, (long)(fd), (long)(offset_high), \ + (long)(offset_low), (long)(result), \ + (long)(origin)) +#define __sanitizer_syscall_pre_read(fd, buf, count) \ + __sanitizer_syscall_pre_impl_read((long)(fd), (long)(buf), (long)(count)) +#define __sanitizer_syscall_post_read(res, fd, buf, count) \ + __sanitizer_syscall_post_impl_read(res, (long)(fd), (long)(buf), \ + (long)(count)) +#define __sanitizer_syscall_pre_readv(fd, vec, vlen) \ + __sanitizer_syscall_pre_impl_readv((long)(fd), (long)(vec), (long)(vlen)) +#define __sanitizer_syscall_post_readv(res, fd, vec, vlen) \ + __sanitizer_syscall_post_impl_readv(res, (long)(fd), (long)(vec), \ + (long)(vlen)) +#define __sanitizer_syscall_pre_write(fd, buf, count) \ + __sanitizer_syscall_pre_impl_write((long)(fd), (long)(buf), (long)(count)) +#define __sanitizer_syscall_post_write(res, fd, buf, count) \ + __sanitizer_syscall_post_impl_write(res, (long)(fd), (long)(buf), \ + (long)(count)) +#define __sanitizer_syscall_pre_writev(fd, vec, vlen) \ + __sanitizer_syscall_pre_impl_writev((long)(fd), (long)(vec), (long)(vlen)) +#define __sanitizer_syscall_post_writev(res, fd, vec, vlen) \ + __sanitizer_syscall_post_impl_writev(res, (long)(fd), (long)(vec), \ + (long)(vlen)) + +#ifdef _LP64 +#define __sanitizer_syscall_pre_pread64(fd, buf, count, pos) \ + __sanitizer_syscall_pre_impl_pread64((long)(fd), (long)(buf), (long)(count), \ + (long)(pos)) +#define __sanitizer_syscall_post_pread64(res, fd, buf, count, pos) \ + __sanitizer_syscall_post_impl_pread64(res, (long)(fd), (long)(buf), \ + (long)(count), (long)(pos)) +#define __sanitizer_syscall_pre_pwrite64(fd, buf, count, pos) \ + __sanitizer_syscall_pre_impl_pwrite64((long)(fd), (long)(buf), \ + (long)(count), (long)(pos)) +#define __sanitizer_syscall_post_pwrite64(res, fd, buf, count, pos) \ + __sanitizer_syscall_post_impl_pwrite64(res, (long)(fd), (long)(buf), \ + (long)(count), (long)(pos)) +#else +#define __sanitizer_syscall_pre_pread64(fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_pre_impl_pread64((long)(fd), (long)(buf), (long)(count), \ + (long)(pos)) +#define __sanitizer_syscall_post_pread64(res, fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_post_impl_pread64(res, (long)(fd), (long)(buf), \ + (long)(count), (long)(pos)) +#define __sanitizer_syscall_pre_pwrite64(fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_pre_impl_pwrite64( \ + (long)(fd), (long)(buf), (long)(count), (long)(pos0), (long)(pos1)) +#define __sanitizer_syscall_post_pwrite64(res, fd, buf, count, pos0, pos1) \ + __sanitizer_syscall_post_impl_pwrite64( \ + res, (long)(fd), (long)(buf), (long)(count), (long)(pos0), (long)(pos1)) +#endif + +#define __sanitizer_syscall_pre_preadv(fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_pre_impl_preadv((long)(fd), (long)(vec), (long)(vlen), \ + (long)(pos_l), (long)(pos_h)) +#define __sanitizer_syscall_post_preadv(res, fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_post_impl_preadv(res, (long)(fd), (long)(vec), \ + (long)(vlen), (long)(pos_l), \ + (long)(pos_h)) +#define __sanitizer_syscall_pre_pwritev(fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_pre_impl_pwritev((long)(fd), (long)(vec), (long)(vlen), \ + (long)(pos_l), (long)(pos_h)) +#define __sanitizer_syscall_post_pwritev(res, fd, vec, vlen, pos_l, pos_h) \ + __sanitizer_syscall_post_impl_pwritev(res, (long)(fd), (long)(vec), \ + (long)(vlen), (long)(pos_l), \ + (long)(pos_h)) +#define __sanitizer_syscall_pre_getcwd(buf, size) \ + __sanitizer_syscall_pre_impl_getcwd((long)(buf), (long)(size)) +#define __sanitizer_syscall_post_getcwd(res, buf, size) \ + __sanitizer_syscall_post_impl_getcwd(res, (long)(buf), (long)(size)) +#define __sanitizer_syscall_pre_mkdir(pathname, mode) \ + __sanitizer_syscall_pre_impl_mkdir((long)(pathname), (long)(mode)) +#define __sanitizer_syscall_post_mkdir(res, pathname, mode) \ + __sanitizer_syscall_post_impl_mkdir(res, (long)(pathname), (long)(mode)) +#define __sanitizer_syscall_pre_chdir(filename) \ + __sanitizer_syscall_pre_impl_chdir((long)(filename)) +#define __sanitizer_syscall_post_chdir(res, filename) \ + __sanitizer_syscall_post_impl_chdir(res, (long)(filename)) +#define __sanitizer_syscall_pre_fchdir(fd) \ + __sanitizer_syscall_pre_impl_fchdir((long)(fd)) +#define __sanitizer_syscall_post_fchdir(res, fd) \ + __sanitizer_syscall_post_impl_fchdir(res, (long)(fd)) +#define __sanitizer_syscall_pre_rmdir(pathname) \ + __sanitizer_syscall_pre_impl_rmdir((long)(pathname)) +#define __sanitizer_syscall_post_rmdir(res, pathname) \ + __sanitizer_syscall_post_impl_rmdir(res, (long)(pathname)) +#define __sanitizer_syscall_pre_lookup_dcookie(cookie64, buf, len) \ + __sanitizer_syscall_pre_impl_lookup_dcookie((long)(cookie64), (long)(buf), \ + (long)(len)) +#define __sanitizer_syscall_post_lookup_dcookie(res, cookie64, buf, len) \ + __sanitizer_syscall_post_impl_lookup_dcookie(res, (long)(cookie64), \ + (long)(buf), (long)(len)) +#define __sanitizer_syscall_pre_quotactl(cmd, special, id, addr) \ + __sanitizer_syscall_pre_impl_quotactl((long)(cmd), (long)(special), \ + (long)(id), (long)(addr)) +#define __sanitizer_syscall_post_quotactl(res, cmd, special, id, addr) \ + __sanitizer_syscall_post_impl_quotactl(res, (long)(cmd), (long)(special), \ + (long)(id), (long)(addr)) +#define __sanitizer_syscall_pre_getdents(fd, dirent, count) \ + __sanitizer_syscall_pre_impl_getdents((long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_post_getdents(res, fd, dirent, count) \ + __sanitizer_syscall_post_impl_getdents(res, (long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_pre_getdents64(fd, dirent, count) \ + __sanitizer_syscall_pre_impl_getdents64((long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_post_getdents64(res, fd, dirent, count) \ + __sanitizer_syscall_post_impl_getdents64(res, (long)(fd), (long)(dirent), \ + (long)(count)) +#define __sanitizer_syscall_pre_setsockopt(fd, level, optname, optval, optlen) \ + __sanitizer_syscall_pre_impl_setsockopt((long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_post_setsockopt(res, fd, level, optname, optval, \ + optlen) \ + __sanitizer_syscall_post_impl_setsockopt(res, (long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_pre_getsockopt(fd, level, optname, optval, optlen) \ + __sanitizer_syscall_pre_impl_getsockopt((long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_post_getsockopt(res, fd, level, optname, optval, \ + optlen) \ + __sanitizer_syscall_post_impl_getsockopt(res, (long)(fd), (long)(level), \ + (long)(optname), (long)(optval), \ + (long)(optlen)) +#define __sanitizer_syscall_pre_bind(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_bind((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_bind(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_bind(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_connect(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_connect((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_connect(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_connect(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_accept(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_accept((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_accept(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_accept(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_accept4(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_accept4((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_post_accept4(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_accept4(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_getsockname(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_getsockname((long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_getsockname(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_getsockname(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_getpeername(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_getpeername((long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_getpeername(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_getpeername(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_send(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_send((long)(arg0), (long)(arg1), (long)(arg2), \ + (long)(arg3)) +#define __sanitizer_syscall_post_send(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_send(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_sendto(arg0, arg1, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_sendto((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_sendto(res, arg0, arg1, arg2, arg3, arg4, \ + arg5) \ + __sanitizer_syscall_post_impl_sendto(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_pre_sendmsg(fd, msg, flags) \ + __sanitizer_syscall_pre_impl_sendmsg((long)(fd), (long)(msg), (long)(flags)) +#define __sanitizer_syscall_post_sendmsg(res, fd, msg, flags) \ + __sanitizer_syscall_post_impl_sendmsg(res, (long)(fd), (long)(msg), \ + (long)(flags)) +#define __sanitizer_syscall_pre_sendmmsg(fd, msg, vlen, flags) \ + __sanitizer_syscall_pre_impl_sendmmsg((long)(fd), (long)(msg), (long)(vlen), \ + (long)(flags)) +#define __sanitizer_syscall_post_sendmmsg(res, fd, msg, vlen, flags) \ + __sanitizer_syscall_post_impl_sendmmsg(res, (long)(fd), (long)(msg), \ + (long)(vlen), (long)(flags)) +#define __sanitizer_syscall_pre_recv(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_recv((long)(arg0), (long)(arg1), (long)(arg2), \ + (long)(arg3)) +#define __sanitizer_syscall_post_recv(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_recv(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_recvfrom(arg0, arg1, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_recvfrom((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_recvfrom(res, arg0, arg1, arg2, arg3, arg4, \ + arg5) \ + __sanitizer_syscall_post_impl_recvfrom(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_pre_recvmsg(fd, msg, flags) \ + __sanitizer_syscall_pre_impl_recvmsg((long)(fd), (long)(msg), (long)(flags)) +#define __sanitizer_syscall_post_recvmsg(res, fd, msg, flags) \ + __sanitizer_syscall_post_impl_recvmsg(res, (long)(fd), (long)(msg), \ + (long)(flags)) +#define __sanitizer_syscall_pre_recvmmsg(fd, msg, vlen, flags, timeout) \ + __sanitizer_syscall_pre_impl_recvmmsg((long)(fd), (long)(msg), (long)(vlen), \ + (long)(flags), (long)(timeout)) +#define __sanitizer_syscall_post_recvmmsg(res, fd, msg, vlen, flags, timeout) \ + __sanitizer_syscall_post_impl_recvmmsg(res, (long)(fd), (long)(msg), \ + (long)(vlen), (long)(flags), \ + (long)(timeout)) +#define __sanitizer_syscall_pre_socket(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_socket((long)(arg0), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_socket(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_socket(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_socketpair(arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_pre_impl_socketpair((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_post_socketpair(res, arg0, arg1, arg2, arg3) \ + __sanitizer_syscall_post_impl_socketpair(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3)) +#define __sanitizer_syscall_pre_socketcall(call, args) \ + __sanitizer_syscall_pre_impl_socketcall((long)(call), (long)(args)) +#define __sanitizer_syscall_post_socketcall(res, call, args) \ + __sanitizer_syscall_post_impl_socketcall(res, (long)(call), (long)(args)) +#define __sanitizer_syscall_pre_listen(arg0, arg1) \ + __sanitizer_syscall_pre_impl_listen((long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_post_listen(res, arg0, arg1) \ + __sanitizer_syscall_post_impl_listen(res, (long)(arg0), (long)(arg1)) +#define __sanitizer_syscall_pre_poll(ufds, nfds, timeout) \ + __sanitizer_syscall_pre_impl_poll((long)(ufds), (long)(nfds), (long)(timeout)) +#define __sanitizer_syscall_post_poll(res, ufds, nfds, timeout) \ + __sanitizer_syscall_post_impl_poll(res, (long)(ufds), (long)(nfds), \ + (long)(timeout)) +#define __sanitizer_syscall_pre_select(n, inp, outp, exp, tvp) \ + __sanitizer_syscall_pre_impl_select((long)(n), (long)(inp), (long)(outp), \ + (long)(exp), (long)(tvp)) +#define __sanitizer_syscall_post_select(res, n, inp, outp, exp, tvp) \ + __sanitizer_syscall_post_impl_select(res, (long)(n), (long)(inp), \ + (long)(outp), (long)(exp), (long)(tvp)) +#define __sanitizer_syscall_pre_old_select(arg) \ + __sanitizer_syscall_pre_impl_old_select((long)(arg)) +#define __sanitizer_syscall_post_old_select(res, arg) \ + __sanitizer_syscall_post_impl_old_select(res, (long)(arg)) +#define __sanitizer_syscall_pre_epoll_create(size) \ + __sanitizer_syscall_pre_impl_epoll_create((long)(size)) +#define __sanitizer_syscall_post_epoll_create(res, size) \ + __sanitizer_syscall_post_impl_epoll_create(res, (long)(size)) +#define __sanitizer_syscall_pre_epoll_create1(flags) \ + __sanitizer_syscall_pre_impl_epoll_create1((long)(flags)) +#define __sanitizer_syscall_post_epoll_create1(res, flags) \ + __sanitizer_syscall_post_impl_epoll_create1(res, (long)(flags)) +#define __sanitizer_syscall_pre_epoll_ctl(epfd, op, fd, event) \ + __sanitizer_syscall_pre_impl_epoll_ctl((long)(epfd), (long)(op), (long)(fd), \ + (long)(event)) +#define __sanitizer_syscall_post_epoll_ctl(res, epfd, op, fd, event) \ + __sanitizer_syscall_post_impl_epoll_ctl(res, (long)(epfd), (long)(op), \ + (long)(fd), (long)(event)) +#define __sanitizer_syscall_pre_epoll_wait(epfd, events, maxevents, timeout) \ + __sanitizer_syscall_pre_impl_epoll_wait((long)(epfd), (long)(events), \ + (long)(maxevents), (long)(timeout)) +#define __sanitizer_syscall_post_epoll_wait(res, epfd, events, maxevents, \ + timeout) \ + __sanitizer_syscall_post_impl_epoll_wait(res, (long)(epfd), (long)(events), \ + (long)(maxevents), (long)(timeout)) +#define __sanitizer_syscall_pre_epoll_pwait(epfd, events, maxevents, timeout, \ + sigmask, sigsetsize) \ + __sanitizer_syscall_pre_impl_epoll_pwait( \ + (long)(epfd), (long)(events), (long)(maxevents), (long)(timeout), \ + (long)(sigmask), (long)(sigsetsize)) +#define __sanitizer_syscall_post_epoll_pwait(res, epfd, events, maxevents, \ + timeout, sigmask, sigsetsize) \ + __sanitizer_syscall_post_impl_epoll_pwait( \ + res, (long)(epfd), (long)(events), (long)(maxevents), (long)(timeout), \ + (long)(sigmask), (long)(sigsetsize)) +#define __sanitizer_syscall_pre_gethostname(name, len) \ + __sanitizer_syscall_pre_impl_gethostname((long)(name), (long)(len)) +#define __sanitizer_syscall_post_gethostname(res, name, len) \ + __sanitizer_syscall_post_impl_gethostname(res, (long)(name), (long)(len)) +#define __sanitizer_syscall_pre_sethostname(name, len) \ + __sanitizer_syscall_pre_impl_sethostname((long)(name), (long)(len)) +#define __sanitizer_syscall_post_sethostname(res, name, len) \ + __sanitizer_syscall_post_impl_sethostname(res, (long)(name), (long)(len)) +#define __sanitizer_syscall_pre_setdomainname(name, len) \ + __sanitizer_syscall_pre_impl_setdomainname((long)(name), (long)(len)) +#define __sanitizer_syscall_post_setdomainname(res, name, len) \ + __sanitizer_syscall_post_impl_setdomainname(res, (long)(name), (long)(len)) +#define __sanitizer_syscall_pre_newuname(name) \ + __sanitizer_syscall_pre_impl_newuname((long)(name)) +#define __sanitizer_syscall_post_newuname(res, name) \ + __sanitizer_syscall_post_impl_newuname(res, (long)(name)) +#define __sanitizer_syscall_pre_uname(arg0) \ + __sanitizer_syscall_pre_impl_uname((long)(arg0)) +#define __sanitizer_syscall_post_uname(res, arg0) \ + __sanitizer_syscall_post_impl_uname(res, (long)(arg0)) +#define __sanitizer_syscall_pre_olduname(arg0) \ + __sanitizer_syscall_pre_impl_olduname((long)(arg0)) +#define __sanitizer_syscall_post_olduname(res, arg0) \ + __sanitizer_syscall_post_impl_olduname(res, (long)(arg0)) +#define __sanitizer_syscall_pre_getrlimit(resource, rlim) \ + __sanitizer_syscall_pre_impl_getrlimit((long)(resource), (long)(rlim)) +#define __sanitizer_syscall_post_getrlimit(res, resource, rlim) \ + __sanitizer_syscall_post_impl_getrlimit(res, (long)(resource), (long)(rlim)) +#define __sanitizer_syscall_pre_old_getrlimit(resource, rlim) \ + __sanitizer_syscall_pre_impl_old_getrlimit((long)(resource), (long)(rlim)) +#define __sanitizer_syscall_post_old_getrlimit(res, resource, rlim) \ + __sanitizer_syscall_post_impl_old_getrlimit(res, (long)(resource), \ + (long)(rlim)) +#define __sanitizer_syscall_pre_setrlimit(resource, rlim) \ + __sanitizer_syscall_pre_impl_setrlimit((long)(resource), (long)(rlim)) +#define __sanitizer_syscall_post_setrlimit(res, resource, rlim) \ + __sanitizer_syscall_post_impl_setrlimit(res, (long)(resource), (long)(rlim)) +#define __sanitizer_syscall_pre_prlimit64(pid, resource, new_rlim, old_rlim) \ + __sanitizer_syscall_pre_impl_prlimit64((long)(pid), (long)(resource), \ + (long)(new_rlim), (long)(old_rlim)) +#define __sanitizer_syscall_post_prlimit64(res, pid, resource, new_rlim, \ + old_rlim) \ + __sanitizer_syscall_post_impl_prlimit64(res, (long)(pid), (long)(resource), \ + (long)(new_rlim), (long)(old_rlim)) +#define __sanitizer_syscall_pre_getrusage(who, ru) \ + __sanitizer_syscall_pre_impl_getrusage((long)(who), (long)(ru)) +#define __sanitizer_syscall_post_getrusage(res, who, ru) \ + __sanitizer_syscall_post_impl_getrusage(res, (long)(who), (long)(ru)) +#define __sanitizer_syscall_pre_umask(mask) \ + __sanitizer_syscall_pre_impl_umask((long)(mask)) +#define __sanitizer_syscall_post_umask(res, mask) \ + __sanitizer_syscall_post_impl_umask(res, (long)(mask)) +#define __sanitizer_syscall_pre_msgget(key, msgflg) \ + __sanitizer_syscall_pre_impl_msgget((long)(key), (long)(msgflg)) +#define __sanitizer_syscall_post_msgget(res, key, msgflg) \ + __sanitizer_syscall_post_impl_msgget(res, (long)(key), (long)(msgflg)) +#define __sanitizer_syscall_pre_msgsnd(msqid, msgp, msgsz, msgflg) \ + __sanitizer_syscall_pre_impl_msgsnd((long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgflg)) +#define __sanitizer_syscall_post_msgsnd(res, msqid, msgp, msgsz, msgflg) \ + __sanitizer_syscall_post_impl_msgsnd(res, (long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgflg)) +#define __sanitizer_syscall_pre_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg) \ + __sanitizer_syscall_pre_impl_msgrcv((long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgtyp), \ + (long)(msgflg)) +#define __sanitizer_syscall_post_msgrcv(res, msqid, msgp, msgsz, msgtyp, \ + msgflg) \ + __sanitizer_syscall_post_impl_msgrcv(res, (long)(msqid), (long)(msgp), \ + (long)(msgsz), (long)(msgtyp), \ + (long)(msgflg)) +#define __sanitizer_syscall_pre_msgctl(msqid, cmd, buf) \ + __sanitizer_syscall_pre_impl_msgctl((long)(msqid), (long)(cmd), (long)(buf)) +#define __sanitizer_syscall_post_msgctl(res, msqid, cmd, buf) \ + __sanitizer_syscall_post_impl_msgctl(res, (long)(msqid), (long)(cmd), \ + (long)(buf)) +#define __sanitizer_syscall_pre_semget(key, nsems, semflg) \ + __sanitizer_syscall_pre_impl_semget((long)(key), (long)(nsems), \ + (long)(semflg)) +#define __sanitizer_syscall_post_semget(res, key, nsems, semflg) \ + __sanitizer_syscall_post_impl_semget(res, (long)(key), (long)(nsems), \ + (long)(semflg)) +#define __sanitizer_syscall_pre_semop(semid, sops, nsops) \ + __sanitizer_syscall_pre_impl_semop((long)(semid), (long)(sops), (long)(nsops)) +#define __sanitizer_syscall_post_semop(res, semid, sops, nsops) \ + __sanitizer_syscall_post_impl_semop(res, (long)(semid), (long)(sops), \ + (long)(nsops)) +#define __sanitizer_syscall_pre_semctl(semid, semnum, cmd, arg) \ + __sanitizer_syscall_pre_impl_semctl((long)(semid), (long)(semnum), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_post_semctl(res, semid, semnum, cmd, arg) \ + __sanitizer_syscall_post_impl_semctl(res, (long)(semid), (long)(semnum), \ + (long)(cmd), (long)(arg)) +#define __sanitizer_syscall_pre_semtimedop(semid, sops, nsops, timeout) \ + __sanitizer_syscall_pre_impl_semtimedop((long)(semid), (long)(sops), \ + (long)(nsops), (long)(timeout)) +#define __sanitizer_syscall_post_semtimedop(res, semid, sops, nsops, timeout) \ + __sanitizer_syscall_post_impl_semtimedop(res, (long)(semid), (long)(sops), \ + (long)(nsops), (long)(timeout)) +#define __sanitizer_syscall_pre_shmat(shmid, shmaddr, shmflg) \ + __sanitizer_syscall_pre_impl_shmat((long)(shmid), (long)(shmaddr), \ + (long)(shmflg)) +#define __sanitizer_syscall_post_shmat(res, shmid, shmaddr, shmflg) \ + __sanitizer_syscall_post_impl_shmat(res, (long)(shmid), (long)(shmaddr), \ + (long)(shmflg)) +#define __sanitizer_syscall_pre_shmget(key, size, flag) \ + __sanitizer_syscall_pre_impl_shmget((long)(key), (long)(size), (long)(flag)) +#define __sanitizer_syscall_post_shmget(res, key, size, flag) \ + __sanitizer_syscall_post_impl_shmget(res, (long)(key), (long)(size), \ + (long)(flag)) +#define __sanitizer_syscall_pre_shmdt(shmaddr) \ + __sanitizer_syscall_pre_impl_shmdt((long)(shmaddr)) +#define __sanitizer_syscall_post_shmdt(res, shmaddr) \ + __sanitizer_syscall_post_impl_shmdt(res, (long)(shmaddr)) +#define __sanitizer_syscall_pre_shmctl(shmid, cmd, buf) \ + __sanitizer_syscall_pre_impl_shmctl((long)(shmid), (long)(cmd), (long)(buf)) +#define __sanitizer_syscall_post_shmctl(res, shmid, cmd, buf) \ + __sanitizer_syscall_post_impl_shmctl(res, (long)(shmid), (long)(cmd), \ + (long)(buf)) +#define __sanitizer_syscall_pre_ipc(call, first, second, third, ptr, fifth) \ + __sanitizer_syscall_pre_impl_ipc((long)(call), (long)(first), \ + (long)(second), (long)(third), (long)(ptr), \ + (long)(fifth)) +#define __sanitizer_syscall_post_ipc(res, call, first, second, third, ptr, \ + fifth) \ + __sanitizer_syscall_post_impl_ipc(res, (long)(call), (long)(first), \ + (long)(second), (long)(third), \ + (long)(ptr), (long)(fifth)) +#define __sanitizer_syscall_pre_mq_open(name, oflag, mode, attr) \ + __sanitizer_syscall_pre_impl_mq_open((long)(name), (long)(oflag), \ + (long)(mode), (long)(attr)) +#define __sanitizer_syscall_post_mq_open(res, name, oflag, mode, attr) \ + __sanitizer_syscall_post_impl_mq_open(res, (long)(name), (long)(oflag), \ + (long)(mode), (long)(attr)) +#define __sanitizer_syscall_pre_mq_unlink(name) \ + __sanitizer_syscall_pre_impl_mq_unlink((long)(name)) +#define __sanitizer_syscall_post_mq_unlink(res, name) \ + __sanitizer_syscall_post_impl_mq_unlink(res, (long)(name)) +#define __sanitizer_syscall_pre_mq_timedsend(mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_pre_impl_mq_timedsend((long)(mqdes), (long)(msg_ptr), \ + (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_post_mq_timedsend(res, mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_post_impl_mq_timedsend( \ + res, (long)(mqdes), (long)(msg_ptr), (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_pre_mq_timedreceive(mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_pre_impl_mq_timedreceive( \ + (long)(mqdes), (long)(msg_ptr), (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_post_mq_timedreceive(res, mqdes, msg_ptr, msg_len, \ + msg_prio, abs_timeout) \ + __sanitizer_syscall_post_impl_mq_timedreceive( \ + res, (long)(mqdes), (long)(msg_ptr), (long)(msg_len), (long)(msg_prio), \ + (long)(abs_timeout)) +#define __sanitizer_syscall_pre_mq_notify(mqdes, notification) \ + __sanitizer_syscall_pre_impl_mq_notify((long)(mqdes), (long)(notification)) +#define __sanitizer_syscall_post_mq_notify(res, mqdes, notification) \ + __sanitizer_syscall_post_impl_mq_notify(res, (long)(mqdes), \ + (long)(notification)) +#define __sanitizer_syscall_pre_mq_getsetattr(mqdes, mqstat, omqstat) \ + __sanitizer_syscall_pre_impl_mq_getsetattr((long)(mqdes), (long)(mqstat), \ + (long)(omqstat)) +#define __sanitizer_syscall_post_mq_getsetattr(res, mqdes, mqstat, omqstat) \ + __sanitizer_syscall_post_impl_mq_getsetattr(res, (long)(mqdes), \ + (long)(mqstat), (long)(omqstat)) +#define __sanitizer_syscall_pre_pciconfig_iobase(which, bus, devfn) \ + __sanitizer_syscall_pre_impl_pciconfig_iobase((long)(which), (long)(bus), \ + (long)(devfn)) +#define __sanitizer_syscall_post_pciconfig_iobase(res, which, bus, devfn) \ + __sanitizer_syscall_post_impl_pciconfig_iobase(res, (long)(which), \ + (long)(bus), (long)(devfn)) +#define __sanitizer_syscall_pre_pciconfig_read(bus, dfn, off, len, buf) \ + __sanitizer_syscall_pre_impl_pciconfig_read( \ + (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_post_pciconfig_read(res, bus, dfn, off, len, buf) \ + __sanitizer_syscall_post_impl_pciconfig_read( \ + res, (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_pre_pciconfig_write(bus, dfn, off, len, buf) \ + __sanitizer_syscall_pre_impl_pciconfig_write( \ + (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_post_pciconfig_write(res, bus, dfn, off, len, buf) \ + __sanitizer_syscall_post_impl_pciconfig_write( \ + res, (long)(bus), (long)(dfn), (long)(off), (long)(len), (long)(buf)) +#define __sanitizer_syscall_pre_swapon(specialfile, swap_flags) \ + __sanitizer_syscall_pre_impl_swapon((long)(specialfile), (long)(swap_flags)) +#define __sanitizer_syscall_post_swapon(res, specialfile, swap_flags) \ + __sanitizer_syscall_post_impl_swapon(res, (long)(specialfile), \ + (long)(swap_flags)) +#define __sanitizer_syscall_pre_swapoff(specialfile) \ + __sanitizer_syscall_pre_impl_swapoff((long)(specialfile)) +#define __sanitizer_syscall_post_swapoff(res, specialfile) \ + __sanitizer_syscall_post_impl_swapoff(res, (long)(specialfile)) +#define __sanitizer_syscall_pre_sysctl(args) \ + __sanitizer_syscall_pre_impl_sysctl((long)(args)) +#define __sanitizer_syscall_post_sysctl(res, args) \ + __sanitizer_syscall_post_impl_sysctl(res, (long)(args)) +#define __sanitizer_syscall_pre_sysinfo(info) \ + __sanitizer_syscall_pre_impl_sysinfo((long)(info)) +#define __sanitizer_syscall_post_sysinfo(res, info) \ + __sanitizer_syscall_post_impl_sysinfo(res, (long)(info)) +#define __sanitizer_syscall_pre_sysfs(option, arg1, arg2) \ + __sanitizer_syscall_pre_impl_sysfs((long)(option), (long)(arg1), (long)(arg2)) +#define __sanitizer_syscall_post_sysfs(res, option, arg1, arg2) \ + __sanitizer_syscall_post_impl_sysfs(res, (long)(option), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_syslog(type, buf, len) \ + __sanitizer_syscall_pre_impl_syslog((long)(type), (long)(buf), (long)(len)) +#define __sanitizer_syscall_post_syslog(res, type, buf, len) \ + __sanitizer_syscall_post_impl_syslog(res, (long)(type), (long)(buf), \ + (long)(len)) +#define __sanitizer_syscall_pre_uselib(library) \ + __sanitizer_syscall_pre_impl_uselib((long)(library)) +#define __sanitizer_syscall_post_uselib(res, library) \ + __sanitizer_syscall_post_impl_uselib(res, (long)(library)) +#define __sanitizer_syscall_pre_ni_syscall() \ + __sanitizer_syscall_pre_impl_ni_syscall() +#define __sanitizer_syscall_post_ni_syscall(res) \ + __sanitizer_syscall_post_impl_ni_syscall(res) +#define __sanitizer_syscall_pre_ptrace(request, pid, addr, data) \ + __sanitizer_syscall_pre_impl_ptrace((long)(request), (long)(pid), \ + (long)(addr), (long)(data)) +#define __sanitizer_syscall_post_ptrace(res, request, pid, addr, data) \ + __sanitizer_syscall_post_impl_ptrace(res, (long)(request), (long)(pid), \ + (long)(addr), (long)(data)) +#define __sanitizer_syscall_pre_add_key(_type, _description, _payload, plen, \ + destringid) \ + __sanitizer_syscall_pre_impl_add_key((long)(_type), (long)(_description), \ + (long)(_payload), (long)(plen), \ + (long)(destringid)) +#define __sanitizer_syscall_post_add_key(res, _type, _description, _payload, \ + plen, destringid) \ + __sanitizer_syscall_post_impl_add_key( \ + res, (long)(_type), (long)(_description), (long)(_payload), \ + (long)(plen), (long)(destringid)) +#define __sanitizer_syscall_pre_request_key(_type, _description, \ + _callout_info, destringid) \ + __sanitizer_syscall_pre_impl_request_key( \ + (long)(_type), (long)(_description), (long)(_callout_info), \ + (long)(destringid)) +#define __sanitizer_syscall_post_request_key(res, _type, _description, \ + _callout_info, destringid) \ + __sanitizer_syscall_post_impl_request_key( \ + res, (long)(_type), (long)(_description), (long)(_callout_info), \ + (long)(destringid)) +#define __sanitizer_syscall_pre_keyctl(cmd, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_keyctl((long)(cmd), (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_keyctl(res, cmd, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_post_impl_keyctl(res, (long)(cmd), (long)(arg2), \ + (long)(arg3), (long)(arg4), \ + (long)(arg5)) +#define __sanitizer_syscall_pre_ioprio_set(which, who, ioprio) \ + __sanitizer_syscall_pre_impl_ioprio_set((long)(which), (long)(who), \ + (long)(ioprio)) +#define __sanitizer_syscall_post_ioprio_set(res, which, who, ioprio) \ + __sanitizer_syscall_post_impl_ioprio_set(res, (long)(which), (long)(who), \ + (long)(ioprio)) +#define __sanitizer_syscall_pre_ioprio_get(which, who) \ + __sanitizer_syscall_pre_impl_ioprio_get((long)(which), (long)(who)) +#define __sanitizer_syscall_post_ioprio_get(res, which, who) \ + __sanitizer_syscall_post_impl_ioprio_get(res, (long)(which), (long)(who)) +#define __sanitizer_syscall_pre_set_mempolicy(mode, nmask, maxnode) \ + __sanitizer_syscall_pre_impl_set_mempolicy((long)(mode), (long)(nmask), \ + (long)(maxnode)) +#define __sanitizer_syscall_post_set_mempolicy(res, mode, nmask, maxnode) \ + __sanitizer_syscall_post_impl_set_mempolicy(res, (long)(mode), \ + (long)(nmask), (long)(maxnode)) +#define __sanitizer_syscall_pre_migrate_pages(pid, maxnode, from, to) \ + __sanitizer_syscall_pre_impl_migrate_pages((long)(pid), (long)(maxnode), \ + (long)(from), (long)(to)) +#define __sanitizer_syscall_post_migrate_pages(res, pid, maxnode, from, to) \ + __sanitizer_syscall_post_impl_migrate_pages( \ + res, (long)(pid), (long)(maxnode), (long)(from), (long)(to)) +#define __sanitizer_syscall_pre_move_pages(pid, nr_pages, pages, nodes, \ + status, flags) \ + __sanitizer_syscall_pre_impl_move_pages((long)(pid), (long)(nr_pages), \ + (long)(pages), (long)(nodes), \ + (long)(status), (long)(flags)) +#define __sanitizer_syscall_post_move_pages(res, pid, nr_pages, pages, nodes, \ + status, flags) \ + __sanitizer_syscall_post_impl_move_pages(res, (long)(pid), (long)(nr_pages), \ + (long)(pages), (long)(nodes), \ + (long)(status), (long)(flags)) +#define __sanitizer_syscall_pre_mbind(start, len, mode, nmask, maxnode, flags) \ + __sanitizer_syscall_pre_impl_mbind((long)(start), (long)(len), (long)(mode), \ + (long)(nmask), (long)(maxnode), \ + (long)(flags)) +#define __sanitizer_syscall_post_mbind(res, start, len, mode, nmask, maxnode, \ + flags) \ + __sanitizer_syscall_post_impl_mbind(res, (long)(start), (long)(len), \ + (long)(mode), (long)(nmask), \ + (long)(maxnode), (long)(flags)) +#define __sanitizer_syscall_pre_get_mempolicy(policy, nmask, maxnode, addr, \ + flags) \ + __sanitizer_syscall_pre_impl_get_mempolicy((long)(policy), (long)(nmask), \ + (long)(maxnode), (long)(addr), \ + (long)(flags)) +#define __sanitizer_syscall_post_get_mempolicy(res, policy, nmask, maxnode, \ + addr, flags) \ + __sanitizer_syscall_post_impl_get_mempolicy(res, (long)(policy), \ + (long)(nmask), (long)(maxnode), \ + (long)(addr), (long)(flags)) +#define __sanitizer_syscall_pre_inotify_init() \ + __sanitizer_syscall_pre_impl_inotify_init() +#define __sanitizer_syscall_post_inotify_init(res) \ + __sanitizer_syscall_post_impl_inotify_init(res) +#define __sanitizer_syscall_pre_inotify_init1(flags) \ + __sanitizer_syscall_pre_impl_inotify_init1((long)(flags)) +#define __sanitizer_syscall_post_inotify_init1(res, flags) \ + __sanitizer_syscall_post_impl_inotify_init1(res, (long)(flags)) +#define __sanitizer_syscall_pre_inotify_add_watch(fd, path, mask) \ + __sanitizer_syscall_pre_impl_inotify_add_watch((long)(fd), (long)(path), \ + (long)(mask)) +#define __sanitizer_syscall_post_inotify_add_watch(res, fd, path, mask) \ + __sanitizer_syscall_post_impl_inotify_add_watch(res, (long)(fd), \ + (long)(path), (long)(mask)) +#define __sanitizer_syscall_pre_inotify_rm_watch(fd, wd) \ + __sanitizer_syscall_pre_impl_inotify_rm_watch((long)(fd), (long)(wd)) +#define __sanitizer_syscall_post_inotify_rm_watch(res, fd, wd) \ + __sanitizer_syscall_post_impl_inotify_rm_watch(res, (long)(fd), (long)(wd)) +#define __sanitizer_syscall_pre_spu_run(fd, unpc, ustatus) \ + __sanitizer_syscall_pre_impl_spu_run((long)(fd), (long)(unpc), \ + (long)(ustatus)) +#define __sanitizer_syscall_post_spu_run(res, fd, unpc, ustatus) \ + __sanitizer_syscall_post_impl_spu_run(res, (long)(fd), (long)(unpc), \ + (long)(ustatus)) +#define __sanitizer_syscall_pre_spu_create(name, flags, mode, fd) \ + __sanitizer_syscall_pre_impl_spu_create((long)(name), (long)(flags), \ + (long)(mode), (long)(fd)) +#define __sanitizer_syscall_post_spu_create(res, name, flags, mode, fd) \ + __sanitizer_syscall_post_impl_spu_create(res, (long)(name), (long)(flags), \ + (long)(mode), (long)(fd)) +#define __sanitizer_syscall_pre_mknodat(dfd, filename, mode, dev) \ + __sanitizer_syscall_pre_impl_mknodat((long)(dfd), (long)(filename), \ + (long)(mode), (long)(dev)) +#define __sanitizer_syscall_post_mknodat(res, dfd, filename, mode, dev) \ + __sanitizer_syscall_post_impl_mknodat(res, (long)(dfd), (long)(filename), \ + (long)(mode), (long)(dev)) +#define __sanitizer_syscall_pre_mkdirat(dfd, pathname, mode) \ + __sanitizer_syscall_pre_impl_mkdirat((long)(dfd), (long)(pathname), \ + (long)(mode)) +#define __sanitizer_syscall_post_mkdirat(res, dfd, pathname, mode) \ + __sanitizer_syscall_post_impl_mkdirat(res, (long)(dfd), (long)(pathname), \ + (long)(mode)) +#define __sanitizer_syscall_pre_unlinkat(dfd, pathname, flag) \ + __sanitizer_syscall_pre_impl_unlinkat((long)(dfd), (long)(pathname), \ + (long)(flag)) +#define __sanitizer_syscall_post_unlinkat(res, dfd, pathname, flag) \ + __sanitizer_syscall_post_impl_unlinkat(res, (long)(dfd), (long)(pathname), \ + (long)(flag)) +#define __sanitizer_syscall_pre_symlinkat(oldname, newdfd, newname) \ + __sanitizer_syscall_pre_impl_symlinkat((long)(oldname), (long)(newdfd), \ + (long)(newname)) +#define __sanitizer_syscall_post_symlinkat(res, oldname, newdfd, newname) \ + __sanitizer_syscall_post_impl_symlinkat(res, (long)(oldname), \ + (long)(newdfd), (long)(newname)) +#define __sanitizer_syscall_pre_linkat(olddfd, oldname, newdfd, newname, \ + flags) \ + __sanitizer_syscall_pre_impl_linkat((long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname), \ + (long)(flags)) +#define __sanitizer_syscall_post_linkat(res, olddfd, oldname, newdfd, newname, \ + flags) \ + __sanitizer_syscall_post_impl_linkat(res, (long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname), \ + (long)(flags)) +#define __sanitizer_syscall_pre_renameat(olddfd, oldname, newdfd, newname) \ + __sanitizer_syscall_pre_impl_renameat((long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname)) +#define __sanitizer_syscall_post_renameat(res, olddfd, oldname, newdfd, \ + newname) \ + __sanitizer_syscall_post_impl_renameat(res, (long)(olddfd), (long)(oldname), \ + (long)(newdfd), (long)(newname)) +#define __sanitizer_syscall_pre_futimesat(dfd, filename, utimes) \ + __sanitizer_syscall_pre_impl_futimesat((long)(dfd), (long)(filename), \ + (long)(utimes)) +#define __sanitizer_syscall_post_futimesat(res, dfd, filename, utimes) \ + __sanitizer_syscall_post_impl_futimesat(res, (long)(dfd), (long)(filename), \ + (long)(utimes)) +#define __sanitizer_syscall_pre_faccessat(dfd, filename, mode) \ + __sanitizer_syscall_pre_impl_faccessat((long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_post_faccessat(res, dfd, filename, mode) \ + __sanitizer_syscall_post_impl_faccessat(res, (long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_pre_fchmodat(dfd, filename, mode) \ + __sanitizer_syscall_pre_impl_fchmodat((long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_post_fchmodat(res, dfd, filename, mode) \ + __sanitizer_syscall_post_impl_fchmodat(res, (long)(dfd), (long)(filename), \ + (long)(mode)) +#define __sanitizer_syscall_pre_fchownat(dfd, filename, user, group, flag) \ + __sanitizer_syscall_pre_impl_fchownat((long)(dfd), (long)(filename), \ + (long)(user), (long)(group), \ + (long)(flag)) +#define __sanitizer_syscall_post_fchownat(res, dfd, filename, user, group, \ + flag) \ + __sanitizer_syscall_post_impl_fchownat(res, (long)(dfd), (long)(filename), \ + (long)(user), (long)(group), \ + (long)(flag)) +#define __sanitizer_syscall_pre_openat(dfd, filename, flags, mode) \ + __sanitizer_syscall_pre_impl_openat((long)(dfd), (long)(filename), \ + (long)(flags), (long)(mode)) +#define __sanitizer_syscall_post_openat(res, dfd, filename, flags, mode) \ + __sanitizer_syscall_post_impl_openat(res, (long)(dfd), (long)(filename), \ + (long)(flags), (long)(mode)) +#define __sanitizer_syscall_pre_newfstatat(dfd, filename, statbuf, flag) \ + __sanitizer_syscall_pre_impl_newfstatat((long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_post_newfstatat(res, dfd, filename, statbuf, flag) \ + __sanitizer_syscall_post_impl_newfstatat(res, (long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_pre_fstatat64(dfd, filename, statbuf, flag) \ + __sanitizer_syscall_pre_impl_fstatat64((long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_post_fstatat64(res, dfd, filename, statbuf, flag) \ + __sanitizer_syscall_post_impl_fstatat64(res, (long)(dfd), (long)(filename), \ + (long)(statbuf), (long)(flag)) +#define __sanitizer_syscall_pre_readlinkat(dfd, path, buf, bufsiz) \ + __sanitizer_syscall_pre_impl_readlinkat((long)(dfd), (long)(path), \ + (long)(buf), (long)(bufsiz)) +#define __sanitizer_syscall_post_readlinkat(res, dfd, path, buf, bufsiz) \ + __sanitizer_syscall_post_impl_readlinkat(res, (long)(dfd), (long)(path), \ + (long)(buf), (long)(bufsiz)) +#define __sanitizer_syscall_pre_utimensat(dfd, filename, utimes, flags) \ + __sanitizer_syscall_pre_impl_utimensat((long)(dfd), (long)(filename), \ + (long)(utimes), (long)(flags)) +#define __sanitizer_syscall_post_utimensat(res, dfd, filename, utimes, flags) \ + __sanitizer_syscall_post_impl_utimensat(res, (long)(dfd), (long)(filename), \ + (long)(utimes), (long)(flags)) +#define __sanitizer_syscall_pre_unshare(unshare_flags) \ + __sanitizer_syscall_pre_impl_unshare((long)(unshare_flags)) +#define __sanitizer_syscall_post_unshare(res, unshare_flags) \ + __sanitizer_syscall_post_impl_unshare(res, (long)(unshare_flags)) +#define __sanitizer_syscall_pre_splice(fd_in, off_in, fd_out, off_out, len, \ + flags) \ + __sanitizer_syscall_pre_impl_splice((long)(fd_in), (long)(off_in), \ + (long)(fd_out), (long)(off_out), \ + (long)(len), (long)(flags)) +#define __sanitizer_syscall_post_splice(res, fd_in, off_in, fd_out, off_out, \ + len, flags) \ + __sanitizer_syscall_post_impl_splice(res, (long)(fd_in), (long)(off_in), \ + (long)(fd_out), (long)(off_out), \ + (long)(len), (long)(flags)) +#define __sanitizer_syscall_pre_vmsplice(fd, iov, nr_segs, flags) \ + __sanitizer_syscall_pre_impl_vmsplice((long)(fd), (long)(iov), \ + (long)(nr_segs), (long)(flags)) +#define __sanitizer_syscall_post_vmsplice(res, fd, iov, nr_segs, flags) \ + __sanitizer_syscall_post_impl_vmsplice(res, (long)(fd), (long)(iov), \ + (long)(nr_segs), (long)(flags)) +#define __sanitizer_syscall_pre_tee(fdin, fdout, len, flags) \ + __sanitizer_syscall_pre_impl_tee((long)(fdin), (long)(fdout), (long)(len), \ + (long)(flags)) +#define __sanitizer_syscall_post_tee(res, fdin, fdout, len, flags) \ + __sanitizer_syscall_post_impl_tee(res, (long)(fdin), (long)(fdout), \ + (long)(len), (long)(flags)) +#define __sanitizer_syscall_pre_get_robust_list(pid, head_ptr, len_ptr) \ + __sanitizer_syscall_pre_impl_get_robust_list((long)(pid), (long)(head_ptr), \ + (long)(len_ptr)) +#define __sanitizer_syscall_post_get_robust_list(res, pid, head_ptr, len_ptr) \ + __sanitizer_syscall_post_impl_get_robust_list( \ + res, (long)(pid), (long)(head_ptr), (long)(len_ptr)) +#define __sanitizer_syscall_pre_set_robust_list(head, len) \ + __sanitizer_syscall_pre_impl_set_robust_list((long)(head), (long)(len)) +#define __sanitizer_syscall_post_set_robust_list(res, head, len) \ + __sanitizer_syscall_post_impl_set_robust_list(res, (long)(head), (long)(len)) +#define __sanitizer_syscall_pre_getcpu(cpu, node, cache) \ + __sanitizer_syscall_pre_impl_getcpu((long)(cpu), (long)(node), (long)(cache)) +#define __sanitizer_syscall_post_getcpu(res, cpu, node, cache) \ + __sanitizer_syscall_post_impl_getcpu(res, (long)(cpu), (long)(node), \ + (long)(cache)) +#define __sanitizer_syscall_pre_signalfd(ufd, user_mask, sizemask) \ + __sanitizer_syscall_pre_impl_signalfd((long)(ufd), (long)(user_mask), \ + (long)(sizemask)) +#define __sanitizer_syscall_post_signalfd(res, ufd, user_mask, sizemask) \ + __sanitizer_syscall_post_impl_signalfd(res, (long)(ufd), (long)(user_mask), \ + (long)(sizemask)) +#define __sanitizer_syscall_pre_signalfd4(ufd, user_mask, sizemask, flags) \ + __sanitizer_syscall_pre_impl_signalfd4((long)(ufd), (long)(user_mask), \ + (long)(sizemask), (long)(flags)) +#define __sanitizer_syscall_post_signalfd4(res, ufd, user_mask, sizemask, \ + flags) \ + __sanitizer_syscall_post_impl_signalfd4(res, (long)(ufd), (long)(user_mask), \ + (long)(sizemask), (long)(flags)) +#define __sanitizer_syscall_pre_timerfd_create(clockid, flags) \ + __sanitizer_syscall_pre_impl_timerfd_create((long)(clockid), (long)(flags)) +#define __sanitizer_syscall_post_timerfd_create(res, clockid, flags) \ + __sanitizer_syscall_post_impl_timerfd_create(res, (long)(clockid), \ + (long)(flags)) +#define __sanitizer_syscall_pre_timerfd_settime(ufd, flags, utmr, otmr) \ + __sanitizer_syscall_pre_impl_timerfd_settime((long)(ufd), (long)(flags), \ + (long)(utmr), (long)(otmr)) +#define __sanitizer_syscall_post_timerfd_settime(res, ufd, flags, utmr, otmr) \ + __sanitizer_syscall_post_impl_timerfd_settime( \ + res, (long)(ufd), (long)(flags), (long)(utmr), (long)(otmr)) +#define __sanitizer_syscall_pre_timerfd_gettime(ufd, otmr) \ + __sanitizer_syscall_pre_impl_timerfd_gettime((long)(ufd), (long)(otmr)) +#define __sanitizer_syscall_post_timerfd_gettime(res, ufd, otmr) \ + __sanitizer_syscall_post_impl_timerfd_gettime(res, (long)(ufd), (long)(otmr)) +#define __sanitizer_syscall_pre_eventfd(count) \ + __sanitizer_syscall_pre_impl_eventfd((long)(count)) +#define __sanitizer_syscall_post_eventfd(res, count) \ + __sanitizer_syscall_post_impl_eventfd(res, (long)(count)) +#define __sanitizer_syscall_pre_eventfd2(count, flags) \ + __sanitizer_syscall_pre_impl_eventfd2((long)(count), (long)(flags)) +#define __sanitizer_syscall_post_eventfd2(res, count, flags) \ + __sanitizer_syscall_post_impl_eventfd2(res, (long)(count), (long)(flags)) +#define __sanitizer_syscall_pre_old_readdir(arg0, arg1, arg2) \ + __sanitizer_syscall_pre_impl_old_readdir((long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_post_old_readdir(res, arg0, arg1, arg2) \ + __sanitizer_syscall_post_impl_old_readdir(res, (long)(arg0), (long)(arg1), \ + (long)(arg2)) +#define __sanitizer_syscall_pre_pselect6(arg0, arg1, arg2, arg3, arg4, arg5) \ + __sanitizer_syscall_pre_impl_pselect6((long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_post_pselect6(res, arg0, arg1, arg2, arg3, arg4, \ + arg5) \ + __sanitizer_syscall_post_impl_pselect6(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4), (long)(arg5)) +#define __sanitizer_syscall_pre_ppoll(arg0, arg1, arg2, arg3, arg4) \ + __sanitizer_syscall_pre_impl_ppoll((long)(arg0), (long)(arg1), (long)(arg2), \ + (long)(arg3), (long)(arg4)) +#define __sanitizer_syscall_post_ppoll(res, arg0, arg1, arg2, arg3, arg4) \ + __sanitizer_syscall_post_impl_ppoll(res, (long)(arg0), (long)(arg1), \ + (long)(arg2), (long)(arg3), \ + (long)(arg4)) +#define __sanitizer_syscall_pre_syncfs(fd) \ + __sanitizer_syscall_pre_impl_syncfs((long)(fd)) +#define __sanitizer_syscall_post_syncfs(res, fd) \ + __sanitizer_syscall_post_impl_syncfs(res, (long)(fd)) +#define __sanitizer_syscall_pre_perf_event_open(attr_uptr, pid, cpu, group_fd, \ + flags) \ + __sanitizer_syscall_pre_impl_perf_event_open((long)(attr_uptr), (long)(pid), \ + (long)(cpu), (long)(group_fd), \ + (long)(flags)) +#define __sanitizer_syscall_post_perf_event_open(res, attr_uptr, pid, cpu, \ + group_fd, flags) \ + __sanitizer_syscall_post_impl_perf_event_open( \ + res, (long)(attr_uptr), (long)(pid), (long)(cpu), (long)(group_fd), \ + (long)(flags)) +#define __sanitizer_syscall_pre_mmap_pgoff(addr, len, prot, flags, fd, pgoff) \ + __sanitizer_syscall_pre_impl_mmap_pgoff((long)(addr), (long)(len), \ + (long)(prot), (long)(flags), \ + (long)(fd), (long)(pgoff)) +#define __sanitizer_syscall_post_mmap_pgoff(res, addr, len, prot, flags, fd, \ + pgoff) \ + __sanitizer_syscall_post_impl_mmap_pgoff(res, (long)(addr), (long)(len), \ + (long)(prot), (long)(flags), \ + (long)(fd), (long)(pgoff)) +#define __sanitizer_syscall_pre_old_mmap(arg) \ + __sanitizer_syscall_pre_impl_old_mmap((long)(arg)) +#define __sanitizer_syscall_post_old_mmap(res, arg) \ + __sanitizer_syscall_post_impl_old_mmap(res, (long)(arg)) +#define __sanitizer_syscall_pre_name_to_handle_at(dfd, name, handle, mnt_id, \ + flag) \ + __sanitizer_syscall_pre_impl_name_to_handle_at( \ + (long)(dfd), (long)(name), (long)(handle), (long)(mnt_id), (long)(flag)) +#define __sanitizer_syscall_post_name_to_handle_at(res, dfd, name, handle, \ + mnt_id, flag) \ + __sanitizer_syscall_post_impl_name_to_handle_at( \ + res, (long)(dfd), (long)(name), (long)(handle), (long)(mnt_id), \ + (long)(flag)) +#define __sanitizer_syscall_pre_open_by_handle_at(mountdirfd, handle, flags) \ + __sanitizer_syscall_pre_impl_open_by_handle_at( \ + (long)(mountdirfd), (long)(handle), (long)(flags)) +#define __sanitizer_syscall_post_open_by_handle_at(res, mountdirfd, handle, \ + flags) \ + __sanitizer_syscall_post_impl_open_by_handle_at( \ + res, (long)(mountdirfd), (long)(handle), (long)(flags)) +#define __sanitizer_syscall_pre_setns(fd, nstype) \ + __sanitizer_syscall_pre_impl_setns((long)(fd), (long)(nstype)) +#define __sanitizer_syscall_post_setns(res, fd, nstype) \ + __sanitizer_syscall_post_impl_setns(res, (long)(fd), (long)(nstype)) +#define __sanitizer_syscall_pre_process_vm_readv(pid, lvec, liovcnt, rvec, \ + riovcnt, flags) \ + __sanitizer_syscall_pre_impl_process_vm_readv( \ + (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_post_process_vm_readv(res, pid, lvec, liovcnt, \ + rvec, riovcnt, flags) \ + __sanitizer_syscall_post_impl_process_vm_readv( \ + res, (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_pre_process_vm_writev(pid, lvec, liovcnt, rvec, \ + riovcnt, flags) \ + __sanitizer_syscall_pre_impl_process_vm_writev( \ + (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_post_process_vm_writev(res, pid, lvec, liovcnt, \ + rvec, riovcnt, flags) \ + __sanitizer_syscall_post_impl_process_vm_writev( \ + res, (long)(pid), (long)(lvec), (long)(liovcnt), (long)(rvec), \ + (long)(riovcnt), (long)(flags)) +#define __sanitizer_syscall_pre_fork() \ + __sanitizer_syscall_pre_impl_fork() +#define __sanitizer_syscall_post_fork(res) \ + __sanitizer_syscall_post_impl_fork(res) +#define __sanitizer_syscall_pre_vfork() \ + __sanitizer_syscall_pre_impl_vfork() +#define __sanitizer_syscall_post_vfork(res) \ + __sanitizer_syscall_post_impl_vfork(res) + +// And now a few syscalls we don't handle yet. +#define __sanitizer_syscall_pre_afs_syscall(...) +#define __sanitizer_syscall_pre_arch_prctl(...) +#define __sanitizer_syscall_pre_break(...) +#define __sanitizer_syscall_pre_chown32(...) +#define __sanitizer_syscall_pre_clone(...) +#define __sanitizer_syscall_pre_create_module(...) +#define __sanitizer_syscall_pre_epoll_ctl_old(...) +#define __sanitizer_syscall_pre_epoll_wait_old(...) +#define __sanitizer_syscall_pre_execve(...) +#define __sanitizer_syscall_pre_fadvise64(...) +#define __sanitizer_syscall_pre_fadvise64_64(...) +#define __sanitizer_syscall_pre_fallocate(...) +#define __sanitizer_syscall_pre_fanotify_init(...) +#define __sanitizer_syscall_pre_fanotify_mark(...) +#define __sanitizer_syscall_pre_fchown32(...) +#define __sanitizer_syscall_pre_ftime(...) +#define __sanitizer_syscall_pre_ftruncate64(...) +#define __sanitizer_syscall_pre_futex(...) +#define __sanitizer_syscall_pre_getegid32(...) +#define __sanitizer_syscall_pre_geteuid32(...) +#define __sanitizer_syscall_pre_getgid32(...) +#define __sanitizer_syscall_pre_getgroups32(...) +#define __sanitizer_syscall_pre_get_kernel_syms(...) +#define __sanitizer_syscall_pre_getpmsg(...) +#define __sanitizer_syscall_pre_getresgid32(...) +#define __sanitizer_syscall_pre_getresuid32(...) +#define __sanitizer_syscall_pre_get_thread_area(...) +#define __sanitizer_syscall_pre_getuid32(...) +#define __sanitizer_syscall_pre_gtty(...) +#define __sanitizer_syscall_pre_idle(...) +#define __sanitizer_syscall_pre_iopl(...) +#define __sanitizer_syscall_pre_lchown32(...) +#define __sanitizer_syscall_pre__llseek(...) +#define __sanitizer_syscall_pre_lock(...) +#define __sanitizer_syscall_pre_madvise1(...) +#define __sanitizer_syscall_pre_mmap(...) +#define __sanitizer_syscall_pre_mmap2(...) +#define __sanitizer_syscall_pre_modify_ldt(...) +#define __sanitizer_syscall_pre_mpx(...) +#define __sanitizer_syscall_pre__newselect(...) +#define __sanitizer_syscall_pre_nfsservctl(...) +#define __sanitizer_syscall_pre_oldfstat(...) +#define __sanitizer_syscall_pre_oldlstat(...) +#define __sanitizer_syscall_pre_oldolduname(...) +#define __sanitizer_syscall_pre_oldstat(...) +#define __sanitizer_syscall_pre_prctl(...) +#define __sanitizer_syscall_pre_prof(...) +#define __sanitizer_syscall_pre_profil(...) +#define __sanitizer_syscall_pre_putpmsg(...) +#define __sanitizer_syscall_pre_query_module(...) +#define __sanitizer_syscall_pre_readahead(...) +#define __sanitizer_syscall_pre_readdir(...) +#define __sanitizer_syscall_pre_rt_sigaction(...) +#define __sanitizer_syscall_pre_rt_sigreturn(...) +#define __sanitizer_syscall_pre_rt_sigsuspend(...) +#define __sanitizer_syscall_pre_security(...) +#define __sanitizer_syscall_pre_setfsgid32(...) +#define __sanitizer_syscall_pre_setfsuid32(...) +#define __sanitizer_syscall_pre_setgid32(...) +#define __sanitizer_syscall_pre_setgroups32(...) +#define __sanitizer_syscall_pre_setregid32(...) +#define __sanitizer_syscall_pre_setresgid32(...) +#define __sanitizer_syscall_pre_setresuid32(...) +#define __sanitizer_syscall_pre_setreuid32(...) +#define __sanitizer_syscall_pre_set_thread_area(...) +#define __sanitizer_syscall_pre_setuid32(...) +#define __sanitizer_syscall_pre_sigaction(...) +#define __sanitizer_syscall_pre_sigaltstack(...) +#define __sanitizer_syscall_pre_sigreturn(...) +#define __sanitizer_syscall_pre_sigsuspend(...) +#define __sanitizer_syscall_pre_stty(...) +#define __sanitizer_syscall_pre_sync_file_range(...) +#define __sanitizer_syscall_pre__sysctl(...) +#define __sanitizer_syscall_pre_truncate64(...) +#define __sanitizer_syscall_pre_tuxcall(...) +#define __sanitizer_syscall_pre_ugetrlimit(...) +#define __sanitizer_syscall_pre_ulimit(...) +#define __sanitizer_syscall_pre_umount2(...) +#define __sanitizer_syscall_pre_vm86(...) +#define __sanitizer_syscall_pre_vm86old(...) +#define __sanitizer_syscall_pre_vserver(...) + +#define __sanitizer_syscall_post_afs_syscall(res, ...) +#define __sanitizer_syscall_post_arch_prctl(res, ...) +#define __sanitizer_syscall_post_break(res, ...) +#define __sanitizer_syscall_post_chown32(res, ...) +#define __sanitizer_syscall_post_clone(res, ...) +#define __sanitizer_syscall_post_create_module(res, ...) +#define __sanitizer_syscall_post_epoll_ctl_old(res, ...) +#define __sanitizer_syscall_post_epoll_wait_old(res, ...) +#define __sanitizer_syscall_post_execve(res, ...) +#define __sanitizer_syscall_post_fadvise64(res, ...) +#define __sanitizer_syscall_post_fadvise64_64(res, ...) +#define __sanitizer_syscall_post_fallocate(res, ...) +#define __sanitizer_syscall_post_fanotify_init(res, ...) +#define __sanitizer_syscall_post_fanotify_mark(res, ...) +#define __sanitizer_syscall_post_fchown32(res, ...) +#define __sanitizer_syscall_post_ftime(res, ...) +#define __sanitizer_syscall_post_ftruncate64(res, ...) +#define __sanitizer_syscall_post_futex(res, ...) +#define __sanitizer_syscall_post_getegid32(res, ...) +#define __sanitizer_syscall_post_geteuid32(res, ...) +#define __sanitizer_syscall_post_getgid32(res, ...) +#define __sanitizer_syscall_post_getgroups32(res, ...) +#define __sanitizer_syscall_post_get_kernel_syms(res, ...) +#define __sanitizer_syscall_post_getpmsg(res, ...) +#define __sanitizer_syscall_post_getresgid32(res, ...) +#define __sanitizer_syscall_post_getresuid32(res, ...) +#define __sanitizer_syscall_post_get_thread_area(res, ...) +#define __sanitizer_syscall_post_getuid32(res, ...) +#define __sanitizer_syscall_post_gtty(res, ...) +#define __sanitizer_syscall_post_idle(res, ...) +#define __sanitizer_syscall_post_iopl(res, ...) +#define __sanitizer_syscall_post_lchown32(res, ...) +#define __sanitizer_syscall_post__llseek(res, ...) +#define __sanitizer_syscall_post_lock(res, ...) +#define __sanitizer_syscall_post_madvise1(res, ...) +#define __sanitizer_syscall_post_mmap2(res, ...) +#define __sanitizer_syscall_post_mmap(res, ...) +#define __sanitizer_syscall_post_modify_ldt(res, ...) +#define __sanitizer_syscall_post_mpx(res, ...) +#define __sanitizer_syscall_post__newselect(res, ...) +#define __sanitizer_syscall_post_nfsservctl(res, ...) +#define __sanitizer_syscall_post_oldfstat(res, ...) +#define __sanitizer_syscall_post_oldlstat(res, ...) +#define __sanitizer_syscall_post_oldolduname(res, ...) +#define __sanitizer_syscall_post_oldstat(res, ...) +#define __sanitizer_syscall_post_prctl(res, ...) +#define __sanitizer_syscall_post_profil(res, ...) +#define __sanitizer_syscall_post_prof(res, ...) +#define __sanitizer_syscall_post_putpmsg(res, ...) +#define __sanitizer_syscall_post_query_module(res, ...) +#define __sanitizer_syscall_post_readahead(res, ...) +#define __sanitizer_syscall_post_readdir(res, ...) +#define __sanitizer_syscall_post_rt_sigaction(res, ...) +#define __sanitizer_syscall_post_rt_sigreturn(res, ...) +#define __sanitizer_syscall_post_rt_sigsuspend(res, ...) +#define __sanitizer_syscall_post_security(res, ...) +#define __sanitizer_syscall_post_setfsgid32(res, ...) +#define __sanitizer_syscall_post_setfsuid32(res, ...) +#define __sanitizer_syscall_post_setgid32(res, ...) +#define __sanitizer_syscall_post_setgroups32(res, ...) +#define __sanitizer_syscall_post_setregid32(res, ...) +#define __sanitizer_syscall_post_setresgid32(res, ...) +#define __sanitizer_syscall_post_setresuid32(res, ...) +#define __sanitizer_syscall_post_setreuid32(res, ...) +#define __sanitizer_syscall_post_set_thread_area(res, ...) +#define __sanitizer_syscall_post_setuid32(res, ...) +#define __sanitizer_syscall_post_sigaction(res, ...) +#define __sanitizer_syscall_post_sigaltstack(res, ...) +#define __sanitizer_syscall_post_sigreturn(res, ...) +#define __sanitizer_syscall_post_sigsuspend(res, ...) +#define __sanitizer_syscall_post_stty(res, ...) +#define __sanitizer_syscall_post_sync_file_range(res, ...) +#define __sanitizer_syscall_post__sysctl(res, ...) +#define __sanitizer_syscall_post_truncate64(res, ...) +#define __sanitizer_syscall_post_tuxcall(res, ...) +#define __sanitizer_syscall_post_ugetrlimit(res, ...) +#define __sanitizer_syscall_post_ulimit(res, ...) +#define __sanitizer_syscall_post_umount2(res, ...) +#define __sanitizer_syscall_post_vm86old(res, ...) +#define __sanitizer_syscall_post_vm86(res, ...) +#define __sanitizer_syscall_post_vserver(res, ...) + +#ifdef __cplusplus +extern "C" { +#endif + +// Private declarations. Do not call directly from user code. Use macros above. +void __sanitizer_syscall_pre_impl_time(long tloc); +void __sanitizer_syscall_post_impl_time(long res, long tloc); +void __sanitizer_syscall_pre_impl_stime(long tptr); +void __sanitizer_syscall_post_impl_stime(long res, long tptr); +void __sanitizer_syscall_pre_impl_gettimeofday(long tv, long tz); +void __sanitizer_syscall_post_impl_gettimeofday(long res, long tv, long tz); +void __sanitizer_syscall_pre_impl_settimeofday(long tv, long tz); +void __sanitizer_syscall_post_impl_settimeofday(long res, long tv, long tz); +void __sanitizer_syscall_pre_impl_adjtimex(long txc_p); +void __sanitizer_syscall_post_impl_adjtimex(long res, long txc_p); +void __sanitizer_syscall_pre_impl_times(long tbuf); +void __sanitizer_syscall_post_impl_times(long res, long tbuf); +void __sanitizer_syscall_pre_impl_gettid(); +void __sanitizer_syscall_post_impl_gettid(long res); +void __sanitizer_syscall_pre_impl_nanosleep(long rqtp, long rmtp); +void __sanitizer_syscall_post_impl_nanosleep(long res, long rqtp, long rmtp); +void __sanitizer_syscall_pre_impl_alarm(long seconds); +void __sanitizer_syscall_post_impl_alarm(long res, long seconds); +void __sanitizer_syscall_pre_impl_getpid(); +void __sanitizer_syscall_post_impl_getpid(long res); +void __sanitizer_syscall_pre_impl_getppid(); +void __sanitizer_syscall_post_impl_getppid(long res); +void __sanitizer_syscall_pre_impl_getuid(); +void __sanitizer_syscall_post_impl_getuid(long res); +void __sanitizer_syscall_pre_impl_geteuid(); +void __sanitizer_syscall_post_impl_geteuid(long res); +void __sanitizer_syscall_pre_impl_getgid(); +void __sanitizer_syscall_post_impl_getgid(long res); +void __sanitizer_syscall_pre_impl_getegid(); +void __sanitizer_syscall_post_impl_getegid(long res); +void __sanitizer_syscall_pre_impl_getresuid(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_getresuid(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_getresgid(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_getresgid(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_getpgid(long pid); +void __sanitizer_syscall_post_impl_getpgid(long res, long pid); +void __sanitizer_syscall_pre_impl_getpgrp(); +void __sanitizer_syscall_post_impl_getpgrp(long res); +void __sanitizer_syscall_pre_impl_getsid(long pid); +void __sanitizer_syscall_post_impl_getsid(long res, long pid); +void __sanitizer_syscall_pre_impl_getgroups(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_getgroups(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_setregid(long rgid, long egid); +void __sanitizer_syscall_post_impl_setregid(long res, long rgid, long egid); +void __sanitizer_syscall_pre_impl_setgid(long gid); +void __sanitizer_syscall_post_impl_setgid(long res, long gid); +void __sanitizer_syscall_pre_impl_setreuid(long ruid, long euid); +void __sanitizer_syscall_post_impl_setreuid(long res, long ruid, long euid); +void __sanitizer_syscall_pre_impl_setuid(long uid); +void __sanitizer_syscall_post_impl_setuid(long res, long uid); +void __sanitizer_syscall_pre_impl_setresuid(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_setresuid(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_setresgid(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_setresgid(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_setfsuid(long uid); +void __sanitizer_syscall_post_impl_setfsuid(long res, long uid); +void __sanitizer_syscall_pre_impl_setfsgid(long gid); +void __sanitizer_syscall_post_impl_setfsgid(long res, long gid); +void __sanitizer_syscall_pre_impl_setpgid(long pid, long pgid); +void __sanitizer_syscall_post_impl_setpgid(long res, long pid, long pgid); +void __sanitizer_syscall_pre_impl_setsid(); +void __sanitizer_syscall_post_impl_setsid(long res); +void __sanitizer_syscall_pre_impl_setgroups(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_setgroups(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_acct(long name); +void __sanitizer_syscall_post_impl_acct(long res, long name); +void __sanitizer_syscall_pre_impl_capget(long header, long dataptr); +void __sanitizer_syscall_post_impl_capget(long res, long header, long dataptr); +void __sanitizer_syscall_pre_impl_capset(long header, long data); +void __sanitizer_syscall_post_impl_capset(long res, long header, long data); +void __sanitizer_syscall_pre_impl_personality(long personality); +void __sanitizer_syscall_post_impl_personality(long res, long personality); +void __sanitizer_syscall_pre_impl_sigpending(long set); +void __sanitizer_syscall_post_impl_sigpending(long res, long set); +void __sanitizer_syscall_pre_impl_sigprocmask(long how, long set, long oset); +void __sanitizer_syscall_post_impl_sigprocmask(long res, long how, long set, + long oset); +void __sanitizer_syscall_pre_impl_getitimer(long which, long value); +void __sanitizer_syscall_post_impl_getitimer(long res, long which, long value); +void __sanitizer_syscall_pre_impl_setitimer(long which, long value, + long ovalue); +void __sanitizer_syscall_post_impl_setitimer(long res, long which, long value, + long ovalue); +void __sanitizer_syscall_pre_impl_timer_create(long which_clock, + long timer_event_spec, + long created_timer_id); +void __sanitizer_syscall_post_impl_timer_create(long res, long which_clock, + long timer_event_spec, + long created_timer_id); +void __sanitizer_syscall_pre_impl_timer_gettime(long timer_id, long setting); +void __sanitizer_syscall_post_impl_timer_gettime(long res, long timer_id, + long setting); +void __sanitizer_syscall_pre_impl_timer_getoverrun(long timer_id); +void __sanitizer_syscall_post_impl_timer_getoverrun(long res, long timer_id); +void __sanitizer_syscall_pre_impl_timer_settime(long timer_id, long flags, + long new_setting, + long old_setting); +void __sanitizer_syscall_post_impl_timer_settime(long res, long timer_id, + long flags, long new_setting, + long old_setting); +void __sanitizer_syscall_pre_impl_timer_delete(long timer_id); +void __sanitizer_syscall_post_impl_timer_delete(long res, long timer_id); +void __sanitizer_syscall_pre_impl_clock_settime(long which_clock, long tp); +void __sanitizer_syscall_post_impl_clock_settime(long res, long which_clock, + long tp); +void __sanitizer_syscall_pre_impl_clock_gettime(long which_clock, long tp); +void __sanitizer_syscall_post_impl_clock_gettime(long res, long which_clock, + long tp); +void __sanitizer_syscall_pre_impl_clock_adjtime(long which_clock, long tx); +void __sanitizer_syscall_post_impl_clock_adjtime(long res, long which_clock, + long tx); +void __sanitizer_syscall_pre_impl_clock_getres(long which_clock, long tp); +void __sanitizer_syscall_post_impl_clock_getres(long res, long which_clock, + long tp); +void __sanitizer_syscall_pre_impl_clock_nanosleep(long which_clock, long flags, + long rqtp, long rmtp); +void __sanitizer_syscall_post_impl_clock_nanosleep(long res, long which_clock, + long flags, long rqtp, + long rmtp); +void __sanitizer_syscall_pre_impl_nice(long increment); +void __sanitizer_syscall_post_impl_nice(long res, long increment); +void __sanitizer_syscall_pre_impl_sched_setscheduler(long pid, long policy, + long param); +void __sanitizer_syscall_post_impl_sched_setscheduler(long res, long pid, + long policy, long param); +void __sanitizer_syscall_pre_impl_sched_setparam(long pid, long param); +void __sanitizer_syscall_post_impl_sched_setparam(long res, long pid, + long param); +void __sanitizer_syscall_pre_impl_sched_getscheduler(long pid); +void __sanitizer_syscall_post_impl_sched_getscheduler(long res, long pid); +void __sanitizer_syscall_pre_impl_sched_getparam(long pid, long param); +void __sanitizer_syscall_post_impl_sched_getparam(long res, long pid, + long param); +void __sanitizer_syscall_pre_impl_sched_setaffinity(long pid, long len, + long user_mask_ptr); +void __sanitizer_syscall_post_impl_sched_setaffinity(long res, long pid, + long len, + long user_mask_ptr); +void __sanitizer_syscall_pre_impl_sched_getaffinity(long pid, long len, + long user_mask_ptr); +void __sanitizer_syscall_post_impl_sched_getaffinity(long res, long pid, + long len, + long user_mask_ptr); +void __sanitizer_syscall_pre_impl_sched_yield(); +void __sanitizer_syscall_post_impl_sched_yield(long res); +void __sanitizer_syscall_pre_impl_sched_get_priority_max(long policy); +void __sanitizer_syscall_post_impl_sched_get_priority_max(long res, + long policy); +void __sanitizer_syscall_pre_impl_sched_get_priority_min(long policy); +void __sanitizer_syscall_post_impl_sched_get_priority_min(long res, + long policy); +void __sanitizer_syscall_pre_impl_sched_rr_get_interval(long pid, + long interval); +void __sanitizer_syscall_post_impl_sched_rr_get_interval(long res, long pid, + long interval); +void __sanitizer_syscall_pre_impl_setpriority(long which, long who, + long niceval); +void __sanitizer_syscall_post_impl_setpriority(long res, long which, long who, + long niceval); +void __sanitizer_syscall_pre_impl_getpriority(long which, long who); +void __sanitizer_syscall_post_impl_getpriority(long res, long which, long who); +void __sanitizer_syscall_pre_impl_shutdown(long arg0, long arg1); +void __sanitizer_syscall_post_impl_shutdown(long res, long arg0, long arg1); +void __sanitizer_syscall_pre_impl_reboot(long magic1, long magic2, long cmd, + long arg); +void __sanitizer_syscall_post_impl_reboot(long res, long magic1, long magic2, + long cmd, long arg); +void __sanitizer_syscall_pre_impl_restart_syscall(); +void __sanitizer_syscall_post_impl_restart_syscall(long res); +void __sanitizer_syscall_pre_impl_kexec_load(long entry, long nr_segments, + long segments, long flags); +void __sanitizer_syscall_post_impl_kexec_load(long res, long entry, + long nr_segments, long segments, + long flags); +void __sanitizer_syscall_pre_impl_exit(long error_code); +void __sanitizer_syscall_post_impl_exit(long res, long error_code); +void __sanitizer_syscall_pre_impl_exit_group(long error_code); +void __sanitizer_syscall_post_impl_exit_group(long res, long error_code); +void __sanitizer_syscall_pre_impl_wait4(long pid, long stat_addr, long options, + long ru); +void __sanitizer_syscall_post_impl_wait4(long res, long pid, long stat_addr, + long options, long ru); +void __sanitizer_syscall_pre_impl_waitid(long which, long pid, long infop, + long options, long ru); +void __sanitizer_syscall_post_impl_waitid(long res, long which, long pid, + long infop, long options, long ru); +void __sanitizer_syscall_pre_impl_waitpid(long pid, long stat_addr, + long options); +void __sanitizer_syscall_post_impl_waitpid(long res, long pid, long stat_addr, + long options); +void __sanitizer_syscall_pre_impl_set_tid_address(long tidptr); +void __sanitizer_syscall_post_impl_set_tid_address(long res, long tidptr); +void __sanitizer_syscall_pre_impl_init_module(long umod, long len, long uargs); +void __sanitizer_syscall_post_impl_init_module(long res, long umod, long len, + long uargs); +void __sanitizer_syscall_pre_impl_delete_module(long name_user, long flags); +void __sanitizer_syscall_post_impl_delete_module(long res, long name_user, + long flags); +void __sanitizer_syscall_pre_impl_rt_sigprocmask(long how, long set, long oset, + long sigsetsize); +void __sanitizer_syscall_post_impl_rt_sigprocmask(long res, long how, long set, + long oset, long sigsetsize); +void __sanitizer_syscall_pre_impl_rt_sigpending(long set, long sigsetsize); +void __sanitizer_syscall_post_impl_rt_sigpending(long res, long set, + long sigsetsize); +void __sanitizer_syscall_pre_impl_rt_sigtimedwait(long uthese, long uinfo, + long uts, long sigsetsize); +void __sanitizer_syscall_post_impl_rt_sigtimedwait(long res, long uthese, + long uinfo, long uts, + long sigsetsize); +void __sanitizer_syscall_pre_impl_rt_tgsigqueueinfo(long tgid, long pid, + long sig, long uinfo); +void __sanitizer_syscall_post_impl_rt_tgsigqueueinfo(long res, long tgid, + long pid, long sig, + long uinfo); +void __sanitizer_syscall_pre_impl_kill(long pid, long sig); +void __sanitizer_syscall_post_impl_kill(long res, long pid, long sig); +void __sanitizer_syscall_pre_impl_tgkill(long tgid, long pid, long sig); +void __sanitizer_syscall_post_impl_tgkill(long res, long tgid, long pid, + long sig); +void __sanitizer_syscall_pre_impl_tkill(long pid, long sig); +void __sanitizer_syscall_post_impl_tkill(long res, long pid, long sig); +void __sanitizer_syscall_pre_impl_rt_sigqueueinfo(long pid, long sig, + long uinfo); +void __sanitizer_syscall_post_impl_rt_sigqueueinfo(long res, long pid, long sig, + long uinfo); +void __sanitizer_syscall_pre_impl_sgetmask(); +void __sanitizer_syscall_post_impl_sgetmask(long res); +void __sanitizer_syscall_pre_impl_ssetmask(long newmask); +void __sanitizer_syscall_post_impl_ssetmask(long res, long newmask); +void __sanitizer_syscall_pre_impl_signal(long sig, long handler); +void __sanitizer_syscall_post_impl_signal(long res, long sig, long handler); +void __sanitizer_syscall_pre_impl_pause(); +void __sanitizer_syscall_post_impl_pause(long res); +void __sanitizer_syscall_pre_impl_sync(); +void __sanitizer_syscall_post_impl_sync(long res); +void __sanitizer_syscall_pre_impl_fsync(long fd); +void __sanitizer_syscall_post_impl_fsync(long res, long fd); +void __sanitizer_syscall_pre_impl_fdatasync(long fd); +void __sanitizer_syscall_post_impl_fdatasync(long res, long fd); +void __sanitizer_syscall_pre_impl_bdflush(long func, long data); +void __sanitizer_syscall_post_impl_bdflush(long res, long func, long data); +void __sanitizer_syscall_pre_impl_mount(long dev_name, long dir_name, long type, + long flags, long data); +void __sanitizer_syscall_post_impl_mount(long res, long dev_name, long dir_name, + long type, long flags, long data); +void __sanitizer_syscall_pre_impl_umount(long name, long flags); +void __sanitizer_syscall_post_impl_umount(long res, long name, long flags); +void __sanitizer_syscall_pre_impl_oldumount(long name); +void __sanitizer_syscall_post_impl_oldumount(long res, long name); +void __sanitizer_syscall_pre_impl_truncate(long path, long length); +void __sanitizer_syscall_post_impl_truncate(long res, long path, long length); +void __sanitizer_syscall_pre_impl_ftruncate(long fd, long length); +void __sanitizer_syscall_post_impl_ftruncate(long res, long fd, long length); +void __sanitizer_syscall_pre_impl_stat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_stat(long res, long filename, long statbuf); +void __sanitizer_syscall_pre_impl_statfs(long path, long buf); +void __sanitizer_syscall_post_impl_statfs(long res, long path, long buf); +void __sanitizer_syscall_pre_impl_statfs64(long path, long sz, long buf); +void __sanitizer_syscall_post_impl_statfs64(long res, long path, long sz, + long buf); +void __sanitizer_syscall_pre_impl_fstatfs(long fd, long buf); +void __sanitizer_syscall_post_impl_fstatfs(long res, long fd, long buf); +void __sanitizer_syscall_pre_impl_fstatfs64(long fd, long sz, long buf); +void __sanitizer_syscall_post_impl_fstatfs64(long res, long fd, long sz, + long buf); +void __sanitizer_syscall_pre_impl_lstat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_lstat(long res, long filename, long statbuf); +void __sanitizer_syscall_pre_impl_fstat(long fd, long statbuf); +void __sanitizer_syscall_post_impl_fstat(long res, long fd, long statbuf); +void __sanitizer_syscall_pre_impl_newstat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_newstat(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_newlstat(long filename, long statbuf); +void __sanitizer_syscall_post_impl_newlstat(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_newfstat(long fd, long statbuf); +void __sanitizer_syscall_post_impl_newfstat(long res, long fd, long statbuf); +void __sanitizer_syscall_pre_impl_ustat(long dev, long ubuf); +void __sanitizer_syscall_post_impl_ustat(long res, long dev, long ubuf); +void __sanitizer_syscall_pre_impl_stat64(long filename, long statbuf); +void __sanitizer_syscall_post_impl_stat64(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_fstat64(long fd, long statbuf); +void __sanitizer_syscall_post_impl_fstat64(long res, long fd, long statbuf); +void __sanitizer_syscall_pre_impl_lstat64(long filename, long statbuf); +void __sanitizer_syscall_post_impl_lstat64(long res, long filename, + long statbuf); +void __sanitizer_syscall_pre_impl_setxattr(long path, long name, long value, + long size, long flags); +void __sanitizer_syscall_post_impl_setxattr(long res, long path, long name, + long value, long size, long flags); +void __sanitizer_syscall_pre_impl_lsetxattr(long path, long name, long value, + long size, long flags); +void __sanitizer_syscall_post_impl_lsetxattr(long res, long path, long name, + long value, long size, long flags); +void __sanitizer_syscall_pre_impl_fsetxattr(long fd, long name, long value, + long size, long flags); +void __sanitizer_syscall_post_impl_fsetxattr(long res, long fd, long name, + long value, long size, long flags); +void __sanitizer_syscall_pre_impl_getxattr(long path, long name, long value, + long size); +void __sanitizer_syscall_post_impl_getxattr(long res, long path, long name, + long value, long size); +void __sanitizer_syscall_pre_impl_lgetxattr(long path, long name, long value, + long size); +void __sanitizer_syscall_post_impl_lgetxattr(long res, long path, long name, + long value, long size); +void __sanitizer_syscall_pre_impl_fgetxattr(long fd, long name, long value, + long size); +void __sanitizer_syscall_post_impl_fgetxattr(long res, long fd, long name, + long value, long size); +void __sanitizer_syscall_pre_impl_listxattr(long path, long list, long size); +void __sanitizer_syscall_post_impl_listxattr(long res, long path, long list, + long size); +void __sanitizer_syscall_pre_impl_llistxattr(long path, long list, long size); +void __sanitizer_syscall_post_impl_llistxattr(long res, long path, long list, + long size); +void __sanitizer_syscall_pre_impl_flistxattr(long fd, long list, long size); +void __sanitizer_syscall_post_impl_flistxattr(long res, long fd, long list, + long size); +void __sanitizer_syscall_pre_impl_removexattr(long path, long name); +void __sanitizer_syscall_post_impl_removexattr(long res, long path, long name); +void __sanitizer_syscall_pre_impl_lremovexattr(long path, long name); +void __sanitizer_syscall_post_impl_lremovexattr(long res, long path, long name); +void __sanitizer_syscall_pre_impl_fremovexattr(long fd, long name); +void __sanitizer_syscall_post_impl_fremovexattr(long res, long fd, long name); +void __sanitizer_syscall_pre_impl_brk(long brk); +void __sanitizer_syscall_post_impl_brk(long res, long brk); +void __sanitizer_syscall_pre_impl_mprotect(long start, long len, long prot); +void __sanitizer_syscall_post_impl_mprotect(long res, long start, long len, + long prot); +void __sanitizer_syscall_pre_impl_mremap(long addr, long old_len, long new_len, + long flags, long new_addr); +void __sanitizer_syscall_post_impl_mremap(long res, long addr, long old_len, + long new_len, long flags, + long new_addr); +void __sanitizer_syscall_pre_impl_remap_file_pages(long start, long size, + long prot, long pgoff, + long flags); +void __sanitizer_syscall_post_impl_remap_file_pages(long res, long start, + long size, long prot, + long pgoff, long flags); +void __sanitizer_syscall_pre_impl_msync(long start, long len, long flags); +void __sanitizer_syscall_post_impl_msync(long res, long start, long len, + long flags); +void __sanitizer_syscall_pre_impl_munmap(long addr, long len); +void __sanitizer_syscall_post_impl_munmap(long res, long addr, long len); +void __sanitizer_syscall_pre_impl_mlock(long start, long len); +void __sanitizer_syscall_post_impl_mlock(long res, long start, long len); +void __sanitizer_syscall_pre_impl_munlock(long start, long len); +void __sanitizer_syscall_post_impl_munlock(long res, long start, long len); +void __sanitizer_syscall_pre_impl_mlockall(long flags); +void __sanitizer_syscall_post_impl_mlockall(long res, long flags); +void __sanitizer_syscall_pre_impl_munlockall(); +void __sanitizer_syscall_post_impl_munlockall(long res); +void __sanitizer_syscall_pre_impl_madvise(long start, long len, long behavior); +void __sanitizer_syscall_post_impl_madvise(long res, long start, long len, + long behavior); +void __sanitizer_syscall_pre_impl_mincore(long start, long len, long vec); +void __sanitizer_syscall_post_impl_mincore(long res, long start, long len, + long vec); +void __sanitizer_syscall_pre_impl_pivot_root(long new_root, long put_old); +void __sanitizer_syscall_post_impl_pivot_root(long res, long new_root, + long put_old); +void __sanitizer_syscall_pre_impl_chroot(long filename); +void __sanitizer_syscall_post_impl_chroot(long res, long filename); +void __sanitizer_syscall_pre_impl_mknod(long filename, long mode, long dev); +void __sanitizer_syscall_post_impl_mknod(long res, long filename, long mode, + long dev); +void __sanitizer_syscall_pre_impl_link(long oldname, long newname); +void __sanitizer_syscall_post_impl_link(long res, long oldname, long newname); +void __sanitizer_syscall_pre_impl_symlink(long old, long new_); +void __sanitizer_syscall_post_impl_symlink(long res, long old, long new_); +void __sanitizer_syscall_pre_impl_unlink(long pathname); +void __sanitizer_syscall_post_impl_unlink(long res, long pathname); +void __sanitizer_syscall_pre_impl_rename(long oldname, long newname); +void __sanitizer_syscall_post_impl_rename(long res, long oldname, long newname); +void __sanitizer_syscall_pre_impl_chmod(long filename, long mode); +void __sanitizer_syscall_post_impl_chmod(long res, long filename, long mode); +void __sanitizer_syscall_pre_impl_fchmod(long fd, long mode); +void __sanitizer_syscall_post_impl_fchmod(long res, long fd, long mode); +void __sanitizer_syscall_pre_impl_fcntl(long fd, long cmd, long arg); +void __sanitizer_syscall_post_impl_fcntl(long res, long fd, long cmd, long arg); +void __sanitizer_syscall_pre_impl_fcntl64(long fd, long cmd, long arg); +void __sanitizer_syscall_post_impl_fcntl64(long res, long fd, long cmd, + long arg); +void __sanitizer_syscall_pre_impl_pipe(long fildes); +void __sanitizer_syscall_post_impl_pipe(long res, long fildes); +void __sanitizer_syscall_pre_impl_pipe2(long fildes, long flags); +void __sanitizer_syscall_post_impl_pipe2(long res, long fildes, long flags); +void __sanitizer_syscall_pre_impl_dup(long fildes); +void __sanitizer_syscall_post_impl_dup(long res, long fildes); +void __sanitizer_syscall_pre_impl_dup2(long oldfd, long newfd); +void __sanitizer_syscall_post_impl_dup2(long res, long oldfd, long newfd); +void __sanitizer_syscall_pre_impl_dup3(long oldfd, long newfd, long flags); +void __sanitizer_syscall_post_impl_dup3(long res, long oldfd, long newfd, + long flags); +void __sanitizer_syscall_pre_impl_ioperm(long from, long num, long on); +void __sanitizer_syscall_post_impl_ioperm(long res, long from, long num, + long on); +void __sanitizer_syscall_pre_impl_ioctl(long fd, long cmd, long arg); +void __sanitizer_syscall_post_impl_ioctl(long res, long fd, long cmd, long arg); +void __sanitizer_syscall_pre_impl_flock(long fd, long cmd); +void __sanitizer_syscall_post_impl_flock(long res, long fd, long cmd); +void __sanitizer_syscall_pre_impl_io_setup(long nr_reqs, long ctx); +void __sanitizer_syscall_post_impl_io_setup(long res, long nr_reqs, long ctx); +void __sanitizer_syscall_pre_impl_io_destroy(long ctx); +void __sanitizer_syscall_post_impl_io_destroy(long res, long ctx); +void __sanitizer_syscall_pre_impl_io_getevents(long ctx_id, long min_nr, + long nr, long events, + long timeout); +void __sanitizer_syscall_post_impl_io_getevents(long res, long ctx_id, + long min_nr, long nr, + long events, long timeout); +void __sanitizer_syscall_pre_impl_io_submit(long ctx_id, long arg1, long arg2); +void __sanitizer_syscall_post_impl_io_submit(long res, long ctx_id, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_io_cancel(long ctx_id, long iocb, + long result); +void __sanitizer_syscall_post_impl_io_cancel(long res, long ctx_id, long iocb, + long result); +void __sanitizer_syscall_pre_impl_sendfile(long out_fd, long in_fd, long offset, + long count); +void __sanitizer_syscall_post_impl_sendfile(long res, long out_fd, long in_fd, + long offset, long count); +void __sanitizer_syscall_pre_impl_sendfile64(long out_fd, long in_fd, + long offset, long count); +void __sanitizer_syscall_post_impl_sendfile64(long res, long out_fd, long in_fd, + long offset, long count); +void __sanitizer_syscall_pre_impl_readlink(long path, long buf, long bufsiz); +void __sanitizer_syscall_post_impl_readlink(long res, long path, long buf, + long bufsiz); +void __sanitizer_syscall_pre_impl_creat(long pathname, long mode); +void __sanitizer_syscall_post_impl_creat(long res, long pathname, long mode); +void __sanitizer_syscall_pre_impl_open(long filename, long flags, long mode); +void __sanitizer_syscall_post_impl_open(long res, long filename, long flags, + long mode); +void __sanitizer_syscall_pre_impl_close(long fd); +void __sanitizer_syscall_post_impl_close(long res, long fd); +void __sanitizer_syscall_pre_impl_access(long filename, long mode); +void __sanitizer_syscall_post_impl_access(long res, long filename, long mode); +void __sanitizer_syscall_pre_impl_vhangup(); +void __sanitizer_syscall_post_impl_vhangup(long res); +void __sanitizer_syscall_pre_impl_chown(long filename, long user, long group); +void __sanitizer_syscall_post_impl_chown(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_lchown(long filename, long user, long group); +void __sanitizer_syscall_post_impl_lchown(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_fchown(long fd, long user, long group); +void __sanitizer_syscall_post_impl_fchown(long res, long fd, long user, + long group); +void __sanitizer_syscall_pre_impl_chown16(long filename, long user, long group); +void __sanitizer_syscall_post_impl_chown16(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_lchown16(long filename, long user, + long group); +void __sanitizer_syscall_post_impl_lchown16(long res, long filename, long user, + long group); +void __sanitizer_syscall_pre_impl_fchown16(long fd, long user, long group); +void __sanitizer_syscall_post_impl_fchown16(long res, long fd, long user, + long group); +void __sanitizer_syscall_pre_impl_setregid16(long rgid, long egid); +void __sanitizer_syscall_post_impl_setregid16(long res, long rgid, long egid); +void __sanitizer_syscall_pre_impl_setgid16(long gid); +void __sanitizer_syscall_post_impl_setgid16(long res, long gid); +void __sanitizer_syscall_pre_impl_setreuid16(long ruid, long euid); +void __sanitizer_syscall_post_impl_setreuid16(long res, long ruid, long euid); +void __sanitizer_syscall_pre_impl_setuid16(long uid); +void __sanitizer_syscall_post_impl_setuid16(long res, long uid); +void __sanitizer_syscall_pre_impl_setresuid16(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_setresuid16(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_getresuid16(long ruid, long euid, long suid); +void __sanitizer_syscall_post_impl_getresuid16(long res, long ruid, long euid, + long suid); +void __sanitizer_syscall_pre_impl_setresgid16(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_setresgid16(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_getresgid16(long rgid, long egid, long sgid); +void __sanitizer_syscall_post_impl_getresgid16(long res, long rgid, long egid, + long sgid); +void __sanitizer_syscall_pre_impl_setfsuid16(long uid); +void __sanitizer_syscall_post_impl_setfsuid16(long res, long uid); +void __sanitizer_syscall_pre_impl_setfsgid16(long gid); +void __sanitizer_syscall_post_impl_setfsgid16(long res, long gid); +void __sanitizer_syscall_pre_impl_getgroups16(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_getgroups16(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_setgroups16(long gidsetsize, long grouplist); +void __sanitizer_syscall_post_impl_setgroups16(long res, long gidsetsize, + long grouplist); +void __sanitizer_syscall_pre_impl_getuid16(); +void __sanitizer_syscall_post_impl_getuid16(long res); +void __sanitizer_syscall_pre_impl_geteuid16(); +void __sanitizer_syscall_post_impl_geteuid16(long res); +void __sanitizer_syscall_pre_impl_getgid16(); +void __sanitizer_syscall_post_impl_getgid16(long res); +void __sanitizer_syscall_pre_impl_getegid16(); +void __sanitizer_syscall_post_impl_getegid16(long res); +void __sanitizer_syscall_pre_impl_utime(long filename, long times); +void __sanitizer_syscall_post_impl_utime(long res, long filename, long times); +void __sanitizer_syscall_pre_impl_utimes(long filename, long utimes); +void __sanitizer_syscall_post_impl_utimes(long res, long filename, long utimes); +void __sanitizer_syscall_pre_impl_lseek(long fd, long offset, long origin); +void __sanitizer_syscall_post_impl_lseek(long res, long fd, long offset, + long origin); +void __sanitizer_syscall_pre_impl_llseek(long fd, long offset_high, + long offset_low, long result, + long origin); +void __sanitizer_syscall_post_impl_llseek(long res, long fd, long offset_high, + long offset_low, long result, + long origin); +void __sanitizer_syscall_pre_impl_read(long fd, long buf, long count); +void __sanitizer_syscall_post_impl_read(long res, long fd, long buf, + long count); +void __sanitizer_syscall_pre_impl_readv(long fd, long vec, long vlen); +void __sanitizer_syscall_post_impl_readv(long res, long fd, long vec, + long vlen); +void __sanitizer_syscall_pre_impl_write(long fd, long buf, long count); +void __sanitizer_syscall_post_impl_write(long res, long fd, long buf, + long count); +void __sanitizer_syscall_pre_impl_writev(long fd, long vec, long vlen); +void __sanitizer_syscall_post_impl_writev(long res, long fd, long vec, + long vlen); + +#ifdef _LP64 +void __sanitizer_syscall_pre_impl_pread64(long fd, long buf, long count, + long pos); +void __sanitizer_syscall_post_impl_pread64(long res, long fd, long buf, + long count, long pos); +void __sanitizer_syscall_pre_impl_pwrite64(long fd, long buf, long count, + long pos); +void __sanitizer_syscall_post_impl_pwrite64(long res, long fd, long buf, + long count, long pos); +#else +void __sanitizer_syscall_pre_impl_pread64(long fd, long buf, long count, + long pos0, long pos1); +void __sanitizer_syscall_post_impl_pread64(long res, long fd, long buf, + long count, long pos0, long pos1); +void __sanitizer_syscall_pre_impl_pwrite64(long fd, long buf, long count, + long pos0, long pos1); +void __sanitizer_syscall_post_impl_pwrite64(long res, long fd, long buf, + long count, long pos0, long pos1); +#endif + +void __sanitizer_syscall_pre_impl_preadv(long fd, long vec, long vlen, + long pos_l, long pos_h); +void __sanitizer_syscall_post_impl_preadv(long res, long fd, long vec, + long vlen, long pos_l, long pos_h); +void __sanitizer_syscall_pre_impl_pwritev(long fd, long vec, long vlen, + long pos_l, long pos_h); +void __sanitizer_syscall_post_impl_pwritev(long res, long fd, long vec, + long vlen, long pos_l, long pos_h); +void __sanitizer_syscall_pre_impl_getcwd(long buf, long size); +void __sanitizer_syscall_post_impl_getcwd(long res, long buf, long size); +void __sanitizer_syscall_pre_impl_mkdir(long pathname, long mode); +void __sanitizer_syscall_post_impl_mkdir(long res, long pathname, long mode); +void __sanitizer_syscall_pre_impl_chdir(long filename); +void __sanitizer_syscall_post_impl_chdir(long res, long filename); +void __sanitizer_syscall_pre_impl_fchdir(long fd); +void __sanitizer_syscall_post_impl_fchdir(long res, long fd); +void __sanitizer_syscall_pre_impl_rmdir(long pathname); +void __sanitizer_syscall_post_impl_rmdir(long res, long pathname); +void __sanitizer_syscall_pre_impl_lookup_dcookie(long cookie64, long buf, + long len); +void __sanitizer_syscall_post_impl_lookup_dcookie(long res, long cookie64, + long buf, long len); +void __sanitizer_syscall_pre_impl_quotactl(long cmd, long special, long id, + long addr); +void __sanitizer_syscall_post_impl_quotactl(long res, long cmd, long special, + long id, long addr); +void __sanitizer_syscall_pre_impl_getdents(long fd, long dirent, long count); +void __sanitizer_syscall_post_impl_getdents(long res, long fd, long dirent, + long count); +void __sanitizer_syscall_pre_impl_getdents64(long fd, long dirent, long count); +void __sanitizer_syscall_post_impl_getdents64(long res, long fd, long dirent, + long count); +void __sanitizer_syscall_pre_impl_setsockopt(long fd, long level, long optname, + long optval, long optlen); +void __sanitizer_syscall_post_impl_setsockopt(long res, long fd, long level, + long optname, long optval, + long optlen); +void __sanitizer_syscall_pre_impl_getsockopt(long fd, long level, long optname, + long optval, long optlen); +void __sanitizer_syscall_post_impl_getsockopt(long res, long fd, long level, + long optname, long optval, + long optlen); +void __sanitizer_syscall_pre_impl_bind(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_bind(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_connect(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_connect(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_accept(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_accept(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_accept4(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_accept4(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_getsockname(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_getsockname(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_getpeername(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_getpeername(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_send(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_send(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_sendto(long arg0, long arg1, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_post_impl_sendto(long res, long arg0, long arg1, + long arg2, long arg3, long arg4, + long arg5); +void __sanitizer_syscall_pre_impl_sendmsg(long fd, long msg, long flags); +void __sanitizer_syscall_post_impl_sendmsg(long res, long fd, long msg, + long flags); +void __sanitizer_syscall_pre_impl_sendmmsg(long fd, long msg, long vlen, + long flags); +void __sanitizer_syscall_post_impl_sendmmsg(long res, long fd, long msg, + long vlen, long flags); +void __sanitizer_syscall_pre_impl_recv(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_recv(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_recvfrom(long arg0, long arg1, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_post_impl_recvfrom(long res, long arg0, long arg1, + long arg2, long arg3, long arg4, + long arg5); +void __sanitizer_syscall_pre_impl_recvmsg(long fd, long msg, long flags); +void __sanitizer_syscall_post_impl_recvmsg(long res, long fd, long msg, + long flags); +void __sanitizer_syscall_pre_impl_recvmmsg(long fd, long msg, long vlen, + long flags, long timeout); +void __sanitizer_syscall_post_impl_recvmmsg(long res, long fd, long msg, + long vlen, long flags, + long timeout); +void __sanitizer_syscall_pre_impl_socket(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_socket(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_socketpair(long arg0, long arg1, long arg2, + long arg3); +void __sanitizer_syscall_post_impl_socketpair(long res, long arg0, long arg1, + long arg2, long arg3); +void __sanitizer_syscall_pre_impl_socketcall(long call, long args); +void __sanitizer_syscall_post_impl_socketcall(long res, long call, long args); +void __sanitizer_syscall_pre_impl_listen(long arg0, long arg1); +void __sanitizer_syscall_post_impl_listen(long res, long arg0, long arg1); +void __sanitizer_syscall_pre_impl_poll(long ufds, long nfds, long timeout); +void __sanitizer_syscall_post_impl_poll(long res, long ufds, long nfds, + long timeout); +void __sanitizer_syscall_pre_impl_select(long n, long inp, long outp, long exp, + long tvp); +void __sanitizer_syscall_post_impl_select(long res, long n, long inp, long outp, + long exp, long tvp); +void __sanitizer_syscall_pre_impl_old_select(long arg); +void __sanitizer_syscall_post_impl_old_select(long res, long arg); +void __sanitizer_syscall_pre_impl_epoll_create(long size); +void __sanitizer_syscall_post_impl_epoll_create(long res, long size); +void __sanitizer_syscall_pre_impl_epoll_create1(long flags); +void __sanitizer_syscall_post_impl_epoll_create1(long res, long flags); +void __sanitizer_syscall_pre_impl_epoll_ctl(long epfd, long op, long fd, + long event); +void __sanitizer_syscall_post_impl_epoll_ctl(long res, long epfd, long op, + long fd, long event); +void __sanitizer_syscall_pre_impl_epoll_wait(long epfd, long events, + long maxevents, long timeout); +void __sanitizer_syscall_post_impl_epoll_wait(long res, long epfd, long events, + long maxevents, long timeout); +void __sanitizer_syscall_pre_impl_epoll_pwait(long epfd, long events, + long maxevents, long timeout, + long sigmask, long sigsetsize); +void __sanitizer_syscall_post_impl_epoll_pwait(long res, long epfd, long events, + long maxevents, long timeout, + long sigmask, long sigsetsize); +void __sanitizer_syscall_pre_impl_gethostname(long name, long len); +void __sanitizer_syscall_post_impl_gethostname(long res, long name, long len); +void __sanitizer_syscall_pre_impl_sethostname(long name, long len); +void __sanitizer_syscall_post_impl_sethostname(long res, long name, long len); +void __sanitizer_syscall_pre_impl_setdomainname(long name, long len); +void __sanitizer_syscall_post_impl_setdomainname(long res, long name, long len); +void __sanitizer_syscall_pre_impl_newuname(long name); +void __sanitizer_syscall_post_impl_newuname(long res, long name); +void __sanitizer_syscall_pre_impl_uname(long arg0); +void __sanitizer_syscall_post_impl_uname(long res, long arg0); +void __sanitizer_syscall_pre_impl_olduname(long arg0); +void __sanitizer_syscall_post_impl_olduname(long res, long arg0); +void __sanitizer_syscall_pre_impl_getrlimit(long resource, long rlim); +void __sanitizer_syscall_post_impl_getrlimit(long res, long resource, + long rlim); +void __sanitizer_syscall_pre_impl_old_getrlimit(long resource, long rlim); +void __sanitizer_syscall_post_impl_old_getrlimit(long res, long resource, + long rlim); +void __sanitizer_syscall_pre_impl_setrlimit(long resource, long rlim); +void __sanitizer_syscall_post_impl_setrlimit(long res, long resource, + long rlim); +void __sanitizer_syscall_pre_impl_prlimit64(long pid, long resource, + long new_rlim, long old_rlim); +void __sanitizer_syscall_post_impl_prlimit64(long res, long pid, long resource, + long new_rlim, long old_rlim); +void __sanitizer_syscall_pre_impl_getrusage(long who, long ru); +void __sanitizer_syscall_post_impl_getrusage(long res, long who, long ru); +void __sanitizer_syscall_pre_impl_umask(long mask); +void __sanitizer_syscall_post_impl_umask(long res, long mask); +void __sanitizer_syscall_pre_impl_msgget(long key, long msgflg); +void __sanitizer_syscall_post_impl_msgget(long res, long key, long msgflg); +void __sanitizer_syscall_pre_impl_msgsnd(long msqid, long msgp, long msgsz, + long msgflg); +void __sanitizer_syscall_post_impl_msgsnd(long res, long msqid, long msgp, + long msgsz, long msgflg); +void __sanitizer_syscall_pre_impl_msgrcv(long msqid, long msgp, long msgsz, + long msgtyp, long msgflg); +void __sanitizer_syscall_post_impl_msgrcv(long res, long msqid, long msgp, + long msgsz, long msgtyp, long msgflg); +void __sanitizer_syscall_pre_impl_msgctl(long msqid, long cmd, long buf); +void __sanitizer_syscall_post_impl_msgctl(long res, long msqid, long cmd, + long buf); +void __sanitizer_syscall_pre_impl_semget(long key, long nsems, long semflg); +void __sanitizer_syscall_post_impl_semget(long res, long key, long nsems, + long semflg); +void __sanitizer_syscall_pre_impl_semop(long semid, long sops, long nsops); +void __sanitizer_syscall_post_impl_semop(long res, long semid, long sops, + long nsops); +void __sanitizer_syscall_pre_impl_semctl(long semid, long semnum, long cmd, + long arg); +void __sanitizer_syscall_post_impl_semctl(long res, long semid, long semnum, + long cmd, long arg); +void __sanitizer_syscall_pre_impl_semtimedop(long semid, long sops, long nsops, + long timeout); +void __sanitizer_syscall_post_impl_semtimedop(long res, long semid, long sops, + long nsops, long timeout); +void __sanitizer_syscall_pre_impl_shmat(long shmid, long shmaddr, long shmflg); +void __sanitizer_syscall_post_impl_shmat(long res, long shmid, long shmaddr, + long shmflg); +void __sanitizer_syscall_pre_impl_shmget(long key, long size, long flag); +void __sanitizer_syscall_post_impl_shmget(long res, long key, long size, + long flag); +void __sanitizer_syscall_pre_impl_shmdt(long shmaddr); +void __sanitizer_syscall_post_impl_shmdt(long res, long shmaddr); +void __sanitizer_syscall_pre_impl_shmctl(long shmid, long cmd, long buf); +void __sanitizer_syscall_post_impl_shmctl(long res, long shmid, long cmd, + long buf); +void __sanitizer_syscall_pre_impl_ipc(long call, long first, long second, + long third, long ptr, long fifth); +void __sanitizer_syscall_post_impl_ipc(long res, long call, long first, + long second, long third, long ptr, + long fifth); +void __sanitizer_syscall_pre_impl_mq_open(long name, long oflag, long mode, + long attr); +void __sanitizer_syscall_post_impl_mq_open(long res, long name, long oflag, + long mode, long attr); +void __sanitizer_syscall_pre_impl_mq_unlink(long name); +void __sanitizer_syscall_post_impl_mq_unlink(long res, long name); +void __sanitizer_syscall_pre_impl_mq_timedsend(long mqdes, long msg_ptr, + long msg_len, long msg_prio, + long abs_timeout); +void __sanitizer_syscall_post_impl_mq_timedsend(long res, long mqdes, + long msg_ptr, long msg_len, + long msg_prio, + long abs_timeout); +void __sanitizer_syscall_pre_impl_mq_timedreceive(long mqdes, long msg_ptr, + long msg_len, long msg_prio, + long abs_timeout); +void __sanitizer_syscall_post_impl_mq_timedreceive(long res, long mqdes, + long msg_ptr, long msg_len, + long msg_prio, + long abs_timeout); +void __sanitizer_syscall_pre_impl_mq_notify(long mqdes, long notification); +void __sanitizer_syscall_post_impl_mq_notify(long res, long mqdes, + long notification); +void __sanitizer_syscall_pre_impl_mq_getsetattr(long mqdes, long mqstat, + long omqstat); +void __sanitizer_syscall_post_impl_mq_getsetattr(long res, long mqdes, + long mqstat, long omqstat); +void __sanitizer_syscall_pre_impl_pciconfig_iobase(long which, long bus, + long devfn); +void __sanitizer_syscall_post_impl_pciconfig_iobase(long res, long which, + long bus, long devfn); +void __sanitizer_syscall_pre_impl_pciconfig_read(long bus, long dfn, long off, + long len, long buf); +void __sanitizer_syscall_post_impl_pciconfig_read(long res, long bus, long dfn, + long off, long len, long buf); +void __sanitizer_syscall_pre_impl_pciconfig_write(long bus, long dfn, long off, + long len, long buf); +void __sanitizer_syscall_post_impl_pciconfig_write(long res, long bus, long dfn, + long off, long len, + long buf); +void __sanitizer_syscall_pre_impl_swapon(long specialfile, long swap_flags); +void __sanitizer_syscall_post_impl_swapon(long res, long specialfile, + long swap_flags); +void __sanitizer_syscall_pre_impl_swapoff(long specialfile); +void __sanitizer_syscall_post_impl_swapoff(long res, long specialfile); +void __sanitizer_syscall_pre_impl_sysctl(long args); +void __sanitizer_syscall_post_impl_sysctl(long res, long args); +void __sanitizer_syscall_pre_impl_sysinfo(long info); +void __sanitizer_syscall_post_impl_sysinfo(long res, long info); +void __sanitizer_syscall_pre_impl_sysfs(long option, long arg1, long arg2); +void __sanitizer_syscall_post_impl_sysfs(long res, long option, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_syslog(long type, long buf, long len); +void __sanitizer_syscall_post_impl_syslog(long res, long type, long buf, + long len); +void __sanitizer_syscall_pre_impl_uselib(long library); +void __sanitizer_syscall_post_impl_uselib(long res, long library); +void __sanitizer_syscall_pre_impl_ni_syscall(); +void __sanitizer_syscall_post_impl_ni_syscall(long res); +void __sanitizer_syscall_pre_impl_ptrace(long request, long pid, long addr, + long data); +void __sanitizer_syscall_post_impl_ptrace(long res, long request, long pid, + long addr, long data); +void __sanitizer_syscall_pre_impl_add_key(long _type, long _description, + long _payload, long plen, + long destringid); +void __sanitizer_syscall_post_impl_add_key(long res, long _type, + long _description, long _payload, + long plen, long destringid); +void __sanitizer_syscall_pre_impl_request_key(long _type, long _description, + long _callout_info, + long destringid); +void __sanitizer_syscall_post_impl_request_key(long res, long _type, + long _description, + long _callout_info, + long destringid); +void __sanitizer_syscall_pre_impl_keyctl(long cmd, long arg2, long arg3, + long arg4, long arg5); +void __sanitizer_syscall_post_impl_keyctl(long res, long cmd, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_pre_impl_ioprio_set(long which, long who, long ioprio); +void __sanitizer_syscall_post_impl_ioprio_set(long res, long which, long who, + long ioprio); +void __sanitizer_syscall_pre_impl_ioprio_get(long which, long who); +void __sanitizer_syscall_post_impl_ioprio_get(long res, long which, long who); +void __sanitizer_syscall_pre_impl_set_mempolicy(long mode, long nmask, + long maxnode); +void __sanitizer_syscall_post_impl_set_mempolicy(long res, long mode, + long nmask, long maxnode); +void __sanitizer_syscall_pre_impl_migrate_pages(long pid, long maxnode, + long from, long to); +void __sanitizer_syscall_post_impl_migrate_pages(long res, long pid, + long maxnode, long from, + long to); +void __sanitizer_syscall_pre_impl_move_pages(long pid, long nr_pages, + long pages, long nodes, + long status, long flags); +void __sanitizer_syscall_post_impl_move_pages(long res, long pid, long nr_pages, + long pages, long nodes, + long status, long flags); +void __sanitizer_syscall_pre_impl_mbind(long start, long len, long mode, + long nmask, long maxnode, long flags); +void __sanitizer_syscall_post_impl_mbind(long res, long start, long len, + long mode, long nmask, long maxnode, + long flags); +void __sanitizer_syscall_pre_impl_get_mempolicy(long policy, long nmask, + long maxnode, long addr, + long flags); +void __sanitizer_syscall_post_impl_get_mempolicy(long res, long policy, + long nmask, long maxnode, + long addr, long flags); +void __sanitizer_syscall_pre_impl_inotify_init(); +void __sanitizer_syscall_post_impl_inotify_init(long res); +void __sanitizer_syscall_pre_impl_inotify_init1(long flags); +void __sanitizer_syscall_post_impl_inotify_init1(long res, long flags); +void __sanitizer_syscall_pre_impl_inotify_add_watch(long fd, long path, + long mask); +void __sanitizer_syscall_post_impl_inotify_add_watch(long res, long fd, + long path, long mask); +void __sanitizer_syscall_pre_impl_inotify_rm_watch(long fd, long wd); +void __sanitizer_syscall_post_impl_inotify_rm_watch(long res, long fd, long wd); +void __sanitizer_syscall_pre_impl_spu_run(long fd, long unpc, long ustatus); +void __sanitizer_syscall_post_impl_spu_run(long res, long fd, long unpc, + long ustatus); +void __sanitizer_syscall_pre_impl_spu_create(long name, long flags, long mode, + long fd); +void __sanitizer_syscall_post_impl_spu_create(long res, long name, long flags, + long mode, long fd); +void __sanitizer_syscall_pre_impl_mknodat(long dfd, long filename, long mode, + long dev); +void __sanitizer_syscall_post_impl_mknodat(long res, long dfd, long filename, + long mode, long dev); +void __sanitizer_syscall_pre_impl_mkdirat(long dfd, long pathname, long mode); +void __sanitizer_syscall_post_impl_mkdirat(long res, long dfd, long pathname, + long mode); +void __sanitizer_syscall_pre_impl_unlinkat(long dfd, long pathname, long flag); +void __sanitizer_syscall_post_impl_unlinkat(long res, long dfd, long pathname, + long flag); +void __sanitizer_syscall_pre_impl_symlinkat(long oldname, long newdfd, + long newname); +void __sanitizer_syscall_post_impl_symlinkat(long res, long oldname, + long newdfd, long newname); +void __sanitizer_syscall_pre_impl_linkat(long olddfd, long oldname, long newdfd, + long newname, long flags); +void __sanitizer_syscall_post_impl_linkat(long res, long olddfd, long oldname, + long newdfd, long newname, + long flags); +void __sanitizer_syscall_pre_impl_renameat(long olddfd, long oldname, + long newdfd, long newname); +void __sanitizer_syscall_post_impl_renameat(long res, long olddfd, long oldname, + long newdfd, long newname); +void __sanitizer_syscall_pre_impl_futimesat(long dfd, long filename, + long utimes); +void __sanitizer_syscall_post_impl_futimesat(long res, long dfd, long filename, + long utimes); +void __sanitizer_syscall_pre_impl_faccessat(long dfd, long filename, long mode); +void __sanitizer_syscall_post_impl_faccessat(long res, long dfd, long filename, + long mode); +void __sanitizer_syscall_pre_impl_fchmodat(long dfd, long filename, long mode); +void __sanitizer_syscall_post_impl_fchmodat(long res, long dfd, long filename, + long mode); +void __sanitizer_syscall_pre_impl_fchownat(long dfd, long filename, long user, + long group, long flag); +void __sanitizer_syscall_post_impl_fchownat(long res, long dfd, long filename, + long user, long group, long flag); +void __sanitizer_syscall_pre_impl_openat(long dfd, long filename, long flags, + long mode); +void __sanitizer_syscall_post_impl_openat(long res, long dfd, long filename, + long flags, long mode); +void __sanitizer_syscall_pre_impl_newfstatat(long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_post_impl_newfstatat(long res, long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_pre_impl_fstatat64(long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_post_impl_fstatat64(long res, long dfd, long filename, + long statbuf, long flag); +void __sanitizer_syscall_pre_impl_readlinkat(long dfd, long path, long buf, + long bufsiz); +void __sanitizer_syscall_post_impl_readlinkat(long res, long dfd, long path, + long buf, long bufsiz); +void __sanitizer_syscall_pre_impl_utimensat(long dfd, long filename, + long utimes, long flags); +void __sanitizer_syscall_post_impl_utimensat(long res, long dfd, long filename, + long utimes, long flags); +void __sanitizer_syscall_pre_impl_unshare(long unshare_flags); +void __sanitizer_syscall_post_impl_unshare(long res, long unshare_flags); +void __sanitizer_syscall_pre_impl_splice(long fd_in, long off_in, long fd_out, + long off_out, long len, long flags); +void __sanitizer_syscall_post_impl_splice(long res, long fd_in, long off_in, + long fd_out, long off_out, long len, + long flags); +void __sanitizer_syscall_pre_impl_vmsplice(long fd, long iov, long nr_segs, + long flags); +void __sanitizer_syscall_post_impl_vmsplice(long res, long fd, long iov, + long nr_segs, long flags); +void __sanitizer_syscall_pre_impl_tee(long fdin, long fdout, long len, + long flags); +void __sanitizer_syscall_post_impl_tee(long res, long fdin, long fdout, + long len, long flags); +void __sanitizer_syscall_pre_impl_get_robust_list(long pid, long head_ptr, + long len_ptr); +void __sanitizer_syscall_post_impl_get_robust_list(long res, long pid, + long head_ptr, long len_ptr); +void __sanitizer_syscall_pre_impl_set_robust_list(long head, long len); +void __sanitizer_syscall_post_impl_set_robust_list(long res, long head, + long len); +void __sanitizer_syscall_pre_impl_getcpu(long cpu, long node, long cache); +void __sanitizer_syscall_post_impl_getcpu(long res, long cpu, long node, + long cache); +void __sanitizer_syscall_pre_impl_signalfd(long ufd, long user_mask, + long sizemask); +void __sanitizer_syscall_post_impl_signalfd(long res, long ufd, long user_mask, + long sizemask); +void __sanitizer_syscall_pre_impl_signalfd4(long ufd, long user_mask, + long sizemask, long flags); +void __sanitizer_syscall_post_impl_signalfd4(long res, long ufd, long user_mask, + long sizemask, long flags); +void __sanitizer_syscall_pre_impl_timerfd_create(long clockid, long flags); +void __sanitizer_syscall_post_impl_timerfd_create(long res, long clockid, + long flags); +void __sanitizer_syscall_pre_impl_timerfd_settime(long ufd, long flags, + long utmr, long otmr); +void __sanitizer_syscall_post_impl_timerfd_settime(long res, long ufd, + long flags, long utmr, + long otmr); +void __sanitizer_syscall_pre_impl_timerfd_gettime(long ufd, long otmr); +void __sanitizer_syscall_post_impl_timerfd_gettime(long res, long ufd, + long otmr); +void __sanitizer_syscall_pre_impl_eventfd(long count); +void __sanitizer_syscall_post_impl_eventfd(long res, long count); +void __sanitizer_syscall_pre_impl_eventfd2(long count, long flags); +void __sanitizer_syscall_post_impl_eventfd2(long res, long count, long flags); +void __sanitizer_syscall_pre_impl_old_readdir(long arg0, long arg1, long arg2); +void __sanitizer_syscall_post_impl_old_readdir(long res, long arg0, long arg1, + long arg2); +void __sanitizer_syscall_pre_impl_pselect6(long arg0, long arg1, long arg2, + long arg3, long arg4, long arg5); +void __sanitizer_syscall_post_impl_pselect6(long res, long arg0, long arg1, + long arg2, long arg3, long arg4, + long arg5); +void __sanitizer_syscall_pre_impl_ppoll(long arg0, long arg1, long arg2, + long arg3, long arg4); +void __sanitizer_syscall_post_impl_ppoll(long res, long arg0, long arg1, + long arg2, long arg3, long arg4); +void __sanitizer_syscall_pre_impl_fanotify_init(long flags, long event_f_flags); +void __sanitizer_syscall_post_impl_fanotify_init(long res, long flags, + long event_f_flags); +void __sanitizer_syscall_pre_impl_fanotify_mark(long fanotify_fd, long flags, + long mask, long fd, + long pathname); +void __sanitizer_syscall_post_impl_fanotify_mark(long res, long fanotify_fd, + long flags, long mask, long fd, + long pathname); +void __sanitizer_syscall_pre_impl_syncfs(long fd); +void __sanitizer_syscall_post_impl_syncfs(long res, long fd); +void __sanitizer_syscall_pre_impl_perf_event_open(long attr_uptr, long pid, + long cpu, long group_fd, + long flags); +void __sanitizer_syscall_post_impl_perf_event_open(long res, long attr_uptr, + long pid, long cpu, + long group_fd, long flags); +void __sanitizer_syscall_pre_impl_mmap_pgoff(long addr, long len, long prot, + long flags, long fd, long pgoff); +void __sanitizer_syscall_post_impl_mmap_pgoff(long res, long addr, long len, + long prot, long flags, long fd, + long pgoff); +void __sanitizer_syscall_pre_impl_old_mmap(long arg); +void __sanitizer_syscall_post_impl_old_mmap(long res, long arg); +void __sanitizer_syscall_pre_impl_name_to_handle_at(long dfd, long name, + long handle, long mnt_id, + long flag); +void __sanitizer_syscall_post_impl_name_to_handle_at(long res, long dfd, + long name, long handle, + long mnt_id, long flag); +void __sanitizer_syscall_pre_impl_open_by_handle_at(long mountdirfd, + long handle, long flags); +void __sanitizer_syscall_post_impl_open_by_handle_at(long res, long mountdirfd, + long handle, long flags); +void __sanitizer_syscall_pre_impl_setns(long fd, long nstype); +void __sanitizer_syscall_post_impl_setns(long res, long fd, long nstype); +void __sanitizer_syscall_pre_impl_process_vm_readv(long pid, long lvec, + long liovcnt, long rvec, + long riovcnt, long flags); +void __sanitizer_syscall_post_impl_process_vm_readv(long res, long pid, + long lvec, long liovcnt, + long rvec, long riovcnt, + long flags); +void __sanitizer_syscall_pre_impl_process_vm_writev(long pid, long lvec, + long liovcnt, long rvec, + long riovcnt, long flags); +void __sanitizer_syscall_post_impl_process_vm_writev(long res, long pid, + long lvec, long liovcnt, + long rvec, long riovcnt, + long flags); +void __sanitizer_syscall_pre_impl_fork(); +void __sanitizer_syscall_post_impl_fork(long res); +void __sanitizer_syscall_pre_impl_vfork(); +void __sanitizer_syscall_post_impl_vfork(long res); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_LINUX_SYSCALL_HOOKS_H diff --git a/libsanitizer/include/sanitizer/lsan_interface.h b/libsanitizer/include/sanitizer/lsan_interface.h new file mode 100644 index 00000000000..ec9c730eee3 --- /dev/null +++ b/libsanitizer/include/sanitizer/lsan_interface.h @@ -0,0 +1,50 @@ +//===-- sanitizer/lsan_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 LeakSanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LSAN_INTERFACE_H +#define SANITIZER_LSAN_INTERFACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + // Allocations made between calls to __lsan_disable() and __lsan_enable() will + // 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); + // 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(); +#ifdef __cplusplus +} // extern "C" + +namespace __lsan { +class ScopedDisabler { + public: + ScopedDisabler() { __lsan_disable(); } + ~ScopedDisabler() { __lsan_enable(); } +}; +} // namespace __lsan +#endif + +#endif // SANITIZER_LSAN_INTERFACE_H diff --git a/libsanitizer/include/sanitizer/msan_interface.h b/libsanitizer/include/sanitizer/msan_interface.h new file mode 100644 index 00000000000..f531cf347c9 --- /dev/null +++ b/libsanitizer/include/sanitizer/msan_interface.h @@ -0,0 +1,160 @@ +//===-- msan_interface.h --------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef MSAN_INTERFACE_H +#define MSAN_INTERFACE_H + +#include + +#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); + + /* Returns non-zero if tracking origins. */ + int __msan_get_track_origins(); + + /* Returns the origin id of the latest UMR in the calling thread. */ + uint32_t __msan_get_umr_origin(); + + /* 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). */ + void __msan_poison(const volatile void *a, size_t size); + + /* Make memory region partially uninitialized (without changing its contents). + */ + void __msan_partial_poison(const volatile void *data, void *shadow, + size_t size); + + /* Returns the offset of the first (at least partially) poisoned byte in the + 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); + + /* For testing: + __msan_set_expect_umr(1); + ... some buggy code ... + __msan_set_expect_umr(0); + The last line will verify that a UMR happened. */ + void __msan_set_expect_umr(int expect_umr); + + /* Change the value of keep_going flag. Non-zero value means don't terminate + program execution when an error is detected. This will not affect error in + 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 + format. */ + void __msan_print_shadow(const volatile void *x, size_t size); + + /* Print current function arguments shadow and origin to stdout in a + human-readable format. */ + void __msan_print_param_shadow(); + + /* Returns true if running under a dynamic tool (DynamoRio-based). */ + int __msan_has_dynamic_component(); + + /* Tell MSan about newly allocated memory (ex.: custom allocator). + Memory will be marked uninitialized, with origin at the call site. */ + void __msan_allocated_memory(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(); + + + /***********************************/ + /* 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) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/libsanitizer/interception/interception.h b/libsanitizer/interception/interception.h index c4c5026c7ed..71740c5c403 100644 --- a/libsanitizer/interception/interception.h +++ b/libsanitizer/interception/interception.h @@ -21,27 +21,20 @@ // These typedefs should be used only in the interceptor definitions to replace // the standard system types (e.g. SSIZE_T instead of ssize_t) -typedef __sanitizer::uptr SIZE_T; -typedef __sanitizer::sptr SSIZE_T; -typedef __sanitizer::sptr PTRDIFF_T; -typedef __sanitizer::s64 INTMAX_T; -// 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 defined(__APPLE__) || (defined(__linux__) && defined(__x86_64__)) -typedef __sanitizer::u64 OFF_T; -#else -typedef __sanitizer::uptr OFF_T; -#endif -typedef __sanitizer::u64 OFF64_T; +typedef __sanitizer::uptr SIZE_T; +typedef __sanitizer::sptr SSIZE_T; +typedef __sanitizer::sptr PTRDIFF_T; +typedef __sanitizer::s64 INTMAX_T; +typedef __sanitizer::OFF_T OFF_T; +typedef __sanitizer::OFF64_T OFF64_T; // How to add an interceptor: // Suppose you need to wrap/replace system function (generally, from libc): // int foo(const char *bar, double baz); // You'll need to: // 1) define INTERCEPTOR(int, foo, const char *bar, double baz) { ... } in -// your source file. +// your source file. See the notes below for cases when +// INTERCEPTOR_WITH_SUFFIX(...) should be used instead. // 2) Call "INTERCEPT_FUNCTION(foo)" prior to the first call of "foo". // INTERCEPT_FUNCTION(foo) evaluates to "true" iff the function was // intercepted successfully. @@ -55,15 +48,20 @@ typedef __sanitizer::u64 OFF64_T; // 3b) add DECLARE_REAL_AND_INTERCEPTOR(int, foo, const char*, double) // to a header file. -// Notes: 1. Things may not work properly if macro INTERCEPT(...) {...} or +// Notes: 1. Things may not work properly if macro INTERCEPTOR(...) {...} or // DECLARE_REAL(...) are located inside namespaces. -// 2. On Mac you can also use: "OVERRIDE_FUNCTION(foo, zoo);" to +// 2. On Mac you can also use: "OVERRIDE_FUNCTION(foo, zoo)" to // effectively redirect calls from "foo" to "zoo". In this case // you aren't required to implement // INTERCEPTOR(int, foo, const char *bar, double baz) {...} // but instead you'll have to add -// DEFINE_REAL(int, foo, const char *bar, double baz) in your +// DECLARE_REAL(int, foo, const char *bar, double baz) in your // source file (to define a pointer to overriden function). +// 3. Some Mac functions have symbol variants discriminated by +// additional suffixes, e.g. _$UNIX2003 (see +// https://developer.apple.com/library/mac/#releasenotes/Darwin/SymbolVariantsRelNotes/index.html +// for more details). To intercept such functions you need to use the +// INTERCEPTOR_WITH_SUFFIX(...) macro. // How it works: // To replace system functions on Linux we just need to declare functions @@ -73,6 +71,7 @@ typedef __sanitizer::u64 OFF64_T; // we intercept. To resolve this we declare our interceptors with __interceptor_ // prefix, and then make actual interceptors weak aliases to __interceptor_ // functions. +// // This is not so on Mac OS, where the two-level namespace makes // our replacement functions invisible to other libraries. This may be overcomed // using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared @@ -82,12 +81,43 @@ typedef __sanitizer::u64 OFF64_T; // preloaded before an executable using DYLD_INSERT_LIBRARIES, it routes all // the calls to interposed functions done through stubs to the wrapper // functions. +// As it's decided at compile time which functions are to be intercepted on Mac, +// INTERCEPT_FUNCTION() is effectively a no-op on this system. #if defined(__APPLE__) +#include // For __DARWIN_ALIAS_C(). + +// Just a pair of pointers. +struct interpose_substitution { + const uptr replacement; + const uptr original; +}; + +// For a function foo() create a global pair of pointers { wrap_foo, foo } in +// the __DATA,__interpose section. +// As a result all the calls to foo() will be routed to wrap_foo() at runtime. +#define INTERPOSER(func_name) __attribute__((used)) \ +const interpose_substitution substitution_##func_name[] \ + __attribute__((section("__DATA, __interpose"))) = { \ + { reinterpret_cast(WRAP(func_name)), \ + reinterpret_cast(func_name) } \ +} + +// For a function foo() and a wrapper function bar() create a global pair +// of pointers { bar, foo } in the __DATA,__interpose section. +// As a result all the calls to foo() will be routed to bar() at runtime. +#define INTERPOSER_2(func_name, wrapper_name) __attribute__((used)) \ +const interpose_substitution substitution_##func_name[] \ + __attribute__((section("__DATA, __interpose"))) = { \ + { reinterpret_cast(wrapper_name), \ + reinterpret_cast(func_name) } \ +} + # define WRAP(x) wrap_##x # define WRAPPER_NAME(x) "wrap_"#x # define INTERCEPTOR_ATTRIBUTE # define DECLARE_WRAPPER(ret_type, func, ...) + #elif defined(_WIN32) # if defined(_DLL) // DLL CRT # define WRAP(x) x @@ -98,7 +128,10 @@ typedef __sanitizer::u64 OFF64_T; # define WRAPPER_NAME(x) "wrap_"#x # define INTERCEPTOR_ATTRIBUTE # endif -# define DECLARE_WRAPPER(ret_type, func, ...) +# 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__); #else # define WRAP(x) __interceptor_ ## x # define WRAPPER_NAME(x) "__interceptor_" #x @@ -142,6 +175,7 @@ typedef __sanitizer::u64 OFF64_T; # define DEFINE_REAL(ret_type, func, ...) #endif +#if !defined(__APPLE__) #define INTERCEPTOR(ret_type, func, ...) \ DEFINE_REAL(ret_type, func, __VA_ARGS__) \ DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \ @@ -149,13 +183,36 @@ typedef __sanitizer::u64 OFF64_T; INTERCEPTOR_ATTRIBUTE \ ret_type WRAP(func)(__VA_ARGS__) +// We don't need INTERCEPTOR_WITH_SUFFIX on non-Darwin for now. +#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \ + INTERCEPTOR(ret_type, func, __VA_ARGS__) + +#else // __APPLE__ + +#define INTERCEPTOR_ZZZ(suffix, ret_type, func, ...) \ + extern "C" ret_type func(__VA_ARGS__) suffix; \ + extern "C" ret_type WRAP(func)(__VA_ARGS__); \ + INTERPOSER(func); \ + extern "C" INTERCEPTOR_ATTRIBUTE ret_type WRAP(func)(__VA_ARGS__) + +#define INTERCEPTOR(ret_type, func, ...) \ + INTERCEPTOR_ZZZ(/*no symbol variants*/, ret_type, func, __VA_ARGS__) + +#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \ + INTERCEPTOR_ZZZ(__DARWIN_ALIAS_C(func), ret_type, func, __VA_ARGS__) + +// Override |overridee| with |overrider|. +#define OVERRIDE_FUNCTION(overridee, overrider) \ + INTERPOSER_2(overridee, WRAP(overrider)) +#endif + #if defined(_WIN32) # define INTERCEPTOR_WINAPI(ret_type, func, ...) \ typedef ret_type (__stdcall *FUNC_TYPE(func))(__VA_ARGS__); \ namespace __interception { \ FUNC_TYPE(func) PTR_TO_REAL(func); \ } \ - DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \ + DECLARE_WRAPPER_WINAPI(ret_type, func, __VA_ARGS__) \ extern "C" \ INTERCEPTOR_ATTRIBUTE \ ret_type __stdcall WRAP(func)(__VA_ARGS__) @@ -181,8 +238,6 @@ typedef unsigned long uptr; // NOLINT # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX(func) #elif defined(__APPLE__) # include "interception_mac.h" -# define OVERRIDE_FUNCTION(old_func, new_func) \ - OVERRIDE_FUNCTION_MAC(old_func, new_func) # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func) #else // defined(_WIN32) # include "interception_win.h" diff --git a/libsanitizer/interception/interception_linux.cc b/libsanitizer/interception/interception_linux.cc index 4929a7fce49..0a8df474ab4 100644 --- a/libsanitizer/interception/interception_linux.cc +++ b/libsanitizer/interception/interception_linux.cc @@ -13,7 +13,6 @@ #ifdef __linux__ #include "interception.h" -#include // for NULL #include // for dlsym namespace __interception { @@ -22,6 +21,13 @@ bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, *func_addr = (uptr)dlsym(RTLD_NEXT, func_name); return real == wrapper; } + +#if !defined(__ANDROID__) // android does not have dlvsym +void *GetFuncAddrVer(const char *func_name, const char *ver) { + return dlvsym(RTLD_NEXT, func_name, ver); +} +#endif // !defined(__ANDROID__) + } // namespace __interception diff --git a/libsanitizer/interception/interception_linux.h b/libsanitizer/interception/interception_linux.h index 7940ef257c8..fbbfecbe037 100644 --- a/libsanitizer/interception/interception_linux.h +++ b/libsanitizer/interception/interception_linux.h @@ -23,6 +23,7 @@ namespace __interception { // returns true if a function with the given name was found. bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, uptr real, uptr wrapper); +void *GetFuncAddrVer(const char *func_name, const char *ver); } // namespace __interception #define INTERCEPT_FUNCTION_LINUX(func) \ @@ -31,5 +32,11 @@ bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, (::__interception::uptr)&(func), \ (::__interception::uptr)&WRAP(func)) +#if !defined(__ANDROID__) // android does not have dlvsym +#define INTERCEPT_FUNCTION_VER(func, symver) \ + ::__interception::real_##func = (func##_f)(unsigned long) \ + ::__interception::GetFuncAddrVer(#func, #symver) +#endif // !defined(__ANDROID__) + #endif // INTERCEPTION_LINUX_H #endif // __linux__ diff --git a/libsanitizer/lsan/Makefile.am b/libsanitizer/lsan/Makefile.am new file mode 100644 index 00000000000..5c8726fb35b --- /dev/null +++ b/libsanitizer/lsan/Makefile.am @@ -0,0 +1,60 @@ +AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir) + +# 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 +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros +AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS) +ACLOCAL_AMFLAGS = -I m4 + +noinst_LTLIBRARIES = libsanitizer_lsan.la + +sanitizer_lsan_files = \ + lsan_common.cc \ + lsan_common_linux.cc + +libsanitizer_lsan_la_SOURCES = $(sanitizer_lsan_files) + +# 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 +# friends when we are called from the top level Makefile. +AM_MAKEFLAGS = \ + "AR_FLAGS=$(AR_FLAGS)" \ + "CC_FOR_BUILD=$(CC_FOR_BUILD)" \ + "CFLAGS=$(CFLAGS)" \ + "CXXFLAGS=$(CXXFLAGS)" \ + "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \ + "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \ + "INSTALL=$(INSTALL)" \ + "INSTALL_DATA=$(INSTALL_DATA)" \ + "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \ + "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \ + "JC1FLAGS=$(JC1FLAGS)" \ + "LDFLAGS=$(LDFLAGS)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \ + "MAKE=$(MAKE)" \ + "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \ + "PICFLAG=$(PICFLAG)" \ + "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \ + "SHELL=$(SHELL)" \ + "RUNTESTFLAGS=$(RUNTESTFLAGS)" \ + "exec_prefix=$(exec_prefix)" \ + "infodir=$(infodir)" \ + "libdir=$(libdir)" \ + "prefix=$(prefix)" \ + "includedir=$(includedir)" \ + "AR=$(AR)" \ + "AS=$(AS)" \ + "LD=$(LD)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "NM=$(NM)" \ + "PICFLAG=$(PICFLAG)" \ + "RANLIB=$(RANLIB)" \ + "DESTDIR=$(DESTDIR)" + +MAKEOVERRIDES= + +## ################################################################ + diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in new file mode 100644 index 00000000000..e01f65b8663 --- /dev/null +++ b/libsanitizer/lsan/Makefile.in @@ -0,0 +1,514 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = lsan +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \ + $(top_srcdir)/../config/depstand.m4 \ + $(top_srcdir)/../config/lead-dot.m4 \ + $(top_srcdir)/../config/libstdc++-raw-cxx.m4 \ + $(top_srcdir)/../config/multi.m4 \ + $(top_srcdir)/../config/override.m4 \ + $(top_srcdir)/../ltoptions.m4 $(top_srcdir)/../ltsugar.m4 \ + $(top_srcdir)/../ltversion.m4 $(top_srcdir)/../lt~obsolete.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/../libtool.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libsanitizer_lsan_la_LIBADD = +am__objects_1 = lsan_common.lo lsan_common_linux.lo +am_libsanitizer_lsan_la_OBJECTS = $(am__objects_1) +libsanitizer_lsan_la_OBJECTS = $(am_libsanitizer_lsan_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/../depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libsanitizer_lsan_la_SOURCES) +ETAGS = etags +CTAGS = ctags +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +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 +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX_RAW_CXX_CXXFLAGS = @LIBSTDCXX_RAW_CXX_CXXFLAGS@ +LIBSTDCXX_RAW_CXX_LDFLAGS = @LIBSTDCXX_RAW_CXX_LDFLAGS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +enable_shared = @enable_shared@ +enable_static = @enable_static@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +multi_basedir = @multi_basedir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_noncanonical = @target_noncanonical@ +target_os = @target_os@ +target_vendor = @target_vendor@ +toolexecdir = @toolexecdir@ +toolexeclibdir = @toolexeclibdir@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir) + +# May be used by toolexeclibdir. +gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER) +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \ + -Wno-long-long -fPIC -fno-builtin -fno-exceptions \ + -fomit-frame-pointer -funwind-tables -fvisibility=hidden \ + -Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) +ACLOCAL_AMFLAGS = -I m4 +noinst_LTLIBRARIES = libsanitizer_lsan.la +sanitizer_lsan_files = \ + lsan_common.cc \ + lsan_common_linux.cc + +libsanitizer_lsan_la_SOURCES = $(sanitizer_lsan_files) + +# 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 +# friends when we are called from the top level Makefile. +AM_MAKEFLAGS = \ + "AR_FLAGS=$(AR_FLAGS)" \ + "CC_FOR_BUILD=$(CC_FOR_BUILD)" \ + "CFLAGS=$(CFLAGS)" \ + "CXXFLAGS=$(CXXFLAGS)" \ + "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \ + "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \ + "INSTALL=$(INSTALL)" \ + "INSTALL_DATA=$(INSTALL_DATA)" \ + "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \ + "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \ + "JC1FLAGS=$(JC1FLAGS)" \ + "LDFLAGS=$(LDFLAGS)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \ + "MAKE=$(MAKE)" \ + "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \ + "PICFLAG=$(PICFLAG)" \ + "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \ + "SHELL=$(SHELL)" \ + "RUNTESTFLAGS=$(RUNTESTFLAGS)" \ + "exec_prefix=$(exec_prefix)" \ + "infodir=$(infodir)" \ + "libdir=$(libdir)" \ + "prefix=$(prefix)" \ + "includedir=$(includedir)" \ + "AR=$(AR)" \ + "AS=$(AS)" \ + "LD=$(LD)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "NM=$(NM)" \ + "PICFLAG=$(PICFLAG)" \ + "RANLIB=$(RANLIB)" \ + "DESTDIR=$(DESTDIR)" + +MAKEOVERRIDES = +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lsan/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign lsan/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libsanitizer_lsan.la: $(libsanitizer_lsan_la_OBJECTS) $(libsanitizer_lsan_la_DEPENDENCIES) + $(CXXLINK) $(libsanitizer_lsan_la_OBJECTS) $(libsanitizer_lsan_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common_linux.Plo@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# 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. +.NOEXPORT: diff --git a/libsanitizer/lsan/libtool-version b/libsanitizer/lsan/libtool-version new file mode 100644 index 00000000000..204fdd2d8e5 --- /dev/null +++ b/libsanitizer/lsan/libtool-version @@ -0,0 +1,6 @@ +# This file is used to maintain libtool version info for libmudflap. See +# the libtool manual to understand the meaning of the fields. This is +# a separate file so that version updates don't involve re-running +# automake. +# CURRENT:REVISION:AGE +0:0:0 diff --git a/libsanitizer/lsan/lsan.cc b/libsanitizer/lsan/lsan.cc new file mode 100644 index 00000000000..500da50622c --- /dev/null +++ b/libsanitizer/lsan/lsan.cc @@ -0,0 +1,63 @@ +//=-- lsan.cc -------------------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Standalone LSan RTL. +// +//===----------------------------------------------------------------------===// + +#include "lsan.h" + +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "lsan_allocator.h" +#include "lsan_common.h" +#include "lsan_thread.h" + +namespace __lsan { + +static void InitializeCommonFlags() { + CommonFlags *cf = common_flags(); + cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); + cf->symbolize = true; + cf->strip_path_prefix = ""; + cf->fast_unwind_on_malloc = true; + cf->malloc_context_size = 30; + cf->detect_leaks = true; + cf->leak_check_at_exit = true; + + ParseCommonFlagsFromString(GetEnv("LSAN_OPTIONS")); +} + +void Init() { + static bool inited; + if (inited) + return; + inited = true; + SanitizerToolName = "LeakSanitizer"; + InitializeCommonFlags(); + InitializeAllocator(); + InitTlsSize(); + InitializeInterceptors(); + InitializeThreadRegistry(); + u32 tid = ThreadCreate(0, 0, true); + CHECK_EQ(tid, 0); + ThreadStart(tid, GetTid()); + SetCurrentThread(tid); + + // Start symbolizer process if necessary. + if (common_flags()->symbolize) { + getSymbolizer() + ->InitializeExternal(common_flags()->external_symbolizer_path); + } + + InitCommonLsan(); + if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) + Atexit(DoLeakCheck); +} + +} // namespace __lsan diff --git a/libsanitizer/lsan/lsan.h b/libsanitizer/lsan/lsan.h new file mode 100644 index 00000000000..18ff5da6281 --- /dev/null +++ b/libsanitizer/lsan/lsan.h @@ -0,0 +1,21 @@ +//=-- lsan.h --------------------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Private header for standalone LSan RTL. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" + +namespace __lsan { + +void Init(); +void InitializeInterceptors(); + +} // namespace __lsan diff --git a/libsanitizer/lsan/lsan_allocator.cc b/libsanitizer/lsan/lsan_allocator.cc new file mode 100644 index 00000000000..66af603e656 --- /dev/null +++ b/libsanitizer/lsan/lsan_allocator.cc @@ -0,0 +1,191 @@ +//=-- lsan_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 LeakSanitizer. +// See lsan_allocator.h for details. +// +//===----------------------------------------------------------------------===// + +#include "lsan_allocator.h" + +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "lsan_common.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. + ChunkTag tag : 2; + uptr requested_size : 54; + u32 stack_trace_id; +}; + +typedef SizeClassAllocator64 PrimaryAllocator; +typedef SizeClassAllocatorLocalCache AllocatorCache; +typedef LargeMmapAllocator<> SecondaryAllocator; +typedef CombinedAllocator Allocator; + +static Allocator allocator; +static THREADLOCAL AllocatorCache cache; + +void InitializeAllocator() { + allocator.Init(); +} + +void AllocatorThreadFinish() { + allocator.SwallowCache(&cache); +} + +static ChunkMetadata *Metadata(void *p) { + return reinterpret_cast(allocator.GetMetaData(p)); +} + +static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) { + if (!p) return; + ChunkMetadata *m = Metadata(p); + CHECK(m); + m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked; + m->stack_trace_id = StackDepotPut(stack.trace, stack.size); + m->requested_size = size; + atomic_store(reinterpret_cast(m), 1, memory_order_relaxed); +} + +static void RegisterDeallocation(void *p) { + if (!p) return; + ChunkMetadata *m = Metadata(p); + CHECK(m); + atomic_store(reinterpret_cast(m), 0, memory_order_relaxed); +} + +void *Allocate(const StackTrace &stack, uptr size, uptr alignment, + bool cleared) { + if (size == 0) + size = 1; + if (size > kMaxAllowedMallocSize) { + Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size); + return 0; + } + void *p = allocator.Allocate(&cache, size, alignment, cleared); + RegisterAllocation(stack, p, size); + return p; +} + +void Deallocate(void *p) { + RegisterDeallocation(p); + allocator.Deallocate(&cache, p); +} + +void *Reallocate(const StackTrace &stack, void *p, uptr new_size, + uptr alignment) { + RegisterDeallocation(p); + if (new_size > kMaxAllowedMallocSize) { + Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size); + allocator.Deallocate(&cache, p); + return 0; + } + p = allocator.Reallocate(&cache, p, new_size, alignment); + RegisterAllocation(stack, p, new_size); + return p; +} + +void GetAllocatorCacheRange(uptr *begin, uptr *end) { + *begin = (uptr)&cache; + *end = *begin + sizeof(cache); +} + +uptr GetMallocUsableSize(void *p) { + ChunkMetadata *m = Metadata(p); + if (!m) return 0; + return m->requested_size; +} + +///// Interface to the common LSan module. ///// + +void LockAllocator() { + allocator.ForceLock(); +} + +void UnlockAllocator() { + allocator.ForceUnlock(); +} + +void GetAllocatorGlobalRange(uptr *begin, uptr *end) { + *begin = (uptr)&allocator; + *end = *begin + sizeof(allocator); +} + +uptr PointsIntoChunk(void* p) { + uptr addr = reinterpret_cast(p); + uptr chunk = reinterpret_cast(allocator.GetBlockBeginFastLocked(p)); + if (!chunk) return 0; + // LargeMmapAllocator considers pointers to the meta-region of a chunk to be + // valid, but we don't want that. + if (addr < chunk) return 0; + ChunkMetadata *m = Metadata(reinterpret_cast(chunk)); + CHECK(m); + if (m->allocated && addr < chunk + m->requested_size) + return chunk; + return 0; +} + +uptr GetUserBegin(uptr chunk) { + return chunk; +} + +LsanMetadata::LsanMetadata(uptr chunk) { + metadata_ = Metadata(reinterpret_cast(chunk)); + CHECK(metadata_); +} + +bool LsanMetadata::allocated() const { + return reinterpret_cast(metadata_)->allocated; +} + +ChunkTag LsanMetadata::tag() const { + return reinterpret_cast(metadata_)->tag; +} + +void LsanMetadata::set_tag(ChunkTag value) { + reinterpret_cast(metadata_)->tag = value; +} + +uptr LsanMetadata::requested_size() const { + return reinterpret_cast(metadata_)->requested_size; +} + +u32 LsanMetadata::stack_trace_id() const { + return reinterpret_cast(metadata_)->stack_trace_id; +} + +void ForEachChunk(ForEachChunkCallback callback, void *arg) { + allocator.ForEachChunk(callback, arg); +} + +IgnoreObjectResult IgnoreObjectLocked(const void *p) { + void *chunk = allocator.GetBlockBegin(p); + if (!chunk || p < chunk) return kIgnoreObjectInvalid; + ChunkMetadata *m = Metadata(chunk); + CHECK(m); + if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) { + if (m->tag == kIgnored) + return kIgnoreObjectAlreadyIgnored; + m->tag = kIgnored; + return kIgnoreObjectSuccess; + } else { + return kIgnoreObjectInvalid; + } +} +} // namespace __lsan diff --git a/libsanitizer/lsan/lsan_allocator.h b/libsanitizer/lsan/lsan_allocator.h new file mode 100644 index 00000000000..61ea86572ef --- /dev/null +++ b/libsanitizer/lsan/lsan_allocator.h @@ -0,0 +1,37 @@ +//=-- lsan_allocator.h ----------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Allocator for standalone LSan. +// +//===----------------------------------------------------------------------===// + +#ifndef LSAN_ALLOCATOR_H +#define LSAN_ALLOCATOR_H + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_internal_defs.h" + +namespace __lsan { + +void *Allocate(const StackTrace &stack, uptr size, uptr alignment, + bool cleared); +void Deallocate(void *p); +void *Reallocate(const StackTrace &stack, void *p, uptr new_size, + uptr alignment); +uptr GetMallocUsableSize(void *p); + +template +void ForEachChunk(const Callable &callback); + +void GetAllocatorCacheRange(uptr *begin, uptr *end); +void AllocatorThreadFinish(); +void InitializeAllocator(); + +} // namespace __lsan + +#endif // LSAN_ALLOCATOR_H diff --git a/libsanitizer/lsan/lsan_common.cc b/libsanitizer/lsan/lsan_common.cc new file mode 100644 index 00000000000..ce82430f48b --- /dev/null +++ b/libsanitizer/lsan/lsan_common.cc @@ -0,0 +1,577 @@ +//=-- lsan_common.cc ------------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Implementation of common leak checking functionality. +// +//===----------------------------------------------------------------------===// + +#include "lsan_common.h" + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_placement_new.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. +BlockingMutex global_mutex(LINKER_INITIALIZED); + +THREADLOCAL int disable_counter; +bool DisabledInThisThread() { return disable_counter > 0; } + +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"); + } +} + +SuppressionContext *suppression_ctx; + +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(); + } + if (&__lsan_default_suppressions) + suppression_ctx->Parse(__lsan_default_suppressions()); +} + +void InitCommonLsan() { + InitializeFlags(); + InitializeSuppressions(); + InitializePlatformSpecificModules(); +} + +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Error() { return Red(); } + const char *Leak() { return Blue(); } + const char *End() { return Default(); } +}; + +static inline bool CanBeAHeapPointer(uptr p) { + // Since our heap is located in mmap-ed memory, we can assume a sensible lower + // bound on heap addresses. + const uptr kMinAddress = 4 * 4096; + if (p < kMinAddress) return false; +#ifdef __x86_64__ + // Accept only canonical form user-space addresses. + return ((p >> 47) == 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 +// (|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) { + const uptr alignment = flags()->pointer_alignment(); + if (flags()->log_pointers) + Report("Scanning %s range %p-%p.\n", region_type, begin, end); + uptr pp = begin; + if (pp % alignment) + pp = pp + alignment - pp % alignment; + for (; pp + sizeof(void *) <= end; pp += alignment) { // NOLINT + void *p = *reinterpret_cast(pp); + if (!CanBeAHeapPointer(reinterpret_cast(p))) continue; + uptr chunk = PointsIntoChunk(p); + if (!chunk) continue; + LsanMetadata m(chunk); + // Reachable beats ignored beats leaked. + if (m.tag() == kReachable) continue; + if (m.tag() == kIgnored && tag != kReachable) 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()); + if (frontier) + frontier->push_back(chunk); + } +} + +// Scans thread data (stacks and TLS) for heap pointers. +static void ProcessThreads(SuspendedThreadsList const &suspended_threads, + Frontier *frontier) { + InternalScopedBuffer registers(SuspendedThreadsList::RegisterCount()); + uptr registers_begin = reinterpret_cast(registers.data()); + uptr registers_end = registers_begin + registers.size(); + for (uptr i = 0; i < suspended_threads.thread_count(); i++) { + uptr os_id = static_cast(suspended_threads.GetThreadID(i)); + if (flags()->log_threads) Report("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, + &cache_begin, &cache_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); + continue; + } + uptr sp; + bool have_registers = + (suspended_threads.GetRegistersAndSP(i, registers.data(), &sp) == 0); + if (!have_registers) { + Report("Unable to get registers from thread %d.\n"); + // If unable to get SP, consider the entire stack to be reachable. + sp = stack_begin; + } + + if (flags()->use_registers && have_registers) + ScanRangeForPointers(registers_begin, registers_end, frontier, + "REGISTERS", kReachable); + + if (flags()->use_stacks) { + if (flags()->log_threads) + Report("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"); + } else { + // Shrink the stack range to ignore out-of-scope values. + stack_begin = sp; + } + ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK", + kReachable); + } + + if (flags()->use_tls) { + if (flags()->log_threads) Report("TLS at %p-%p.\n", tls_begin, tls_end); + if (cache_begin == cache_end) { + ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable); + } else { + // Because LSan should not be loaded with dlopen(), we can assume + // that allocator cache will be part of static TLS image. + CHECK_LE(tls_begin, cache_begin); + CHECK_GE(tls_end, cache_end); + if (tls_begin < cache_begin) + ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS", + kReachable); + if (tls_end > cache_end) + ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable); + } + } + } +} + +static void FloodFillTag(Frontier *frontier, ChunkTag tag) { + while (frontier->size()) { + uptr next_chunk = frontier->back(); + frontier->pop_back(); + LsanMetadata m(next_chunk); + ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier, + "HEAP", tag); + } +} + +// ForEachChunk callback. If the chunk is marked as leaked, marks all chunks +// which are reachable from it as indirectly leaked. +static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) { + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (m.allocated() && m.tag() != kReachable) { + ScanRangeForPointers(chunk, chunk + m.requested_size(), + /* frontier */ 0, "HEAP", kIndirectlyLeaked); + } +} + +// ForEachChunk callback. If chunk is marked as ignored, adds its address to +// frontier. +static void CollectIgnoredCb(uptr chunk, void *arg) { + CHECK(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (m.allocated() && m.tag() == kIgnored) + reinterpret_cast(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()); + + if (flags()->use_globals) + ProcessGlobalRegions(&frontier); + ProcessThreads(suspended_threads, &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. + 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 */); +} + +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, common_flags()->symbolize, + common_flags()->strip_path_prefix, 0); +} + +// ForEachChunk callback. Aggregates unreachable chunks into a LeakReport. +static void CollectLeaksCb(uptr chunk, void *arg) { + CHECK(arg); + LeakReport *leak_report = reinterpret_cast(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (!m.allocated()) return; + if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { + uptr resolution = flags()->resolution; + 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()); + } else { + leak_report->Add(m.stack_trace_id(), m.requested_size(), m.tag()); + } + } +} + +// 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); + } +} + +static void PrintMatchedSuppressions() { + InternalMmapVector matched(1); + suppression_ctx->GetMatched(&matched); + if (!matched.size()) + return; + const char *line = "-----------------------------------------------------"; + Printf("%s\n", 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(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 { + bool success; + LeakReport leak_report; +}; + +static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads, + void *arg) { + DoLeakCheckParam *param = reinterpret_cast(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(); + param->success = true; +} + +void DoLeakCheck() { + EnsureMainThreadIDIsCorrect(); + BlockingMutexLock l(&global_mutex); + static bool already_done; + if (already_done) return; + already_done = true; + if (&__lsan_is_turned_off && __lsan_is_turned_off()) + return; + + DoLeakCheckParam param; + param.success = false; + LockThreadRegistry(); + LockAllocator(); + StopTheWorld(DoLeakCheckCallback, ¶m); + UnlockAllocator(); + UnlockThreadRegistry(); + + if (!param.success) { + Report("LeakSanitizer has encountered a fatal error.\n"); + Die(); + } + uptr have_unsuppressed = param.leak_report.ApplySuppressions(); + if (have_unsuppressed) { + Decorator d; + Printf("\n" + "=================================================================" + "\n"); + Printf("%s", d.Error()); + Report("ERROR: LeakSanitizer: detected memory leaks\n"); + Printf("%s", d.End()); + param.leak_report.PrintLargest(flags()->max_leaks); + } + if (have_unsuppressed || (flags()->verbosity >= 1)) { + PrintMatchedSuppressions(); + param.leak_report.PrintSummary(); + } + if (have_unsuppressed && flags()->exitcode) + internal__exit(flags()->exitcode); +} + +static Suppression *GetSuppressionForAddr(uptr addr) { + static const uptr kMaxAddrFrames = 16; + InternalScopedBuffer addr_frames(kMaxAddrFrames); + for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo(); + uptr addr_frames_num = + getSymbolizer()->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)) + return s; + } + return 0; +} + +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])); + if (s) return s; + } + return 0; +} + +///// 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. +// 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) { + CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked); + bool is_directly_leaked = (tag == kDirectlyLeaked); + for (uptr 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; + } + 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); +} + +static bool LeakComparator(const Leak &leak1, const Leak &leak2) { + if (leak1.is_directly_leaked == leak2.is_directly_leaked) + return leak1.total_size > leak2.total_size; + else + return leak1.is_directly_leaked; +} + +void LeakReport::PrintLargest(uptr num_leaks_to_print) { + CHECK(leaks_.size() <= kMaxLeaksConsidered); + Printf("\n"); + if (leaks_.size() == kMaxLeaksConsidered) + Printf("Too many leaks! Only the first %zu leaks encountered will be " + "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); + InternalSort(&leaks_, leaks_.size(), LeakComparator); + uptr leaks_printed = 0; + Decorator d; + 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); + Printf("\n"); + leaks_printed++; + if (leaks_printed == num_leaks_to_print) break; + } + if (leaks_printed < unsuppressed_count) { + uptr remaining = unsuppressed_count - leaks_printed; + Printf("Omitting %zu more leak(s).\n", remaining); + } +} + +void LeakReport::PrintSummary() { + CHECK(leaks_.size() <= kMaxLeaksConsidered); + uptr bytes = 0, allocations = 0; + for (uptr i = 0; i < leaks_.size(); i++) { + if (leaks_[i].is_suppressed) continue; + bytes += leaks_[i].total_size; + allocations += leaks_[i].hit_count; + } + const int kMaxSummaryLength = 128; + InternalScopedBuffer summary(kMaxSummaryLength); + internal_snprintf(summary.data(), kMaxSummaryLength, + "LeakSanitizer: %zu byte(s) leaked in %zu allocation(s).", + bytes, allocations); + __sanitizer_report_error_summary(summary.data()); +} + +uptr LeakReport::ApplySuppressions() { + uptr unsuppressed_count = 0; + 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; + leaks_[i].is_suppressed = true; + } else { + unsuppressed_count++; + } + } + return unsuppressed_count; +} +} // namespace __lsan +#endif // CAN_SANITIZE_LEAKS + +using namespace __lsan; // NOLINT + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_ignore_object(const void *p) { +#if CAN_SANITIZE_LEAKS + // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not + // 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(): " + "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 +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_disable() { +#if CAN_SANITIZE_LEAKS + __lsan::disable_counter++; +#endif +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_enable() { +#if CAN_SANITIZE_LEAKS + if (!__lsan::disable_counter) { + Report("Unmatched call to __lsan_enable().\n"); + Die(); + } + __lsan::disable_counter--; +#endif +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __lsan_do_leak_check() { +#if CAN_SANITIZE_LEAKS + if (common_flags()->detect_leaks) + __lsan::DoLeakCheck(); +#endif // CAN_SANITIZE_LEAKS +} + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __lsan_is_turned_off() { + return 0; +} +#endif +} // extern "C" diff --git a/libsanitizer/lsan/lsan_common.h b/libsanitizer/lsan/lsan_common.h new file mode 100644 index 00000000000..7906ecb9177 --- /dev/null +++ b/libsanitizer/lsan/lsan_common.h @@ -0,0 +1,174 @@ +//=-- lsan_common.h -------------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Private LSan header. +// +//===----------------------------------------------------------------------===// + +#ifndef LSAN_COMMON_H +#define LSAN_COMMON_H + +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_platform.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +#if SANITIZER_LINUX && defined(__x86_64__) +#define CAN_SANITIZE_LEAKS 1 +#else +#define CAN_SANITIZE_LEAKS 0 +#endif + +namespace __lsan { + +// Chunk tags. +enum ChunkTag { + kDirectlyLeaked = 0, // default + kIndirectlyLeaked = 1, + kReachable = 2, + kIgnored = 3 +}; + +struct Flags { + 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; } + +struct Leak { + uptr hit_count; + uptr total_size; + u32 stack_trace_id; + bool is_directly_leaked; + bool is_suppressed; +}; + +// 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); + void PrintSummary(); + bool IsEmpty() { return leaks_.size() == 0; } + uptr ApplySuppressions(); + private: + InternalMmapVector leaks_; +}; + +typedef InternalMmapVector Frontier; + +// Platform-specific functions. +void InitializePlatformSpecificModules(); +void ProcessGlobalRegions(Frontier *frontier); +void ProcessPlatformSpecificAllocations(Frontier *frontier); + +void ScanRangeForPointers(uptr begin, uptr end, + Frontier *frontier, + const char *region_type, ChunkTag tag); + +enum IgnoreObjectResult { + kIgnoreObjectSuccess, + kIgnoreObjectAlreadyIgnored, + kIgnoreObjectInvalid +}; + +// Functions called from the parent tool. +void InitCommonLsan(); +void DoLeakCheck(); +bool DisabledInThisThread(); + +// The following must be implemented in the parent tool. + +void ForEachChunk(ForEachChunkCallback callback, void *arg); +// Returns the address range occupied by the global allocator object. +void GetAllocatorGlobalRange(uptr *begin, uptr *end); +// Wrappers for allocator's ForceLock()/ForceUnlock(). +void LockAllocator(); +void UnlockAllocator(); +// Wrappers for ThreadRegistry access. +void LockThreadRegistry(); +void UnlockThreadRegistry(); +bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, + uptr *cache_begin, uptr *cache_end); +// If called from the main thread, updates the main thread's TID in the thread +// registry. We need this to handle processes that fork() without a subsequent +// exec(), which invalidates the recorded TID. To update it, we must call +// gettid() from the main thread. Our solution is to call this function before +// leak checking and also before every call to pthread_create() (to handle cases +// where leak checking is initiated from a non-main thread). +void EnsureMainThreadIDIsCorrect(); +// If p points into a chunk that has been allocated to the user, returns its +// user-visible address. Otherwise, returns 0. +uptr PointsIntoChunk(void *p); +// Returns address of user-visible chunk contained in this allocator chunk. +uptr GetUserBegin(uptr chunk); +// Helper for __lsan_ignore_object(). +IgnoreObjectResult IgnoreObjectLocked(const void *p); +// Wrapper for chunk metadata operations. +class LsanMetadata { + public: + // Constructor accepts address of user-visible chunk. + explicit LsanMetadata(uptr chunk); + bool allocated() const; + ChunkTag tag() const; + void set_tag(ChunkTag value); + uptr requested_size() const; + u32 stack_trace_id() const; + private: + void *metadata_; +}; + +} // namespace __lsan + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __lsan_is_turned_off(); + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +const char *__lsan_default_suppressions(); +} // extern "C" + +#endif // LSAN_COMMON_H diff --git a/libsanitizer/lsan/lsan_common_linux.cc b/libsanitizer/lsan/lsan_common_linux.cc new file mode 100644 index 00000000000..80d2459a9ad --- /dev/null +++ b/libsanitizer/lsan/lsan_common_linux.cc @@ -0,0 +1,139 @@ +//=-- lsan_common_linux.cc ------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Implementation of common leak checking functionality. Linux-specific code. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#include "lsan_common.h" + +#if CAN_SANITIZE_LEAKS && SANITIZER_LINUX +#include + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_linux.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + +namespace __lsan { + +static const char kLinkerName[] = "ld"; +// 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 bool IsLinker(const char* full_name) { + return LibraryNameIs(full_name, kLinkerName); +} + +void InitializePlatformSpecificModules() { + internal_memset(linker_placeholder, 0, sizeof(linker_placeholder)); + uptr num_matches = GetListOfModules( + reinterpret_cast(linker_placeholder), 2, IsLinker); + if (num_matches == 1) { + linker = reinterpret_cast(linker_placeholder); + return; + } + if (num_matches == 0) + Report("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; +} + +static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, + void *data) { + Frontier *frontier = reinterpret_cast(data); + for (uptr j = 0; j < info->dlpi_phnum; j++) { + const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]); + // We're looking for .data and .bss sections, which reside in writeable, + // loadable segments. + if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) || + (phdr->p_memsz == 0)) + continue; + uptr begin = info->dlpi_addr + phdr->p_vaddr; + uptr end = begin + phdr->p_memsz; + uptr allocator_begin = 0, allocator_end = 0; + GetAllocatorGlobalRange(&allocator_begin, &allocator_end); + if (begin <= allocator_begin && allocator_begin < end) { + CHECK_LE(allocator_begin, allocator_end); + CHECK_LT(allocator_end, end); + if (begin < allocator_begin) + ScanRangeForPointers(begin, allocator_begin, frontier, "GLOBAL", + kReachable); + if (allocator_end < end) + ScanRangeForPointers(allocator_end, end, frontier, "GLOBAL", + kReachable); + } else { + ScanRangeForPointers(begin, end, frontier, "GLOBAL", kReachable); + } + } + return 0; +} + +// 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. + 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); + // The top frame is our malloc/calloc/etc. The next frame is the caller. + if (size >= 2) + return trace[1]; + return 0; +} + +struct ProcessPlatformAllocParam { + Frontier *frontier; + StackDepotReverseMap *stack_depot_reverse_map; +}; + +// ForEachChunk callback. Identifies unreachable chunks which must be treated as +// reachable. Marks them as reachable and adds them to the frontier. +static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) { + CHECK(arg); + ProcessPlatformAllocParam *param = + reinterpret_cast(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (m.allocated() && m.tag() != kReachable) { + u32 stack_id = m.stack_trace_id(); + uptr caller_pc = 0; + if (stack_id > 0) + caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map); + // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark + // it as reachable, as we can't properly report its allocation stack anyway. + if (caller_pc == 0 || linker->containsAddress(caller_pc)) { + m.set_tag(kReachable); + param->frontier->push_back(chunk); + } + } +} + +// Handles dynamically allocated TLS blocks by treating all chunks allocated +// from ld-linux.so as reachable. +void ProcessPlatformSpecificAllocations(Frontier *frontier) { + if (!flags()->use_tls) return; + if (!linker) return; + StackDepotReverseMap stack_depot_reverse_map; + ProcessPlatformAllocParam arg = {frontier, &stack_depot_reverse_map}; + ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg); +} + +} // namespace __lsan +#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX diff --git a/libsanitizer/lsan/lsan_interceptors.cc b/libsanitizer/lsan/lsan_interceptors.cc new file mode 100644 index 00000000000..40ddc7773e2 --- /dev/null +++ b/libsanitizer/lsan/lsan_interceptors.cc @@ -0,0 +1,279 @@ +//=-- lsan_interceptors.cc ------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Interceptors for standalone LSan. +// +//===----------------------------------------------------------------------===// + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_linux.h" +#include "sanitizer_common/sanitizer_platform_limits_posix.h" +#include "lsan.h" +#include "lsan_allocator.h" +#include "lsan_thread.h" + +using namespace __lsan; + +extern "C" { +int pthread_attr_init(void *attr); +int pthread_attr_destroy(void *attr); +int pthread_attr_getdetachstate(void *attr, int *v); +int pthread_key_create(unsigned *key, void (*destructor)(void* v)); +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(); \ + } \ + GetStackTrace(&stack, __sanitizer::common_flags()->malloc_context_size, \ + StackTrace::GetCurrentPc(), \ + GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \ + } + +///// Malloc/free interceptors. ///// + +const bool kAlwaysClearMemory = true; + +namespace std { + struct nothrow_t; +} + +INTERCEPTOR(void*, malloc, uptr size) { + Init(); + GET_STACK_TRACE; + return Allocate(stack, size, 1, kAlwaysClearMemory); +} + +INTERCEPTOR(void, free, void *p) { + Init(); + Deallocate(p); +} + +INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; + Init(); + GET_STACK_TRACE; + size *= nmemb; + return Allocate(stack, size, 1, true); +} + +INTERCEPTOR(void*, realloc, void *q, uptr size) { + Init(); + GET_STACK_TRACE; + return Reallocate(stack, q, size, 1); +} + +INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { + Init(); + GET_STACK_TRACE; + return Allocate(stack, size, alignment, kAlwaysClearMemory); +} + +INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { + Init(); + GET_STACK_TRACE; + *memptr = Allocate(stack, size, alignment, kAlwaysClearMemory); + // FIXME: Return ENOMEM if user requested more than max alloc size. + return 0; +} + +INTERCEPTOR(void*, valloc, uptr size) { + Init(); + GET_STACK_TRACE; + if (size == 0) + size = GetPageSizeCached(); + return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); +} + +INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { + Init(); + return GetMallocUsableSize(ptr); +} + +struct fake_mallinfo { + int x[10]; +}; + +INTERCEPTOR(struct fake_mallinfo, mallinfo, void) { + struct fake_mallinfo res; + internal_memset(&res, 0, sizeof(res)); + return res; +} + +INTERCEPTOR(int, mallopt, int cmd, int value) { + return -1; +} + +INTERCEPTOR(void*, pvalloc, uptr size) { + Init(); + GET_STACK_TRACE; + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); +} + +INTERCEPTOR(void, cfree, void *p) ALIAS("free"); + +#define OPERATOR_NEW_BODY \ + Init(); \ + GET_STACK_TRACE; \ + return Allocate(stack, size, 1, kAlwaysClearMemory); + +INTERCEPTOR_ATTRIBUTE +void *operator new(uptr size) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE +void *operator new[](uptr size) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE +void *operator new(uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE +void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } + +#define OPERATOR_DELETE_BODY \ + Init(); \ + Deallocate(ptr); + +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const &) { + OPERATOR_DELETE_BODY; +} + +// 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"); + +///// Thread initialization and finalization. ///// + +static unsigned g_thread_finalize_key; + +static void thread_finalize(void *v) { + uptr iter = (uptr)v; + if (iter > 1) { + if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) { + Report("LeakSanitizer: failed to set thread key.\n"); + Die(); + } + return; + } + ThreadFinish(); +} + +struct ThreadParam { + void *(*callback)(void *arg); + void *param; + atomic_uintptr_t tid; +}; + +// PTHREAD_DESTRUCTOR_ITERATIONS from glibc. +const uptr kPthreadDestructorIterations = 4; + +extern "C" void *__lsan_thread_start_func(void *arg) { + ThreadParam *p = (ThreadParam*)arg; + void* (*callback)(void *arg) = p->callback; + void *param = p->param; + // 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)) { + 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()); + return callback(param); +} + +INTERCEPTOR(int, pthread_create, void *th, void *attr, + void *(*callback)(void *), void *param) { + Init(); + EnsureMainThreadIDIsCorrect(); + __sanitizer_pthread_attr_t myattr; + if (attr == 0) { + pthread_attr_init(&myattr); + attr = &myattr; + } + AdjustStackSizeLinux(attr, 0); + int detached = 0; + pthread_attr_getdetachstate(attr, &detached); + ThreadParam p; + p.callback = callback; + p.param = param; + atomic_store(&p.tid, 0, memory_order_relaxed); + int res = REAL(pthread_create)(th, attr, __lsan_thread_start_func, &p); + if (res == 0) { + int tid = ThreadCreate(GetCurrentThread(), *(uptr *)th, detached); + CHECK_NE(tid, 0); + atomic_store(&p.tid, tid, memory_order_release); + while (atomic_load(&p.tid, memory_order_acquire) != 0) + internal_sched_yield(); + } + if (attr == &myattr) + pthread_attr_destroy(&myattr); + return res; +} + +INTERCEPTOR(int, pthread_join, void *th, void **ret) { + Init(); + int tid = ThreadTid((uptr)th); + int res = REAL(pthread_join)(th, ret); + if (res == 0) + ThreadJoin(tid); + return res; +} + +namespace __lsan { + +void InitializeInterceptors() { + INTERCEPT_FUNCTION(malloc); + INTERCEPT_FUNCTION(free); + INTERCEPT_FUNCTION(cfree); + INTERCEPT_FUNCTION(calloc); + INTERCEPT_FUNCTION(realloc); + INTERCEPT_FUNCTION(memalign); + INTERCEPT_FUNCTION(posix_memalign); + INTERCEPT_FUNCTION(__libc_memalign); + INTERCEPT_FUNCTION(valloc); + INTERCEPT_FUNCTION(pvalloc); + INTERCEPT_FUNCTION(malloc_usable_size); + INTERCEPT_FUNCTION(mallinfo); + INTERCEPT_FUNCTION(mallopt); + INTERCEPT_FUNCTION(pthread_create); + INTERCEPT_FUNCTION(pthread_join); + + if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { + Report("LeakSanitizer: failed to create thread key.\n"); + Die(); + } +} + +} // namespace __lsan diff --git a/libsanitizer/lsan/lsan_thread.cc b/libsanitizer/lsan/lsan_thread.cc new file mode 100644 index 00000000000..c260972cb47 --- /dev/null +++ b/libsanitizer/lsan/lsan_thread.cc @@ -0,0 +1,154 @@ +//=-- lsan_thread.cc ------------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// See lsan_thread.h for details. +// +//===----------------------------------------------------------------------===// + +#include "lsan_thread.h" + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_thread_registry.h" +#include "lsan_allocator.h" + +namespace __lsan { + +const u32 kInvalidTid = (u32) -1; + +static ThreadRegistry *thread_registry; +static THREADLOCAL u32 current_thread_tid = kInvalidTid; + +static ThreadContextBase *CreateThreadContext(u32 tid) { + void *mem = MmapOrDie(sizeof(ThreadContext), "ThreadContext"); + return new(mem) ThreadContext(tid); +} + +static const uptr kMaxThreads = 1 << 13; +static const uptr kThreadQuarantineSize = 64; + +void InitializeThreadRegistry() { + static char thread_registry_placeholder[sizeof(ThreadRegistry)] ALIGNED(64); + thread_registry = new(thread_registry_placeholder) + ThreadRegistry(CreateThreadContext, kMaxThreads, kThreadQuarantineSize); +} + +u32 GetCurrentThread() { + return current_thread_tid; +} + +void SetCurrentThread(u32 tid) { + current_thread_tid = tid; +} + +ThreadContext::ThreadContext(int tid) + : ThreadContextBase(tid), + stack_begin_(0), + stack_end_(0), + cache_begin_(0), + cache_end_(0), + tls_begin_(0), + tls_end_(0) {} + +struct OnStartedArgs { + uptr stack_begin, stack_end, + cache_begin, cache_end, + tls_begin, tls_end; +}; + +void ThreadContext::OnStarted(void *arg) { + OnStartedArgs *args = reinterpret_cast(arg); + stack_begin_ = args->stack_begin; + stack_end_ = args->stack_end; + tls_begin_ = args->tls_begin; + tls_end_ = args->tls_end; + cache_begin_ = args->cache_begin; + cache_end_ = args->cache_end; +} + +void ThreadContext::OnFinished() { + AllocatorThreadFinish(); +} + +u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) { + return thread_registry->CreateThread(user_id, detached, parent_tid, + /* arg */ 0); +} + +void ThreadStart(u32 tid, uptr os_id) { + OnStartedArgs args; + uptr stack_size = 0; + uptr tls_size = 0; + GetThreadStackAndTls(tid == 0, &args.stack_begin, &stack_size, + &args.tls_begin, &tls_size); + args.stack_end = args.stack_begin + stack_size; + args.tls_end = args.tls_begin + tls_size; + GetAllocatorCacheRange(&args.cache_begin, &args.cache_end); + thread_registry->StartThread(tid, os_id, &args); +} + +void ThreadFinish() { + thread_registry->FinishThread(GetCurrentThread()); +} + +ThreadContext *CurrentThreadContext() { + if (!thread_registry) return 0; + if (GetCurrentThread() == kInvalidTid) + return 0; + // No lock needed when getting current thread. + return (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread()); +} + +static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) { + uptr uid = (uptr)arg; + if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) { + return true; + } + return false; +} + +u32 ThreadTid(uptr uid) { + return thread_registry->FindThread(FindThreadByUid, (void*)uid); +} + +void ThreadJoin(u32 tid) { + CHECK_NE(tid, kInvalidTid); + thread_registry->JoinThread(tid, /* arg */0); +} + +void EnsureMainThreadIDIsCorrect() { + if (GetCurrentThread() == 0) + CurrentThreadContext()->os_id = GetTid(); +} + +///// Interface to the common LSan module. ///// + +bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, + uptr *cache_begin, uptr *cache_end) { + ThreadContext *context = static_cast( + thread_registry->FindThreadContextByOsIDLocked(os_id)); + if (!context) return false; + *stack_begin = context->stack_begin(); + *stack_end = context->stack_end(); + *tls_begin = context->tls_begin(); + *tls_end = context->tls_end(); + *cache_begin = context->cache_begin(); + *cache_end = context->cache_end(); + return true; +} + +void LockThreadRegistry() { + thread_registry->Lock(); +} + +void UnlockThreadRegistry() { + thread_registry->Unlock(); +} + +} // namespace __lsan diff --git a/libsanitizer/lsan/lsan_thread.h b/libsanitizer/lsan/lsan_thread.h new file mode 100644 index 00000000000..cd13fdb5c52 --- /dev/null +++ b/libsanitizer/lsan/lsan_thread.h @@ -0,0 +1,51 @@ +//=-- lsan_thread.h -------------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// Thread registry for standalone LSan. +// +//===----------------------------------------------------------------------===// + +#ifndef LSAN_THREAD_H +#define LSAN_THREAD_H + +#include "sanitizer_common/sanitizer_thread_registry.h" + +namespace __lsan { + +class ThreadContext : public ThreadContextBase { + public: + explicit ThreadContext(int tid); + void OnStarted(void *arg); + void OnFinished(); + uptr stack_begin() { return stack_begin_; } + uptr stack_end() { return stack_end_; } + uptr tls_begin() { return tls_begin_; } + uptr tls_end() { return tls_end_; } + uptr cache_begin() { return cache_begin_; } + uptr cache_end() { return cache_end_; } + private: + uptr stack_begin_, stack_end_, + cache_begin_, cache_end_, + tls_begin_, tls_end_; +}; + +void InitializeThreadRegistry(); + +void ThreadStart(u32 tid, uptr os_id); +void ThreadFinish(); +u32 ThreadCreate(u32 tid, uptr uid, bool detached); +void ThreadJoin(u32 tid); +u32 ThreadTid(uptr uid); + +u32 GetCurrentThread(); +void SetCurrentThread(u32 tid); +ThreadContext *CurrentThreadContext(); +void EnsureMainThreadIDIsCorrect(); +} // namespace __lsan + +#endif // LSAN_THREAD_H diff --git a/libsanitizer/merge.sh b/libsanitizer/merge.sh index 23748a701bb..aae8eb41f7d 100755 --- a/libsanitizer/merge.sh +++ b/libsanitizer/merge.sh @@ -66,6 +66,7 @@ CUR_REV=$(get_current_rev) echo Current upstream revision: $CUR_REV merge include/sanitizer include/sanitizer merge lib/asan asan +merge lib/lsan lsan merge lib/tsan/rtl tsan merge lib/sanitizer_common sanitizer_common merge lib/interception interception diff --git a/libsanitizer/sanitizer_common/Makefile.am b/libsanitizer/sanitizer_common/Makefile.am index c53a3c6bfc5..6cd51187ccb 100644 --- a/libsanitizer/sanitizer_common/Makefile.am +++ b/libsanitizer/sanitizer_common/Makefile.am @@ -11,23 +11,27 @@ ACLOCAL_AMFLAGS = -I m4 noinst_LTLIBRARIES = libsanitizer_common.la sanitizer_common_files = \ - sanitizer_allocator.cc \ - sanitizer_common.cc \ - sanitizer_flags.cc \ - sanitizer_libc.cc \ - sanitizer_linux.cc \ - sanitizer_mac.cc \ + sanitizer_allocator.cc \ + sanitizer_common.cc \ + sanitizer_common_libcdep.cc \ + sanitizer_flags.cc \ + sanitizer_libc.cc \ + sanitizer_linux.cc \ + sanitizer_linux_libcdep.cc \ + sanitizer_mac.cc \ + sanitizer_platform_limits_linux.cc \ sanitizer_platform_limits_posix.cc \ - sanitizer_posix.cc \ - sanitizer_printf.cc \ - sanitizer_stackdepot.cc \ - sanitizer_stacktrace.cc \ - sanitizer_symbolizer.cc \ - sanitizer_symbolizer_itanium.cc \ - sanitizer_symbolizer_linux.cc \ - sanitizer_symbolizer_mac.cc \ - sanitizer_symbolizer_win.cc \ - sanitizer_win.cc + sanitizer_posix.cc \ + sanitizer_posix_libcdep.cc \ + sanitizer_printf.cc \ + sanitizer_stackdepot.cc \ + sanitizer_stacktrace.cc \ + sanitizer_stoptheworld_linux_libcdep.cc \ + sanitizer_suppressions.cc \ + sanitizer_symbolizer_posix_libcdep.cc \ + sanitizer_symbolizer_win.cc \ + sanitizer_thread_registry.cc \ + sanitizer_win.cc libsanitizer_common_la_SOURCES = $(sanitizer_common_files) diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in index a3030836d35..68c9849e169 100644 --- a/libsanitizer/sanitizer_common/Makefile.in +++ b/libsanitizer/sanitizer_common/Makefile.in @@ -56,12 +56,17 @@ CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libsanitizer_common_la_LIBADD = am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \ - sanitizer_flags.lo sanitizer_libc.lo sanitizer_linux.lo \ - sanitizer_mac.lo sanitizer_platform_limits_posix.lo \ - sanitizer_posix.lo sanitizer_printf.lo sanitizer_stackdepot.lo \ - sanitizer_stacktrace.lo sanitizer_symbolizer.lo \ - sanitizer_symbolizer_itanium.lo sanitizer_symbolizer_linux.lo \ - sanitizer_symbolizer_mac.lo sanitizer_symbolizer_win.lo \ + sanitizer_common_libcdep.lo sanitizer_flags.lo \ + sanitizer_libc.lo sanitizer_linux.lo \ + sanitizer_linux_libcdep.lo sanitizer_mac.lo \ + sanitizer_platform_limits_linux.lo \ + sanitizer_platform_limits_posix.lo sanitizer_posix.lo \ + sanitizer_posix_libcdep.lo sanitizer_printf.lo \ + sanitizer_stackdepot.lo sanitizer_stacktrace.lo \ + sanitizer_stoptheworld_linux_libcdep.lo \ + sanitizer_suppressions.lo \ + sanitizer_symbolizer_posix_libcdep.lo \ + sanitizer_symbolizer_win.lo sanitizer_thread_registry.lo \ sanitizer_win.lo am_libsanitizer_common_la_OBJECTS = $(am__objects_1) libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS) @@ -223,23 +228,27 @@ AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \ ACLOCAL_AMFLAGS = -I m4 noinst_LTLIBRARIES = libsanitizer_common.la sanitizer_common_files = \ - sanitizer_allocator.cc \ - sanitizer_common.cc \ - sanitizer_flags.cc \ - sanitizer_libc.cc \ - sanitizer_linux.cc \ - sanitizer_mac.cc \ + sanitizer_allocator.cc \ + sanitizer_common.cc \ + sanitizer_common_libcdep.cc \ + sanitizer_flags.cc \ + sanitizer_libc.cc \ + sanitizer_linux.cc \ + sanitizer_linux_libcdep.cc \ + sanitizer_mac.cc \ + sanitizer_platform_limits_linux.cc \ sanitizer_platform_limits_posix.cc \ - sanitizer_posix.cc \ - sanitizer_printf.cc \ - sanitizer_stackdepot.cc \ - sanitizer_stacktrace.cc \ - sanitizer_symbolizer.cc \ - sanitizer_symbolizer_itanium.cc \ - sanitizer_symbolizer_linux.cc \ - sanitizer_symbolizer_mac.cc \ - sanitizer_symbolizer_win.cc \ - sanitizer_win.cc + sanitizer_posix.cc \ + sanitizer_posix_libcdep.cc \ + sanitizer_printf.cc \ + sanitizer_stackdepot.cc \ + sanitizer_stacktrace.cc \ + sanitizer_stoptheworld_linux_libcdep.cc \ + sanitizer_suppressions.cc \ + sanitizer_symbolizer_posix_libcdep.cc \ + sanitizer_symbolizer_win.cc \ + sanitizer_thread_registry.cc \ + sanitizer_win.cc libsanitizer_common_la_SOURCES = $(sanitizer_common_files) @@ -336,20 +345,24 @@ distclean-compile: @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_flags.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libc.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_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_stackdepot.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_itanium.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_linux.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_mac.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_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_win.Plo@am__quote@ .cc.o: diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.cc b/libsanitizer/sanitizer_common/sanitizer_allocator.cc index a54de9d6f9a..9d38e9429ac 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.cc +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.cc @@ -7,44 +7,103 @@ // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. -// This allocator that is used inside run-times. +// This allocator is used inside run-times. //===----------------------------------------------------------------------===// +#include "sanitizer_allocator.h" +#include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" - -// FIXME: We should probably use more low-level allocator that would -// mmap some pages and split them into chunks to fulfill requests. -#if defined(__linux__) && !defined(__ANDROID__) -extern "C" void *__libc_malloc(__sanitizer::uptr size); -extern "C" void __libc_free(void *ptr); -# define LIBC_MALLOC __libc_malloc -# define LIBC_FREE __libc_free -#else // __linux__ && !ANDROID -# include -# define LIBC_MALLOC malloc -# define LIBC_FREE free -#endif // __linux__ && !ANDROID +#include "sanitizer_flags.h" namespace __sanitizer { +// ThreadSanitizer for Go uses libc malloc/free. +#if defined(SANITIZER_GO) +# if SANITIZER_LINUX && !SANITIZER_ANDROID +extern "C" void *__libc_malloc(uptr size); +extern "C" void __libc_free(void *ptr); +# define LIBC_MALLOC __libc_malloc +# define LIBC_FREE __libc_free +# else +# include +# define LIBC_MALLOC malloc +# define LIBC_FREE free +# endif + +static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { + (void)cache; + return LIBC_MALLOC(size); +} + +static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { + (void)cache; + LIBC_FREE(ptr); +} + +InternalAllocator *internal_allocator() { + return 0; +} + +#else // SANITIZER_GO + +static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)]; +static atomic_uint8_t internal_allocator_initialized; +static StaticSpinMutex internal_alloc_init_mu; + +static InternalAllocatorCache internal_allocator_cache; +static StaticSpinMutex internal_allocator_cache_mu; + +InternalAllocator *internal_allocator() { + InternalAllocator *internal_allocator_instance = + reinterpret_cast(&internal_alloc_placeholder); + if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) { + SpinMutexLock l(&internal_alloc_init_mu); + if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) == + 0) { + internal_allocator_instance->Init(); + atomic_store(&internal_allocator_initialized, 1, memory_order_release); + } + } + return internal_allocator_instance; +} + +static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { + if (cache == 0) { + SpinMutexLock l(&internal_allocator_cache_mu); + return internal_allocator()->Allocate(&internal_allocator_cache, size, 8, + false); + } + return internal_allocator()->Allocate(cache, size, 8, false); +} + +static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { + if (cache == 0) { + SpinMutexLock l(&internal_allocator_cache_mu); + return internal_allocator()->Deallocate(&internal_allocator_cache, ptr); + } + internal_allocator()->Deallocate(cache, ptr); +} + +#endif // SANITIZER_GO + const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull; -void *InternalAlloc(uptr size) { +void *InternalAlloc(uptr size, InternalAllocatorCache *cache) { if (size + sizeof(u64) < size) return 0; - void *p = LIBC_MALLOC(size + sizeof(u64)); + void *p = RawInternalAlloc(size + sizeof(u64), cache); if (p == 0) return 0; ((u64*)p)[0] = kBlockMagic; return (char*)p + sizeof(u64); } -void InternalFree(void *addr) { +void InternalFree(void *addr, InternalAllocatorCache *cache) { if (addr == 0) return; addr = (char*)addr - sizeof(u64); - CHECK_EQ(((u64*)addr)[0], kBlockMagic); + CHECK_EQ(kBlockMagic, ((u64*)addr)[0]); ((u64*)addr)[0] = 0; - LIBC_FREE(addr); + RawInternalFree(addr, cache); } // LowLevelAllocator @@ -79,4 +138,14 @@ bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) { return (max / size) < n; } +void *AllocatorReturnNull() { + if (common_flags()->allocator_may_return_null) + return 0; + 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; +} + } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.h b/libsanitizer/sanitizer_common/sanitizer_allocator.h index 889281216fc..505fa5b8837 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.h @@ -21,18 +21,21 @@ namespace __sanitizer { +// Depending on allocator_may_return_null either return 0 or crash. +void *AllocatorReturnNull(); + // SizeClassMap maps allocation sizes into size classes and back. // Class 0 corresponds to size 0. // Classes 1 - 16 correspond to sizes 16 to 256 (size = class_id * 16). -// Next 8 classes: 256 + i * 32 (i = 1 to 8). -// Next 8 classes: 512 + i * 64 (i = 1 to 8). +// Next 4 classes: 256 + i * 64 (i = 1 to 4). +// Next 4 classes: 512 + i * 128 (i = 1 to 4). // ... -// Next 8 classes: 2^k + i * 2^(k-3) (i = 1 to 8). +// Next 4 classes: 2^k + i * 2^(k-2) (i = 1 to 4). // Last class corresponds to kMaxSize = 1 << kMaxSizeLog. // // This structure of the size class map gives us: // - Efficient table-free class-to-size and size-to-class functions. -// - Difference between two consequent size classes is betweed 12% and 6% +// - Difference between two consequent size classes is betweed 14% and 25% // // This class also gives a hint to a thread-caching allocator about the amount // of chunks that need to be cached per-thread: @@ -59,46 +62,51 @@ namespace __sanitizer { // c15 => s: 240 diff: +16 07% l 7 cached: 256 61440; id 15 // // c16 => s: 256 diff: +16 06% l 8 cached: 256 65536; id 16 -// c17 => s: 288 diff: +32 12% l 8 cached: 227 65376; id 17 -// c18 => s: 320 diff: +32 11% l 8 cached: 204 65280; id 18 -// c19 => s: 352 diff: +32 10% l 8 cached: 186 65472; id 19 -// c20 => s: 384 diff: +32 09% l 8 cached: 170 65280; id 20 -// c21 => s: 416 diff: +32 08% l 8 cached: 157 65312; id 21 -// c22 => s: 448 diff: +32 07% l 8 cached: 146 65408; id 22 -// c23 => s: 480 diff: +32 07% l 8 cached: 136 65280; id 23 +// c17 => s: 320 diff: +64 25% l 8 cached: 204 65280; id 17 +// c18 => s: 384 diff: +64 20% l 8 cached: 170 65280; id 18 +// c19 => s: 448 diff: +64 16% l 8 cached: 146 65408; id 19 // -// c24 => s: 512 diff: +32 06% l 9 cached: 128 65536; id 24 -// c25 => s: 576 diff: +64 12% l 9 cached: 113 65088; id 25 -// c26 => s: 640 diff: +64 11% l 9 cached: 102 65280; id 26 -// c27 => s: 704 diff: +64 10% l 9 cached: 93 65472; id 27 -// c28 => s: 768 diff: +64 09% l 9 cached: 85 65280; id 28 -// c29 => s: 832 diff: +64 08% l 9 cached: 78 64896; id 29 -// c30 => s: 896 diff: +64 07% l 9 cached: 73 65408; id 30 -// c31 => s: 960 diff: +64 07% l 9 cached: 68 65280; id 31 +// c20 => s: 512 diff: +64 14% l 9 cached: 128 65536; id 20 +// c21 => s: 640 diff: +128 25% l 9 cached: 102 65280; id 21 +// c22 => s: 768 diff: +128 20% l 9 cached: 85 65280; id 22 +// c23 => s: 896 diff: +128 16% l 9 cached: 73 65408; id 23 // -// c32 => s: 1024 diff: +64 06% l 10 cached: 64 65536; id 32 +// c24 => s: 1024 diff: +128 14% l 10 cached: 64 65536; id 24 +// c25 => s: 1280 diff: +256 25% l 10 cached: 51 65280; id 25 +// c26 => s: 1536 diff: +256 20% l 10 cached: 42 64512; id 26 +// c27 => s: 1792 diff: +256 16% l 10 cached: 36 64512; id 27 +// +// ... +// +// c48 => s: 65536 diff: +8192 14% l 16 cached: 1 65536; id 48 +// c49 => s: 81920 diff: +16384 25% l 16 cached: 1 81920; id 49 +// c50 => s: 98304 diff: +16384 20% l 16 cached: 1 98304; id 50 +// c51 => s: 114688 diff: +16384 16% l 16 cached: 1 114688; id 51 +// +// c52 => s: 131072 diff: +16384 14% l 17 cached: 1 131072; id 52 -template +template class SizeClassMap { static const uptr kMinSizeLog = 4; static const uptr kMidSizeLog = kMinSizeLog + 4; static const uptr kMinSize = 1 << kMinSizeLog; static const uptr kMidSize = 1 << kMidSizeLog; static const uptr kMidClass = kMidSize / kMinSize; - static const uptr S = 3; + static const uptr S = 2; static const uptr M = (1 << S) - 1; public: static const uptr kMaxNumCached = kMaxNumCachedT; + // We transfer chunks between central and thread-local free lists in batches. + // For small size classes we allocate batches separately. + // For large size classes we use one of the chunks to store the batch. struct TransferBatch { TransferBatch *next; uptr count; void *batch[kMaxNumCached]; }; - static const uptr kMinBatchClass = kMinBatchClassT; - static const uptr kMaxSize = 1 << kMaxSizeLog; + static const uptr kMaxSize = 1UL << kMaxSizeLog; static const uptr kNumClasses = kMidClass + ((kMaxSizeLog - kMidSizeLog) << S) + 1; COMPILER_CHECK(kNumClasses >= 32 && kNumClasses <= 256); @@ -141,7 +149,7 @@ class SizeClassMap { Printf("\n"); uptr d = s - prev_s; uptr p = prev_s ? (d * 100 / prev_s) : 0; - uptr l = MostSignificantSetBitIndex(s); + uptr l = s ? MostSignificantSetBitIndex(s) : 0; uptr cached = MaxCached(i) * s; Printf("c%02zd => s: %zd diff: +%zd %02zd%% l %zd " "cached: %zd %zd; id %zd\n", @@ -152,10 +160,16 @@ class SizeClassMap { Printf("Total cached: %zd\n", total_cached); } + static bool SizeClassRequiresSeparateTransferBatch(uptr class_id) { + return Size(class_id) < sizeof(TransferBatch) - + sizeof(uptr) * (kMaxNumCached - MaxCached(class_id)); + } + static void Validate() { for (uptr c = 1; c < kNumClasses; c++) { // Printf("Validate: c%zd\n", c); uptr s = Size(c); + CHECK_NE(s, 0U); CHECK_EQ(ClassID(s), c); if (c != kNumClasses - 1) CHECK_EQ(ClassID(s + 1), c + 1); @@ -173,24 +187,11 @@ class SizeClassMap { if (c > 0) CHECK_LT(Size(c-1), s); } - - // TransferBatch for kMinBatchClass must fit into the block itself. - const uptr batch_size = sizeof(TransferBatch) - - sizeof(void*) // NOLINT - * (kMaxNumCached - MaxCached(kMinBatchClass)); - CHECK_LE(batch_size, Size(kMinBatchClass)); - // TransferBatch for kMinBatchClass-1 must not fit into the block itself. - const uptr batch_size1 = sizeof(TransferBatch) - - sizeof(void*) // NOLINT - * (kMaxNumCached - MaxCached(kMinBatchClass - 1)); - CHECK_GT(batch_size1, Size(kMinBatchClass - 1)); } }; -typedef SizeClassMap<17, 256, 16, FIRST_32_SECOND_64(25, 28)> - DefaultSizeClassMap; -typedef SizeClassMap<17, 64, 14, FIRST_32_SECOND_64(17, 20)> - CompactSizeClassMap; +typedef SizeClassMap<17, 128, 16> DefaultSizeClassMap; +typedef SizeClassMap<17, 64, 14> CompactSizeClassMap; template struct SizeClassAllocatorLocalCache; // Memory allocator statistics @@ -279,6 +280,9 @@ struct NoOpMapUnmapCallback { void OnUnmap(uptr p, uptr size) const { } }; +// Callback type for iterating over chunks. +typedef void (*ForEachChunkCallback)(uptr chunk, void *arg); + // SizeClassAllocator64 -- allocator for 64-bit address space. // // Space: a portion of address space of kSpaceSize bytes starting at @@ -339,25 +343,28 @@ class SizeClassAllocator64 { NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) { RegionInfo *region = GetRegionInfo(class_id); + CHECK_GT(b->count, 0); region->free_list.Push(b); region->n_freed += b->count; } - static bool PointerIsMine(void *p) { + static bool PointerIsMine(const void *p) { return reinterpret_cast(p) / kSpaceSize == kSpaceBeg / kSpaceSize; } - static uptr GetSizeClass(void *p) { + static uptr GetSizeClass(const void *p) { return (reinterpret_cast(p) / kRegionSize) % kNumClassesRounded; } - void *GetBlockBegin(void *p) { + void *GetBlockBegin(const void *p) { uptr class_id = GetSizeClass(p); uptr size = SizeClassMap::Size(class_id); + if (!size) return 0; 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; RegionInfo *region = GetRegionInfo(class_id); if (region->mapped_user >= next_beg) return reinterpret_cast(reg_beg + beg); @@ -371,7 +378,7 @@ class SizeClassAllocator64 { uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); } - void *GetMetaData(void *p) { + void *GetMetaData(const void *p) { uptr class_id = GetSizeClass(p); uptr size = SizeClassMap::Size(class_id); uptr chunk_idx = GetChunkIdx(reinterpret_cast(p), size); @@ -430,6 +437,22 @@ class SizeClassAllocator64 { } } + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { + for (uptr class_id = 1; class_id < kNumClasses; class_id++) { + RegionInfo *region = GetRegionInfo(class_id); + uptr chunk_size = SizeClassMap::Size(class_id); + uptr region_beg = kSpaceBeg + class_id * kRegionSize; + for (uptr chunk = region_beg; + chunk < region_beg + region->allocated_user; + chunk += chunk_size) { + // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk)); + callback(chunk, arg); + } + } + } + typedef SizeClassMap SizeClassMapT; static const uptr kNumClasses = SizeClassMap::kNumClasses; static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded; @@ -471,11 +494,12 @@ class SizeClassAllocator64 { } static uptr GetChunkIdx(uptr chunk, uptr size) { - u32 offset = chunk % kRegionSize; + uptr offset = chunk % kRegionSize; // Here we divide by a non-constant. This is costly. - // We require that kRegionSize is at least 2^32 so that offset is 32-bit. - // We save 2x by using 32-bit div, but may need to use a 256-way switch. - return offset / (u32)size; + // size always fits into 32-bits. If the offset fits too, use 32-bit div. + if (offset >> (SANITIZER_WORDSIZE / 2)) + return offset / size; + return (u32)offset / (u32)size; } NOINLINE Batch* PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, @@ -513,14 +537,14 @@ class SizeClassAllocator64 { region->mapped_meta += map_size; } CHECK_LE(region->allocated_meta, region->mapped_meta); - if (region->allocated_user + region->allocated_meta > kRegionSize) { - Printf("Out of memory. Dying.\n"); + if (region->mapped_user + region->mapped_meta > kRegionSize) { + Printf("%s: Out of memory. Dying. ", SanitizerToolName); Printf("The process has exhausted %zuMB for size class %zu.\n", kRegionSize / 1024 / 1024, size); Die(); } for (;;) { - if (class_id < SizeClassMap::kMinBatchClass) + if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch))); else b = (Batch*)(region_beg + beg_idx); @@ -532,12 +556,37 @@ class SizeClassAllocator64 { beg_idx += count * size; if (beg_idx + count * size + size > region->mapped_user) break; + CHECK_GT(b->count, 0); region->free_list.Push(b); } return b; } }; +// Maps integers in rage [0, kSize) to u8 values. +template +class FlatByteMap { + public: + void TestOnlyInit() { + internal_memset(map_, 0, sizeof(map_)); + } + + void set(uptr idx, u8 val) { + CHECK_LT(idx, kSize); + CHECK_EQ(0U, map_[idx]); + map_[idx] = val; + } + u8 operator[] (uptr idx) { + CHECK_LT(idx, kSize); + // FIXME: CHECK may be too expensive here. + return map_[idx]; + } + private: + u8 map_[kSize]; +}; + +// FIXME: Also implement TwoLevelByteMap. + // SizeClassAllocator32 -- allocator for 32-bit address space. // This allocator can theoretically be used on 64-bit arch, but there it is less // efficient than SizeClassAllocator64. @@ -549,7 +598,7 @@ class SizeClassAllocator64 { // a result of a single call to MmapAlignedOrDie(kRegionSize, kRegionSize). // Since the regions are aligned by kRegionSize, there are exactly // kNumPossibleRegions possible regions in the address space and so we keep -// an u8 array possible_regions[kNumPossibleRegions] to store the size classes. +// a ByteMap possible_regions to store the size classes of each Region. // 0 size class means the region is not used by the allocator. // // One Region is used to allocate chunks of a single size class. @@ -560,16 +609,19 @@ class SizeClassAllocator64 { // chache-line aligned. template class SizeClassAllocator32 { public: typedef typename SizeClassMap::TransferBatch Batch; typedef SizeClassAllocator32 ThisT; + SizeClassMap, kRegionSizeLog, ByteMap, MapUnmapCallback> ThisT; typedef SizeClassAllocatorLocalCache AllocatorCache; void Init() { - state_ = reinterpret_cast(MapWithCallback(sizeof(State))); + possible_regions.TestOnlyInit(); + internal_memset(size_class_info_array, 0, sizeof(size_class_info_array)); } void *MapWithCallback(uptr size) { @@ -589,7 +641,7 @@ class SizeClassAllocator32 { alignment <= SizeClassMap::kMaxSize; } - void *GetMetaData(void *p) { + void *GetMetaData(const void *p) { CHECK(PointerIsMine(p)); uptr mem = reinterpret_cast(p); uptr beg = ComputeRegionBeg(mem); @@ -617,18 +669,19 @@ class SizeClassAllocator32 { CHECK_LT(class_id, kNumClasses); SizeClassInfo *sci = GetSizeClassInfo(class_id); SpinMutexLock l(&sci->mutex); + CHECK_GT(b->count, 0); sci->free_list.push_front(b); } - bool PointerIsMine(void *p) { + bool PointerIsMine(const void *p) { return GetSizeClass(p) != 0; } - uptr GetSizeClass(void *p) { - return state_->possible_regions[ComputeRegionId(reinterpret_cast(p))]; + uptr GetSizeClass(const void *p) { + return possible_regions[ComputeRegionId(reinterpret_cast(p))]; } - void *GetBlockBegin(void *p) { + void *GetBlockBegin(const void *p) { CHECK(PointerIsMine(p)); uptr mem = reinterpret_cast(p); uptr beg = ComputeRegionBeg(mem); @@ -650,16 +703,15 @@ class SizeClassAllocator32 { // No need to lock here. uptr res = 0; for (uptr i = 0; i < kNumPossibleRegions; i++) - if (state_->possible_regions[i]) + if (possible_regions[i]) res += kRegionSize; return res; } void TestOnlyUnmap() { for (uptr i = 0; i < kNumPossibleRegions; i++) - if (state_->possible_regions[i]) + if (possible_regions[i]) UnmapWithCallback((i * kRegionSize), kRegionSize); - UnmapWithCallback(reinterpret_cast(state_), sizeof(State)); } // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone @@ -676,6 +728,23 @@ class SizeClassAllocator32 { } } + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { + for (uptr region = 0; region < kNumPossibleRegions; region++) + if (possible_regions[region]) { + uptr chunk_size = SizeClassMap::Size(possible_regions[region]); + uptr max_chunks_in_region = kRegionSize / (chunk_size + kMetadataSize); + uptr region_beg = region * kRegionSize; + for (uptr chunk = region_beg; + chunk < region_beg + max_chunks_in_region * chunk_size; + chunk += chunk_size) { + // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk)); + callback(chunk, arg); + } + } + } + void PrintStats() { } @@ -683,7 +752,6 @@ class SizeClassAllocator32 { static const uptr kNumClasses = SizeClassMap::kNumClasses; private: - static const uptr kRegionSizeLog = SANITIZER_WORDSIZE == 64 ? 24 : 20; static const uptr kRegionSize = 1 << kRegionSizeLog; static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize; @@ -711,14 +779,13 @@ class SizeClassAllocator32 { MapUnmapCallback().OnMap(res, kRegionSize); stat->Add(AllocatorStatMmapped, kRegionSize); CHECK_EQ(0U, (res & (kRegionSize - 1))); - CHECK_EQ(0U, state_->possible_regions[ComputeRegionId(res)]); - state_->possible_regions[ComputeRegionId(res)] = class_id; + possible_regions.set(ComputeRegionId(res), static_cast(class_id)); return res; } SizeClassInfo *GetSizeClassInfo(uptr class_id) { CHECK_LT(class_id, kNumClasses); - return &state_->size_class_info_array[class_id]; + return &size_class_info_array[class_id]; } void PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, @@ -730,7 +797,7 @@ class SizeClassAllocator32 { Batch *b = 0; for (uptr i = reg; i < reg + n_chunks * size; i += size) { if (b == 0) { - if (class_id < SizeClassMap::kMinBatchClass) + if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch))); else b = (Batch*)i; @@ -738,19 +805,19 @@ class SizeClassAllocator32 { } b->batch[b->count++] = (void*)i; if (b->count == max_count) { + CHECK_GT(b->count, 0); sci->free_list.push_back(b); b = 0; } } - if (b) + if (b) { + CHECK_GT(b->count, 0); sci->free_list.push_back(b); + } } - struct State { - u8 possible_regions[kNumPossibleRegions]; - SizeClassInfo size_class_info_array[kNumClasses]; - }; - State *state_; + ByteMap possible_regions; + SizeClassInfo size_class_info_array[kNumClasses]; }; // Objects of this type should be used as local caches for SizeClassAllocator64 @@ -788,8 +855,12 @@ struct SizeClassAllocatorLocalCache { void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); + // 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)); PerClass *c = &per_class_[class_id]; + CHECK_NE(c->max_count, 0UL); if (UNLIKELY(c->count == c->max_count)) Drain(allocator, class_id); c->batch[c->count++] = p; @@ -815,7 +886,7 @@ struct SizeClassAllocatorLocalCache { AllocatorStats stats_; void InitCache() { - if (per_class_[0].max_count) + if (per_class_[1].max_count) return; for (uptr i = 0; i < kNumClasses; i++) { PerClass *c = &per_class_[i]; @@ -831,7 +902,7 @@ struct SizeClassAllocatorLocalCache { for (uptr i = 0; i < b->count; i++) c->batch[i] = b->batch[i]; c->count = b->count; - if (class_id < SizeClassMap::kMinBatchClass) + if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) Deallocate(allocator, SizeClassMap::ClassID(sizeof(Batch)), b); } @@ -839,7 +910,7 @@ struct SizeClassAllocatorLocalCache { InitCache(); PerClass *c = &per_class_[class_id]; Batch *b; - if (class_id < SizeClassMap::kMinBatchClass) + if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) b = (Batch*)Allocate(allocator, SizeClassMap::ClassID(sizeof(Batch))); else b = (Batch*)c->batch[0]; @@ -850,6 +921,7 @@ struct SizeClassAllocatorLocalCache { } b->count = cnt; c->count -= cnt; + CHECK_GT(b->count, 0); allocator->DeallocateBatch(&stats_, class_id, b); } }; @@ -870,7 +942,7 @@ class LargeMmapAllocator { uptr map_size = RoundUpMapSize(size); if (alignment > page_size_) map_size += alignment; - if (map_size < size) return 0; // Overflow. + if (map_size < size) return AllocatorReturnNull(); // Overflow. uptr map_beg = reinterpret_cast( MmapOrDie(map_size, "LargeMmapAllocator")); MapUnmapCallback().OnMap(map_beg, map_size); @@ -889,6 +961,7 @@ class LargeMmapAllocator { { SpinMutexLock l(&mutex_); uptr idx = n_chunks_++; + chunks_sorted_ = false; CHECK_LT(idx, kMaxNumChunks); h->chunk_idx = idx; chunks_[idx] = h; @@ -912,6 +985,7 @@ class LargeMmapAllocator { chunks_[idx] = chunks_[n_chunks_ - 1]; chunks_[idx]->chunk_idx = idx; n_chunks_--; + chunks_sorted_ = false; stats.n_frees++; stats.currently_allocated -= h->map_size; stat->Add(AllocatorStatFreed, h->map_size); @@ -932,7 +1006,7 @@ class LargeMmapAllocator { return res; } - bool PointerIsMine(void *p) { + bool PointerIsMine(const void *p) { return GetBlockBegin(p) != 0; } @@ -941,13 +1015,16 @@ class LargeMmapAllocator { } // At least page_size_/2 metadata bytes is available. - void *GetMetaData(void *p) { + void *GetMetaData(const void *p) { // Too slow: CHECK_EQ(p, GetBlockBegin(p)); - CHECK(IsAligned(reinterpret_cast(p), page_size_)); + if (!IsAligned(reinterpret_cast(p), page_size_)) { + Printf("%s: bad pointer %p\n", SanitizerToolName, p); + CHECK(IsAligned(reinterpret_cast(p), page_size_)); + } return GetHeader(p) + 1; } - void *GetBlockBegin(void *ptr) { + void *GetBlockBegin(const void *ptr) { uptr p = reinterpret_cast(ptr); SpinMutexLock l(&mutex_); uptr nearest_chunk = 0; @@ -964,7 +1041,49 @@ class LargeMmapAllocator { 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) + if (h->map_beg + h->map_size <= p) + return 0; + return GetUser(h); + } + + // This function does the same as GetBlockBegin, but is much faster. + // Must be called with the allocator locked. + void *GetBlockBeginFastLocked(void *ptr) { + uptr p = reinterpret_cast(ptr); + uptr n = n_chunks_; + if (!n) return 0; + if (!chunks_sorted_) { + // Do one-time sort. chunks_sorted_ is reset in Allocate/Deallocate. + SortArray(reinterpret_cast(chunks_), n); + for (uptr i = 0; i < n; i++) + chunks_[i]->chunk_idx = i; + chunks_sorted_ = true; + min_mmap_ = reinterpret_cast(chunks_[0]); + max_mmap_ = reinterpret_cast(chunks_[n - 1]) + + chunks_[n - 1]->map_size; + } + if (p < min_mmap_ || p >= max_mmap_) + return 0; + 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. + while (end - beg >= 2) { + uptr mid = (beg + end) / 2; // Invariant: mid >= beg + 1 + if (p < reinterpret_cast(chunks_[mid])) + end = mid - 1; // We are not interested in chunks_[mid]. + else + beg = mid; // chunks_[mid] may still be what we want. + } + + if (beg < end) { + CHECK_EQ(beg + 1, end); + // There are 2 chunks left, choose one. + if (p >= reinterpret_cast(chunks_[end])) + beg = end; + } + + Header *h = chunks_[beg]; + if (h->map_beg + h->map_size <= p || p < h->map_beg) return 0; return GetUser(h); } @@ -992,6 +1111,13 @@ class LargeMmapAllocator { mutex_.Unlock(); } + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { + for (uptr i = 0; i < n_chunks_; i++) + callback(reinterpret_cast(GetUser(chunks_[i])), arg); + } + private: static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18); struct Header { @@ -1002,13 +1128,15 @@ class LargeMmapAllocator { }; Header *GetHeader(uptr p) { - CHECK_EQ(p % page_size_, 0); + CHECK(IsAligned(p, page_size_)); return reinterpret_cast(p - page_size_); } - Header *GetHeader(void *p) { return GetHeader(reinterpret_cast(p)); } + Header *GetHeader(const void *p) { + return GetHeader(reinterpret_cast(p)); + } void *GetUser(Header *h) { - CHECK_EQ((uptr)h % page_size_, 0); + CHECK(IsAligned((uptr)h, page_size_)); return reinterpret_cast(reinterpret_cast(h) + page_size_); } @@ -1019,6 +1147,8 @@ class LargeMmapAllocator { uptr page_size_; Header *chunks_[kMaxNumChunks]; uptr n_chunks_; + uptr min_mmap_, max_mmap_; + bool chunks_sorted_; struct Stats { uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64]; } stats; @@ -1047,7 +1177,7 @@ class CombinedAllocator { if (size == 0) size = 1; if (size + alignment < size) - return 0; + return AllocatorReturnNull(); if (alignment > 8) size = RoundUpTo(size, alignment); void *res; @@ -1098,18 +1228,26 @@ class CombinedAllocator { return primary_.PointerIsMine(p); } - void *GetMetaData(void *p) { + void *GetMetaData(const void *p) { if (primary_.PointerIsMine(p)) return primary_.GetMetaData(p); return secondary_.GetMetaData(p); } - void *GetBlockBegin(void *p) { + void *GetBlockBegin(const void *p) { if (primary_.PointerIsMine(p)) return primary_.GetBlockBegin(p); return secondary_.GetBlockBegin(p); } + // This function does the same as GetBlockBegin, but is much faster. + // Must be called with the allocator locked. + void *GetBlockBeginFastLocked(void *p) { + if (primary_.PointerIsMine(p)) + return primary_.GetBlockBegin(p); + return secondary_.GetBlockBeginFastLocked(p); + } + uptr GetActuallyAllocatedSize(void *p) { if (primary_.PointerIsMine(p)) return primary_.GetActuallyAllocatedSize(p); @@ -1155,6 +1293,13 @@ class CombinedAllocator { primary_.ForceUnlock(); } + // Iterate over all existing chunks. + // The allocator must be locked when calling this function. + void ForEachChunk(ForEachChunkCallback callback, void *arg) { + primary_.ForEachChunk(callback, arg); + secondary_.ForEachChunk(callback, arg); + } + private: PrimaryAllocator primary_; SecondaryAllocator secondary_; diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h b/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h new file mode 100644 index 00000000000..c033b96e4dd --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h @@ -0,0 +1,62 @@ +//===-- sanitizer_allocator_internal.h -------------------------- C++ -----===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This allocator is used inside run-times. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_ALLOCATOR_INTERNAL_H +#define SANITIZER_ALLOCATOR_INTERNAL_H + +#include "sanitizer_allocator.h" +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +// FIXME: Check if we may use even more compact size class map for internal +// purposes. +typedef CompactSizeClassMap InternalSizeClassMap; + +static const uptr kInternalAllocatorSpace = 0; +#if SANITIZER_WORDSIZE == 32 +static const u64 kInternalAllocatorSize = (1ULL << 32); +static const uptr kInternalAllocatorRegionSizeLog = 20; +#else +static const u64 kInternalAllocatorSize = (1ULL << 47); +static const uptr kInternalAllocatorRegionSizeLog = 24; +#endif +static const uptr kInternalAllocatorFlatByteMapSize = + kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog; +typedef SizeClassAllocator32< + kInternalAllocatorSpace, kInternalAllocatorSize, 16, InternalSizeClassMap, + kInternalAllocatorRegionSizeLog, + FlatByteMap > PrimaryInternalAllocator; + +typedef SizeClassAllocatorLocalCache + InternalAllocatorCache; + +// We don't want our internal allocator to do any map/unmap operations. +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 > + InternalAllocator; + +void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0); +void InternalFree(void *p, InternalAllocatorCache *cache = 0); +InternalAllocator *internal_allocator(); + +} // namespace __sanitizer + +#endif // SANITIZER_ALLOCATOR_INTERNAL_H diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h b/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h index bb4611d51e6..88819e32a73 100644 --- a/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h +++ b/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h @@ -39,7 +39,17 @@ INLINE typename T::Type atomic_load( | memory_order_acquire | memory_order_seq_cst)); DCHECK(!((uptr)a % sizeof(*a))); typename T::Type v; - // FIXME(dvyukov): 64-bit load is not atomic on 32-bits. + // 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 { @@ -55,7 +65,6 @@ 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))); - // FIXME(dvyukov): 64-bit store is not atomic on 32-bits. if (mo == memory_order_relaxed) { a->val_dont_use = v; } else { @@ -111,12 +120,14 @@ INLINE bool atomic_compare_exchange_strong(volatile T *a, template INLINE bool atomic_compare_exchange_weak(volatile T *a, - typename T::Type *cmp, - typename T::Type xchg, - memory_order mo) { + typename T::Type *cmp, + typename T::Type xchg, + memory_order mo) { return atomic_compare_exchange_strong(a, cmp, xchg, mo); } } // namespace __sanitizer +#undef ATOMIC_ORDER + #endif // SANITIZER_ATOMIC_CLANG_H diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h b/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h index 919e24f3b11..dac7c19199b 100644 --- a/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h +++ b/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h @@ -132,6 +132,27 @@ INLINE u16 atomic_exchange(volatile atomic_uint16_t *a, return v; } +INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a, + u8 *cmp, + u8 xchgv, + memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + u8 cmpv = *cmp; + u8 prev; + __asm { + mov al, cmpv + mov ecx, a + mov dl, xchgv + lock cmpxchg [ecx], dl + mov prev, al + } + if (prev == cmpv) + return true; + *cmp = prev; + return false; +} + INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a, uptr *cmp, uptr xchg, @@ -147,9 +168,9 @@ INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a, template INLINE bool atomic_compare_exchange_weak(volatile T *a, - typename T::Type *cmp, - typename T::Type xchg, - memory_order mo) { + typename T::Type *cmp, + typename T::Type xchg, + memory_order mo) { return atomic_compare_exchange_strong(a, cmp, xchg, mo); } diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cc b/libsanitizer/sanitizer_common/sanitizer_common.cc index f8d2d0e3fe5..f689df421aa 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common.cc @@ -15,6 +15,7 @@ namespace __sanitizer { const char *SanitizerToolName = "SanitizerTool"; +uptr SanitizerVerbosity = 0; uptr GetPageSizeCached() { static uptr PageSize; @@ -23,22 +24,29 @@ uptr GetPageSizeCached() { return PageSize; } -static bool log_to_file = false; // Set to true by __sanitizer_set_report_path // 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.". -static fd_t report_fd = kStderrFd; -static char report_path_prefix[4096]; // Set via __sanitizer_set_report_path. +fd_t report_fd = kStderrFd; + +// Set via __sanitizer_set_report_path. +bool log_to_file = false; +char report_path_prefix[sizeof(report_path_prefix)]; + // PID of process that opened |report_fd|. If a fork() occurs, the PID of the // child thread will be different from |report_fd_pid|. -static int report_fd_pid = 0; +uptr report_fd_pid = 0; -static void (*DieCallback)(void); -void SetDieCallback(void (*callback)(void)) { +static DieCallbackType DieCallback; +void SetDieCallback(DieCallbackType callback) { DieCallback = callback; } +DieCallbackType GetDieCallback() { + return DieCallback; +} + void NORETURN Die() { if (DieCallback) { DieCallback(); @@ -61,41 +69,6 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond, Die(); } -static void MaybeOpenReportFile() { - if (!log_to_file || (report_fd_pid == GetPid())) return; - InternalScopedBuffer report_path_full(4096); - internal_snprintf(report_path_full.data(), report_path_full.size(), - "%s.%d", report_path_prefix, GetPid()); - fd_t fd = OpenFile(report_path_full.data(), true); - if (fd == kInvalidFd) { - 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 = fd; - report_fd_pid = GetPid(); -} - -bool PrintsToTty() { - MaybeOpenReportFile(); - return internal_isatty(report_fd); -} - -void RawWrite(const char *buffer) { - static const char *kRawWriteError = "RawWrite can't output requested buffer!"; - uptr length = (uptr)internal_strlen(buffer); - MaybeOpenReportFile(); - if (length != internal_write(report_fd, buffer, length)) { - internal_write(report_fd, kRawWriteError, internal_strlen(kRawWriteError)); - Die(); - } -} - uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, uptr max_len) { uptr PageSize = GetPageSizeCached(); @@ -105,8 +78,9 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, *buff_size = 0; // The files we usually open are not seekable, so try different buffer sizes. for (uptr size = kMinFileLen; size <= max_len; size *= 2) { - fd_t fd = OpenFile(file_name, /*write*/ false); - if (fd == kInvalidFd) return 0; + uptr openrv = OpenFile(file_name, /*write*/ false); + if (internal_iserror(openrv)) return 0; + fd_t fd = openrv; UnmapOrDie(*buff, *buff_size); *buff = (char*)MmapOrDie(size, __FUNCTION__); *buff_size = size; @@ -128,45 +102,15 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, return read_len; } -// We don't want to use std::sort to avoid including , as -// we may end up with two implementation of std::sort - one in instrumented -// code, and the other in runtime. -// qsort() from stdlib won't work as it calls malloc(), which results -// in deadlock in ASan allocator. -// We re-implement in-place sorting w/o recursion as straightforward heapsort. +typedef bool UptrComparisonFunction(const uptr &a, const uptr &b); + +template +static inline bool CompareLess(const T &a, const T &b) { + return a < b; +} + void SortArray(uptr *array, uptr size) { - if (size < 2) - return; - // Stage 1: insert elements to the heap. - for (uptr i = 1; i < size; i++) { - uptr j, p; - for (j = i; j > 0; j = p) { - p = (j - 1) / 2; - if (array[j] > array[p]) - Swap(array[j], array[p]); - else - break; - } - } - // Stage 2: swap largest element with the last one, - // and sink the new top. - for (uptr i = size - 1; i > 0; i--) { - Swap(array[0], array[i]); - uptr j, max_ind; - for (j = 0; j < i; j = max_ind) { - uptr left = 2 * j + 1; - uptr right = 2 * j + 2; - max_ind = j; - if (left < i && array[left] > array[max_ind]) - max_ind = left; - if (right < i && array[right] > array[max_ind]) - max_ind = right; - if (max_ind != j) - Swap(array[j], array[max_ind]); - else - break; - } - } + InternalSort(&array, size, CompareLess); } // We want to map a chunk of address space aligned to 'alignment'. @@ -200,6 +144,27 @@ void ReportErrorSummary(const char *error_type, const char *file, __sanitizer_report_error_summary(buff.data()); } +LoadedModule::LoadedModule(const char *module_name, uptr base_address) { + 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_++; +} + +bool LoadedModule::containsAddress(uptr address) const { + for (uptr i = 0; i < n_ranges_; i++) { + if (ranges_[i].beg <= address && address < ranges_[i].end) + return true; + } + return false; +} + } // namespace __sanitizer using namespace __sanitizer; // NOLINT diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h index d2782b6c9dc..417f71f987c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.h +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -15,6 +15,7 @@ #define SANITIZER_COMMON_H #include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" #include "sanitizer_mutex.h" namespace __sanitizer { @@ -30,17 +31,22 @@ const uptr kCacheLineSize = 128; const uptr kCacheLineSize = 64; #endif +const uptr kMaxPathLength = 512; + extern const char *SanitizerToolName; // Can be changed by the tool. +extern uptr SanitizerVerbosity; uptr GetPageSize(); uptr GetPageSizeCached(); uptr GetMmapGranularity(); +uptr GetMaxVirtualAddress(); // Threads -int GetPid(); uptr GetTid(); uptr GetThreadSelf(); void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom); +void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, + uptr *tls_addr, uptr *tls_size); // Memory management void *MmapOrDie(uptr size, const char *mem_type); @@ -54,10 +60,6 @@ void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type); bool MemoryRangeIsAvailable(uptr range_start, uptr range_end); void FlushUnneededShadowMemory(uptr addr, uptr size); -// Internal allocator -void *InternalAlloc(uptr size); -void InternalFree(void *p); - // InternalScopedBuffer can be used instead of large stack arrays to // keep frame size low. // FIXME: use InternalAlloc instead of MmapOrDie once @@ -103,13 +105,20 @@ void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback); // IO void RawWrite(const char *buffer); bool PrintsToTty(); +// Caching version of PrintsToTty(). Not thread-safe. +bool PrintsToTtyCached(); void Printf(const char *format, ...); void Report(const char *format, ...); void SetPrintfAndReportCallback(void (*callback)(const char *)); // 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; -fd_t OpenFile(const char *filename, bool write); +uptr OpenFile(const char *filename, bool write); // 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', @@ -126,23 +135,29 @@ void DisableCoreDumper(); 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); u32 GetUid(); void ReExec(); bool StackSizeIsUnlimited(); void SetStackSizeLimitInBytes(uptr limit); void PrepareForSandboxing(); +void InitTlsSize(); +uptr GetTlsSize(); + // Other void SleepForSeconds(int seconds); void SleepForMillis(int millis); +u64 NanoTime(); int Atexit(void (*function)(void)); void SortArray(uptr *array, uptr size); // Exit void NORETURN Abort(); void NORETURN Die(); -void NORETURN SANITIZER_INTERFACE_ATTRIBUTE +void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); // Set the name of the current thread to 'name', return true on succees. @@ -154,7 +169,9 @@ bool SanitizerGetThreadName(char *name, int max_len); // Specific tools may override behavior of "Die" and "CheckFailed" functions // to do tool-specific job. -void SetDieCallback(void (*callback)(void)); +typedef void (*DieCallbackType)(void); +void SetDieCallback(DieCallbackType); +DieCallbackType GetDieCallback(); typedef void (*CheckFailedCallbackType)(const char *, int, const char *, u64, u64); void SetCheckFailedCallback(CheckFailedCallbackType callback); @@ -166,7 +183,7 @@ void ReportErrorSummary(const char *error_type, const char *file, int line, const char *function); // Math -#if defined(_WIN32) && !defined(__clang__) +#if SANITIZER_WINDOWS && !defined(__clang__) && !defined(__GNUC__) extern "C" { unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT @@ -178,9 +195,9 @@ unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); / #endif INLINE uptr MostSignificantSetBitIndex(uptr x) { - CHECK(x != 0); + CHECK_NE(x, 0U); unsigned long up; // NOLINT -#if !defined(_WIN32) || defined(__clang__) +#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x); #elif defined(_WIN64) _BitScanReverse64(&up, x); @@ -219,7 +236,7 @@ INLINE bool IsAligned(uptr a, uptr alignment) { INLINE uptr Log2(uptr x) { CHECK(IsPowerOfTwo(x)); -#if !defined(_WIN32) || defined(__clang__) +#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) return __builtin_ctzl(x); #elif defined(_WIN64) unsigned long ret; // NOLINT @@ -260,6 +277,160 @@ INLINE int ToLower(int c) { # 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 +class InternalMmapVector { + public: + explicit InternalMmapVector(uptr initial_capacity) { + CHECK_GT(initial_capacity, 0); + capacity_ = initial_capacity; + size_ = 0; + data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVector"); + } + ~InternalMmapVector() { + UnmapOrDie(data_, capacity_ * sizeof(T)); + } + T &operator[](uptr i) { + CHECK_LT(i, size_); + return data_[i]; + } + const T &operator[](uptr i) const { + CHECK_LT(i, size_); + return data_[i]; + } + void push_back(const T &element) { + CHECK_LE(size_, capacity_); + if (size_ == capacity_) { + uptr new_capacity = RoundUpToPowerOfTwo(size_ + 1); + Resize(new_capacity); + } + data_[size_++] = element; + } + T &back() { + CHECK_GT(size_, 0); + return data_[size_ - 1]; + } + void pop_back() { + CHECK_GT(size_, 0); + size_--; + } + uptr size() const { + return size_; + } + const T *data() const { + return data_; + } + uptr capacity() const { + return capacity_; + } + + private: + void Resize(uptr new_capacity) { + CHECK_GT(new_capacity, 0); + CHECK_LE(size_, new_capacity); + T *new_data = (T *)MmapOrDie(new_capacity * sizeof(T), + "InternalMmapVector"); + internal_memcpy(new_data, data_, size_ * sizeof(T)); + T *old_data = data_; + data_ = new_data; + 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_; +}; + +// HeapSort for arrays and InternalMmapVector. +template +void InternalSort(Container *v, uptr size, Compare comp) { + if (size < 2) + return; + // Stage 1: insert elements to the heap. + for (uptr i = 1; i < size; i++) { + uptr j, p; + for (j = i; j > 0; j = p) { + p = (j - 1) / 2; + if (comp((*v)[p], (*v)[j])) + Swap((*v)[j], (*v)[p]); + else + break; + } + } + // Stage 2: swap largest element with the last one, + // and sink the new top. + for (uptr i = size - 1; i > 0; i--) { + Swap((*v)[0], (*v)[i]); + uptr j, max_ind; + for (j = 0; j < i; j = max_ind) { + uptr left = 2 * j + 1; + uptr right = 2 * j + 2; + max_ind = j; + if (left < i && comp((*v)[max_ind], (*v)[left])) + max_ind = left; + if (right < i && comp((*v)[max_ind], (*v)[right])) + max_ind = right; + if (max_ind != j) + Swap((*v)[j], (*v)[max_ind]); + else + break; + } + } +} + +template +uptr InternalBinarySearch(const Container &v, uptr first, uptr last, + const Value &val, Compare comp) { + uptr not_found = last + 1; + while (last >= first) { + uptr mid = (first + last) / 2; + if (comp(v[mid], val)) + first = mid + 1; + else if (comp(val, v[mid])) + last = mid - 1; + else + return mid; + } + return not_found; +} + +// Represents a binary loaded into virtual memory (e.g. this can be an +// executable or a shared object). +class LoadedModule { + public: + LoadedModule(const char *module_name, uptr base_address); + void addAddressRange(uptr beg, uptr end); + bool containsAddress(uptr address) const; + + const char *full_name() const { return full_name_; } + uptr base_address() const { return base_address_; } + + private: + struct AddressRange { + uptr beg; + uptr end; + }; + char *full_name_; + uptr base_address_; + static const uptr kMaxNumberOfAddressRanges = 6; + AddressRange ranges_[kMaxNumberOfAddressRanges]; + uptr n_ranges_; +}; + +// OS-dependent function that fills array with descriptions of at most +// "max_modules" currently loaded modules. Returns the number of +// initialized modules. If filter is nonzero, ignores modules for which +// filter(full_name) is false. +typedef bool (*string_predicate_t)(const char *); +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter); + } // namespace __sanitizer #endif // SANITIZER_COMMON_H diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc index af27603ebdd..17ef72e0c98 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc @@ -22,10 +22,157 @@ #include -#ifdef _WIN32 +#if SANITIZER_WINDOWS #define va_copy(dst, src) ((dst) = (src)) #endif // _WIN32 +#if SANITIZER_INTERCEPT_STRCMP +static inline int CharCmpX(unsigned char c1, unsigned char c2) { + return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; +} + +INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2); + unsigned char c1, c2; + uptr i; + for (i = 0; ; i++) { + c1 = (unsigned char)s1[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); + return CharCmpX(c1, c2); +} + +INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size); + unsigned char c1 = 0, c2 = 0; + uptr i; + for (i = 0; i < size; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (c1 != c2 || c1 == '\0') 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); +} + +#define INIT_STRCMP INTERCEPT_FUNCTION(strcmp) +#define INIT_STRNCMP INTERCEPT_FUNCTION(strncmp) +#else +#define INIT_STRCMP +#define INIT_STRNCMP +#endif + +#if SANITIZER_INTERCEPT_STRCASECMP +static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { + int c1_low = ToLower(c1); + int c2_low = ToLower(c2); + return c1_low - c2_low; +} + +INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcasecmp, s1, s2); + unsigned char c1 = 0, c2 = 0; + uptr i; + for (i = 0; ; i++) { + c1 = (unsigned char)s1[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); + return CharCaseCmp(c1, c2); +} + +INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strncasecmp, s1, s2, n); + unsigned char c1 = 0, c2 = 0; + uptr i; + for (i = 0; i < n; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') + break; + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, n)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, n)); + return CharCaseCmp(c1, c2); +} + +#define INIT_STRCASECMP INTERCEPT_FUNCTION(strcasecmp) +#define INIT_STRNCASECMP INTERCEPT_FUNCTION(strncasecmp) +#else +#define INIT_STRCASECMP +#define INIT_STRNCASECMP +#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); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); + return res; +} + +#define INIT_FREXP INTERCEPT_FUNCTION(frexp); +#else +#define INIT_FREXP +#endif // SANITIZER_INTERCEPT_FREXP + +#if SANITIZER_INTERCEPT_FREXPF_FREXPL +INTERCEPTOR(float, frexpf, float x, int *exp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, frexpf, x, exp); + 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); + long double res = REAL(frexpl)(x, exp); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); + return res; +} + +#define INIT_FREXPF_FREXPL \ + INTERCEPT_FUNCTION(frexpf); \ + INTERCEPT_FUNCTION(frexpl) +#else +#define INIT_FREXPF_FREXPL +#endif // SANITIZER_INTERCEPT_FREXPF_FREXPL + +#if SI_NOT_WINDOWS +static void write_iovec(void *ctx, struct __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec[i].iov_base, sz); + maxlen -= sz; + } +} + +static void read_iovec(void *ctx, struct __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec) * iovlen); + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec[i].iov_base, sz); + maxlen -= sz; + } +} +#endif + #if SANITIZER_INTERCEPT_READ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { void *ctx; @@ -74,6 +221,51 @@ INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { #define INIT_PREAD64 #endif +#if SANITIZER_INTERCEPT_READV +INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov, + int iovcnt) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt); + SSIZE_T res = REAL(readv)(fd, iov, iovcnt); + if (res > 0) write_iovec(ctx, iov, iovcnt, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_READV INTERCEPT_FUNCTION(readv) +#else +#define INIT_READV +#endif + +#if SANITIZER_INTERCEPT_PREADV +INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset); + SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset); + if (res > 0) write_iovec(ctx, iov, iovcnt, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_PREADV INTERCEPT_FUNCTION(preadv) +#else +#define INIT_PREADV +#endif + +#if SANITIZER_INTERCEPT_PREADV64 +INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF64_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset); + SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset); + if (res > 0) write_iovec(ctx, iov, iovcnt, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + return res; +} +#define INIT_PREADV64 INTERCEPT_FUNCTION(preadv64) +#else +#define INIT_PREADV64 +#endif + #if SANITIZER_INTERCEPT_WRITE INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { void *ctx; @@ -81,6 +273,7 @@ INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(write)(fd, ptr, count); + // FIXME: this check should be _before_ the call to REAL(write), not after if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; @@ -123,6 +316,51 @@ INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count, #define INIT_PWRITE64 #endif +#if SANITIZER_INTERCEPT_WRITEV +INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov, + int iovcnt) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(writev)(fd, iov, iovcnt); + if (res > 0) read_iovec(ctx, iov, iovcnt, res); + return res; +} +#define INIT_WRITEV INTERCEPT_FUNCTION(writev) +#else +#define INIT_WRITEV +#endif + +#if SANITIZER_INTERCEPT_PWRITEV +INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset); + if (res > 0) read_iovec(ctx, iov, iovcnt, res); + return res; +} +#define INIT_PWRITEV INTERCEPT_FUNCTION(pwritev) +#else +#define INIT_PWRITEV +#endif + +#if SANITIZER_INTERCEPT_PWRITEV64 +INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt, + OFF64_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset); + if (res > 0) read_iovec(ctx, iov, iovcnt, res); + return res; +} +#define INIT_PWRITEV64 INTERCEPT_FUNCTION(pwritev64) +#else +#define INIT_PWRITEV64 +#endif + #if SANITIZER_INTERCEPT_PRCTL INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3, // NOLINT @@ -144,6 +382,24 @@ INTERCEPTOR(int, prctl, int option, #define INIT_PRCTL #endif // SANITIZER_INTERCEPT_PRCTL + +#if SANITIZER_INTERCEPT_TIME +INTERCEPTOR(unsigned long, time, unsigned long *t) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, time, t); + unsigned long res = REAL(time)(t); + if (t && res != (unsigned long)-1) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, t, sizeof(*t)); + } + return res; +} +#define INIT_TIME \ + INTERCEPT_FUNCTION(time); +#else +#define INIT_TIME +#endif // SANITIZER_INTERCEPT_TIME + + #if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS INTERCEPTOR(void *, localtime, unsigned long *timep) { void *ctx; @@ -279,9 +535,9 @@ VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap) #define SCANF_INTERCEPTOR_IMPL(name, vname, ...) \ { \ void *ctx; \ - COMMON_INTERCEPTOR_ENTER(ctx, name, __VA_ARGS__); \ 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; \ @@ -307,31 +563,1659 @@ INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...) SCANF_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) #endif -#define INIT_SCANF \ - INTERCEPT_FUNCTION(scanf); \ - INTERCEPT_FUNCTION(sscanf); \ - INTERCEPT_FUNCTION(fscanf); \ - INTERCEPT_FUNCTION(vscanf); \ - INTERCEPT_FUNCTION(vsscanf); \ - INTERCEPT_FUNCTION(vfscanf); \ - INTERCEPT_FUNCTION(__isoc99_scanf); \ - INTERCEPT_FUNCTION(__isoc99_sscanf); \ - INTERCEPT_FUNCTION(__isoc99_fscanf); \ - INTERCEPT_FUNCTION(__isoc99_vscanf); \ - INTERCEPT_FUNCTION(__isoc99_vsscanf); \ - INTERCEPT_FUNCTION(__isoc99_vfscanf); +#endif +#if SANITIZER_INTERCEPT_SCANF +#define INIT_SCANF \ + INTERCEPT_FUNCTION(scanf); \ + INTERCEPT_FUNCTION(sscanf); \ + INTERCEPT_FUNCTION(fscanf); \ + INTERCEPT_FUNCTION(vscanf); \ + INTERCEPT_FUNCTION(vsscanf); \ + INTERCEPT_FUNCTION(vfscanf); #else #define INIT_SCANF #endif -#define SANITIZER_COMMON_INTERCEPTORS_INIT \ - INIT_READ; \ - INIT_PREAD; \ - INIT_PREAD64; \ - INIT_PRCTL; \ - INIT_WRITE; \ - INIT_PWRITE; \ - INIT_PWRITE64; \ - INIT_LOCALTIME_AND_FRIENDS; \ - INIT_SCANF; +#if SANITIZER_INTERCEPT_ISOC99_SCANF +#define INIT_ISOC99_SCANF \ + INTERCEPT_FUNCTION(__isoc99_scanf); \ + INTERCEPT_FUNCTION(__isoc99_sscanf); \ + INTERCEPT_FUNCTION(__isoc99_fscanf); \ + INTERCEPT_FUNCTION(__isoc99_vscanf); \ + INTERCEPT_FUNCTION(__isoc99_vsscanf); \ + INTERCEPT_FUNCTION(__isoc99_vfscanf); +#else +#define INIT_ISOC99_SCANF +#endif + +#if SANITIZER_INTERCEPT_IOCTL +#include "sanitizer_common_interceptors_ioctl.inc" +INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ioctl, d, request, arg); + + CHECK(ioctl_initialized); + + // Note: TSan does not use common flags, and they are zero-initialized. + // This effectively disables ioctl handling in TSan. + if (!common_flags()->handle_ioctl) + return REAL(ioctl)(d, request, arg); + + const ioctl_desc *desc = ioctl_lookup(request); + if (!desc) + Printf("WARNING: unknown ioctl %x\n", request); + + if (desc) + ioctl_common_pre(ctx, desc, d, request, arg); + int res = REAL(ioctl)(d, request, arg); + // FIXME: some ioctls have different return values for success and failure. + if (desc && res != -1) + ioctl_common_post(ctx, desc, res, d, request, arg); + return res; +} +#define INIT_IOCTL \ + ioctl_init(); \ + INTERCEPT_FUNCTION(ioctl); +#else +#define INIT_IOCTL +#endif + + +#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS +INTERCEPTOR(void *, 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); + return res; +} +INTERCEPTOR(void *, 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); + return res; +} +INTERCEPTOR(void *, 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); + return res; +} +INTERCEPTOR(void *, 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); + return res; +} +#define INIT_GETPWNAM_AND_FRIENDS \ + INTERCEPT_FUNCTION(getpwnam); \ + INTERCEPT_FUNCTION(getpwuid); \ + INTERCEPT_FUNCTION(getgrnam); \ + INTERCEPT_FUNCTION(getgrgid); +#else +#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) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getpwnam_r, name, pwd, buf, buflen, result); + COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); + int res = REAL(getpwnam_r)(name, pwd, buf, buflen, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, struct_passwd_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); + } + return res; +} +INTERCEPTOR(int, getpwuid_r, u32 uid, void *pwd, + char *buf, SIZE_T buflen, void **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result); + int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, struct_passwd_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); + } + return res; +} +INTERCEPTOR(int, getgrnam_r, const char *name, void *grp, + char *buf, SIZE_T buflen, void **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getgrnam_r, name, grp, buf, buflen, result); + COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); + int res = REAL(getgrnam_r)(name, grp, buf, buflen, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, struct_group_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); + } + return res; +} +INTERCEPTOR(int, getgrgid_r, u32 gid, void *grp, + char *buf, SIZE_T buflen, void **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result); + int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, struct_group_sz); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); + } + return res; +} +#define INIT_GETPWNAM_R_AND_FRIENDS \ + INTERCEPT_FUNCTION(getpwnam_r); \ + INTERCEPT_FUNCTION(getpwuid_r); \ + INTERCEPT_FUNCTION(getgrnam_r); \ + INTERCEPT_FUNCTION(getgrgid_r); +#else +#define INIT_GETPWNAM_R_AND_FRIENDS +#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); + int res = REAL(clock_getres)(clk_id, tp); + if (!res && tp) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); + } + return res; +} +INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, clock_gettime, clk_id, tp); + int res = REAL(clock_gettime)(clk_id, tp); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); + } + return res; +} +INTERCEPTOR(int, clock_settime, u32 clk_id, const void *tp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, clock_settime, clk_id, tp); + COMMON_INTERCEPTOR_READ_RANGE(ctx, tp, struct_timespec_sz); + return REAL(clock_settime)(clk_id, tp); +} +#define INIT_CLOCK_GETTIME \ + INTERCEPT_FUNCTION(clock_getres); \ + INTERCEPT_FUNCTION(clock_gettime); \ + INTERCEPT_FUNCTION(clock_settime); +#else +#define INIT_CLOCK_GETTIME +#endif + + +#if SANITIZER_INTERCEPT_GETITIMER +INTERCEPTOR(int, getitimer, int which, void *curr_value) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getitimer, which, curr_value); + int res = REAL(getitimer)(which, curr_value); + if (!res && curr_value) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerval_sz); + } + return res; +} +INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, setitimer, which, new_value, old_value); + if (new_value) + COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz); + int res = REAL(setitimer)(which, new_value, old_value); + if (!res && old_value) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerval_sz); + } + return res; +} +#define INIT_GETITIMER \ + INTERCEPT_FUNCTION(getitimer); \ + INTERCEPT_FUNCTION(setitimer); +#else +#define INIT_GETITIMER +#endif + +#if SANITIZER_INTERCEPT_GLOB +static void unpoison_glob_t(void *ctx, __sanitizer_glob_t *pglob) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pglob, sizeof(*pglob)); + // +1 for NULL pointer at the end. + if (pglob->gl_pathv) + COMMON_INTERCEPTOR_WRITE_RANGE( + ctx, pglob->gl_pathv, (pglob->gl_pathc + 1) * sizeof(*pglob->gl_pathv)); + for (SIZE_T i = 0; i < pglob->gl_pathc; ++i) { + char *p = pglob->gl_pathv[i]; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, REAL(strlen)(p) + 1); + } +} + +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); + pglob_copy->gl_closedir(dir); +} + +static void *wrapped_gl_readdir(void *dir) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 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); + 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); + 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); + return pglob_copy->gl_stat(s, st); +} + +INTERCEPTOR(int, glob, const char *pattern, int flags, + int (*errfunc)(const char *epath, int eerrno), + __sanitizer_glob_t *pglob) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); + __sanitizer_glob_t glob_copy = {0, 0, 0, 0, wrapped_gl_closedir, + wrapped_gl_readdir, wrapped_gl_opendir, + wrapped_gl_lstat, wrapped_gl_stat}; + if (flags & glob_altdirfunc) { + Swap(pglob->gl_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + 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_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + Swap(pglob->gl_lstat, glob_copy.gl_lstat); + 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(int, glob64, const char *pattern, int flags, + int (*errfunc)(const char *epath, int eerrno), + __sanitizer_glob_t *pglob) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); + __sanitizer_glob_t glob_copy = {0, 0, 0, 0, wrapped_gl_closedir, + wrapped_gl_readdir, wrapped_gl_opendir, + wrapped_gl_lstat, wrapped_gl_stat}; + if (flags & glob_altdirfunc) { + Swap(pglob->gl_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + 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_closedir, glob_copy.gl_closedir); + Swap(pglob->gl_readdir, glob_copy.gl_readdir); + Swap(pglob->gl_opendir, glob_copy.gl_opendir); + Swap(pglob->gl_lstat, glob_copy.gl_lstat); + 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; +} +#define INIT_GLOB \ + INTERCEPT_FUNCTION(glob); \ + INTERCEPT_FUNCTION(glob64); +#else // SANITIZER_INTERCEPT_GLOB +#define INIT_GLOB +#endif // SANITIZER_INTERCEPT_GLOB + +#if SANITIZER_INTERCEPT_WAIT +// According to sys/wait.h, wait(), waitid(), waitpid() may have symbol version +// suffixes on Darwin. See the declaration of INTERCEPTOR_WITH_SUFFIX for +// details. +INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wait, status); + int res = REAL(wait)(status); + if (res != -1 && status) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); + return res; +} +INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop, + int options) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options); + int res = REAL(waitid)(idtype, id, infop, options); + if (res != -1 && infop) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz); + return res; +} +INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, waitpid, pid, status, options); + int res = REAL(waitpid)(pid, status, options); + if (res != -1 && status) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); + return res; +} +INTERCEPTOR(int, wait3, int *status, int options, void *rusage) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage); + int res = REAL(wait3)(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; +} +INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage); + 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_WAIT \ + INTERCEPT_FUNCTION(wait); \ + INTERCEPT_FUNCTION(waitid); \ + INTERCEPT_FUNCTION(waitpid); \ + INTERCEPT_FUNCTION(wait3); \ + INTERCEPT_FUNCTION(wait4); +#else +#define INIT_WAIT +#endif + +#if SANITIZER_INTERCEPT_INET +INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, inet_ntop, af, src, dst, size); + 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. + 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); + // FIXME: figure out read size based on the address family. + int res = REAL(inet_pton)(af, src, dst); + if (res == 1) { + uptr sz = __sanitizer_in_addr_sz(af); + if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); + } + return res; +} +#define INIT_INET \ + INTERCEPT_FUNCTION(inet_ntop); \ + INTERCEPT_FUNCTION(inet_pton); +#else +#define INIT_INET +#endif + +#if SANITIZER_INTERCEPT_INET +INTERCEPTOR(int, inet_aton, const char *cp, void *dst) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, inet_aton, cp, dst); + if (cp) COMMON_INTERCEPTOR_READ_RANGE(ctx, cp, REAL(strlen)(cp) + 1); + int res = REAL(inet_aton)(cp, dst); + if (res != 0) { + uptr sz = __sanitizer_in_addr_sz(af_inet); + if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); + } + return res; +} +#define INIT_INET_ATON INTERCEPT_FUNCTION(inet_aton); +#else +#define INIT_INET_ATON +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM +INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_getschedparam, thread, policy, param); + int res = REAL(pthread_getschedparam)(thread, policy, param); + if (res == 0) { + if (policy) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, policy, sizeof(*policy)); + if (param) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, sizeof(*param)); + } + return res; +} +#define INIT_PTHREAD_GETSCHEDPARAM INTERCEPT_FUNCTION(pthread_getschedparam); +#else +#define INIT_PTHREAD_GETSCHEDPARAM +#endif + +#if SANITIZER_INTERCEPT_GETADDRINFO +INTERCEPTOR(int, getaddrinfo, char *node, char *service, + struct __sanitizer_addrinfo *hints, + struct __sanitizer_addrinfo **out) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getaddrinfo, node, service, hints, out); + if (node) COMMON_INTERCEPTOR_READ_RANGE(ctx, node, REAL(strlen)(node) + 1); + if (service) + COMMON_INTERCEPTOR_READ_RANGE(ctx, service, REAL(strlen)(service) + 1); + if (hints) + COMMON_INTERCEPTOR_READ_RANGE(ctx, hints, sizeof(__sanitizer_addrinfo)); + int res = REAL(getaddrinfo)(node, service, hints, out); + if (res == 0 && out) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, out, sizeof(*out)); + struct __sanitizer_addrinfo *p = *out; + while (p) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); + if (p->ai_addr) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_addr, p->ai_addrlen); + if (p->ai_canonname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_canonname, + REAL(strlen)(p->ai_canonname) + 1); + p = p->ai_next; + } + } + return res; +} +#define INIT_GETADDRINFO INTERCEPT_FUNCTION(getaddrinfo); +#else +#define INIT_GETADDRINFO +#endif + +#if SANITIZER_INTERCEPT_GETNAMEINFO +INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host, + unsigned hostlen, char *serv, unsigned servlen, int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getnameinfo, sockaddr, salen, host, hostlen, + serv, servlen, flags); + // FIXME: consider adding READ_RANGE(sockaddr, salen) + // There is padding in in_addr that may make this too noisy + int res = + REAL(getnameinfo)(sockaddr, salen, host, hostlen, serv, servlen, flags); + if (res == 0) { + if (host && hostlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, host, REAL(strlen)(host) + 1); + if (serv && servlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, serv, REAL(strlen)(serv) + 1); + } + return res; +} +#define INIT_GETNAMEINFO INTERCEPT_FUNCTION(getnameinfo); +#else +#define INIT_GETNAMEINFO +#endif + +#if SANITIZER_INTERCEPT_GETSOCKNAME +INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getsockname, sock_fd, addr, addrlen); + COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); + int addrlen_in = *addrlen; + int res = REAL(getsockname)(sock_fd, addr, addrlen); + if (res == 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addrlen_in, *addrlen)); + } + return res; +} +#define INIT_GETSOCKNAME INTERCEPT_FUNCTION(getsockname); +#else +#define INIT_GETSOCKNAME +#endif + +#if SANITIZER_INTERCEPT_GETHOSTBYNAME || SANITIZER_INTERCEPT_GETHOSTBYNAME_R +static void write_hostent(void *ctx, struct __sanitizer_hostent *h) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h, sizeof(__sanitizer_hostent)); + if (h->h_name) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h->h_name, REAL(strlen)(h->h_name) + 1); + char **p = h->h_aliases; + while (*p) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1); + ++p; + } + COMMON_INTERCEPTOR_WRITE_RANGE( + ctx, h->h_aliases, (p - h->h_aliases + 1) * sizeof(*h->h_aliases)); + p = h->h_addr_list; + while (*p) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, h->h_length); + ++p; + } + COMMON_INTERCEPTOR_WRITE_RANGE( + ctx, h->h_addr_list, (p - h->h_addr_list + 1) * sizeof(*h->h_addr_list)); +} +#endif + +#if SANITIZER_INTERCEPT_GETHOSTBYNAME +INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname, char *name) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname, name); + struct __sanitizer_hostent *res = REAL(gethostbyname)(name); + if (res) write_hostent(ctx, res); + return res; +} + +INTERCEPTOR(struct __sanitizer_hostent *, gethostbyaddr, void *addr, int len, + int type) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr, addr, len, type); + COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); + struct __sanitizer_hostent *res = REAL(gethostbyaddr)(addr, len, type); + if (res) write_hostent(ctx, res); + return res; +} + +INTERCEPTOR(struct __sanitizer_hostent *, gethostent) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostent); + struct __sanitizer_hostent *res = REAL(gethostent)(); + if (res) write_hostent(ctx, res); + return res; +} + +INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname2, char *name, int af) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2, name, af); + struct __sanitizer_hostent *res = REAL(gethostbyname2)(name, af); + if (res) write_hostent(ctx, res); + return res; +} +#define INIT_GETHOSTBYNAME \ + INTERCEPT_FUNCTION(gethostent); \ + INTERCEPT_FUNCTION(gethostbyaddr); \ + INTERCEPT_FUNCTION(gethostbyname); \ + INTERCEPT_FUNCTION(gethostbyname2); +#else +#define INIT_GETHOSTBYNAME +#endif + +#if SANITIZER_INTERCEPT_GETHOSTBYNAME_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); + 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)); + } + return res; +} + +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) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr_r, addr, len, type, ret, buf, + buflen, result, h_errnop); + COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); + 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)); + } + return res; +} + +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); + 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)); + } + return res; +} +#define INIT_GETHOSTBYNAME_R \ + INTERCEPT_FUNCTION(gethostent_r); \ + INTERCEPT_FUNCTION(gethostbyaddr_r); \ + INTERCEPT_FUNCTION(gethostbyname_r); \ + INTERCEPT_FUNCTION(gethostbyname2_r); +#else +#define INIT_GETHOSTBYNAME_R +#endif + +#if SANITIZER_INTERCEPT_GETSOCKOPT +INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval, + int *optlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getsockopt, sockfd, level, optname, optval, + optlen); + if (optlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, optlen, sizeof(*optlen)); + int res = REAL(getsockopt)(sockfd, level, optname, optval, optlen); + if (res == 0) + if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen); + return res; +} +#define INIT_GETSOCKOPT INTERCEPT_FUNCTION(getsockopt); +#else +#define INIT_GETSOCKOPT +#endif + +#if SANITIZER_INTERCEPT_ACCEPT +INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, accept, fd, addr, addrlen); + unsigned addrlen0; + if (addrlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); + addrlen0 = *addrlen; + } + int fd2 = REAL(accept)(fd, addr, addrlen); + if (fd2 >= 0) { + if (fd >= 0) + COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); + if (addr && addrlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); + } + return fd2; +} +#define INIT_ACCEPT INTERCEPT_FUNCTION(accept); +#else +#define INIT_ACCEPT +#endif + +#if SANITIZER_INTERCEPT_ACCEPT4 +INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, accept4, fd, addr, addrlen, f); + unsigned addrlen0; + if (addrlen) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); + addrlen0 = *addrlen; + } + int fd2 = REAL(accept4)(fd, addr, addrlen, f); + if (fd2 >= 0) { + if (fd >= 0) + COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); + if (addr && addrlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); + } + return fd2; +} +#define INIT_ACCEPT4 INTERCEPT_FUNCTION(accept4); +#else +#define INIT_ACCEPT4 +#endif + +#if SANITIZER_INTERCEPT_MODF +INTERCEPTOR(double, modf, double x, double *iptr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, modf, x, iptr); + double res = REAL(modf)(x, iptr); + if (iptr) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); + } + return res; +} +INTERCEPTOR(float, modff, float x, float *iptr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, modff, x, iptr); + float res = REAL(modff)(x, iptr); + if (iptr) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); + } + return res; +} +INTERCEPTOR(long double, modfl, long double x, long double *iptr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, modfl, x, iptr); + long double res = REAL(modfl)(x, iptr); + if (iptr) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); + } + return res; +} +#define INIT_MODF \ + INTERCEPT_FUNCTION(modf); \ + INTERCEPT_FUNCTION(modff); \ + INTERCEPT_FUNCTION(modfl); +#else +#define INIT_MODF +#endif + +#if SANITIZER_INTERCEPT_RECVMSG +static void write_msghdr(void *ctx, struct __sanitizer_msghdr *msg, + SSIZE_T maxlen) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg, sizeof(*msg)); + if (msg->msg_name) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_name, + REAL(strlen)((char *)msg->msg_name) + 1); + if (msg->msg_iov) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_iov, + sizeof(*msg->msg_iov) * msg->msg_iovlen); + write_iovec(ctx, msg->msg_iov, msg->msg_iovlen, maxlen); + if (msg->msg_control) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_control, msg->msg_controllen); +} + +INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg, + int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, recvmsg, fd, msg, flags); + SSIZE_T res = REAL(recvmsg)(fd, msg, flags); + if (res >= 0) { + if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + if (msg) write_msghdr(ctx, msg, res); + } + return res; +} +#define INIT_RECVMSG INTERCEPT_FUNCTION(recvmsg); +#else +#define INIT_RECVMSG +#endif + +#if SANITIZER_INTERCEPT_GETPEERNAME +INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getpeername, sockfd, addr, addrlen); + unsigned addr_sz; + if (addrlen) addr_sz = *addrlen; + int res = REAL(getpeername)(sockfd, addr, addrlen); + if (!res && addr && addrlen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen)); + return res; +} +#define INIT_GETPEERNAME INTERCEPT_FUNCTION(getpeername); +#else +#define INIT_GETPEERNAME +#endif + +#if SANITIZER_INTERCEPT_SYSINFO +INTERCEPTOR(int, sysinfo, void *info) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sysinfo, info); + int res = REAL(sysinfo)(info); + if (!res && info) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, struct_sysinfo_sz); + return res; +} +#define INIT_SYSINFO INTERCEPT_FUNCTION(sysinfo); +#else +#define INIT_SYSINFO +#endif + +#if SANITIZER_INTERCEPT_READDIR +INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp); + __sanitizer_dirent *res = REAL(readdir)(dirp); + if (res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); + return res; +} + +INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry, + __sanitizer_dirent **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir_r, dirp, entry, result); + int res = REAL(readdir_r)(dirp, entry, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + if (*result) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); + } + return res; +} + +#define INIT_READDIR \ + INTERCEPT_FUNCTION(readdir); \ + INTERCEPT_FUNCTION(readdir_r); +#else +#define INIT_READDIR +#endif + +#if SANITIZER_INTERCEPT_READDIR64 +INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp); + __sanitizer_dirent64 *res = REAL(readdir64)(dirp); + if (res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); + return res; +} + +INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry, + __sanitizer_dirent64 **result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, readdir64_r, dirp, entry, result); + int res = REAL(readdir64_r)(dirp, entry, result); + if (!res) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + if (*result) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); + } + return res; +} +#define INIT_READDIR64 \ + INTERCEPT_FUNCTION(readdir64); \ + INTERCEPT_FUNCTION(readdir64_r); +#else +#define INIT_READDIR64 +#endif + +#if SANITIZER_INTERCEPT_PTRACE +INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data); + + if (data) { + if (request == ptrace_setregs) + COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_regs_struct_sz); + else if (request == ptrace_setfpregs) + 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_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); + } + } + + uptr res = REAL(ptrace)(request, pid, addr, data); + + if (!res && data) { + // Note that PEEK* requests assing 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); + else if (request == ptrace_getfpregs) + 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_getsiginfo) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz); + else if (request == ptrace_getregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iov->iov_base, iov->iov_len); + } + } + return res; +} + +#define INIT_PTRACE \ + INTERCEPT_FUNCTION(ptrace); +#else +#define INIT_PTRACE +#endif + +#if SANITIZER_INTERCEPT_SETLOCALE +INTERCEPTOR(char *, setlocale, int category, char *locale) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, setlocale, category, locale); + if (locale) + COMMON_INTERCEPTOR_READ_RANGE(ctx, locale, REAL(strlen)(locale) + 1); + char *res = REAL(setlocale)(category, locale); + if (res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} + +#define INIT_SETLOCALE \ + INTERCEPT_FUNCTION(setlocale); +#else +#define INIT_SETLOCALE +#endif + +#if SANITIZER_INTERCEPT_GETCWD +INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size); + char *res = REAL(getcwd)(buf, size); + if (res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_GETCWD \ + INTERCEPT_FUNCTION(getcwd); +#else +#define INIT_GETCWD +#endif + +#if SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME +INTERCEPTOR(char *, get_current_dir_name) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name); + char *res = REAL(get_current_dir_name)(); + if (res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} + +#define INIT_GET_CURRENT_DIR_NAME \ + INTERCEPT_FUNCTION(get_current_dir_name); +#else +#define INIT_GET_CURRENT_DIR_NAME +#endif + +#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)); + 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)); + return res; +} + +#define INIT_STRTOIMAX \ + INTERCEPT_FUNCTION(strtoimax); \ + INTERCEPT_FUNCTION(strtoumax); +#else +#define INIT_STRTOIMAX +#endif + +#if SANITIZER_INTERCEPT_MBSTOWCS +INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mbstowcs, dest, src, len); + SIZE_T res = REAL(mbstowcs)(dest, src, len); + if (res != (SIZE_T) - 1 && dest) { + SIZE_T write_cnt = res + (res < len); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len, + void *ps) { + void *ctx; + 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); + 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 + // \0 character. They write it iff they clear *src. + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +#define INIT_MBSTOWCS \ + INTERCEPT_FUNCTION(mbstowcs); \ + INTERCEPT_FUNCTION(mbsrtowcs); +#else +#define INIT_MBSTOWCS +#endif + +#if SANITIZER_INTERCEPT_MBSNRTOWCS +INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms, + SIZE_T len, void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, mbsnrtowcs, dest, src, nms, len, ps); + if (src) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); + } + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps); + if (res != (SIZE_T)(-1) && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); + } + return res; +} + +#define INIT_MBSNRTOWCS INTERCEPT_FUNCTION(mbsnrtowcs); +#else +#define INIT_MBSNRTOWCS +#endif + +#if SANITIZER_INTERCEPT_WCSTOMBS +INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcstombs, dest, src, len); + SIZE_T res = REAL(wcstombs)(dest, src, len); + if (res != (SIZE_T) - 1 && dest) { + SIZE_T write_cnt = res + (res < len); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len, + void *ps) { + void *ctx; + 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); + SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps); + if (res != (SIZE_T) - 1 && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +#define INIT_WCSTOMBS \ + INTERCEPT_FUNCTION(wcstombs); \ + INTERCEPT_FUNCTION(wcsrtombs); +#else +#define INIT_WCSTOMBS +#endif + +#if SANITIZER_INTERCEPT_WCSNRTOMBS +INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, + SIZE_T len, void *ps) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcsnrtombs, dest, src, nms, len, ps); + if (src) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); + if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); + } + if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); + SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps); + if (res != (SIZE_T) - 1 && dest && src) { + SIZE_T write_cnt = res + !*src; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); + } + return res; +} + +#define INIT_WCSNRTOMBS INTERCEPT_FUNCTION(wcsnrtombs); +#else +#define INIT_WCSNRTOMBS +#endif + + +#if SANITIZER_INTERCEPT_TCGETATTR +INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tcgetattr, fd, termios_p); + int res = REAL(tcgetattr)(fd, termios_p); + if (!res && termios_p) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, termios_p, struct_termios_sz); + return res; +} + +#define INIT_TCGETATTR INTERCEPT_FUNCTION(tcgetattr); +#else +#define INIT_TCGETATTR +#endif + + +#if SANITIZER_INTERCEPT_REALPATH +INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, realpath, path, resolved_path); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + + // Workaround a bug in glibc where dlsym(RTLD_NEXT, ...) returns the oldest + // 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; + if (!resolved_path) + allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1); + + char *res = REAL(realpath)(path, resolved_path); + if (allocated_path && !res) + WRAP(free)(allocated_path); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_REALPATH INTERCEPT_FUNCTION(realpath); +#else +#define INIT_REALPATH +#endif + +#if SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME +INTERCEPTOR(char *, canonicalize_file_name, const char *path) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, canonicalize_file_name, path); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + char *res = REAL(canonicalize_file_name)(path); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_CANONICALIZE_FILE_NAME INTERCEPT_FUNCTION(canonicalize_file_name); +#else +#define INIT_CANONICALIZE_FILE_NAME +#endif + +#if SANITIZER_INTERCEPT_CONFSTR +INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, confstr, name, buf, len); + SIZE_T res = REAL(confstr)(name, buf, len); + if (buf && res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len); + return res; +} +#define INIT_CONFSTR INTERCEPT_FUNCTION(confstr); +#else +#define INIT_CONFSTR +#endif + +#if SANITIZER_INTERCEPT_SCHED_GETAFFINITY +INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask); + int res = REAL(sched_getaffinity)(pid, cpusetsize, mask); + if (mask && !res) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize); + return res; +} +#define INIT_SCHED_GETAFFINITY INTERCEPT_FUNCTION(sched_getaffinity); +#else +#define INIT_SCHED_GETAFFINITY +#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_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_STRERROR INTERCEPT_FUNCTION(strerror); +#else +#define INIT_STRERROR +#endif + +#if SANITIZER_INTERCEPT_STRERROR_R +INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen); + 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, + // writes message to buf. + // * GNU version returns message pointer, which points to either buf or some + // static storage. + SIZE_T posix_res = (SIZE_T)res; + if (posix_res < 1024 || posix_res > (SIZE_T) - 1024) { + // POSIX version. Spec is not clear on whether buf is NULL-terminated. + // At least on OSX, buf contents are valid even when the call fails. + SIZE_T sz = internal_strnlen(buf, buflen); + if (sz < buflen) ++sz; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); + } else { + // GNU version. + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + } + return res; +} +#define INIT_STRERROR_R INTERCEPT_FUNCTION(strerror_r); +#else +#define INIT_STRERROR_R +#endif + +#if SANITIZER_INTERCEPT_SCANDIR +typedef int (*scandir_filter_f)(const struct __sanitizer_dirent *); +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); + 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); + return scandir_compar(a, b); +} + +INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist, + scandir_filter_f filter, scandir_compar_f compar) { + 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; + if (namelist && res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); + for (int i = 0; i < res; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], + (*namelist)[i]->d_reclen); + } + return res; +} +#define INIT_SCANDIR INTERCEPT_FUNCTION(scandir); +#else +#define INIT_SCANDIR +#endif + +#if SANITIZER_INTERCEPT_SCANDIR64 +typedef int (*scandir64_filter_f)(const struct __sanitizer_dirent64 *); +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); + 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); + return scandir64_compar(a, b); +} + +INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist, + scandir64_filter_f filter, scandir64_compar_f compar) { + 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; + 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; + if (namelist && res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); + for (int i = 0; i < res; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], + (*namelist)[i]->d_reclen); + } + return res; +} +#define INIT_SCANDIR64 INTERCEPT_FUNCTION(scandir64); +#else +#define INIT_SCANDIR64 +#endif + +#if SANITIZER_INTERCEPT_GETGROUPS +INTERCEPTOR(int, getgroups, int size, u32 *lst) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst); + int res = REAL(getgroups)(size, lst); + if (res && lst) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); + return res; +} +#define INIT_GETGROUPS INTERCEPT_FUNCTION(getgroups); +#else +#define INIT_GETGROUPS +#endif + +#if SANITIZER_INTERCEPT_POLL +static void read_pollfd(void *ctx, __sanitizer_pollfd *fds, + __sanitizer_nfds_t nfds) { + for (unsigned i = 0; i < nfds; ++i) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].fd, sizeof(fds[i].fd)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].events, sizeof(fds[i].events)); + } +} + +static void write_pollfd(void *ctx, __sanitizer_pollfd *fds, + __sanitizer_nfds_t nfds) { + for (unsigned i = 0; i < nfds; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &fds[i].revents, + sizeof(fds[i].revents)); +} + +INTERCEPTOR(int, poll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, + int timeout) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, poll, fds, nfds, timeout); + if (fds && nfds) read_pollfd(ctx, fds, nfds); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(poll)(fds, nfds, timeout); + if (fds && nfds) write_pollfd(ctx, fds, nfds); + return res; +} +#define INIT_POLL INTERCEPT_FUNCTION(poll); +#else +#define INIT_POLL +#endif + +#if SANITIZER_INTERCEPT_PPOLL +INTERCEPTOR(int, ppoll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, + void *timeout_ts, __sanitizer_sigset_t *sigmask) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ppoll, fds, nfds, timeout_ts, sigmask); + if (fds && nfds) read_pollfd(ctx, fds, nfds); + if (timeout_ts) + COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout_ts, struct_timespec_sz); + // FIXME: read sigmask when all of sigemptyset, etc are intercepted. + int res = + COMMON_INTERCEPTOR_BLOCK_REAL(ppoll)(fds, nfds, timeout_ts, sigmask); + if (fds && nfds) write_pollfd(ctx, fds, nfds); + return res; +} +#define INIT_PPOLL INTERCEPT_FUNCTION(ppoll); +#else +#define INIT_PPOLL +#endif + +#if SANITIZER_INTERCEPT_WORDEXP +INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wordexp, s, p, flags); + if (s) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1); + int res = REAL(wordexp)(s, p, flags); + if (!res && p) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); + if (p->we_wordc) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->we_wordv, + sizeof(*p->we_wordv) * p->we_wordc); + for (uptr i = 0; i < p->we_wordc; ++i) { + char *w = p->we_wordv[i]; + if (w) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, w, REAL(strlen)(w) + 1); + } + } + return res; +} +#define INIT_WORDEXP INTERCEPT_FUNCTION(wordexp); +#else +#define INIT_WORDEXP +#endif + +#if SANITIZER_INTERCEPT_SIGWAIT +INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigwait, set, sig); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigwait)(set, sig); + if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig)); + return res; +} +#define INIT_SIGWAIT INTERCEPT_FUNCTION(sigwait); +#else +#define INIT_SIGWAIT +#endif + +#if SANITIZER_INTERCEPT_SIGWAITINFO +INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigwaitinfo, set, info); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigwaitinfo)(set, info); + if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); + return res; +} +#define INIT_SIGWAITINFO INTERCEPT_FUNCTION(sigwaitinfo); +#else +#define INIT_SIGWAITINFO +#endif + +#if SANITIZER_INTERCEPT_SIGTIMEDWAIT +INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info, + void *timeout) { + void *ctx; + 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 + int res = REAL(sigtimedwait)(set, info, timeout); + if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); + return res; +} +#define INIT_SIGTIMEDWAIT INTERCEPT_FUNCTION(sigtimedwait); +#else +#define INIT_SIGTIMEDWAIT +#endif + +#if SANITIZER_INTERCEPT_SIGSETOPS +INTERCEPTOR(int, sigemptyset, __sanitizer_sigset_t *set) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigemptyset, set); + 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); + int res = REAL(sigfillset)(set); + if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); + return res; +} +#define INIT_SIGSETOPS \ + INTERCEPT_FUNCTION(sigemptyset); \ + INTERCEPT_FUNCTION(sigfillset); +#else +#define INIT_SIGSETOPS +#endif + +#if SANITIZER_INTERCEPT_SIGPENDING +INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigpending, set); + int res = REAL(sigpending)(set); + if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); + return res; +} +#define INIT_SIGPENDING INTERCEPT_FUNCTION(sigpending); +#else +#define INIT_SIGPENDING +#endif + +#if SANITIZER_INTERCEPT_SIGPROCMASK +INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset); + // FIXME: read sigset_t when all of sigemptyset, etc are intercepted + int res = REAL(sigprocmask)(how, set, oldset); + if (!res && oldset) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset)); + return res; +} +#define INIT_SIGPROCMASK INTERCEPT_FUNCTION(sigprocmask); +#else +#define INIT_SIGPROCMASK +#endif + +#if SANITIZER_INTERCEPT_BACKTRACE +INTERCEPTOR(int, backtrace, void **buffer, int size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size); + int res = REAL(backtrace)(buffer, size); + if (res && buffer) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer)); + return res; +} + +INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, backtrace_symbols, buffer, size); + if (buffer && size) + COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer)); + char ** res = REAL(backtrace_symbols)(buffer, size); + if (res && size) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res)); + for (int i = 0; i < size; ++i) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res[i], REAL(strlen(res[i])) + 1); + } + return res; +} +#define INIT_BACKTRACE \ + INTERCEPT_FUNCTION(backtrace); \ + INTERCEPT_FUNCTION(backtrace_symbols); +#else +#define INIT_BACKTRACE +#endif + +#define SANITIZER_COMMON_INTERCEPTORS_INIT \ + 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_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_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; diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc new file mode 100755 index 00000000000..50db0d71d89 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc @@ -0,0 +1,566 @@ +//===-- sanitizer_common_interceptors_ioctl.inc -----------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Ioctl handling in common sanitizer interceptors. +//===----------------------------------------------------------------------===// + +#include "sanitizer_flags.h" + +struct ioctl_desc { + unsigned req; + // FIXME: support read+write arguments. Those are currently marked as WRITE. + enum { + NONE, + READ, + WRITE, + CUSTOM + } type : 2; + unsigned size : 30; + const char* name; +}; + +const unsigned ioctl_table_max = 500; +static ioctl_desc ioctl_table[ioctl_table_max]; +static unsigned ioctl_table_size = 0; + +// This can not be declared as a global, because references to struct_*_sz +// require a global initializer. And this table must be available before global +// initializers are run. +static void ioctl_table_fill() { +#define _(rq, tp, sz) \ + if (IOCTL_##rq != IOCTL_NOT_PRESENT) { \ + CHECK(ioctl_table_size < ioctl_table_max); \ + ioctl_table[ioctl_table_size].req = IOCTL_##rq; \ + ioctl_table[ioctl_table_size].type = ioctl_desc::tp; \ + ioctl_table[ioctl_table_size].size = sz; \ + ioctl_table[ioctl_table_size].name = #rq; \ + ++ioctl_table_size; \ + } + + _(FIOASYNC, READ, sizeof(int)); + _(FIOCLEX, NONE, 0); + _(FIOGETOWN, WRITE, sizeof(int)); + _(FIONBIO, READ, sizeof(int)); + _(FIONCLEX, NONE, 0); + _(FIOSETOWN, READ, sizeof(int)); + _(SIOCADDMULTI, READ, struct_ifreq_sz); + _(SIOCATMARK, WRITE, sizeof(int)); + _(SIOCDELMULTI, READ, struct_ifreq_sz); + _(SIOCGIFADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFBRDADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFCONF, CUSTOM, 0); + _(SIOCGIFDSTADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFFLAGS, WRITE, struct_ifreq_sz); + _(SIOCGIFMETRIC, WRITE, struct_ifreq_sz); + _(SIOCGIFMTU, WRITE, struct_ifreq_sz); + _(SIOCGIFNETMASK, WRITE, struct_ifreq_sz); + _(SIOCGPGRP, WRITE, sizeof(int)); + _(SIOCSIFADDR, READ, struct_ifreq_sz); + _(SIOCSIFBRDADDR, READ, struct_ifreq_sz); + _(SIOCSIFDSTADDR, READ, struct_ifreq_sz); + _(SIOCSIFFLAGS, READ, struct_ifreq_sz); + _(SIOCSIFMETRIC, READ, struct_ifreq_sz); + _(SIOCSIFMTU, READ, struct_ifreq_sz); + _(SIOCSIFNETMASK, READ, struct_ifreq_sz); + _(SIOCSPGRP, READ, sizeof(int)); + _(TIOCCONS, NONE, 0); + _(TIOCEXCL, NONE, 0); + _(TIOCGETD, WRITE, sizeof(int)); + _(TIOCGPGRP, WRITE, pid_t_sz); + _(TIOCGWINSZ, WRITE, struct_winsize_sz); + _(TIOCMBIC, READ, sizeof(int)); + _(TIOCMBIS, READ, sizeof(int)); + _(TIOCMGET, WRITE, sizeof(int)); + _(TIOCMSET, READ, sizeof(int)); + _(TIOCNOTTY, NONE, 0); + _(TIOCNXCL, NONE, 0); + _(TIOCOUTQ, WRITE, sizeof(int)); + _(TIOCPKT, READ, sizeof(int)); + _(TIOCSCTTY, NONE, 0); + _(TIOCSETD, READ, sizeof(int)); + _(TIOCSPGRP, READ, pid_t_sz); + _(TIOCSTI, READ, sizeof(char)); + _(TIOCSWINSZ, READ, struct_winsize_sz); + +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_MAC + _(SIOCGETSGCNT, WRITE, struct_sioc_sg_req_sz); + _(SIOCGETVIFCNT, WRITE, struct_sioc_vif_req_sz); +#endif + +#if SANITIZER_LINUX + // Conflicting request ids. + // _(CDROMAUDIOBUFSIZ, NONE, 0); + // _(SNDCTL_TMR_CONTINUE, NONE, 0); + // _(SNDCTL_TMR_START, NONE, 0); + // _(SNDCTL_TMR_STOP, NONE, 0); + // _(SOUND_MIXER_READ_LOUD, WRITE, sizeof(int)); // same as ...READ_ENHANCE + // _(SOUND_MIXER_READ_MUTE, WRITE, sizeof(int)); // same as ...READ_ENHANCE + // _(SOUND_MIXER_WRITE_LOUD, WRITE, sizeof(int)); // same as ...WRITE_ENHANCE + // _(SOUND_MIXER_WRITE_MUTE, WRITE, sizeof(int)); // same as ...WRITE_ENHANCE + _(BLKFLSBUF, NONE, 0); + _(BLKGETSIZE, WRITE, sizeof(uptr)); + _(BLKRAGET, WRITE, sizeof(int)); + _(BLKRASET, NONE, 0); + _(BLKROGET, WRITE, sizeof(int)); + _(BLKROSET, READ, sizeof(int)); + _(BLKRRPART, NONE, 0); + _(CDROMEJECT, NONE, 0); + _(CDROMEJECT_SW, NONE, 0); + _(CDROMMULTISESSION, WRITE, struct_cdrom_multisession_sz); + _(CDROMPAUSE, NONE, 0); + _(CDROMPLAYMSF, READ, struct_cdrom_msf_sz); + _(CDROMPLAYTRKIND, READ, struct_cdrom_ti_sz); + _(CDROMREADAUDIO, READ, struct_cdrom_read_audio_sz); + _(CDROMREADCOOKED, READ, struct_cdrom_msf_sz); + _(CDROMREADMODE1, READ, struct_cdrom_msf_sz); + _(CDROMREADMODE2, READ, struct_cdrom_msf_sz); + _(CDROMREADRAW, READ, struct_cdrom_msf_sz); + _(CDROMREADTOCENTRY, WRITE, struct_cdrom_tocentry_sz); + _(CDROMREADTOCHDR, WRITE, struct_cdrom_tochdr_sz); + _(CDROMRESET, NONE, 0); + _(CDROMRESUME, NONE, 0); + _(CDROMSEEK, READ, struct_cdrom_msf_sz); + _(CDROMSTART, NONE, 0); + _(CDROMSTOP, NONE, 0); + _(CDROMSUBCHNL, WRITE, struct_cdrom_subchnl_sz); + _(CDROMVOLCTRL, READ, struct_cdrom_volctrl_sz); + _(CDROMVOLREAD, WRITE, struct_cdrom_volctrl_sz); + _(CDROM_GET_UPC, WRITE, 8); + _(EVIOCGABS, WRITE, struct_input_absinfo_sz); // fixup + _(EVIOCGBIT, WRITE, struct_input_id_sz); // fixup + _(EVIOCGEFFECTS, WRITE, sizeof(int)); + _(EVIOCGID, WRITE, struct_input_id_sz); + _(EVIOCGKEY, WRITE, 0); + _(EVIOCGKEYCODE, WRITE, sizeof(int) * 2); + _(EVIOCGLED, WRITE, 0); + _(EVIOCGNAME, WRITE, 0); + _(EVIOCGPHYS, WRITE, 0); + _(EVIOCGRAB, READ, sizeof(int)); + _(EVIOCGREP, WRITE, sizeof(int) * 2); + _(EVIOCGSND, WRITE, 0); + _(EVIOCGSW, WRITE, 0); + _(EVIOCGUNIQ, WRITE, 0); + _(EVIOCGVERSION, WRITE, sizeof(int)); + _(EVIOCRMFF, READ, sizeof(int)); + _(EVIOCSABS, READ, struct_input_absinfo_sz); // fixup + _(EVIOCSFF, READ, struct_ff_effect_sz); + _(EVIOCSKEYCODE, READ, sizeof(int) * 2); + _(EVIOCSREP, READ, sizeof(int) * 2); + _(FDCLRPRM, NONE, 0); + _(FDDEFPRM, READ, struct_floppy_struct_sz); + _(FDFLUSH, NONE, 0); + _(FDFMTBEG, NONE, 0); + _(FDFMTEND, NONE, 0); + _(FDFMTTRK, READ, struct_format_descr_sz); + _(FDGETDRVPRM, WRITE, struct_floppy_drive_params_sz); + _(FDGETDRVSTAT, WRITE, struct_floppy_drive_struct_sz); + _(FDGETDRVTYP, WRITE, 16); + _(FDGETFDCSTAT, WRITE, struct_floppy_fdc_state_sz); + _(FDGETMAXERRS, WRITE, struct_floppy_max_errors_sz); + _(FDGETPRM, WRITE, struct_floppy_struct_sz); + _(FDMSGOFF, NONE, 0); + _(FDMSGON, NONE, 0); + _(FDPOLLDRVSTAT, WRITE, struct_floppy_drive_struct_sz); + _(FDRAWCMD, WRITE, struct_floppy_raw_cmd_sz); + _(FDRESET, NONE, 0); + _(FDSETDRVPRM, READ, struct_floppy_drive_params_sz); + _(FDSETEMSGTRESH, NONE, 0); + _(FDSETMAXERRS, READ, struct_floppy_max_errors_sz); + _(FDSETPRM, READ, struct_floppy_struct_sz); + _(FDTWADDLE, NONE, 0); + _(FDWERRORCLR, NONE, 0); + _(FDWERRORGET, WRITE, struct_floppy_write_errors_sz); + _(HDIO_DRIVE_CMD, WRITE, sizeof(int)); + _(HDIO_GETGEO, WRITE, struct_hd_geometry_sz); + _(HDIO_GET_32BIT, WRITE, sizeof(int)); + _(HDIO_GET_DMA, WRITE, sizeof(int)); + _(HDIO_GET_IDENTITY, WRITE, struct_hd_driveid_sz); + _(HDIO_GET_KEEPSETTINGS, WRITE, sizeof(int)); + _(HDIO_GET_MULTCOUNT, WRITE, sizeof(int)); + _(HDIO_GET_NOWERR, WRITE, sizeof(int)); + _(HDIO_GET_UNMASKINTR, WRITE, sizeof(int)); + _(HDIO_SET_32BIT, NONE, 0); + _(HDIO_SET_DMA, NONE, 0); + _(HDIO_SET_KEEPSETTINGS, NONE, 0); + _(HDIO_SET_MULTCOUNT, NONE, 0); + _(HDIO_SET_NOWERR, NONE, 0); + _(HDIO_SET_UNMASKINTR, NONE, 0); + _(MTIOCGET, WRITE, struct_mtget_sz); + _(MTIOCPOS, WRITE, struct_mtpos_sz); + _(MTIOCTOP, READ, struct_mtop_sz); + _(PPPIOCGASYNCMAP, WRITE, sizeof(int)); + _(PPPIOCGDEBUG, WRITE, sizeof(int)); + _(PPPIOCGFLAGS, WRITE, sizeof(int)); + _(PPPIOCGUNIT, WRITE, sizeof(int)); + _(PPPIOCGXASYNCMAP, WRITE, sizeof(int) * 8); + _(PPPIOCSASYNCMAP, READ, sizeof(int)); + _(PPPIOCSDEBUG, READ, sizeof(int)); + _(PPPIOCSFLAGS, READ, sizeof(int)); + _(PPPIOCSMAXCID, READ, sizeof(int)); + _(PPPIOCSMRU, READ, sizeof(int)); + _(PPPIOCSXASYNCMAP, READ, sizeof(int) * 8); + _(SIOCADDRT, READ, struct_rtentry_sz); + _(SIOCDARP, READ, struct_arpreq_sz); + _(SIOCDELRT, READ, struct_rtentry_sz); + _(SIOCDRARP, READ, struct_arpreq_sz); + _(SIOCGARP, WRITE, struct_arpreq_sz); + _(SIOCGIFENCAP, WRITE, sizeof(int)); + _(SIOCGIFHWADDR, WRITE, struct_ifreq_sz); + _(SIOCGIFMAP, WRITE, struct_ifreq_sz); + _(SIOCGIFMEM, WRITE, struct_ifreq_sz); + _(SIOCGIFNAME, NONE, 0); + _(SIOCGIFSLAVE, NONE, 0); + _(SIOCGRARP, WRITE, struct_arpreq_sz); + _(SIOCGSTAMP, WRITE, timeval_sz); + _(SIOCSARP, READ, struct_arpreq_sz); + _(SIOCSIFENCAP, READ, sizeof(int)); + _(SIOCSIFHWADDR, READ, struct_ifreq_sz); + _(SIOCSIFLINK, NONE, 0); + _(SIOCSIFMAP, READ, struct_ifreq_sz); + _(SIOCSIFMEM, READ, struct_ifreq_sz); + _(SIOCSIFSLAVE, NONE, 0); + _(SIOCSRARP, READ, struct_arpreq_sz); + _(SNDCTL_COPR_HALT, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_LOAD, READ, struct_copr_buffer_sz); + _(SNDCTL_COPR_RCODE, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_RCVMSG, WRITE, struct_copr_msg_sz); + _(SNDCTL_COPR_RDATA, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_RESET, NONE, 0); + _(SNDCTL_COPR_RUN, WRITE, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_SENDMSG, READ, struct_copr_msg_sz); + _(SNDCTL_COPR_WCODE, READ, struct_copr_debug_buf_sz); + _(SNDCTL_COPR_WDATA, READ, struct_copr_debug_buf_sz); + _(SNDCTL_DSP_GETBLKSIZE, WRITE, sizeof(int)); + _(SNDCTL_DSP_GETFMTS, WRITE, sizeof(int)); + _(SNDCTL_DSP_NONBLOCK, NONE, 0); + _(SNDCTL_DSP_POST, NONE, 0); + _(SNDCTL_DSP_RESET, NONE, 0); + _(SNDCTL_DSP_SETFMT, WRITE, sizeof(int)); + _(SNDCTL_DSP_SETFRAGMENT, WRITE, sizeof(int)); + _(SNDCTL_DSP_SPEED, WRITE, sizeof(int)); + _(SNDCTL_DSP_STEREO, WRITE, sizeof(int)); + _(SNDCTL_DSP_SUBDIVIDE, WRITE, sizeof(int)); + _(SNDCTL_DSP_SYNC, NONE, 0); + _(SNDCTL_FM_4OP_ENABLE, READ, sizeof(int)); + _(SNDCTL_FM_LOAD_INSTR, READ, struct_sbi_instrument_sz); + _(SNDCTL_MIDI_INFO, WRITE, struct_midi_info_sz); + _(SNDCTL_MIDI_PRETIME, WRITE, sizeof(int)); + _(SNDCTL_SEQ_CTRLRATE, WRITE, sizeof(int)); + _(SNDCTL_SEQ_GETINCOUNT, WRITE, sizeof(int)); + _(SNDCTL_SEQ_GETOUTCOUNT, WRITE, sizeof(int)); + _(SNDCTL_SEQ_NRMIDIS, WRITE, sizeof(int)); + _(SNDCTL_SEQ_NRSYNTHS, WRITE, sizeof(int)); + _(SNDCTL_SEQ_OUTOFBAND, READ, struct_seq_event_rec_sz); + _(SNDCTL_SEQ_PANIC, NONE, 0); + _(SNDCTL_SEQ_PERCMODE, NONE, 0); + _(SNDCTL_SEQ_RESET, NONE, 0); + _(SNDCTL_SEQ_RESETSAMPLES, READ, sizeof(int)); + _(SNDCTL_SEQ_SYNC, NONE, 0); + _(SNDCTL_SEQ_TESTMIDI, READ, sizeof(int)); + _(SNDCTL_SEQ_THRESHOLD, READ, sizeof(int)); + _(SNDCTL_SYNTH_INFO, WRITE, struct_synth_info_sz); + _(SNDCTL_SYNTH_MEMAVL, WRITE, sizeof(int)); + _(SNDCTL_TMR_METRONOME, READ, sizeof(int)); + _(SNDCTL_TMR_SELECT, WRITE, sizeof(int)); + _(SNDCTL_TMR_SOURCE, WRITE, sizeof(int)); + _(SNDCTL_TMR_TEMPO, WRITE, sizeof(int)); + _(SNDCTL_TMR_TIMEBASE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_ALTPCM, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_BASS, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_CAPS, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_CD, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_DEVMASK, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_ENHANCE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_IGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_IMIX, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE1, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE2, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_LINE3, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_MIC, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_OGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_PCM, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_RECLEV, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_RECMASK, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_RECSRC, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_SPEAKER, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_STEREODEVS, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_SYNTH, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_TREBLE, WRITE, sizeof(int)); + _(SOUND_MIXER_READ_VOLUME, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_ALTPCM, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_BASS, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_CD, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_ENHANCE, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_IGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_IMIX, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE1, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE2, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_LINE3, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_MIC, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_OGAIN, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_PCM, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_RECLEV, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_RECSRC, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_SPEAKER, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_SYNTH, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_TREBLE, WRITE, sizeof(int)); + _(SOUND_MIXER_WRITE_VOLUME, WRITE, sizeof(int)); + _(SOUND_PCM_READ_BITS, WRITE, sizeof(int)); + _(SOUND_PCM_READ_CHANNELS, WRITE, sizeof(int)); + _(SOUND_PCM_READ_FILTER, WRITE, sizeof(int)); + _(SOUND_PCM_READ_RATE, WRITE, sizeof(int)); + _(SOUND_PCM_WRITE_CHANNELS, WRITE, sizeof(int)); + _(SOUND_PCM_WRITE_FILTER, WRITE, sizeof(int)); + _(TCFLSH, NONE, 0); + _(TCGETA, WRITE, struct_termio_sz); + _(TCGETS, WRITE, struct_termios_sz); + _(TCSBRK, NONE, 0); + _(TCSBRKP, NONE, 0); + _(TCSETA, READ, struct_termio_sz); + _(TCSETAF, READ, struct_termio_sz); + _(TCSETAW, READ, struct_termio_sz); + _(TCSETS, READ, struct_termios_sz); + _(TCSETSF, READ, struct_termios_sz); + _(TCSETSW, READ, struct_termios_sz); + _(TCXONC, NONE, 0); + _(TIOCGLCKTRMIOS, WRITE, struct_termios_sz); + _(TIOCGSOFTCAR, WRITE, sizeof(int)); + _(TIOCINQ, WRITE, sizeof(int)); + _(TIOCLINUX, READ, sizeof(char)); + _(TIOCSERCONFIG, NONE, 0); + _(TIOCSERGETLSR, WRITE, sizeof(int)); + _(TIOCSERGWILD, WRITE, sizeof(int)); + _(TIOCSERSWILD, READ, sizeof(int)); + _(TIOCSLCKTRMIOS, READ, struct_termios_sz); + _(TIOCSSOFTCAR, READ, sizeof(int)); + _(VT_ACTIVATE, NONE, 0); + _(VT_DISALLOCATE, NONE, 0); + _(VT_GETMODE, WRITE, struct_vt_mode_sz); + _(VT_GETSTATE, WRITE, struct_vt_stat_sz); + _(VT_OPENQRY, WRITE, sizeof(int)); + _(VT_RELDISP, NONE, 0); + _(VT_RESIZE, READ, struct_vt_sizes_sz); + _(VT_RESIZEX, READ, struct_vt_consize_sz); + _(VT_SENDSIG, NONE, 0); + _(VT_SETMODE, READ, struct_vt_mode_sz); + _(VT_WAITACTIVE, NONE, 0); +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + // _(SIOCDEVPLIP, WRITE, struct_ifreq_sz); // the same as EQL_ENSLAVE + _(CYGETDEFTHRESH, WRITE, sizeof(int)); + _(CYGETDEFTIMEOUT, WRITE, sizeof(int)); + _(CYGETMON, WRITE, struct_cyclades_monitor_sz); + _(CYGETTHRESH, WRITE, sizeof(int)); + _(CYGETTIMEOUT, WRITE, sizeof(int)); + _(CYSETDEFTHRESH, NONE, 0); + _(CYSETDEFTIMEOUT, NONE, 0); + _(CYSETTHRESH, NONE, 0); + _(CYSETTIMEOUT, NONE, 0); + _(EQL_EMANCIPATE, WRITE, struct_ifreq_sz); + _(EQL_ENSLAVE, WRITE, struct_ifreq_sz); + _(EQL_GETMASTRCFG, WRITE, struct_ifreq_sz); + _(EQL_GETSLAVECFG, WRITE, struct_ifreq_sz); + _(EQL_SETMASTRCFG, WRITE, struct_ifreq_sz); + _(EQL_SETSLAVECFG, WRITE, struct_ifreq_sz); + _(EVIOCGKEYCODE_V2, WRITE, struct_input_keymap_entry_sz); + _(EVIOCGPROP, WRITE, 0); + _(EVIOCSKEYCODE_V2, READ, struct_input_keymap_entry_sz); + _(FS_IOC_GETFLAGS, WRITE, sizeof(int)); + _(FS_IOC_GETVERSION, WRITE, sizeof(int)); + _(FS_IOC_SETFLAGS, READ, sizeof(int)); + _(FS_IOC_SETVERSION, READ, sizeof(int)); + _(GIO_CMAP, WRITE, 48); + _(GIO_FONT, WRITE, 8192); + _(GIO_SCRNMAP, WRITE, e_tabsz); + _(GIO_UNIMAP, WRITE, struct_unimapdesc_sz); + _(GIO_UNISCRNMAP, WRITE, sizeof(short) * e_tabsz); + _(KDADDIO, NONE, 0); + _(KDDELIO, NONE, 0); + _(KDDISABIO, NONE, 0); + _(KDENABIO, NONE, 0); + _(KDGETKEYCODE, WRITE, struct_kbkeycode_sz); + _(KDGETLED, WRITE, 1); + _(KDGETMODE, WRITE, sizeof(int)); + _(KDGKBDIACR, WRITE, struct_kbdiacrs_sz); + _(KDGKBENT, WRITE, struct_kbentry_sz); + _(KDGKBLED, WRITE, sizeof(int)); + _(KDGKBMETA, WRITE, sizeof(int)); + _(KDGKBMODE, WRITE, sizeof(int)); + _(KDGKBSENT, WRITE, struct_kbsentry_sz); + _(KDGKBTYPE, WRITE, 1); + _(KDMAPDISP, NONE, 0); + _(KDMKTONE, NONE, 0); + _(KDSETKEYCODE, READ, struct_kbkeycode_sz); + _(KDSETLED, NONE, 0); + _(KDSETMODE, NONE, 0); + _(KDSIGACCEPT, NONE, 0); + _(KDSKBDIACR, READ, struct_kbdiacrs_sz); + _(KDSKBENT, READ, struct_kbentry_sz); + _(KDSKBLED, NONE, 0); + _(KDSKBMETA, NONE, 0); + _(KDSKBMODE, NONE, 0); + _(KDSKBSENT, READ, struct_kbsentry_sz); + _(KDUNMAPDISP, NONE, 0); + _(KIOCSOUND, NONE, 0); + _(LPABORT, NONE, 0); + _(LPABORTOPEN, NONE, 0); + _(LPCAREFUL, NONE, 0); + _(LPCHAR, NONE, 0); + _(LPGETIRQ, WRITE, sizeof(int)); + _(LPGETSTATUS, WRITE, sizeof(int)); + _(LPRESET, NONE, 0); + _(LPSETIRQ, NONE, 0); + _(LPTIME, NONE, 0); + _(LPWAIT, NONE, 0); + _(MTIOCGETCONFIG, WRITE, struct_mtconfiginfo_sz); + _(MTIOCSETCONFIG, READ, struct_mtconfiginfo_sz); + _(PIO_CMAP, NONE, 0); + _(PIO_FONT, READ, 8192); + _(PIO_SCRNMAP, READ, e_tabsz); + _(PIO_UNIMAP, READ, struct_unimapdesc_sz); + _(PIO_UNIMAPCLR, READ, struct_unimapinit_sz); + _(PIO_UNISCRNMAP, READ, sizeof(short) * e_tabsz); + _(SCSI_IOCTL_PROBE_HOST, READ, sizeof(int)); + _(SCSI_IOCTL_TAGGED_DISABLE, NONE, 0); + _(SCSI_IOCTL_TAGGED_ENABLE, NONE, 0); + _(SNDCTL_DSP_GETISPACE, WRITE, struct_audio_buf_info_sz); + _(SNDCTL_DSP_GETOSPACE, WRITE, struct_audio_buf_info_sz); + _(TIOCGSERIAL, WRITE, struct_serial_struct_sz); + _(TIOCSERGETMULTI, WRITE, struct_serial_multiport_struct_sz); + _(TIOCSERSETMULTI, READ, struct_serial_multiport_struct_sz); + _(TIOCSSERIAL, READ, struct_serial_struct_sz); + + // The following ioctl requests are shared between AX25, IPX, netrom and + // mrouted. + // _(SIOCAIPXITFCRT, READ, sizeof(char)); + // _(SIOCAX25GETUID, READ, struct_sockaddr_ax25_sz); + // _(SIOCNRGETPARMS, WRITE, struct_nr_parms_struct_sz); + // _(SIOCAIPXPRISLT, READ, sizeof(char)); + // _(SIOCNRSETPARMS, READ, struct_nr_parms_struct_sz); + // _(SIOCAX25ADDUID, READ, struct_sockaddr_ax25_sz); + // _(SIOCNRDECOBS, NONE, 0); + // _(SIOCAX25DELUID, READ, struct_sockaddr_ax25_sz); + // _(SIOCIPXCFGDATA, WRITE, struct_ipx_config_data_sz); + // _(SIOCAX25NOUID, READ, sizeof(int)); + // _(SIOCNRRTCTL, READ, sizeof(int)); + // _(SIOCAX25DIGCTL, READ, sizeof(int)); + // _(SIOCAX25GETPARMS, WRITE, struct_ax25_parms_struct_sz); + // _(SIOCAX25SETPARMS, READ, struct_ax25_parms_struct_sz); +#endif +#undef _ +} + +static bool ioctl_initialized = false; + +struct ioctl_desc_compare { + bool operator()(const ioctl_desc& left, const ioctl_desc& right) const { + return left.req < right.req; + } +}; + +static void ioctl_init() { + ioctl_table_fill(); + InternalSort(&ioctl_table, ioctl_table_size, ioctl_desc_compare()); + + bool bad = false; + for (unsigned i = 0; i < ioctl_table_size - 1; ++i) { + if (ioctl_table[i].req >= ioctl_table[i + 1].req) { + Printf("Duplicate or unsorted ioctl request id %x >= %x (%s vs %s)\n", + ioctl_table[i].req, ioctl_table[i + 1].req, ioctl_table[i].name, + ioctl_table[i + 1].name); + bad = true; + } + } + + if (bad) Die(); + + ioctl_initialized = true; +} + +// 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) + return IOCTL_EVIOCGBIT; + if ((req & ~0x3fU) == IOCTL_EVIOCGABS) + return IOCTL_EVIOCGABS; + if ((req & ~0x3fU) == IOCTL_EVIOCSABS) + return IOCTL_EVIOCSABS; +#endif + return req; +} + +static const ioctl_desc *ioctl_table_lookup(unsigned req) { + int left = 0; + int right = ioctl_table_size; + while (left < right) { + int mid = (left + right) / 2; + if (ioctl_table[mid].req < req) + left = mid + 1; + else + right = mid; + } + if (left == right && ioctl_table[left].req == req) + return ioctl_table + left; + else + return 0; +} + +static const ioctl_desc *ioctl_lookup(unsigned req) { + req = ioctl_request_fixup(req); + const ioctl_desc *desc = ioctl_table_lookup(req); + if (desc) return desc; + + // Try stripping access size from the request id. + desc = ioctl_table_lookup(req & ~0x3fff0000U); + // 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)) + return desc; + return 0; +} + +static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d, + unsigned request, void *arg) { + if (desc->type == ioctl_desc::READ) { + 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; + } + } + 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) { + // 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; + } + } + return; +} diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc index 5b761382d3e..2660dada2be 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc @@ -276,7 +276,7 @@ static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc, CHECK_GT(n_inputs, 0); const char *p = format; - while (*p && n_inputs) { + while (*p) { ScanfDirective dir; p = scanf_parse_next(p, allowGnuMalloc, &dir); if (!p) @@ -299,6 +299,8 @@ static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc, 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; } diff --git a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc new file mode 100644 index 00000000000..215a61deab6 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc @@ -0,0 +1,35 @@ +//===-- sanitizer_common_libcdep.cc ---------------------------------------===// +// +// 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_common.h" + +namespace __sanitizer { + +bool PrintsToTty() { + MaybeOpenReportFile(); + return internal_isatty(report_fd) != 0; +} + +bool PrintsToTtyCached() { + // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color + // printing on Windows. + if (SANITIZER_WINDOWS) + return 0; + + static int cached = 0; + static bool prints_to_tty; + if (!cached) { // Not thread-safe. + prints_to_tty = PrintsToTty(); + cached = 1; + } + return prints_to_tty; +} +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc b/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc new file mode 100644 index 00000000000..bc097c847d4 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc @@ -0,0 +1,2732 @@ +//===-- sanitizer_common_syscalls.inc ---------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Common syscalls handlers for tools like AddressSanitizer, +// ThreadSanitizer, MemorySanitizer, etc. +// +// This file should be included into the tool's interceptor file, +// which has to define it's own macros: +// COMMON_SYSCALL_PRE_READ_RANGE +// Called in prehook for regions that will be read by the kernel and +// must be initialized. +// COMMON_SYSCALL_PRE_WRITE_RANGE +// Called in prehook for regions that will be written to by the kernel +// and must be addressable. The actual write range may be smaller than +// reported in the prehook. See POST_WRITE_RANGE. +// COMMON_SYSCALL_POST_READ_RANGE +// Called in posthook for regions that were read by the kernel. Does +// not make much sense. +// COMMON_SYSCALL_POST_WRITE_RANGE +// Called in posthook for regions that were written to by the kernel +// and are now initialized. +// COMMON_SYSCALL_FD_CLOSE(fd) +// Called before closing file descriptor fd. +// COMMON_SYSCALL_PRE_FORK() +// Called before fork syscall. +// COMMON_SYSCALL_POST_FORK(long res) +// Called after fork syscall. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_libc.h" + +#define PRE_SYSCALL(name) \ + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_pre_impl_##name +#define PRE_READ(p, s) COMMON_SYSCALL_PRE_READ_RANGE(p, s) +#define PRE_WRITE(p, s) COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) + +#define POST_SYSCALL(name) \ + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_post_impl_##name +#define POST_READ(p, s) COMMON_SYSCALL_POST_READ_RANGE(p, s) +#define POST_WRITE(p, s) COMMON_SYSCALL_POST_WRITE_RANGE(p, s) + +#ifndef COMMON_SYSCALL_FD_CLOSE +# define COMMON_SYSCALL_FD_CLOSE(fd) +#endif + +#ifndef COMMON_SYSCALL_PRE_FORK +# define COMMON_SYSCALL_PRE_FORK() +#endif + +#ifndef COMMON_SYSCALL_POST_FORK +# define COMMON_SYSCALL_POST_FORK(res) +#endif + +// FIXME: do some kind of PRE_READ for all syscall arguments (int(s) and such). + +extern "C" { +struct sanitizer_kernel_iovec { + void *iov_base; + unsigned long iov_len; +}; + +struct sanitizer_kernel_msghdr { + void *msg_name; + int msg_namelen; + struct sanitizer_kernel_iovec *msg_iov; + unsigned long msg_iovlen; + void *msg_control; + unsigned long msg_controllen; + unsigned msg_flags; +}; + +struct sanitizer_kernel_mmsghdr { + struct sanitizer_kernel_msghdr msg_hdr; + unsigned msg_len; +}; + +struct sanitizer_kernel_timespec { + long tv_sec; + long tv_nsec; +}; + +struct sanitizer_kernel_timeval { + long tv_sec; + long tv_usec; +}; + +struct sanitizer_kernel_rusage { + struct sanitizer_kernel_timeval ru_timeval[2]; + long ru_long[14]; +}; + +struct sanitizer_kernel_sockaddr { + unsigned short sa_family; + char sa_data[14]; +}; + +// Real sigset size is always passed as a syscall argument. +// Declare it "void" to catch sizeof(kernel_sigset_t). +typedef void kernel_sigset_t; + +static void kernel_write_iovec(const __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + POST_WRITE(iovec[i].iov_base, sz); + maxlen -= sz; + } +} + +// This functions uses POST_READ, because it needs to run after syscall to know +// the real read range. +static void kernel_read_iovec(const __sanitizer_iovec *iovec, + SIZE_T iovlen, SIZE_T maxlen) { + POST_READ(iovec, sizeof(*iovec) * iovlen); + for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { + SSIZE_T sz = Min(iovec[i].iov_len, maxlen); + POST_READ(iovec[i].iov_base, sz); + maxlen -= sz; + } +} + +PRE_SYSCALL(recvmsg)(long sockfd, sanitizer_kernel_msghdr *msg, long flags) { + PRE_READ(msg, sizeof(*msg)); +} + +POST_SYSCALL(recvmsg)(long res, long sockfd, sanitizer_kernel_msghdr *msg, + long flags) { + if (res >= 0) { + if (msg) { + for (unsigned long i = 0; i < msg->msg_iovlen; ++i) { + POST_WRITE(msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len); + } + POST_WRITE(msg->msg_control, msg->msg_controllen); + } + } +} + +PRE_SYSCALL(recvmmsg)(long fd, sanitizer_kernel_mmsghdr *msg, long vlen, + long flags, void *timeout) { + PRE_READ(msg, vlen * sizeof(*msg)); +} + +POST_SYSCALL(recvmmsg)(long res, long fd, sanitizer_kernel_mmsghdr *msg, + long vlen, long flags, void *timeout) { + if (res >= 0) { + if (msg) { + for (unsigned long i = 0; i < msg->msg_hdr.msg_iovlen; ++i) { + POST_WRITE(msg->msg_hdr.msg_iov[i].iov_base, + msg->msg_hdr.msg_iov[i].iov_len); + } + POST_WRITE(msg->msg_hdr.msg_control, msg->msg_hdr.msg_controllen); + POST_WRITE(&msg->msg_len, sizeof(msg->msg_len)); + } + if (timeout) POST_WRITE(timeout, struct_timespec_sz); + } +} + +PRE_SYSCALL(read)(long fd, void *buf, uptr count) { + if (buf) { + PRE_WRITE(buf, count); + } +} + +POST_SYSCALL(read)(long res, long fd, void *buf, uptr count) { + if (res > 0 && buf) { + POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(time)(void *tloc) {} + +POST_SYSCALL(time)(long res, void *tloc) { + if (res >= 0) { + if (tloc) POST_WRITE(tloc, sizeof(long)); + } +} + +PRE_SYSCALL(stime)(void *tptr) {} + +POST_SYSCALL(stime)(long res, void *tptr) { + if (res >= 0) { + if (tptr) POST_WRITE(tptr, sizeof(long)); + } +} + +PRE_SYSCALL(gettimeofday)(void *tv, void *tz) {} + +POST_SYSCALL(gettimeofday)(long res, void *tv, void *tz) { + if (res >= 0) { + if (tv) POST_WRITE(tv, timeval_sz); + if (tz) POST_WRITE(tz, struct_timezone_sz); + } +} + +PRE_SYSCALL(settimeofday)(void *tv, void *tz) {} + +POST_SYSCALL(settimeofday)(long res, void *tv, void *tz) { + if (res >= 0) { + if (tv) POST_WRITE(tv, timeval_sz); + if (tz) POST_WRITE(tz, struct_timezone_sz); + } +} + +PRE_SYSCALL(adjtimex)(void *txc_p) {} + +POST_SYSCALL(adjtimex)(long res, void *txc_p) { + if (res >= 0) { + if (txc_p) POST_WRITE(txc_p, struct_timex_sz); + } +} + +PRE_SYSCALL(times)(void *tbuf) {} + +POST_SYSCALL(times)(long res, void *tbuf) { + if (res >= 0) { + if (tbuf) POST_WRITE(tbuf, struct_tms_sz); + } +} + +PRE_SYSCALL(gettid)() {} + +POST_SYSCALL(gettid)(long res) {} + +PRE_SYSCALL(nanosleep)(void *rqtp, void *rmtp) {} + +POST_SYSCALL(nanosleep)(long res, void *rqtp, void *rmtp) { + if (res >= 0) { + if (rqtp) POST_WRITE(rqtp, struct_timespec_sz); + if (rmtp) POST_WRITE(rmtp, struct_timespec_sz); + } +} + +PRE_SYSCALL(alarm)(long seconds) {} + +POST_SYSCALL(alarm)(long res, long seconds) {} + +PRE_SYSCALL(getpid)() {} + +POST_SYSCALL(getpid)(long res) {} + +PRE_SYSCALL(getppid)() {} + +POST_SYSCALL(getppid)(long res) {} + +PRE_SYSCALL(getuid)() {} + +POST_SYSCALL(getuid)(long res) {} + +PRE_SYSCALL(geteuid)() {} + +POST_SYSCALL(geteuid)(long res) {} + +PRE_SYSCALL(getgid)() {} + +POST_SYSCALL(getgid)(long res) {} + +PRE_SYSCALL(getegid)() {} + +POST_SYSCALL(getegid)(long res) {} + +PRE_SYSCALL(getresuid)(void *ruid, void *euid, void *suid) {} + +POST_SYSCALL(getresuid)(long res, void *ruid, void *euid, void *suid) { + if (res >= 0) { + if (ruid) POST_WRITE(ruid, sizeof(unsigned)); + if (euid) POST_WRITE(euid, sizeof(unsigned)); + if (suid) POST_WRITE(suid, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getresgid)(void *rgid, void *egid, void *sgid) {} + +POST_SYSCALL(getresgid)(long res, void *rgid, void *egid, void *sgid) { + if (res >= 0) { + if (rgid) POST_WRITE(rgid, sizeof(unsigned)); + if (egid) POST_WRITE(egid, sizeof(unsigned)); + if (sgid) POST_WRITE(sgid, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getpgid)(long pid) {} + +POST_SYSCALL(getpgid)(long res, long pid) {} + +PRE_SYSCALL(getpgrp)() {} + +POST_SYSCALL(getpgrp)(long res) {} + +PRE_SYSCALL(getsid)(long pid) {} + +POST_SYSCALL(getsid)(long res, long pid) {} + +PRE_SYSCALL(getgroups)(long gidsetsize, void *grouplist) {} + +POST_SYSCALL(getgroups)(long res, long gidsetsize, + __sanitizer___kernel_gid_t *grouplist) { + if (res >= 0) { + if (grouplist) POST_WRITE(grouplist, res * sizeof(*grouplist)); + } +} + +PRE_SYSCALL(setregid)(long rgid, long egid) {} + +POST_SYSCALL(setregid)(long res, long rgid, long egid) {} + +PRE_SYSCALL(setgid)(long gid) {} + +POST_SYSCALL(setgid)(long res, long gid) {} + +PRE_SYSCALL(setreuid)(long ruid, long euid) {} + +POST_SYSCALL(setreuid)(long res, long ruid, long euid) {} + +PRE_SYSCALL(setuid)(long uid) {} + +POST_SYSCALL(setuid)(long res, long uid) {} + +PRE_SYSCALL(setresuid)(long ruid, long euid, long suid) {} + +POST_SYSCALL(setresuid)(long res, long ruid, long euid, long suid) {} + +PRE_SYSCALL(setresgid)(long rgid, long egid, long sgid) {} + +POST_SYSCALL(setresgid)(long res, long rgid, long egid, long sgid) {} + +PRE_SYSCALL(setfsuid)(long uid) {} + +POST_SYSCALL(setfsuid)(long res, long uid) {} + +PRE_SYSCALL(setfsgid)(long gid) {} + +POST_SYSCALL(setfsgid)(long res, long gid) {} + +PRE_SYSCALL(setpgid)(long pid, long pgid) {} + +POST_SYSCALL(setpgid)(long res, long pid, long pgid) {} + +PRE_SYSCALL(setsid)() {} + +POST_SYSCALL(setsid)(long res) {} + +PRE_SYSCALL(setgroups)(long gidsetsize, __sanitizer___kernel_gid_t *grouplist) { + if (grouplist) POST_WRITE(grouplist, gidsetsize * sizeof(*grouplist)); +} + +POST_SYSCALL(setgroups)(long res, long gidsetsize, + __sanitizer___kernel_gid_t *grouplist) {} + +PRE_SYSCALL(acct)(const void *name) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(acct)(long res, const void *name) {} + +PRE_SYSCALL(capget)(void *header, void *dataptr) {} + +POST_SYSCALL(capget)(long res, void *header, void *dataptr) { + if (res >= 0) { + if (header) POST_WRITE(header, __user_cap_header_struct_sz); + if (dataptr) POST_WRITE(dataptr, __user_cap_data_struct_sz); + } +} + +PRE_SYSCALL(capset)(void *header, const void *data) { + 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); + } +} + +PRE_SYSCALL(personality)(long personality) {} + +POST_SYSCALL(personality)(long res, long personality) {} + +PRE_SYSCALL(sigpending)(void *set) {} + +POST_SYSCALL(sigpending)(long res, void *set) { + if (res >= 0) { + if (set) POST_WRITE(set, old_sigset_t_sz); + } +} + +PRE_SYSCALL(sigprocmask)(long how, void *set, void *oset) {} + +POST_SYSCALL(sigprocmask)(long res, long how, void *set, void *oset) { + if (res >= 0) { + if (set) POST_WRITE(set, old_sigset_t_sz); + if (oset) POST_WRITE(oset, old_sigset_t_sz); + } +} + +PRE_SYSCALL(getitimer)(long which, void *value) {} + +POST_SYSCALL(getitimer)(long res, long which, void *value) { + if (res >= 0) { + if (value) POST_WRITE(value, struct_itimerval_sz); + } +} + +PRE_SYSCALL(setitimer)(long which, void *value, void *ovalue) {} + +POST_SYSCALL(setitimer)(long res, long which, void *value, void *ovalue) { + if (res >= 0) { + if (value) POST_WRITE(value, struct_itimerval_sz); + if (ovalue) POST_WRITE(ovalue, struct_itimerval_sz); + } +} + +PRE_SYSCALL(timer_create)(long which_clock, void *timer_event_spec, + void *created_timer_id) {} + +POST_SYSCALL(timer_create)(long res, long which_clock, void *timer_event_spec, + void *created_timer_id) { + if (res >= 0) { + if (timer_event_spec) POST_WRITE(timer_event_spec, struct_sigevent_sz); + if (created_timer_id) POST_WRITE(created_timer_id, sizeof(long)); + } +} + +PRE_SYSCALL(timer_gettime)(long timer_id, void *setting) {} + +POST_SYSCALL(timer_gettime)(long res, long timer_id, void *setting) { + if (res >= 0) { + if (setting) POST_WRITE(setting, struct_itimerspec_sz); + } +} + +PRE_SYSCALL(timer_getoverrun)(long timer_id) {} + +POST_SYSCALL(timer_getoverrun)(long res, long timer_id) {} + +PRE_SYSCALL(timer_settime)(long timer_id, long flags, const void *new_setting, + void *old_setting) { + if (new_setting) PRE_READ(new_setting, struct_itimerspec_sz); +} + +POST_SYSCALL(timer_settime)(long res, long timer_id, long flags, + const void *new_setting, void *old_setting) { + if (res >= 0) { + if (old_setting) POST_WRITE(old_setting, struct_itimerspec_sz); + } +} + +PRE_SYSCALL(timer_delete)(long timer_id) {} + +POST_SYSCALL(timer_delete)(long res, long timer_id) {} + +PRE_SYSCALL(clock_settime)(long which_clock, const void *tp) { + if (tp) PRE_READ(tp, struct_timespec_sz); +} + +POST_SYSCALL(clock_settime)(long res, long which_clock, const void *tp) {} + +PRE_SYSCALL(clock_gettime)(long which_clock, void *tp) {} + +POST_SYSCALL(clock_gettime)(long res, long which_clock, void *tp) { + if (res >= 0) { + if (tp) POST_WRITE(tp, struct_timespec_sz); + } +} + +PRE_SYSCALL(clock_adjtime)(long which_clock, void *tx) {} + +POST_SYSCALL(clock_adjtime)(long res, long which_clock, void *tx) { + if (res >= 0) { + if (tx) POST_WRITE(tx, struct_timex_sz); + } +} + +PRE_SYSCALL(clock_getres)(long which_clock, void *tp) {} + +POST_SYSCALL(clock_getres)(long res, long which_clock, void *tp) { + if (res >= 0) { + if (tp) POST_WRITE(tp, struct_timespec_sz); + } +} + +PRE_SYSCALL(clock_nanosleep)(long which_clock, long flags, const void *rqtp, + void *rmtp) { + if (rqtp) PRE_READ(rqtp, struct_timespec_sz); +} + +POST_SYSCALL(clock_nanosleep)(long res, long which_clock, long flags, + const void *rqtp, void *rmtp) { + if (res >= 0) { + if (rmtp) POST_WRITE(rmtp, struct_timespec_sz); + } +} + +PRE_SYSCALL(nice)(long increment) {} + +POST_SYSCALL(nice)(long res, long increment) {} + +PRE_SYSCALL(sched_setscheduler)(long pid, long policy, void *param) {} + +POST_SYSCALL(sched_setscheduler)(long res, long pid, long policy, void *param) { + if (res >= 0) { + if (param) POST_WRITE(param, struct_sched_param_sz); + } +} + +PRE_SYSCALL(sched_setparam)(long pid, void *param) { + if (param) PRE_READ(param, struct_sched_param_sz); +} + +POST_SYSCALL(sched_setparam)(long res, long pid, void *param) {} + +PRE_SYSCALL(sched_getscheduler)(long pid) {} + +POST_SYSCALL(sched_getscheduler)(long res, long pid) {} + +PRE_SYSCALL(sched_getparam)(long pid, void *param) {} + +POST_SYSCALL(sched_getparam)(long res, long pid, void *param) { + if (res >= 0) { + if (param) POST_WRITE(param, struct_sched_param_sz); + } +} + +PRE_SYSCALL(sched_setaffinity)(long pid, long len, void *user_mask_ptr) { + if (user_mask_ptr) PRE_READ(user_mask_ptr, len); +} + +POST_SYSCALL(sched_setaffinity)(long res, long pid, long len, + void *user_mask_ptr) {} + +PRE_SYSCALL(sched_getaffinity)(long pid, long len, void *user_mask_ptr) {} + +POST_SYSCALL(sched_getaffinity)(long res, long pid, long len, + void *user_mask_ptr) { + if (res >= 0) { + if (user_mask_ptr) POST_WRITE(user_mask_ptr, len); + } +} + +PRE_SYSCALL(sched_yield)() {} + +POST_SYSCALL(sched_yield)(long res) {} + +PRE_SYSCALL(sched_get_priority_max)(long policy) {} + +POST_SYSCALL(sched_get_priority_max)(long res, long policy) {} + +PRE_SYSCALL(sched_get_priority_min)(long policy) {} + +POST_SYSCALL(sched_get_priority_min)(long res, long policy) {} + +PRE_SYSCALL(sched_rr_get_interval)(long pid, void *interval) {} + +POST_SYSCALL(sched_rr_get_interval)(long res, long pid, void *interval) { + if (res >= 0) { + if (interval) POST_WRITE(interval, struct_timespec_sz); + } +} + +PRE_SYSCALL(setpriority)(long which, long who, long niceval) {} + +POST_SYSCALL(setpriority)(long res, long which, long who, long niceval) {} + +PRE_SYSCALL(getpriority)(long which, long who) {} + +POST_SYSCALL(getpriority)(long res, long which, long who) {} + +PRE_SYSCALL(shutdown)(long arg0, long arg1) {} + +POST_SYSCALL(shutdown)(long res, long arg0, long arg1) {} + +PRE_SYSCALL(reboot)(long magic1, long magic2, long cmd, void *arg) {} + +POST_SYSCALL(reboot)(long res, long magic1, long magic2, long cmd, void *arg) {} + +PRE_SYSCALL(restart_syscall)() {} + +POST_SYSCALL(restart_syscall)(long res) {} + +PRE_SYSCALL(kexec_load)(long entry, long nr_segments, void *segments, + long flags) {} + +POST_SYSCALL(kexec_load)(long res, long entry, long nr_segments, void *segments, + long flags) { + if (res >= 0) { + if (segments) POST_WRITE(segments, struct_kexec_segment_sz); + } +} + +PRE_SYSCALL(exit)(long error_code) {} + +POST_SYSCALL(exit)(long res, long error_code) {} + +PRE_SYSCALL(exit_group)(long error_code) {} + +POST_SYSCALL(exit_group)(long res, long error_code) {} + +PRE_SYSCALL(wait4)(long pid, void *stat_addr, long options, void *ru) {} + +POST_SYSCALL(wait4)(long res, long pid, void *stat_addr, long options, + void *ru) { + if (res >= 0) { + if (stat_addr) POST_WRITE(stat_addr, sizeof(int)); + if (ru) POST_WRITE(ru, struct_rusage_sz); + } +} + +PRE_SYSCALL(waitid)(long which, long pid, void *infop, long options, void *ru) { +} + +POST_SYSCALL(waitid)(long res, long which, long pid, void *infop, long options, + void *ru) { + if (res >= 0) { + if (infop) POST_WRITE(infop, siginfo_t_sz); + if (ru) POST_WRITE(ru, struct_rusage_sz); + } +} + +PRE_SYSCALL(waitpid)(long pid, void *stat_addr, long options) {} + +POST_SYSCALL(waitpid)(long res, long pid, void *stat_addr, long options) { + if (res >= 0) { + if (stat_addr) POST_WRITE(stat_addr, sizeof(int)); + } +} + +PRE_SYSCALL(set_tid_address)(void *tidptr) {} + +POST_SYSCALL(set_tid_address)(long res, void *tidptr) { + if (res >= 0) { + if (tidptr) POST_WRITE(tidptr, sizeof(int)); + } +} + +PRE_SYSCALL(init_module)(void *umod, long len, const void *uargs) { + if (uargs) + PRE_READ(uargs, __sanitizer::internal_strlen((const char *)uargs) + 1); +} + +POST_SYSCALL(init_module)(long res, void *umod, long len, const void *uargs) {} + +PRE_SYSCALL(delete_module)(const void *name_user, long flags) { + if (name_user) + PRE_READ(name_user, + __sanitizer::internal_strlen((const char *)name_user) + 1); +} + +POST_SYSCALL(delete_module)(long res, const void *name_user, long flags) {} + +PRE_SYSCALL(rt_sigprocmask)(long how, void *set, void *oset, long sigsetsize) {} + +POST_SYSCALL(rt_sigprocmask)(long res, long how, kernel_sigset_t *set, + kernel_sigset_t *oset, long sigsetsize) { + if (res >= 0) { + if (set) POST_WRITE(set, sigsetsize); + if (oset) POST_WRITE(oset, sigsetsize); + } +} + +PRE_SYSCALL(rt_sigpending)(void *set, long sigsetsize) {} + +POST_SYSCALL(rt_sigpending)(long res, kernel_sigset_t *set, long sigsetsize) { + if (res >= 0) { + if (set) POST_WRITE(set, sigsetsize); + } +} + +PRE_SYSCALL(rt_sigtimedwait)(const kernel_sigset_t *uthese, void *uinfo, + const void *uts, long sigsetsize) { + if (uthese) PRE_READ(uthese, sigsetsize); + if (uts) PRE_READ(uts, struct_timespec_sz); +} + +POST_SYSCALL(rt_sigtimedwait)(long res, const void *uthese, void *uinfo, + const void *uts, long sigsetsize) { + if (res >= 0) { + if (uinfo) POST_WRITE(uinfo, siginfo_t_sz); + } +} + +PRE_SYSCALL(rt_tgsigqueueinfo)(long tgid, long pid, long sig, void *uinfo) {} + +POST_SYSCALL(rt_tgsigqueueinfo)(long res, long tgid, long pid, long sig, + void *uinfo) { + if (res >= 0) { + if (uinfo) POST_WRITE(uinfo, siginfo_t_sz); + } +} + +PRE_SYSCALL(kill)(long pid, long sig) {} + +POST_SYSCALL(kill)(long res, long pid, long sig) {} + +PRE_SYSCALL(tgkill)(long tgid, long pid, long sig) {} + +POST_SYSCALL(tgkill)(long res, long tgid, long pid, long sig) {} + +PRE_SYSCALL(tkill)(long pid, long sig) {} + +POST_SYSCALL(tkill)(long res, long pid, long sig) {} + +PRE_SYSCALL(rt_sigqueueinfo)(long pid, long sig, void *uinfo) {} + +POST_SYSCALL(rt_sigqueueinfo)(long res, long pid, long sig, void *uinfo) { + if (res >= 0) { + if (uinfo) POST_WRITE(uinfo, siginfo_t_sz); + } +} + +PRE_SYSCALL(sgetmask)() {} + +POST_SYSCALL(sgetmask)(long res) {} + +PRE_SYSCALL(ssetmask)(long newmask) {} + +POST_SYSCALL(ssetmask)(long res, long newmask) {} + +PRE_SYSCALL(signal)(long sig, long handler) {} + +POST_SYSCALL(signal)(long res, long sig, long handler) {} + +PRE_SYSCALL(pause)() {} + +POST_SYSCALL(pause)(long res) {} + +PRE_SYSCALL(sync)() {} + +POST_SYSCALL(sync)(long res) {} + +PRE_SYSCALL(fsync)(long fd) {} + +POST_SYSCALL(fsync)(long res, long fd) {} + +PRE_SYSCALL(fdatasync)(long fd) {} + +POST_SYSCALL(fdatasync)(long res, long fd) {} + +PRE_SYSCALL(bdflush)(long func, long data) {} + +POST_SYSCALL(bdflush)(long res, long func, long data) {} + +PRE_SYSCALL(mount)(void *dev_name, void *dir_name, void *type, long flags, + void *data) {} + +POST_SYSCALL(mount)(long res, void *dev_name, void *dir_name, void *type, + long flags, void *data) { + if (res >= 0) { + if (dev_name) + POST_WRITE(dev_name, + __sanitizer::internal_strlen((const char *)dev_name) + 1); + if (dir_name) + POST_WRITE(dir_name, + __sanitizer::internal_strlen((const char *)dir_name) + 1); + if (type) + POST_WRITE(type, __sanitizer::internal_strlen((const char *)type) + 1); + } +} + +PRE_SYSCALL(umount)(void *name, long flags) {} + +POST_SYSCALL(umount)(long res, void *name, long flags) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(oldumount)(void *name) {} + +POST_SYSCALL(oldumount)(long res, void *name) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(truncate)(const void *path, long length) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(truncate)(long res, const void *path, long length) {} + +PRE_SYSCALL(ftruncate)(long fd, long length) {} + +POST_SYSCALL(ftruncate)(long res, long fd, long length) {} + +PRE_SYSCALL(stat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(stat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz); + } +} + +PRE_SYSCALL(statfs)(const void *path, void *buf) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(statfs)(long res, const void *path, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs_sz); + } +} + +PRE_SYSCALL(statfs64)(const void *path, long sz, void *buf) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(statfs64)(long res, const void *path, long sz, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs64_sz); + } +} + +PRE_SYSCALL(fstatfs)(long fd, void *buf) {} + +POST_SYSCALL(fstatfs)(long res, long fd, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs_sz); + } +} + +PRE_SYSCALL(fstatfs64)(long fd, long sz, void *buf) {} + +POST_SYSCALL(fstatfs64)(long res, long fd, long sz, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_statfs64_sz); + } +} + +PRE_SYSCALL(lstat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lstat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz); + } +} + +PRE_SYSCALL(fstat)(long fd, void *statbuf) {} + +POST_SYSCALL(fstat)(long res, long fd, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct___old_kernel_stat_sz); + } +} + +PRE_SYSCALL(newstat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(newstat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(newlstat)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(newlstat)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(newfstat)(long fd, void *statbuf) {} + +POST_SYSCALL(newfstat)(long res, long fd, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(ustat)(long dev, void *ubuf) {} + +POST_SYSCALL(ustat)(long res, long dev, void *ubuf) { + if (res >= 0) { + if (ubuf) POST_WRITE(ubuf, struct_ustat_sz); + } +} + +PRE_SYSCALL(stat64)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(stat64)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(fstat64)(long fd, void *statbuf) {} + +POST_SYSCALL(fstat64)(long res, long fd, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(lstat64)(const void *filename, void *statbuf) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lstat64)(long res, const void *filename, void *statbuf) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(setxattr)(const void *path, const void *name, const void *value, + long size, long flags) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); + if (value) PRE_READ(value, size); +} + +POST_SYSCALL(setxattr)(long res, const void *path, const void *name, + const void *value, long size, long flags) {} + +PRE_SYSCALL(lsetxattr)(const void *path, const void *name, const void *value, + long size, long flags) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); + if (value) PRE_READ(value, size); +} + +POST_SYSCALL(lsetxattr)(long res, const void *path, const void *name, + const void *value, long size, long flags) {} + +PRE_SYSCALL(fsetxattr)(long fd, const void *name, const void *value, long size, + long flags) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); + if (value) PRE_READ(value, size); +} + +POST_SYSCALL(fsetxattr)(long res, long fd, const void *name, const void *value, + long size, long flags) {} + +PRE_SYSCALL(getxattr)(const void *path, const void *name, void *value, + long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(getxattr)(long res, const void *path, const void *name, + void *value, long size) { + if (res >= 0) { + if (value) POST_WRITE(value, size); + } +} + +PRE_SYSCALL(lgetxattr)(const void *path, const void *name, void *value, + long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(lgetxattr)(long res, const void *path, const void *name, + void *value, long size) { + if (res >= 0) { + if (value) POST_WRITE(value, size); + } +} + +PRE_SYSCALL(fgetxattr)(long fd, const void *name, void *value, long size) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(fgetxattr)(long res, long fd, const void *name, void *value, + long size) { + if (res >= 0) { + if (value) POST_WRITE(value, size); + } +} + +PRE_SYSCALL(listxattr)(const void *path, void *list, long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(listxattr)(long res, const void *path, void *list, long size) { + if (res >= 0) { + if (list) POST_WRITE(list, size); + } +} + +PRE_SYSCALL(llistxattr)(const void *path, void *list, long size) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(llistxattr)(long res, const void *path, void *list, long size) { + if (res >= 0) { + if (list) POST_WRITE(list, size); + } +} + +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); + } +} + +PRE_SYSCALL(removexattr)(const void *path, const void *name) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(removexattr)(long res, const void *path, const void *name) {} + +PRE_SYSCALL(lremovexattr)(const void *path, const void *name) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(lremovexattr)(long res, const void *path, const void *name) {} + +PRE_SYSCALL(fremovexattr)(long fd, const void *name) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(fremovexattr)(long res, long fd, const void *name) {} + +PRE_SYSCALL(brk)(long brk) {} + +POST_SYSCALL(brk)(long res, long brk) {} + +PRE_SYSCALL(mprotect)(long start, long len, long prot) {} + +POST_SYSCALL(mprotect)(long res, long start, long len, long prot) {} + +PRE_SYSCALL(mremap)(long addr, long old_len, long new_len, long flags, + long new_addr) {} + +POST_SYSCALL(mremap)(long res, long addr, long old_len, long new_len, + long flags, long new_addr) {} + +PRE_SYSCALL(remap_file_pages)(long start, long size, long prot, long pgoff, + long flags) {} + +POST_SYSCALL(remap_file_pages)(long res, long start, long size, long prot, + long pgoff, long flags) {} + +PRE_SYSCALL(msync)(long start, long len, long flags) {} + +POST_SYSCALL(msync)(long res, long start, long len, long flags) {} + +PRE_SYSCALL(munmap)(long addr, long len) {} + +POST_SYSCALL(munmap)(long res, long addr, long len) {} + +PRE_SYSCALL(mlock)(long start, long len) {} + +POST_SYSCALL(mlock)(long res, long start, long len) {} + +PRE_SYSCALL(munlock)(long start, long len) {} + +POST_SYSCALL(munlock)(long res, long start, long len) {} + +PRE_SYSCALL(mlockall)(long flags) {} + +POST_SYSCALL(mlockall)(long res, long flags) {} + +PRE_SYSCALL(munlockall)() {} + +POST_SYSCALL(munlockall)(long res) {} + +PRE_SYSCALL(madvise)(long start, long len, long behavior) {} + +POST_SYSCALL(madvise)(long res, long start, long len, long behavior) {} + +PRE_SYSCALL(mincore)(long start, long len, void *vec) {} + +POST_SYSCALL(mincore)(long res, long start, long len, void *vec) { + if (res >= 0) { + if (vec) { + POST_WRITE(vec, (len + GetPageSizeCached() - 1) / GetPageSizeCached()); + } + } +} + +PRE_SYSCALL(pivot_root)(const void *new_root, const void *put_old) { + if (new_root) + PRE_READ(new_root, + __sanitizer::internal_strlen((const char *)new_root) + 1); + if (put_old) + PRE_READ(put_old, __sanitizer::internal_strlen((const char *)put_old) + 1); +} + +POST_SYSCALL(pivot_root)(long res, const void *new_root, const void *put_old) {} + +PRE_SYSCALL(chroot)(const void *filename) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chroot)(long res, const void *filename) {} + +PRE_SYSCALL(mknod)(const void *filename, long mode, long dev) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(mknod)(long res, const void *filename, long mode, long dev) {} + +PRE_SYSCALL(link)(const void *oldname, const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(link)(long res, const void *oldname, const void *newname) {} + +PRE_SYSCALL(symlink)(const void *old, const void *new_) { + if (old) PRE_READ(old, __sanitizer::internal_strlen((const char *)old) + 1); + if (new_) + PRE_READ(new_, __sanitizer::internal_strlen((const char *)new_) + 1); +} + +POST_SYSCALL(symlink)(long res, const void *old, const void *new_) {} + +PRE_SYSCALL(unlink)(const void *pathname) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(unlink)(long res, const void *pathname) {} + +PRE_SYSCALL(rename)(const void *oldname, const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(rename)(long res, const void *oldname, const void *newname) {} + +PRE_SYSCALL(chmod)(const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chmod)(long res, const void *filename, long mode) {} + +PRE_SYSCALL(fchmod)(long fd, long mode) {} + +POST_SYSCALL(fchmod)(long res, long fd, long mode) {} + +PRE_SYSCALL(fcntl)(long fd, long cmd, long arg) {} + +POST_SYSCALL(fcntl)(long res, long fd, long cmd, long arg) {} + +PRE_SYSCALL(fcntl64)(long fd, long cmd, long arg) {} + +POST_SYSCALL(fcntl64)(long res, long fd, long cmd, long arg) {} + +PRE_SYSCALL(pipe)(void *fildes) {} + +POST_SYSCALL(pipe)(long res, void *fildes) { + if (res >= 0) { + if (fildes) POST_WRITE(fildes, sizeof(int)); + } +} + +PRE_SYSCALL(pipe2)(void *fildes, long flags) {} + +POST_SYSCALL(pipe2)(long res, void *fildes, long flags) { + if (res >= 0) { + if (fildes) POST_WRITE(fildes, sizeof(int)); + } +} + +PRE_SYSCALL(dup)(long fildes) {} + +POST_SYSCALL(dup)(long res, long fildes) {} + +PRE_SYSCALL(dup2)(long oldfd, long newfd) {} + +POST_SYSCALL(dup2)(long res, long oldfd, long newfd) {} + +PRE_SYSCALL(dup3)(long oldfd, long newfd, long flags) {} + +POST_SYSCALL(dup3)(long res, long oldfd, long newfd, long flags) {} + +PRE_SYSCALL(ioperm)(long from, long num, long on) {} + +POST_SYSCALL(ioperm)(long res, long from, long num, long on) {} + +PRE_SYSCALL(ioctl)(long fd, long cmd, long arg) {} + +POST_SYSCALL(ioctl)(long res, long fd, long cmd, long arg) {} + +PRE_SYSCALL(flock)(long fd, long cmd) {} + +POST_SYSCALL(flock)(long res, long fd, long cmd) {} + +PRE_SYSCALL(io_setup)(long nr_reqs, void *ctx) {} + +POST_SYSCALL(io_setup)(long res, long nr_reqs, void *ctx) { + if (res >= 0) { + if (ctx) POST_WRITE(ctx, sizeof(long)); + } +} + +PRE_SYSCALL(io_destroy)(long ctx) {} + +POST_SYSCALL(io_destroy)(long res, long ctx) {} + +PRE_SYSCALL(io_getevents)(long ctx_id, long min_nr, long nr, void *events, + void *timeout) { + if (timeout) PRE_READ(timeout, struct_timespec_sz); +} + +POST_SYSCALL(io_getevents)(long res, long ctx_id, long min_nr, long nr, + void *events, void *timeout) { + if (res >= 0) { + if (events) POST_WRITE(events, res * struct_io_event_sz); + if (timeout) POST_WRITE(timeout, struct_timespec_sz); + } +} + +PRE_SYSCALL(io_submit)(long, long arg1, void *arg2) {} + +POST_SYSCALL(io_submit)(long res, long, long arg1, void *arg2) {} + +PRE_SYSCALL(io_cancel)(long ctx_id, void *iocb, void *result) {} + +POST_SYSCALL(io_cancel)(long res, long ctx_id, void *iocb, void *result) { + if (res >= 0) { + if (iocb) POST_WRITE(iocb, struct_iocb_sz); + if (result) POST_WRITE(result, struct_io_event_sz); + } +} + +PRE_SYSCALL(sendfile)(long out_fd, long in_fd, void *offset, long count) {} + +POST_SYSCALL(sendfile)(long res, long out_fd, long in_fd, + __sanitizer___kernel_off_t *offset, long count) { + if (res >= 0) { + if (offset) POST_WRITE(offset, sizeof(*offset)); + } +} + +PRE_SYSCALL(sendfile64)(long out_fd, long in_fd, void *offset, long count) {} + +POST_SYSCALL(sendfile64)(long res, long out_fd, long in_fd, + __sanitizer___kernel_loff_t *offset, long count) { + if (res >= 0) { + if (offset) POST_WRITE(offset, sizeof(*offset)); + } +} + +PRE_SYSCALL(readlink)(const void *path, void *buf, long bufsiz) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(readlink)(long res, const void *path, void *buf, long bufsiz) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(creat)(const void *pathname, long mode) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(creat)(long res, const void *pathname, long mode) {} + +PRE_SYSCALL(open)(const void *filename, long flags, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(open)(long res, const void *filename, long flags, long mode) {} + +PRE_SYSCALL(close)(long fd) { + COMMON_SYSCALL_FD_CLOSE((int)fd); +} + +POST_SYSCALL(close)(long res, long fd) {} + +PRE_SYSCALL(access)(const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(access)(long res, const void *filename, long mode) {} + +PRE_SYSCALL(vhangup)() {} + +POST_SYSCALL(vhangup)(long res) {} + +PRE_SYSCALL(chown)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chown)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(lchown)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lchown)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(fchown)(long fd, long user, long group) {} + +POST_SYSCALL(fchown)(long res, long fd, long user, long group) {} + +PRE_SYSCALL(chown16)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chown16)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(lchown16)(const void *filename, long user, long group) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(lchown16)(long res, const void *filename, long user, long group) {} + +PRE_SYSCALL(fchown16)(long fd, long user, long group) {} + +POST_SYSCALL(fchown16)(long res, long fd, long user, long group) {} + +PRE_SYSCALL(setregid16)(long rgid, long egid) {} + +POST_SYSCALL(setregid16)(long res, long rgid, long egid) {} + +PRE_SYSCALL(setgid16)(long gid) {} + +POST_SYSCALL(setgid16)(long res, long gid) {} + +PRE_SYSCALL(setreuid16)(long ruid, long euid) {} + +POST_SYSCALL(setreuid16)(long res, long ruid, long euid) {} + +PRE_SYSCALL(setuid16)(long uid) {} + +POST_SYSCALL(setuid16)(long res, long uid) {} + +PRE_SYSCALL(setresuid16)(long ruid, long euid, long suid) {} + +POST_SYSCALL(setresuid16)(long res, long ruid, long euid, long suid) {} + +PRE_SYSCALL(getresuid16)(void *ruid, void *euid, void *suid) {} + +POST_SYSCALL(getresuid16)(long res, __sanitizer___kernel_old_uid_t *ruid, + __sanitizer___kernel_old_uid_t *euid, + __sanitizer___kernel_old_uid_t *suid) { + if (res >= 0) { + if (ruid) POST_WRITE(ruid, sizeof(*ruid)); + if (euid) POST_WRITE(euid, sizeof(*euid)); + if (suid) POST_WRITE(suid, sizeof(*suid)); + } +} + +PRE_SYSCALL(setresgid16)(long rgid, long egid, long sgid) {} + +POST_SYSCALL(setresgid16)(long res, long rgid, long egid, long sgid) {} + +PRE_SYSCALL(getresgid16)(void *rgid, void *egid, void *sgid) {} + +POST_SYSCALL(getresgid16)(long res, __sanitizer___kernel_old_gid_t *rgid, + __sanitizer___kernel_old_gid_t *egid, + __sanitizer___kernel_old_gid_t *sgid) { + if (res >= 0) { + if (rgid) POST_WRITE(rgid, sizeof(*rgid)); + if (egid) POST_WRITE(egid, sizeof(*egid)); + if (sgid) POST_WRITE(sgid, sizeof(*sgid)); + } +} + +PRE_SYSCALL(setfsuid16)(long uid) {} + +POST_SYSCALL(setfsuid16)(long res, long uid) {} + +PRE_SYSCALL(setfsgid16)(long gid) {} + +POST_SYSCALL(setfsgid16)(long res, long gid) {} + +PRE_SYSCALL(getgroups16)(long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) {} + +POST_SYSCALL(getgroups16)(long res, long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) { + if (res >= 0) { + if (grouplist) POST_WRITE(grouplist, res * sizeof(*grouplist)); + } +} + +PRE_SYSCALL(setgroups16)(long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) { + if (grouplist) POST_WRITE(grouplist, gidsetsize * sizeof(*grouplist)); +} + +POST_SYSCALL(setgroups16)(long res, long gidsetsize, + __sanitizer___kernel_old_gid_t *grouplist) {} + +PRE_SYSCALL(getuid16)() {} + +POST_SYSCALL(getuid16)(long res) {} + +PRE_SYSCALL(geteuid16)() {} + +POST_SYSCALL(geteuid16)(long res) {} + +PRE_SYSCALL(getgid16)() {} + +POST_SYSCALL(getgid16)(long res) {} + +PRE_SYSCALL(getegid16)() {} + +POST_SYSCALL(getegid16)(long res) {} + +PRE_SYSCALL(utime)(void *filename, void *times) {} + +POST_SYSCALL(utime)(long res, void *filename, void *times) { + if (res >= 0) { + if (filename) + POST_WRITE(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); + if (times) POST_WRITE(times, struct_utimbuf_sz); + } +} + +PRE_SYSCALL(utimes)(void *filename, void *utimes) {} + +POST_SYSCALL(utimes)(long res, void *filename, void *utimes) { + if (res >= 0) { + if (filename) + POST_WRITE(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); + if (utimes) POST_WRITE(utimes, timeval_sz); + } +} + +PRE_SYSCALL(lseek)(long fd, long offset, long origin) {} + +POST_SYSCALL(lseek)(long res, long fd, long offset, long origin) {} + +PRE_SYSCALL(llseek)(long fd, long offset_high, long offset_low, void *result, + long origin) {} + +POST_SYSCALL(llseek)(long res, long fd, long offset_high, long offset_low, + void *result, long origin) { + if (res >= 0) { + if (result) POST_WRITE(result, sizeof(long long)); + } +} + +PRE_SYSCALL(readv)(long fd, const __sanitizer_iovec *vec, long vlen) {} + +POST_SYSCALL(readv)(long res, long fd, const __sanitizer_iovec *vec, + long vlen) { + if (res >= 0) { + if (vec) kernel_write_iovec(vec, vlen, res); + } +} + +PRE_SYSCALL(write)(long fd, const void *buf, long count) { + if (buf) PRE_READ(buf, count); +} + +POST_SYSCALL(write)(long res, long fd, const void *buf, long count) {} + +PRE_SYSCALL(writev)(long fd, const __sanitizer_iovec *vec, long vlen) {} + +POST_SYSCALL(writev)(long res, long fd, const __sanitizer_iovec *vec, + long vlen) { + if (res >= 0) { + if (vec) kernel_read_iovec(vec, vlen, res); + } +} + +#ifdef _LP64 +PRE_SYSCALL(pread64)(long fd, void *buf, long count, long pos) {} + +POST_SYSCALL(pread64)(long res, long fd, void *buf, long count, long pos) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(pwrite64)(long fd, const void *buf, long count, long pos) { + if (buf) PRE_READ(buf, count); +} + +POST_SYSCALL(pwrite64)(long res, long fd, const void *buf, long count, + long pos) {} +#else +PRE_SYSCALL(pread64)(long fd, void *buf, long count, long pos0, long pos1) {} + +POST_SYSCALL(pread64)(long res, long fd, void *buf, long count, long pos0, + long pos1) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(pwrite64)(long fd, const void *buf, long count, long pos0, + long pos1) { + if (buf) PRE_READ(buf, count); +} + +POST_SYSCALL(pwrite64)(long res, long fd, const void *buf, long count, + long pos0, long pos1) {} +#endif + +PRE_SYSCALL(preadv)(long fd, const __sanitizer_iovec *vec, long vlen, + long pos_l, long pos_h) {} + +POST_SYSCALL(preadv)(long res, long fd, const __sanitizer_iovec *vec, long vlen, + long pos_l, long pos_h) { + if (res >= 0) { + if (vec) kernel_write_iovec(vec, vlen, res); + } +} + +PRE_SYSCALL(pwritev)(long fd, const __sanitizer_iovec *vec, long vlen, + long pos_l, long pos_h) {} + +POST_SYSCALL(pwritev)(long res, long fd, const __sanitizer_iovec *vec, + long vlen, long pos_l, long pos_h) { + if (res >= 0) { + if (vec) kernel_read_iovec(vec, vlen, res); + } +} + +PRE_SYSCALL(getcwd)(void *buf, long size) {} + +POST_SYSCALL(getcwd)(long res, void *buf, long size) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(mkdir)(const void *pathname, long mode) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(mkdir)(long res, const void *pathname, long mode) {} + +PRE_SYSCALL(chdir)(const void *filename) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(chdir)(long res, const void *filename) {} + +PRE_SYSCALL(fchdir)(long fd) {} + +POST_SYSCALL(fchdir)(long res, long fd) {} + +PRE_SYSCALL(rmdir)(const void *pathname) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(rmdir)(long res, const void *pathname) {} + +PRE_SYSCALL(lookup_dcookie)(u64 cookie64, void *buf, long len) {} + +POST_SYSCALL(lookup_dcookie)(long res, u64 cookie64, void *buf, long len) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(quotactl)(long cmd, const void *special, long id, void *addr) { + if (special) + PRE_READ(special, __sanitizer::internal_strlen((const char *)special) + 1); +} + +POST_SYSCALL(quotactl)(long res, long cmd, const void *special, long id, + void *addr) {} + +PRE_SYSCALL(getdents)(long fd, void *dirent, long count) {} + +POST_SYSCALL(getdents)(long res, long fd, void *dirent, long count) { + if (res >= 0) { + if (dirent) POST_WRITE(dirent, res); + } +} + +PRE_SYSCALL(getdents64)(long fd, void *dirent, long count) {} + +POST_SYSCALL(getdents64)(long res, long fd, void *dirent, long count) { + if (res >= 0) { + if (dirent) POST_WRITE(dirent, res); + } +} + +PRE_SYSCALL(setsockopt)(long fd, long level, long optname, void *optval, + long optlen) {} + +POST_SYSCALL(setsockopt)(long res, long fd, long level, long optname, + void *optval, long optlen) { + if (res >= 0) { + if (optval) + POST_WRITE(optval, + __sanitizer::internal_strlen((const char *)optval) + 1); + } +} + +PRE_SYSCALL(getsockopt)(long fd, long level, long optname, void *optval, + void *optlen) {} + +POST_SYSCALL(getsockopt)(long res, long fd, long level, long optname, + void *optval, void *optlen) { + if (res >= 0) { + if (optval) + POST_WRITE(optval, + __sanitizer::internal_strlen((const char *)optval) + 1); + if (optlen) POST_WRITE(optlen, sizeof(int)); + } +} + +PRE_SYSCALL(bind)(long arg0, sanitizer_kernel_sockaddr *arg1, long arg2) {} + +POST_SYSCALL(bind)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + long arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + } +} + +PRE_SYSCALL(connect)(long arg0, sanitizer_kernel_sockaddr *arg1, long arg2) {} + +POST_SYSCALL(connect)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + long arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + } +} + +PRE_SYSCALL(accept)(long arg0, sanitizer_kernel_sockaddr *arg1, void *arg2) {} + +POST_SYSCALL(accept)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(accept4)(long arg0, sanitizer_kernel_sockaddr *arg1, void *arg2, + long arg3) {} + +POST_SYSCALL(accept4)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2, long arg3) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getsockname)(long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) {} + +POST_SYSCALL(getsockname)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(getpeername)(long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) {} + +POST_SYSCALL(getpeername)(long res, long arg0, sanitizer_kernel_sockaddr *arg1, + void *arg2) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(unsigned)); + } +} + +PRE_SYSCALL(send)(long arg0, void *arg1, long arg2, long arg3) {} + +POST_SYSCALL(send)(long res, long arg0, void *arg1, long arg2, long arg3) { + if (res) { + if (arg1) POST_READ(arg1, res); + } +} + +PRE_SYSCALL(sendto)(long arg0, void *arg1, long arg2, long arg3, + sanitizer_kernel_sockaddr *arg4, long arg5) {} + +POST_SYSCALL(sendto)(long res, long arg0, void *arg1, long arg2, long arg3, + sanitizer_kernel_sockaddr *arg4, long arg5) { + if (res >= 0) { + if (arg1) POST_READ(arg1, res); + if (arg4) POST_WRITE(arg4, sizeof(*arg4)); + } +} + +PRE_SYSCALL(sendmsg)(long fd, void *msg, long flags) {} + +POST_SYSCALL(sendmsg)(long res, long fd, void *msg, long flags) { + // FIXME: POST_READ +} + +PRE_SYSCALL(sendmmsg)(long fd, void *msg, long vlen, long flags) {} + +POST_SYSCALL(sendmmsg)(long res, long fd, void *msg, long vlen, long flags) { + // FIXME: POST_READ +} + +PRE_SYSCALL(recv)(long arg0, void *buf, long len, long flags) {} + +POST_SYSCALL(recv)(long res, void *buf, long len, long flags) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + } +} + +PRE_SYSCALL(recvfrom)(long arg0, void *buf, long len, long flags, + sanitizer_kernel_sockaddr *arg4, void *arg5) {} + +POST_SYSCALL(recvfrom)(long res, long arg0, void *buf, long len, long flags, + sanitizer_kernel_sockaddr *arg4, void *arg5) { + if (res >= 0) { + if (buf) POST_WRITE(buf, res); + if (arg4) POST_WRITE(arg4, sizeof(*arg4)); + if (arg5) POST_WRITE(arg5, sizeof(int)); + } +} + +PRE_SYSCALL(socket)(long arg0, long arg1, long arg2) {} + +POST_SYSCALL(socket)(long res, long arg0, long arg1, long arg2) {} + +PRE_SYSCALL(socketpair)(long arg0, long arg1, long arg2, void *arg3) {} + +POST_SYSCALL(socketpair)(long res, long arg0, long arg1, long arg2, + void *arg3) { + if (res >= 0) { + if (arg3) POST_WRITE(arg3, sizeof(int)); + } +} + +PRE_SYSCALL(socketcall)(long call, void *args) {} + +POST_SYSCALL(socketcall)(long res, long call, void *args) { + if (res >= 0) { + if (args) POST_WRITE(args, sizeof(long)); + } +} + +PRE_SYSCALL(listen)(long arg0, long arg1) {} + +POST_SYSCALL(listen)(long res, long arg0, long arg1) {} + +PRE_SYSCALL(poll)(void *ufds, long nfds, long timeout) {} + +POST_SYSCALL(poll)(long res, __sanitizer_pollfd *ufds, long nfds, + long timeout) { + if (res >= 0) { + if (ufds) POST_WRITE(ufds, nfds * sizeof(*ufds)); + } +} + +PRE_SYSCALL(select)(long n, __sanitizer___kernel_fd_set *inp, + __sanitizer___kernel_fd_set *outp, + __sanitizer___kernel_fd_set *exp, void *tvp) {} + +POST_SYSCALL(select)(long res, long n, __sanitizer___kernel_fd_set *inp, + __sanitizer___kernel_fd_set *outp, + __sanitizer___kernel_fd_set *exp, void *tvp) { + if (res >= 0) { + if (inp) POST_WRITE(inp, sizeof(*inp)); + if (outp) POST_WRITE(outp, sizeof(*outp)); + if (exp) POST_WRITE(exp, sizeof(*exp)); + if (tvp) POST_WRITE(tvp, timeval_sz); + } +} + +PRE_SYSCALL(old_select)(void *arg) {} + +POST_SYSCALL(old_select)(long res, void *arg) {} + +PRE_SYSCALL(epoll_create)(long size) {} + +POST_SYSCALL(epoll_create)(long res, long size) {} + +PRE_SYSCALL(epoll_create1)(long flags) {} + +POST_SYSCALL(epoll_create1)(long res, long flags) {} + +PRE_SYSCALL(epoll_ctl)(long epfd, long op, long fd, void *event) {} + +POST_SYSCALL(epoll_ctl)(long res, long epfd, long op, long fd, void *event) { + if (res >= 0) { + if (event) POST_WRITE(event, struct_epoll_event_sz); + } +} + +PRE_SYSCALL(epoll_wait)(long epfd, void *events, long maxevents, long timeout) { +} + +POST_SYSCALL(epoll_wait)(long res, long epfd, void *events, long maxevents, + long timeout) { + if (res >= 0) { + if (events) POST_WRITE(events, struct_epoll_event_sz); + } +} + +PRE_SYSCALL(epoll_pwait)(long epfd, void *events, long maxevents, long timeout, + const kernel_sigset_t *sigmask, long sigsetsize) { + if (sigmask) PRE_READ(sigmask, sigsetsize); +} + +POST_SYSCALL(epoll_pwait)(long res, long epfd, void *events, long maxevents, + long timeout, const void *sigmask, long sigsetsize) { + if (res >= 0) { + if (events) POST_WRITE(events, struct_epoll_event_sz); + } +} + +PRE_SYSCALL(gethostname)(void *name, long len) {} + +POST_SYSCALL(gethostname)(long res, void *name, long len) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(sethostname)(void *name, long len) {} + +POST_SYSCALL(sethostname)(long res, void *name, long len) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(setdomainname)(void *name, long len) {} + +POST_SYSCALL(setdomainname)(long res, void *name, long len) { + if (res >= 0) { + if (name) + POST_WRITE(name, __sanitizer::internal_strlen((const char *)name) + 1); + } +} + +PRE_SYSCALL(newuname)(void *name) {} + +POST_SYSCALL(newuname)(long res, void *name) { + if (res >= 0) { + if (name) POST_WRITE(name, struct_new_utsname_sz); + } +} + +PRE_SYSCALL(uname)(void *arg0) {} + +POST_SYSCALL(uname)(long res, void *arg0) { + if (res >= 0) { + if (arg0) POST_WRITE(arg0, struct_old_utsname_sz); + } +} + +PRE_SYSCALL(olduname)(void *arg0) {} + +POST_SYSCALL(olduname)(long res, void *arg0) { + if (res >= 0) { + if (arg0) POST_WRITE(arg0, struct_oldold_utsname_sz); + } +} + +PRE_SYSCALL(getrlimit)(long resource, void *rlim) {} + +POST_SYSCALL(getrlimit)(long res, long resource, void *rlim) { + if (res >= 0) { + if (rlim) POST_WRITE(rlim, struct_rlimit_sz); + } +} + +PRE_SYSCALL(old_getrlimit)(long resource, void *rlim) {} + +POST_SYSCALL(old_getrlimit)(long res, long resource, void *rlim) { + if (res >= 0) { + if (rlim) POST_WRITE(rlim, struct_rlimit_sz); + } +} + +PRE_SYSCALL(setrlimit)(long resource, void *rlim) {} + +POST_SYSCALL(setrlimit)(long res, long resource, void *rlim) { + if (res >= 0) { + if (rlim) POST_WRITE(rlim, struct_rlimit_sz); + } +} + +#if !SANITIZER_ANDROID +PRE_SYSCALL(prlimit64)(long pid, long resource, const void *new_rlim, + void *old_rlim) { + if (new_rlim) PRE_READ(new_rlim, struct_rlimit64_sz); +} + +POST_SYSCALL(prlimit64)(long res, long pid, long resource, const void *new_rlim, + void *old_rlim) { + if (res >= 0) { + if (old_rlim) POST_WRITE(old_rlim, struct_rlimit64_sz); + } +} +#endif + +PRE_SYSCALL(getrusage)(long who, void *ru) {} + +POST_SYSCALL(getrusage)(long res, long who, void *ru) { + if (res >= 0) { + if (ru) POST_WRITE(ru, struct_rusage_sz); + } +} + +PRE_SYSCALL(umask)(long mask) {} + +POST_SYSCALL(umask)(long res, long mask) {} + +PRE_SYSCALL(msgget)(long key, long msgflg) {} + +POST_SYSCALL(msgget)(long res, long key, long msgflg) {} + +PRE_SYSCALL(msgsnd)(long msqid, void *msgp, long msgsz, long msgflg) { + if (msgp) PRE_READ(msgp, msgsz); +} + +POST_SYSCALL(msgsnd)(long res, long msqid, void *msgp, long msgsz, + long msgflg) {} + +PRE_SYSCALL(msgrcv)(long msqid, void *msgp, long msgsz, long msgtyp, + long msgflg) {} + +POST_SYSCALL(msgrcv)(long res, long msqid, void *msgp, long msgsz, long msgtyp, + long msgflg) { + if (res >= 0) { + if (msgp) POST_WRITE(msgp, res); + } +} + +PRE_SYSCALL(msgctl)(long msqid, long cmd, void *buf) {} + +POST_SYSCALL(msgctl)(long res, long msqid, long cmd, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_msqid_ds_sz); + } +} + +PRE_SYSCALL(semget)(long key, long nsems, long semflg) {} + +POST_SYSCALL(semget)(long res, long key, long nsems, long semflg) {} + +PRE_SYSCALL(semop)(long semid, void *sops, long nsops) {} + +POST_SYSCALL(semop)(long res, long semid, void *sops, long nsops) {} + +PRE_SYSCALL(semctl)(long semid, long semnum, long cmd, void *arg) {} + +POST_SYSCALL(semctl)(long res, long semid, long semnum, long cmd, void *arg) {} + +PRE_SYSCALL(semtimedop)(long semid, void *sops, long nsops, + const void *timeout) { + if (timeout) PRE_READ(timeout, struct_timespec_sz); +} + +POST_SYSCALL(semtimedop)(long res, long semid, void *sops, long nsops, + const void *timeout) {} + +PRE_SYSCALL(shmat)(long shmid, void *shmaddr, long shmflg) {} + +POST_SYSCALL(shmat)(long res, long shmid, void *shmaddr, long shmflg) { + if (res >= 0) { + if (shmaddr) + POST_WRITE(shmaddr, + __sanitizer::internal_strlen((const char *)shmaddr) + 1); + } +} + +PRE_SYSCALL(shmget)(long key, long size, long flag) {} + +POST_SYSCALL(shmget)(long res, long key, long size, long flag) {} + +PRE_SYSCALL(shmdt)(void *shmaddr) {} + +POST_SYSCALL(shmdt)(long res, void *shmaddr) { + if (res >= 0) { + if (shmaddr) + POST_WRITE(shmaddr, + __sanitizer::internal_strlen((const char *)shmaddr) + 1); + } +} + +PRE_SYSCALL(shmctl)(long shmid, long cmd, void *buf) {} + +POST_SYSCALL(shmctl)(long res, long shmid, long cmd, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, struct_shmid_ds_sz); + } +} + +PRE_SYSCALL(ipc)(long call, long first, long second, long third, void *ptr, + long fifth) {} + +POST_SYSCALL(ipc)(long res, long call, long first, long second, long third, + void *ptr, long fifth) {} + +#if !SANITIZER_ANDROID +PRE_SYSCALL(mq_open)(const void *name, long oflag, long mode, void *attr) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(mq_open)(long res, const void *name, long oflag, long mode, + void *attr) { + if (res >= 0) { + if (attr) POST_WRITE(attr, struct_mq_attr_sz); + } +} + +PRE_SYSCALL(mq_unlink)(const void *name) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(mq_unlink)(long res, const void *name) {} + +PRE_SYSCALL(mq_timedsend)(long mqdes, const void *msg_ptr, long msg_len, + long msg_prio, const void *abs_timeout) { + if (msg_ptr) PRE_READ(msg_ptr, msg_len); + if (abs_timeout) PRE_READ(abs_timeout, struct_timespec_sz); +} + +POST_SYSCALL(mq_timedsend)(long res, long mqdes, const void *msg_ptr, + long msg_len, long msg_prio, + const void *abs_timeout) {} + +PRE_SYSCALL(mq_timedreceive)(long mqdes, void *msg_ptr, long msg_len, + void *msg_prio, const void *abs_timeout) { + if (abs_timeout) PRE_READ(abs_timeout, struct_timespec_sz); +} + +POST_SYSCALL(mq_timedreceive)(long res, long mqdes, void *msg_ptr, long msg_len, + int *msg_prio, const void *abs_timeout) { + if (res >= 0) { + if (msg_ptr) POST_WRITE(msg_ptr, res); + if (msg_prio) POST_WRITE(msg_prio, sizeof(*msg_prio)); + } +} + +PRE_SYSCALL(mq_notify)(long mqdes, const void *notification) { + if (notification) PRE_READ(notification, struct_sigevent_sz); +} + +POST_SYSCALL(mq_notify)(long res, long mqdes, const void *notification) {} + +PRE_SYSCALL(mq_getsetattr)(long mqdes, const void *mqstat, void *omqstat) { + if (mqstat) PRE_READ(mqstat, struct_mq_attr_sz); +} + +POST_SYSCALL(mq_getsetattr)(long res, long mqdes, const void *mqstat, + void *omqstat) { + if (res >= 0) { + if (omqstat) POST_WRITE(omqstat, struct_mq_attr_sz); + } +} +#endif // SANITIZER_ANDROID + +PRE_SYSCALL(pciconfig_iobase)(long which, long bus, long devfn) {} + +POST_SYSCALL(pciconfig_iobase)(long res, long which, long bus, long devfn) {} + +PRE_SYSCALL(pciconfig_read)(long bus, long dfn, long off, long len, void *buf) { +} + +POST_SYSCALL(pciconfig_read)(long res, long bus, long dfn, long off, long len, + void *buf) {} + +PRE_SYSCALL(pciconfig_write)(long bus, long dfn, long off, long len, + void *buf) {} + +POST_SYSCALL(pciconfig_write)(long res, long bus, long dfn, long off, long len, + void *buf) {} + +PRE_SYSCALL(swapon)(const void *specialfile, long swap_flags) { + if (specialfile) + PRE_READ(specialfile, + __sanitizer::internal_strlen((const char *)specialfile) + 1); +} + +POST_SYSCALL(swapon)(long res, const void *specialfile, long swap_flags) {} + +PRE_SYSCALL(swapoff)(const void *specialfile) { + if (specialfile) + PRE_READ(specialfile, + __sanitizer::internal_strlen((const char *)specialfile) + 1); +} + +POST_SYSCALL(swapoff)(long res, const void *specialfile) {} + +PRE_SYSCALL(sysctl)(__sanitizer___sysctl_args *args) { + if (args) { + if (args->name) PRE_READ(args->name, args->nlen * sizeof(*args->name)); + if (args->newval) PRE_READ(args->name, args->newlen); + } +} + +POST_SYSCALL(sysctl)(long res, __sanitizer___sysctl_args *args) { + if (res >= 0) { + if (args && args->oldval && args->oldlenp) { + POST_WRITE(args->oldlenp, sizeof(*args->oldlenp)); + POST_WRITE(args->oldval, *args->oldlenp); + } + } +} + +PRE_SYSCALL(sysinfo)(void *info) {} + +POST_SYSCALL(sysinfo)(long res, void *info) { + if (res >= 0) { + if (info) POST_WRITE(info, struct_sysinfo_sz); + } +} + +PRE_SYSCALL(sysfs)(long option, long arg1, long arg2) {} + +POST_SYSCALL(sysfs)(long res, long option, long arg1, long arg2) {} + +PRE_SYSCALL(syslog)(long type, void *buf, long len) {} + +POST_SYSCALL(syslog)(long res, long type, void *buf, long len) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(uselib)(const void *library) { + if (library) + PRE_READ(library, __sanitizer::internal_strlen((const char *)library) + 1); +} + +POST_SYSCALL(uselib)(long res, const void *library) {} + +PRE_SYSCALL(ni_syscall)() {} + +POST_SYSCALL(ni_syscall)(long res) {} + +PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {} + +POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {} + +PRE_SYSCALL(add_key)(const void *_type, const void *_description, + const void *_payload, long plen, long destringid) { + if (_type) + PRE_READ(_type, __sanitizer::internal_strlen((const char *)_type) + 1); + if (_description) + PRE_READ(_description, + __sanitizer::internal_strlen((const char *)_description) + 1); +} + +POST_SYSCALL(add_key)(long res, const void *_type, const void *_description, + const void *_payload, long plen, long destringid) {} + +PRE_SYSCALL(request_key)(const void *_type, const void *_description, + const void *_callout_info, long destringid) { + if (_type) + PRE_READ(_type, __sanitizer::internal_strlen((const char *)_type) + 1); + if (_description) + PRE_READ(_description, + __sanitizer::internal_strlen((const char *)_description) + 1); + if (_callout_info) + PRE_READ(_callout_info, + __sanitizer::internal_strlen((const char *)_callout_info) + 1); +} + +POST_SYSCALL(request_key)(long res, const void *_type, const void *_description, + const void *_callout_info, long destringid) {} + +PRE_SYSCALL(keyctl)(long cmd, long arg2, long arg3, long arg4, long arg5) {} + +POST_SYSCALL(keyctl)(long res, long cmd, long arg2, long arg3, long arg4, + long arg5) {} + +PRE_SYSCALL(ioprio_set)(long which, long who, long ioprio) {} + +POST_SYSCALL(ioprio_set)(long res, long which, long who, long ioprio) {} + +PRE_SYSCALL(ioprio_get)(long which, long who) {} + +POST_SYSCALL(ioprio_get)(long res, long which, long who) {} + +PRE_SYSCALL(set_mempolicy)(long mode, void *nmask, long maxnode) {} + +POST_SYSCALL(set_mempolicy)(long res, long mode, void *nmask, long maxnode) { + if (res >= 0) { + if (nmask) POST_WRITE(nmask, sizeof(long)); + } +} + +PRE_SYSCALL(migrate_pages)(long pid, long maxnode, const void *from, + const void *to) { + if (from) PRE_READ(from, sizeof(long)); + if (to) PRE_READ(to, sizeof(long)); +} + +POST_SYSCALL(migrate_pages)(long res, long pid, long maxnode, const void *from, + const void *to) {} + +PRE_SYSCALL(move_pages)(long pid, long nr_pages, const void **pages, + const int *nodes, int *status, long flags) { + if (pages) PRE_READ(pages, nr_pages * sizeof(*pages)); + if (nodes) PRE_READ(nodes, nr_pages * sizeof(*nodes)); +} + +POST_SYSCALL(move_pages)(long res, long pid, long nr_pages, const void **pages, + const int *nodes, int *status, long flags) { + if (res >= 0) { + if (status) POST_WRITE(status, nr_pages * sizeof(*status)); + } +} + +PRE_SYSCALL(mbind)(long start, long len, long mode, void *nmask, long maxnode, + long flags) {} + +POST_SYSCALL(mbind)(long res, long start, long len, long mode, void *nmask, + long maxnode, long flags) { + if (res >= 0) { + if (nmask) POST_WRITE(nmask, sizeof(long)); + } +} + +PRE_SYSCALL(get_mempolicy)(void *policy, void *nmask, long maxnode, long addr, + long flags) {} + +POST_SYSCALL(get_mempolicy)(long res, void *policy, void *nmask, long maxnode, + long addr, long flags) { + if (res >= 0) { + if (policy) POST_WRITE(policy, sizeof(int)); + if (nmask) POST_WRITE(nmask, sizeof(long)); + } +} + +PRE_SYSCALL(inotify_init)() {} + +POST_SYSCALL(inotify_init)(long res) {} + +PRE_SYSCALL(inotify_init1)(long flags) {} + +POST_SYSCALL(inotify_init1)(long res, long flags) {} + +PRE_SYSCALL(inotify_add_watch)(long fd, const void *path, long mask) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(inotify_add_watch)(long res, long fd, const void *path, + long mask) {} + +PRE_SYSCALL(inotify_rm_watch)(long fd, long wd) {} + +POST_SYSCALL(inotify_rm_watch)(long res, long fd, long wd) {} + +PRE_SYSCALL(spu_run)(long fd, void *unpc, void *ustatus) {} + +POST_SYSCALL(spu_run)(long res, long fd, unsigned *unpc, unsigned *ustatus) { + if (res >= 0) { + if (unpc) POST_WRITE(unpc, sizeof(*unpc)); + if (ustatus) POST_WRITE(ustatus, sizeof(*ustatus)); + } +} + +PRE_SYSCALL(spu_create)(const void *name, long flags, long mode, long fd) { + if (name) + PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); +} + +POST_SYSCALL(spu_create)(long res, const void *name, long flags, long mode, + long fd) {} + +PRE_SYSCALL(mknodat)(long dfd, const void *filename, long mode, long dev) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(mknodat)(long res, long dfd, const void *filename, long mode, + long dev) {} + +PRE_SYSCALL(mkdirat)(long dfd, const void *pathname, long mode) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(mkdirat)(long res, long dfd, const void *pathname, long mode) {} + +PRE_SYSCALL(unlinkat)(long dfd, const void *pathname, long flag) { + if (pathname) + PRE_READ(pathname, + __sanitizer::internal_strlen((const char *)pathname) + 1); +} + +POST_SYSCALL(unlinkat)(long res, long dfd, const void *pathname, long flag) {} + +PRE_SYSCALL(symlinkat)(const void *oldname, long newdfd, const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(symlinkat)(long res, const void *oldname, long newdfd, + const void *newname) {} + +PRE_SYSCALL(linkat)(long olddfd, const void *oldname, long newdfd, + const void *newname, long flags) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(linkat)(long res, long olddfd, const void *oldname, long newdfd, + const void *newname, long flags) {} + +PRE_SYSCALL(renameat)(long olddfd, const void *oldname, long newdfd, + const void *newname) { + if (oldname) + PRE_READ(oldname, __sanitizer::internal_strlen((const char *)oldname) + 1); + if (newname) + PRE_READ(newname, __sanitizer::internal_strlen((const char *)newname) + 1); +} + +POST_SYSCALL(renameat)(long res, long olddfd, const void *oldname, long newdfd, + const void *newname) {} + +PRE_SYSCALL(futimesat)(long dfd, const void *filename, void *utimes) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(futimesat)(long res, long dfd, const void *filename, + void *utimes) { + if (res >= 0) { + if (utimes) POST_WRITE(utimes, timeval_sz); + } +} + +PRE_SYSCALL(faccessat)(long dfd, const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(faccessat)(long res, long dfd, const void *filename, long mode) {} + +PRE_SYSCALL(fchmodat)(long dfd, const void *filename, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(fchmodat)(long res, long dfd, const void *filename, long mode) {} + +PRE_SYSCALL(fchownat)(long dfd, const void *filename, long user, long group, + long flag) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(fchownat)(long res, long dfd, const void *filename, long user, + long group, long flag) {} + +PRE_SYSCALL(openat)(long dfd, const void *filename, long flags, long mode) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(openat)(long res, long dfd, const void *filename, long flags, + long mode) {} + +PRE_SYSCALL(newfstatat)(long dfd, const void *filename, void *statbuf, + long flag) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(newfstatat)(long res, long dfd, const void *filename, + void *statbuf, long flag) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat_sz); + } +} + +PRE_SYSCALL(fstatat64)(long dfd, const void *filename, void *statbuf, + long flag) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(fstatat64)(long res, long dfd, const void *filename, void *statbuf, + long flag) { + if (res >= 0) { + if (statbuf) POST_WRITE(statbuf, struct_kernel_stat64_sz); + } +} + +PRE_SYSCALL(readlinkat)(long dfd, const void *path, void *buf, long bufsiz) { + if (path) + PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); +} + +POST_SYSCALL(readlinkat)(long res, long dfd, const void *path, void *buf, + long bufsiz) { + if (res >= 0) { + if (buf) + POST_WRITE(buf, __sanitizer::internal_strlen((const char *)buf) + 1); + } +} + +PRE_SYSCALL(utimensat)(long dfd, const void *filename, void *utimes, + long flags) { + if (filename) + PRE_READ(filename, + __sanitizer::internal_strlen((const char *)filename) + 1); +} + +POST_SYSCALL(utimensat)(long res, long dfd, const void *filename, void *utimes, + long flags) { + if (res >= 0) { + if (utimes) POST_WRITE(utimes, struct_timespec_sz); + } +} + +PRE_SYSCALL(unshare)(long unshare_flags) {} + +POST_SYSCALL(unshare)(long res, long unshare_flags) {} + +PRE_SYSCALL(splice)(long fd_in, void *off_in, long fd_out, void *off_out, + long len, long flags) {} + +POST_SYSCALL(splice)(long res, long fd_in, void *off_in, long fd_out, + void *off_out, long len, long flags) { + if (res >= 0) { + if (off_in) POST_WRITE(off_in, sizeof(long long)); + if (off_out) POST_WRITE(off_out, sizeof(long long)); + } +} + +PRE_SYSCALL(vmsplice)(long fd, const __sanitizer_iovec *iov, long nr_segs, + long flags) {} + +POST_SYSCALL(vmsplice)(long res, long fd, const __sanitizer_iovec *iov, + long nr_segs, long flags) { + if (res >= 0) { + if (iov) kernel_read_iovec(iov, nr_segs, res); + } +} + +PRE_SYSCALL(tee)(long fdin, long fdout, long len, long flags) {} + +POST_SYSCALL(tee)(long res, long fdin, long fdout, long len, long flags) {} + +PRE_SYSCALL(get_robust_list)(long pid, void *head_ptr, void *len_ptr) {} + +POST_SYSCALL(get_robust_list)(long res, long pid, void *head_ptr, + void *len_ptr) {} + +PRE_SYSCALL(set_robust_list)(void *head, long len) {} + +POST_SYSCALL(set_robust_list)(long res, void *head, long len) {} + +PRE_SYSCALL(getcpu)(void *cpu, void *node, void *cache) {} + +POST_SYSCALL(getcpu)(long res, void *cpu, void *node, void *cache) { + if (res >= 0) { + if (cpu) POST_WRITE(cpu, sizeof(unsigned)); + if (node) POST_WRITE(node, sizeof(unsigned)); + // The third argument to this system call is nowadays unused. + } +} + +PRE_SYSCALL(signalfd)(long ufd, void *user_mask, long sizemask) {} + +POST_SYSCALL(signalfd)(long res, long ufd, kernel_sigset_t *user_mask, + long sizemask) { + if (res >= 0) { + if (user_mask) POST_WRITE(user_mask, sizemask); + } +} + +PRE_SYSCALL(signalfd4)(long ufd, void *user_mask, long sizemask, long flags) {} + +POST_SYSCALL(signalfd4)(long res, long ufd, kernel_sigset_t *user_mask, + long sizemask, long flags) { + if (res >= 0) { + if (user_mask) POST_WRITE(user_mask, sizemask); + } +} + +PRE_SYSCALL(timerfd_create)(long clockid, long flags) {} + +POST_SYSCALL(timerfd_create)(long res, long clockid, long flags) {} + +PRE_SYSCALL(timerfd_settime)(long ufd, long flags, const void *utmr, + void *otmr) { + if (utmr) PRE_READ(utmr, struct_itimerspec_sz); +} + +POST_SYSCALL(timerfd_settime)(long res, long ufd, long flags, const void *utmr, + void *otmr) { + if (res >= 0) { + if (otmr) POST_WRITE(otmr, struct_itimerspec_sz); + } +} + +PRE_SYSCALL(timerfd_gettime)(long ufd, void *otmr) {} + +POST_SYSCALL(timerfd_gettime)(long res, long ufd, void *otmr) { + if (res >= 0) { + if (otmr) POST_WRITE(otmr, struct_itimerspec_sz); + } +} + +PRE_SYSCALL(eventfd)(long count) {} + +POST_SYSCALL(eventfd)(long res, long count) {} + +PRE_SYSCALL(eventfd2)(long count, long flags) {} + +POST_SYSCALL(eventfd2)(long res, long count, long flags) {} + +PRE_SYSCALL(old_readdir)(long arg0, void *arg1, long arg2) {} + +POST_SYSCALL(old_readdir)(long res, long arg0, void *arg1, long arg2) { + // Missing definition of 'struct old_linux_dirent'. +} + +PRE_SYSCALL(pselect6)(long arg0, __sanitizer___kernel_fd_set *arg1, + __sanitizer___kernel_fd_set *arg2, + __sanitizer___kernel_fd_set *arg3, void *arg4, + void *arg5) {} + +POST_SYSCALL(pselect6)(long res, long arg0, __sanitizer___kernel_fd_set *arg1, + __sanitizer___kernel_fd_set *arg2, + __sanitizer___kernel_fd_set *arg3, void *arg4, + void *arg5) { + if (res >= 0) { + if (arg1) POST_WRITE(arg1, sizeof(*arg1)); + if (arg2) POST_WRITE(arg2, sizeof(*arg2)); + if (arg3) POST_WRITE(arg3, sizeof(*arg3)); + if (arg4) POST_WRITE(arg4, struct_timespec_sz); + } +} + +PRE_SYSCALL(ppoll)(__sanitizer_pollfd *arg0, long arg1, void *arg2, + const kernel_sigset_t *arg3, long arg4) { + if (arg3) PRE_READ(arg3, arg4); +} + +POST_SYSCALL(ppoll)(long res, __sanitizer_pollfd *arg0, long arg1, void *arg2, + const void *arg3, long arg4) { + if (res >= 0) { + if (arg0) POST_WRITE(arg0, sizeof(*arg0)); + if (arg2) POST_WRITE(arg2, struct_timespec_sz); + } +} + +PRE_SYSCALL(syncfs)(long fd) {} + +POST_SYSCALL(syncfs)(long res, long fd) {} + +PRE_SYSCALL(perf_event_open)(void *attr_uptr, long pid, long cpu, long group_fd, + long flags) {} + +POST_SYSCALL(perf_event_open)(long res, void *attr_uptr, long pid, long cpu, + long group_fd, long flags) { + if (res >= 0) { + if (attr_uptr) POST_WRITE(attr_uptr, struct_perf_event_attr_sz); + } +} + +PRE_SYSCALL(mmap_pgoff)(long addr, long len, long prot, long flags, long fd, + long pgoff) {} + +POST_SYSCALL(mmap_pgoff)(long res, long addr, long len, long prot, long flags, + long fd, long pgoff) {} + +PRE_SYSCALL(old_mmap)(void *arg) {} + +POST_SYSCALL(old_mmap)(long res, void *arg) {} + +PRE_SYSCALL(name_to_handle_at)(long dfd, const void *name, void *handle, + void *mnt_id, long flag) {} + +POST_SYSCALL(name_to_handle_at)(long res, long dfd, const void *name, + void *handle, void *mnt_id, long flag) {} + +PRE_SYSCALL(open_by_handle_at)(long mountdirfd, void *handle, long flags) {} + +POST_SYSCALL(open_by_handle_at)(long res, long mountdirfd, void *handle, + long flags) {} + +PRE_SYSCALL(setns)(long fd, long nstype) {} + +POST_SYSCALL(setns)(long res, long fd, long nstype) {} + +PRE_SYSCALL(process_vm_readv)(long pid, const __sanitizer_iovec *lvec, + long liovcnt, const void *rvec, long riovcnt, + long flags) {} + +POST_SYSCALL(process_vm_readv)(long res, long pid, + const __sanitizer_iovec *lvec, long liovcnt, + const void *rvec, long riovcnt, long flags) { + if (res >= 0) { + if (lvec) kernel_write_iovec(lvec, liovcnt, res); + } +} + +PRE_SYSCALL(process_vm_writev)(long pid, const __sanitizer_iovec *lvec, + long liovcnt, const void *rvec, long riovcnt, + long flags) {} + +POST_SYSCALL(process_vm_writev)(long res, long pid, + const __sanitizer_iovec *lvec, long liovcnt, + const void *rvec, long riovcnt, long flags) { + if (res >= 0) { + if (lvec) kernel_read_iovec(lvec, liovcnt, res); + } +} + +PRE_SYSCALL(fork)() { + COMMON_SYSCALL_PRE_FORK(); +} + +POST_SYSCALL(fork)(long res) { + COMMON_SYSCALL_POST_FORK(res); +} + +PRE_SYSCALL(vfork)() { + COMMON_SYSCALL_PRE_FORK(); +} + +POST_SYSCALL(vfork)(long res) { + COMMON_SYSCALL_POST_FORK(res); +} +} // extern "C" + +#undef PRE_SYSCALL +#undef PRE_READ +#undef PRE_WRITE +#undef POST_SYSCALL +#undef POST_READ +#undef POST_WRITE + +#endif // SANITIZER_LINUX diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.cc b/libsanitizer/sanitizer_common/sanitizer_flags.cc index 2152c7bdff4..06ac5435b9d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.cc +++ b/libsanitizer/sanitizer_common/sanitizer_flags.cc @@ -16,15 +16,40 @@ namespace __sanitizer { +CommonFlags common_flags_dont_use_directly; + +void ParseCommonFlagsFromString(const char *str) { + CommonFlags *f = common_flags(); + ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); + 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->symbolize, "symbolize"); + ParseFlag(str, &f->handle_ioctl, "handle_ioctl"); + ParseFlag(str, &f->log_path, "log_path"); + 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"); +} + static bool GetFlagValue(const char *env, const char *name, const char **value, int *value_length) { if (env == 0) return false; - const char *pos = internal_strstr(env, name); - const char *end; - if (pos == 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 { @@ -75,7 +100,7 @@ void ParseFlag(const char *env, int *flag, const char *name) { int value_length; if (!GetFlagValue(env, name, &value, &value_length)) return; - *flag = internal_atoll(value); + *flag = static_cast(internal_atoll(value)); } static LowLevelAllocator allocator_for_flags; diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.h b/libsanitizer/sanitizer_common/sanitizer_flags.h index 1b9bf5bffdb..62aa96bbb69 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.h +++ b/libsanitizer/sanitizer_common/sanitizer_flags.h @@ -20,6 +20,41 @@ 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. + 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" instead of stderr. + const char *log_path; + // 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; +}; + +extern CommonFlags common_flags_dont_use_directly; + +inline CommonFlags *common_flags() { + return &common_flags_dont_use_directly; +} + +void ParseCommonFlagsFromString(const char *str); + } // namespace __sanitizer #endif // SANITIZER_FLAGS_H diff --git a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h index 577c9a9c17f..cc9233cad5f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h +++ b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h @@ -11,9 +11,12 @@ #ifndef SANITIZER_DEFS_H #define SANITIZER_DEFS_H -#if defined(_WIN32) -// FIXME find out what we need on Windows. __declspec(dllexport) ? -# define SANITIZER_INTERFACE_ATTRIBUTE +#include "sanitizer_platform.h" + +// Only use SANITIZER_*ATTRIBUTE* before the function return type! +#if SANITIZER_WINDOWS +# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport) +// FIXME find out what we need on Windows, if anything. # define SANITIZER_WEAK_ATTRIBUTE #elif defined(SANITIZER_GO) # define SANITIZER_INTERFACE_ATTRIBUTE @@ -23,7 +26,7 @@ # define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) #endif -#ifdef __linux__ +#if SANITIZER_LINUX && !defined(SANITIZER_GO) # define SANITIZER_SUPPORTS_WEAK_HOOKS 1 #else # define SANITIZER_SUPPORTS_WEAK_HOOKS 0 @@ -64,29 +67,39 @@ typedef signed int s32; typedef signed long long s64; // NOLINT typedef int fd_t; +// 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__)) +typedef u64 OFF_T; +#else +typedef uptr OFF_T; +#endif +typedef u64 OFF64_T; } // namespace __sanitizer extern "C" { // Tell the tools to write their reports to "path." instead of stderr. - void __sanitizer_set_report_path(const char *path) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __sanitizer_set_report_path(const char *path); // Tell the tools to write their reports to given file descriptor instead of // stderr. - void __sanitizer_set_report_fd(int fd) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __sanitizer_set_report_fd(int fd); // 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) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + 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. - void __sanitizer_report_error_summary(const char *error_summary) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + void __sanitizer_report_error_summary(const char *error_summary); } // extern "C" @@ -95,13 +108,13 @@ using namespace __sanitizer; // NOLINT // This header should NOT include any other headers to avoid portability issues. // Common defs. -#define INLINE static inline +#define INLINE inline #define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE #define WEAK SANITIZER_WEAK_ATTRIBUTE // Platform-specific defs. #if defined(_MSC_VER) -# define ALWAYS_INLINE __declspec(forceinline) +# define ALWAYS_INLINE __forceinline // FIXME(timurrrr): do we need this on Windows? # define ALIAS(x) # define ALIGNED(x) __declspec(align(x)) @@ -116,8 +129,10 @@ using namespace __sanitizer; // NOLINT # define USED # define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */ #else // _MSC_VER -# define ALWAYS_INLINE __attribute__((always_inline)) +# define ALWAYS_INLINE inline __attribute__((always_inline)) # define ALIAS(x) __attribute__((alias(x))) +// Please only use the ALIGNED macro before the type. +// Using ALIGNED after the variable declaration is not portable! # define ALIGNED(x) __attribute__((aligned(x))) # define FORMAT(f, a) __attribute__((format(printf, f, a))) # define NOINLINE __attribute__((noinline)) @@ -136,7 +151,15 @@ using namespace __sanitizer; // NOLINT # endif #endif // _MSC_VER -#if defined(_WIN32) +// Unaligned versions of basic types. +typedef ALIGNED(1) u16 uu16; +typedef ALIGNED(1) u32 uu32; +typedef ALIGNED(1) u64 uu64; +typedef ALIGNED(1) s16 us16; +typedef ALIGNED(1) s32 us32; +typedef ALIGNED(1) s64 us64; + +#if SANITIZER_WINDOWS typedef unsigned long DWORD; // NOLINT typedef DWORD thread_return_t; # define THREAD_CALLING_CONV __stdcall @@ -155,6 +178,9 @@ typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg); // NOTE: Functions below must be defined in each run-time. namespace __sanitizer { void NORETURN Die(); + +// FIXME: No, this shouldn't be in the sanitizer interface. +SANITIZER_INTERFACE_ATTRIBUTE void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); } // namespace __sanitizer @@ -259,10 +285,12 @@ extern "C" void* _ReturnAddress(void); # define GET_CURRENT_FRAME() (uptr)0xDEADBEEF #endif -#define HANDLE_EINTR(res, f) { \ - do { \ - res = (f); \ - } while (res == -1 && errno == EINTR); \ +#define HANDLE_EINTR(res, f) \ + { \ + int rverrno; \ + do { \ + res = (f); \ + } while (internal_iserror(res, &rverrno) && rverrno == EINTR); \ } #endif // SANITIZER_DEFS_H diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.cc b/libsanitizer/sanitizer_common/sanitizer_libc.cc index c57128a54cb..2a75e431b31 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.cc +++ b/libsanitizer/sanitizer_common/sanitizer_libc.cc @@ -8,6 +8,7 @@ // 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" @@ -122,6 +123,13 @@ char* internal_strchr(const char *s, int c) { } } +char *internal_strchrnul(const char *s, int c) { + char *res = internal_strchr(s, c); + if (!res) + res = (char*)s + internal_strlen(s); + return res; +} + char *internal_strrchr(const char *s, int c) { const char *res = 0; for (uptr i = 0; s[i]; i++) { @@ -149,8 +157,7 @@ char *internal_strncpy(char *dst, const char *src, uptr n) { uptr i; for (i = 0; i < n && src[i]; i++) dst[i] = src[i]; - for (; i < n; i++) - dst[i] = '\0'; + internal_memset(dst + i, '\0', n - i); return dst; } diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.h b/libsanitizer/sanitizer_common/sanitizer_libc.h index 16239413356..f90ffcc679a 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.h +++ b/libsanitizer/sanitizer_common/sanitizer_libc.h @@ -30,6 +30,7 @@ void *internal_memmove(void *dest, const void *src, uptr n); // Should not be used in performance-critical places. void *internal_memset(void *s, int c, uptr n); char* internal_strchr(const char *s, int c); +char *internal_strchrnul(const char *s, int c); int internal_strcmp(const char *s1, const char *s2); uptr internal_strcspn(const char *s, const char *reject); char *internal_strdup(const char *s); @@ -51,36 +52,46 @@ bool mem_is_zero(const char *mem, uptr size); // Memory -void *internal_mmap(void *addr, uptr length, int prot, int flags, - int fd, u64 offset); -int internal_munmap(void *addr, uptr length); +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 kStdinFd = 0; const fd_t kStdoutFd = 1; const fd_t kStderrFd = 2; -int internal_close(fd_t fd); +uptr internal_close(fd_t fd); int internal_isatty(fd_t fd); // Use __sanitizer::OpenFile() instead. -fd_t internal_open(const char *filename, int flags); -fd_t internal_open(const char *filename, int flags, u32 mode); +uptr internal_open(const char *filename, int flags); +uptr internal_open(const char *filename, int flags, u32 mode); uptr internal_read(fd_t fd, void *buf, uptr count); uptr internal_write(fd_t fd, const void *buf, uptr count); // OS uptr internal_filesize(fd_t fd); // -1 on error. -int internal_stat(const char *path, void *buf); -int internal_lstat(const char *path, void *buf); -int internal_fstat(fd_t fd, void *buf); -int internal_dup2(int oldfd, int newfd); +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(); // Threading -int internal_sched_yield(); +uptr internal_sched_yield(); + +// Error handling +bool internal_iserror(uptr retval, int *rverrno = 0); } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.cc b/libsanitizer/sanitizer_common/sanitizer_linux.cc index 1ab6f780fa3..666f15b4ed6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux.cc +++ b/libsanitizer/sanitizer_common/sanitizer_linux.cc @@ -9,29 +9,48 @@ // run-time libraries and implements linux-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// -#ifdef __linux__ + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" +#include "sanitizer_linux.h" #include "sanitizer_mutex.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" +#include +#include +#include #include +#if !SANITIZER_ANDROID +#include +#endif #include #include #include +#include #include #include #include #include #include -#include #include #include -#include + +#if !SANITIZER_ANDROID +#include +#endif + +// +struct kernel_timeval { + long tv_sec; + long tv_usec; +}; // is broken on some linux distributions. const int FUTEX_WAIT = 0; @@ -48,165 +67,158 @@ const int FUTEX_WAKE = 1; namespace __sanitizer { +#ifdef __x86_64__ +#include "sanitizer_syscall_linux_x86_64.inc" +#else +#include "sanitizer_syscall_generic.inc" +#endif + // --------------- sanitizer_libc.h -void *internal_mmap(void *addr, uptr length, int prot, int flags, +uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, u64 offset) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS - return (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset); + return internal_syscall(__NR_mmap, addr, length, prot, flags, fd, offset); #else - return (void *)syscall(__NR_mmap2, addr, length, prot, flags, fd, offset); + return internal_syscall(__NR_mmap2, addr, length, prot, flags, fd, offset); #endif } -int internal_munmap(void *addr, uptr length) { - return syscall(__NR_munmap, addr, length); +uptr internal_munmap(void *addr, uptr length) { + return internal_syscall(__NR_munmap, addr, length); } -int internal_close(fd_t fd) { - return syscall(__NR_close, fd); +uptr internal_close(fd_t fd) { + return internal_syscall(__NR_close, fd); } -fd_t internal_open(const char *filename, int flags) { - return syscall(__NR_open, filename, flags); +uptr internal_open(const char *filename, int flags) { + return internal_syscall(__NR_open, filename, flags); } -fd_t internal_open(const char *filename, int flags, u32 mode) { - return syscall(__NR_open, filename, flags, mode); +uptr internal_open(const char *filename, int flags, u32 mode) { + return internal_syscall(__NR_open, filename, flags, mode); } -fd_t OpenFile(const char *filename, bool write) { +uptr OpenFile(const char *filename, bool write) { return internal_open(filename, write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); } uptr internal_read(fd_t fd, void *buf, uptr count) { sptr res; - HANDLE_EINTR(res, (sptr)syscall(__NR_read, fd, buf, count)); + HANDLE_EINTR(res, (sptr)internal_syscall(__NR_read, fd, buf, count)); return res; } uptr internal_write(fd_t fd, const void *buf, uptr count) { sptr res; - HANDLE_EINTR(res, (sptr)syscall(__NR_write, fd, buf, count)); + HANDLE_EINTR(res, (sptr)internal_syscall(__NR_write, fd, buf, count)); return res; } -int internal_stat(const char *path, void *buf) { +#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS +static void stat64_to_stat(struct stat64 *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; + out->st_mtime = in->st_mtime; + out->st_ctime = in->st_ctime; + out->st_ino = in->st_ino; +} +#endif + +uptr internal_stat(const char *path, void *buf) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS - return syscall(__NR_stat, path, buf); + return internal_syscall(__NR_stat, path, buf); #else - return syscall(__NR_stat64, path, buf); + struct stat64 buf64; + int res = internal_syscall(__NR_stat64, path, &buf64); + stat64_to_stat(&buf64, (struct stat *)buf); + return res; #endif } -int internal_lstat(const char *path, void *buf) { +uptr internal_lstat(const char *path, void *buf) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS - return syscall(__NR_lstat, path, buf); + return internal_syscall(__NR_lstat, path, buf); #else - return syscall(__NR_lstat64, path, buf); + struct stat64 buf64; + int res = internal_syscall(__NR_lstat64, path, &buf64); + stat64_to_stat(&buf64, (struct stat *)buf); + return res; #endif } -int internal_fstat(fd_t fd, void *buf) { +uptr internal_fstat(fd_t fd, void *buf) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS - return syscall(__NR_fstat, fd, buf); + return internal_syscall(__NR_fstat, fd, buf); #else - return syscall(__NR_fstat64, fd, buf); + struct stat64 buf64; + int res = internal_syscall(__NR_fstat64, fd, &buf64); + stat64_to_stat(&buf64, (struct stat *)buf); + return res; #endif } uptr internal_filesize(fd_t fd) { -#if SANITIZER_LINUX_USES_64BIT_SYSCALLS struct stat st; -#else - struct stat64 st; -#endif if (internal_fstat(fd, &st)) return -1; return (uptr)st.st_size; } -int internal_dup2(int oldfd, int newfd) { - return syscall(__NR_dup2, oldfd, newfd); +uptr internal_dup2(int oldfd, int newfd) { + return internal_syscall(__NR_dup2, oldfd, newfd); } uptr internal_readlink(const char *path, char *buf, uptr bufsize) { - return (uptr)syscall(__NR_readlink, path, buf, bufsize); + return internal_syscall(__NR_readlink, path, buf, bufsize); } -int internal_sched_yield() { - return syscall(__NR_sched_yield); +uptr internal_unlink(const char *path) { + return internal_syscall(__NR_unlink, path); +} + +uptr internal_sched_yield() { + return internal_syscall(__NR_sched_yield); } void internal__exit(int exitcode) { - syscall(__NR_exit_group, exitcode); + internal_syscall(__NR_exit_group, exitcode); Die(); // Unreachable. } +uptr internal_execve(const char *filename, char *const argv[], + char *const envp[]) { + return internal_syscall(__NR_execve, filename, argv, envp); +} + // ----------------- sanitizer_common.h bool FileExists(const char *filename) { -#if SANITIZER_LINUX_USES_64BIT_SYSCALLS struct stat st; - if (syscall(__NR_stat, filename, &st)) + if (internal_stat(filename, &st)) return false; -#else - struct stat64 st; - if (syscall(__NR_stat64, filename, &st)) - return false; -#endif // Sanity check: filename is a regular file. return S_ISREG(st.st_mode); } uptr GetTid() { - return syscall(__NR_gettid); + return internal_syscall(__NR_gettid); } -void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, - uptr *stack_bottom) { - static const uptr kMaxThreadStackSize = 256 * (1 << 20); // 256M - CHECK(stack_top); - CHECK(stack_bottom); - if (at_initialization) { - // This is the main thread. Libpthread may not be initialized yet. - struct rlimit rl; - CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); - - // Find the mapping that contains a stack variable. - MemoryMappingLayout proc_maps; - uptr start, end, offset; - uptr prev_end = 0; - while (proc_maps.Next(&start, &end, &offset, 0, 0)) { - if ((uptr)&rl < end) - break; - prev_end = end; - } - CHECK((uptr)&rl >= start && (uptr)&rl < end); - - // Get stacksize from rlimit, but clip it so that it does not overlap - // with other mappings. - uptr stacksize = rl.rlim_cur; - if (stacksize > end - prev_end) - stacksize = end - prev_end; - // When running with unlimited stack size, we still want to set some limit. - // The unlimited stack size is caused by 'ulimit -s unlimited'. - // Also, for some reason, GNU make spawns subprocesses with unlimited stack. - if (stacksize > kMaxThreadStackSize) - stacksize = kMaxThreadStackSize; - *stack_top = end; - *stack_bottom = end - stacksize; - return; - } - pthread_attr_t attr; - CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); - uptr stacksize = 0; - void *stackaddr = 0; - pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); - pthread_attr_destroy(&attr); - - *stack_top = (uptr)stackaddr + stacksize; - *stack_bottom = (uptr)stackaddr; - CHECK(stacksize < kMaxThreadStackSize); // Sanity check. +u64 NanoTime() { + kernel_timeval tv = {}; + internal_syscall(__NR_gettimeofday, &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. @@ -237,21 +249,11 @@ const char *GetEnv(const char *name) { return 0; // Not found. } -#ifdef __GLIBC__ - extern "C" { - extern void *__libc_stack_end; + SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end; } -static void GetArgsAndEnv(char ***argv, char ***envp) { - uptr *stack_end = (uptr *)__libc_stack_end; - int argc = *stack_end; - *argv = (char**)(stack_end + 1); - *envp = (char**)(stack_end + argc + 2); -} - -#else // __GLIBC__ - +#if !SANITIZER_GO static void ReadNullSepFileToArray(const char *path, char ***arr, int arr_size) { char *buff; @@ -270,20 +272,32 @@ static void ReadNullSepFileToArray(const char *path, char ***arr, } (*arr)[count] = 0; } +#endif -static void GetArgsAndEnv(char ***argv, char ***envp) { - static const int kMaxArgv = 2000, kMaxEnvp = 2000; - ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv); - ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp); +static void GetArgsAndEnv(char*** argv, char*** envp) { +#if !SANITIZER_GO + if (&__libc_stack_end) { +#endif + uptr* stack_end = (uptr*)__libc_stack_end; + int argc = *stack_end; + *argv = (char**)(stack_end + 1); + *envp = (char**)(stack_end + argc + 2); +#if !SANITIZER_GO + } else { + static const int kMaxArgv = 2000, kMaxEnvp = 2000; + ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv); + ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp); + } +#endif } -#endif // __GLIBC__ - void ReExec() { char **argv, **envp; GetArgsAndEnv(&argv, &envp); - execve("/proc/self/exe", argv, envp); - Printf("execve failed, errno %d\n", errno); + uptr rv = internal_execve("/proc/self/exe", argv, envp); + int rverrno; + CHECK_EQ(internal_iserror(rv, &rverrno), true); + Printf("execve failed, errno %d\n", rverrno); Die(); } @@ -293,6 +307,10 @@ void PrepareForSandboxing() { // 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 + getSymbolizer()->PrepareForSandboxing(); +#endif } // ----------------- sanitizer_procmaps.h @@ -300,18 +318,22 @@ void PrepareForSandboxing() { ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_; StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized. -MemoryMappingLayout::MemoryMappingLayout() { +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 (proc_self_maps_.mmaped_size == 0) { - LoadFromCache(); - CHECK_GT(proc_self_maps_.len, 0); + 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); } - // internal_write(2, proc_self_maps_.data, proc_self_maps_.len); Reset(); // FIXME: in the future we may want to cache the mappings on demand only. - CacheMemoryMappings(); + if (cache_enabled) + CacheMemoryMappings(); } MemoryMappingLayout::~MemoryMappingLayout() { @@ -373,7 +395,7 @@ static uptr ParseHex(char **str) { return x; } -static bool IsOnOf(char c, char c1, char c2) { +static bool IsOneOf(char c, char c1, char c2) { return c == c1 || c == c2; } @@ -381,8 +403,33 @@ 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) { + char filename[], uptr filename_size, + uptr *protection) { char *last = proc_self_maps_.data + proc_self_maps_.len; if (current_ >= last) return false; uptr dummy; @@ -397,10 +444,22 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, CHECK_EQ(*current_++, '-'); *end = ParseHex(¤t_); CHECK_EQ(*current_++, ' '); - CHECK(IsOnOf(*current_++, '-', 'r')); - CHECK(IsOnOf(*current_++, '-', 'w')); - CHECK(IsOnOf(*current_++, '-', 'x')); - CHECK(IsOnOf(*current_++, 's', 'p')); + 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_++, ' '); @@ -432,87 +491,35 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, // Gets the object name and the offset by walking MemoryMappingLayout. bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset, char filename[], - uptr filename_size) { - return IterateForObjectNameAndOffset(addr, offset, filename, filename_size); + uptr filename_size, + uptr *protection) { + return IterateForObjectNameAndOffset(addr, offset, filename, filename_size, + protection); } -bool SanitizerSetThreadName(const char *name) { -#ifdef PR_SET_NAME - return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); // NOLINT -#else - return false; -#endif -} - -bool SanitizerGetThreadName(char *name, int max_len) { -#ifdef PR_GET_NAME - char buff[17]; - if (prctl(PR_GET_NAME, (unsigned long)buff, 0, 0, 0)) // NOLINT - return false; - internal_strncpy(name, buff, max_len); - name[max_len] = 0; - return true; -#else - return false; -#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 -} - -_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { - StackTrace *b = (StackTrace*)param; - CHECK(b->size < b->max_size); - uptr pc = Unwind_GetIP(ctx); - b->trace[b->size++] = pc; - if (b->size == b->max_size) return UNWIND_STOP; - return UNWIND_CONTINUE; -} - -static bool MatchPc(uptr cur_pc, uptr trace_pc) { - return cur_pc - trace_pc <= 64 || trace_pc - cur_pc <= 64; -} - -void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { - this->size = 0; - this->max_size = max_depth; - if (max_depth > 1) { - _Unwind_Backtrace(Unwind_Trace, this); - // We need to pop a few frames so that pc is on top. - // trace[0] belongs to the current function so we always pop it. - int to_pop = 1; - /**/ if (size > 1 && MatchPc(pc, trace[1])) to_pop = 1; - else if (size > 2 && MatchPc(pc, trace[2])) to_pop = 2; - else if (size > 3 && MatchPc(pc, trace[3])) to_pop = 3; - else if (size > 4 && MatchPc(pc, trace[4])) to_pop = 4; - else if (size > 5 && MatchPc(pc, trace[5])) to_pop = 5; - this->PopStackFrames(to_pop); +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') {} } - this->trace[0] = pc; + UnmapOrDie(smaps, smaps_cap); } -#endif // #ifndef SANITIZER_GO - enum MutexState { MtxUnlocked = 0, MtxLocked = 1, @@ -523,12 +530,16 @@ BlockingMutex::BlockingMutex(LinkerInitialized) { CHECK_EQ(owner_, 0); } +BlockingMutex::BlockingMutex() { + internal_memset(this, 0, sizeof(*this)); +} + void BlockingMutex::Lock() { atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked) return; while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) - syscall(__NR_futex, m, FUTEX_WAIT, MtxSleeping, 0, 0, 0); + internal_syscall(__NR_futex, m, FUTEX_WAIT, MtxSleeping, 0, 0, 0); } void BlockingMutex::Unlock() { @@ -536,9 +547,281 @@ void BlockingMutex::Unlock() { u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed); CHECK_NE(v, MtxUnlocked); if (v == MtxSleeping) - syscall(__NR_futex, m, FUTEX_WAKE, 1, 0, 0, 0); + internal_syscall(__NR_futex, m, FUTEX_WAKE, 1, 0, 0, 0); } +void BlockingMutex::CheckLocked() { + atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); + CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed)); +} + +// ----------------- sanitizer_linux.h +// The actual size of this structure is specified by d_reclen. +// Note that getdents64 uses a different structure format. We only provide the +// 32-bit syscall here. +struct linux_dirent { + unsigned long d_ino; + unsigned long d_off; + unsigned short d_reclen; + char d_name[256]; +}; + +// Syscall wrappers. +uptr internal_ptrace(int request, int pid, void *addr, void *data) { + return internal_syscall(__NR_ptrace, request, pid, addr, data); +} + +uptr internal_waitpid(int pid, int *status, int options) { + return internal_syscall(__NR_wait4, pid, status, options, 0 /* rusage */); +} + +uptr internal_getpid() { + return internal_syscall(__NR_getpid); +} + +uptr internal_getppid() { + return internal_syscall(__NR_getppid); +} + +uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) { + return internal_syscall(__NR_getdents, fd, dirp, count); +} + +uptr internal_lseek(fd_t fd, OFF_T offset, int whence) { + return internal_syscall(__NR_lseek, fd, offset, whence); +} + +uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) { + return internal_syscall(__NR_prctl, option, arg2, arg3, arg4, arg5); +} + +uptr internal_sigaltstack(const struct sigaltstack *ss, + struct sigaltstack *oss) { + return internal_syscall(__NR_sigaltstack, ss, oss); +} + +// ThreadLister implementation. +ThreadLister::ThreadLister(int pid) + : pid_(pid), + descriptor_(-1), + buffer_(4096), + error_(true), + entry_((struct linux_dirent *)buffer_.data()), + bytes_read_(0) { + char task_directory_path[80]; + internal_snprintf(task_directory_path, sizeof(task_directory_path), + "/proc/%d/task/", pid); + uptr openrv = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY); + if (internal_iserror(openrv)) { + error_ = true; + Report("Can't open /proc/%d/task for reading.\n", pid); + } else { + error_ = false; + descriptor_ = openrv; + } +} + +int ThreadLister::GetNextTID() { + int tid = -1; + do { + if (error_) + return -1; + if ((char *)entry_ >= &buffer_[bytes_read_] && !GetDirectoryEntries()) + return -1; + if (entry_->d_ino != 0 && entry_->d_name[0] >= '0' && + entry_->d_name[0] <= '9') { + // Found a valid tid. + tid = (int)internal_atoll(entry_->d_name); + } + entry_ = (struct linux_dirent *)(((char *)entry_) + entry_->d_reclen); + } while (tid < 0); + return tid; +} + +void ThreadLister::Reset() { + if (error_ || descriptor_ < 0) + return; + internal_lseek(descriptor_, 0, SEEK_SET); +} + +ThreadLister::~ThreadLister() { + if (descriptor_ >= 0) + internal_close(descriptor_); +} + +bool ThreadLister::error() { return error_; } + +bool ThreadLister::GetDirectoryEntries() { + CHECK_GE(descriptor_, 0); + CHECK_NE(error_, true); + bytes_read_ = internal_getdents(descriptor_, + (struct linux_dirent *)buffer_.data(), + buffer_.size()); + if (internal_iserror(bytes_read_)) { + Report("Can't read directory entries from /proc/%d/task.\n", pid_); + error_ = true; + return false; + } else if (bytes_read_ == 0) { + return false; + } + entry_ = (struct linux_dirent *)buffer_.data(); + return true; +} + +uptr GetPageSize() { +#if 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) { + uptr module_name_len = internal_readlink( + "/proc/self/exe", 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"); + } + 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); + } +} + +// Match full names of the form /path/to/base_name{-,.}* +bool LibraryNameIs(const char *full_name, const char *base_name) { + const char *name = full_name; + // Strip path. + while (*name != '\0') name++; + while (name > full_name && *name != '/') name--; + if (*name == '/') name++; + uptr base_name_length = internal_strlen(base_name); + if (internal_strncmp(name, base_name, base_name_length)) return false; + return (name[base_name_length] == '-' || name[base_name_length] == '.'); +} + +#if !SANITIZER_ANDROID +// Call cb for each region mapped by map. +void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) { + typedef ElfW(Phdr) Elf_Phdr; + typedef ElfW(Ehdr) Elf_Ehdr; + char *base = (char *)map->l_addr; + Elf_Ehdr *ehdr = (Elf_Ehdr *)base; + char *phdrs = base + ehdr->e_phoff; + char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize; + + // Find the segment with the minimum base so we can "relocate" the p_vaddr + // fields. Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC + // objects have a non-zero base. + uptr preferred_base = (uptr)-1; + for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { + Elf_Phdr *phdr = (Elf_Phdr *)iter; + if (phdr->p_type == PT_LOAD && preferred_base > (uptr)phdr->p_vaddr) + preferred_base = (uptr)phdr->p_vaddr; + } + + // Compute the delta from the real base to get a relocation delta. + sptr delta = (uptr)base - preferred_base; + // Now we can figure out what the loader really mapped. + for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { + Elf_Phdr *phdr = (Elf_Phdr *)iter; + if (phdr->p_type == PT_LOAD) { + uptr seg_start = phdr->p_vaddr + delta; + uptr seg_end = seg_start + phdr->p_memsz; + // None of these values are aligned. We consider the ragged edges of the + // load command as defined, since they are mapped from the file. + seg_start = RoundDownTo(seg_start, GetPageSizeCached()); + seg_end = RoundUpTo(seg_end, GetPageSizeCached()); + cb((void *)seg_start, seg_end - seg_start); + } + } +} +#endif + +#if defined(__x86_64__) +// 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 +// the parent (because we don't know how to allocate a new thread +// descriptor to keep glibc happy). So the stock version of clone(), when +// used with CLONE_VM, would end up corrupting the parent's thread descriptor. +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(void *); + ((void **)child_stack)[0] = (void *)(uptr)fn; + ((void **)child_stack)[1] = arg; + __asm__ __volatile__( + /* %rax = syscall(%rax = __NR_clone, + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + "movq %6,%%r8\n" + "movq %7,%%r10\n" + ".cfi_endproc\n" + "syscall\n" + + /* if (%rax != 0) + * return; + */ + "testq %%rax,%%rax\n" + "jnz 1f\n" + + /* In the child. Terminate unwind chain. */ + ".cfi_startproc\n" + ".cfi_undefined %%rip;\n" + "xorq %%rbp,%%rbp\n" + + /* Call "fn(arg)". */ + "popq %%rax\n" + "popq %%rdi\n" + "call *%%rax\n" + + /* Call _exit(%rax). */ + "movq %%rax,%%rdi\n" + "movq %2,%%rax\n" + "syscall\n" + + /* Return to parent. */ + "1:\n" + : "=a" (res) + : "a"(__NR_clone), "i"(__NR_exit), + "S"(child_stack), + "D"(flags), + "d"(parent_tidptr), + "r"(newtls), + "r"(child_tidptr) + : "rsp", "memory", "r8", "r10", "r11", "rcx"); + return res; +} +#endif // defined(__x86_64__) } // namespace __sanitizer -#endif // __linux__ +#endif // SANITIZER_LINUX diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.h b/libsanitizer/sanitizer_common/sanitizer_linux.h new file mode 100644 index 00000000000..5bbf47904d8 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_linux.h @@ -0,0 +1,81 @@ +//===-- sanitizer_linux.h ---------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Linux-specific syscall wrappers and classes. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_LINUX_H +#define SANITIZER_LINUX_H + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" + +struct link_map; // Opaque type returned by dlopen(). +struct sigaltstack; + +namespace __sanitizer { +// Dirent structure for getdents(). Note that this structure is different from +// the one in , which is used by readdir(). +struct linux_dirent; + +// 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); +#ifdef __x86_64__ +uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, + int *parent_tidptr, void *newtls, int *child_tidptr); +#endif + +// This class reads thread IDs from /proc//task using only syscalls. +class ThreadLister { + public: + explicit ThreadLister(int pid); + ~ThreadLister(); + // GetNextTID returns -1 if the list of threads is exhausted, or if there has + // been an error. + int GetNextTID(); + void Reset(); + bool error(); + + private: + bool GetDirectoryEntries(); + + int pid_; + int descriptor_; + InternalScopedBuffer buffer_; + bool error_; + struct linux_dirent* entry_; + int bytes_read_; +}; + +void AdjustStackSizeLinux(void *attr, int verbosity); + +// Exposed for testing. +uptr ThreadDescriptorSize(); +uptr ThreadSelf(); +uptr ThreadSelfOffset(); + +// Matches a library's file name against a base name (stripping path and version +// 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_LINUX_H diff --git a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc new file mode 100644 index 00000000000..7d6c63918de --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc @@ -0,0 +1,351 @@ +//===-- sanitizer_linux_libcdep.cc ----------------------------------------===// +// +// 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 implements linux-specific functions from +// sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_common.h" +#include "sanitizer_linux.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" + +#include +#include +#include +#include +#include + +#if !SANITIZER_ANDROID +#include +#include +#endif + +namespace __sanitizer { + +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) { + // This is the main thread. Libpthread may not be initialized yet. + struct rlimit rl; + CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); + + // Find the mapping that contains a stack variable. + 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)) { + if ((uptr)&rl < end) + break; + prev_end = end; + } + CHECK((uptr)&rl >= start && (uptr)&rl < end); + + // Get stacksize from rlimit, but clip it so that it does not overlap + // with other mappings. + uptr stacksize = rl.rlim_cur; + if (stacksize > end - prev_end) + stacksize = end - prev_end; + // When running with unlimited stack size, we still want to set some limit. + // The unlimited stack size is caused by 'ulimit -s unlimited'. + // Also, for some reason, GNU make spawns subprocesses with unlimited stack. + if (stacksize > kMaxThreadStackSize) + stacksize = kMaxThreadStackSize; + *stack_top = end; + *stack_bottom = end - stacksize; + return; + } + pthread_attr_t attr; + CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); + uptr stacksize = 0; + void *stackaddr = 0; + pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); + pthread_attr_destroy(&attr); + + CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check. + *stack_top = (uptr)stackaddr + stacksize; + *stack_bottom = (uptr)stackaddr; +} + +// Does not compile for Go because dlsym() requires -ldl +#ifndef SANITIZER_GO +bool SetEnv(const char *name, const char *value) { + void *f = dlsym(RTLD_NEXT, "setenv"); + if (f == 0) + return false; + typedef int(*setenv_ft)(const char *name, const char *value, int overwrite); + setenv_ft setenv_f; + CHECK_EQ(sizeof(setenv_f), sizeof(f)); + internal_memcpy(&setenv_f, &f, sizeof(f)); + return setenv_f(name, value, 1) == 0; +} +#endif + +bool SanitizerSetThreadName(const char *name) { +#ifdef PR_SET_NAME + return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); // NOLINT +#else + return false; +#endif +} + +bool SanitizerGetThreadName(char *name, int max_len) { +#ifdef PR_GET_NAME + char buff[17]; + if (prctl(PR_GET_NAME, (unsigned long)buff, 0, 0, 0)) // NOLINT + return false; + internal_strncpy(name, buff, max_len); + name[max_len] = 0; + return true; +#else + return false; +#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 +} + +_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { + StackTrace *b = (StackTrace*)param; + CHECK(b->size < b->max_size); + uptr pc = Unwind_GetIP(ctx); + b->trace[b->size++] = pc; + if (b->size == b->max_size) return UNWIND_STOP; + return UNWIND_CONTINUE; +} + +static bool MatchPc(uptr cur_pc, uptr trace_pc) { + return cur_pc - trace_pc <= 64 || trace_pc - cur_pc <= 64; +} + +void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { + this->size = 0; + this->max_size = max_depth; + if (max_depth > 1) { + _Unwind_Backtrace(Unwind_Trace, this); + // We need to pop a few frames so that pc is on top. + // trace[0] belongs to the current function so we always pop it. + int to_pop = 1; + /**/ if (size > 1 && MatchPc(pc, trace[1])) to_pop = 1; + else if (size > 2 && MatchPc(pc, trace[2])) to_pop = 2; + else if (size > 3 && MatchPc(pc, trace[3])) to_pop = 3; + else if (size > 4 && MatchPc(pc, trace[4])) to_pop = 4; + else if (size > 5 && MatchPc(pc, trace[5])) to_pop = 5; + this->PopStackFrames(to_pop); + } + this->trace[0] = pc; +} + +#endif // !SANITIZER_GO + +static uptr g_tls_size; + +#ifdef __i386__ +# define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall)) +#else +# define DL_INTERNAL_FUNCTION +#endif + +void InitTlsSize() { +#if !defined(SANITIZER_GO) && !SANITIZER_ANDROID + 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"); + CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr)); + internal_memcpy(&get_tls, &get_tls_static_info_ptr, + sizeof(get_tls_static_info_ptr)); + CHECK_NE(get_tls, 0); + 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 defined(__x86_64__) || defined(__i386__) +// sizeof(struct thread) from glibc. +// There has been a report of this being different on glibc 2.11 and 2.13. We +// don't know when this change happened, so 2.14 is a conservative estimate. +#if __GLIBC_PREREQ(2, 14) +const uptr kThreadDescriptorSize = FIRST_32_SECOND_64(1216, 2304); +#else +const uptr kThreadDescriptorSize = FIRST_32_SECOND_64(1168, 2304); +#endif + +uptr ThreadDescriptorSize() { + return kThreadDescriptorSize; +} + +// The offset at which pointer to self is located in the thread descriptor. +const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16); + +uptr ThreadSelfOffset() { + return kThreadSelfOffset; +} + +uptr ThreadSelf() { + uptr descr_addr; +#ifdef __i386__ + asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); +#else + asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); +#endif + return descr_addr; +} +#endif // defined(__x86_64__) || defined(__i386__) + +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 += kThreadDescriptorSize; +#else + *tls_addr = 0; + *tls_size = 0; +#endif + + uptr stack_top, stack_bottom; + GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); + *stk_addr = stack_bottom; + *stk_size = stack_top - stack_bottom; + + if (!main) { + // If stack and tls intersect, make them non-intersecting. + if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) { + CHECK_GT(*tls_addr + *tls_size, *stk_addr); + CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size); + *stk_size -= *tls_size; + *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_, int verbosity) { + pthread_attr_t *attr = (pthread_attr_t *)attr_; + uptr stackaddr = 0; + size_t stacksize = 0; + 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 (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"); + } + } +} + +#if SANITIZER_ANDROID +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + return 0; +} +#else // SANITIZER_ANDROID +typedef ElfW(Phdr) Elf_Phdr; + +struct DlIteratePhdrData { + LoadedModule *modules; + uptr current_n; + bool first; + uptr max_n; + string_predicate_t filter; +}; + +static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { + DlIteratePhdrData *data = (DlIteratePhdrData*)arg; + if (data->current_n == data->max_n) + return 0; + InternalScopedBuffer module_name(kMaxPathLength); + module_name.data()[0] = '\0'; + if (data->first) { + data->first = false; + // First module is the binary itself. + ReadBinaryName(module_name.data(), module_name.size()); + } else if (info->dlpi_name) { + internal_strncpy(module_name.data(), info->dlpi_name, module_name.size()); + } + if (module_name.data()[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); + 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); + } + } + return 0; +} + +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + CHECK(modules); + DlIteratePhdrData data = {modules, 0, true, max_modules, filter}; + dl_iterate_phdr(dl_iterate_phdr_cb, &data); + return data.current_n; +} +#endif // SANITIZER_ANDROID + +} // namespace __sanitizer + +#endif // SANITIZER_LINUX diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_mac.cc index d7885bb3509..fa146b5b4e8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.cc +++ b/libsanitizer/sanitizer_common/sanitizer_mac.cc @@ -10,7 +10,9 @@ // sanitizer_libc.h. //===----------------------------------------------------------------------===// -#ifdef __APPLE__ +#include "sanitizer_platform.h" +#if SANITIZER_MAC + // Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so // the clients will most certainly use 64-bit ones as well. #ifndef _DARWIN_USE_64_BIT_INODE @@ -21,6 +23,7 @@ #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" +#include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include // for _NSGetEnviron @@ -35,32 +38,35 @@ #include #include #include +#include namespace __sanitizer { +#include "sanitizer_syscall_generic.inc" + // ---------------------- sanitizer_libc.h -void *internal_mmap(void *addr, size_t length, int prot, int flags, - int fd, u64 offset) { - return mmap(addr, length, prot, flags, fd, offset); +uptr internal_mmap(void *addr, size_t length, int prot, int flags, + int fd, u64 offset) { + return (uptr)mmap(addr, length, prot, flags, fd, offset); } -int internal_munmap(void *addr, uptr length) { +uptr internal_munmap(void *addr, uptr length) { return munmap(addr, length); } -int internal_close(fd_t fd) { +uptr internal_close(fd_t fd) { return close(fd); } -fd_t internal_open(const char *filename, int flags) { +uptr internal_open(const char *filename, int flags) { return open(filename, flags); } -fd_t internal_open(const char *filename, int flags, u32 mode) { +uptr internal_open(const char *filename, int flags, u32 mode) { return open(filename, flags, mode); } -fd_t OpenFile(const char *filename, bool write) { +uptr OpenFile(const char *filename, bool write) { return internal_open(filename, write ? O_WRONLY | O_CREAT : O_RDONLY, 0660); } @@ -73,15 +79,15 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { return write(fd, buf, count); } -int internal_stat(const char *path, void *buf) { +uptr internal_stat(const char *path, void *buf) { return stat(path, (struct stat *)buf); } -int internal_lstat(const char *path, void *buf) { +uptr internal_lstat(const char *path, void *buf) { return lstat(path, (struct stat *)buf); } -int internal_fstat(fd_t fd, void *buf) { +uptr internal_fstat(fd_t fd, void *buf) { return fstat(fd, (struct stat *)buf); } @@ -92,7 +98,7 @@ uptr internal_filesize(fd_t fd) { return (uptr)st.st_size; } -int internal_dup2(int oldfd, int newfd) { +uptr internal_dup2(int oldfd, int newfd) { return dup2(oldfd, newfd); } @@ -100,7 +106,7 @@ uptr internal_readlink(const char *path, char *buf, uptr bufsize) { return readlink(path, buf, bufsize); } -int internal_sched_yield() { +uptr internal_sched_yield() { return sched_yield(); } @@ -108,6 +114,10 @@ void internal__exit(int exitcode) { _exit(exitcode); } +uptr internal_getpid() { + return getpid(); +} + // ----------------- sanitizer_common.h bool FileExists(const char *filename) { struct stat st; @@ -159,9 +169,13 @@ void PrepareForSandboxing() { // Nothing here for now. } +uptr GetPageSize() { + return sysconf(_SC_PAGESIZE); +} + // ----------------- sanitizer_procmaps.h -MemoryMappingLayout::MemoryMappingLayout() { +MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) { Reset(); } @@ -214,7 +228,9 @@ void MemoryMappingLayout::LoadFromCache() { template bool MemoryMappingLayout::NextSegmentLoad( uptr *start, uptr *end, uptr *offset, - char filename[], uptr filename_size) { + 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) { @@ -239,7 +255,8 @@ bool MemoryMappingLayout::NextSegmentLoad( } bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, - char filename[], uptr filename_size) { + 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; @@ -271,14 +288,14 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, #ifdef MH_MAGIC_64 case MH_MAGIC_64: { if (NextSegmentLoad( - start, end, offset, filename, filename_size)) + start, end, offset, filename, filename_size, protection)) return true; break; } #endif case MH_MAGIC: { if (NextSegmentLoad( - start, end, offset, filename, filename_size)) + start, end, offset, filename, filename_size, protection)) return true; break; } @@ -292,18 +309,24 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset, char filename[], - uptr filename_size) { - return IterateForObjectNameAndOffset(addr, offset, filename, filename_size); + uptr filename_size, + uptr *protection) { + return IterateForObjectNameAndOffset(addr, offset, filename, filename_size, + protection); } BlockingMutex::BlockingMutex(LinkerInitialized) { // We assume that OS_SPINLOCK_INIT is zero } +BlockingMutex::BlockingMutex() { + internal_memset(this, 0, sizeof(*this)); +} + void BlockingMutex::Lock() { CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_)); - CHECK(OS_SPINLOCK_INIT == 0); - CHECK(owner_ != (uptr)pthread_self()); + CHECK_EQ(OS_SPINLOCK_INIT, 0); + CHECK_NE(owner_, (uptr)pthread_self()); OSSpinLockLock((OSSpinLock*)&opaque_storage_); CHECK(!owner_); owner_ = (uptr)pthread_self(); @@ -315,6 +338,69 @@ void BlockingMutex::Unlock() { OSSpinLockUnlock((OSSpinLock*)&opaque_storage_); } +void BlockingMutex::CheckLocked() { + CHECK_EQ((uptr)pthread_self(), owner_); +} + +u64 NanoTime() { + return 0; +} + +uptr GetTlsSize() { + return 0; +} + +void InitTlsSize() { +} + +void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, + uptr *tls_addr, uptr *tls_size) { +#ifndef SANITIZER_GO + uptr stack_top, stack_bottom; + GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); + *stk_addr = stack_bottom; + *stk_size = stack_top - stack_bottom; + *tls_addr = 0; + *tls_size = 0; +#else + *stk_addr = 0; + *stk_size = 0; + *tls_addr = 0; + *tls_size = 0; +#endif +} + +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 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++; + } + cur_module->addAddressRange(cur_beg, cur_end); + } + return n_modules; +} + } // namespace __sanitizer -#endif // __APPLE__ +#endif // SANITIZER_MAC diff --git a/libsanitizer/sanitizer_common/sanitizer_mutex.h b/libsanitizer/sanitizer_common/sanitizer_mutex.h index 27009118e62..b3501146279 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mutex.h +++ b/libsanitizer/sanitizer_common/sanitizer_mutex.h @@ -68,8 +68,10 @@ class SpinMutex : public StaticSpinMutex { class BlockingMutex { public: explicit BlockingMutex(LinkerInitialized); + BlockingMutex(); void Lock(); void Unlock(); + void CheckLocked(); private: uptr opaque_storage_[10]; uptr owner_; // for debugging diff --git a/libsanitizer/sanitizer_common/sanitizer_placement_new.h b/libsanitizer/sanitizer_common/sanitizer_placement_new.h index e32d65702df..310327fd8f0 100644 --- a/libsanitizer/sanitizer_common/sanitizer_placement_new.h +++ b/libsanitizer/sanitizer_common/sanitizer_placement_new.h @@ -17,7 +17,7 @@ #include "sanitizer_internal_defs.h" namespace __sanitizer { -#if (SANITIZER_WORDSIZE == 64) || defined(__APPLE__) +#if (SANITIZER_WORDSIZE == 64) || SANITIZER_MAC typedef uptr operator_new_ptr_type; #else typedef u32 operator_new_ptr_type; diff --git a/libsanitizer/sanitizer_common/sanitizer_platform.h b/libsanitizer/sanitizer_common/sanitizer_platform.h new file mode 100644 index 00000000000..2270709449b --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_platform.h @@ -0,0 +1,44 @@ +//===-- sanitizer_platform.h ------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Common platform macros. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_PLATFORM_H +#define SANITIZER_PLATFORM_H + +#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) +# error "This operating system is not supported" +#endif + +#if defined(__linux__) +# define SANITIZER_LINUX 1 +#else +# define SANITIZER_LINUX 0 +#endif + +#if defined(__APPLE__) +# define SANITIZER_MAC 1 +#else +# define SANITIZER_MAC 0 +#endif + +#if defined(_WIN32) +# define SANITIZER_WINDOWS 1 +#else +# define SANITIZER_WINDOWS 0 +#endif + +#if defined(__ANDROID__) || defined(ANDROID) +# define SANITIZER_ANDROID 1 +#else +# define SANITIZER_ANDROID 0 +#endif + +#define SANITIZER_POSIX (SANITIZER_LINUX || SANITIZER_MAC) + +#endif // SANITIZER_PLATFORM_H diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h index 9b40c0cc523..e0199482f10 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h @@ -9,38 +9,120 @@ // given library functions on a given platform. // //===----------------------------------------------------------------------===// +#ifndef SANITIZER_PLATFORM_INTERCEPTORS_H +#define SANITIZER_PLATFORM_INTERCEPTORS_H #include "sanitizer_internal_defs.h" -#if !defined(_WIN32) +#if !SANITIZER_WINDOWS # define SI_NOT_WINDOWS 1 # include "sanitizer_platform_limits_posix.h" #else # define SI_NOT_WINDOWS 0 #endif -#if defined(__linux__) && !defined(ANDROID) +#if SANITIZER_LINUX && !SANITIZER_ANDROID # define SI_LINUX_NOT_ANDROID 1 #else # define SI_LINUX_NOT_ANDROID 0 #endif -#if defined(__linux__) +#if SANITIZER_LINUX # define SI_LINUX 1 #else # define SI_LINUX 0 #endif +#if SANITIZER_MAC +# define SI_MAC 1 +#else +# define SI_MAC 0 +#endif + +# define SANITIZER_INTERCEPT_STRCMP 1 +# define SANITIZER_INTERCEPT_STRCASECMP 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_PRCTL SI_LINUX_NOT_ANDROID +#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_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_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_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 + +#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc new file mode 100644 index 00000000000..fbea5962c81 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc @@ -0,0 +1,45 @@ +//===-- sanitizer_platform_limits_linux.cc --------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Sanitizer common code. +// +// Sizes and layouts of linux kernel data structures. +//===----------------------------------------------------------------------===// + +// This is a separate compilation unit for linux headers that conflict with +// userspace headers. +// Most "normal" includes go in sanitizer_platform_limits_posix.cc + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +// This header seems to contain the definitions of _kernel_ stat* structs. +#include +#include + +#if !SANITIZER_ANDROID +#include +#endif + +namespace __sanitizer { + unsigned struct___old_kernel_stat_sz = sizeof(struct __old_kernel_stat); + unsigned struct_kernel_stat_sz = sizeof(struct stat); + unsigned struct_io_event_sz = sizeof(struct io_event); + unsigned struct_iocb_sz = sizeof(struct iocb); + +#ifndef _LP64 + unsigned struct_kernel_stat64_sz = sizeof(struct stat64); +#else + unsigned struct_kernel_stat64_sz = 0; +#endif + +#if !SANITIZER_ANDROID + unsigned struct_perf_event_attr_sz = sizeof(struct perf_event_attr); +#endif +} // namespace __sanitizer + +#endif // SANITIZER_LINUX diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc index c4be1aa42da..971a19314d8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -10,24 +10,105 @@ // Sizes and layouts of platform-specific POSIX data structures. //===----------------------------------------------------------------------===// -#if defined(__linux__) || defined(__APPLE__) + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX || SANITIZER_MAC #include "sanitizer_internal_defs.h" #include "sanitizer_platform_limits_posix.h" +#include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include #include +#include -#if defined(__linux__) +#if SANITIZER_LINUX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if !SANITIZER_ANDROID +#include +#include +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID + +#if SANITIZER_ANDROID +#include +#include +#include +#include +#endif + +#if SANITIZER_LINUX +#include #include #include -#endif // __linux__ +// #include +#include +#endif // SANITIZER_LINUX + +#if SANITIZER_MAC +#include +#include +#include +#endif namespace __sanitizer { unsigned struct_utsname_sz = sizeof(struct utsname); @@ -35,34 +116,768 @@ namespace __sanitizer { unsigned struct_stat64_sz = sizeof(struct stat64); unsigned struct_rusage_sz = sizeof(struct rusage); unsigned struct_tm_sz = sizeof(struct tm); + unsigned struct_passwd_sz = sizeof(struct passwd); + unsigned struct_group_sz = sizeof(struct group); + unsigned siginfo_t_sz = sizeof(siginfo_t); + unsigned struct_sigaction_sz = sizeof(struct sigaction); + unsigned struct_itimerval_sz = sizeof(struct itimerval); + unsigned pthread_t_sz = sizeof(pthread_t); + unsigned pid_t_sz = sizeof(pid_t); + unsigned timeval_sz = sizeof(timeval); + unsigned uid_t_sz = sizeof(uid_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); -#if defined(__linux__) +#if !SANITIZER_ANDROID + unsigned ucontext_t_sz = sizeof(ucontext_t); +#endif // !SANITIZER_ANDROID + +#if SANITIZER_LINUX unsigned struct_rlimit_sz = sizeof(struct rlimit); - unsigned struct_dirent_sz = sizeof(struct dirent); unsigned struct_statfs_sz = sizeof(struct statfs); unsigned struct_epoll_event_sz = sizeof(struct epoll_event); -#endif // __linux__ + 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 defined(__linux__) && !defined(__ANDROID__) +#if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned struct_rlimit64_sz = sizeof(struct rlimit64); unsigned struct_statfs64_sz = sizeof(struct statfs64); -#endif // __linux__ && !__ANDROID__ + unsigned struct_timex_sz = sizeof(struct timex); + unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds); + unsigned struct_shmid_ds_sz = sizeof(struct shmid_ds); + unsigned struct_mq_attr_sz = sizeof(struct mq_attr); +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID - void* __sanitizer_get_msghdr_iov_iov_base(void* msg, int idx) { - return ((struct msghdr *)msg)->msg_iov[idx].iov_base; + uptr sig_ign = (uptr)SIG_IGN; + uptr sig_dfl = (uptr)SIG_DFL; + uptr sa_siginfo = (uptr)SA_SIGINFO; + +#if SANITIZER_LINUX + int e_tabsz = (int)E_TABSZ; +#endif + + int af_inet = (int)AF_INET; + int af_inet6 = (int)AF_INET6; + + uptr __sanitizer_in_addr_sz(int af) { + if (af == AF_INET) + return sizeof(struct in_addr); + else if (af == AF_INET6) + return sizeof(struct in6_addr); + else + return 0; } - uptr __sanitizer_get_msghdr_iov_iov_len(void* msg, int idx) { - return ((struct msghdr *)msg)->msg_iov[idx].iov_len; - } +#if SANITIZER_LINUX && !SANITIZER_ANDROID + int glob_nomatch = GLOB_NOMATCH; + int glob_altdirfunc = GLOB_ALTDIRFUNC; +#endif - uptr __sanitizer_get_msghdr_iovlen(void* msg) { - return ((struct msghdr *)msg)->msg_iovlen; - } +#if SANITIZER_LINUX && !SANITIZER_ANDROID && \ + (defined(__i386) || defined (__x86_64)) // NOLINT + unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct); + unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); +#if __WORDSIZE == 64 + unsigned struct_user_fpxregs_struct_sz = 0; +#else + unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); +#endif - uptr __sanitizer_get_socklen_t(void* socklen_ptr) { - return *(socklen_t*)socklen_ptr; - } + int ptrace_getregs = PTRACE_GETREGS; + int ptrace_setregs = PTRACE_SETREGS; + int ptrace_getfpregs = PTRACE_GETFPREGS; + int ptrace_setfpregs = PTRACE_SETFPREGS; + int ptrace_getfpxregs = PTRACE_GETFPXREGS; + int ptrace_setfpxregs = PTRACE_SETFPXREGS; + int ptrace_getsiginfo = PTRACE_GETSIGINFO; + int ptrace_setsiginfo = 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 + + 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_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_subchnl_sz = sizeof(struct cdrom_subchnl); + unsigned struct_cdrom_ti_sz = sizeof(struct cdrom_ti); + 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_floppy_fdc_state_sz = sizeof(struct floppy_fdc_state); + unsigned struct_floppy_max_errors_sz = sizeof(struct floppy_max_errors); + unsigned struct_floppy_raw_cmd_sz = sizeof(struct floppy_raw_cmd); + unsigned struct_floppy_struct_sz = sizeof(struct floppy_struct); + unsigned struct_floppy_write_errors_sz = sizeof(struct floppy_write_errors); + unsigned struct_format_descr_sz = sizeof(struct format_descr); + unsigned struct_hd_driveid_sz = sizeof(struct hd_driveid); + 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_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 + +#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_input_keymap_entry_sz = sizeof(struct input_keymap_entry); +#else + unsigned struct_input_keymap_entry_sz = 0; +#endif + unsigned struct_ipx_config_data_sz = sizeof(struct ipx_config_data); + unsigned struct_kbdiacrs_sz = sizeof(struct kbdiacrs); + unsigned struct_kbentry_sz = sizeof(struct kbentry); + unsigned struct_kbkeycode_sz = sizeof(struct kbkeycode); + 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 + = sizeof(struct serial_multiport_struct); + unsigned struct_serial_struct_sz = sizeof(struct serial_struct); + unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25); + unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc); + unsigned struct_unimapinit_sz = sizeof(struct unimapinit); +#endif + +#if !SANITIZER_ANDROID + 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; + + unsigned IOCTL_FIOASYNC = FIOASYNC; + unsigned IOCTL_FIOCLEX = FIOCLEX; + unsigned IOCTL_FIOGETOWN = FIOGETOWN; + unsigned IOCTL_FIONBIO = FIONBIO; + unsigned IOCTL_FIONCLEX = FIONCLEX; + unsigned IOCTL_FIOSETOWN = FIOSETOWN; + unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI; + unsigned IOCTL_SIOCATMARK = SIOCATMARK; + unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI; + unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR; + unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR; + unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF; + unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR; + unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS; + unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC; + unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU; + unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK; + unsigned IOCTL_SIOCGPGRP = SIOCGPGRP; + unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR; + unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR; + unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR; + unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS; + unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC; + unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU; + unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK; + unsigned IOCTL_SIOCSPGRP = SIOCSPGRP; + unsigned IOCTL_TIOCCONS = TIOCCONS; + unsigned IOCTL_TIOCEXCL = TIOCEXCL; + unsigned IOCTL_TIOCGETD = TIOCGETD; + unsigned IOCTL_TIOCGPGRP = TIOCGPGRP; + unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ; + unsigned IOCTL_TIOCMBIC = TIOCMBIC; + unsigned IOCTL_TIOCMBIS = TIOCMBIS; + unsigned IOCTL_TIOCMGET = TIOCMGET; + unsigned IOCTL_TIOCMSET = TIOCMSET; + unsigned IOCTL_TIOCNOTTY = TIOCNOTTY; + unsigned IOCTL_TIOCNXCL = TIOCNXCL; + unsigned IOCTL_TIOCOUTQ = TIOCOUTQ; + unsigned IOCTL_TIOCPKT = TIOCPKT; + unsigned IOCTL_TIOCSCTTY = TIOCSCTTY; + unsigned IOCTL_TIOCSETD = TIOCSETD; + unsigned IOCTL_TIOCSPGRP = TIOCSPGRP; + unsigned IOCTL_TIOCSTI = TIOCSTI; + unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ; +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_MAC + 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_EVIOCGEFFECTS = EVIOCGEFFECTS; + unsigned IOCTL_EVIOCGID = EVIOCGID; + unsigned IOCTL_EVIOCGKEY = EVIOCGKEY(0); + unsigned IOCTL_EVIOCGKEYCODE = EVIOCGKEYCODE; + unsigned IOCTL_EVIOCGLED = EVIOCGLED(0); + unsigned IOCTL_EVIOCGNAME = EVIOCGNAME(0); + unsigned IOCTL_EVIOCGPHYS = EVIOCGPHYS(0); + unsigned IOCTL_EVIOCGRAB = EVIOCGRAB; + unsigned IOCTL_EVIOCGREP = EVIOCGREP; + unsigned IOCTL_EVIOCGSND = EVIOCGSND(0); + unsigned IOCTL_EVIOCGSW = EVIOCGSW(0); + unsigned IOCTL_EVIOCGUNIQ = EVIOCGUNIQ(0); + unsigned IOCTL_EVIOCGVERSION = EVIOCGVERSION; + unsigned IOCTL_EVIOCRMFF = EVIOCRMFF; + unsigned IOCTL_EVIOCSABS = EVIOCSABS(0); + unsigned IOCTL_EVIOCSFF = EVIOCSFF; + unsigned IOCTL_EVIOCSKEYCODE = EVIOCSKEYCODE; + unsigned IOCTL_EVIOCSREP = EVIOCSREP; + unsigned IOCTL_BLKFLSBUF = BLKFLSBUF; + unsigned IOCTL_BLKGETSIZE = BLKGETSIZE; + unsigned IOCTL_BLKRAGET = BLKRAGET; + unsigned IOCTL_BLKRASET = BLKRASET; + unsigned IOCTL_BLKROGET = BLKROGET; + unsigned IOCTL_BLKROSET = BLKROSET; + unsigned IOCTL_BLKRRPART = BLKRRPART; + unsigned IOCTL_CDROMAUDIOBUFSIZ = CDROMAUDIOBUFSIZ; + unsigned IOCTL_CDROMEJECT = CDROMEJECT; + unsigned IOCTL_CDROMEJECT_SW = CDROMEJECT_SW; + unsigned IOCTL_CDROMMULTISESSION = CDROMMULTISESSION; + unsigned IOCTL_CDROMPAUSE = CDROMPAUSE; + unsigned IOCTL_CDROMPLAYMSF = CDROMPLAYMSF; + unsigned IOCTL_CDROMPLAYTRKIND = CDROMPLAYTRKIND; + unsigned IOCTL_CDROMREADAUDIO = CDROMREADAUDIO; + unsigned IOCTL_CDROMREADCOOKED = CDROMREADCOOKED; + unsigned IOCTL_CDROMREADMODE1 = CDROMREADMODE1; + unsigned IOCTL_CDROMREADMODE2 = CDROMREADMODE2; + unsigned IOCTL_CDROMREADRAW = CDROMREADRAW; + unsigned IOCTL_CDROMREADTOCENTRY = CDROMREADTOCENTRY; + unsigned IOCTL_CDROMREADTOCHDR = CDROMREADTOCHDR; + unsigned IOCTL_CDROMRESET = CDROMRESET; + unsigned IOCTL_CDROMRESUME = CDROMRESUME; + unsigned IOCTL_CDROMSEEK = CDROMSEEK; + unsigned IOCTL_CDROMSTART = CDROMSTART; + unsigned IOCTL_CDROMSTOP = CDROMSTOP; + unsigned IOCTL_CDROMSUBCHNL = CDROMSUBCHNL; + unsigned IOCTL_CDROMVOLCTRL = CDROMVOLCTRL; + unsigned IOCTL_CDROMVOLREAD = CDROMVOLREAD; + unsigned IOCTL_CDROM_GET_UPC = CDROM_GET_UPC; + unsigned IOCTL_FDCLRPRM = FDCLRPRM; + unsigned IOCTL_FDDEFPRM = FDDEFPRM; + unsigned IOCTL_FDFLUSH = FDFLUSH; + unsigned IOCTL_FDFMTBEG = FDFMTBEG; + unsigned IOCTL_FDFMTEND = FDFMTEND; + unsigned IOCTL_FDFMTTRK = FDFMTTRK; + unsigned IOCTL_FDGETDRVPRM = FDGETDRVPRM; + unsigned IOCTL_FDGETDRVSTAT = FDGETDRVSTAT; + unsigned IOCTL_FDGETDRVTYP = FDGETDRVTYP; + unsigned IOCTL_FDGETFDCSTAT = FDGETFDCSTAT; + unsigned IOCTL_FDGETMAXERRS = FDGETMAXERRS; + unsigned IOCTL_FDGETPRM = FDGETPRM; + unsigned IOCTL_FDMSGOFF = FDMSGOFF; + unsigned IOCTL_FDMSGON = FDMSGON; + unsigned IOCTL_FDPOLLDRVSTAT = FDPOLLDRVSTAT; + unsigned IOCTL_FDRAWCMD = FDRAWCMD; + unsigned IOCTL_FDRESET = FDRESET; + unsigned IOCTL_FDSETDRVPRM = FDSETDRVPRM; + unsigned IOCTL_FDSETEMSGTRESH = FDSETEMSGTRESH; + unsigned IOCTL_FDSETMAXERRS = FDSETMAXERRS; + unsigned IOCTL_FDSETPRM = FDSETPRM; + unsigned IOCTL_FDTWADDLE = FDTWADDLE; + unsigned IOCTL_FDWERRORCLR = FDWERRORCLR; + unsigned IOCTL_FDWERRORGET = FDWERRORGET; + unsigned IOCTL_HDIO_DRIVE_CMD = HDIO_DRIVE_CMD; + unsigned IOCTL_HDIO_GETGEO = HDIO_GETGEO; + unsigned IOCTL_HDIO_GET_32BIT = HDIO_GET_32BIT; + unsigned IOCTL_HDIO_GET_DMA = HDIO_GET_DMA; + unsigned IOCTL_HDIO_GET_IDENTITY = HDIO_GET_IDENTITY; + unsigned IOCTL_HDIO_GET_KEEPSETTINGS = HDIO_GET_KEEPSETTINGS; + unsigned IOCTL_HDIO_GET_MULTCOUNT = HDIO_GET_MULTCOUNT; + unsigned IOCTL_HDIO_GET_NOWERR = HDIO_GET_NOWERR; + unsigned IOCTL_HDIO_GET_UNMASKINTR = HDIO_GET_UNMASKINTR; + unsigned IOCTL_HDIO_SET_32BIT = HDIO_SET_32BIT; + unsigned IOCTL_HDIO_SET_DMA = HDIO_SET_DMA; + unsigned IOCTL_HDIO_SET_KEEPSETTINGS = HDIO_SET_KEEPSETTINGS; + 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_PPPIOCGUNIT = PPPIOCGUNIT; + unsigned IOCTL_PPPIOCGXASYNCMAP = PPPIOCGXASYNCMAP; + unsigned IOCTL_PPPIOCSASYNCMAP = PPPIOCSASYNCMAP; + unsigned IOCTL_PPPIOCSDEBUG = PPPIOCSDEBUG; + unsigned IOCTL_PPPIOCSFLAGS = PPPIOCSFLAGS; + unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID; + unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU; + unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP; + unsigned IOCTL_SIOCADDRT = SIOCADDRT; + unsigned IOCTL_SIOCDARP = SIOCDARP; + unsigned IOCTL_SIOCDELRT = SIOCDELRT; + unsigned IOCTL_SIOCDRARP = SIOCDRARP; + unsigned IOCTL_SIOCGARP = SIOCGARP; + unsigned IOCTL_SIOCGIFENCAP = SIOCGIFENCAP; + unsigned IOCTL_SIOCGIFHWADDR = SIOCGIFHWADDR; + unsigned IOCTL_SIOCGIFMAP = SIOCGIFMAP; + unsigned IOCTL_SIOCGIFMEM = SIOCGIFMEM; + unsigned IOCTL_SIOCGIFNAME = SIOCGIFNAME; + unsigned IOCTL_SIOCGIFSLAVE = SIOCGIFSLAVE; + unsigned IOCTL_SIOCGRARP = SIOCGRARP; + unsigned IOCTL_SIOCGSTAMP = SIOCGSTAMP; + unsigned IOCTL_SIOCSARP = SIOCSARP; + unsigned IOCTL_SIOCSIFENCAP = SIOCSIFENCAP; + unsigned IOCTL_SIOCSIFHWADDR = SIOCSIFHWADDR; + unsigned IOCTL_SIOCSIFLINK = SIOCSIFLINK; + unsigned IOCTL_SIOCSIFMAP = SIOCSIFMAP; + unsigned IOCTL_SIOCSIFMEM = SIOCSIFMEM; + unsigned IOCTL_SIOCSIFSLAVE = SIOCSIFSLAVE; + unsigned IOCTL_SIOCSRARP = SIOCSRARP; +#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_SNDCTL_COPR_RCVMSG = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RDATA = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RESET = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_RUN = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_SENDMSG = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_WCODE = IOCTL_NOT_PRESENT; + unsigned IOCTL_SNDCTL_COPR_WDATA = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_BITS = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_CHANNELS = IOCTL_NOT_PRESENT; + unsigned IOCTL_SOUND_PCM_READ_FILTER = 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 + 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_SNDCTL_COPR_RCVMSG = SNDCTL_COPR_RCVMSG; + unsigned IOCTL_SNDCTL_COPR_RDATA = SNDCTL_COPR_RDATA; + unsigned IOCTL_SNDCTL_COPR_RESET = SNDCTL_COPR_RESET; + unsigned IOCTL_SNDCTL_COPR_RUN = SNDCTL_COPR_RUN; + unsigned IOCTL_SNDCTL_COPR_SENDMSG = SNDCTL_COPR_SENDMSG; + unsigned IOCTL_SNDCTL_COPR_WCODE = SNDCTL_COPR_WCODE; + unsigned IOCTL_SNDCTL_COPR_WDATA = SNDCTL_COPR_WDATA; + unsigned IOCTL_SOUND_PCM_READ_BITS = SOUND_PCM_READ_BITS; + unsigned IOCTL_SOUND_PCM_READ_CHANNELS = SOUND_PCM_READ_CHANNELS; + unsigned IOCTL_SOUND_PCM_READ_FILTER = SOUND_PCM_READ_FILTER; + 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 + 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_SNDCTL_DSP_POST = SNDCTL_DSP_POST; + unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET; + unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT; + unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT; + unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED; + unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO; + unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE; + unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC; + unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE; + unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR; + unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO; + unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME; + unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE; + unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT; + unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT; + unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS; + unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS; + unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND; + unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC; + unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE; + unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET; + unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES; + unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC; + unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI; + unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD; + unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO; + unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL; + unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE; + unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME; + unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT; + unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE; + unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START; + unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP; + unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO; + unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE; + unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM; + unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS; + unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS; + unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD; + unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK; + unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE; + unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN; + unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX; + unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE; + unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1; + unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2; + unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3; + unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD; + unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC; + unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE; + unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN; + unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM; + unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV; + unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK; + unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC; + unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER; + unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS; + unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH; + unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE; + unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME; + unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM; + unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS; + unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD; + unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE; + unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN; + unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2; + unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3; + unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD; + unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC; + unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE; + unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN; + unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM; + unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV; + unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC; + unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER; + 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 +#if SANITIZER_LINUX && !SANITIZER_ANDROID + unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH; + unsigned IOCTL_CYGETDEFTIMEOUT = CYGETDEFTIMEOUT; + unsigned IOCTL_CYGETMON = CYGETMON; + unsigned IOCTL_CYGETTHRESH = CYGETTHRESH; + unsigned IOCTL_CYGETTIMEOUT = CYGETTIMEOUT; + unsigned IOCTL_CYSETDEFTHRESH = CYSETDEFTHRESH; + unsigned IOCTL_CYSETDEFTIMEOUT = CYSETDEFTIMEOUT; + unsigned IOCTL_CYSETTHRESH = CYSETTHRESH; + unsigned IOCTL_CYSETTIMEOUT = CYSETTIMEOUT; + unsigned IOCTL_EQL_EMANCIPATE = EQL_EMANCIPATE; + unsigned IOCTL_EQL_ENSLAVE = EQL_ENSLAVE; + unsigned IOCTL_EQL_GETMASTRCFG = EQL_GETMASTRCFG; + unsigned IOCTL_EQL_GETSLAVECFG = EQL_GETSLAVECFG; + unsigned IOCTL_EQL_SETMASTRCFG = EQL_SETMASTRCFG; + unsigned IOCTL_EQL_SETSLAVECFG = EQL_SETSLAVECFG; +#if EV_VERSION > (0x010000) + unsigned IOCTL_EVIOCGKEYCODE_V2 = EVIOCGKEYCODE_V2; + unsigned IOCTL_EVIOCGPROP = EVIOCGPROP(0); + unsigned IOCTL_EVIOCSKEYCODE_V2 = EVIOCSKEYCODE_V2; +#else + unsigned IOCTL_EVIOCGKEYCODE_V2 = IOCTL_NOT_PRESENT; + unsigned IOCTL_EVIOCGPROP = IOCTL_NOT_PRESENT; + unsigned IOCTL_EVIOCSKEYCODE_V2 = IOCTL_NOT_PRESENT; +#endif + unsigned IOCTL_FS_IOC_GETFLAGS = FS_IOC_GETFLAGS; + unsigned IOCTL_FS_IOC_GETVERSION = FS_IOC_GETVERSION; + unsigned IOCTL_FS_IOC_SETFLAGS = FS_IOC_SETFLAGS; + 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_LPCHAR = LPCHAR; + unsigned IOCTL_LPGETIRQ = LPGETIRQ; + unsigned IOCTL_LPGETSTATUS = LPGETSTATUS; + unsigned IOCTL_LPRESET = LPRESET; + unsigned IOCTL_LPSETIRQ = LPSETIRQ; + unsigned IOCTL_LPTIME = LPTIME; + unsigned IOCTL_LPWAIT = LPWAIT; + unsigned IOCTL_MTIOCGETCONFIG = MTIOCGETCONFIG; + 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_SCSI_IOCTL_GET_IDLUN = SCSI_IOCTL_GET_IDLUN; + unsigned IOCTL_SCSI_IOCTL_PROBE_HOST = SCSI_IOCTL_PROBE_HOST; + unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE = SCSI_IOCTL_TAGGED_DISABLE; + unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE = SCSI_IOCTL_TAGGED_ENABLE; + unsigned IOCTL_SIOCAIPXITFCRT = SIOCAIPXITFCRT; + unsigned IOCTL_SIOCAIPXPRISLT = SIOCAIPXPRISLT; + unsigned IOCTL_SIOCAX25ADDUID = SIOCAX25ADDUID; + unsigned IOCTL_SIOCAX25DELUID = SIOCAX25DELUID; + unsigned IOCTL_SIOCAX25GETPARMS = SIOCAX25GETPARMS; + unsigned IOCTL_SIOCAX25GETUID = SIOCAX25GETUID; + unsigned IOCTL_SIOCAX25NOUID = SIOCAX25NOUID; + unsigned IOCTL_SIOCAX25SETPARMS = SIOCAX25SETPARMS; + unsigned IOCTL_SIOCDEVPLIP = SIOCDEVPLIP; + unsigned IOCTL_SIOCIPXCFGDATA = SIOCIPXCFGDATA; + unsigned IOCTL_SIOCNRDECOBS = SIOCNRDECOBS; + 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 } // namespace __sanitizer -#endif // __linux__ || __APPLE__ +#define CHECK_TYPE_SIZE(TYPE) \ + COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE)) + +#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \ + COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *) NULL)->MEMBER) == \ + sizeof(((CLASS *) NULL)->MEMBER)); \ + COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \ + offsetof(CLASS, MEMBER)) + +// For sigaction, which is a function and struct at the same time, +// and thus requires explicit "struct" in sizeof() expression. +#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \ + COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *) NULL)->MEMBER) == \ + sizeof(((struct CLASS *) NULL)->MEMBER)); \ + COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \ + offsetof(struct CLASS, MEMBER)) + +COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t)); + +COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned)); +CHECK_TYPE_SIZE(pthread_key_t); + +#if SANITIZER_LINUX +// 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_addr); +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); + +COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678)); +#endif + +#if SANITIZER_LINUX && !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(glob_t, gl_offs); +CHECK_SIZE_AND_OFFSET(glob_t, gl_flags); +CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir); +CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir); +CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir); +CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat); +CHECK_SIZE_AND_OFFSET(glob_t, gl_stat); +#endif + +CHECK_TYPE_SIZE(addrinfo); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_family); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname); +CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr); + +CHECK_TYPE_SIZE(hostent); +CHECK_SIZE_AND_OFFSET(hostent, h_name); +CHECK_SIZE_AND_OFFSET(hostent, h_aliases); +CHECK_SIZE_AND_OFFSET(hostent, h_addrtype); +CHECK_SIZE_AND_OFFSET(hostent, h_length); +CHECK_SIZE_AND_OFFSET(hostent, h_addr_list); + +CHECK_TYPE_SIZE(iovec); +CHECK_SIZE_AND_OFFSET(iovec, iov_base); +CHECK_SIZE_AND_OFFSET(iovec, iov_len); + +CHECK_TYPE_SIZE(msghdr); +CHECK_SIZE_AND_OFFSET(msghdr, msg_name); +CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen); +CHECK_SIZE_AND_OFFSET(msghdr, msg_iov); +CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen); +CHECK_SIZE_AND_OFFSET(msghdr, msg_control); +CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen); +CHECK_SIZE_AND_OFFSET(msghdr, msg_flags); + +CHECK_TYPE_SIZE(cmsghdr); +CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len); +CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level); +CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type); + +COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent)); +CHECK_SIZE_AND_OFFSET(dirent, d_ino); +#if SANITIZER_MAC +CHECK_SIZE_AND_OFFSET(dirent, d_seekoff); +#else +CHECK_SIZE_AND_OFFSET(dirent, d_off); +#endif +CHECK_SIZE_AND_OFFSET(dirent, d_reclen); + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64)); +CHECK_SIZE_AND_OFFSET(dirent64, d_ino); +CHECK_SIZE_AND_OFFSET(dirent64, d_off); +CHECK_SIZE_AND_OFFSET(dirent64, d_reclen); +#endif + +CHECK_TYPE_SIZE(ifconf); +CHECK_SIZE_AND_OFFSET(ifconf, ifc_len); +CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu); + +CHECK_TYPE_SIZE(pollfd); +CHECK_SIZE_AND_OFFSET(pollfd, fd); +CHECK_SIZE_AND_OFFSET(pollfd, events); +CHECK_SIZE_AND_OFFSET(pollfd, revents); + +CHECK_TYPE_SIZE(nfds_t); + +CHECK_TYPE_SIZE(sigset_t); + +COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction)); +// Can't write checks for sa_handler and sa_sigaction due to them being +// preprocessor macros. +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask); +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags); +#if SANITIZER_LINUX +CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer); +#endif + +#if SANITIZER_LINUX +CHECK_TYPE_SIZE(__sysctl_args); +CHECK_SIZE_AND_OFFSET(__sysctl_args, name); +CHECK_SIZE_AND_OFFSET(__sysctl_args, nlen); +CHECK_SIZE_AND_OFFSET(__sysctl_args, oldval); +CHECK_SIZE_AND_OFFSET(__sysctl_args, oldlenp); +CHECK_SIZE_AND_OFFSET(__sysctl_args, newval); +CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen); + +CHECK_TYPE_SIZE(__kernel_uid_t); +CHECK_TYPE_SIZE(__kernel_gid_t); +CHECK_TYPE_SIZE(__kernel_old_uid_t); +CHECK_TYPE_SIZE(__kernel_old_gid_t); +CHECK_TYPE_SIZE(__kernel_off_t); +CHECK_TYPE_SIZE(__kernel_loff_t); +CHECK_TYPE_SIZE(__kernel_fd_set); +#endif + +#if !SANITIZER_ANDROID +CHECK_TYPE_SIZE(wordexp_t); +CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc); +CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv); +CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs); +#endif + +#endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h index dd53da94be6..007b4ec7b96 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h @@ -13,29 +13,799 @@ #ifndef SANITIZER_PLATFORM_LIMITS_POSIX_H #define SANITIZER_PLATFORM_LIMITS_POSIX_H +#include "sanitizer_platform.h" + namespace __sanitizer { extern unsigned struct_utsname_sz; extern unsigned struct_stat_sz; extern unsigned struct_stat64_sz; extern unsigned struct_rusage_sz; extern unsigned struct_tm_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 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; + +#if !SANITIZER_ANDROID + extern unsigned ucontext_t_sz; +#endif // !SANITIZER_ANDROID + +#if SANITIZER_LINUX + extern unsigned struct___old_kernel_stat_sz; + extern unsigned struct_kernel_stat_sz; + extern unsigned struct_kernel_stat64_sz; + extern unsigned struct_io_event_sz; + extern unsigned struct_iocb_sz; + extern unsigned struct_utimbuf_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_shmid_ds_sz; + extern unsigned struct_mq_attr_sz; + extern unsigned struct_perf_event_attr_sz; + extern unsigned struct_timex_sz; + extern unsigned struct_ustat_sz; -#if defined(__linux__) extern unsigned struct_rlimit_sz; - extern unsigned struct_dirent_sz; extern unsigned struct_statfs_sz; extern unsigned struct_epoll_event_sz; -#endif // __linux__ + extern unsigned struct_sysinfo_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); -#if defined(__linux__) && !defined(__ANDROID__) + struct __sanitizer___sysctl_args { + int *name; + int nlen; + void *oldval; + uptr *oldlenp; + void *newval; + uptr newlen; + unsigned long ___unused[4]; + }; +#endif // SANITIZER_LINUX + +#if SANITIZER_LINUX && !SANITIZER_ANDROID extern unsigned struct_rlimit64_sz; extern unsigned struct_statfs64_sz; -#endif // __linux__ && !__ANDROID__ +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID - void* __sanitizer_get_msghdr_iov_iov_base(void* msg, int idx); - uptr __sanitizer_get_msghdr_iov_iov_len(void* msg, int idx); - uptr __sanitizer_get_msghdr_iovlen(void* msg); - uptr __sanitizer_get_socklen_t(void* socklen_ptr); + struct __sanitizer_iovec { + void *iov_base; + uptr iov_len; + }; + +#if SANITIZER_MAC + typedef unsigned long __sanitizer_pthread_key_t; +#else + typedef unsigned __sanitizer_pthread_key_t; +#endif + +#if SANITIZER_ANDROID || SANITIZER_MAC + struct __sanitizer_msghdr { + void *msg_name; + unsigned msg_namelen; + struct __sanitizer_iovec *msg_iov; + unsigned msg_iovlen; + void *msg_control; + unsigned msg_controllen; + int msg_flags; + }; + struct __sanitizer_cmsghdr { + unsigned cmsg_len; + int cmsg_level; + int cmsg_type; + }; +#else + struct __sanitizer_msghdr { + void *msg_name; + unsigned msg_namelen; + struct __sanitizer_iovec *msg_iov; + uptr msg_iovlen; + void *msg_control; + uptr msg_controllen; + int msg_flags; + }; + struct __sanitizer_cmsghdr { + uptr cmsg_len; + int cmsg_level; + int cmsg_type; + }; +#endif + +#if SANITIZER_MAC + struct __sanitizer_dirent { + unsigned long long d_ino; + unsigned long long d_seekoff; + unsigned short d_reclen; + // more fields that we don't care about + }; +#elif SANITIZER_ANDROID + struct __sanitizer_dirent { + unsigned long long d_ino; + unsigned long long d_off; + unsigned short d_reclen; + // more fields that we don't care about + }; +#else + struct __sanitizer_dirent { + uptr d_ino; + uptr d_off; + unsigned short d_reclen; + // more fields that we don't care about + }; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + struct __sanitizer_dirent64 { + unsigned long long d_ino; + unsigned long long d_off; + unsigned short d_reclen; + // more fields that we don't care about + }; +#endif + +#if SANITIZER_LINUX +#ifdef _LP64 + typedef unsigned __sanitizer___kernel_uid_t; + typedef unsigned __sanitizer___kernel_gid_t; +#else + typedef unsigned short __sanitizer___kernel_uid_t; + typedef unsigned short __sanitizer___kernel_gid_t; +#endif + typedef unsigned short __sanitizer___kernel_old_uid_t; + typedef unsigned short __sanitizer___kernel_old_gid_t; + typedef long __sanitizer___kernel_off_t; + typedef long long __sanitizer___kernel_loff_t; + typedef struct { + unsigned long fds_bits[1024 / (8 * sizeof(long))]; + } __sanitizer___kernel_fd_set; +#endif + + // This thing depends on the platform. We are only interested in the upper + // limit. Verified with a compiler assert in .cc. + const int pthread_attr_t_max_sz = 128; + union __sanitizer_pthread_attr_t { + char size[pthread_attr_t_max_sz]; // NOLINT + void *align; + }; + +#if SANITIZER_ANDROID + typedef unsigned long __sanitizer_sigset_t; +#elif SANITIZER_MAC + typedef unsigned __sanitizer_sigset_t; +#elif SANITIZER_LINUX + struct __sanitizer_sigset_t { + // The size is determined by looking at sizeof of real sigset_t on linux. + uptr val[128 / sizeof(uptr)]; + }; +#endif + + struct __sanitizer_sigaction { + union { + void (*sa_handler)(int sig); + void (*sa_sigaction)(int sig, void *siginfo, void *uctx); + }; + __sanitizer_sigset_t sa_mask; + int sa_flags; +#if SANITIZER_LINUX + void (*sa_restorer)(); +#endif + }; + + extern uptr sig_ign; + extern uptr sig_dfl; + extern uptr sa_siginfo; + +#if SANITIZER_LINUX + extern int e_tabsz; +#endif + + extern int af_inet; + extern int af_inet6; + uptr __sanitizer_in_addr_sz(int af); + +#if SANITIZER_LINUX + struct __sanitizer_dl_phdr_info { + uptr dlpi_addr; + const char *dlpi_name; + const void *dlpi_phdr; + short dlpi_phnum; + }; +#endif + + struct __sanitizer_addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; +#if SANITIZER_ANDROID || SANITIZER_MAC + unsigned ai_addrlen; + char *ai_canonname; + void *ai_addr; +#else // LINUX + unsigned ai_addrlen; + void *ai_addr; + char *ai_canonname; +#endif + struct __sanitizer_addrinfo *ai_next; + }; + + struct __sanitizer_hostent { + char *h_name; + char **h_aliases; + int h_addrtype; + int h_length; + char **h_addr_list; + }; + + struct __sanitizer_pollfd { + int fd; + short events; + short revents; + }; + +#if SANITIZER_ANDROID || SANITIZER_MAC + typedef unsigned __sanitizer_nfds_t; +#else + typedef unsigned long __sanitizer_nfds_t; +#endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + struct __sanitizer_glob_t { + uptr gl_pathc; + char **gl_pathv; + uptr gl_offs; + int gl_flags; + + void (*gl_closedir)(void *dirp); + void *(*gl_readdir)(void *dirp); + void *(*gl_opendir)(const char *); + int (*gl_lstat)(const char *, void *); + int (*gl_stat)(const char *, void *); + }; + + extern int glob_nomatch; + extern int glob_altdirfunc; +#endif + + extern unsigned path_max; + + struct __sanitizer_wordexp_t { + uptr we_wordc; + char **we_wordv; + uptr we_offs; + }; + +#if SANITIZER_LINUX && !SANITIZER_ANDROID && \ + (defined(__i386) || defined (__x86_64)) // NOLINT + extern unsigned struct_user_regs_struct_sz; + extern unsigned struct_user_fpregs_struct_sz; + extern unsigned struct_user_fpxregs_struct_sz; + + extern int ptrace_getregs; + extern int ptrace_setregs; + extern int ptrace_getfpregs; + extern int ptrace_setfpregs; + extern int ptrace_getfpxregs; + extern int ptrace_setfpxregs; + extern int ptrace_getsiginfo; + extern int ptrace_setsiginfo; + extern int ptrace_getregset; + extern int ptrace_setregset; +#endif + + // ioctl arguments + struct __sanitizer_ifconf { + int ifc_len; + union { + void *ifcu_req; + } ifc_ifcu; +#if SANITIZER_MAC + } __attribute__((packed)); +#else + }; +#endif + +#define IOC_SIZE(nr) (((nr) >> 16) & 0x3fff) + + 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_cdrom_msf_sz; + extern unsigned struct_cdrom_multisession_sz; + extern unsigned struct_cdrom_read_audio_sz; + extern unsigned struct_cdrom_subchnl_sz; + extern unsigned struct_cdrom_ti_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_floppy_fdc_state_sz; + extern unsigned struct_floppy_max_errors_sz; + extern unsigned struct_floppy_raw_cmd_sz; + extern unsigned struct_floppy_struct_sz; + extern unsigned struct_floppy_write_errors_sz; + extern unsigned struct_format_descr_sz; + extern unsigned struct_hd_driveid_sz; + extern unsigned struct_hd_geometry_sz; + extern unsigned struct_input_absinfo_sz; + extern unsigned struct_input_id_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 + +#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_ipx_config_data_sz; + extern unsigned struct_kbdiacrs_sz; + extern unsigned struct_kbentry_sz; + extern unsigned struct_kbkeycode_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_serial_struct_sz; + extern unsigned struct_sockaddr_ax25_sz; + extern unsigned struct_unimapdesc_sz; + extern unsigned struct_unimapinit_sz; +#endif + +#if !SANITIZER_ANDROID + extern unsigned struct_sioc_sg_req_sz; + extern unsigned struct_sioc_vif_req_sz; +#endif + + // ioctl request identifiers + + // 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 unsigned IOCTL_FIOASYNC; + extern unsigned IOCTL_FIOCLEX; + extern unsigned IOCTL_FIOGETOWN; + extern unsigned IOCTL_FIONBIO; + extern unsigned IOCTL_FIONCLEX; + extern unsigned IOCTL_FIOSETOWN; + extern unsigned IOCTL_SIOCADDMULTI; + extern unsigned IOCTL_SIOCATMARK; + extern unsigned IOCTL_SIOCDELMULTI; + extern unsigned IOCTL_SIOCGIFADDR; + extern unsigned IOCTL_SIOCGIFBRDADDR; + extern unsigned IOCTL_SIOCGIFCONF; + extern unsigned IOCTL_SIOCGIFDSTADDR; + extern unsigned IOCTL_SIOCGIFFLAGS; + extern unsigned IOCTL_SIOCGIFMETRIC; + extern unsigned IOCTL_SIOCGIFMTU; + extern unsigned IOCTL_SIOCGIFNETMASK; + extern unsigned IOCTL_SIOCGPGRP; + extern unsigned IOCTL_SIOCSIFADDR; + extern unsigned IOCTL_SIOCSIFBRDADDR; + extern unsigned IOCTL_SIOCSIFDSTADDR; + extern unsigned IOCTL_SIOCSIFFLAGS; + extern unsigned IOCTL_SIOCSIFMETRIC; + extern unsigned IOCTL_SIOCSIFMTU; + extern unsigned IOCTL_SIOCSIFNETMASK; + extern unsigned IOCTL_SIOCSPGRP; + extern unsigned IOCTL_TIOCCONS; + extern unsigned IOCTL_TIOCEXCL; + extern unsigned IOCTL_TIOCGETD; + extern unsigned IOCTL_TIOCGPGRP; + extern unsigned IOCTL_TIOCGWINSZ; + extern unsigned IOCTL_TIOCMBIC; + extern unsigned IOCTL_TIOCMBIS; + extern unsigned IOCTL_TIOCMGET; + extern unsigned IOCTL_TIOCMSET; + extern unsigned IOCTL_TIOCNOTTY; + extern unsigned IOCTL_TIOCNXCL; + extern unsigned IOCTL_TIOCOUTQ; + extern unsigned IOCTL_TIOCPKT; + extern unsigned IOCTL_TIOCSCTTY; + extern unsigned IOCTL_TIOCSETD; + extern unsigned IOCTL_TIOCSPGRP; + extern unsigned IOCTL_TIOCSTI; + extern unsigned IOCTL_TIOCSWINSZ; +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_MAC + extern unsigned IOCTL_SIOCGETSGCNT; + extern unsigned IOCTL_SIOCGETVIFCNT; +#endif +#if SANITIZER_LINUX + extern unsigned IOCTL_EVIOCGABS; + extern unsigned IOCTL_EVIOCGBIT; + extern unsigned IOCTL_EVIOCGEFFECTS; + extern unsigned IOCTL_EVIOCGID; + extern unsigned IOCTL_EVIOCGKEY; + extern unsigned IOCTL_EVIOCGKEYCODE; + extern unsigned IOCTL_EVIOCGLED; + extern unsigned IOCTL_EVIOCGNAME; + extern unsigned IOCTL_EVIOCGPHYS; + extern unsigned IOCTL_EVIOCGRAB; + extern unsigned IOCTL_EVIOCGREP; + extern unsigned IOCTL_EVIOCGSND; + extern unsigned IOCTL_EVIOCGSW; + extern unsigned IOCTL_EVIOCGUNIQ; + extern unsigned IOCTL_EVIOCGVERSION; + extern unsigned IOCTL_EVIOCRMFF; + extern unsigned IOCTL_EVIOCSABS; + extern unsigned IOCTL_EVIOCSFF; + extern unsigned IOCTL_EVIOCSKEYCODE; + extern unsigned IOCTL_EVIOCSREP; + extern unsigned IOCTL_BLKFLSBUF; + extern unsigned IOCTL_BLKGETSIZE; + extern unsigned IOCTL_BLKRAGET; + extern unsigned IOCTL_BLKRASET; + extern unsigned IOCTL_BLKROGET; + extern unsigned IOCTL_BLKROSET; + extern unsigned IOCTL_BLKRRPART; + extern unsigned IOCTL_CDROMAUDIOBUFSIZ; + extern unsigned IOCTL_CDROMEJECT; + extern unsigned IOCTL_CDROMEJECT_SW; + extern unsigned IOCTL_CDROMMULTISESSION; + extern unsigned IOCTL_CDROMPAUSE; + extern unsigned IOCTL_CDROMPLAYMSF; + extern unsigned IOCTL_CDROMPLAYTRKIND; + extern unsigned IOCTL_CDROMREADAUDIO; + extern unsigned IOCTL_CDROMREADCOOKED; + extern unsigned IOCTL_CDROMREADMODE1; + extern unsigned IOCTL_CDROMREADMODE2; + extern unsigned IOCTL_CDROMREADRAW; + extern unsigned IOCTL_CDROMREADTOCENTRY; + extern unsigned IOCTL_CDROMREADTOCHDR; + extern unsigned IOCTL_CDROMRESET; + extern unsigned IOCTL_CDROMRESUME; + extern unsigned IOCTL_CDROMSEEK; + extern unsigned IOCTL_CDROMSTART; + extern unsigned IOCTL_CDROMSTOP; + extern unsigned IOCTL_CDROMSUBCHNL; + extern unsigned IOCTL_CDROMVOLCTRL; + extern unsigned IOCTL_CDROMVOLREAD; + extern unsigned IOCTL_CDROM_GET_UPC; + extern unsigned IOCTL_FDCLRPRM; + extern unsigned IOCTL_FDDEFPRM; + extern unsigned IOCTL_FDFLUSH; + extern unsigned IOCTL_FDFMTBEG; + extern unsigned IOCTL_FDFMTEND; + extern unsigned IOCTL_FDFMTTRK; + extern unsigned IOCTL_FDGETDRVPRM; + extern unsigned IOCTL_FDGETDRVSTAT; + extern unsigned IOCTL_FDGETDRVTYP; + extern unsigned IOCTL_FDGETFDCSTAT; + extern unsigned IOCTL_FDGETMAXERRS; + extern unsigned IOCTL_FDGETPRM; + extern unsigned IOCTL_FDMSGOFF; + extern unsigned IOCTL_FDMSGON; + extern unsigned IOCTL_FDPOLLDRVSTAT; + extern unsigned IOCTL_FDRAWCMD; + extern unsigned IOCTL_FDRESET; + extern unsigned IOCTL_FDSETDRVPRM; + extern unsigned IOCTL_FDSETEMSGTRESH; + extern unsigned IOCTL_FDSETMAXERRS; + extern unsigned IOCTL_FDSETPRM; + extern unsigned IOCTL_FDTWADDLE; + extern unsigned IOCTL_FDWERRORCLR; + extern unsigned IOCTL_FDWERRORGET; + extern unsigned IOCTL_HDIO_DRIVE_CMD; + extern unsigned IOCTL_HDIO_GETGEO; + extern unsigned IOCTL_HDIO_GET_32BIT; + extern unsigned IOCTL_HDIO_GET_DMA; + extern unsigned IOCTL_HDIO_GET_IDENTITY; + extern unsigned IOCTL_HDIO_GET_KEEPSETTINGS; + extern unsigned IOCTL_HDIO_GET_MULTCOUNT; + extern unsigned IOCTL_HDIO_GET_NOWERR; + extern unsigned IOCTL_HDIO_GET_UNMASKINTR; + extern unsigned IOCTL_HDIO_SET_32BIT; + extern unsigned IOCTL_HDIO_SET_DMA; + extern unsigned IOCTL_HDIO_SET_KEEPSETTINGS; + 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_PPPIOCGUNIT; + extern unsigned IOCTL_PPPIOCGXASYNCMAP; + extern unsigned IOCTL_PPPIOCSASYNCMAP; + extern unsigned IOCTL_PPPIOCSDEBUG; + extern unsigned IOCTL_PPPIOCSFLAGS; + 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_SIOCGIFHWADDR; + extern unsigned IOCTL_SIOCGIFMAP; + extern unsigned IOCTL_SIOCGIFMEM; + extern unsigned IOCTL_SIOCGIFNAME; + extern unsigned IOCTL_SIOCGIFSLAVE; + extern unsigned IOCTL_SIOCGRARP; + extern unsigned IOCTL_SIOCGSTAMP; + extern unsigned IOCTL_SIOCSARP; + extern unsigned IOCTL_SIOCSIFENCAP; + extern unsigned IOCTL_SIOCSIFHWADDR; + extern unsigned IOCTL_SIOCSIFLINK; + extern unsigned IOCTL_SIOCSIFMAP; + extern unsigned IOCTL_SIOCSIFMEM; + extern unsigned IOCTL_SIOCSIFSLAVE; + extern unsigned IOCTL_SIOCSRARP; + extern unsigned IOCTL_SNDCTL_COPR_HALT; + extern unsigned IOCTL_SNDCTL_COPR_LOAD; + extern unsigned IOCTL_SNDCTL_COPR_RCODE; + extern unsigned IOCTL_SNDCTL_COPR_RCVMSG; + extern unsigned IOCTL_SNDCTL_COPR_RDATA; + extern unsigned IOCTL_SNDCTL_COPR_RESET; + extern unsigned IOCTL_SNDCTL_COPR_RUN; + extern unsigned IOCTL_SNDCTL_COPR_SENDMSG; + extern unsigned IOCTL_SNDCTL_COPR_WCODE; + extern unsigned IOCTL_SNDCTL_COPR_WDATA; + extern unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE; + extern unsigned IOCTL_SNDCTL_DSP_GETFMTS; + extern unsigned IOCTL_SNDCTL_DSP_NONBLOCK; + extern unsigned IOCTL_SNDCTL_DSP_POST; + extern unsigned IOCTL_SNDCTL_DSP_RESET; + extern unsigned IOCTL_SNDCTL_DSP_SETFMT; + extern unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT; + extern unsigned IOCTL_SNDCTL_DSP_SPEED; + extern unsigned IOCTL_SNDCTL_DSP_STEREO; + extern unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE; + extern unsigned IOCTL_SNDCTL_DSP_SYNC; + extern unsigned IOCTL_SNDCTL_FM_4OP_ENABLE; + extern unsigned IOCTL_SNDCTL_FM_LOAD_INSTR; + extern unsigned IOCTL_SNDCTL_MIDI_INFO; + extern unsigned IOCTL_SNDCTL_MIDI_PRETIME; + extern unsigned IOCTL_SNDCTL_SEQ_CTRLRATE; + extern unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT; + extern unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT; + extern unsigned IOCTL_SNDCTL_SEQ_NRMIDIS; + extern unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS; + extern unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND; + extern unsigned IOCTL_SNDCTL_SEQ_PANIC; + extern unsigned IOCTL_SNDCTL_SEQ_PERCMODE; + extern unsigned IOCTL_SNDCTL_SEQ_RESET; + extern unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES; + extern unsigned IOCTL_SNDCTL_SEQ_SYNC; + extern unsigned IOCTL_SNDCTL_SEQ_TESTMIDI; + extern unsigned IOCTL_SNDCTL_SEQ_THRESHOLD; + extern unsigned IOCTL_SNDCTL_SYNTH_INFO; + extern unsigned IOCTL_SNDCTL_SYNTH_MEMAVL; + extern unsigned IOCTL_SNDCTL_TMR_CONTINUE; + extern unsigned IOCTL_SNDCTL_TMR_METRONOME; + extern unsigned IOCTL_SNDCTL_TMR_SELECT; + extern unsigned IOCTL_SNDCTL_TMR_SOURCE; + extern unsigned IOCTL_SNDCTL_TMR_START; + extern unsigned IOCTL_SNDCTL_TMR_STOP; + extern unsigned IOCTL_SNDCTL_TMR_TEMPO; + extern unsigned IOCTL_SNDCTL_TMR_TIMEBASE; + extern unsigned IOCTL_SOUND_MIXER_READ_ALTPCM; + extern unsigned IOCTL_SOUND_MIXER_READ_BASS; + extern unsigned IOCTL_SOUND_MIXER_READ_CAPS; + extern unsigned IOCTL_SOUND_MIXER_READ_CD; + extern unsigned IOCTL_SOUND_MIXER_READ_DEVMASK; + extern unsigned IOCTL_SOUND_MIXER_READ_ENHANCE; + extern unsigned IOCTL_SOUND_MIXER_READ_IGAIN; + extern unsigned IOCTL_SOUND_MIXER_READ_IMIX; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE1; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE2; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE3; + extern unsigned IOCTL_SOUND_MIXER_READ_LINE; + extern unsigned IOCTL_SOUND_MIXER_READ_LOUD; + extern unsigned IOCTL_SOUND_MIXER_READ_MIC; + extern unsigned IOCTL_SOUND_MIXER_READ_MUTE; + extern unsigned IOCTL_SOUND_MIXER_READ_OGAIN; + extern unsigned IOCTL_SOUND_MIXER_READ_PCM; + extern unsigned IOCTL_SOUND_MIXER_READ_RECLEV; + extern unsigned IOCTL_SOUND_MIXER_READ_RECMASK; + extern unsigned IOCTL_SOUND_MIXER_READ_RECSRC; + extern unsigned IOCTL_SOUND_MIXER_READ_SPEAKER; + extern unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS; + extern unsigned IOCTL_SOUND_MIXER_READ_SYNTH; + extern unsigned IOCTL_SOUND_MIXER_READ_TREBLE; + extern unsigned IOCTL_SOUND_MIXER_READ_VOLUME; + extern unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM; + extern unsigned IOCTL_SOUND_MIXER_WRITE_BASS; + extern unsigned IOCTL_SOUND_MIXER_WRITE_CD; + extern unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN; + extern unsigned IOCTL_SOUND_MIXER_WRITE_IMIX; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE1; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE2; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE3; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_LOUD; + extern unsigned IOCTL_SOUND_MIXER_WRITE_MIC; + extern unsigned IOCTL_SOUND_MIXER_WRITE_MUTE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN; + extern unsigned IOCTL_SOUND_MIXER_WRITE_PCM; + extern unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV; + extern unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC; + extern unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER; + extern unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH; + extern unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE; + extern unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME; + extern unsigned IOCTL_SOUND_PCM_READ_BITS; + extern unsigned IOCTL_SOUND_PCM_READ_CHANNELS; + extern unsigned IOCTL_SOUND_PCM_READ_FILTER; + 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 +#if SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned IOCTL_CYGETDEFTHRESH; + extern unsigned IOCTL_CYGETDEFTIMEOUT; + extern unsigned IOCTL_CYGETMON; + extern unsigned IOCTL_CYGETTHRESH; + extern unsigned IOCTL_CYGETTIMEOUT; + extern unsigned IOCTL_CYSETDEFTHRESH; + extern unsigned IOCTL_CYSETDEFTIMEOUT; + extern unsigned IOCTL_CYSETTHRESH; + extern unsigned IOCTL_CYSETTIMEOUT; + extern unsigned IOCTL_EQL_EMANCIPATE; + extern unsigned IOCTL_EQL_ENSLAVE; + extern unsigned IOCTL_EQL_GETMASTRCFG; + extern unsigned IOCTL_EQL_GETSLAVECFG; + extern unsigned IOCTL_EQL_SETMASTRCFG; + extern unsigned IOCTL_EQL_SETSLAVECFG; + extern unsigned IOCTL_EVIOCGKEYCODE_V2; + extern unsigned IOCTL_EVIOCGPROP; + extern unsigned IOCTL_EVIOCSKEYCODE_V2; + extern unsigned IOCTL_FS_IOC_GETFLAGS; + extern unsigned IOCTL_FS_IOC_GETVERSION; + extern unsigned IOCTL_FS_IOC_SETFLAGS; + 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_LPCHAR; + extern unsigned IOCTL_LPGETIRQ; + extern unsigned IOCTL_LPGETSTATUS; + extern unsigned IOCTL_LPRESET; + extern unsigned IOCTL_LPSETIRQ; + extern unsigned IOCTL_LPTIME; + extern unsigned IOCTL_LPWAIT; + extern unsigned IOCTL_MTIOCGETCONFIG; + 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_SCSI_IOCTL_GET_IDLUN; + extern unsigned IOCTL_SCSI_IOCTL_PROBE_HOST; + extern unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE; + extern unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE; + extern unsigned IOCTL_SIOCAIPXITFCRT; + extern unsigned IOCTL_SIOCAIPXPRISLT; + extern unsigned IOCTL_SIOCAX25ADDUID; + extern unsigned IOCTL_SIOCAX25DELUID; + extern unsigned IOCTL_SIOCAX25GETPARMS; + extern unsigned IOCTL_SIOCAX25GETUID; + extern unsigned IOCTL_SIOCAX25NOUID; + extern unsigned IOCTL_SIOCAX25SETPARMS; + extern unsigned IOCTL_SIOCDEVPLIP; + extern unsigned IOCTL_SIOCIPXCFGDATA; + extern unsigned IOCTL_SIOCNRDECOBS; + extern unsigned IOCTL_SIOCNRGETPARMS; + extern unsigned IOCTL_SIOCNRRTCTL; + extern unsigned IOCTL_SIOCNRSETPARMS; + extern unsigned IOCTL_SNDCTL_DSP_GETISPACE; + extern unsigned IOCTL_SNDCTL_DSP_GETOSPACE; + extern unsigned IOCTL_TIOCGSERIAL; + extern unsigned IOCTL_TIOCSERGETMULTI; + extern unsigned IOCTL_TIOCSERSETMULTI; + extern unsigned IOCTL_TIOCSSERIAL; +#endif } // namespace __sanitizer #endif diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.cc b/libsanitizer/sanitizer_common/sanitizer_posix.cc index 1c6ff0a2ebb..6d999e91961 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix.cc +++ b/libsanitizer/sanitizer_common/sanitizer_posix.cc @@ -9,53 +9,49 @@ // run-time libraries and implements POSIX-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// -#if defined(__linux__) || defined(__APPLE__) + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX || SANITIZER_MAC #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include namespace __sanitizer { // ------------- sanitizer_common.h -uptr GetPageSize() { - return sysconf(_SC_PAGESIZE); -} - uptr GetMmapGranularity() { return GetPageSize(); } -int GetPid() { - return getpid(); -} - -u32 GetUid() { - return getuid(); -} - -uptr GetThreadSelf() { - return (uptr)pthread_self(); +uptr GetMaxVirtualAddress() { +#if SANITIZER_WORDSIZE == 64 +# if defined(__powerpc64__) + // 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 + // 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 +# else + return (1ULL << 47) - 1; // 0x00007fffffffffffUL; +# endif +#else // SANITIZER_WORDSIZE == 32 + // FIXME: We can probably lower this on Android? + return (1ULL << 32) - 1; // 0xffffffff; +#endif // SANITIZER_WORDSIZE } void *MmapOrDie(uptr size, const char *mem_type) { size = RoundUpTo(size, GetPageSizeCached()); - void *res = internal_mmap(0, size, + uptr res = internal_mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); - if (res == (void*)-1) { + 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. @@ -64,18 +60,18 @@ void *MmapOrDie(uptr size, const char *mem_type) { Die(); } recursion_count++; - Report("ERROR: %s failed to allocate 0x%zx (%zd) bytes of %s: %s\n", - SanitizerToolName, size, size, mem_type, strerror(errno)); + 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); } - return res; + return (void *)res; } void UnmapOrDie(void *addr, uptr size) { if (!addr || !size) return; - int res = internal_munmap(addr, size); - if (res != 0) { + uptr res = internal_munmap(addr, size); + if (internal_iserror(res)) { Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n", SanitizerToolName, size, size, addr); CHECK("unable to unmap" && 0); @@ -84,54 +80,53 @@ void UnmapOrDie(void *addr, uptr size) { void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { uptr PageSize = GetPageSizeCached(); - void *p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), + 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); - if (p == (void*)-1) + 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, errno); - return p; + SanitizerToolName, size, size, fixed_addr, reserrno); + return (void *)p; } void *MmapFixedOrDie(uptr fixed_addr, uptr size) { uptr PageSize = GetPageSizeCached(); - void *p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), + uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); - if (p == (void*)-1) { + 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, errno); + SanitizerToolName, size, size, fixed_addr, reserrno); CHECK("unable to mmap" && 0); } - return p; + return (void *)p; } void *Mprotect(uptr fixed_addr, uptr size) { - return internal_mmap((void*)fixed_addr, size, - PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, - -1, 0); -} - -void FlushUnneededShadowMemory(uptr addr, uptr size) { - madvise((void*)addr, size, MADV_DONTNEED); + return (void *)internal_mmap((void*)fixed_addr, size, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | + MAP_NORESERVE, -1, 0); } void *MapFileToMemory(const char *file_name, uptr *buff_size) { - fd_t fd = OpenFile(file_name, false); - CHECK_NE(fd, kInvalidFd); + uptr openrv = OpenFile(file_name, false); + CHECK(!internal_iserror(openrv)); + fd_t fd = openrv; uptr fsize = internal_filesize(fd); CHECK_NE(fsize, (uptr)-1); CHECK_GT(fsize, 0); *buff_size = RoundUpTo(fsize, GetPageSizeCached()); - void *map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0); - return (map == MAP_FAILED) ? 0 : map; + uptr map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0); + return internal_iserror(map) ? 0 : (void *)map; } @@ -147,10 +142,11 @@ static inline bool IntervalsAreSeparate(uptr start1, uptr end1, // several worker threads on Mac, which aren't expected to map big chunks of // memory). bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { - MemoryMappingLayout procmaps; + MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end; - while (procmaps.Next(&start, &end, - /*offset*/0, /*filename*/0, /*filename_size*/0)) { + while (proc_maps.Next(&start, &end, + /*offset*/0, /*filename*/0, /*filename_size*/0, + /*protection*/0)) { if (!IntervalsAreSeparate(start, end, range_start, range_end)) return false; } @@ -158,13 +154,13 @@ bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { } void DumpProcessMap() { - MemoryMappingLayout proc_maps; + MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end; const sptr kBufSize = 4095; char *filename = (char*)MmapOrDie(kBufSize, __FUNCTION__); Report("Process memory map follows:\n"); while (proc_maps.Next(&start, &end, /* file_offset */0, - filename, kBufSize)) { + filename, kBufSize, /* protection */0)) { Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename); } Report("End of process memory map.\n"); @@ -175,54 +171,77 @@ const char *GetPwd() { return GetEnv("PWD"); } -void DisableCoreDumper() { - struct rlimit nocore; - nocore.rlim_cur = 0; - nocore.rlim_max = 0; - setrlimit(RLIMIT_CORE, &nocore); +char *FindPathToBinary(const char *name) { + const char *path = GetEnv("PATH"); + if (!path) + return 0; + uptr name_len = internal_strlen(name); + InternalScopedBuffer 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 StackSizeIsUnlimited() { - struct rlimit rlim; - CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim)); - return (rlim.rlim_cur == (uptr)-1); -} - -void SetStackSizeLimitInBytes(uptr limit) { - struct rlimit rlim; - rlim.rlim_cur = limit; - rlim.rlim_max = limit; - if (setrlimit(RLIMIT_STACK, &rlim)) { - Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno); +void MaybeOpenReportFile() { + if (!log_to_file || (report_fd_pid == internal_getpid())) return; + InternalScopedBuffer report_path_full(4096); + internal_snprintf(report_path_full.data(), report_path_full.size(), + "%s.%d", report_path_prefix, internal_getpid()); + 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(); } - CHECK(!StackSizeIsUnlimited()); + if (report_fd != kInvalidFd) { + // We're in the child. Close the parent's log. + internal_close(report_fd); + } + report_fd = openrv; + report_fd_pid = internal_getpid(); } -void SleepForSeconds(int seconds) { - sleep(seconds); +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)); + Die(); + } } -void SleepForMillis(int millis) { - usleep(millis * 1000); -} - -void Abort() { - abort(); -} - -int Atexit(void (*function)(void)) { -#ifndef SANITIZER_GO - return atexit(function); -#else - return 0; -#endif -} - -int internal_isatty(fd_t fd) { - return isatty(fd); +bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) { + uptr s, e, off, prot; + InternalMmapVector fn(4096); + fn.push_back(0); + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + while (proc_maps.Next(&s, &e, &off, &fn[0], fn.capacity(), &prot)) { + if ((prot & MemoryMappingLayout::kProtectionExecute) != 0 + && internal_strcmp(module, &fn[0]) == 0) { + *start = s; + *end = e; + return true; + } + } + return false; } } // namespace __sanitizer -#endif // __linux__ || __APPLE_ +#endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc new file mode 100644 index 00000000000..3d4f92db581 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc @@ -0,0 +1,114 @@ +//===-- sanitizer_posix_libcdep.cc ----------------------------------------===// +// +// 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 implements libc-dependent POSIX-specific functions +// from sanitizer_libc.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" + +#if SANITIZER_LINUX || SANITIZER_MAC +#include "sanitizer_common.h" +#include "sanitizer_stacktrace.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace __sanitizer { + +u32 GetUid() { + return getuid(); +} + +uptr GetThreadSelf() { + return (uptr)pthread_self(); +} + +void FlushUnneededShadowMemory(uptr addr, uptr size) { + madvise((void*)addr, size, MADV_DONTNEED); +} + +void DisableCoreDumper() { + struct rlimit nocore; + nocore.rlim_cur = 0; + nocore.rlim_max = 0; + setrlimit(RLIMIT_CORE, &nocore); +} + +bool StackSizeIsUnlimited() { + struct rlimit rlim; + CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim)); + return (rlim.rlim_cur == (uptr)-1); +} + +void SetStackSizeLimitInBytes(uptr limit) { + struct rlimit rlim; + rlim.rlim_cur = limit; + rlim.rlim_max = limit; + if (setrlimit(RLIMIT_STACK, &rlim)) { + Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno); + Die(); + } + CHECK(!StackSizeIsUnlimited()); +} + +void SleepForSeconds(int seconds) { + sleep(seconds); +} + +void SleepForMillis(int millis) { + usleep(millis * 1000); +} + +void Abort() { + abort(); +} + +int Atexit(void (*function)(void)) { +#ifndef SANITIZER_GO + return atexit(function); +#else + return 0; +#endif +} + +int internal_isatty(fd_t fd) { + return isatty(fd); +} + +#ifndef SANITIZER_GO +void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, + uptr stack_top, uptr stack_bottom, bool fast) { +#if !SANITIZER_CAN_FAST_UNWIND + fast = false; +#endif +#if SANITIZER_MAC + // Always unwind fast on Mac. + (void)fast; +#else + if (!fast) + return stack->SlowUnwindStack(pc, max_s); +#endif // SANITIZER_MAC + stack->size = 0; + stack->trace[0] = pc; + if (max_s > 1) { + stack->max_size = max_s; + stack->FastUnwindStack(pc, bp, stack_top, stack_bottom); + } +} +#endif // SANITIZER_GO + +} // namespace __sanitizer + +#endif diff --git a/libsanitizer/sanitizer_common/sanitizer_printf.cc b/libsanitizer/sanitizer_common/sanitizer_printf.cc index 8298f12bd7b..d7ce9736b24 100644 --- a/libsanitizer/sanitizer_common/sanitizer_printf.cc +++ b/libsanitizer/sanitizer_common/sanitizer_printf.cc @@ -19,6 +19,10 @@ #include #include +#if SANITIZER_WINDOWS && !defined(va_copy) +# define va_copy(dst, src) ((dst) = (src)) +#endif + namespace __sanitizer { StaticSpinMutex CommonSanitizerReportMutex; @@ -32,45 +36,60 @@ static int AppendChar(char **buff, const char *buff_end, char c) { } // Appends number in a given base to buffer. If its length is less than -// "minimal_num_length", it is padded with leading zeroes. -static int AppendUnsigned(char **buff, const char *buff_end, u64 num, - u8 base, u8 minimal_num_length) { +// |minimal_num_length|, it is padded with leading zeroes or spaces, depending +// on the value of |pad_with_zero|. +static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value, + u8 base, u8 minimal_num_length, bool pad_with_zero, + bool negative) { uptr const kMaxLen = 30; RAW_CHECK(base == 10 || base == 16); + RAW_CHECK(base == 10 || !negative); + RAW_CHECK(absolute_value || !negative); RAW_CHECK(minimal_num_length < kMaxLen); + int result = 0; + if (negative && minimal_num_length) + --minimal_num_length; + if (negative && pad_with_zero) + result += AppendChar(buff, buff_end, '-'); uptr num_buffer[kMaxLen]; - uptr pos = 0; + int pos = 0; do { - RAW_CHECK_MSG(pos < kMaxLen, "appendNumber buffer overflow"); - num_buffer[pos++] = num % base; - num /= base; - } while (num > 0); + RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow"); + num_buffer[pos++] = absolute_value % base; + absolute_value /= base; + } while (absolute_value > 0); if (pos < minimal_num_length) { // Make sure compiler doesn't insert call to memset here. internal_memset(&num_buffer[pos], 0, sizeof(num_buffer[0]) * (minimal_num_length - pos)); pos = minimal_num_length; } - int result = 0; - while (pos-- > 0) { - uptr digit = num_buffer[pos]; + RAW_CHECK(pos > 0); + pos--; + for (; pos >= 0 && num_buffer[pos] == 0; pos--) { + char c = (pad_with_zero || pos == 0) ? '0' : ' '; + result += AppendChar(buff, buff_end, c); + } + if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-'); + for (; pos >= 0; pos--) { + char digit = static_cast(num_buffer[pos]); result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit : 'a' + digit - 10); } return result; } +static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base, + u8 minimal_num_length, bool pad_with_zero) { + return AppendNumber(buff, buff_end, num, base, minimal_num_length, + pad_with_zero, false /* negative */); +} + static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num, - u8 minimal_num_length) { - int result = 0; - if (num < 0) { - result += AppendChar(buff, buff_end, '-'); - num = -num; - if (minimal_num_length) - --minimal_num_length; - } - result += AppendUnsigned(buff, buff_end, (u64)num, 10, minimal_num_length); - return result; + u8 minimal_num_length, bool pad_with_zero) { + bool negative = (num < 0); + return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10, + minimal_num_length, pad_with_zero, negative); } static int AppendString(char **buff, const char *buff_end, const char *s) { @@ -87,14 +106,14 @@ static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) { int result = 0; result += AppendString(buff, buff_end, "0x"); result += AppendUnsigned(buff, buff_end, ptr_value, 16, - (SANITIZER_WORDSIZE == 64) ? 12 : 8); + (SANITIZER_WORDSIZE == 64) ? 12 : 8, true); return result; } int VSNPrintf(char *buff, int buff_length, const char *format, va_list args) { static const char *kPrintfFormatsHelp = - "Supported Printf formats: %(0[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]; @@ -106,11 +125,11 @@ int VSNPrintf(char *buff, int buff_length, continue; } cur++; - bool have_width = (*cur == '0'); + bool have_width = (*cur >= '0' && *cur <= '9'); + bool pad_with_zero = (*cur == '0'); int width = 0; if (have_width) { while (*cur >= '0' && *cur <= '9') { - have_width = true; width = width * 10 + *cur++ - '0'; } } @@ -126,7 +145,8 @@ int VSNPrintf(char *buff, int buff_length, dval = have_ll ? va_arg(args, s64) : have_z ? va_arg(args, sptr) : va_arg(args, int); - result += AppendSignedDecimal(&buff, buff_end, dval, width); + result += AppendSignedDecimal(&buff, buff_end, dval, width, + pad_with_zero); break; } case 'u': @@ -135,7 +155,7 @@ int VSNPrintf(char *buff, int buff_length, : have_z ? va_arg(args, uptr) : va_arg(args, unsigned); result += AppendUnsigned(&buff, buff_end, uval, - (*cur == 'u') ? 10 : 16, width); + (*cur == 'u') ? 10 : 16, width, pad_with_zero); break; } case 'p': { @@ -173,17 +193,86 @@ void SetPrintfAndReportCallback(void (*callback)(const char *)) { PrintfAndReportCallback = callback; } -void Printf(const char *format, ...) { +#if SANITIZER_SUPPORTS_WEAK_HOOKS +// Can be overriden in frontend. +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void OnPrint(const char *str); +#endif + +static void CallPrintfAndReportCallback(const char *str) { +#if SANITIZER_SUPPORTS_WEAK_HOOKS + if (&OnPrint != NULL) + OnPrint(str); +#endif + if (PrintfAndReportCallback) + PrintfAndReportCallback(str); +} + +static void SharedPrintfCode(bool append_pid, const char *format, + va_list args) { + va_list args2; + va_copy(args2, args); const int kLen = 16 * 1024; - InternalScopedBuffer buffer(kLen); + // |local_buffer| is small enough not to overflow the stack and/or violate + // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other + // hand, the bigger the buffer is, the more the chance the error report will + // fit into it. + char local_buffer[400]; + int needed_length; + char *buffer = local_buffer; + int buffer_size = ARRAY_SIZE(local_buffer); + // First try to print a message using a local buffer, and then fall back to + // mmaped buffer. + for (int use_mmap = 0; use_mmap < 2; use_mmap++) { + if (use_mmap) { + va_end(args); + va_copy(args, args2); + buffer = (char*)MmapOrDie(kLen, "Report"); + buffer_size = kLen; + } + needed_length = 0; + 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"); + } + } + 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"); + } + // If the message fit into the buffer, print it and exit. + break; + } + RawWrite(buffer); + CallPrintfAndReportCallback(buffer); + // If we had mapped any memory, clean up. + if (buffer != local_buffer) + UnmapOrDie((void *)buffer, buffer_size); + va_end(args2); +} + +void Printf(const char *format, ...) { va_list args; va_start(args, format); - int needed_length = VSNPrintf(buffer.data(), kLen, format, args); + SharedPrintfCode(false, format, args); + va_end(args); +} + +// Like Printf, but prints the current PID before the output string. +void Report(const char *format, ...) { + va_list args; + va_start(args, format); + SharedPrintfCode(true, format, args); va_end(args); - RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n"); - RawWrite(buffer.data()); - if (PrintfAndReportCallback) - PrintfAndReportCallback(buffer.data()); } // Writes at most "length" symbols to "buffer" (including trailing '\0'). @@ -198,22 +287,4 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...) { return needed_length; } -// Like Printf, but prints the current PID before the output string. -void Report(const char *format, ...) { - const int kLen = 16 * 1024; - InternalScopedBuffer buffer(kLen); - int needed_length = internal_snprintf(buffer.data(), - kLen, "==%d== ", GetPid()); - RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); - va_list args; - va_start(args, format); - needed_length += VSNPrintf(buffer.data() + needed_length, - kLen - needed_length, format, args); - va_end(args); - RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); - RawWrite(buffer.data()); - if (PrintfAndReportCallback) - PrintfAndReportCallback(buffer.data()); -} - } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps.h b/libsanitizer/sanitizer_common/sanitizer_procmaps.h index 400fd7a8bc4..87887f6b74b 100644 --- a/libsanitizer/sanitizer_common/sanitizer_procmaps.h +++ b/libsanitizer/sanitizer_common/sanitizer_procmaps.h @@ -17,51 +17,63 @@ namespace __sanitizer { -#ifdef _WIN32 +#if SANITIZER_WINDOWS class MemoryMappingLayout { public: - MemoryMappingLayout() {} + explicit MemoryMappingLayout(bool cache_enabled) { + (void)cache_enabled; + } bool GetObjectNameAndOffset(uptr addr, uptr *offset, - char filename[], uptr filename_size) { + char filename[], uptr filename_size, + uptr *protection) { UNIMPLEMENTED(); } }; -#else // _WIN32 -#if defined(__linux__) +#else // SANITIZER_WINDOWS +#if SANITIZER_LINUX struct ProcSelfMapsBuff { char *data; uptr mmaped_size; uptr len; }; -#endif // defined(__linux__) +#endif // SANITIZER_LINUX class MemoryMappingLayout { public: - MemoryMappingLayout(); + explicit MemoryMappingLayout(bool cache_enabled); bool Next(uptr *start, uptr *end, uptr *offset, - char filename[], uptr filename_size); + 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); + 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(); + // Memory protection masks. + static const uptr kProtectionRead = 1; + static const uptr kProtectionWrite = 2; + static const uptr kProtectionExecute = 4; + static const uptr kProtectionShared = 8; + 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) { + 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); + 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: @@ -84,17 +96,18 @@ class MemoryMappingLayout { return false; } -# if defined __linux__ +# if SANITIZER_LINUX ProcSelfMapsBuff proc_self_maps_; char *current_; // Static mappings cache. static ProcSelfMapsBuff cached_proc_self_maps_; static StaticSpinMutex cache_lock_; // protects cached_proc_self_maps_. -# elif defined __APPLE__ +# elif SANITIZER_MAC template bool NextSegmentLoad(uptr *start, uptr *end, uptr *offset, - char filename[], uptr filename_size); + char filename[], uptr filename_size, + uptr *protection); int current_image_; u32 current_magic_; u32 current_filetype_; @@ -103,7 +116,18 @@ class MemoryMappingLayout { # endif }; -#endif // _WIN32 +typedef void (*fill_profile_f)(uptr start, uptr rss, bool file, + /*out*/uptr *stats, uptr stats_size); + +// Parse the contents of /proc/self/smaps and generate a memory profile. +// |cb| is a tool-specific callback that fills the |stats| array containing +// |stats_size| elements. +void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size); + +// Returns code range for the specified module. +bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end); + +#endif // SANITIZER_WINDOWS } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_quarantine.h b/libsanitizer/sanitizer_common/sanitizer_quarantine.h index f3ad91a9cc2..7f567f97d7c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_quarantine.h +++ b/libsanitizer/sanitizer_common/sanitizer_quarantine.h @@ -145,7 +145,9 @@ class QuarantineCache { return 0; QuarantineBatch *b = list_.front(); list_.pop_front(); - SizeAdd(-b->size); + // FIXME: should probably add SizeSub method? + // See https://code.google.com/p/thread-sanitizer/issues/detail?id=20 + SizeAdd(0 - b->size); return b; } diff --git a/libsanitizer/sanitizer_common/sanitizer_report_decorator.h b/libsanitizer/sanitizer_common/sanitizer_report_decorator.h index cabf08e6082..c6e79ad0c7c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_report_decorator.h +++ b/libsanitizer/sanitizer_common/sanitizer_report_decorator.h @@ -17,6 +17,8 @@ namespace __sanitizer { class AnsiColorDecorator { + // 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) { } const char *Bold() const { return ansi_ ? "\033[1m" : ""; } diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc index 2e22155fa75..e8d9f01e7f9 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc @@ -199,4 +199,38 @@ const uptr *StackDepotGet(u32 id, uptr *size) { return 0; } +bool StackDepotReverseMap::IdDescPair::IdComparator( + const StackDepotReverseMap::IdDescPair &a, + const StackDepotReverseMap::IdDescPair &b) { + return a.id < b.id; +} + +StackDepotReverseMap::StackDepotReverseMap() + : map_(StackDepotGetStats()->n_uniq_ids + 100) { + for (int idx = 0; idx < kTabSize; idx++) { + 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) { + 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}; + 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; +} + } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h index bf73cf14aad..c2c04ef9d64 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h @@ -11,6 +11,7 @@ #ifndef SANITIZER_STACKDEPOT_H #define SANITIZER_STACKDEPOT_H +#include "sanitizer_common.h" #include "sanitizer_internal_defs.h" namespace __sanitizer { @@ -29,6 +30,31 @@ struct StackDepotStats { StackDepotStats *StackDepotGetStats(); +struct StackDesc; + +// Instantiating this class creates a snapshot of StackDepot which can be +// efficiently queried with StackDepotGet(). You can use it concurrently with +// StackDepot, but the snapshot is only guaranteed to contain those stack traces +// which were stored before it was instantiated. +class StackDepotReverseMap { + public: + StackDepotReverseMap(); + const uptr *Get(u32 id, uptr *size); + + private: + struct IdDescPair { + u32 id; + StackDesc *desc; + + static bool IdComparator(const IdDescPair &a, const IdDescPair &b); + }; + + InternalMmapVector map_; + + // Disallow evil constructors. + StackDepotReverseMap(const StackDepotReverseMap&); + void operator=(const StackDepotReverseMap&); +}; } // namespace __sanitizer #endif // SANITIZER_STACKDEPOT_H diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc index e14ea447264..4e7972424dc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc @@ -18,8 +18,9 @@ namespace __sanitizer { const char *StripPathPrefix(const char *filepath, const char *strip_file_prefix) { if (filepath == 0) return 0; - if (filepath == internal_strstr(filepath, strip_file_prefix)) - return filepath + internal_strlen(strip_file_prefix); + const char *prefix_beg = internal_strstr(filepath, strip_file_prefix); + if (prefix_beg) + return prefix_beg + internal_strlen(strip_file_prefix); return filepath; } @@ -62,7 +63,7 @@ static void PrintModuleAndOffset(const char *module, uptr offset, void StackTrace::PrintStack(const uptr *addr, uptr size, bool symbolize, const char *strip_file_prefix, SymbolizeCallback symbolize_callback ) { - MemoryMappingLayout proc_maps; + MemoryMappingLayout proc_maps(/*cache_enabled*/true); InternalScopedBuffer buff(GetPageSizeCached() * 2); InternalScopedBuffer addr_frames(64); uptr frame_num = 0; @@ -83,10 +84,10 @@ void StackTrace::PrintStack(const uptr *addr, uptr size, frame_num++; } } - if (symbolize && addr_frames_num == 0) { + if (symbolize && addr_frames_num == 0 && &getSymbolizer) { // Use our own (online) symbolizer, if necessary. - addr_frames_num = SymbolizeCode(pc, addr_frames.data(), - addr_frames.size()); + addr_frames_num = getSymbolizer()->SymbolizeCode( + pc, addr_frames.data(), addr_frames.size()); for (uptr j = 0; j < addr_frames_num; j++) { AddressInfo &info = addr_frames[j]; PrintStackFramePrefix(frame_num, pc); @@ -111,7 +112,8 @@ void StackTrace::PrintStack(const uptr *addr, uptr size, PrintStackFramePrefix(frame_num, pc); uptr offset; if (proc_maps.GetObjectNameAndOffset(pc, &offset, - buff.data(), buff.size())) { + buff.data(), buff.size(), + /* protection */0)) { PrintModuleAndOffset(buff.data(), offset, strip_file_prefix); } Printf("\n"); @@ -130,10 +132,12 @@ void StackTrace::FastUnwindStack(uptr pc, uptr bp, size = 1; uhwptr *frame = (uhwptr *)bp; uhwptr *prev_frame = frame - 1; + if (stack_top < 4096) return; // Sanity check for stack top. // 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 && + IsAligned((uptr)frame, sizeof(*frame)) && size < max_size) { uhwptr pc1 = frame[1]; if (pc1 != pc) { diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h index fd0c4671a61..1f05753d207 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h @@ -17,6 +17,15 @@ namespace __sanitizer { static const uptr kStackTraceMax = 256; +#if SANITIZER_LINUX && (defined(__arm__) || \ + defined(__powerpc__) || defined(__powerpc64__) || \ + defined(__sparc__) || \ + defined(__mips__)) +#define SANITIZER_CAN_FAST_UNWIND 0 +#else +#define SANITIZER_CAN_FAST_UNWIND 1 +#endif + struct StackTrace { typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer, int out_size); @@ -49,8 +58,10 @@ struct StackTrace { static uptr GetCurrentPc(); static uptr GetPreviousInstructionPc(uptr pc); + SANITIZER_INTERFACE_ATTRIBUTE static uptr CompressStack(StackTrace *stack, u32 *compressed, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE static void UncompressStack(StackTrace *stack, u32 *compressed, uptr size); }; @@ -59,6 +70,9 @@ struct StackTrace { const char *StripPathPrefix(const char *filepath, const char *strip_file_prefix); +void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, + uptr stack_top, uptr stack_bottom, bool fast); + } // namespace __sanitizer // Use this macro if you want to print stack trace with the caller diff --git a/libsanitizer/sanitizer_common/sanitizer_stoptheworld.h b/libsanitizer/sanitizer_common/sanitizer_stoptheworld.h new file mode 100644 index 00000000000..b1241da1f73 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_stoptheworld.h @@ -0,0 +1,66 @@ +//===-- sanitizer_stoptheworld.h --------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the StopTheWorld function which suspends the execution of the current +// process and runs the user-supplied callback in the same address space. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_STOPTHEWORLD_H +#define SANITIZER_STOPTHEWORLD_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_common.h" + +namespace __sanitizer { +typedef int SuspendedThreadID; + +// Holds the list of suspended threads and provides an interface to dump their +// register contexts. +class SuspendedThreadsList { + public: + SuspendedThreadsList() + : thread_ids_(1024) {} + SuspendedThreadID GetThreadID(uptr index) const { + CHECK_LT(index, thread_ids_.size()); + return thread_ids_[index]; + } + int GetRegistersAndSP(uptr index, uptr *buffer, uptr *sp) const; + // The buffer in GetRegistersAndSP should be at least this big. + static uptr RegisterCount(); + uptr thread_count() const { return thread_ids_.size(); } + bool Contains(SuspendedThreadID thread_id) const { + for (uptr i = 0; i < thread_ids_.size(); i++) { + if (thread_ids_[i] == thread_id) + return true; + } + return false; + } + void Append(SuspendedThreadID thread_id) { + thread_ids_.push_back(thread_id); + } + + private: + InternalMmapVector thread_ids_; + + // Prohibit copy and assign. + SuspendedThreadsList(const SuspendedThreadsList&); + void operator=(const SuspendedThreadsList&); +}; + +typedef void (*StopTheWorldCallback)( + const SuspendedThreadsList &suspended_threads_list, + void *argument); + +// 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. +// This function should NOT be called from multiple threads simultaneously. +void StopTheWorld(StopTheWorldCallback callback, void *argument); + +} // namespace __sanitizer + +#endif // SANITIZER_STOPTHEWORLD_H diff --git a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc new file mode 100644 index 00000000000..4f44a036982 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -0,0 +1,450 @@ +//===-- sanitizer_stoptheworld_linux_libcdep.cc ---------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// See sanitizer_stoptheworld.h for details. +// This implementation was inspired by Markus Gutschke's linuxthreads.cc. +// +//===----------------------------------------------------------------------===// + + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX && defined(__x86_64__) + +#include "sanitizer_stoptheworld.h" + +#include +#include // for CLONE_* definitions +#include +#include // for PR_* definitions +#include // for PTRACE_* definitions +#include // for pid_t +#if SANITIZER_ANDROID && defined(__arm__) +# include // for pt_regs +#else +# include // for user_regs_struct +#endif +#include // for signal-related stuff + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_linux.h" +#include "sanitizer_mutex.h" +#include "sanitizer_placement_new.h" + +// This module works by spawning a Linux task which then attaches to every +// thread in the caller process with ptrace. This suspends the threads, and +// PTRACE_GETREGS can then be used to obtain their register state. The callback +// supplied to StopTheWorld() is run in the tracer task while the threads are +// suspended. +// The tracer task must be placed in a different thread group for ptrace to +// work, so it cannot be spawned as a pthread. Instead, we use the low-level +// clone() interface (we want to share the address space with the caller +// process, so we prefer clone() over fork()). +// +// We avoid the use of libc for two reasons: +// 1. calling a library function while threads are suspended could cause a +// deadlock, if one of the treads happens to be holding a libc lock; +// 2. it's generally not safe to call libc functions from the tracer task, +// because clone() does not set up a thread-local storage for it. Any +// thread-local variables used by libc will be shared between the tracer task +// and the thread which spawned it. +// +// We deal with this by replacing libc calls with calls to our own +// implementations defined in sanitizer_libc.h and sanitizer_linux.h. However, +// there are still some libc functions which are used here: +// +// * All of the system calls ultimately go through the libc syscall() function. +// We're operating under the assumption that syscall()'s implementation does +// not acquire any locks or use any thread-local data (except for the errno +// variable, which we handle separately). +// +// * We lack custom implementations of sigfillset() and sigaction(), so we use +// the libc versions instead. The same assumptions as above apply. +// +// * It is safe to call libc functions before the cloned thread is spawned or +// after it has exited. The following functions are used in this manner: +// sigdelset() +// sigprocmask() + +COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t)); + +namespace __sanitizer { +// This class handles thread suspending/unsuspending in the tracer thread. +class ThreadSuspender { + public: + explicit ThreadSuspender(pid_t pid) + : pid_(pid) { + CHECK_GE(pid, 0); + } + bool SuspendAllThreads(); + void ResumeAllThreads(); + void KillAllThreads(); + SuspendedThreadsList &suspended_threads_list() { + return suspended_threads_list_; + } + private: + SuspendedThreadsList suspended_threads_list_; + pid_t pid_; + bool SuspendThread(SuspendedThreadID thread_id); +}; + +bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) { + // 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)) + return false; + int pterrno; + if (internal_iserror(internal_ptrace(PTRACE_ATTACH, thread_id, NULL, NULL), + &pterrno)) { + // Either the thread is dead, or something prevented us from attaching. + // Log this event and move on. + Report("Could not attach to thread %d (errno %d).\n", thread_id, pterrno); + return false; + } else { + if (SanitizerVerbosity > 0) + Report("Attached to thread %d.\n", thread_id); + // 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. + Report("Waiting on thread %d failed, detaching (errno %d).\n", thread_id, + wperrno); + internal_ptrace(PTRACE_DETACH, thread_id, NULL, NULL); + return false; + } + suspended_threads_list_.Append(thread_id); + return true; + } +} + +void ThreadSuspender::ResumeAllThreads() { + 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), + &pterrno)) { + if (SanitizerVerbosity > 0) + Report("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. + Report("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); +} + +bool ThreadSuspender::SuspendAllThreads() { + ThreadLister thread_lister(pid_); + bool added_threads; + do { + // Run through the directory entries once. + added_threads = false; + pid_t tid = thread_lister.GetNextTID(); + while (tid >= 0) { + if (SuspendThread(tid)) + added_threads = true; + tid = thread_lister.GetNextTID(); + } + if (thread_lister.error()) { + // Detach threads and fail. + ResumeAllThreads(); + return false; + } + thread_lister.Reset(); + } while (added_threads); + return true; +} + +// 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 finished its + // preparations. + BlockingMutex mutex; +}; + +static DieCallbackType old_die_callback; + +// Signal handler to wake up suspended threads when the tracer thread dies. +void TracerThreadSignalHandler(int signum, siginfo_t *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); +} + +static void TracerThreadDieCallback() { + // Generally a call to Die() in the tracer thread should be fatal to the + // parent process as well, because they share the address space. + // This really only works correctly if all the threads are suspended at this + // 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(); +} + +// Size of alternative stack for signal handlers in the tracer thread. +static const int kHandlerStackSize = 4096; + +// This function will be run as a cloned task. +static int TracerThread(void* argument) { + TracerThreadArgument *tracer_thread_argument = + (TracerThreadArgument *)argument; + + // Wait for the parent thread to finish preparations. + tracer_thread_argument->mutex.Lock(); + tracer_thread_argument->mutex.Unlock(); + + SetDieCallback(TracerThreadDieCallback); + + ThreadSuspender thread_suspender(internal_getppid()); + // Global pointer for the signal handler. + thread_suspender_instance = &thread_suspender; + + // Alternate stack for signal handling. + InternalScopedBuffer handler_stack_memory(kHandlerStackSize); + struct sigaltstack handler_stack; + 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++) { + struct sigaction new_sigaction; + internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); + new_sigaction.sa_sigaction = TracerThreadSignalHandler; + new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO; + sigfillset(&new_sigaction.sa_mask); + sigaction(kUnblockedSignals[signal_index], &new_sigaction, NULL); + } + + int exit_code = 0; + if (!thread_suspender.SuspendAllThreads()) { + Report("Failed suspending threads.\n"); + exit_code = 3; + } else { + tracer_thread_argument->callback(thread_suspender.suspended_threads_list(), + tracer_thread_argument->callback_argument); + thread_suspender.ResumeAllThreads(); + exit_code = 0; + } + thread_suspender_instance = NULL; + handler_stack.ss_flags = SS_DISABLE; + internal_sigaltstack(&handler_stack, NULL); + return exit_code; +} + +class ScopedStackSpaceWithGuard { + public: + explicit ScopedStackSpaceWithGuard(uptr stack_size) { + stack_size_ = stack_size; + guard_size_ = GetPageSizeCached(); + // FIXME: Omitting MAP_STACK here works in current kernels but might break + // in the future. + guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_, + "ScopedStackWithGuard"); + CHECK_EQ(guard_start_, (uptr)Mprotect((uptr)guard_start_, guard_size_)); + } + ~ScopedStackSpaceWithGuard() { + UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_); + } + void *Bottom() const { + return (void *)(guard_start_ + stack_size_ + guard_size_); + } + + private: + uptr stack_size_; + uptr guard_size_; + uptr guard_start_; +}; + +NOINLINE static void WipeStack() { + char arr[256]; + internal_memset(arr, 0, sizeof(arr)); +} + +// We have a limitation on the stack frame size, so some stuff had to be moved +// into globals. +static sigset_t blocked_sigset; +static sigset_t old_sigset; +static struct sigaction old_sigactions[ARRAY_SIZE(kUnblockedSignals)]; + +class StopTheWorldScope { + public: + StopTheWorldScope() { + // Glibc's sigaction() has a side-effect where it copies garbage stack + // values into oldact, which can cause false negatives in LSan. As a quick + // workaround we zero some stack space here. + WipeStack(); + // 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). + sigfillset(&blocked_sigset); + for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); + signal_index++) { + // Remove the signal from the set of blocked signals. + sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]); + // Install the default handler. + struct sigaction new_sigaction; + internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); + new_sigaction.sa_handler = SIG_DFL; + sigfillset(&new_sigaction.sa_mask); + sigaction(kUnblockedSignals[signal_index], &new_sigaction, + &old_sigactions[signal_index]); + } + int sigprocmask_status = + 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++) { + sigaction(kUnblockedSignals[signal_index], + &old_sigactions[signal_index], NULL); + } + sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset); + } + + private: + int process_was_dumpable_; +}; + +void StopTheWorld(StopTheWorldCallback callback, void *argument) { + StopTheWorldScope in_stoptheworld; + // Prepare the arguments for TracerThread. + struct TracerThreadArgument tracer_thread_argument; + tracer_thread_argument.callback = callback; + tracer_thread_argument.callback_argument = argument; + 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(); + 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 */); + int local_errno = 0; + if (internal_iserror(tracer_pid, &local_errno)) { + Report("Failed spawning a tracer thread (errno %d).\n", local_errno); + tracer_thread_argument.mutex.Unlock(); + } else { + // On some systems we have to explicitly declare that we want to be traced + // by the tracer thread. +#ifdef PR_SET_PTRACER + internal_prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0); +#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)) + Report("Waiting on the tracer thread failed (errno %d).\n", local_errno); + } +} + +// Platform-specific methods from SuspendedThreadsList. +#if SANITIZER_ANDROID && defined(__arm__) +typedef pt_regs regs_struct; +#define REG_SP ARM_sp + +#elif SANITIZER_LINUX && defined(__arm__) +typedef user_regs regs_struct; +#define REG_SP uregs[13] + +#elif defined(__i386__) || defined(__x86_64__) +typedef user_regs_struct regs_struct; +#if defined(__i386__) +#define REG_SP esp +#else +#define REG_SP rsp +#endif + +#elif defined(__powerpc__) || defined(__powerpc64__) +typedef pt_regs regs_struct; +#define REG_SP gpr[PT_R1] + +#elif defined(__mips__) +typedef struct user regs_struct; +#define REG_SP regs[EF_REG29] + +#else +#error "Unsupported architecture" +#endif // SANITIZER_ANDROID && defined(__arm__) + +int SuspendedThreadsList::GetRegistersAndSP(uptr index, + uptr *buffer, + uptr *sp) const { + pid_t tid = GetThreadID(index); + regs_struct regs; + int pterrno; + if (internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, NULL, ®s), + &pterrno)) { + Report("Could not get registers from thread %d (errno %d).\n", + tid, pterrno); + return -1; + } + + *sp = regs.REG_SP; + internal_memcpy(buffer, ®s, sizeof(regs)); + return 0; +} + +uptr SuspendedThreadsList::RegisterCount() { + return sizeof(regs_struct) / sizeof(uptr); +} +} // namespace __sanitizer + +#endif // SANITIZER_LINUX && defined(__x86_64__) diff --git a/libsanitizer/sanitizer_common/sanitizer_suppressions.cc b/libsanitizer/sanitizer_common/sanitizer_suppressions.cc new file mode 100644 index 00000000000..d8e8976a4b2 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_suppressions.cc @@ -0,0 +1,146 @@ +//===-- sanitizer_suppressions.cc -----------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Suppression parsing/matching code shared between TSan and LSan. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_suppressions.h" + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" + +namespace __sanitizer { + +static const char *const kTypeStrings[SuppressionTypeCount] = { + "none", "race", "mutex", "thread", "signal", "leak" +}; + +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++; + } + 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 true; +} + +bool SuppressionContext::Match(const char *str, SuppressionType 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; +} + +static const char *StripPrefix(const char *str, const char *prefix) { + while (str && *str == *prefix) { + str++; + prefix++; + } + if (!*prefix) + return str; + return 0; +} + +void SuppressionContext::Parse(const char *str) { + // Context must not mutate once Match has been called. + CHECK(can_parse_); + const char *line = str; + while (line) { + while (line[0] == ' ' || line[0] == '\t') + line++; + const char *end = internal_strchr(line, '\n'); + if (end == 0) + end = line + internal_strlen(line); + if (line != end && line[0] != '#') { + const char *end2 = end; + while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t')) + end2--; + int type; + for (type = 0; type < SuppressionTypeCount; type++) { + const char *next_char = StripPrefix(line, kTypeStrings[type]); + if (next_char && *next_char == ':') { + line = ++next_char; + break; + } + } + if (type == SuppressionTypeCount) { + Printf("%s: failed to parse suppressions\n", SanitizerToolName); + Die(); + } + Suppression s; + s.type = static_cast(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); + } + if (end[0] == 0) + break; + line = end + 1; + } +} + +uptr SuppressionContext::SuppressionCount() { + return suppressions_.size(); +} + +void SuppressionContext::GetMatched( + InternalMmapVector *matched) { + for (uptr i = 0; i < suppressions_.size(); i++) + if (suppressions_[i].hit_count) + matched->push_back(&suppressions_[i]); +} + +const char *SuppressionTypeString(SuppressionType t) { + CHECK(t < SuppressionTypeCount); + return kTypeStrings[t]; +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_suppressions.h b/libsanitizer/sanitizer_common/sanitizer_suppressions.h new file mode 100644 index 00000000000..9a0d87b383e --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_suppressions.h @@ -0,0 +1,58 @@ +//===-- sanitizer_suppressions.h --------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Suppression parsing/matching code shared between TSan and LSan. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SUPPRESSIONS_H +#define SANITIZER_SUPPRESSIONS_H + +#include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { + +enum SuppressionType { + SuppressionNone, + SuppressionRace, + SuppressionMutex, + SuppressionThread, + SuppressionSignal, + SuppressionLeak, + SuppressionTypeCount +}; + +struct Suppression { + SuppressionType type; + char *templ; + unsigned hit_count; + uptr weight; +}; + +class SuppressionContext { + public: + SuppressionContext() : suppressions_(1), can_parse_(true) {} + void Parse(const char *str); + bool Match(const char* str, SuppressionType type, Suppression **s); + uptr SuppressionCount(); + void GetMatched(InternalMmapVector *matched); + + private: + InternalMmapVector suppressions_; + bool can_parse_; + + friend class SuppressionContextTest; +}; + +const char *SuppressionTypeString(SuppressionType t); + +// Exposed for testing. +bool TemplateMatch(char *templ, const char *str); + +} // namespace __sanitizer + +#endif // SANITIZER_SUPPRESSIONS_H diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h index 751806e8472..2c84773eebc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h @@ -22,6 +22,7 @@ #ifndef SANITIZER_SYMBOLIZER_H #define SANITIZER_SYMBOLIZER_H +#include "sanitizer_allocator_internal.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" // WARNING: Do not include system headers here. See details above. @@ -40,8 +41,14 @@ struct AddressInfo { AddressInfo() { internal_memset(this, 0, sizeof(AddressInfo)); } + // Deletes all strings and sets all fields to zero. - void Clear(); + void Clear() { + InternalFree(module); + InternalFree(function); + InternalFree(file); + internal_memset(this, 0, sizeof(AddressInfo)); + } void FillAddressAndModuleInfo(uptr addr, const char *mod_name, uptr mod_offset) { @@ -60,52 +67,39 @@ struct DataInfo { uptr size; }; -// 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. -// This function should NOT be called from two threads simultaneously. -uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames); -bool SymbolizeData(uptr address, DataInfo *info); - -bool IsSymbolizerAvailable(); - -// Attempts to demangle the provided C++ mangled name. -const char *Demangle(const char *Name); - -// Starts external symbolizer program in a subprocess. Sanitizer communicates -// with external symbolizer via pipes. -bool InitializeExternalSymbolizer(const char *path_to_symbolizer); - -class LoadedModule { +class SymbolizerInterface { public: - LoadedModule(const char *module_name, uptr base_address); - void addAddressRange(uptr beg, uptr end); - bool containsAddress(uptr address) const; - - const char *full_name() const { return full_name_; } - uptr base_address() const { return base_address_; } - - private: - struct AddressRange { - uptr beg; - uptr end; - }; - char *full_name_; - uptr base_address_; - static const uptr kMaxNumberOfAddressRanges = 6; - AddressRange ranges_[kMaxNumberOfAddressRanges]; - uptr n_ranges_; + // 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; + } + // Release internal caches (if any). + virtual void Flush() {} + // Attempts to demangle the provided C++ mangled name. + virtual const char *Demangle(const char *name) { + return name; + } + virtual void PrepareForSandboxing() {} + // Starts external symbolizer program in a subprocess. Sanitizer communicates + // with external symbolizer via pipes. If path_to_symbolizer is NULL or empty, + // tries to look for llvm-symbolizer in PATH. + virtual bool InitializeExternal(const char *path_to_symbolizer) { + return false; + } }; -// Creates external symbolizer connected via pipe, user should write -// to output_fd and read from input_fd. -bool StartSymbolizerSubprocess(const char *path_to_symbolizer, - int *input_fd, int *output_fd); - -// OS-dependent function that fills array with descriptions of at most -// "max_modules" currently loaded modules. Returns the number of -// initialized modules. -uptr GetListOfModules(LoadedModule *modules, uptr max_modules); +// Returns platform-specific implementation of SymbolizerInterface. It can't be +// used from multiple threads simultaneously. +SANITIZER_WEAK_ATTRIBUTE SymbolizerInterface *getSymbolizer(); } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_itanium.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_itanium.cc deleted file mode 100644 index b356f9a09e3..00000000000 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_itanium.cc +++ /dev/null @@ -1,40 +0,0 @@ -//===-- sanitizer_symbolizer_itanium.cc -----------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is shared between the sanitizer run-time libraries. -// Itanium C++ ABI-specific implementation of symbolizer parts. -//===----------------------------------------------------------------------===// -#if defined(__APPLE__) || defined(__linux__) - -#include "sanitizer_symbolizer.h" - -#include - -// 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 __cxxabiv1 { - extern "C" char *__cxa_demangle(const char *mangled, char *buffer, - size_t *length, int *status) - SANITIZER_WEAK_ATTRIBUTE; -} - -const char *__sanitizer::Demangle(const char *MangledName) { - // 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 - // it does not allocate). For now, we just call it anyway, and we leak - // the returned value. - if (__cxxabiv1::__cxa_demangle) - if (const char *Demangled = - __cxxabiv1::__cxa_demangle(MangledName, 0, 0, 0)) - return Demangled; - - return MangledName; -} - -#endif // __APPLE__ || __linux__ diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_linux.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_linux.cc deleted file mode 100644 index 01f1e4588da..00000000000 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_linux.cc +++ /dev/null @@ -1,180 +0,0 @@ -//===-- sanitizer_symbolizer_linux.cc -------------------------------------===// -// -// 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. -// Linux-specific implementation of symbolizer parts. -//===----------------------------------------------------------------------===// -#ifdef __linux__ -#include "sanitizer_common.h" -#include "sanitizer_internal_defs.h" -#include "sanitizer_libc.h" -#include "sanitizer_placement_new.h" -#include "sanitizer_symbolizer.h" - -#include -#include -#include -#include -#include -#include -#include - -#if !defined(__ANDROID__) && !defined(ANDROID) -#include -#endif - -namespace __sanitizer { - -static const int kSymbolizerStartupTimeMillis = 10; - -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"); - 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); - 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); - - int pid = 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 = getdtablesize(); fd > 2; fd--) - internal_close(fd); - execl(path_to_symbolizer, path_to_symbolizer, (char*)0); - internal__exit(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); - int exited_pid = waitpid(pid, &pid_status, WNOHANG); - if (exited_pid != 0) { - // Either waitpid failed, or child has already exited. - Report("WARNING: external symbolizer didn't start up correctly!\n"); - return false; - } - - return true; -} - -#if defined(__ANDROID__) || defined(ANDROID) -uptr GetListOfModules(LoadedModule *modules, uptr max_modules) { - UNIMPLEMENTED(); -} -#else // ANDROID -typedef ElfW(Phdr) Elf_Phdr; - -struct DlIteratePhdrData { - LoadedModule *modules; - uptr current_n; - uptr max_n; -}; - -static const uptr kMaxPathLength = 512; - -static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { - DlIteratePhdrData *data = (DlIteratePhdrData*)arg; - if (data->current_n == data->max_n) - return 0; - InternalScopedBuffer module_name(kMaxPathLength); - module_name.data()[0] = '\0'; - if (data->current_n == 0) { - // First module is the binary itself. - uptr module_name_len = internal_readlink( - "/proc/self/exe", module_name.data(), module_name.size()); - CHECK_NE(module_name_len, (uptr)-1); - CHECK_LT(module_name_len, module_name.size()); - module_name[module_name_len] = '\0'; - } else if (info->dlpi_name) { - internal_strncpy(module_name.data(), info->dlpi_name, module_name.size()); - } - if (module_name.data()[0] == '\0') - return 0; - void *mem = &data->modules[data->current_n]; - LoadedModule *cur_module = new(mem) LoadedModule(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); - } - } - return 0; -} - -uptr GetListOfModules(LoadedModule *modules, uptr max_modules) { - CHECK(modules); - DlIteratePhdrData data = {modules, 0, max_modules}; - dl_iterate_phdr(dl_iterate_phdr_cb, &data); - return data.current_n; -} -#endif // ANDROID - -} // namespace __sanitizer - -#endif // __linux__ diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc deleted file mode 100644 index a1b931b737e..00000000000 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc +++ /dev/null @@ -1,29 +0,0 @@ -//===-- 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 AddressSanitizer and ThreadSanitizer -// run-time libraries. -// Mac-specific implementation of symbolizer parts. -//===----------------------------------------------------------------------===// -#ifdef __APPLE__ -#include "sanitizer_internal_defs.h" -#include "sanitizer_symbolizer.h" - -namespace __sanitizer { - -bool StartSymbolizerSubprocess(const char *path_to_symbolizer, - int *input_fd, int *output_fd) { - UNIMPLEMENTED(); -} - -uptr GetListOfModules(LoadedModule *modules, uptr max_modules) { - UNIMPLEMENTED(); -} - -} // namespace __sanitizer - -#endif // __APPLE__ diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc similarity index 58% rename from libsanitizer/sanitizer_common/sanitizer_symbolizer.cc rename to libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc index 2c9cb2b0a55..34063081c77 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -1,4 +1,4 @@ -//===-- sanitizer_symbolizer.cc -------------------------------------------===// +//===-- sanitizer_symbolizer_posix_libcdep.cc -----------------------------===// // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. @@ -6,42 +6,150 @@ //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer -// run-time libraries. See sanitizer_symbolizer.h for details. +// run-time libraries. +// POSIX-specific implementation of symbolizer parts. //===----------------------------------------------------------------------===// +#include "sanitizer_platform.h" +#if SANITIZER_POSIX +#include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_linux.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_symbolizer.h" +#include +#include +#include +#include + +// 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 __cxxabiv1 { + extern "C" SANITIZER_WEAK_ATTRIBUTE + char *__cxa_demangle(const char *mangled, char *buffer, + size_t *length, int *status); +} + namespace __sanitizer { -void AddressInfo::Clear() { - InternalFree(module); - InternalFree(function); - InternalFree(file); - internal_memset(this, 0, sizeof(AddressInfo)); +// Attempts to demangle the name via __cxa_demangle from __cxxabiv1. +static 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 + // it does not allocate). For now, we just call it anyway, and we leak + // the returned value. + if (__cxxabiv1::__cxa_demangle) + if (const char *demangled_name = + __cxxabiv1::__cxa_demangle(name, 0, 0, 0)) + return demangled_name; + + return name; } -LoadedModule::LoadedModule(const char *module_name, uptr base_address) { - full_name_ = internal_strdup(module_name); - base_address_ = base_address; - n_ranges_ = 0; -} +#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 -void LoadedModule::addAddressRange(uptr beg, uptr end) { - CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges); - ranges_[n_ranges_].beg = beg; - ranges_[n_ranges_].end = end; - n_ranges_++; -} +static const int kSymbolizerStartupTimeMillis = 10; -bool LoadedModule::containsAddress(uptr address) const { - for (uptr i = 0; i < n_ranges_; i++) { - if (ranges_[i].beg <= address && address < ranges_[i].end) - return true; +// 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"); + return false; } - 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); + 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); + + int pid = 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 = getdtablesize(); fd > 2; fd--) + internal_close(fd); + execl(path_to_symbolizer, path_to_symbolizer, kSymbolizerArch, (char*)0); + internal__exit(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); + int exited_pid = waitpid(pid, &pid_status, WNOHANG); + if (exited_pid != 0) { + // Either waitpid failed, or child has already exited. + Report("WARNING: external symbolizer didn't start up correctly!\n"); + return false; + } + + return true; } // Extracts the prefix of "str" that consists of any characters not @@ -109,7 +217,7 @@ class ExternalSymbolizer { char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { CHECK(module_name); - internal_snprintf(buffer_, kBufferSize, "%s%s 0x%zx\n", + internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", is_data ? "DATA " : "", module_name, module_offset); if (!writeToSymbolizer(buffer_, internal_strlen(buffer_))) return 0; @@ -126,6 +234,9 @@ class ExternalSymbolizer { return StartSymbolizerSubprocess(path_, &input_fd_, &output_fd_); } + void Flush() { + } + private: bool readFromSymbolizer(char *buffer, uptr max_length) { if (max_length == 0) @@ -176,17 +287,23 @@ static LowLevelAllocator symbolizer_allocator; // Linker initialized. #if SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset, char *Buffer, int MaxLength); -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset, char *Buffer, int MaxLength); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_symbolize_flush(); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +int __sanitizer_symbolize_demangle(const char *Name, char *Buffer, + int MaxLength); } // extern "C" class InternalSymbolizer { public: typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); + static InternalSymbolizer *get() { if (__sanitizer_symbolize_code != 0 && __sanitizer_symbolize_data != 0) { @@ -195,6 +312,7 @@ class InternalSymbolizer { } 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; @@ -203,10 +321,34 @@ class InternalSymbolizer { return 0; } + void Flush() { + if (__sanitizer_symbolize_flush) + __sanitizer_symbolize_flush(); + } + + const char *Demangle(const char *name) { + if (__sanitizer_symbolize_demangle) { + for (uptr res_length = 1024; + res_length <= InternalSizeClassMap::kMaxSize;) { + char *res_buff = static_cast(InternalAlloc(res_length)); + uptr req_length = + __sanitizer_symbolize_demangle(name, res_buff, res_length); + if (req_length > res_length) { + res_length = req_length + 1; + InternalFree(res_buff); + continue; + } + return res_buff; + } + } + return name; + } + private: InternalSymbolizer() { } static const int kBufferSize = 16 * 1024; + static const int kMaxDemangledNameSize = 1024; char buffer_[kBufferSize]; }; #else // SANITIZER_SUPPORTS_WEAK_HOOKS @@ -217,11 +359,15 @@ class InternalSymbolizer { 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 Symbolizer { +class Symbolizer : public SymbolizerInterface { + // This class has no constructor, as global constructors are forbidden in + // sanitizer_common. It should be linker initialized instead. public: uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) { if (max_frames == 0) @@ -303,7 +449,12 @@ class Symbolizer { return true; } - bool InitializeExternalSymbolizer(const char *path_to_symbolizer) { + bool InitializeExternal(const char *path_to_symbolizer) { + if (!path_to_symbolizer || path_to_symbolizer[0] == '\0') { + path_to_symbolizer = FindPathToBinary("llvm-symbolizer"); + if (!path_to_symbolizer) + return false; + } int input_fd, output_fd; if (!StartSymbolizerSubprocess(path_to_symbolizer, &input_fd, &output_fd)) return false; @@ -313,17 +464,37 @@ class Symbolizer { return true; } - bool IsSymbolizerAvailable() { + bool IsAvailable() { if (internal_symbolizer_ == 0) internal_symbolizer_ = InternalSymbolizer::get(); return internal_symbolizer_ || external_symbolizer_; } + void Flush() { + if (internal_symbolizer_) + internal_symbolizer_->Flush(); + if (external_symbolizer_) + external_symbolizer_->Flush(); + } + + const char *Demangle(const char *name) { + if (IsAvailable() && internal_symbolizer_ != 0) + return internal_symbolizer_->Demangle(name); + return DemangleCXXABI(name); + } + + void PrepareForSandboxing() { +#if SANITIZER_LINUX && !SANITIZER_ANDROID + // Cache /proc/self/exe on Linux. + CacheBinaryName(); +#endif + } + private: char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { // First, try to use internal symbolizer. - if (internal_symbolizer_ == 0) { - internal_symbolizer_ = InternalSymbolizer::get(); + if (!IsAvailable()) { + return 0; } if (internal_symbolizer_) { return internal_symbolizer_->SendCommand(is_data, module_name, @@ -353,21 +524,35 @@ class Symbolizer { } LoadedModule *FindModuleForAddress(uptr address) { - if (modules_ == 0) { + 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); - CHECK_GT(n_modules_, 0); + 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; } + void ReportExternalSymbolizerError(const char *msg) { // Don't use atomics here for now, as SymbolizeCode can't be called // from multiple threads anyway. @@ -382,27 +567,29 @@ class Symbolizer { 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_; ExternalSymbolizer *external_symbolizer_; // Leaked. InternalSymbolizer *internal_symbolizer_; // Leaked. }; -static Symbolizer symbolizer; // Linker initialized. +static ALIGNED(64) char symbolizer_placeholder[sizeof(Symbolizer)]; +static Symbolizer *symbolizer; -uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames) { - return symbolizer.SymbolizeCode(address, frames, max_frames); -} - -bool SymbolizeData(uptr address, DataInfo *info) { - return symbolizer.SymbolizeData(address, info); -} - -bool InitializeExternalSymbolizer(const char *path_to_symbolizer) { - return symbolizer.InitializeExternalSymbolizer(path_to_symbolizer); -} - -bool IsSymbolizerAvailable() { - return symbolizer.IsSymbolizerAvailable(); +SymbolizerInterface *getSymbolizer() { + static atomic_uint8_t initialized; + static StaticSpinMutex init_mu; + if (atomic_load(&initialized, memory_order_acquire) == 0) { + SpinMutexLock l(&init_mu); + if (atomic_load(&initialized, memory_order_relaxed) == 0) { + symbolizer = new(symbolizer_placeholder) Symbolizer(); + atomic_store(&initialized, 1, memory_order_release); + } + } + return symbolizer; } } // namespace __sanitizer + +#endif // SANITIZER_POSIX diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc index ad0053234f0..44801f39b31 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc @@ -9,25 +9,18 @@ // run-time libraries. // Windows-specific implementation of symbolizer parts. //===----------------------------------------------------------------------===// -#ifdef _WIN32 -#include +#include "sanitizer_platform.h" +#if SANITIZER_WINDOWS #include "sanitizer_internal_defs.h" #include "sanitizer_symbolizer.h" namespace __sanitizer { -bool StartSymbolizerSubprocess(const char *path_to_symbolizer, - int *input_fd, int *output_fd) { - UNIMPLEMENTED(); -} +static SymbolizerInterface win_symbolizer; // Linker initialized. -uptr GetListOfModules(LoadedModule *modules, uptr max_modules) { - UNIMPLEMENTED(); -}; - -const char *Demangle(const char *MangledName) { - return MangledName; +SymbolizerInterface *getSymbolizer() { + return &win_symbolizer; } } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_syscall_generic.inc b/libsanitizer/sanitizer_common/sanitizer_syscall_generic.inc new file mode 100644 index 00000000000..6b2c915a3bc --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_syscall_generic.inc @@ -0,0 +1,22 @@ +//===-- sanitizer_syscall_generic.inc ---------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Generic implementations of internal_syscall and internal_iserror. +// +//===----------------------------------------------------------------------===// + +#define internal_syscall syscall + +bool internal_iserror(uptr retval, int *rverrno) { + if (retval == (uptr)-1) { + if (rverrno) + *rverrno = errno; + return true; + } else { + return false; + } +} diff --git a/libsanitizer/sanitizer_common/sanitizer_syscall_linux_x86_64.inc b/libsanitizer/sanitizer_common/sanitizer_syscall_linux_x86_64.inc new file mode 100644 index 00000000000..4f405d92b05 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_syscall_linux_x86_64.inc @@ -0,0 +1,85 @@ +//===-- sanitizer_syscall_linux_x86_64.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/x86_64. +// +//===----------------------------------------------------------------------===// + +static uptr internal_syscall(u64 nr) { + u64 retval; + asm volatile("syscall" : "=a"(retval) : "a"(nr) : "rcx", "r11"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1) { + u64 retval; + asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1) : + "rcx", "r11"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2) { + u64 retval; + asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), + "S"((u64)arg2) : "rcx", "r11"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3) { + u64 retval; + asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), + "S"((u64)arg2), "d"((u64)arg3) : "rcx", "r11"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { + u64 retval; + asm volatile("mov %5, %%r10;" + "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), + "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4) : + "rcx", "r11", "r10"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4, + T5 arg5) { + u64 retval; + asm volatile("mov %5, %%r10;" + "mov %6, %%r8;" + "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), + "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4), "r"((u64)arg5) : + "rcx", "r11", "r10", "r8"); + return retval; +} + +template +static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4, + T5 arg5, T6 arg6) { + u64 retval; + asm volatile("mov %5, %%r10;" + "mov %6, %%r8;" + "mov %7, %%r9;" + "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), + "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4), "r"((u64)arg5), + "r"((u64)arg6) : "rcx", "r11", "r10", "r8", "r9"); + return retval; +} + +bool internal_iserror(uptr retval, int *rverrno) { + if (retval >= (uptr)-4095) { + if (rverrno) + *rverrno = -retval; + return true; + } + return false; +} diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_registry.cc b/libsanitizer/sanitizer_common/sanitizer_thread_registry.cc new file mode 100644 index 00000000000..e639430f73a --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_thread_registry.cc @@ -0,0 +1,277 @@ +//===-- sanitizer_thread_registry.cc --------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between sanitizer tools. +// +// General thread bookkeeping functionality. +//===----------------------------------------------------------------------===// + +#include "sanitizer_thread_registry.h" + +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) { + name[0] = '\0'; +} + +ThreadContextBase::~ThreadContextBase() { + // ThreadContextBase should never be deleted. + CHECK(0); +} + +void ThreadContextBase::SetName(const char *new_name) { + name[0] = '\0'; + if (new_name) { + internal_strncpy(name, new_name, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + } +} + +void ThreadContextBase::SetDead() { + CHECK(status == ThreadStatusRunning || + status == ThreadStatusFinished); + status = ThreadStatusDead; + user_id = 0; + OnDead(); +} + +void ThreadContextBase::SetJoined(void *arg) { + // FIXME(dvyukov): print message and continue (it's user error). + CHECK_EQ(false, detached); + CHECK_EQ(ThreadStatusFinished, status); + status = ThreadStatusDead; + user_id = 0; + OnJoined(arg); +} + +void ThreadContextBase::SetFinished() { + if (!detached) + status = ThreadStatusFinished; + OnFinished(); +} + +void ThreadContextBase::SetStarted(uptr _os_id, void *arg) { + status = ThreadStatusRunning; + os_id = _os_id; + OnStarted(arg); +} + +void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id, + bool _detached, u32 _parent_tid, void *arg) { + status = ThreadStatusCreated; + user_id = _user_id; + unique_id = _unique_id; + detached = _detached; + // Parent tid makes no sense for the main thread. + if (tid != 0) + parent_tid = _parent_tid; + OnCreated(arg); +} + +void ThreadContextBase::Reset() { + status = ThreadStatusInvalid; + reuse_count++; + SetName(0); + OnReset(); +} + +// ThreadRegistry implementation. + +const u32 ThreadRegistry::kUnknownTid = ~0U; + +ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads, + u32 thread_quarantine_size) + : context_factory_(factory), + max_threads_(max_threads), + thread_quarantine_size_(thread_quarantine_size), + mtx_(), + n_contexts_(0), + total_threads_(0), + alive_threads_(0), + max_alive_threads_(0), + running_threads_(0) { + threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]), + "ThreadRegistry"); + dead_threads_.clear(); + invalid_threads_.clear(); +} + +void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running, + uptr *alive) { + BlockingMutexLock l(&mtx_); + if (total) *total = n_contexts_; + if (running) *running = running_threads_; + if (alive) *alive = alive_threads_; +} + +uptr ThreadRegistry::GetMaxAliveThreads() { + BlockingMutexLock l(&mtx_); + return max_alive_threads_; +} + +u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid, + void *arg) { + BlockingMutexLock l(&mtx_); + u32 tid = kUnknownTid; + ThreadContextBase *tctx = QuarantinePop(); + if (tctx) { + tid = tctx->tid; + } else if (n_contexts_ < max_threads_) { + // Allocate new thread context and tid. + tid = n_contexts_++; + tctx = context_factory_(tid); + threads_[tid] = tctx; + } else { + Report("%s: Thread limit (%u threads) exceeded. Dying.\n", + SanitizerToolName, max_threads_); + Die(); + } + CHECK_NE(tctx, 0); + CHECK_NE(tid, kUnknownTid); + CHECK_LT(tid, max_threads_); + CHECK_EQ(tctx->status, ThreadStatusInvalid); + alive_threads_++; + if (max_alive_threads_ < alive_threads_) { + max_alive_threads_++; + CHECK_EQ(alive_threads_, max_alive_threads_); + } + tctx->SetCreated(user_id, total_threads_++, detached, + parent_tid, arg); + return tid; +} + +void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb, + void *arg) { + CheckLocked(); + for (u32 tid = 0; tid < n_contexts_; tid++) { + ThreadContextBase *tctx = threads_[tid]; + if (tctx == 0) + continue; + cb(tctx, arg); + } +} + +u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) { + BlockingMutexLock l(&mtx_); + for (u32 tid = 0; tid < n_contexts_; tid++) { + ThreadContextBase *tctx = threads_[tid]; + if (tctx != 0 && cb(tctx, arg)) + return tctx->tid; + } + return kUnknownTid; +} + +ThreadContextBase * +ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) { + CheckLocked(); + for (u32 tid = 0; tid < n_contexts_; tid++) { + ThreadContextBase *tctx = threads_[tid]; + if (tctx != 0 && cb(tctx, arg)) + return tctx; + } + return 0; +} + +static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx, + void *arg) { + return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid && + tctx->status != ThreadStatusDead); +} + +ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(uptr os_id) { + return FindThreadContextLocked(FindThreadContextByOsIdCallback, + (void *)os_id); +} + +void ThreadRegistry::SetThreadName(u32 tid, const char *name) { + BlockingMutexLock l(&mtx_); + CHECK_LT(tid, n_contexts_); + ThreadContextBase *tctx = threads_[tid]; + CHECK_NE(tctx, 0); + CHECK_EQ(ThreadStatusRunning, tctx->status); + tctx->SetName(name); +} + +void ThreadRegistry::DetachThread(u32 tid) { + BlockingMutexLock l(&mtx_); + CHECK_LT(tid, n_contexts_); + ThreadContextBase *tctx = threads_[tid]; + CHECK_NE(tctx, 0); + if (tctx->status == ThreadStatusInvalid) { + Report("%s: Detach of non-existent thread\n", SanitizerToolName); + return; + } + if (tctx->status == ThreadStatusFinished) { + tctx->SetDead(); + QuarantinePush(tctx); + } else { + tctx->detached = true; + } +} + +void ThreadRegistry::JoinThread(u32 tid, void *arg) { + BlockingMutexLock l(&mtx_); + CHECK_LT(tid, n_contexts_); + ThreadContextBase *tctx = threads_[tid]; + CHECK_NE(tctx, 0); + if (tctx->status == ThreadStatusInvalid) { + Report("%s: Join of non-existent thread\n", SanitizerToolName); + return; + } + tctx->SetJoined(arg); + QuarantinePush(tctx); +} + +void ThreadRegistry::FinishThread(u32 tid) { + BlockingMutexLock l(&mtx_); + CHECK_GT(alive_threads_, 0); + alive_threads_--; + CHECK_GT(running_threads_, 0); + running_threads_--; + CHECK_LT(tid, n_contexts_); + ThreadContextBase *tctx = threads_[tid]; + CHECK_NE(tctx, 0); + CHECK_EQ(ThreadStatusRunning, tctx->status); + tctx->SetFinished(); + if (tctx->detached) { + tctx->SetDead(); + QuarantinePush(tctx); + } +} + +void ThreadRegistry::StartThread(u32 tid, uptr os_id, void *arg) { + BlockingMutexLock l(&mtx_); + running_threads_++; + CHECK_LT(tid, n_contexts_); + ThreadContextBase *tctx = threads_[tid]; + CHECK_NE(tctx, 0); + CHECK_EQ(ThreadStatusCreated, tctx->status); + tctx->SetStarted(os_id, arg); +} + +void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) { + dead_threads_.push_back(tctx); + if (dead_threads_.size() <= thread_quarantine_size_) + return; + tctx = dead_threads_.front(); + dead_threads_.pop_front(); + CHECK_EQ(tctx->status, ThreadStatusDead); + tctx->Reset(); + invalid_threads_.push_back(tctx); +} + +ThreadContextBase *ThreadRegistry::QuarantinePop() { + if (invalid_threads_.size() == 0) + return 0; + ThreadContextBase *tctx = invalid_threads_.front(); + invalid_threads_.pop_front(); + return tctx; +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_registry.h b/libsanitizer/sanitizer_common/sanitizer_thread_registry.h new file mode 100644 index 00000000000..1ae47b800ca --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_thread_registry.h @@ -0,0 +1,142 @@ +//===-- sanitizer_thread_registry.h -----------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between sanitizer tools. +// +// General thread bookkeeping functionality. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_THREAD_REGISTRY_H +#define SANITIZER_THREAD_REGISTRY_H + +#include "sanitizer_common.h" +#include "sanitizer_list.h" +#include "sanitizer_mutex.h" + +namespace __sanitizer { + +enum ThreadStatus { + ThreadStatusInvalid, // Non-existent thread, data is invalid. + ThreadStatusCreated, // Created but not yet running. + ThreadStatusRunning, // The thread is currently running. + ThreadStatusFinished, // Joinable thread is finished but not yet joined. + ThreadStatusDead // Joined, but some info is still available. +}; + +// Generic thread context. Specific sanitizer tools may inherit from it. +// If thread is dead, context may optionally be reused for a new thread. +class ThreadContextBase { + public: + explicit ThreadContextBase(u32 tid); + ~ThreadContextBase(); // Should never be called. + + const u32 tid; // Thread ID. Main thread should have tid = 0. + u64 unique_id; // Unique thread ID. + 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. + + void SetName(const char *new_name); + + void SetDead(); + void SetJoined(void *arg); + void SetFinished(); + void SetStarted(uptr _os_id, void *arg); + void SetCreated(uptr _user_id, u64 _unique_id, bool _detached, + u32 _parent_tid, void *arg); + void Reset(); + + // The following methods may be overriden by subclasses. + // Some of them take opaque arg that may be optionally be used + // by subclasses. + virtual void OnDead() {} + virtual void OnJoined(void *arg) {} + virtual void OnFinished() {} + virtual void OnStarted(void *arg) {} + virtual void OnCreated(void *arg) {} + virtual void OnReset() {} +}; + +typedef ThreadContextBase* (*ThreadContextFactory)(u32 tid); + +class ThreadRegistry { + public: + 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); + uptr GetMaxAliveThreads(); + + void Lock() { mtx_.Lock(); } + void CheckLocked() { mtx_.CheckLocked(); } + void Unlock() { mtx_.Unlock(); } + + // Should be guarded by ThreadRegistryLock. + ThreadContextBase *GetThreadLocked(u32 tid) { + DCHECK_LT(tid, n_contexts_); + return threads_[tid]; + } + + u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg); + + typedef void (*ThreadCallback)(ThreadContextBase *tctx, void *arg); + // Invokes callback with a specified arg for each thread context. + // Should be guarded by ThreadRegistryLock. + void RunCallbackForEachThreadLocked(ThreadCallback cb, void *arg); + + typedef bool (*FindThreadCallback)(ThreadContextBase *tctx, void *arg); + // Finds a thread using the provided callback. Returns kUnknownTid if no + // thread is found. + u32 FindThread(FindThreadCallback cb, void *arg); + // Should be guarded by ThreadRegistryLock. Return 0 if no thread + // is found. + ThreadContextBase *FindThreadContextLocked(FindThreadCallback cb, + void *arg); + ThreadContextBase *FindThreadContextByOsIDLocked(uptr os_id); + + void SetThreadName(u32 tid, const char *name); + void DetachThread(u32 tid); + void JoinThread(u32 tid, void *arg); + void FinishThread(u32 tid); + void StartThread(u32 tid, uptr os_id, void *arg); + + private: + const ThreadContextFactory context_factory_; + const u32 max_threads_; + const u32 thread_quarantine_size_; + + BlockingMutex mtx_; + + u32 n_contexts_; // Number of created thread contexts, + // at most max_threads_. + u64 total_threads_; // Total number of created threads. May be greater than + // max_threads_ if contexts were reused. + uptr alive_threads_; // Created or running. + uptr max_alive_threads_; + uptr running_threads_; + + ThreadContextBase **threads_; // Array of thread contexts is leaked. + IntrusiveList dead_threads_; + IntrusiveList invalid_threads_; + + void QuarantinePush(ThreadContextBase *tctx); + ThreadContextBase *QuarantinePop(); +}; + +typedef GenericScopedLock ThreadRegistryLock; + +} // namespace __sanitizer + +#endif // SANITIZER_THREAD_REGISTRY_H diff --git a/libsanitizer/sanitizer_common/sanitizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_win.cc index 695265594b3..0a5fe81ae45 100644 --- a/libsanitizer/sanitizer_common/sanitizer_win.cc +++ b/libsanitizer/sanitizer_common/sanitizer_win.cc @@ -9,7 +9,10 @@ // run-time libraries and implements windows-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// -#ifdef _WIN32 + +#include "sanitizer_platform.h" +#if SANITIZER_WINDOWS + #define WIN32_LEAN_AND_MEAN #define NOGDI #include @@ -18,11 +21,14 @@ #include "sanitizer_common.h" #include "sanitizer_libc.h" -#include "sanitizer_placement_new.h" #include "sanitizer_mutex.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_stacktrace.h" namespace __sanitizer { +#include "sanitizer_syscall_generic.inc" + // --------------------- sanitizer_common.h uptr GetPageSize() { return 1U << 14; // FIXME: is this configurable? @@ -32,18 +38,30 @@ uptr GetMmapGranularity() { return 1U << 16; // FIXME: is this configurable? } +uptr GetMaxVirtualAddress() { + SYSTEM_INFO si; + GetSystemInfo(&si); + return (uptr)si.lpMaximumApplicationAddress; +} + bool FileExists(const char *filename) { UNIMPLEMENTED(); } -int GetPid() { +uptr internal_getpid() { return GetProcessId(GetCurrentProcess()); } -uptr GetThreadSelf() { +// In contrast to POSIX, on Windows GetCurrentThreadId() +// returns a system-unique identifier. +uptr GetTid() { return GetCurrentThreadId(); } +uptr GetThreadSelf() { + return GetTid(); +} + void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom) { CHECK(stack_top); @@ -109,19 +127,38 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) { UNIMPLEMENTED(); } +static const int kMaxEnvNameLength = 128; +static const DWORD kMaxEnvValueLength = 32767; + +namespace { + +struct EnvVariable { + char name[kMaxEnvNameLength]; + char value[kMaxEnvValueLength]; +}; + +} // namespace + +static const int kEnvVariables = 5; +static EnvVariable env_vars[kEnvVariables]; +static int num_env_vars; + const char *GetEnv(const char *name) { - static char env_buffer[32767] = {}; - - // Note: this implementation stores the result in a static buffer so we only - // allow it to be called just once. - static bool called_once = false; - if (called_once) - UNIMPLEMENTED(); - called_once = true; - - DWORD rv = GetEnvironmentVariableA(name, env_buffer, sizeof(env_buffer)); - if (rv > 0 && rv < sizeof(env_buffer)) - return env_buffer; + // Note: this implementation caches the values of the environment variables + // and limits their quantity. + for (int i = 0; i < num_env_vars; i++) { + if (0 == internal_strcmp(name, env_vars[i].name)) + return env_vars[i].value; + } + CHECK_LT(num_env_vars, kEnvVariables); + DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value, + kMaxEnvValueLength); + if (rv > 0 && rv < kMaxEnvValueLength) { + CHECK_LT(internal_strlen(name), kMaxEnvNameLength); + internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength); + num_env_vars++; + return env_vars[num_env_vars - 1].value; + } return 0; } @@ -157,6 +194,11 @@ void SetStackSizeLimitInBytes(uptr limit) { UNIMPLEMENTED(); } +char *FindPathToBinary(const char *name) { + // Nothing here for now. + return 0; +} + void SleepForSeconds(int seconds) { Sleep(seconds * 1000); } @@ -165,11 +207,20 @@ void SleepForMillis(int millis) { Sleep(millis); } +u64 NanoTime() { + return 0; +} + void Abort() { abort(); _exit(-1); // abort is not NORETURN on Windows. } +uptr GetListOfModules(LoadedModule *modules, uptr max_modules, + string_predicate_t filter) { + UNIMPLEMENTED(); +}; + #ifndef SANITIZER_GO int Atexit(void (*function)(void)) { return atexit(function); @@ -177,16 +228,16 @@ int Atexit(void (*function)(void)) { #endif // ------------------ sanitizer_libc.h -void *internal_mmap(void *addr, uptr length, int prot, int flags, - int fd, u64 offset) { +uptr internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, u64 offset) { UNIMPLEMENTED(); } -int internal_munmap(void *addr, uptr length) { +uptr internal_munmap(void *addr, uptr length) { UNIMPLEMENTED(); } -int internal_close(fd_t fd) { +uptr internal_close(fd_t fd) { UNIMPLEMENTED(); } @@ -194,15 +245,15 @@ int internal_isatty(fd_t fd) { return _isatty(fd); } -fd_t internal_open(const char *filename, int flags) { +uptr internal_open(const char *filename, int flags) { UNIMPLEMENTED(); } -fd_t internal_open(const char *filename, int flags, u32 mode) { +uptr internal_open(const char *filename, int flags, u32 mode) { UNIMPLEMENTED(); } -fd_t OpenFile(const char *filename, bool write) { +uptr OpenFile(const char *filename, bool write) { UNIMPLEMENTED(); } @@ -222,15 +273,15 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { return ret; } -int internal_stat(const char *path, void *buf) { +uptr internal_stat(const char *path, void *buf) { UNIMPLEMENTED(); } -int internal_lstat(const char *path, void *buf) { +uptr internal_lstat(const char *path, void *buf) { UNIMPLEMENTED(); } -int internal_fstat(fd_t fd, void *buf) { +uptr internal_fstat(fd_t fd, void *buf) { UNIMPLEMENTED(); } @@ -238,7 +289,7 @@ uptr internal_filesize(fd_t fd) { UNIMPLEMENTED(); } -int internal_dup2(int oldfd, int newfd) { +uptr internal_dup2(int oldfd, int newfd) { UNIMPLEMENTED(); } @@ -246,7 +297,7 @@ uptr internal_readlink(const char *path, char *buf, uptr bufsize) { UNIMPLEMENTED(); } -int internal_sched_yield() { +uptr internal_sched_yield() { Sleep(0); return 0; } @@ -268,6 +319,12 @@ BlockingMutex::BlockingMutex(LinkerInitialized li) { owner_ = LOCK_READY; } +BlockingMutex::BlockingMutex() { + CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); + InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); + owner_ = LOCK_READY; +} + void BlockingMutex::Lock() { if (owner_ == LOCK_UNINITIALIZED) { // FIXME: hm, global BlockingMutex objects are not initialized?!? @@ -289,6 +346,79 @@ void BlockingMutex::Unlock() { LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); } +void BlockingMutex::CheckLocked() { + CHECK_EQ(owner_, GetThreadSelf()); +} + +uptr GetTlsSize() { + return 0; +} + +void InitTlsSize() { +} + +void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, + uptr *tls_addr, uptr *tls_size) { +#ifdef SANITIZER_GO + *stk_addr = 0; + *stk_size = 0; + *tls_addr = 0; + *tls_size = 0; +#else + uptr stack_top, stack_bottom; + GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); + *stk_addr = stack_bottom; + *stk_size = stack_top - stack_bottom; + *tls_addr = 0; + *tls_size = 0; +#endif +} + +void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, + uptr stack_top, uptr stack_bottom, bool fast) { + (void)fast; + (void)stack_top; + (void)stack_bottom; + stack->max_size = max_s; + void *tmp[kStackTraceMax]; + + // FIXME: CaptureStackBackTrace might be too slow for us. + // FIXME: Compare with StackWalk64. + // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc + uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0); + uptr offset = 0; + // Skip the RTL frames by searching for the PC in the stacktrace. + // FIXME: this doesn't work well for the malloc/free stacks yet. + for (uptr i = 0; i < cs_ret; i++) { + if (pc != (uptr)tmp[i]) + continue; + offset = i; + break; + } + + stack->size = cs_ret - offset; + for (uptr i = 0; i < stack->size; i++) + stack->trace[i] = (uptr)tmp[i + offset]; +} + +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 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)) { + // 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. + OutputDebugStringA(buffer); + } +} + } // namespace __sanitizer #endif // _WIN32 diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h index b2937a428f3..68936e02e4b 100644 --- a/libsanitizer/tsan/tsan_defs.h +++ b/libsanitizer/tsan/tsan_defs.h @@ -154,7 +154,6 @@ struct MD5Hash { MD5Hash md5_hash(const void *data, uptr size); struct ThreadState; -struct ThreadContext; struct Context; struct ReportStack; class ReportDesc; diff --git a/libsanitizer/tsan/tsan_fd.cc b/libsanitizer/tsan/tsan_fd.cc index a75d9bde08a..de852b185a6 100644 --- a/libsanitizer/tsan/tsan_fd.cc +++ b/libsanitizer/tsan/tsan_fd.cc @@ -72,13 +72,14 @@ static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) { uptr l1 = atomic_load(pl1, memory_order_consume); if (l1 == 0) { uptr size = kTableSizeL2 * sizeof(FdDesc); - void *p = internal_alloc(MBlockFD, size); + // We need this to reside in user memory to properly catch races on it. + void *p = user_alloc(thr, pc, size); 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 - internal_free(p); + user_free(thr, pc, p); } return &((FdDesc*)l1)[fd % kTableSizeL2]; // NOLINT } @@ -157,9 +158,9 @@ void FdRelease(ThreadState *thr, uptr pc, int fd) { FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); + MemoryRead(thr, pc, (uptr)d, kSizeLog8); if (s) Release(thr, pc, (uptr)s); - MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdAccess(ThreadState *thr, uptr pc, int fd) { diff --git a/libsanitizer/tsan/tsan_flags.cc b/libsanitizer/tsan/tsan_flags.cc index ae748a13e15..dbde4212c59 100644 --- a/libsanitizer/tsan/tsan_flags.cc +++ b/libsanitizer/tsan/tsan_flags.cc @@ -47,17 +47,22 @@ void InitializeFlags(Flags *f, const char *env) { f->force_seq_cst_atomics = false; f->strip_path_prefix = ""; f->suppressions = ""; + f->print_suppressions = false; + f->print_benign = false; f->exitcode = 66; + f->halt_on_error = false; f->log_path = "stderr"; f->atexit_sleep_ms = 1000; f->verbosity = 0; f->profile_memory = ""; f->flush_memory_ms = 0; + f->flush_symbolizer_ms = 5000; f->stop_on_start = false; f->running_on_valgrind = false; f->external_symbolizer_path = ""; f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go. f->io_sync = 1; + f->allocator_may_return_null = false; // Let a frontend override. OverrideFlags(f); @@ -75,16 +80,21 @@ void InitializeFlags(Flags *f, const char *env) { ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); ParseFlag(env, &f->strip_path_prefix, "strip_path_prefix"); 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->log_path, "log_path"); ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); ParseFlag(env, &f->verbosity, "verbosity"); 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->stop_on_start, "stop_on_start"); ParseFlag(env, &f->external_symbolizer_path, "external_symbolizer_path"); ParseFlag(env, &f->history_size, "history_size"); ParseFlag(env, &f->io_sync, "io_sync"); + ParseFlag(env, &f->allocator_may_return_null, "allocator_may_return_null"); if (!f->report_bugs) { f->report_thread_leaks = false; @@ -103,6 +113,8 @@ void InitializeFlags(Flags *f, const char *env) { " (must be [0..2])\n"); Die(); } + + common_flags()->allocator_may_return_null = f->allocator_may_return_null; } } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_flags.h b/libsanitizer/tsan/tsan_flags.h index 480b41538f9..a7571c92b5e 100644 --- a/libsanitizer/tsan/tsan_flags.h +++ b/libsanitizer/tsan/tsan_flags.h @@ -50,8 +50,14 @@ struct Flags { const char *strip_path_prefix; // 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; // Write logs to "log_path.pid". // The special values are "stdout" and "stderr". // The default is "stderr". @@ -65,6 +71,8 @@ struct Flags { const char *profile_memory; // Flush shadow memory every X ms. int flush_memory_ms; + // Flush symbolizer caches every X ms. + int flush_symbolizer_ms; // Stops on start until __tsan_resume() is called (for debugging). bool stop_on_start; // Controls whether RunningOnValgrind() returns true or false. @@ -82,6 +90,8 @@ struct Flags { // 1 - reasonable level of synchronization (write->read) // 2 - global synchronization of all IO operations int io_sync; + // If false, the allocator will crash instead of returning 0 on out-of-memory. + bool allocator_may_return_null; }; Flags *flags(); diff --git a/libsanitizer/tsan/tsan_interceptors.cc b/libsanitizer/tsan/tsan_interceptors.cc index 8a54511b6e0..eaaf9e3ebf6 100644 --- a/libsanitizer/tsan/tsan_interceptors.cc +++ b/libsanitizer/tsan/tsan_interceptors.cc @@ -8,11 +8,13 @@ // This file is a part of ThreadSanitizer (TSan), a race detector. // // FIXME: move as many interceptors as possible into -// sanitizer_common/sanitizer_common_interceptors.h +// sanitizer_common/sanitizer_common_interceptors.inc //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_linux.h" +#include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "interception/interception.h" @@ -24,18 +26,16 @@ using namespace __tsan; // NOLINT -const int kSigCount = 128; +const int kSigCount = 64; struct my_siginfo_t { - int opaque[128]; -}; - -struct sigset_t { - u64 val[1024 / 8 / sizeof(u64)]; + // The size is determined by looking at sizeof of real siginfo_t on linux. + u64 opaque[128 / sizeof(u64)]; }; struct ucontext_t { - uptr opaque[117]; + // The size is determined by looking at sizeof of real ucontext_t on linux. + u64 opaque[936 / sizeof(u64) + 1]; }; extern "C" int pthread_attr_init(void *attr); @@ -47,8 +47,10 @@ 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(); -extern "C" int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset); -extern "C" int sigfillset(sigset_t *set); +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) extern "C" void *pthread_self(); extern "C" void _exit(int status); extern "C" int *__errno_location(); @@ -57,9 +59,9 @@ 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 mallopt(int param, int value); const int PTHREAD_MUTEX_RECURSIVE = 1; const int PTHREAD_MUTEX_RECURSIVE_NP = 1; -const int kPthreadAttrSize = 56; const int EINVAL = 22; const int EBUSY = 16; const int EPOLL_CTL_ADD = 1; @@ -69,6 +71,7 @@ const int SIGFPE = 8; const int SIGSEGV = 11; const int SIGPIPE = 13; const int SIGBUS = 7; +const int SIGSYS = 31; void *const MAP_FAILED = (void*)-1; const int PTHREAD_BARRIER_SERIAL_THREAD = -1; const int MAP_FIXED = 0x10; @@ -84,17 +87,12 @@ typedef void (*sighandler_t)(int sig); #define errno (*__errno_location()) -union pthread_attr_t { - char size[kPthreadAttrSize]; - void *align; -}; - struct sigaction_t { union { sighandler_t sa_handler; void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx); }; - sigset_t sa_mask; + __sanitizer_sigset_t sa_mask; int sa_flags; void (*sa_restorer)(); }; @@ -125,6 +123,21 @@ struct SignalContext { int pending_signal_count; SignalDesc pending_signals[kSigCount]; }; + +// Used to ignore interceptors coming directly from libjvm.so. +atomic_uintptr_t libjvm_begin; +atomic_uintptr_t libjvm_end; + +static bool libjvm_check(uptr pc) { + uptr begin = atomic_load(&libjvm_begin, memory_order_relaxed); + if (begin != 0 && pc >= begin) { + uptr end = atomic_load(&libjvm_end, memory_order_relaxed); + if (end != 0 && pc < end) + return true; + } + return false; +} + } // namespace __tsan static SignalContext *SigCtx(ThreadState *thr) { @@ -178,8 +191,7 @@ ScopedInterceptor::~ScopedInterceptor() { StatInc(thr, StatInt_##func); \ const uptr caller_pc = GET_CALLER_PC(); \ ScopedInterceptor si(thr, #func, caller_pc); \ - const uptr pc = __sanitizer::StackTrace::GetPreviousInstructionPc( \ - __sanitizer::StackTrace::GetCurrentPc()); \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ (void)pc; \ /**/ @@ -189,7 +201,7 @@ ScopedInterceptor::~ScopedInterceptor() { Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ Die(); \ } \ - if (thr->in_rtl > 1) \ + if (thr->in_rtl > 1 || libjvm_check(pc)) \ return REAL(func)(__VA_ARGS__); \ /**/ @@ -293,15 +305,6 @@ class AtExitContext { static AtExitContext *atexit_ctx; -static void finalize(void *arg) { - ThreadState * thr = cur_thread(); - uptr pc = 0; - atexit_ctx->exit(thr, pc); - int status = Finalize(cur_thread()); - if (status) - _exit(status); -} - TSAN_INTERCEPTOR(int, atexit, void (*f)()) { if (cur_thread()->in_symbolizer) return 0; @@ -320,25 +323,123 @@ TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { if (cur_thread()->in_symbolizer) return 0; SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso); - if (dso) - return REAL(__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); + int res = REAL(__cxa_atexit)(f, arg, dso); + ThreadIgnoreEnd(thr); + return res; + } return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg); } -TSAN_INTERCEPTOR(void, longjmp, void *env, int val) { - SCOPED_TSAN_INTERCEPTOR(longjmp, env, val); - Printf("ThreadSanitizer: longjmp() is not supported\n"); - Die(); +// Cleanup old bufs. +static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) { + for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) { + JmpBuf *buf = &thr->jmp_bufs[i]; + if (buf->sp <= sp) { + uptr sz = thr->jmp_bufs.Size(); + thr->jmp_bufs[i] = thr->jmp_bufs[sz - 1]; + thr->jmp_bufs.PopBack(); + i--; + } + } } -TSAN_INTERCEPTOR(void, siglongjmp, void *env, int val) { - SCOPED_TSAN_INTERCEPTOR(siglongjmp, env, val); - Printf("ThreadSanitizer: siglongjmp() is not supported\n"); - Die(); +static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) { + if (thr->shadow_stack_pos == 0) // called from libc guts during bootstrap + return; + // Cleanup old bufs. + JmpBufGarbageCollect(thr, sp); + // Remember the buf. + JmpBuf *buf = thr->jmp_bufs.PushBack(); + buf->sp = sp; + buf->mangled_sp = mangled_sp; + buf->shadow_stack_pos = thr->shadow_stack_pos; +} + +static void LongJmp(ThreadState *thr, uptr *env) { + uptr mangled_sp = env[6]; + // Find the saved buf by mangled_sp. + for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) { + JmpBuf *buf = &thr->jmp_bufs[i]; + if (buf->mangled_sp == mangled_sp) { + CHECK_GE(thr->shadow_stack_pos, buf->shadow_stack_pos); + // Unwind the stack. + while (thr->shadow_stack_pos > buf->shadow_stack_pos) + FuncExit(thr); + JmpBufGarbageCollect(thr, buf->sp - 1); // do not collect buf->sp + return; + } + } + Printf("ThreadSanitizer: can't find longjmp buf\n"); + CHECK(0); +} + +// 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); +} + +// Not called. Merely to satisfy TSAN_INTERCEPT(). +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor_setjmp(void *env); +extern "C" int __interceptor_setjmp(void *env) { + CHECK(0); + return 0; +} + +// FIXME: any reason to have a separate declaration? +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor__setjmp(void *env); +extern "C" int __interceptor__setjmp(void *env) { + CHECK(0); + return 0; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor_sigsetjmp(void *env); +extern "C" int __interceptor_sigsetjmp(void *env) { + CHECK(0); + return 0; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +int __interceptor___sigsetjmp(void *env); +extern "C" int __interceptor___sigsetjmp(void *env) { + CHECK(0); + return 0; +} + +extern "C" int setjmp(void *env); +extern "C" int _setjmp(void *env); +extern "C" int sigsetjmp(void *env); +extern "C" int __sigsetjmp(void *env); +DEFINE_REAL(int, setjmp, void *env) +DEFINE_REAL(int, _setjmp, void *env) +DEFINE_REAL(int, sigsetjmp, void *env) +DEFINE_REAL(int, __sigsetjmp, void *env) + +TSAN_INTERCEPTOR(void, longjmp, uptr *env, int val) { + { + SCOPED_TSAN_INTERCEPTOR(longjmp, env, val); + } + LongJmp(cur_thread(), env); + REAL(longjmp)(env, val); +} + +TSAN_INTERCEPTOR(void, siglongjmp, uptr *env, int val) { + { + SCOPED_TSAN_INTERCEPTOR(siglongjmp, env, val); + } + LongJmp(cur_thread(), env); + REAL(siglongjmp)(env, val); } TSAN_INTERCEPTOR(void*, malloc, uptr size) { - if (cur_thread()->in_symbolizer) + if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) return __libc_malloc(size); void *p = 0; { @@ -355,21 +456,23 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { } TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { - if (cur_thread()->in_symbolizer) + if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) return __libc_calloc(size, n); - if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) return 0; + 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); + if (p) + internal_memset(p, 0, n * size); } invoke_malloc_hook(p, n * size); return p; } TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { - if (cur_thread()->in_symbolizer) + if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) return __libc_realloc(p, size); if (p) invoke_free_hook(p); @@ -384,7 +487,7 @@ TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { TSAN_INTERCEPTOR(void, free, void *p) { if (p == 0) return; - if (cur_thread()->in_symbolizer) + if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(free, p); @@ -394,15 +497,22 @@ TSAN_INTERCEPTOR(void, free, void *p) { TSAN_INTERCEPTOR(void, cfree, void *p) { if (p == 0) return; - if (cur_thread()->in_symbolizer) + if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(cfree, p); user_free(thr, pc, p); } +TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { + SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p); + if (libjvm_check(pc)) + return malloc_usable_size(p); + return user_alloc_usable_size(thr, pc, p); +} + #define OPERATOR_NEW_BODY(mangled_name) \ - if (cur_thread()->in_symbolizer) \ + if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) \ return __libc_malloc(size); \ void *p = 0; \ { \ @@ -412,36 +522,58 @@ TSAN_INTERCEPTOR(void, cfree, void *p) { 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) \ + if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) \ 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); } @@ -479,30 +611,6 @@ TSAN_INTERCEPTOR(int, memcmp, const void *s1, const void *s2, uptr n) { return res; } -TSAN_INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { - SCOPED_TSAN_INTERCEPTOR(strcmp, s1, s2); - uptr len = 0; - for (; s1[len] && s2[len]; len++) { - if (s1[len] != s2[len]) - break; - } - MemoryAccessRange(thr, pc, (uptr)s1, len + 1, false); - MemoryAccessRange(thr, pc, (uptr)s2, len + 1, false); - return s1[len] - s2[len]; -} - -TSAN_INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr n) { - SCOPED_TSAN_INTERCEPTOR(strncmp, s1, s2, n); - uptr len = 0; - for (; len < n && s1[len] && s2[len]; len++) { - if (s1[len] != s2[len]) - break; - } - MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false); - MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false); - return len == n ? 0 : s1[len] - s2[len]; -} - TSAN_INTERCEPTOR(void*, memchr, void *s, int c, uptr n) { SCOPED_TSAN_INTERCEPTOR(memchr, s, c, n); void *res = REAL(memchr)(s, c, n); @@ -572,6 +680,21 @@ TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) { return res; } +TSAN_INTERCEPTOR(char*, strdup, const char *str) { + SCOPED_TSAN_INTERCEPTOR(strdup, str); + if (libjvm_check(pc)) { + // The memory must come from libc malloc, + // and we must not instrument accesses in this case. + uptr n = internal_strlen(str) + 1; + void *p = __libc_malloc(n); + if (p == 0) + return 0; + return (char*)internal_memcpy(p, str, n); + } + // strdup will call malloc, so no instrumentation is required here. + return REAL(strdup)(str); +} + static bool fix_mmap_addr(void **addr, long_t sz, int flags) { if (*addr) { if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) { @@ -595,7 +718,7 @@ TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot, if (res != MAP_FAILED) { if (fd > 0) FdAccess(thr, pc, fd); - MemoryResetRange(thr, pc, (uptr)res, sz); + MemoryRangeImitateWrite(thr, pc, (uptr)res, sz); } return res; } @@ -609,13 +732,14 @@ TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot, if (res != MAP_FAILED) { if (fd > 0) FdAccess(thr, pc, fd); - MemoryResetRange(thr, pc, (uptr)res, sz); + MemoryRangeImitateWrite(thr, pc, (uptr)res, sz); } return res; } TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) { SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz); + DontNeedShadowFor((uptr)addr, sz); int res = REAL(munmap)(addr, sz); return res; } @@ -727,21 +851,21 @@ extern "C" void *__tsan_thread_start_func(void *arg) { TSAN_INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { SCOPED_TSAN_INTERCEPTOR(pthread_create, th, attr, callback, param); - pthread_attr_t myattr; + __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); attr = &myattr; } int detached = 0; pthread_attr_getdetachstate(attr, &detached); - uptr stacksize = 0; - pthread_attr_getstacksize(attr, &stacksize); - // We place the huge ThreadState object into TLS, account for that. - const uptr minstacksize = GetTlsSize() + 128*1024; - if (stacksize < minstacksize) { - DPrintf("ThreadSanitizer: stacksize %zu->%zu\n", stacksize, minstacksize); - pthread_attr_setstacksize(attr, minstacksize); - } + +#if defined(TSAN_DEBUG_OUTPUT) + int verbosity = (TSAN_DEBUG_OUTPUT); +#else + int verbosity = 0; +#endif + AdjustStackSizeLinux(attr, verbosity); + ThreadParam p; p.callback = callback; p.param = param; @@ -960,30 +1084,30 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) { return res; } -// libpthread.so contains several versions of pthread_cond_init symbol. -// When we just dlsym() it, we get the wrong (old) version. -/* TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a); + MemoryWrite(thr, pc, (uptr)c, kSizeLog1); int res = REAL(pthread_cond_init)(c, a); 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_signal, void *c) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, c); + MemoryRead(thr, pc, (uptr)c, kSizeLog1); int res = REAL(pthread_cond_signal)(c); return res; } TSAN_INTERCEPTOR(int, pthread_cond_broadcast, void *c) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, c); + MemoryRead(thr, pc, (uptr)c, kSizeLog1); int res = REAL(pthread_cond_broadcast)(c); return res; } @@ -991,14 +1115,17 @@ TSAN_INTERCEPTOR(int, pthread_cond_broadcast, void *c) { TSAN_INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, c, m); MutexUnlock(thr, pc, (uptr)m); + MemoryRead(thr, pc, (uptr)c, kSizeLog1); int res = REAL(pthread_cond_wait)(c, m); MutexLock(thr, pc, (uptr)m); return res; } -TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { +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; @@ -1309,22 +1436,6 @@ TSAN_INTERCEPTOR(int, listen, int fd, int backlog) { return res; } -TSAN_INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { - SCOPED_TSAN_INTERCEPTOR(accept, fd, addr, addrlen); - int fd2 = REAL(accept)(fd, addr, addrlen); - if (fd >= 0 && fd2 >= 0) - FdSocketAccept(thr, pc, fd, fd2); - return fd2; -} - -TSAN_INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { - SCOPED_TSAN_INTERCEPTOR(accept4, fd, addr, addrlen, f); - int fd2 = REAL(accept4)(fd, addr, addrlen, f); - if (fd >= 0 && fd2 >= 0) - FdSocketAccept(thr, pc, fd, fd2); - return fd2; -} - TSAN_INTERCEPTOR(int, epoll_create, int size) { SCOPED_TSAN_INTERCEPTOR(epoll_create, size); int fd = REAL(epoll_create)(size); @@ -1383,40 +1494,6 @@ TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) { return res; } -TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) { - SCOPED_TSAN_INTERCEPTOR(readv, fd, vec, cnt); - int res = REAL(readv)(fd, vec, cnt); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - -TSAN_INTERCEPTOR(long_t, preadv64, int fd, void *vec, int cnt, u64 off) { - SCOPED_TSAN_INTERCEPTOR(preadv64, fd, vec, cnt, off); - int res = REAL(preadv64)(fd, vec, cnt, off); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - -TSAN_INTERCEPTOR(long_t, writev, int fd, void *vec, int cnt) { - SCOPED_TSAN_INTERCEPTOR(writev, fd, vec, cnt); - if (fd >= 0) - FdRelease(thr, pc, fd); - int res = REAL(writev)(fd, vec, cnt); - return res; -} - -TSAN_INTERCEPTOR(long_t, pwritev64, int fd, void *vec, int cnt, u64 off) { - SCOPED_TSAN_INTERCEPTOR(pwritev64, fd, vec, cnt, off); - if (fd >= 0) - FdRelease(thr, pc, fd); - int res = REAL(pwritev64)(fd, vec, cnt, off); - return res; -} - TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags); if (fd >= 0) @@ -1442,15 +1519,6 @@ TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) { return res; } -TSAN_INTERCEPTOR(long_t, recvmsg, int fd, void *msg, int flags) { - SCOPED_TSAN_INTERCEPTOR(recvmsg, fd, msg, flags); - int res = REAL(recvmsg)(fd, msg, flags); - if (res >= 0 && fd >= 0) { - FdAcquire(thr, pc, fd); - } - return res; -} - TSAN_INTERCEPTOR(int, unlink, char *path) { SCOPED_TSAN_INTERCEPTOR(unlink, path); Release(thr, pc, File2addr(path)); @@ -1515,6 +1583,17 @@ TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { return REAL(fwrite)(p, size, nmemb, f); } +TSAN_INTERCEPTOR(int, fflush, void *stream) { + SCOPED_TSAN_INTERCEPTOR(fflush, stream); + return REAL(fflush)(stream); +} + +TSAN_INTERCEPTOR(void, abort, int fake) { + SCOPED_TSAN_INTERCEPTOR(abort, fake); + REAL(fflush)(0); + REAL(abort)(fake); +} + TSAN_INTERCEPTOR(int, puts, const char *s) { SCOPED_TSAN_INTERCEPTOR(puts, s); MemoryAccessRange(thr, pc, (uptr)s, internal_strlen(s), false); @@ -1556,26 +1635,19 @@ TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) { return res; } -TSAN_INTERCEPTOR(int, poll, void *fds, long_t nfds, int timeout) { - SCOPED_TSAN_INTERCEPTOR(poll, fds, nfds, timeout); - int res = BLOCK_REAL(poll)(fds, nfds, timeout); - return res; -} - -static void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig, +void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig, my_siginfo_t *info, void *ctx) { ThreadState *thr = cur_thread(); SignalContext *sctx = SigCtx(thr); // Don't mess with synchronous signals. if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || - sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || + 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) || // 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)) { - CHECK(thr->in_rtl == 0 || thr->in_rtl == 1); int in_rtl = thr->in_rtl; thr->in_rtl = 0; CHECK_EQ(thr->in_signal_handler, false); @@ -1621,7 +1693,7 @@ TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) { internal_memcpy(&sigactions[sig], act, sizeof(*act)); sigaction_t newact; internal_memcpy(&newact, act, sizeof(newact)); - sigfillset(&newact.sa_mask); + REAL(sigfillset)(&newact.sa_mask); if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) { if (newact.sa_flags & SA_SIGINFO) newact.sa_sigaction = rtl_sigaction; @@ -1644,6 +1716,11 @@ TSAN_INTERCEPTOR(sighandler_t, signal, int sig, sighandler_t h) { return old.sa_handler; } +TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) { + SCOPED_TSAN_INTERCEPTOR(sigsuspend, mask); + return REAL(sigsuspend)(mask); +} + TSAN_INTERCEPTOR(int, raise, int sig) { SCOPED_TSAN_INTERCEPTOR(raise, sig); SignalContext *sctx = SigCtx(thr); @@ -1661,11 +1738,11 @@ TSAN_INTERCEPTOR(int, kill, int pid, int sig) { SignalContext *sctx = SigCtx(thr); CHECK_NE(sctx, 0); int prev = sctx->int_signal_send; - if (pid == GetPid()) { + if (pid == (int)internal_getpid()) { sctx->int_signal_send = sig; } int res = REAL(kill)(pid, sig); - if (pid == GetPid()) { + if (pid == (int)internal_getpid()) { CHECK_EQ(sctx->int_signal_send, sig); sctx->int_signal_send = prev; } @@ -1694,13 +1771,30 @@ TSAN_INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { return REAL(gettimeofday)(tv, tz); } +TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service, + void *hints, void *rv) { + SCOPED_TSAN_INTERCEPTOR(getaddrinfo, node, service, hints, rv); + // We miss atomic synchronization in getaddrinfo, + // and can report false race between malloc and free + // inside of getaddrinfo. So ignore memory accesses. + ThreadIgnoreBegin(thr); + // 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); + 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; - Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n"); + if (flags()->verbosity > 0) + Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n"); } TSAN_INTERCEPTOR(int, mlock, const void *addr, uptr len) { @@ -1725,7 +1819,6 @@ TSAN_INTERCEPTOR(int, munlockall, void) { TSAN_INTERCEPTOR(int, fork, int fake) { SCOPED_TSAN_INTERCEPTOR(fork, fake); - // It's intercepted merely to process pending signals. int pid = REAL(fork)(fake); if (pid == 0) { // child @@ -1742,27 +1835,106 @@ struct TsanInterceptorContext { const uptr pc; }; -#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ - MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \ - ((TsanInterceptorContext*)ctx)->pc, \ - (uptr)ptr, size, true) -#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ - MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \ - ((TsanInterceptorContext*)ctx)->pc, \ - (uptr)ptr, size, false) -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__) \ - TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ - ctx = (void*)&_ctx; \ - (void)ctx; +#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 + +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ + MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)ptr, size, \ + true) +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + MemoryAccessRange(((TsanInterceptorContext *) ctx)->thr, \ + ((TsanInterceptorContext *) ctx)->pc, (uptr) ptr, size, \ + false) +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__); \ + TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ + ctx = (void *)&_ctx; \ + (void) ctx; #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ - FdAcquire(((TsanInterceptorContext*)ctx)->thr, pc, fd) + FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd) #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ - FdRelease(((TsanInterceptorContext*)ctx)->thr, pc, fd) + FdRelease(((TsanInterceptorContext *) ctx)->thr, pc, fd) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + FdSocketAccept(((TsanInterceptorContext *) ctx)->thr, pc, fd, newfd) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ - ThreadSetName(((TsanInterceptorContext*)ctx)->thr, name) + ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name) +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name) #include "sanitizer_common/sanitizer_common_interceptors.inc" +#define TSAN_SYSCALL() \ + ThreadState *thr = cur_thread(); \ + ScopedSyscall scoped_syscall(thr) \ +/**/ + +struct ScopedSyscall { + ThreadState *thr; + + explicit ScopedSyscall(ThreadState *thr) + : thr(thr) { + if (thr->in_rtl == 0) + Initialize(thr); + thr->in_rtl++; + } + + ~ScopedSyscall() { + thr->in_rtl--; + if (thr->in_rtl == 0) + ProcessPendingSignals(thr); + } +}; + +static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) { + TSAN_SYSCALL(); + MemoryAccessRange(thr, pc, p, s, write); +} + +static void syscall_fd_close(uptr pc, int fd) { + TSAN_SYSCALL(); + if (fd >= 0) + FdClose(thr, pc, fd); +} + +static void syscall_pre_fork(uptr pc) { + TSAN_SYSCALL(); +} + +static void syscall_post_fork(uptr pc, int res) { + TSAN_SYSCALL(); + if (res == 0) { + // child + FdOnFork(thr, pc); + } else if (res > 0) { + // parent + } +} + +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \ + syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ + syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ + do { } while (false) +#define COMMON_SYSCALL_FD_CLOSE(fd) \ + syscall_fd_close(GET_CALLER_PC(), fd) +#define COMMON_SYSCALL_PRE_FORK() \ + syscall_pre_fork(GET_CALLER_PC()) +#define COMMON_SYSCALL_POST_FORK(res) \ + syscall_post_fork(GET_CALLER_PC(), res) +#include "sanitizer_common/sanitizer_common_syscalls.inc" + namespace __tsan { void ProcessPendingSignals(ThreadState *thr) { @@ -1774,8 +1946,8 @@ void ProcessPendingSignals(ThreadState *thr) { thr->in_signal_handler = true; sctx->pending_signal_count = 0; // These are too big for stack. - static THREADLOCAL sigset_t emptyset, oldset; - sigfillset(&emptyset); + 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]; @@ -1796,8 +1968,9 @@ void ProcessPendingSignals(ThreadState *thr) { 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); - Lock l(&ctx->thread_mtx); + ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeErrnoInSignal); if (!IsFiredSuppression(ctx, rep, stack)) { rep.AddStack(&stack); @@ -1813,6 +1986,16 @@ void ProcessPendingSignals(ThreadState *thr) { thr->in_signal_handler = false; } +static void finalize(void *arg) { + ThreadState * thr = cur_thread(); + uptr pc = 0; + atexit_ctx->exit(thr, pc); + int status = Finalize(cur_thread()); + REAL(fflush)(0); + if (status) + _exit(status); +} + static void unreachable() { Printf("FATAL: ThreadSanitizer: unreachable called\n"); Die(); @@ -1826,8 +2009,16 @@ void InitializeInterceptors() { REAL(memcpy) = internal_memcpy; REAL(memcmp) = internal_memcmp; + // Instruct libc malloc to consume less memory. + mallopt(1, 0); // M_MXFAST + mallopt(-3, 32*1024); // M_MMAP_THRESHOLD + SANITIZER_COMMON_INTERCEPTORS_INIT; + TSAN_INTERCEPT(setjmp); + TSAN_INTERCEPT(_setjmp); + TSAN_INTERCEPT(sigsetjmp); + TSAN_INTERCEPT(__sigsetjmp); TSAN_INTERCEPT(longjmp); TSAN_INTERCEPT(siglongjmp); @@ -1848,7 +2039,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(strlen); TSAN_INTERCEPT(memset); TSAN_INTERCEPT(memcpy); - TSAN_INTERCEPT(strcmp); TSAN_INTERCEPT(memchr); TSAN_INTERCEPT(memrchr); TSAN_INTERCEPT(memmove); @@ -1856,10 +2046,10 @@ void InitializeInterceptors() { TSAN_INTERCEPT(strchr); TSAN_INTERCEPT(strchrnul); TSAN_INTERCEPT(strrchr); - TSAN_INTERCEPT(strncmp); TSAN_INTERCEPT(strcpy); // NOLINT TSAN_INTERCEPT(strncpy); TSAN_INTERCEPT(strstr); + TSAN_INTERCEPT(strdup); TSAN_INTERCEPT(pthread_create); TSAN_INTERCEPT(pthread_join); @@ -1888,12 +2078,12 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_rwlock_timedwrlock); TSAN_INTERCEPT(pthread_rwlock_unlock); - // TSAN_INTERCEPT(pthread_cond_init); - TSAN_INTERCEPT(pthread_cond_destroy); - TSAN_INTERCEPT(pthread_cond_signal); - TSAN_INTERCEPT(pthread_cond_broadcast); - TSAN_INTERCEPT(pthread_cond_wait); - TSAN_INTERCEPT(pthread_cond_timedwait); + INTERCEPT_FUNCTION_VER(pthread_cond_init, GLIBC_2.3.2); + INTERCEPT_FUNCTION_VER(pthread_cond_destroy, GLIBC_2.3.2); + INTERCEPT_FUNCTION_VER(pthread_cond_signal, GLIBC_2.3.2); + INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, GLIBC_2.3.2); + INTERCEPT_FUNCTION_VER(pthread_cond_wait, GLIBC_2.3.2); + INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, GLIBC_2.3.2); TSAN_INTERCEPT(pthread_barrier_init); TSAN_INTERCEPT(pthread_barrier_destroy); @@ -1937,8 +2127,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(connect); TSAN_INTERCEPT(bind); TSAN_INTERCEPT(listen); - TSAN_INTERCEPT(accept); - TSAN_INTERCEPT(accept4); TSAN_INTERCEPT(epoll_create); TSAN_INTERCEPT(epoll_create1); TSAN_INTERCEPT(close); @@ -1947,14 +2135,9 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pipe); TSAN_INTERCEPT(pipe2); - TSAN_INTERCEPT(readv); - TSAN_INTERCEPT(preadv64); - TSAN_INTERCEPT(writev); - TSAN_INTERCEPT(pwritev64); TSAN_INTERCEPT(send); TSAN_INTERCEPT(sendmsg); TSAN_INTERCEPT(recv); - TSAN_INTERCEPT(recvmsg); TSAN_INTERCEPT(unlink); TSAN_INTERCEPT(fopen); @@ -1962,16 +2145,18 @@ void InitializeInterceptors() { TSAN_INTERCEPT(fclose); TSAN_INTERCEPT(fread); TSAN_INTERCEPT(fwrite); + TSAN_INTERCEPT(fflush); + TSAN_INTERCEPT(abort); TSAN_INTERCEPT(puts); TSAN_INTERCEPT(rmdir); TSAN_INTERCEPT(opendir); TSAN_INTERCEPT(epoll_ctl); TSAN_INTERCEPT(epoll_wait); - TSAN_INTERCEPT(poll); TSAN_INTERCEPT(sigaction); TSAN_INTERCEPT(signal); + TSAN_INTERCEPT(sigsuspend); TSAN_INTERCEPT(raise); TSAN_INTERCEPT(kill); TSAN_INTERCEPT(pthread_kill); @@ -1979,6 +2164,7 @@ void InitializeInterceptors() { TSAN_INTERCEPT(usleep); TSAN_INTERCEPT(nanosleep); TSAN_INTERCEPT(gettimeofday); + TSAN_INTERCEPT(getaddrinfo); TSAN_INTERCEPT(mlock); TSAN_INTERCEPT(munlock); diff --git a/libsanitizer/tsan/tsan_interface.cc b/libsanitizer/tsan/tsan_interface.cc index 992e3834aae..e056bd465bf 100644 --- a/libsanitizer/tsan/tsan_interface.cc +++ b/libsanitizer/tsan/tsan_interface.cc @@ -12,11 +12,16 @@ #include "tsan_interface.h" #include "tsan_interface_ann.h" #include "tsan_rtl.h" +#include "sanitizer_common/sanitizer_internal_defs.h" #define CALLERPC ((uptr)__builtin_return_address(0)) using namespace __tsan; // NOLINT +typedef u16 uint16_t; +typedef u32 uint32_t; +typedef u64 uint64_t; + void __tsan_init() { Initialize(cur_thread()); } @@ -31,6 +36,57 @@ void __tsan_write16(void *addr) { MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } +u16 __tsan_unaligned_read2(const uu16 *addr) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, false, false); + return *addr; +} + +u32 __tsan_unaligned_read4(const uu32 *addr) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, false, false); + return *addr; +} + +u64 __tsan_unaligned_read8(const uu64 *addr) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, false, false); + return *addr; +} + +void __tsan_unaligned_write2(uu16 *addr, u16 v) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, true, false); + *addr = v; +} + +void __tsan_unaligned_write4(uu32 *addr, u32 v) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, true, false); + *addr = v; +} + +void __tsan_unaligned_write8(uu64 *addr, u64 v) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, true, false); + *addr = v; +} + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +uint16_t __sanitizer_unaligned_load16(void *addr) + ALIAS("__tsan_unaligned_read2"); +SANITIZER_INTERFACE_ATTRIBUTE +uint32_t __sanitizer_unaligned_load32(void *addr) + ALIAS("__tsan_unaligned_read4"); +SANITIZER_INTERFACE_ATTRIBUTE +uint64_t __sanitizer_unaligned_load64(void *addr) + ALIAS("__tsan_unaligned_read8"); +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(void *addr, uint16_t v) + ALIAS("__tsan_unaligned_write2"); +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(void *addr, uint32_t v) + ALIAS("__tsan_unaligned_write4"); +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store64(void *addr, uint64_t v) + ALIAS("__tsan_unaligned_write8"); +} + void __tsan_acquire(void *addr) { Acquire(cur_thread(), CALLERPC, (uptr)addr); } diff --git a/libsanitizer/tsan/tsan_interface.h b/libsanitizer/tsan/tsan_interface.h index 2cfd7684183..63de7b2ab5c 100644 --- a/libsanitizer/tsan/tsan_interface.h +++ b/libsanitizer/tsan/tsan_interface.h @@ -25,30 +25,38 @@ 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. -void __tsan_init() SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_init(); -void __tsan_read1(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_read16(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16(void *addr); -void __tsan_write1(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write16(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16(void *addr); -void __tsan_vptr_update(void **vptr_p, void *new_val) - SANITIZER_INTERFACE_ATTRIBUTE; +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); -void __tsan_func_entry(void *call_pc) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_func_exit() SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p); +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_vptr_update(void **vptr_p, void *new_val); -void __tsan_read_range(void *addr, unsigned long size) // NOLINT - SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_write_range(void *addr, unsigned long size) // NOLINT - SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_entry(void *call_pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_exit(); + +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_read_range(void *addr, unsigned long size); // NOLINT +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_write_range(void *addr, unsigned long size); // NOLINT #ifdef __cplusplus } // extern "C" diff --git a/libsanitizer/tsan/tsan_interface_ann.cc b/libsanitizer/tsan/tsan_interface_ann.cc index 7e49fb8b059..46d9e3ec1af 100644 --- a/libsanitizer/tsan/tsan_interface_ann.cc +++ b/libsanitizer/tsan/tsan_interface_ann.cc @@ -11,6 +11,7 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" #include "tsan_interface_ann.h" #include "tsan_mutex.h" #include "tsan_report.h" @@ -18,6 +19,7 @@ #include "tsan_mman.h" #include "tsan_flags.h" #include "tsan_platform.h" +#include "tsan_vector.h" #define CALLERPC ((uptr)__builtin_return_address(0)) @@ -65,6 +67,7 @@ struct ExpectRace { ExpectRace *next; ExpectRace *prev; int hitcount; + int addcount; uptr addr; uptr size; char *file; @@ -89,16 +92,19 @@ static void AddExpectRace(ExpectRace *list, char *f, int l, uptr addr, uptr size, char *desc) { ExpectRace *race = list->next; for (; race != list; race = race->next) { - if (race->addr == addr && race->size == size) + if (race->addr == addr && race->size == size) { + race->addcount++; return; + } } race = (ExpectRace*)internal_alloc(MBlockExpectRace, sizeof(ExpectRace)); - race->hitcount = 0; race->addr = addr; race->size = size; race->file = f; race->line = l; race->desc[0] = 0; + race->hitcount = 0; + race->addcount = 1; if (desc) { int i = 0; for (; i < kMaxDescLen - 1 && desc[i]; i++) @@ -153,6 +159,68 @@ bool IsExpectedReport(uptr addr, uptr size) { return false; } +static void CollectMatchedBenignRaces(Vector *matched, + int *unique_count, int *hit_count, int ExpectRace::*counter) { + ExpectRace *list = &dyn_ann_ctx->benign; + for (ExpectRace *race = list->next; race != list; race = race->next) { + (*unique_count)++; + if (race->*counter == 0) + continue; + (*hit_count) += race->*counter; + 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; + break; + } + } + if (i == matched->Size()) + matched->PushBack(*race); + } +} + +void PrintMatchedBenignRaces() { + Lock lock(&dyn_ann_ctx->mtx); + int unique_count = 0; + int hit_count = 0; + int add_count = 0; + Vector hit_matched(MBlockScopedBuf); + CollectMatchedBenignRaces(&hit_matched, &unique_count, &hit_count, + &ExpectRace::hitcount); + Vector add_matched(MBlockScopedBuf); + CollectMatchedBenignRaces(&add_matched, &unique_count, &add_count, + &ExpectRace::addcount); + if (hit_matched.Size()) { + Printf("ThreadSanitizer: Matched %d \"benign\" races (pid=%d):\n", + 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); + } + } + if (hit_matched.Size()) { + Printf("ThreadSanitizer: Annotated %d \"benign\" races, %d unique" + " (pid=%d):\n", + 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); + } + } +} + +static void ReportMissedExpectedRace(ExpectRace *race) { + Printf("==================\n"); + Printf("WARNING: ThreadSanitizer: missed expected data race\n"); + Printf(" %s addr=%zx %s:%d\n", + race->desc, race->addr, race->file, race->line); + Printf("==================\n"); +} } // namespace __tsan using namespace __tsan; // NOLINT @@ -160,12 +228,12 @@ using namespace __tsan; // NOLINT extern "C" { void INTERFACE_ATTRIBUTE AnnotateHappensBefore(char *f, int l, uptr addr) { SCOPED_ANNOTATION(AnnotateHappensBefore); - Release(cur_thread(), CALLERPC, addr); + Release(thr, pc, addr); } void INTERFACE_ATTRIBUTE AnnotateHappensAfter(char *f, int l, uptr addr) { SCOPED_ANNOTATION(AnnotateHappensAfter); - Acquire(cur_thread(), CALLERPC, addr); + Acquire(thr, pc, addr); } void INTERFACE_ATTRIBUTE AnnotateCondVarSignal(char *f, int l, uptr cv) { @@ -235,14 +303,6 @@ void INTERFACE_ATTRIBUTE AnnotateNoOp(char *f, int l, uptr mem) { SCOPED_ANNOTATION(AnnotateNoOp); } -static void ReportMissedExpectedRace(ExpectRace *race) { - Printf("==================\n"); - Printf("WARNING: ThreadSanitizer: missed expected data race\n"); - Printf(" %s addr=%zx %s:%d\n", - race->desc, race->addr, race->file, race->line); - Printf("==================\n"); -} - void INTERFACE_ATTRIBUTE AnnotateFlushExpectedRaces(char *f, int l) { SCOPED_ANNOTATION(AnnotateFlushExpectedRaces); Lock lock(&dyn_ann_ctx->mtx); @@ -321,22 +381,22 @@ void INTERFACE_ATTRIBUTE AnnotateBenignRace( void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin); - IgnoreCtl(cur_thread(), false, true); + ThreadIgnoreBegin(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsEnd(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd); - IgnoreCtl(cur_thread(), false, false); + ThreadIgnoreEnd(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin); - IgnoreCtl(cur_thread(), true, true); + ThreadIgnoreBegin(thr); } void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesEnd(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd); - IgnoreCtl(thr, true, false); + ThreadIgnoreEnd(thr); } void INTERFACE_ATTRIBUTE AnnotatePublishMemoryRange( @@ -355,6 +415,9 @@ void INTERFACE_ATTRIBUTE AnnotateThreadName( ThreadSetName(thr, name); } +// We deliberately omit the implementation of WTFAnnotateHappensBefore() and +// WTFAnnotateHappensAfter(). Those are being used by Webkit to annotate +// atomic operations, which should be handled by ThreadSanitizer correctly. void INTERFACE_ATTRIBUTE WTFAnnotateHappensBefore(char *f, int l, uptr addr) { SCOPED_ANNOTATION(AnnotateHappensBefore); } @@ -366,6 +429,7 @@ void INTERFACE_ATTRIBUTE WTFAnnotateHappensAfter(char *f, int l, uptr addr) { void INTERFACE_ATTRIBUTE WTFAnnotateBenignRaceSized( char *f, int l, uptr mem, uptr sz, char *desc) { SCOPED_ANNOTATION(AnnotateBenignRaceSized); + BenignRaceImpl(f, l, mem, 1, desc); } int INTERFACE_ATTRIBUTE RunningOnValgrind() { @@ -382,4 +446,7 @@ const char INTERFACE_ATTRIBUTE* ThreadSanitizerQuery(const char *query) { else return "0"; } + +void INTERFACE_ATTRIBUTE +AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {} } // extern "C" diff --git a/libsanitizer/tsan/tsan_interface_ann.h b/libsanitizer/tsan/tsan_interface_ann.h index b6500329428..45c18352e69 100644 --- a/libsanitizer/tsan/tsan_interface_ann.h +++ b/libsanitizer/tsan/tsan_interface_ann.h @@ -21,8 +21,8 @@ extern "C" { #endif -void __tsan_acquire(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; -void __tsan_release(void *addr) SANITIZER_INTERFACE_ATTRIBUTE; +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_acquire(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_release(void *addr); #ifdef __cplusplus } // extern "C" diff --git a/libsanitizer/tsan/tsan_interface_atomic.cc b/libsanitizer/tsan/tsan_interface_atomic.cc index 2c8b2ab049c..02ebb47e6af 100644 --- a/libsanitizer/tsan/tsan_interface_atomic.cc +++ b/libsanitizer/tsan/tsan_interface_atomic.cc @@ -28,34 +28,14 @@ using namespace __tsan; // NOLINT #define SCOPED_ATOMIC(func, ...) \ const uptr callpc = (uptr)__builtin_return_address(0); \ uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ - pc = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); \ 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, __FUNCTION__); \ + ScopedAtomic sa(thr, callpc, a, mo, __FUNCTION__); \ return Atomic##func(thr, pc, __VA_ARGS__); \ /**/ -class ScopedAtomic { - public: - ScopedAtomic(ThreadState *thr, uptr pc, const char *func) - : thr_(thr) { - CHECK_EQ(thr_->in_rtl, 0); - ProcessPendingSignals(thr); - FuncEntry(thr_, pc); - DPrintf("#%d: %s\n", thr_->tid, func); - thr_->in_rtl++; - } - ~ScopedAtomic() { - thr_->in_rtl--; - CHECK_EQ(thr_->in_rtl, 0); - FuncExit(thr_); - } - private: - ThreadState *thr_; -}; - // Some shortcuts. typedef __tsan_memory_order morder; typedef __tsan_atomic8 a8; @@ -70,6 +50,26 @@ 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; +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_; +}; + static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) { StatInc(thr, StatAtomic); StatInc(thr, t); @@ -288,16 +288,20 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v, template static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog()); - SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - if (IsAcqRelOrder(mo)) - thr->clock.acq_rel(&s->clock); - else if (IsReleaseOrder(mo)) - thr->clock.release(&s->clock); - else if (IsAcquireOrder(mo)) - thr->clock.acquire(&s->clock); + SyncVar *s = 0; + if (mo != mo_relaxed) { + s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); + thr->clock.set(thr->tid, thr->fast_state.epoch()); + if (IsAcqRelOrder(mo)) + thr->clock.acq_rel(&s->clock); + else if (IsReleaseOrder(mo)) + thr->clock.release(&s->clock); + else if (IsAcquireOrder(mo)) + thr->clock.acquire(&s->clock); + } v = F(a, v); - s->mtx.Unlock(); + if (s) + s->mtx.Unlock(); return v; } @@ -348,17 +352,21 @@ 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()); - SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - if (IsAcqRelOrder(mo)) - thr->clock.acq_rel(&s->clock); - else if (IsReleaseOrder(mo)) - thr->clock.release(&s->clock); - else if (IsAcquireOrder(mo)) - thr->clock.acquire(&s->clock); + SyncVar *s = 0; + if (mo != mo_relaxed) { + s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); + thr->clock.set(thr->tid, thr->fast_state.epoch()); + if (IsAcqRelOrder(mo)) + thr->clock.acq_rel(&s->clock); + else if (IsReleaseOrder(mo)) + thr->clock.release(&s->clock); + else if (IsAcquireOrder(mo)) + thr->clock.acquire(&s->clock); + } T cc = *c; T pr = func_cas(a, cc, v); - s->mtx.Unlock(); + if (s) + s->mtx.Unlock(); if (pr == cc) return true; *c = pr; @@ -649,14 +657,14 @@ a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v, } #if __TSAN_HAS_INT128 -a128 __tsan_atomic64_compare_exchange_val(volatile a128 *a, a128 c, a128 v, +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 void __tsan_atomic_thread_fence(morder mo) { - char* a; + char* a = 0; SCOPED_ATOMIC(Fence, mo); } diff --git a/libsanitizer/tsan/tsan_interface_inl.h b/libsanitizer/tsan/tsan_interface_inl.h index 92796d1178f..4d6e10bcf7b 100644 --- a/libsanitizer/tsan/tsan_interface_inl.h +++ b/libsanitizer/tsan/tsan_interface_inl.h @@ -50,8 +50,20 @@ void __tsan_write8(void *addr) { void __tsan_vptr_update(void **vptr_p, void *new_val) { CHECK_EQ(sizeof(vptr_p), 8); - if (*vptr_p != new_val) - MemoryWrite(cur_thread(), CALLERPC, (uptr)vptr_p, kSizeLog8); + if (*vptr_p != new_val) { + ThreadState *thr = cur_thread(); + thr->is_vptr_access = true; + MemoryWrite(thr, CALLERPC, (uptr)vptr_p, kSizeLog8); + thr->is_vptr_access = false; + } +} + +void __tsan_vptr_read(void **vptr_p) { + CHECK_EQ(sizeof(vptr_p), 8); + ThreadState *thr = cur_thread(); + thr->is_vptr_access = true; + MemoryRead(thr, CALLERPC, (uptr)vptr_p, kSizeLog8); + thr->is_vptr_access = false; } void __tsan_func_entry(void *pc) { diff --git a/libsanitizer/tsan/tsan_interface_java.cc b/libsanitizer/tsan/tsan_interface_java.cc index f8c0b4eb635..7cc72577977 100644 --- a/libsanitizer/tsan/tsan_interface_java.cc +++ b/libsanitizer/tsan/tsan_interface_java.cc @@ -15,6 +15,8 @@ #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_procmaps.h" using namespace __tsan; // NOLINT @@ -92,6 +94,8 @@ class ScopedJavaFunc { static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1]; static JavaContext *jctx; +extern atomic_uintptr_t libjvm_begin; +extern atomic_uintptr_t libjvm_end; static BlockDesc *getblock(uptr addr) { uptr i = (addr - jctx->heap_begin) / kHeapAlignment; @@ -155,11 +159,22 @@ SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) { #define SCOPED_JAVA_FUNC(func) \ ThreadState *thr = cur_thread(); \ const uptr caller_pc = GET_CALLER_PC(); \ - const uptr pc = (uptr)&func; \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ (void)pc; \ ScopedJavaFunc scoped(thr, caller_pc); \ /**/ +void __tsan_java_preinit(const char *libjvm_path) { + SCOPED_JAVA_FUNC(__tsan_java_preinit); + if (libjvm_path) { + uptr begin, end; + if (GetCodeRangeForFile(libjvm_path, &begin, &end)) { + atomic_store(&libjvm_begin, begin, memory_order_relaxed); + atomic_store(&libjvm_end, end, memory_order_relaxed); + } + } +} + void __tsan_java_init(jptr heap_begin, jptr heap_size) { SCOPED_JAVA_FUNC(__tsan_java_init); DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size); @@ -269,6 +284,7 @@ void __tsan_java_mutex_lock(jptr addr) { CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + MutexCreate(thr, pc, addr, true, true, true); MutexLock(thr, pc, addr); } @@ -289,6 +305,7 @@ void __tsan_java_mutex_read_lock(jptr addr) { CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + MutexCreate(thr, pc, addr, true, true, true); MutexReadLock(thr, pc, addr); } @@ -301,3 +318,25 @@ void __tsan_java_mutex_read_unlock(jptr addr) { MutexReadUnlock(thr, pc, addr); } + +void __tsan_java_mutex_lock_rec(jptr addr, int rec) { + SCOPED_JAVA_FUNC(__tsan_java_mutex_lock_rec); + DPrintf("#%d: java_mutex_lock_rec(%p, %d)\n", thr->tid, addr, rec); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + CHECK_GT(rec, 0); + + MutexCreate(thr, pc, addr, true, true, true); + MutexLock(thr, pc, addr, rec); +} + +int __tsan_java_mutex_unlock_rec(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock_rec); + DPrintf("#%d: java_mutex_unlock_rec(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + return MutexUnlock(thr, pc, addr, true); +} diff --git a/libsanitizer/tsan/tsan_interface_java.h b/libsanitizer/tsan/tsan_interface_java.h index 01922bc9231..818c07b97d4 100644 --- a/libsanitizer/tsan/tsan_interface_java.h +++ b/libsanitizer/tsan/tsan_interface_java.h @@ -32,7 +32,11 @@ extern "C" { typedef unsigned long jptr; // NOLINT -// Must be called before any other callback from Java. +// Must be called before any other callback from Java, right after dlopen +// of JVM shared lib. If libjvm_path is specified, then all interceptors +// coming directly from JVM will be ignored. +void __tsan_java_preinit(const char *libjvm_path) INTERFACE_ATTRIBUTE; +// Must be called after __tsan_java_preinit but before any other callback. void __tsan_java_init(jptr heap_begin, jptr heap_size) INTERFACE_ATTRIBUTE; // Must be called when the application exits. // Not necessary the last callback (concurrently running threads are OK). @@ -53,8 +57,7 @@ void __tsan_java_move(jptr src, jptr dst, jptr size) INTERFACE_ATTRIBUTE; // Mutex lock. // Addr is any unique address associated with the mutex. -// Must not be called on recursive reentry. -// Object.wait() is handled as a pair of unlock/lock. +// Can be called on recursive reentry. void __tsan_java_mutex_lock(jptr addr) INTERFACE_ATTRIBUTE; // Mutex unlock. void __tsan_java_mutex_unlock(jptr addr) INTERFACE_ATTRIBUTE; @@ -62,6 +65,16 @@ void __tsan_java_mutex_unlock(jptr addr) INTERFACE_ATTRIBUTE; void __tsan_java_mutex_read_lock(jptr addr) INTERFACE_ATTRIBUTE; // Mutex read unlock. void __tsan_java_mutex_read_unlock(jptr addr) INTERFACE_ATTRIBUTE; +// Recursive mutex lock, intended for handling of Object.wait(). +// The 'rec' value must be obtained from the previous +// __tsan_java_mutex_unlock_rec(). +void __tsan_java_mutex_lock_rec(jptr addr, int rec) INTERFACE_ATTRIBUTE; +// Recursive mutex unlock, intended for handling of Object.wait(). +// The return value says how many times this thread called lock() +// w/o a pairing unlock() (i.e. how many recursive levels it unlocked). +// It must be passed back to __tsan_java_mutex_lock_rec() to restore +// the same recursion level. +int __tsan_java_mutex_unlock_rec(jptr addr) INTERFACE_ATTRIBUTE; #ifdef __cplusplus } // extern "C" diff --git a/libsanitizer/tsan/tsan_mman.cc b/libsanitizer/tsan/tsan_mman.cc index 23c73c50cc1..832374becf5 100644 --- a/libsanitizer/tsan/tsan_mman.cc +++ b/libsanitizer/tsan/tsan_mman.cc @@ -27,6 +27,41 @@ extern "C" void WEAK __tsan_free_hook(void *ptr) { namespace __tsan { +COMPILER_CHECK(sizeof(MBlock) == 16); + +void MBlock::Lock() { + atomic_uintptr_t *a = reinterpret_cast(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(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); + } +}; + static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64); Allocator *allocator() { return reinterpret_cast(&allocator_placeholder); @@ -38,10 +73,12 @@ void InitializeAllocator() { void AllocatorThreadStart(ThreadState *thr) { allocator()->InitCache(&thr->alloc_cache); + internal_allocator()->InitCache(&thr->internal_alloc_cache); } void AllocatorThreadFinish(ThreadState *thr) { allocator()->DestroyCache(&thr->alloc_cache); + internal_allocator()->DestroyCache(&thr->internal_alloc_cache); } void AllocatorPrintStats() { @@ -54,7 +91,7 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) { Context *ctx = CTX(); StackTrace stack; stack.ObtainCurrent(thr, pc); - Lock l(&ctx->thread_mtx); + ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeSignalUnsafe); if (!IsFiredSuppression(ctx, rep, stack)) { rep.AddStack(&stack); @@ -64,16 +101,18 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) { void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) { CHECK_GT(thr->in_rtl, 0); + if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) + return AllocatorReturnNull(); void *p = allocator()->Allocate(&thr->alloc_cache, sz, align); if (p == 0) return 0; MBlock *b = new(allocator()->GetMetaData(p)) MBlock; - b->size = sz; - b->head = 0; - b->alloc_tid = thr->unique_id; - b->alloc_stack_id = CurrentStackId(thr, pc); + b->Init(sz, thr->tid, CurrentStackId(thr, pc)); if (CTX() && CTX()->initialized) { - MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); + 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); @@ -85,9 +124,9 @@ void user_free(ThreadState *thr, uptr pc, void *p) { CHECK_NE(p, (void*)0); DPrintf("#%d: free(%p)\n", thr->tid, p); MBlock *b = (MBlock*)allocator()->GetMetaData(p); - if (b->head) { - Lock l(&b->mtx); - for (SyncVar *s = b->head; s;) { + if (b->ListHead()) { + MBlock::ScopedLock l(b); + for (SyncVar *s = b->ListHead(); s;) { SyncVar *res = s; s = s->next; StatInc(thr, StatSyncDestroyed); @@ -95,12 +134,12 @@ void user_free(ThreadState *thr, uptr pc, void *p) { res->mtx.Unlock(); DestroyAndFree(res); } - b->head = 0; + b->ListReset(); } if (CTX() && CTX()->initialized && thr->in_rtl == 1) { - MemoryRangeFreed(thr, pc, (uptr)p, b->size); + if (thr->ignore_reads_and_writes == 0) + MemoryRangeFreed(thr, pc, (uptr)p, b->Size()); } - b->~MBlock(); allocator()->Deallocate(&thr->alloc_cache, p); SignalUnsafeCall(thr, pc); } @@ -116,20 +155,29 @@ void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) { return 0; if (p) { MBlock *b = user_mblock(thr, p); - internal_memcpy(p2, p, min(b->size, sz)); + CHECK_NE(b, 0); + internal_memcpy(p2, p, min(b->Size(), sz)); } } - if (p) { + if (p) user_free(thr, pc, p); - } return p2; } +uptr user_alloc_usable_size(ThreadState *thr, uptr pc, void *p) { + CHECK_GT(thr->in_rtl, 0); + 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, (void*)0); + CHECK_NE(p, 0); Allocator *a = allocator(); void *b = a->GetBlockBegin(p); - CHECK_NE(b, 0); + if (b == 0) + return 0; return (MBlock*)a->GetMetaData(b); } @@ -152,11 +200,12 @@ void invoke_free_hook(void *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); } - return InternalAlloc(sz); + return InternalAlloc(sz, &thr->internal_alloc_cache); } void internal_free(void *p) { @@ -166,7 +215,7 @@ void internal_free(void *p) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } - InternalFree(p); + InternalFree(p, &thr->internal_alloc_cache); } } // namespace __tsan @@ -213,6 +262,12 @@ uptr __tsan_get_allocated_size(void *p) { if (p == 0) return 0; MBlock *b = (MBlock*)allocator()->GetMetaData(p); - return b->size; + return b->Size(); +} + +void __tsan_on_thread_idle() { + ThreadState *thr = cur_thread(); + allocator()->SwallowCache(&thr->alloc_cache); + internal_allocator()->SwallowCache(&thr->internal_alloc_cache); } } // extern "C" diff --git a/libsanitizer/tsan/tsan_mman.h b/libsanitizer/tsan/tsan_mman.h index 7a657e124bf..90faaffa1fc 100644 --- a/libsanitizer/tsan/tsan_mman.h +++ b/libsanitizer/tsan/tsan_mman.h @@ -29,6 +29,7 @@ void *user_alloc(ThreadState *thr, uptr pc, uptr sz, void user_free(ThreadState *thr, uptr pc, void *p); 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); @@ -60,6 +61,7 @@ enum MBlockType { MBlockExpectRace, MBlockSignal, MBlockFD, + MBlockJmpBuf, // This must be the last. MBlockTypeCount diff --git a/libsanitizer/tsan/tsan_mutex.cc b/libsanitizer/tsan/tsan_mutex.cc index 716722b0897..e7846c53e4a 100644 --- a/libsanitizer/tsan/tsan_mutex.cc +++ b/libsanitizer/tsan/tsan_mutex.cc @@ -29,8 +29,8 @@ static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = { /*0 MutexTypeInvalid*/ {}, /*1 MutexTypeTrace*/ {MutexTypeLeaf}, /*2 MutexTypeThreads*/ {MutexTypeReport}, - /*3 MutexTypeReport*/ {MutexTypeSyncTab, MutexTypeMBlock, - MutexTypeJavaMBlock}, + /*3 MutexTypeReport*/ {MutexTypeSyncTab, MutexTypeSyncVar, + MutexTypeMBlock, MutexTypeJavaMBlock}, /*4 MutexTypeSyncVar*/ {}, /*5 MutexTypeSyncTab*/ {MutexTypeSyncVar}, /*6 MutexTypeSlab*/ {MutexTypeLeaf}, diff --git a/libsanitizer/tsan/tsan_mutexset.h b/libsanitizer/tsan/tsan_mutexset.h index 6924eade4c6..ef60bd47db2 100644 --- a/libsanitizer/tsan/tsan_mutexset.h +++ b/libsanitizer/tsan/tsan_mutexset.h @@ -20,7 +20,7 @@ class MutexSet { public: // Holds limited number of mutexes. // The oldest mutexes are discarded on overflow. - static const uptr kMaxSize = 64; + static const uptr kMaxSize = 16; struct Desc { u64 id; u64 epoch; diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h index 78c1a7d8195..ac36c5ab67a 100644 --- a/libsanitizer/tsan/tsan_platform.h +++ b/libsanitizer/tsan/tsan_platform.h @@ -35,9 +35,9 @@ C++ COMPAT linux memory layout: Go linux and darwin memory layout: 0000 0000 0000 - 0000 1000 0000: executable 0000 1000 0000 - 00f8 0000 0000: - -00f8 0000 0000 - 0118 0000 0000: heap -0118 0000 0000 - 1000 0000 0000: - -1000 0000 0000 - 1460 0000 0000: shadow +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: - @@ -45,8 +45,8 @@ Go linux and darwin memory layout: Go windows memory layout: 0000 0000 0000 - 0000 1000 0000: executable 0000 1000 0000 - 00f8 0000 0000: - -00f8 0000 0000 - 0118 0000 0000: heap -0118 0000 0000 - 0100 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: - @@ -63,11 +63,11 @@ namespace __tsan { #if defined(TSAN_GO) static const uptr kLinuxAppMemBeg = 0x000000000000ULL; -static const uptr kLinuxAppMemEnd = 0x00fcffffffffULL; -# if defined(_WIN32) +static const uptr kLinuxAppMemEnd = 0x04dfffffffffULL; +# if SANITIZER_WINDOWS static const uptr kLinuxShadowMsk = 0x010000000000ULL; # else -static const uptr kLinuxShadowMsk = 0x100000000000ULL; +static const uptr kLinuxShadowMsk = 0x200000000000ULL; # endif // TSAN_COMPAT_SHADOW is intended for COMPAT virtual memory layout, // when memory addresses are of the 0x2axxxxxxxxxx form. @@ -82,7 +82,7 @@ static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL; static const uptr kLinuxAppMemMsk = 0x7c0000000000ULL; -#if defined(_WIN32) +#if SANITIZER_WINDOWS const uptr kTraceMemBegin = 0x056000000000ULL; #else const uptr kTraceMemBegin = 0x600000000000ULL; @@ -130,13 +130,19 @@ static inline uptr AlternativeAddress(uptr addr) { #endif } -uptr GetShadowMemoryConsumption(); void FlushShadowMemory(); +void WriteMemoryProfile(char *buf, uptr buf_size); const char *InitializePlatform(); void FinalizePlatform(); -uptr ALWAYS_INLINE INLINE GetThreadTrace(int tid) { - uptr p = kTraceMemBegin + (uptr)tid * kTraceSize * sizeof(Event); +uptr ALWAYS_INLINE GetThreadTrace(int tid) { + uptr p = kTraceMemBegin + (uptr)(tid * 2) * kTraceSize * sizeof(Event); + DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); + return p; +} + +uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) { + uptr p = kTraceMemBegin + (uptr)(tid * 2 + 1) * kTraceSize * sizeof(Event); DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); return p; } @@ -146,9 +152,6 @@ void internal_start_thread(void(*func)(void*), void *arg); // Says whether the addr relates to a global var. // Guesses with high probability, may yield both false positives and negatives. bool IsGlobalVar(uptr addr); -uptr GetTlsSize(); -void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, - uptr *tls_addr, uptr *tls_size); int ExtractResolvFDs(void *state, int *fds, int nfd); } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_platform_linux.cc b/libsanitizer/tsan/tsan_platform_linux.cc index f7b05f2bf8f..f13e3c893b3 100644 --- a/libsanitizer/tsan/tsan_platform_linux.cc +++ b/libsanitizer/tsan/tsan_platform_linux.cc @@ -10,7 +10,9 @@ // Linux-specific code. //===----------------------------------------------------------------------===// -#ifdef __linux__ + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" @@ -19,7 +21,6 @@ #include "tsan_rtl.h" #include "tsan_flags.h" -#include #include #include #include @@ -40,11 +41,14 @@ #include #define __need_res_state #include +#include -extern "C" int arch_prctl(int code, __sanitizer::uptr *addr); +extern "C" struct mallinfo __libc_mallinfo(); namespace __tsan { +const uptr kPageSize = 4096; + #ifndef TSAN_GO ScopedInRtl::ScopedInRtl() : thr_(cur_thread()) { @@ -66,8 +70,38 @@ ScopedInRtl::~ScopedInRtl() { } #endif -uptr GetShadowMemoryConsumption() { - return 0; +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; +} + +void WriteMemoryProfile(char *buf, uptr buf_size) { + uptr mem[7] = {}; + __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); } void FlushShadowMemory() { @@ -89,6 +123,63 @@ static void ProtectRange(uptr beg, uptr end) { #endif #ifndef TSAN_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() { + // First create temp file. + const char *tmpdir = GetEnv("TMPDIR"); + if (tmpdir == 0) + tmpdir = GetEnv("TEST_TMPDIR"); +#ifdef P_tmpdir + if (tmpdir == 0) + tmpdir = P_tmpdir; +#endif + if (tmpdir == 0) + return; + char filename[256]; + internal_snprintf(filename, sizeof(filename), "%s/tsan.rodata.%d", + tmpdir, (int)internal_getpid()); + uptr openrv = internal_open(filename, O_RDWR | O_CREAT | O_EXCL, 0600); + if (internal_iserror(openrv)) + return; + fd_t fd = openrv; + // Fill the file with kShadowRodata. + const uptr kMarkerSize = 512 * 1024 / sizeof(u64); + InternalScopedBuffer marker(kMarkerSize); + for (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, + 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]; + while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name), &prot)) { + if (name[0] != 0 && name[0] != '[' + && (prot & MemoryMappingLayout::kProtectionRead) + && (prot & MemoryMappingLayout::kProtectionExecute) + && !(prot & MemoryMappingLayout::kProtectionWrite) + && IsAppMem(start)) { + // Assume it's .rodata + char *shadow_start = (char*)MemToShadow(start); + char *shadow_end = (char*)MemToShadow(end); + for (char *p = shadow_start; p < shadow_end; p += marker.size()) { + internal_mmap(p, Min(marker.size(), shadow_end - p), + PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0); + } + } + } + internal_close(fd); + internal_unlink(filename); +} + void InitializeShadowMemory() { uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg); @@ -115,6 +206,8 @@ void InitializeShadowMemory() { kLinuxAppMemBeg, kLinuxAppMemEnd, (kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30); DPrintf("stack %zx\n", (uptr)&shadow); + + MapRodata(); } #endif @@ -124,10 +217,11 @@ static uptr g_data_end; #ifndef TSAN_GO static void CheckPIE() { // Ensure that the binary is indeed compiled with -pie. - MemoryMappingLayout proc_maps; + MemoryMappingLayout proc_maps(true); uptr start, end; if (proc_maps.Next(&start, &end, - /*offset*/0, /*filename*/0, /*filename_size*/0)) { + /*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", @@ -140,11 +234,12 @@ static void CheckPIE() { } static void InitDataSeg() { - MemoryMappingLayout proc_maps; + MemoryMappingLayout proc_maps(true); uptr start, end, offset; char name[128]; bool prev_is_data = false; - while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name))) { + while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name), + /*protection*/ 0)) { DPrintf("%p-%p %p %s\n", start, end, offset, name); bool is_data = offset != 0 && name[0] != 0; // BSS may get merged with [heap] in /proc/self/maps. This is not very @@ -163,27 +258,6 @@ static void InitDataSeg() { CHECK_LT((uptr)&g_data_start, g_data_end); } -static uptr g_tls_size; - -#ifdef __i386__ -# define INTERNAL_FUNCTION __attribute__((regparm(3), stdcall)) -#else -# define INTERNAL_FUNCTION -#endif - -static int InitTlsSize() { - typedef void (*get_tls_func)(size_t*, size_t*) INTERNAL_FUNCTION; - get_tls_func get_tls; - void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); - CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr)); - internal_memcpy(&get_tls, &get_tls_static_info_ptr, - sizeof(get_tls_static_info_ptr)); - CHECK_NE(get_tls, 0); - size_t tls_size = 0; - size_t tls_align = 0; - get_tls(&tls_size, &tls_align); - return tls_size; -} #endif // #ifndef TSAN_GO static rlim_t getlim(int res) { @@ -238,53 +312,12 @@ const char *InitializePlatform() { #ifndef TSAN_GO CheckPIE(); - g_tls_size = (uptr)InitTlsSize(); + InitTlsSize(); InitDataSeg(); #endif return GetEnv(kTsanOptionsEnv); } -void FinalizePlatform() { - fflush(0); -} - -uptr GetTlsSize() { -#ifndef TSAN_GO - return g_tls_size; -#else - return 0; -#endif -} - -void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, - uptr *tls_addr, uptr *tls_size) { -#ifndef TSAN_GO - arch_prctl(ARCH_GET_FS, tls_addr); - *tls_addr -= g_tls_size; - *tls_size = g_tls_size; - - uptr stack_top, stack_bottom; - GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); - *stk_addr = stack_bottom; - *stk_size = stack_top - stack_bottom; - - if (!main) { - // If stack and tls intersect, make them non-intersecting. - if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) { - CHECK_GT(*tls_addr + *tls_size, *stk_addr); - CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size); - *stk_size -= *tls_size; - *tls_addr = *stk_addr + *stk_size; - } - } -#else - *stk_addr = 0; - *stk_size = 0; - *tls_addr = 0; - *tls_size = 0; -#endif -} - bool IsGlobalVar(uptr addr) { return g_data_start && addr >= g_data_start && addr < g_data_end; } @@ -304,4 +337,4 @@ int ExtractResolvFDs(void *state, int *fds, int nfd) { } // namespace __tsan -#endif // #ifdef __linux__ +#endif // SANITIZER_LINUX diff --git a/libsanitizer/tsan/tsan_platform_mac.cc b/libsanitizer/tsan/tsan_platform_mac.cc index b247468c829..3dca611dc92 100644 --- a/libsanitizer/tsan/tsan_platform_mac.cc +++ b/libsanitizer/tsan/tsan_platform_mac.cc @@ -10,7 +10,8 @@ // Mac-specific code. //===----------------------------------------------------------------------===// -#ifdef __APPLE__ +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" @@ -87,18 +88,6 @@ void FinalizePlatform() { fflush(0); } -uptr GetTlsSize() { - return 0; -} - -void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, - uptr *tls_addr, uptr *tls_size) { - *stk_addr = 0; - *stk_size = 0; - *tls_addr = 0; - *tls_size = 0; -} - } // namespace __tsan -#endif // #ifdef __APPLE__ +#endif // SANITIZER_MAC diff --git a/libsanitizer/tsan/tsan_platform_windows.cc b/libsanitizer/tsan/tsan_platform_windows.cc index 376dc08688b..6e49ef42f0c 100644 --- a/libsanitizer/tsan/tsan_platform_windows.cc +++ b/libsanitizer/tsan/tsan_platform_windows.cc @@ -10,7 +10,8 @@ // Windows-specific code. //===----------------------------------------------------------------------===// -#ifdef _WIN32 +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS #include "tsan_platform.h" @@ -39,18 +40,6 @@ void FinalizePlatform() { fflush(0); } -uptr GetTlsSize() { - return 0; -} - -void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, - uptr *tls_addr, uptr *tls_size) { - *stk_addr = 0; - *stk_size = 0; - *tls_addr = 0; - *tls_size = 0; -} - } // namespace __tsan -#endif // #ifdef _WIN32 +#endif // SANITIZER_WINDOWS diff --git a/libsanitizer/tsan/tsan_report.cc b/libsanitizer/tsan/tsan_report.cc index 098d8262ba1..15ab22b4ba7 100644 --- a/libsanitizer/tsan/tsan_report.cc +++ b/libsanitizer/tsan/tsan_report.cc @@ -11,16 +11,35 @@ #include "tsan_report.h" #include "tsan_platform.h" #include "tsan_rtl.h" +#include "sanitizer_common/sanitizer_report_decorator.h" namespace __tsan { +class Decorator: private __sanitizer::AnsiColorDecorator { + public: + Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + const char *Warning() { return Red(); } + const char *EndWarning() { return Default(); } + const char *Access() { return Blue(); } + const char *EndAccess() { return Default(); } + const char *ThreadDescription() { return Cyan(); } + const char *EndThreadDescription() { return Default(); } + const char *Location() { return Green(); } + const char *EndLocation() { return Default(); } + const char *Sleep() { return Yellow(); } + const char *EndSleep() { return Default(); } + const char *Mutex() { return Magenta(); } + const char *EndMutex() { return Default(); } +}; + ReportDesc::ReportDesc() : stacks(MBlockReportStack) , mops(MBlockReportMop) , locs(MBlockReportLoc) , mutexes(MBlockReportMutex) , threads(MBlockReportThread) - , sleep() { + , sleep() + , count() { } ReportMop::ReportMop() @@ -44,6 +63,8 @@ const char *thread_name(char *buf, int tid) { static const char *ReportTypeString(ReportType typ) { if (typ == ReportTypeRace) return "data race"; + if (typ == ReportTypeVptrRace) + return "data race on vptr (ctor/dtor vs virtual call)"; if (typ == ReportTypeUseAfterFree) return "heap-use-after-free"; if (typ == ReportTypeThreadLeak) @@ -92,18 +113,24 @@ static const char *MopDesc(bool first, bool write, bool atomic) { } static void PrintMop(const ReportMop *mop, bool first) { + Decorator d; char thrbuf[kThreadBufSize]; + Printf("%s", d.Access()); Printf(" %s of size %d at %p by %s", MopDesc(first, mop->write, mop->atomic), mop->size, (void*)mop->addr, thread_name(thrbuf, mop->tid)); PrintMutexSet(mop->mset); Printf(":\n"); + Printf("%s", d.EndAccess()); PrintStack(mop->stack); } static void PrintLocation(const ReportLocation *loc) { + Decorator d; char thrbuf[kThreadBufSize]; + bool print_stack = false; + Printf("%s", d.Location()); if (loc->type == ReportLocationGlobal) { Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n", loc->name, loc->size, loc->addr, loc->module, loc->offset); @@ -111,7 +138,7 @@ static void PrintLocation(const ReportLocation *loc) { 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)); - PrintStack(loc->stack); + print_stack = true; } else if (loc->type == ReportLocationStack) { Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid)); } else if (loc->type == ReportLocationTLS) { @@ -119,24 +146,34 @@ static void PrintLocation(const ReportLocation *loc) { } else if (loc->type == ReportLocationFD) { Printf(" Location is file descriptor %d created by %s at:\n", loc->fd, thread_name(thrbuf, loc->tid)); - PrintStack(loc->stack); + print_stack = true; } + Printf("%s", d.EndLocation()); + if (print_stack) + PrintStack(loc->stack); } static void PrintMutex(const ReportMutex *rm) { + Decorator d; if (rm->destroyed) { + Printf("%s", d.Mutex()); Printf(" Mutex M%llu is already destroyed.\n\n", rm->id); + Printf("%s", d.EndMutex()); } else { + Printf("%s", d.Mutex()); Printf(" Mutex M%llu created at:\n", rm->id); + Printf("%s", d.EndMutex()); PrintStack(rm->stack); } } static void PrintThread(const ReportThread *rt) { + Decorator d; if (rt->id == 0) // Little sense in describing the main thread. return; + Printf("%s", d.ThreadDescription()); Printf(" Thread T%d", rt->id); - if (rt->name) + if (rt->name && rt->name[0] != '\0') Printf(" '%s'", rt->name); char thrbuf[kThreadBufSize]; Printf(" (tid=%zu, %s) created by %s", @@ -145,11 +182,15 @@ static void PrintThread(const ReportThread *rt) { if (rt->stack) Printf(" at:"); Printf("\n"); + Printf("%s", d.EndThreadDescription()); PrintStack(rt->stack); } static void PrintSleep(const ReportStack *s) { + Decorator d; + Printf("%s", d.Sleep()); Printf(" As if synchronized via sleep:\n"); + Printf("%s", d.EndSleep()); PrintStack(s); } @@ -172,9 +213,13 @@ ReportStack *SkipTsanInternalFrames(ReportStack *ent) { } void PrintReport(const ReportDesc *rep) { + Decorator d; Printf("==================\n"); const char *rep_typ_str = ReportTypeString(rep->typ); - Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, GetPid()); + Printf("%s", d.Warning()); + Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str, + (int)internal_getpid()); + Printf("%s", d.EndWarning()); for (uptr i = 0; i < rep->stacks.Size(); i++) { if (i) @@ -197,37 +242,46 @@ void PrintReport(const ReportDesc *rep) { 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); Printf("==================\n"); } -#else +#else // #ifndef TSAN_GO + +const int kMainThreadId = 1; void PrintStack(const ReportStack *ent) { if (ent == 0) { - Printf(" [failed to restore the stack]\n\n"); + 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); } - Printf("\n"); } static void PrintMop(const ReportMop *mop, bool first) { - Printf("%s by goroutine %d:\n", + Printf("\n"); + Printf("%s by ", (first ? (mop->write ? "Write" : "Read") - : (mop->write ? "Previous write" : "Previous read")), - mop->tid); + : (mop->write ? "Previous write" : "Previous read"))); + if (mop->tid == kMainThreadId) + Printf("main goroutine:\n"); + else + Printf("goroutine %d:\n", mop->tid); PrintStack(mop->stack); } static void PrintThread(const ReportThread *rt) { - if (rt->id == 0) // Little sense in describing the main thread. + if (rt->id == kMainThreadId) return; + Printf("\n"); Printf("Goroutine %d (%s) created at:\n", rt->id, rt->running ? "running" : "finished"); PrintStack(rt->stack); @@ -235,7 +289,7 @@ static void PrintThread(const ReportThread *rt) { void PrintReport(const ReportDesc *rep) { Printf("==================\n"); - Printf("WARNING: DATA RACE\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++) diff --git a/libsanitizer/tsan/tsan_report.h b/libsanitizer/tsan/tsan_report.h index eae2b3c721f..c0eef9eb023 100644 --- a/libsanitizer/tsan/tsan_report.h +++ b/libsanitizer/tsan/tsan_report.h @@ -18,6 +18,7 @@ namespace __tsan { enum ReportType { ReportTypeRace, + ReportTypeVptrRace, ReportTypeUseAfterFree, ReportTypeThreadLeak, ReportTypeMutexDestroyLocked, @@ -99,6 +100,7 @@ class ReportDesc { Vector mutexes; Vector threads; ReportStack *sleep; + int count; ReportDesc(); ~ReportDesc(); diff --git a/libsanitizer/tsan/tsan_rtl.cc b/libsanitizer/tsan/tsan_rtl.cc index 673a355f1dc..7f18064e957 100644 --- a/libsanitizer/tsan/tsan_rtl.cc +++ b/libsanitizer/tsan/tsan_rtl.cc @@ -21,6 +21,7 @@ #include "tsan_rtl.h" #include "tsan_mman.h" #include "tsan_suppressions.h" +#include "tsan_symbolize.h" volatile int __tsan_resumed = 0; @@ -45,15 +46,33 @@ 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(); + void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext)); + return new(mem) ThreadContext(tid); +} + +#ifndef TSAN_GO +static const u32 kThreadQuarantineSize = 16; +#else +static const u32 kThreadQuarantineSize = 64; +#endif + Context::Context() : initialized() , report_mtx(MutexTypeReport, StatMtxReport) , nreported() , nmissed_expected() - , thread_mtx(MutexTypeThreads, StatMtxThreads) + , thread_registry(new(thread_registry_placeholder) ThreadRegistry( + CreateThreadContext, kMaxTid, kThreadQuarantineSize)) , racy_stacks(MBlockRacyStacks) , racy_addresses(MBlockRacyAddresses) - , fired_suppressions(MBlockRacyAddresses) { + , fired_suppressions(8) { } // The objects are allocated in TLS, so one may rely on zero-initialization. @@ -63,10 +82,12 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, : fast_state(tid, epoch) // Do not touch these, rely on zero initialization, // they may be accessed before the ctor. - // , fast_ignore_reads() - // , fast_ignore_writes() + // , ignore_reads_and_writes() // , in_rtl() , shadow_stack_pos(&shadow_stack[0]) +#ifndef TSAN_GO + , jmp_bufs(MBlockJmpBuf) +#endif , tid(tid) , unique_id(unique_id) , stk_addr(stk_addr) @@ -75,94 +96,74 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, , tls_size(tls_size) { } -ThreadContext::ThreadContext(int tid) - : tid(tid) - , unique_id() - , os_id() - , user_id() - , thr() - , status(ThreadStatusInvalid) - , detached() - , reuse_count() - , epoch0() - , epoch1() - , dead_info() - , dead_next() - , name() { +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 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())); } -static void WriteMemoryProfile(char *buf, uptr buf_size, int num) { - uptr shadow = GetShadowMemoryConsumption(); +static void BackgroundThread(void *arg) { + ScopedInRtl in_rtl; + Context *ctx = CTX(); + const u64 kMs2Ns = 1000 * 1000; - int nthread = 0; - int nlivethread = 0; - uptr threadmem = 0; - { - Lock l(&ctx->thread_mtx); - for (unsigned i = 0; i < kMaxTid; i++) { - ThreadContext *tctx = ctx->threads[i]; - if (tctx == 0) - continue; - nthread += 1; - threadmem += sizeof(ThreadContext); - if (tctx->status != ThreadStatusRunning) - continue; - nlivethread += 1; - threadmem += sizeof(ThreadState); + fd_t mprof_fd = kInvalidFd; + if (flags()->profile_memory && flags()->profile_memory[0]) { + InternalScopedBuffer 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]); + } else { + mprof_fd = openrv; } } - uptr nsync = 0; - uptr syncmem = CTX()->synctab.GetMemoryConsumption(&nsync); - - internal_snprintf(buf, buf_size, "%d: shadow=%zuMB" - " thread=%zuMB(total=%d/live=%d)" - " sync=%zuMB(cnt=%zu)\n", - num, - shadow >> 20, - threadmem >> 20, nthread, nlivethread, - syncmem >> 20, nsync); -} - -static void MemoryProfileThread(void *arg) { - ScopedInRtl in_rtl; - fd_t fd = (fd_t)(uptr)arg; + u64 last_flush = NanoTime(); for (int i = 0; ; i++) { - InternalScopedBuffer buf(4096); - WriteMemoryProfile(buf.data(), buf.size(), i); - internal_write(fd, buf.data(), internal_strlen(buf.data())); SleepForSeconds(1); + u64 now = NanoTime(); + + // Flush memory if requested. + if (flags()->flush_memory_ms) { + if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) { + FlushShadowMemory(); + last_flush = NanoTime(); + } + } + + // Write memory profile if requested. + 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, + memory_order_relaxed); + if (last != 0 && last + flags()->flush_symbolizer_ms * kMs2Ns < now) { + Lock l(&ctx->report_mtx); + SpinMutexLock l2(&CommonSanitizerReportMutex); + SymbolizeFlush(); + atomic_store(&ctx->last_symbolize_time_ns, 0, memory_order_relaxed); + } + } +#endif } } -static void InitializeMemoryProfile() { - if (flags()->profile_memory == 0 || flags()->profile_memory[0] == 0) - return; - InternalScopedBuffer filename(4096); - internal_snprintf(filename.data(), filename.size(), "%s.%d", - flags()->profile_memory, GetPid()); - fd_t fd = OpenFile(filename.data(), true); - if (fd == kInvalidFd) { - Printf("Failed to open memory profile file '%s'\n", &filename[0]); - Die(); - } - internal_start_thread(&MemoryProfileThread, (void*)(uptr)fd); -} - -static void MemoryFlushThread(void *arg) { - ScopedInRtl in_rtl; - for (int i = 0; ; i++) { - SleepForMillis(flags()->flush_memory_ms); - FlushShadowMemory(); - } -} - -static void InitializeMemoryFlush() { - if (flags()->flush_memory_ms == 0) - return; - if (flags()->flush_memory_ms < 100) - flags()->flush_memory_ms = 100; - internal_start_thread(&MemoryFlushThread, 0); +void DontNeedShadowFor(uptr addr, uptr size) { + uptr shadow_beg = MemToShadow(addr); + uptr shadow_end = MemToShadow(addr + size); + FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); } void MapShadow(uptr addr, uptr size) { @@ -201,9 +202,6 @@ void Initialize(ThreadState *thr) { #ifndef TSAN_GO InitializeShadowMemory(); #endif - ctx->dead_list_size = 0; - ctx->dead_list_head = 0; - ctx->dead_list_tail = 0; InitializeFlags(&ctx->flags, env); // Setup correct file descriptor for error reports. if (internal_strcmp(flags()->log_path, "stdout") == 0) @@ -217,32 +215,30 @@ void Initialize(ThreadState *thr) { // Initialize external symbolizer before internal threads are started. const char *external_symbolizer = flags()->external_symbolizer_path; if (external_symbolizer != 0 && external_symbolizer[0] != '\0') { - if (!InitializeExternalSymbolizer(external_symbolizer)) { + if (!getSymbolizer()->InitializeExternal(external_symbolizer)) { Printf("Failed to start external symbolizer: '%s'\n", external_symbolizer); Die(); } } #endif - InitializeMemoryProfile(); - InitializeMemoryFlush(); + internal_start_thread(&BackgroundThread, 0); if (ctx->flags.verbosity) Printf("***** Running under ThreadSanitizer v2 (pid %d) *****\n", - GetPid()); + (int)internal_getpid()); // Initialize thread 0. - ctx->thread_seq = 0; int tid = ThreadCreate(thr, 0, 0, true); CHECK_EQ(tid, 0); - ThreadStart(thr, tid, GetPid()); + ThreadStart(thr, tid, internal_getpid()); CHECK_EQ(thr->in_rtl, 1); ctx->initialized = true; if (flags()->stop_on_start) { Printf("ThreadSanitizer is suspended at startup (pid %d)." " Call __tsan_resume().\n", - GetPid()); + (int)internal_getpid()); while (__tsan_resumed == 0) {} } } @@ -257,6 +253,8 @@ int Finalize(ThreadState *thr) { // Wait for pending reports. ctx->report_mtx.Lock(); + CommonSanitizerReportMutex.Lock(); + CommonSanitizerReportMutex.Unlock(); ctx->report_mtx.Unlock(); #ifndef TSAN_GO @@ -281,6 +279,13 @@ int Finalize(ThreadState *thr) { ctx->nmissed_expected); } + if (flags()->print_suppressions) + PrintMatchedSuppressions(); +#ifndef TSAN_GO + if (flags()->print_benign) + PrintMatchedBenignRaces(); +#endif + failed = OnFinalize(failed); StatAggregate(ctx->stat, thr->stat); @@ -307,15 +312,20 @@ u32 CurrentStackId(ThreadState *thr, uptr pc) { void TraceSwitch(ThreadState *thr) { thr->nomalloc++; ScopedInRtl in_rtl; - Lock l(&thr->trace.mtx); + 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]; + TraceHeader *hdr = &thr_trace->headers[trace]; hdr->epoch0 = thr->fast_state.epoch(); hdr->stack0.ObtainCurrent(thr, 0); hdr->mset0 = thr->mset; thr->nomalloc--; } +Trace *ThreadTrace(int tid) { + return (Trace*)GetThreadTraceHeader(tid); +} + uptr TraceTopPC(ThreadState *thr) { Event *events = (Event*)GetThreadTrace(thr->tid); uptr pc = events[thr->fast_state.GetTracePos()]; @@ -341,18 +351,18 @@ extern "C" void __tsan_report_race() { #endif ALWAYS_INLINE -static Shadow LoadShadow(u64 *p) { +Shadow LoadShadow(u64 *p) { u64 raw = atomic_load((atomic_uint64_t*)p, memory_order_relaxed); return Shadow(raw); } ALWAYS_INLINE -static void StoreShadow(u64 *sp, u64 s) { +void StoreShadow(u64 *sp, u64 s) { atomic_store((atomic_uint64_t*)sp, s, memory_order_relaxed); } ALWAYS_INLINE -static void StoreIfNotYetStored(u64 *sp, u64 *s) { +void StoreIfNotYetStored(u64 *sp, u64 *s) { StoreShadow(sp, *s); *s = 0; } @@ -377,7 +387,7 @@ static inline bool HappensBefore(Shadow old, ThreadState *thr) { return thr->clock.get(old.TidWithIgnore()) >= old.epoch(); } -ALWAYS_INLINE +ALWAYS_INLINE USED void MemoryAccessImpl(ThreadState *thr, uptr addr, int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, u64 *shadow_mem, Shadow cur) { @@ -451,7 +461,28 @@ void MemoryAccessImpl(ThreadState *thr, uptr addr, return; } -ALWAYS_INLINE +void UnalignedMemoryAccess(ThreadState *thr, uptr pc, uptr addr, + int size, bool kAccessIsWrite, bool kIsAtomic) { + while (size) { + int size1 = 1; + int kAccessSizeLog = kSizeLog1; + if (size >= 8 && (addr & ~7) == ((addr + 8) & ~7)) { + size1 = 8; + kAccessSizeLog = kSizeLog8; + } else if (size >= 4 && (addr & ~7) == ((addr + 4) & ~7)) { + size1 = 4; + kAccessSizeLog = kSizeLog4; + } else if (size >= 2 && (addr & ~7) == ((addr + 2) & ~7)) { + size1 = 2; + kAccessSizeLog = kSizeLog2; + } + MemoryAccess(thr, pc, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic); + addr += size1; + size -= size1; + } +} + +ALWAYS_INLINE USED void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic) { u64 *shadow_mem = (u64*)MemToShadow(addr); @@ -472,6 +503,16 @@ void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, } #endif + if (*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); + StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead); + StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog)); + StatInc(thr, StatMopRodata); + return; + } + FastState fast_state = thr->fast_state; if (fast_state.GetIgnoreBit()) return; @@ -492,6 +533,8 @@ void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size, u64 val) { + (void)thr; + (void)pc; if (size == 0) return; // FIXME: fix me. @@ -508,23 +551,44 @@ static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size, // let it just crash as usual. if (!IsAppMem(addr) || !IsAppMem(addr + size - 1)) return; - (void)thr; - (void)pc; - // Some programs mmap like hundreds of GBs but actually used a small part. - // So, it's better to report a false positive on the memory - // then to hang here senselessly. - const uptr kMaxResetSize = 4ull*1024*1024*1024; - if (size > kMaxResetSize) - size = kMaxResetSize; + // Don't want to touch lots of shadow memory. + // If a program maps 10MB stack, there is no need reset the whole range. size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1); - u64 *p = (u64*)MemToShadow(addr); - CHECK(IsShadowMem((uptr)p)); - CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1))); - // FIXME: may overwrite a part outside the region - for (uptr i = 0; i < size * kShadowCnt / kShadowCell;) { - p[i++] = val; - for (uptr j = 1; j < kShadowCnt; j++) - p[i++] = 0; + // UnmapOrDie/MmapFixedNoReserve does not work on Windows, + // so we do it only for C/C++. + if (kGoMode || size < 64*1024) { + u64 *p = (u64*)MemToShadow(addr); + CHECK(IsShadowMem((uptr)p)); + CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1))); + // FIXME: may overwrite a part outside the region + for (uptr i = 0; i < size / kShadowCell * kShadowCnt;) { + p[i++] = val; + for (uptr j = 1; j < kShadowCnt; j++) + p[i++] = 0; + } + } else { + // The region is big, reset only beginning and end. + const uptr kPageSize = 4096; + u64 *begin = (u64*)MemToShadow(addr); + u64 *end = begin + size / kShadowCell * kShadowCnt; + u64 *p = begin; + // Set at least first kPageSize/2 to page boundary. + while ((p < begin + kPageSize / kShadowSize / 2) || ((uptr)p % kPageSize)) { + *p++ = val; + for (uptr j = 1; j < kShadowCnt; j++) + *p++ = 0; + } + // Reset middle part. + u64 *p1 = p; + p = RoundDown(end, kPageSize); + UnmapOrDie((void*)p1, (uptr)p - (uptr)p1); + MmapFixedNoReserve((uptr)p1, (uptr)p - (uptr)p1); + // Set the ending. + while (p < end) { + *p++ = val; + for (uptr j = 1; j < kShadowCnt; j++) + *p++ = 0; + } } } @@ -533,10 +597,17 @@ void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) { } void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) { + // Processing more than 1k (4k of shadow) is expensive, + // can cause excessive memory consumption (user does not necessary touch + // the whole range) and most likely unnecessary. + if (size > 1024) + size = 1024; CHECK_EQ(thr->is_freeing, false); 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); Shadow s(thr->fast_state); s.ClearIgnoreBit(); s.MarkAsFreed(); @@ -546,6 +617,8 @@ void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) { } void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) { + thr->fast_state.IncrementEpoch(); + TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc); Shadow s(thr->fast_state); s.ClearIgnoreBit(); s.SetWrite(true); @@ -553,7 +626,7 @@ void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) { MemoryRangeSet(thr, pc, addr, size, s.raw()); } -ALWAYS_INLINE +ALWAYS_INLINE USED void FuncEntry(ThreadState *thr, uptr pc) { DCHECK_EQ(thr->in_rtl, 0); StatInc(thr, StatFuncEnter); @@ -583,7 +656,7 @@ void FuncEntry(ThreadState *thr, uptr pc) { thr->shadow_stack_pos++; } -ALWAYS_INLINE +ALWAYS_INLINE USED void FuncExit(ThreadState *thr) { DCHECK_EQ(thr->in_rtl, 0); StatInc(thr, StatFuncExit); @@ -598,13 +671,18 @@ void FuncExit(ThreadState *thr) { thr->shadow_stack_pos--; } -void IgnoreCtl(ThreadState *thr, bool write, bool begin) { - DPrintf("#%d: IgnoreCtl(%d, %d)\n", thr->tid, write, begin); - thr->ignore_reads_and_writes += begin ? 1 : -1; +void ThreadIgnoreBegin(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid); + thr->ignore_reads_and_writes++; CHECK_GE(thr->ignore_reads_and_writes, 0); - if (thr->ignore_reads_and_writes) - thr->fast_state.SetIgnoreBit(); - else + thr->fast_state.SetIgnoreBit(); +} + +void ThreadIgnoreEnd(ThreadState *thr) { + DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid); + thr->ignore_reads_and_writes--; + CHECK_GE(thr->ignore_reads_and_writes, 0); + if (thr->ignore_reads_and_writes == 0) thr->fast_state.ClearIgnoreBit(); } diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h index e939921049a..2548f67b25c 100644 --- a/libsanitizer/tsan/tsan_rtl.h +++ b/libsanitizer/tsan/tsan_rtl.h @@ -24,8 +24,11 @@ #ifndef TSAN_RTL_H #define TSAN_RTL_H -#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_suppressions.h" +#include "sanitizer_common/sanitizer_thread_registry.h" #include "tsan_clock.h" #include "tsan_defs.h" #include "tsan_flags.h" @@ -44,15 +47,73 @@ namespace __tsan { // Descriptor of user's memory block. struct MBlock { - Mutex mtx; - uptr size; - u32 alloc_tid; - u32 alloc_stack_id; - SyncVar *head; + /* + 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]; - MBlock() - : mtx(MutexTypeMBlock, StatMtxMBlock) { + 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 ScopedLock; }; #ifndef TSAN_GO @@ -63,22 +124,11 @@ const uptr kAllocatorSpace = 0x7d0000000000ULL; #endif const uptr kAllocatorSize = 0x10000000000ULL; // 1T. -struct TsanMapUnmapCallback { - 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. - uptr shadow_beg = MemToShadow(p); - uptr shadow_end = MemToShadow(p + size); - CHECK(IsAligned(shadow_end|shadow_beg, GetPageSizeCached())); - FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); - } -}; - +struct MapUnmapCallback; typedef SizeClassAllocator64 PrimaryAllocator; + DefaultSizeClassMap, MapUnmapCallback> PrimaryAllocator; typedef SizeClassAllocatorLocalCache AllocatorCache; -typedef LargeMmapAllocator SecondaryAllocator; +typedef LargeMmapAllocator SecondaryAllocator; typedef CombinedAllocator Allocator; Allocator *allocator(); @@ -87,6 +137,8 @@ Allocator *allocator(); void TsanCheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); +const u64 kShadowRodata = (u64)-1; // .rodata shadow marker + // FastState (from most significant bit): // ignore : 1 // tid : kTidBits @@ -332,6 +384,12 @@ class Shadow : public FastState { struct SignalContext; +struct JmpBuf { + uptr sp; + uptr mangled_sp; + uptr *shadow_stack_pos; +}; + // This struct is stored in TLS. struct ThreadState { FastState fast_state; @@ -354,7 +412,6 @@ struct ThreadState { uptr *shadow_stack_pos; u64 *racy_shadow_addr; u64 racy_state[2]; - Trace trace; #ifndef TSAN_GO // C/C++ uses embed shadow stack of fixed size. uptr shadow_stack[kShadowStackSize]; @@ -367,6 +424,8 @@ struct ThreadState { ThreadClock clock; #ifndef TSAN_GO AllocatorCache alloc_cache; + InternalAllocatorCache internal_alloc_cache; + Vector jmp_bufs; #endif u64 stat[StatCnt]; const int tid; @@ -375,6 +434,7 @@ struct ThreadState { bool in_symbolizer; bool is_alive; bool is_freeing; + bool is_vptr_access; const uptr stk_addr; const uptr stk_size; const uptr tls_addr; @@ -408,41 +468,30 @@ INLINE ThreadState *cur_thread() { } #endif -enum ThreadStatus { - ThreadStatusInvalid, // Non-existent thread, data is invalid. - ThreadStatusCreated, // Created but not yet running. - ThreadStatusRunning, // The thread is currently running. - ThreadStatusFinished, // Joinable thread is finished but not yet joined. - ThreadStatusDead // Joined, but some info (trace) is still alive. -}; - -// An info about a thread that is hold for some time after its termination. -struct ThreadDeadInfo { - Trace trace; -}; - -struct ThreadContext { - const int tid; - int unique_id; // Non-rolling thread id. - uptr os_id; // pid - uptr user_id; // Some opaque user thread id (e.g. pthread_t). +class ThreadContext : public ThreadContextBase { + public: + explicit ThreadContext(int tid); + ~ThreadContext(); ThreadState *thr; - ThreadStatus status; - bool detached; - int reuse_count; +#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, // the event is from a dead thread that shared tid with this thread. u64 epoch0; u64 epoch1; - StackTrace creation_stack; - int creation_tid; - ThreadDeadInfo *dead_info; - ThreadContext *dead_next; // In dead thread list. - char *name; // As annotated by user. - explicit ThreadContext(int tid); + // Override superclass callbacks. + void OnDead(); + void OnJoined(void *arg); + void OnFinished(); + void OnStarted(void *arg); + void OnCreated(void *arg); + void OnReset(); }; struct RacyStacks { @@ -464,6 +513,7 @@ struct RacyAddress { struct FiredSuppression { ReportType type; uptr pc; + Suppression *supp; }; struct Context { @@ -476,20 +526,14 @@ struct Context { Mutex report_mtx; int nreported; int nmissed_expected; + atomic_uint64_t last_symbolize_time_ns; - Mutex thread_mtx; - unsigned thread_seq; - unsigned unique_thread_seq; - int alive_threads; - int max_alive_threads; - ThreadContext *threads[kMaxTid]; - int dead_list_size; - ThreadContext* dead_list_head; - ThreadContext* dead_list_tail; + ThreadRegistry *thread_registry; Vector racy_stacks; Vector racy_addresses; - Vector fired_suppressions; + // Number of fired suppressions may be large enough. + InternalMmapVector fired_suppressions; Flags flags; @@ -520,6 +564,7 @@ class ScopedReport { void AddMutex(const SyncVar *s); void AddLocation(uptr addr, uptr size); void AddSleep(u32 stack_id); + void SetCount(int count); const ReportDesc *GetReport() const; @@ -537,13 +582,18 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset); void StatAggregate(u64 *dst, u64 *src); void StatOutput(u64 *stat); -void ALWAYS_INLINE INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) { +void ALWAYS_INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) { if (kCollectStats) thr->stat[typ] += n; } +void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) { + if (kCollectStats) + thr->stat[typ] = n; +} void MapShadow(uptr addr, uptr size); void MapThreadTrace(uptr addr, uptr size); +void DontNeedShadowFor(uptr addr, uptr size); void InitializeShadowMemory(); void InitializeInterceptors(); void InitializeDynamicAnnotations(); @@ -552,11 +602,13 @@ void ReportRace(ThreadState *thr); bool OutputReport(Context *ctx, const ScopedReport &srep, const ReportStack *suppress_stack1 = 0, - const ReportStack *suppress_stack2 = 0); + const ReportStack *suppress_stack2 = 0, + const ReportLocation *suppress_loc = 0); bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, const StackTrace &trace); bool IsExpectedReport(uptr addr, uptr size); +void PrintMatchedBenignRaces(); bool FrameIsInternal(const ReportStack *frame); ReportStack *SkipTsanInternalFrames(ReportStack *ent); @@ -592,28 +644,30 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, uptr size, bool is_write); void MemoryAccessRangeStep(ThreadState *thr, uptr pc, uptr addr, uptr size, uptr step, bool is_write); +void UnalignedMemoryAccess(ThreadState *thr, uptr pc, uptr addr, + int size, bool kAccessIsWrite, bool kIsAtomic); const int kSizeLog1 = 0; const int kSizeLog2 = 1; const int kSizeLog4 = 2; const int kSizeLog8 = 3; -void ALWAYS_INLINE INLINE MemoryRead(ThreadState *thr, uptr pc, +void ALWAYS_INLINE MemoryRead(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, false, false); } -void ALWAYS_INLINE INLINE MemoryWrite(ThreadState *thr, uptr pc, +void ALWAYS_INLINE MemoryWrite(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, true, false); } -void ALWAYS_INLINE INLINE MemoryReadAtomic(ThreadState *thr, uptr pc, +void ALWAYS_INLINE MemoryReadAtomic(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, false, true); } -void ALWAYS_INLINE INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, +void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, true, true); } @@ -621,7 +675,8 @@ void ALWAYS_INLINE INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); -void IgnoreCtl(ThreadState *thr, bool write, bool begin); +void ThreadIgnoreBegin(ThreadState *thr); +void ThreadIgnoreEnd(ThreadState *thr); void FuncEntry(ThreadState *thr, uptr pc); void FuncExit(ThreadState *thr); @@ -640,8 +695,8 @@ void ProcessPendingSignals(ThreadState *thr); 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); -void MutexUnlock(ThreadState *thr, uptr pc, uptr addr); +void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec = 1); +int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false); void MutexReadLock(ThreadState *thr, uptr pc, uptr addr); void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr); @@ -677,9 +732,10 @@ void TraceSwitch(ThreadState *thr); uptr TraceTopPC(ThreadState *thr); uptr TraceSize(); uptr TraceParts(); +Trace *ThreadTrace(int tid); extern "C" void __tsan_trace_switch(); -void ALWAYS_INLINE INLINE TraceAddEvent(ThreadState *thr, FastState fs, +void ALWAYS_INLINE TraceAddEvent(ThreadState *thr, FastState fs, EventType typ, u64 addr) { DCHECK_GE((int)typ, 0); DCHECK_LE((int)typ, 7); diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cc b/libsanitizer/tsan/tsan_rtl_mutex.cc index 22a71503c5c..d274a7a8cc5 100644 --- a/libsanitizer/tsan/tsan_rtl_mutex.cc +++ b/libsanitizer/tsan/tsan_rtl_mutex.cc @@ -61,7 +61,7 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { && s->owner_tid != SyncVar::kInvalidTid && !s->is_broken) { s->is_broken = true; - Lock l(&ctx->thread_mtx); + ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeMutexDestroyLocked); rep.AddMutex(s); StackTrace trace; @@ -77,9 +77,10 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { DestroyAndFree(s); } -void MutexLock(ThreadState *thr, uptr pc, uptr addr) { +void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) { CHECK_GT(thr->in_rtl, 0); - DPrintf("#%d: MutexLock %zx\n", thr->tid, addr); + 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); @@ -92,7 +93,7 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) { } else if (s->owner_tid == thr->tid) { CHECK_GT(s->recursion, 0); } else { - Printf("ThreadSanitizer WARNING: double lock\n"); + Printf("ThreadSanitizer WARNING: double lock of mutex %p\n", addr); PrintCurrentStack(thr, pc); } if (s->recursion == 0) { @@ -105,33 +106,36 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) { } else if (!s->is_recursive) { StatInc(thr, StatMutexRecLock); } - s->recursion++; + s->recursion += rec; thr->mset.Add(s->GetId(), true, thr->fast_state.epoch()); s->mtx.Unlock(); } -void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) { +int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { CHECK_GT(thr->in_rtl, 0); - DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr); + 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); 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\n"); + Printf("ThreadSanitizer WARNING: unlock of unlocked mutex %p\n", addr); PrintCurrentStack(thr, pc); } } else if (s->owner_tid != thr->tid) { if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n"); + Printf("ThreadSanitizer WARNING: mutex %p is unlocked by wrong thread\n", + addr); PrintCurrentStack(thr, pc); } } else { - s->recursion--; + rec = all ? s->recursion : 1; + s->recursion -= rec; if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); s->owner_tid = SyncVar::kInvalidTid; @@ -145,6 +149,7 @@ void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) { } thr->mset.Del(s->GetId(), true); s->mtx.Unlock(); + return rec; } void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) { @@ -157,7 +162,8 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId()); if (s->owner_tid != SyncVar::kInvalidTid) { - Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n"); + Printf("ThreadSanitizer WARNING: read lock of a write locked mutex %p\n", + addr); PrintCurrentStack(thr, pc); } thr->clock.set(thr->tid, thr->fast_state.epoch()); @@ -178,8 +184,8 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); if (s->owner_tid != SyncVar::kInvalidTid) { - Printf("ThreadSanitizer WARNING: read unlock of a write " - "locked mutex\n"); + Printf("ThreadSanitizer WARNING: read unlock of a write locked mutex %p\n", + addr); PrintCurrentStack(thr, pc); } thr->clock.set(thr->tid, thr->fast_state.epoch()); @@ -229,7 +235,8 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { } } else if (!s->is_broken) { s->is_broken = true; - Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n"); + Printf("ThreadSanitizer WARNING: mutex %p is unlock by wrong thread\n", + addr); PrintCurrentStack(thr, pc); } thr->mset.Del(s->GetId(), write); @@ -246,18 +253,19 @@ void Acquire(ThreadState *thr, uptr pc, uptr addr) { s->mtx.ReadUnlock(); } +static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) { + ThreadState *thr = reinterpret_cast(arg); + ThreadContext *tctx = static_cast(tctx_base); + if (tctx->status == ThreadStatusRunning) + thr->clock.set(tctx->tid, tctx->thr->fast_state.epoch()); + else + thr->clock.set(tctx->tid, tctx->epoch1); +} + void AcquireGlobal(ThreadState *thr, uptr pc) { - Context *ctx = CTX(); - Lock l(&ctx->thread_mtx); - for (unsigned i = 0; i < kMaxTid; i++) { - ThreadContext *tctx = ctx->threads[i]; - if (tctx == 0) - continue; - if (tctx->status == ThreadStatusRunning) - thr->clock.set(i, tctx->thr->fast_state.epoch()); - else - thr->clock.set(i, tctx->epoch1); - } + ThreadRegistryLock l(CTX()->thread_registry); + CTX()->thread_registry->RunCallbackForEachThreadLocked( + UpdateClockCallback, thr); } void Release(ThreadState *thr, uptr pc, uptr addr) { @@ -281,19 +289,20 @@ void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) { } #ifndef TSAN_GO +static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) { + ThreadState *thr = reinterpret_cast(arg); + ThreadContext *tctx = static_cast(tctx_base); + if (tctx->status == ThreadStatusRunning) + thr->last_sleep_clock.set(tctx->tid, tctx->thr->fast_state.epoch()); + else + thr->last_sleep_clock.set(tctx->tid, tctx->epoch1); +} + void AfterSleep(ThreadState *thr, uptr pc) { - Context *ctx = CTX(); thr->last_sleep_stack_id = CurrentStackId(thr, pc); - Lock l(&ctx->thread_mtx); - for (unsigned i = 0; i < kMaxTid; i++) { - ThreadContext *tctx = ctx->threads[i]; - if (tctx == 0) - continue; - if (tctx->status == ThreadStatusRunning) - thr->last_sleep_clock.set(i, tctx->thr->fast_state.epoch()); - else - thr->last_sleep_clock.set(i, tctx->epoch1); - } + ThreadRegistryLock l(CTX()->thread_registry); + CTX()->thread_registry->RunCallbackForEachThreadLocked( + UpdateSleepClockCallback, thr); } #endif diff --git a/libsanitizer/tsan/tsan_rtl_report.cc b/libsanitizer/tsan/tsan_rtl_report.cc index ff1d43bc9e8..7c0a0280071 100644 --- a/libsanitizer/tsan/tsan_rtl_report.cc +++ b/libsanitizer/tsan/tsan_rtl_report.cc @@ -93,8 +93,9 @@ static void StackStripMain(ReportStack *stack) { DPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc); } #else - if (last && 0 == internal_strcmp(last, "schedunlock")) - last_frame2->next = 0; + // The last frame always point into runtime (gosched0, goexit0, runtime.main). + last_frame2->next = 0; + (void)last; #endif } @@ -103,17 +104,25 @@ static ReportStack *SymbolizeStack(const StackTrace& trace) { 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. - bool is_last = (si == trace.Size() - 1); - ReportStack *ent = SymbolizeCode(trace.Get(si) - !is_last); + const uptr pc1 = __sanitizer::StackTrace::GetPreviousInstructionPc(pc); +#else + // FIXME(dvyukov): Go sometimes uses address of a function as top pc. + uptr pc1 = pc; + if (si != trace.Size() - 1) + pc1 -= 1; +#endif + ReportStack *ent = SymbolizeCode(pc1); CHECK_NE(ent, 0); ReportStack *last = ent; while (last->next) { - last->pc += !is_last; + last->pc = pc; // restore original pc for report last = last->next; } - last->pc += !is_last; + last->pc = pc; // restore original pc for report last->next = stack; stack = ent; } @@ -123,14 +132,16 @@ static ReportStack *SymbolizeStack(const StackTrace& trace) { ScopedReport::ScopedReport(ReportType typ) { ctx_ = CTX(); - ctx_->thread_mtx.CheckLocked(); + ctx_->thread_registry->CheckLocked(); void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc)); rep_ = new(mem) ReportDesc; rep_->typ = typ; ctx_->report_mtx.Lock(); + CommonSanitizerReportMutex.Lock(); } ScopedReport::~ScopedReport() { + CommonSanitizerReportMutex.Unlock(); ctx_->report_mtx.Unlock(); DestroyAndFree(rep_); } @@ -175,7 +186,7 @@ void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, void ScopedReport::AddThread(const ThreadContext *tctx) { for (uptr i = 0; i < rep_->threads.Size(); i++) { - if (rep_->threads[i]->id == tctx->tid) + if ((u32)rep_->threads[i]->id == tctx->tid) return; } void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread)); @@ -185,42 +196,65 @@ void ScopedReport::AddThread(const ThreadContext *tctx) { rt->pid = tctx->os_id; rt->running = (tctx->status == ThreadStatusRunning); rt->name = tctx->name ? internal_strdup(tctx->name) : 0; - rt->parent_tid = tctx->creation_tid; + rt->parent_tid = tctx->parent_tid; + rt->stack = 0; +#ifdef TSAN_GO rt->stack = SymbolizeStack(tctx->creation_stack); +#else + uptr ssz = 0; + const uptr *stack = StackDepotGet(tctx->creation_stack_id, &ssz); + if (stack) { + StackTrace trace; + trace.Init(stack, ssz); + rt->stack = SymbolizeStack(trace); + } +#endif } #ifndef TSAN_GO -static ThreadContext *FindThread(int unique_id) { +static ThreadContext *FindThreadByUidLocked(int unique_id) { Context *ctx = CTX(); - ctx->thread_mtx.CheckLocked(); + ctx->thread_registry->CheckLocked(); for (unsigned i = 0; i < kMaxTid; i++) { - ThreadContext *tctx = ctx->threads[i]; - if (tctx && tctx->unique_id == unique_id) { + ThreadContext *tctx = static_cast( + ctx->thread_registry->GetThreadLocked(i)); + if (tctx && tctx->unique_id == (u32)unique_id) { return tctx; } } return 0; } +static ThreadContext *FindThreadByTidLocked(int tid) { + Context *ctx = CTX(); + ctx->thread_registry->CheckLocked(); + return static_cast( + ctx->thread_registry->GetThreadLocked(tid)); +} + +static bool IsInStackOrTls(ThreadContextBase *tctx_base, void *arg) { + uptr addr = (uptr)arg; + ThreadContext *tctx = static_cast(tctx_base); + if (tctx->status != ThreadStatusRunning) + return false; + ThreadState *thr = tctx->thr; + CHECK(thr); + return ((addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) || + (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size)); +} + ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) { Context *ctx = CTX(); - ctx->thread_mtx.CheckLocked(); - for (unsigned i = 0; i < kMaxTid; i++) { - ThreadContext *tctx = ctx->threads[i]; - if (tctx == 0 || tctx->status != ThreadStatusRunning) - continue; - ThreadState *thr = tctx->thr; - CHECK(thr); - if (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) { - *is_stack = true; - return tctx; - } - if (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size) { - *is_stack = false; - return tctx; - } - } - return 0; + ctx->thread_registry->CheckLocked(); + ThreadContext *tctx = static_cast( + ctx->thread_registry->FindThreadContextLocked(IsInStackOrTls, + (void*)addr)); + if (!tctx) + return 0; + ThreadState *thr = tctx->thr; + CHECK(thr); + *is_stack = (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size); + return tctx; } #endif @@ -234,7 +268,16 @@ void ScopedReport::AddMutex(const SyncVar *s) { rep_->mutexes.PushBack(rm); rm->id = s->uid; rm->destroyed = false; - rm->stack = SymbolizeStack(s->creation_stack); + rm->stack = 0; +#ifndef TSAN_GO + uptr ssz = 0; + const uptr *stack = StackDepotGet(s->creation_stack_id, &ssz); + if (stack) { + StackTrace trace; + trace.Init(stack, ssz); + rm->stack = SymbolizeStack(trace); + } +#endif } void ScopedReport::AddMutex(u64 id) { @@ -272,27 +315,28 @@ void ScopedReport::AddLocation(uptr addr, uptr size) { trace.Init(stack, ssz); loc->stack = SymbolizeStack(trace); } - ThreadContext *tctx = FindThread(creat_tid); + ThreadContext *tctx = FindThreadByUidLocked(creat_tid); if (tctx) AddThread(tctx); return; } - if (allocator()->PointerIsMine((void*)addr)) { - MBlock *b = user_mblock(0, (void*)addr); - ThreadContext *tctx = FindThread(b->alloc_tid); + 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(); rep_->locs.PushBack(loc); loc->type = ReportLocationHeap; loc->addr = (uptr)allocator()->GetBlockBegin((void*)addr); - loc->size = b->size; - loc->tid = tctx ? tctx->tid : b->alloc_tid; + loc->size = b->Size(); + loc->tid = tctx ? tctx->tid : b->Tid(); loc->name = 0; loc->file = 0; loc->line = 0; loc->stack = 0; uptr ssz = 0; - const uptr *stack = StackDepotGet(b->alloc_stack_id, &ssz); + const uptr *stack = StackDepotGet(b->StackId(), &ssz); if (stack) { StackTrace trace; trace.Init(stack, ssz); @@ -331,6 +375,10 @@ void ScopedReport::AddSleep(u32 stack_id) { } #endif +void ScopedReport::SetCount(int count) { + rep_->count = count; +} + const ReportDesc *ScopedReport::GetReport() const { return rep_; } @@ -339,21 +387,17 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *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. - ThreadContext *tctx = CTX()->threads[tid]; + Context *ctx = CTX(); + ctx->thread_registry->CheckLocked(); + ThreadContext *tctx = static_cast( + ctx->thread_registry->GetThreadLocked(tid)); if (tctx == 0) return; - Trace* trace = 0; - if (tctx->status == ThreadStatusRunning) { - CHECK(tctx->thr); - trace = &tctx->thr->trace; - } else if (tctx->status == ThreadStatusFinished - || tctx->status == ThreadStatusDead) { - if (tctx->dead_info == 0) - return; - trace = &tctx->dead_info->trace; - } else { + if (tctx->status != ThreadStatusRunning + && tctx->status != ThreadStatusFinished + && tctx->status != ThreadStatusDead) return; - } + Trace* trace = ThreadTrace(tctx->tid); Lock l(&trace->mtx); const int partidx = (epoch / kTracePartSize) % TraceParts(); TraceHeader* hdr = &trace->headers[partidx]; @@ -464,31 +508,58 @@ static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2], bool OutputReport(Context *ctx, const ScopedReport &srep, const ReportStack *suppress_stack1, - const ReportStack *suppress_stack2) { + const ReportStack *suppress_stack2, + const ReportLocation *suppress_loc) { + atomic_store(&ctx->last_symbolize_time_ns, NanoTime(), memory_order_relaxed); const ReportDesc *rep = srep.GetReport(); - uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack1); + Suppression *supp = 0; + uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack1, &supp); if (suppress_pc == 0) - suppress_pc = IsSuppressed(rep->typ, suppress_stack2); + 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 supp = {srep.GetReport()->typ, suppress_pc}; - ctx->fired_suppressions.PushBack(supp); + FiredSuppression s = {srep.GetReport()->typ, suppress_pc, supp}; + ctx->fired_suppressions.push_back(s); } if (OnReport(rep, suppress_pc != 0)) return false; PrintReport(rep); - CTX()->nreported++; + ctx->nreported++; + if (flags()->halt_on_error) + internal__exit(flags()->exitcode); return true; } bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, const StackTrace &trace) { - for (uptr k = 0; k < ctx->fired_suppressions.Size(); k++) { + for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) continue; for (uptr j = 0; j < trace.Size(); j++) { - if (trace.Get(j) == ctx->fired_suppressions[k].pc) + FiredSuppression *s = &ctx->fired_suppressions[k]; + if (trace.Get(j) == s->pc) { + if (s->supp) + s->supp->hit_count++; return true; + } + } + } + return false; +} + +static bool IsFiredSuppression(Context *ctx, + const ScopedReport &srep, + uptr addr) { + for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { + if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) + continue; + FiredSuppression *s = &ctx->fired_suppressions[k]; + if (addr == s->pc) { + if (s->supp) + s->supp->hit_count++; + return true; } } return false; @@ -525,8 +596,8 @@ static bool IsJavaNonsense(const ReportDesc *rep) { || (frame->func == 0 && frame->file == 0 && frame->line == 0 && frame->module == 0)) { if (frame) { - FiredSuppression supp = {rep->typ, frame->pc}; - CTX()->fired_suppressions.PushBack(supp); + FiredSuppression supp = {rep->typ, frame->pc, 0}; + CTX()->fired_suppressions.push_back(supp); } return true; } @@ -557,10 +628,6 @@ void ReportRace(ThreadState *thr) { if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr)) return; - if (thr->in_signal_handler) - Printf("ThreadSanitizer: printing report from signal handler." - " Can crash or hang.\n"); - bool freed = false; { Shadow s(thr->racy_state[1]); @@ -583,9 +650,16 @@ void ReportRace(ThreadState *thr) { } Context *ctx = CTX(); - Lock l0(&ctx->thread_mtx); + ThreadRegistryLock l0(ctx->thread_registry); - ScopedReport rep(freed ? ReportTypeUseAfterFree : ReportTypeRace); + ReportType typ = ReportTypeRace; + if (thr->is_vptr_access) + typ = ReportTypeVptrRace; + else if (freed) + typ = ReportTypeUseAfterFree; + ScopedReport rep(typ); + if (IsFiredSuppression(ctx, rep, addr)) + return; const uptr kMop = 2; StackTrace traces[kMop]; const uptr toppc = TraceTopPC(thr); @@ -596,6 +670,8 @@ void ReportRace(ThreadState *thr) { new(mset2.data()) MutexSet(); Shadow s2(thr->racy_state[1]); RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data()); + if (IsFiredSuppression(ctx, rep, traces[1])) + return; if (HandleRacyStacks(thr, traces, addr_min, addr_max)) return; @@ -611,7 +687,8 @@ void ReportRace(ThreadState *thr) { for (uptr i = 0; i < kMop; i++) { FastState s(thr->racy_state[i]); - ThreadContext *tctx = ctx->threads[s.tid()]; + ThreadContext *tctx = static_cast( + ctx->thread_registry->GetThreadLocked(s.tid())); if (s.epoch() < tctx->epoch0 || s.epoch() > tctx->epoch1) continue; rep.AddThread(tctx); @@ -627,8 +704,11 @@ void ReportRace(ThreadState *thr) { } #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)) + rep.GetReport()->mops[1]->stack, + suppress_loc)) return; AddRacyStacks(thr, traces, addr_min, addr_max); @@ -646,6 +726,11 @@ void PrintCurrentStackSlow() { sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace; ptrace->SlowUnwindStack(__sanitizer::StackTrace::GetCurrentPc(), kStackTraceMax); + 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; + } StackTrace trace; trace.Init(ptrace->trace, ptrace->size); PrintStack(SymbolizeStack(trace)); diff --git a/libsanitizer/tsan/tsan_rtl_thread.cc b/libsanitizer/tsan/tsan_rtl_thread.cc index e30916dc627..9811e1c015f 100644 --- a/libsanitizer/tsan/tsan_rtl_thread.cc +++ b/libsanitizer/tsan/tsan_rtl_thread.cc @@ -18,144 +18,193 @@ namespace __tsan { +// ThreadContext implementation. + +ThreadContext::ThreadContext(int tid) + : ThreadContextBase(tid) + , thr() + , sync() + , epoch0() + , epoch1() { +} + #ifndef TSAN_GO -const int kThreadQuarantineSize = 16; -#else -const int kThreadQuarantineSize = 64; +ThreadContext::~ThreadContext() { +} #endif -static void MaybeReportThreadLeak(ThreadContext *tctx) { - if (tctx->detached) +void ThreadContext::OnDead() { + sync.Reset(); +} + +void ThreadContext::OnJoined(void *arg) { + ThreadState *caller_thr = static_cast(arg); + caller_thr->clock.acquire(&sync); + StatInc(caller_thr, StatSyncAcquire); + sync.Reset(); +} + +struct OnCreatedArgs { + ThreadState *thr; + uptr pc; +}; + +void ThreadContext::OnCreated(void *arg) { + thr = 0; + if (tid == 0) return; - if (tctx->status != ThreadStatusCreated - && tctx->status != ThreadStatusRunning - && tctx->status != ThreadStatusFinished) - return; - ScopedReport rep(ReportTypeThreadLeak); - rep.AddThread(tctx); - OutputReport(CTX(), rep); + OnCreatedArgs *args = static_cast(arg); + 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); + args->thr->clock.set(args->thr->tid, args->thr->fast_state.epoch()); + args->thr->fast_synch_epoch = args->thr->fast_state.epoch(); + args->thr->clock.release(&sync); + StatInc(args->thr, StatSyncRelease); +#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 ThreadFinalize(ThreadState *thr) { - CHECK_GT(thr->in_rtl, 0); - if (!flags()->report_thread_leaks) - return; - Context *ctx = CTX(); - Lock l(&ctx->thread_mtx); - for (unsigned i = 0; i < kMaxTid; i++) { - ThreadContext *tctx = ctx->threads[i]; - if (tctx == 0) - continue; - MaybeReportThreadLeak(tctx); - } +void ThreadContext::OnReset() { + sync.Reset(); + FlushUnneededShadowMemory(GetThreadTrace(tid), TraceSize() * sizeof(Event)); + //!!! FlushUnneededShadowMemory(GetThreadTraceHeader(tid), sizeof(Trace)); } -int ThreadCount(ThreadState *thr) { - CHECK_GT(thr->in_rtl, 0); - Context *ctx = CTX(); - Lock l(&ctx->thread_mtx); - int cnt = 0; - for (unsigned i = 0; i < kMaxTid; i++) { - ThreadContext *tctx = ctx->threads[i]; - if (tctx == 0) - continue; - if (tctx->status != ThreadStatusCreated - && tctx->status != ThreadStatusRunning) - continue; - cnt++; - } - return cnt; +struct OnStartedArgs { + ThreadState *thr; + uptr stk_addr; + uptr stk_size; + uptr tls_addr; + uptr tls_size; +}; + +void ThreadContext::OnStarted(void *arg) { + OnStartedArgs *args = static_cast(arg); + thr = args->thr; + // RoundUp so that one trace part does not contain events + // 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); +#ifdef TSAN_GO + // Setup dynamic shadow stack. + const int kInitStackSize = 8; + args->thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, + kInitStackSize * sizeof(uptr)); + args->thr->shadow_stack_pos = thr->shadow_stack; + args->thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; +#endif +#ifndef TSAN_GO + AllocatorThreadStart(args->thr); +#endif + thr = args->thr; + thr->fast_synch_epoch = epoch0; + thr->clock.set(tid, epoch0); + thr->clock.acquire(&sync); + thr->fast_state.SetHistorySize(flags()->history_size); + const uptr trace = (epoch0 / kTracePartSize) % TraceParts(); + Trace *thr_trace = ThreadTrace(thr->tid); + thr_trace->headers[trace].epoch0 = epoch0; + StatInc(thr, StatSyncAcquire); + sync.Reset(); + 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; } -static void ThreadDead(ThreadState *thr, ThreadContext *tctx) { - Context *ctx = CTX(); - CHECK_GT(thr->in_rtl, 0); - CHECK(tctx->status == ThreadStatusRunning - || tctx->status == ThreadStatusFinished); - DPrintf("#%d: ThreadDead uid=%zu\n", thr->tid, tctx->user_id); - tctx->status = ThreadStatusDead; - tctx->user_id = 0; - tctx->sync.Reset(); - - // Put to dead list. - tctx->dead_next = 0; - if (ctx->dead_list_size == 0) - ctx->dead_list_head = tctx; - else - ctx->dead_list_tail->dead_next = tctx; - ctx->dead_list_tail = tctx; - ctx->dead_list_size++; -} - -int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) { - CHECK_GT(thr->in_rtl, 0); - Context *ctx = CTX(); - Lock l(&ctx->thread_mtx); - StatInc(thr, StatThreadCreate); - int tid = -1; - ThreadContext *tctx = 0; - if (ctx->dead_list_size > kThreadQuarantineSize - || ctx->thread_seq >= kMaxTid) { - // Reusing old thread descriptor and tid. - if (ctx->dead_list_size == 0) { - Printf("ThreadSanitizer: %d thread limit exceeded. Dying.\n", - kMaxTid); - Die(); - } - StatInc(thr, StatThreadReuse); - tctx = ctx->dead_list_head; - ctx->dead_list_head = tctx->dead_next; - ctx->dead_list_size--; - if (ctx->dead_list_size == 0) { - CHECK_EQ(tctx->dead_next, 0); - ctx->dead_list_head = 0; - } - CHECK_EQ(tctx->status, ThreadStatusDead); - tctx->status = ThreadStatusInvalid; - tctx->reuse_count++; - tctx->sync.Reset(); - tid = tctx->tid; - DestroyAndFree(tctx->dead_info); - if (tctx->name) { - internal_free(tctx->name); - tctx->name = 0; - } - } else { - // Allocating new thread descriptor and tid. - StatInc(thr, StatThreadMaxTid); - tid = ctx->thread_seq++; - void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext)); - tctx = new(mem) ThreadContext(tid); - ctx->threads[tid] = tctx; - MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event)); - } - CHECK_NE(tctx, 0); - CHECK_GE(tid, 0); - CHECK_LT(tid, kMaxTid); - DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", thr->tid, tid, uid); - CHECK_EQ(tctx->status, ThreadStatusInvalid); - ctx->alive_threads++; - if (ctx->max_alive_threads < ctx->alive_threads) { - ctx->max_alive_threads++; - CHECK_EQ(ctx->max_alive_threads, ctx->alive_threads); - StatInc(thr, StatThreadMaxAlive); - } - tctx->status = ThreadStatusCreated; - tctx->thr = 0; - tctx->user_id = uid; - tctx->unique_id = ctx->unique_thread_seq++; - tctx->detached = detached; - if (tid) { +void ThreadContext::OnFinished() { + if (!detached) { thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); thr->clock.set(thr->tid, thr->fast_state.epoch()); thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&tctx->sync); + thr->clock.release(&sync); StatInc(thr, StatSyncRelease); - tctx->creation_stack.ObtainCurrent(thr, pc); - tctx->creation_tid = thr->tid; } + epoch1 = thr->fast_state.epoch(); + +#ifndef TSAN_GO + AllocatorThreadFinish(thr); +#endif + thr->~ThreadState(); + StatAggregate(CTX()->stat, thr->stat); + thr = 0; +} + +#ifndef TSAN_GO +struct ThreadLeak { + ThreadContext *tctx; + int count; +}; + +static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) { + Vector &leaks = *(Vector*)arg; + ThreadContext *tctx = static_cast(tctx_base); + if (tctx->detached || tctx->status != ThreadStatusFinished) + return; + for (uptr i = 0; i < leaks.Size(); i++) { + if (leaks[i].tctx->creation_stack_id == tctx->creation_stack_id) { + leaks[i].count++; + return; + } + } + ThreadLeak leak = {tctx, 1}; + leaks.PushBack(leak); +} +#endif + +static void ThreadCheckIgnore(ThreadState *thr) { + if (thr->ignore_reads_and_writes) { + Printf("ThreadSanitizer: thread T%d finished with ignores enabled.\n", + thr->tid); + } +} + +void ThreadFinalize(ThreadState *thr) { + CHECK_GT(thr->in_rtl, 0); + ThreadCheckIgnore(thr); +#ifndef TSAN_GO + if (!flags()->report_thread_leaks) + return; + ThreadRegistryLock l(CTX()->thread_registry); + Vector leaks(MBlockScopedBuf); + CTX()->thread_registry->RunCallbackForEachThreadLocked( + MaybeReportThreadLeak, &leaks); + for (uptr i = 0; i < leaks.Size(); i++) { + ScopedReport rep(ReportTypeThreadLeak); + rep.AddThread(leaks[i].tctx); + rep.SetCount(leaks[i].count); + OutputReport(CTX(), 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); + StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads()); return tid; } @@ -168,9 +217,8 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) { GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size); if (tid) { - if (stk_addr && stk_size) { - MemoryResetRange(thr, /*pc=*/ 1, stk_addr, stk_size); - } + if (stk_addr && stk_size) + MemoryRangeImitateWrite(thr, /*pc=*/ 1, stk_addr, stk_size); if (tls_addr && tls_size) { // Check that the thr object is in tls; @@ -181,116 +229,42 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) { CHECK_GE(thr_end, tls_addr); CHECK_LE(thr_end, tls_addr + tls_size); // Since the thr object is huge, skip it. - MemoryResetRange(thr, /*pc=*/ 2, tls_addr, thr_beg - tls_addr); - MemoryResetRange(thr, /*pc=*/ 2, thr_end, tls_addr + tls_size - thr_end); + MemoryRangeImitateWrite(thr, /*pc=*/ 2, tls_addr, thr_beg - tls_addr); + MemoryRangeImitateWrite(thr, /*pc=*/ 2, + thr_end, tls_addr + tls_size - thr_end); } } - Lock l(&CTX()->thread_mtx); - ThreadContext *tctx = CTX()->threads[tid]; - CHECK_NE(tctx, 0); - CHECK_EQ(tctx->status, ThreadStatusCreated); - tctx->status = ThreadStatusRunning; - tctx->os_id = os_id; - // RoundUp so that one trace part does not contain events - // from different threads. - tctx->epoch0 = RoundUp(tctx->epoch1 + 1, kTracePartSize); - tctx->epoch1 = (u64)-1; - new(thr) ThreadState(CTX(), tid, tctx->unique_id, - tctx->epoch0, stk_addr, stk_size, - tls_addr, tls_size); -#ifdef TSAN_GO - // Setup dynamic shadow stack. - const int kInitStackSize = 8; - thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, - kInitStackSize * sizeof(uptr)); - thr->shadow_stack_pos = thr->shadow_stack; - thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; -#endif -#ifndef TSAN_GO - AllocatorThreadStart(thr); -#endif - tctx->thr = thr; - thr->fast_synch_epoch = tctx->epoch0; - thr->clock.set(tid, tctx->epoch0); - thr->clock.acquire(&tctx->sync); - thr->fast_state.SetHistorySize(flags()->history_size); - const uptr trace = (tctx->epoch0 / kTracePartSize) % TraceParts(); - thr->trace.headers[trace].epoch0 = tctx->epoch0; - StatInc(thr, StatSyncAcquire); - DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx " - "tls_addr=%zx tls_size=%zx\n", - tid, (uptr)tctx->epoch0, stk_addr, stk_size, tls_addr, tls_size); - thr->is_alive = true; + OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size }; + CTX()->thread_registry->StartThread(tid, os_id, &args); } void ThreadFinish(ThreadState *thr) { CHECK_GT(thr->in_rtl, 0); + ThreadCheckIgnore(thr); StatInc(thr, StatThreadFinish); - // FIXME: Treat it as write. if (thr->stk_addr && thr->stk_size) - MemoryResetRange(thr, /*pc=*/ 3, thr->stk_addr, thr->stk_size); - if (thr->tls_addr && thr->tls_size) { - const uptr thr_beg = (uptr)thr; - const uptr thr_end = (uptr)thr + sizeof(*thr); - // Since the thr object is huge, skip it. - MemoryResetRange(thr, /*pc=*/ 4, thr->tls_addr, thr_beg - thr->tls_addr); - MemoryResetRange(thr, /*pc=*/ 5, - thr_end, thr->tls_addr + thr->tls_size - thr_end); - } + 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(); - Lock l(&ctx->thread_mtx); - ThreadContext *tctx = ctx->threads[thr->tid]; - CHECK_NE(tctx, 0); - CHECK_EQ(tctx->status, ThreadStatusRunning); - CHECK_GT(ctx->alive_threads, 0); - ctx->alive_threads--; - if (tctx->detached) { - ThreadDead(thr, tctx); - } else { - thr->fast_state.IncrementEpoch(); - // Can't increment epoch w/o writing to the trace as well. - TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&tctx->sync); - StatInc(thr, StatSyncRelease); - tctx->status = ThreadStatusFinished; - } + ctx->thread_registry->FinishThread(thr->tid); +} - // Save from info about the thread. - tctx->dead_info = new(internal_alloc(MBlockDeadInfo, sizeof(ThreadDeadInfo))) - ThreadDeadInfo(); - for (uptr i = 0; i < TraceParts(); i++) { - tctx->dead_info->trace.headers[i].epoch0 = thr->trace.headers[i].epoch0; - tctx->dead_info->trace.headers[i].stack0.CopyFrom( - thr->trace.headers[i].stack0); +static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) { + uptr uid = (uptr)arg; + if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) { + tctx->user_id = 0; + return true; } - tctx->epoch1 = thr->fast_state.epoch(); - -#ifndef TSAN_GO - AllocatorThreadFinish(thr); -#endif - thr->~ThreadState(); - StatAggregate(ctx->stat, thr->stat); - tctx->thr = 0; + return false; } int ThreadTid(ThreadState *thr, uptr pc, uptr uid) { CHECK_GT(thr->in_rtl, 0); Context *ctx = CTX(); - Lock l(&ctx->thread_mtx); - int res = -1; - for (unsigned tid = 0; tid < kMaxTid; tid++) { - ThreadContext *tctx = ctx->threads[tid]; - if (tctx != 0 && tctx->user_id == uid - && tctx->status != ThreadStatusInvalid) { - tctx->user_id = 0; - res = tid; - break; - } - } + int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid); DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res); return res; } @@ -301,18 +275,7 @@ void ThreadJoin(ThreadState *thr, uptr pc, int tid) { CHECK_LT(tid, kMaxTid); DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid); Context *ctx = CTX(); - Lock l(&ctx->thread_mtx); - ThreadContext *tctx = ctx->threads[tid]; - if (tctx->status == ThreadStatusInvalid) { - Printf("ThreadSanitizer: join of non-existent thread\n"); - return; - } - // FIXME(dvyukov): print message and continue (it's user error). - CHECK_EQ(tctx->detached, false); - CHECK_EQ(tctx->status, ThreadStatusFinished); - thr->clock.acquire(&tctx->sync); - StatInc(thr, StatSyncAcquire); - ThreadDead(thr, tctx); + ctx->thread_registry->JoinThread(tid, thr); } void ThreadDetach(ThreadState *thr, uptr pc, int tid) { @@ -320,31 +283,12 @@ void ThreadDetach(ThreadState *thr, uptr pc, int tid) { CHECK_GT(tid, 0); CHECK_LT(tid, kMaxTid); Context *ctx = CTX(); - Lock l(&ctx->thread_mtx); - ThreadContext *tctx = ctx->threads[tid]; - if (tctx->status == ThreadStatusInvalid) { - Printf("ThreadSanitizer: detach of non-existent thread\n"); - return; - } - if (tctx->status == ThreadStatusFinished) { - ThreadDead(thr, tctx); - } else { - tctx->detached = true; - } + ctx->thread_registry->DetachThread(tid); } void ThreadSetName(ThreadState *thr, const char *name) { - Context *ctx = CTX(); - Lock l(&ctx->thread_mtx); - ThreadContext *tctx = ctx->threads[thr->tid]; - CHECK_NE(tctx, 0); - CHECK_EQ(tctx->status, ThreadStatusRunning); - if (tctx->name) { - internal_free(tctx->name); - tctx->name = 0; - } - if (name) - tctx->name = internal_strdup(name); + CHECK_GT(thr->in_rtl, 0); + CTX()->thread_registry->SetThreadName(thr->tid, name); } void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, @@ -379,6 +323,13 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, StatInc(thr, StatMopRange); + if (*shadow_mem == kShadowRodata) { + // Access to .rodata section, no races here. + // Measurements show that it can be 10-20% of all memory accesses. + StatInc(thr, StatMopRangeRodata); + return; + } + FastState fast_state = thr->fast_state; if (fast_state.GetIgnoreBit()) return; @@ -421,25 +372,4 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, } } -void MemoryAccessRangeStep(ThreadState *thr, uptr pc, uptr addr, - uptr size, uptr step, bool is_write) { - if (size == 0) - return; - FastState fast_state = thr->fast_state; - if (fast_state.GetIgnoreBit()) - return; - StatInc(thr, StatMopRange); - fast_state.IncrementEpoch(); - thr->fast_state = fast_state; - TraceAddEvent(thr, fast_state, EventTypeMop, pc); - - for (uptr addr_end = addr + size; addr < addr_end; addr += step) { - u64 *shadow_mem = (u64*)MemToShadow(addr); - Shadow cur(fast_state); - cur.SetWrite(is_write); - cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kSizeLog1); - MemoryAccessImpl(thr, addr, kSizeLog1, is_write, false, - shadow_mem, cur); - } -} } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_stat.cc b/libsanitizer/tsan/tsan_stat.cc index fbec4225d9c..3a3ac1172d8 100644 --- a/libsanitizer/tsan/tsan_stat.cc +++ b/libsanitizer/tsan/tsan_stat.cc @@ -36,6 +36,8 @@ void StatOutput(u64 *stat) { name[StatMop8] = " size 8 "; name[StatMopSame] = " Including same "; name[StatMopRange] = " Including range "; + name[StatMopRodata] = " Including .rodata "; + name[StatMopRangeRodata] = " Including .rodata range "; name[StatShadowProcessed] = "Shadow processed "; name[StatShadowZero] = " Including empty "; name[StatShadowNonZero] = " Including non empty "; @@ -103,6 +105,7 @@ void StatOutput(u64 *stat) { 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 "; @@ -133,6 +136,9 @@ void StatOutput(u64 *stat) { 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___cxa_guard_acquire] = " __cxa_guard_acquire "; name[StatInt___cxa_guard_release] = " __cxa_guard_release "; @@ -172,6 +178,7 @@ void StatOutput(u64 *stat) { 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_sem_init] = " sem_init "; name[StatInt_sem_destroy] = " sem_destroy "; name[StatInt_sem_wait] = " sem_wait "; @@ -221,11 +228,13 @@ void StatOutput(u64 *stat) { 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 "; @@ -237,13 +246,21 @@ void StatOutput(u64 *stat) { 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 "; @@ -271,6 +288,87 @@ void StatOutput(u64 *stat) { name[StatInt_ctime_r] = " ctime_r "; name[StatInt_asctime] = " asctime "; name[StatInt_asctime_r] = " asctime_r "; + 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_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[StatAnnotation] = "Dynamic annotations "; name[StatAnnotateHappensBefore] = " HappensBefore "; @@ -280,6 +378,7 @@ void StatOutput(u64 *stat) { name[StatAnnotateMutexIsNotPHB] = " MutexIsNotPHB "; name[StatAnnotateCondVarWait] = " CondVarWait "; name[StatAnnotateRWLockCreate] = " RWLockCreate "; + name[StatAnnotateRWLockCreateStatic] = " StatAnnotateRWLockCreateStatic "; name[StatAnnotateRWLockDestroy] = " RWLockDestroy "; name[StatAnnotateRWLockAcquired] = " RWLockAcquired "; name[StatAnnotateRWLockReleased] = " RWLockReleased "; diff --git a/libsanitizer/tsan/tsan_stat.h b/libsanitizer/tsan/tsan_stat.h index 8b08a024be2..e392ff62c63 100644 --- a/libsanitizer/tsan/tsan_stat.h +++ b/libsanitizer/tsan/tsan_stat.h @@ -25,6 +25,8 @@ enum StatType { StatMop8, StatMopSame, StatMopRange, + StatMopRodata, + StatMopRangeRodata, StatShadowProcessed, StatShadowZero, StatShadowNonZero, // Derived. @@ -100,6 +102,7 @@ enum StatType { StatInt_realloc, StatInt_free, StatInt_cfree, + StatInt_malloc_usable_size, StatInt_mmap, StatInt_mmap64, StatInt_munmap, @@ -129,7 +132,10 @@ enum StatType { StatInt_strncmp, StatInt_strcpy, StatInt_strncpy, + StatInt_strcasecmp, + StatInt_strncasecmp, StatInt_strstr, + StatInt_strdup, StatInt_atexit, StatInt___cxa_guard_acquire, StatInt___cxa_guard_release, @@ -167,6 +173,7 @@ enum StatType { StatInt_pthread_barrier_destroy, StatInt_pthread_barrier_wait, StatInt_pthread_once, + StatInt_pthread_getschedparam, StatInt_sem_init, StatInt_sem_destroy, StatInt_sem_wait, @@ -216,11 +223,13 @@ enum StatType { 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, @@ -232,14 +241,18 @@ enum StatType { 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, @@ -270,6 +283,87 @@ enum StatType { StatInt_ctime_r, StatInt_asctime, StatInt_asctime_r, + 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_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, // Dynamic annotations. StatAnnotation, diff --git a/libsanitizer/tsan/tsan_suppressions.cc b/libsanitizer/tsan/tsan_suppressions.cc index 8ee8de7c278..912b3834e87 100644 --- a/libsanitizer/tsan/tsan_suppressions.cc +++ b/libsanitizer/tsan/tsan_suppressions.cc @@ -11,12 +11,25 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_suppressions.h" #include "tsan_suppressions.h" #include "tsan_rtl.h" #include "tsan_flags.h" #include "tsan_mman.h" #include "tsan_platform.h" +// Suppressions for true/false positives in standard libraries. +static const char *const std_suppressions = +// Libstdc++ 4.4 has data races in std::string. +// See http://crbug.com/181502 for an example. +"race:^_M_rep$\n" +"race:^_M_is_leaked$\n" +// False positive when using std . +// Happens because we miss atomic synchronization in libstdc++. +// See http://llvm.org/bugs/show_bug.cgi?id=17066 for details. +"race:std::_Sp_counted_ptr_inplacenext = head; - head = s; - s->type = stype; - s->templ = (char*)internal_alloc(MBlockSuppression, end2 - line + 1); - internal_memcpy(s->templ, line, end2 - line); - s->templ[end2 - line] = 0; - } - if (end[0] == 0) - break; - line = end + 1; - } - return head; -} - void InitializeSuppressions() { + ALIGNED(64) static char placeholder_[sizeof(SuppressionContext)]; + g_ctx = new(placeholder_) SuppressionContext; const char *supp = ReadFile(flags()->suppressions); - g_suppressions = SuppressionParse(0, supp); + g_ctx->Parse(supp); #ifndef TSAN_GO supp = __tsan_default_suppressions(); - g_suppressions = SuppressionParse(g_suppressions, supp); + g_ctx->Parse(supp); + g_ctx->Parse(std_suppressions); #endif } -uptr IsSuppressed(ReportType typ, const ReportStack *stack) { - if (g_suppressions == 0 || stack == 0) - return 0; - SuppressionType stype; +SuppressionType conv(ReportType typ) { if (typ == ReportTypeRace) - stype = SuppressionRace; + return SuppressionRace; + else if (typ == ReportTypeVptrRace) + return SuppressionRace; + else if (typ == ReportTypeUseAfterFree) + return SuppressionRace; else if (typ == ReportTypeThreadLeak) - stype = SuppressionThread; + return SuppressionThread; else if (typ == ReportTypeMutexDestroyLocked) - stype = SuppressionMutex; + return SuppressionMutex; else if (typ == ReportTypeSignalUnsafe) - stype = SuppressionSignal; - else + return SuppressionSignal; + else if (typ == ReportTypeErrnoInSignal) + return SuppressionNone; + Printf("ThreadSanitizer: unknown report type %d\n", typ), + Die(); +} + +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) return 0; + Suppression *s; for (const ReportStack *frame = stack; frame; frame = frame->next) { - for (Suppression *supp = g_suppressions; supp; supp = supp->next) { - if (stype == supp->type && - (SuppressionMatch(supp->templ, frame->func) || - SuppressionMatch(supp->templ, frame->file) || - SuppressionMatch(supp->templ, frame->module))) { - DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ); - return frame->pc; - } + 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; } } return 0; } + +uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) { + CHECK(g_ctx); + if (!g_ctx->SuppressionCount() || loc == 0 || + loc->type != ReportLocationGlobal) + return 0; + SuppressionType stype = conv(typ); + if (stype == SuppressionNone) + 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++; + *sp = s; + return loc->addr; + } + return 0; +} + +void PrintMatchedSuppressions() { + CHECK(g_ctx); + InternalMmapVector matched(1); + g_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; + 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); + } +} } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_suppressions.h b/libsanitizer/tsan/tsan_suppressions.h index d44d8dd3529..e38d81ece85 100644 --- a/libsanitizer/tsan/tsan_suppressions.h +++ b/libsanitizer/tsan/tsan_suppressions.h @@ -11,30 +11,15 @@ #ifndef TSAN_SUPPRESSIONS_H #define TSAN_SUPPRESSIONS_H +#include "sanitizer_common/sanitizer_suppressions.h" #include "tsan_report.h" namespace __tsan { void InitializeSuppressions(); -void FinalizeSuppressions(); -uptr IsSuppressed(ReportType typ, const ReportStack *stack); - -// Exposed for testing. -enum SuppressionType { - SuppressionRace, - SuppressionMutex, - SuppressionThread, - SuppressionSignal -}; - -struct Suppression { - Suppression *next; - SuppressionType type; - char *templ; -}; - -Suppression *SuppressionParse(Suppression *head, const char* supp); -bool SuppressionMatch(char *templ, const char *str); +void PrintMatchedSuppressions(); +uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp); +uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp); } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_symbolize.cc b/libsanitizer/tsan/tsan_symbolize.cc index 65a994670b5..0c7efac7a26 100644 --- a/libsanitizer/tsan/tsan_symbolize.cc +++ b/libsanitizer/tsan/tsan_symbolize.cc @@ -67,16 +67,65 @@ static ReportStack *NewReportStackEntry(const AddressInfo &info) { 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) { + return false; +} + ReportStack *SymbolizeCode(uptr addr) { - if (!IsSymbolizerAvailable()) + // Check if PC comes from non-native land. + if (addr & kExternalPCBit) { + // Declare static to not consume too much stack space. + // We symbolize reports in a single thread, so this is fine. + 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 (!getSymbolizer()->IsAvailable()) return SymbolizeCodeAddr2Line(addr); ScopedInSymbolizer in_symbolizer; static const uptr kMaxAddrFrames = 16; InternalScopedBuffer addr_frames(kMaxAddrFrames); for (uptr i = 0; i < kMaxAddrFrames; i++) new(&addr_frames[i]) AddressInfo(); - uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames.data(), - kMaxAddrFrames); + uptr addr_frames_num = + getSymbolizer()->SymbolizeCode(addr, addr_frames.data(), kMaxAddrFrames); if (addr_frames_num == 0) return NewReportStackEntry(addr); ReportStack *top = 0; @@ -95,11 +144,11 @@ ReportStack *SymbolizeCode(uptr addr) { } ReportLocation *SymbolizeData(uptr addr) { - if (!IsSymbolizerAvailable()) + if (!getSymbolizer()->IsAvailable()) return 0; ScopedInSymbolizer in_symbolizer; DataInfo info; - if (!__sanitizer::SymbolizeData(addr, &info)) + if (!getSymbolizer()->SymbolizeData(addr, &info)) return 0; ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack, sizeof(ReportLocation)); @@ -114,4 +163,11 @@ ReportLocation *SymbolizeData(uptr addr) { return ent; } +void SymbolizeFlush() { + if (!getSymbolizer()->IsAvailable()) + return; + ScopedInSymbolizer in_symbolizer; + getSymbolizer()->Flush(); +} + } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_symbolize.h b/libsanitizer/tsan/tsan_symbolize.h index 5275936a293..c610d1f5492 100644 --- a/libsanitizer/tsan/tsan_symbolize.h +++ b/libsanitizer/tsan/tsan_symbolize.h @@ -18,6 +18,7 @@ namespace __tsan { ReportStack *SymbolizeCode(uptr addr); ReportLocation *SymbolizeData(uptr addr); +void SymbolizeFlush(); ReportStack *SymbolizeCodeAddr2Line(uptr addr); diff --git a/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc b/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc index 9bdd1ffdc5e..da0c87d297e 100644 --- a/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc +++ b/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc @@ -85,7 +85,8 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { DlIteratePhdrCtx *ctx = (DlIteratePhdrCtx*)arg; InternalScopedBuffer tmp(128); if (ctx->is_first) { - internal_snprintf(tmp.data(), tmp.size(), "/proc/%d/exe", GetPid()); + internal_snprintf(tmp.data(), tmp.size(), "/proc/%d/exe", + (int)internal_getpid()); info->dlpi_name = tmp.data(); } ctx->is_first = false; diff --git a/libsanitizer/tsan/tsan_sync.cc b/libsanitizer/tsan/tsan_sync.cc index d392408fd88..04fef615531 100644 --- a/libsanitizer/tsan/tsan_sync.cc +++ b/libsanitizer/tsan/tsan_sync.cc @@ -61,7 +61,7 @@ SyncVar* SyncTab::Create(ThreadState *thr, uptr pc, uptr addr) { 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.ObtainCurrent(thr, pc); + res->creation_stack_id = CurrentStackId(thr, pc); #endif return res; } @@ -80,9 +80,10 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc, // the hashmap anyway. if (PrimaryAllocator::PointerIsMine((void*)addr)) { MBlock *b = user_mblock(thr, (void*)addr); - Lock l(&b->mtx); + CHECK_NE(b, 0); + MBlock::ScopedLock l(b); SyncVar *res = 0; - for (res = b->head; res; res = res->next) { + for (res = b->ListHead(); res; res = res->next) { if (res->addr == addr) break; } @@ -90,8 +91,7 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc, if (!create) return 0; res = Create(thr, pc, addr); - res->next = b->head; - b->head = res; + b->ListPush(res); } if (write_lock) res->mtx.Lock(); @@ -145,27 +145,37 @@ SyncVar* SyncTab::GetAndRemove(ThreadState *thr, uptr pc, uptr addr) { } if (PrimaryAllocator::PointerIsMine((void*)addr)) { MBlock *b = user_mblock(thr, (void*)addr); + CHECK_NE(b, 0); SyncVar *res = 0; { - Lock l(&b->mtx); - SyncVar **prev = &b->head; - res = *prev; - while (res) { + MBlock::ScopedLock l(b); + res = b->ListHead(); + if (res) { if (res->addr == addr) { if (res->is_linker_init) return 0; - *prev = res->next; - break; + 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(); } - prev = &res->next; - res = *prev; } } - if (res) { - StatInc(thr, StatSyncDestroyed); - res->mtx.Lock(); - res->mtx.Unlock(); - } return res; } #endif @@ -195,26 +205,6 @@ SyncVar* SyncTab::GetAndRemove(ThreadState *thr, uptr pc, uptr addr) { return res; } -uptr SyncVar::GetMemoryConsumption() { - return sizeof(*this) - + clock.size() * sizeof(u64) - + read_clock.size() * sizeof(u64) - + creation_stack.Size() * sizeof(uptr); -} - -uptr SyncTab::GetMemoryConsumption(uptr *nsync) { - uptr mem = 0; - for (int i = 0; i < kPartCount; i++) { - Part *p = &tab_[i]; - Lock l(&p->mtx); - for (SyncVar *s = p->val; s; s = s->next) { - *nsync += 1; - mem += s->GetMemoryConsumption(); - } - } - return mem; -} - int SyncTab::PartIdx(uptr addr) { return (addr >> 3) % kPartCount; } diff --git a/libsanitizer/tsan/tsan_sync.h b/libsanitizer/tsan/tsan_sync.h index 4dbb055a17e..2867a8ac79e 100644 --- a/libsanitizer/tsan/tsan_sync.h +++ b/libsanitizer/tsan/tsan_sync.h @@ -57,7 +57,7 @@ struct SyncVar { const u64 uid; // Globally unique id. SyncClock clock; SyncClock read_clock; // Used for rw mutexes only. - StackTrace creation_stack; + u32 creation_stack_id; int owner_tid; // Set only by exclusive owners. u64 last_lock; int recursion; diff --git a/libsanitizer/tsan/tsan_update_shadow_word_inl.h b/libsanitizer/tsan/tsan_update_shadow_word_inl.h index b9aa51c7957..42caf80d349 100644 --- a/libsanitizer/tsan/tsan_update_shadow_word_inl.h +++ b/libsanitizer/tsan/tsan_update_shadow_word_inl.h @@ -55,8 +55,7 @@ do { goto RACE; } // Do the memory access intersect? - // In Go all memory accesses are 1 byte, so there can be no intersections. - if (kCppMode && Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { + if (Shadow::TwoRangesIntersect(old, cur, kAccessSize)) { StatInc(thr, StatShadowIntersect); if (Shadow::TidsAreEqual(old, cur)) { StatInc(thr, StatShadowSameThread); diff --git a/libsanitizer/tsan/tsan_vector.h b/libsanitizer/tsan/tsan_vector.h index 99e9f792c20..4da8b83d5c3 100644 --- a/libsanitizer/tsan/tsan_vector.h +++ b/libsanitizer/tsan/tsan_vector.h @@ -62,6 +62,11 @@ class Vector { return &end_[-1]; } + void PopBack() { + DCHECK_GT(end_, begin_); + end_--; + } + void Resize(uptr size) { uptr old_size = Size(); EnsureSize(size); diff --git a/libsanitizer/ubsan/ubsan_diag.cc b/libsanitizer/ubsan/ubsan_diag.cc index d56ef849b6f..c7378fdbda2 100644 --- a/libsanitizer/ubsan/ubsan_diag.cc +++ b/libsanitizer/ubsan/ubsan_diag.cc @@ -26,7 +26,8 @@ Location __ubsan::getCallerLocation(uptr CallerLoc) { uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc); AddressInfo Info; - if (!SymbolizeCode(Loc, &Info, 1) || !Info.module || !*Info.module) + if (!getSymbolizer()->SymbolizeCode(Loc, &Info, 1) || + !Info.module || !*Info.module) return Location(Loc); if (!Info.file) @@ -107,7 +108,7 @@ static void renderText(const char *Message, const Diag::Arg *Args) { Printf("%s", A.String); break; case Diag::AK_Mangled: { - Printf("'%s'", Demangle(A.String)); + Printf("'%s'", getSymbolizer()->Demangle(A.String)); break; } case Diag::AK_SInt: diff --git a/libsanitizer/ubsan/ubsan_value.h b/libsanitizer/ubsan/ubsan_value.h index 6ca0f56c99d..9b31eb7defb 100644 --- a/libsanitizer/ubsan/ubsan_value.h +++ b/libsanitizer/ubsan/ubsan_value.h @@ -23,8 +23,8 @@ // FIXME: Move this out to a config header. #if __SIZEOF_INT128__ -__extension__ typedef __int128 s128; -__extension__ typedef unsigned __int128 u128; +typedef __int128 s128; +typedef unsigned __int128 u128; #define HAVE_INT128_T 1 #else #define HAVE_INT128_T 0