From 37dfff057418e02f8e5322da12684dd927e3d881 Mon Sep 17 00:00:00 2001 From: Andrew Turner Date: Mon, 30 Jul 2012 10:58:13 +0000 Subject: [PATCH] Import compiler-rt r160957. --- BlocksRuntime/CMakeLists.txt | 13 - CMakeLists.txt | 105 +- LICENSE.TXT | 3 +- SDKs/darwin/usr/include/stdlib.h | 1 + SDKs/darwin/usr/include/string.h | 2 + SDKs/linux/usr/include/stdlib.h | 1 + SDKs/linux/usr/include/string.h | 2 + cmake/ConfigureChecks.cmake | 38 - cmake/Modules/DefineCompilerFlags.cmake | 6 - cmake/Modules/MacroAddCheckTest.cmake | 12 - .../Modules/MacroEnsureOutOfSourceBuild.cmake | 18 - cmake/config.h.cmake | 12 - lib/CMakeLists.txt | 264 ++- lib/Makefile.mk | 10 +- lib/absvti2.c | 4 +- lib/adddf3.c | 4 +- lib/addsf3.c | 4 +- lib/addvti3.c | 4 +- lib/arm/CMakeLists.txt | 0 lib/arm/aeabi_idivmod.S | 27 + lib/arm/aeabi_ldivmod.S | 30 + lib/arm/aeabi_memcmp.S | 19 + lib/arm/aeabi_memcpy.S | 19 + lib/arm/aeabi_memmove.S | 19 + lib/arm/aeabi_memset.S | 32 + lib/arm/aeabi_uidivmod.S | 28 + lib/arm/aeabi_uldivmod.S | 30 + lib/asan/CMakeLists.txt | 82 + lib/asan/Makefile.mk | 4 +- lib/asan/Makefile.old | 115 +- lib/asan/README.txt | 1 - lib/asan/asan_allocator.cc | 566 +++--- lib/asan/asan_allocator.h | 74 +- lib/asan/asan_flags.h | 97 + lib/asan/asan_globals.cc | 64 +- lib/asan/asan_interceptors.cc | 694 +++++-- lib/asan/asan_interceptors.h | 122 +- lib/asan/asan_interface.h | 96 +- lib/asan/asan_internal.h | 210 +-- lib/asan/asan_linux.cc | 170 +- lib/asan/asan_lock.h | 78 +- lib/asan/asan_mac.cc | 416 +++-- lib/asan/asan_mac.h | 92 +- lib/asan/asan_malloc_linux.cc | 84 +- lib/asan/asan_malloc_mac.cc | 218 ++- lib/asan/asan_malloc_win.cc | 141 ++ lib/asan/asan_mapping.h | 62 +- lib/asan/asan_new_delete.cc | 56 + lib/asan/asan_poisoning.cc | 78 +- lib/asan/asan_posix.cc | 126 ++ lib/asan/asan_printf.cc | 150 +- lib/asan/asan_rtl.cc | 953 ++++------ lib/asan/asan_stack.cc | 258 ++- lib/asan/asan_stack.h | 90 +- lib/asan/asan_stats.cc | 44 +- lib/asan/asan_stats.h | 40 +- lib/asan/asan_thread.cc | 196 +- lib/asan/asan_thread.h | 52 +- lib/asan/asan_thread_registry.cc | 157 +- lib/asan/asan_thread_registry.h | 35 +- lib/asan/asan_win.cc | 181 ++ lib/asan/output_tests/clone_test.cc | 34 + lib/asan/output_tests/deep_tail_call.cc | 15 + lib/asan/output_tests/default_options.cc | 12 + .../dlclose-test-so.cc | 2 +- .../{tests => output_tests}/dlclose-test.cc | 3 +- .../global-overflow.cc | 4 + lib/asan/output_tests/heap-overflow.cc | 22 + .../interception_failure_test-linux.cc | 17 + .../interception_malloc_test-linux.cc | 19 + .../output_tests/interception_test-linux.cc | 18 + lib/asan/output_tests/large_func_test.cc | 48 + lib/asan/output_tests/memcmp_test.cc | 10 + lib/asan/output_tests/null_deref.cc | 17 + .../shared-lib-test-so.cc | 2 +- .../shared-lib-test.cc | 9 +- lib/asan/output_tests/stack-overflow.cc | 11 + .../stack-use-after-return.cc.disabled} | 3 + lib/asan/output_tests/strncpy-overflow.cc | 24 + lib/asan/output_tests/test_output.sh | 79 + .../{tests => output_tests}/use-after-free.c | 3 + lib/asan/output_tests/use-after-free.cc | 31 + lib/asan/scripts/asan_symbolize.py | 88 +- lib/asan/sysinfo/LICENSE.TXT | 29 - lib/asan/sysinfo/basictypes.h | 321 ---- lib/asan/sysinfo/sysinfo.cc | 617 ------- lib/asan/sysinfo/sysinfo.h | 234 --- lib/asan/tests/CMakeLists.txt | 118 ++ lib/asan/tests/asan_benchmarks_test.cc | 2 +- lib/asan/tests/asan_break_optimization.cc | 3 +- lib/asan/tests/asan_globals_test.cc | 2 +- lib/asan/tests/asan_interface_test.cc | 334 ---- lib/asan/tests/asan_mac_test.h | 5 +- lib/asan/tests/asan_mac_test.mm | 46 +- lib/asan/tests/asan_noinst_test.cc | 410 ++++- lib/asan/tests/asan_racy_double_free_test.cc | 32 + lib/asan/tests/asan_test.cc | 642 ++++--- lib/asan/tests/asan_test_config.h | 4 + lib/asan/tests/asan_test_utils.h | 28 +- lib/asan/tests/dlclose-test.tmpl | 1 - lib/asan/tests/global-overflow.tmpl | 3 - lib/asan/tests/heap-overflow.cc | 9 - lib/asan/tests/heap-overflow.tmpl | 6 - lib/asan/tests/heap-overflow.tmpl.Darwin | 8 - lib/asan/tests/large_func_test.cc | 33 - lib/asan/tests/large_func_test.tmpl | 8 - lib/asan/tests/match_output.py | 35 - lib/asan/tests/null_deref.cc | 7 - lib/asan/tests/null_deref.tmpl | 4 - lib/asan/tests/shared-lib-test.tmpl | 7 - lib/asan/tests/stack-overflow.cc | 7 - lib/asan/tests/stack-overflow.tmpl | 3 - .../tests/stack-use-after-return.disabled | 3 - lib/asan/tests/strncpy-overflow.cc | 9 - lib/asan/tests/strncpy-overflow.tmpl | 7 - lib/asan/tests/test_output.sh | 47 - lib/asan/tests/use-after-free.cc | 6 - lib/asan/tests/use-after-free.tmpl | 10 - lib/ashldi3.c | 2 +- lib/ashlti3.c | 4 +- lib/ashrdi3.c | 2 +- lib/ashrti3.c | 4 +- lib/assembly.h | 3 + lib/atomic.c | 315 ++++ lib/clzti2.c | 4 +- lib/cmpti2.c | 4 +- lib/ctzti2.c | 4 +- lib/divdf3.c | 2 +- lib/divmoddi4.c | 2 - lib/divsf3.c | 2 +- lib/divsi3.c | 10 +- lib/divti3.c | 4 +- lib/extendsfdf2.c | 2 +- lib/ffsti2.c | 4 +- lib/fixdfdi.c | 2 +- lib/fixdfsi.c | 2 +- lib/fixdfti.c | 4 +- lib/fixsfdi.c | 2 +- lib/fixsfsi.c | 2 +- lib/fixsfti.c | 4 +- lib/fixunsdfdi.c | 2 +- lib/fixunsdfsi.c | 2 +- lib/fixunsdfti.c | 4 +- lib/fixunssfdi.c | 2 +- lib/fixunssfsi.c | 2 +- lib/fixunssfti.c | 4 +- lib/fixunsxfti.c | 4 +- lib/fixxfti.c | 4 +- lib/floatdidf.c | 2 +- lib/floatdisf.c | 2 +- lib/floatsidf.c | 2 +- lib/floatsisf.c | 2 +- lib/floattidf.c | 4 +- lib/floattisf.c | 4 +- lib/floattixf.c | 4 +- lib/floatundidf.c | 2 +- lib/floatundisf.c | 2 +- lib/floatunsidf.c | 2 +- lib/floatunsisf.c | 2 +- lib/floatuntidf.c | 4 +- lib/floatuntisf.c | 4 +- lib/floatuntixf.c | 4 +- lib/fp_lib.h | 2 +- lib/i386/CMakeLists.txt | 3 - lib/int_endianness.h | 9 +- lib/int_util.c | 13 + lib/int_util.h | 7 +- lib/interception/CMakeLists.txt | 37 + lib/interception/Makefile.mk | 23 + lib/interception/interception.h | 168 ++ lib/interception/interception_linux.cc | 29 + lib/interception/interception_linux.h | 35 + lib/interception/interception_mac.cc | 33 + lib/interception/interception_mac.h | 47 + lib/interception/interception_win.cc | 149 ++ lib/interception/interception_win.h | 42 + .../mach_override/LICENSE.TXT | 0 .../mach_override/Makefile.mk | 8 +- .../mach_override/README.txt | 0 .../mach_override/mach_override.c | 118 +- .../mach_override/mach_override.h | 13 + lib/lshrdi3.c | 2 +- lib/lshrti3.c | 4 +- lib/modti3.c | 4 +- lib/muldf3.c | 4 +- lib/muldi3.c | 2 +- lib/muloti4.c | 4 +- lib/mulsf3.c | 4 +- lib/multi3.c | 4 +- lib/mulvti3.c | 4 +- lib/negdf2.c | 2 +- lib/negsf2.c | 2 +- lib/negti2.c | 4 +- lib/negvti2.c | 4 +- lib/parityti2.c | 4 +- lib/popcountti2.c | 4 +- lib/powitf2.c | 4 +- lib/ppc/CMakeLists.txt | 12 - lib/profile/GCDAProfiling.c | 93 +- lib/sanitizer_common/CMakeLists.txt | 35 + .../sysinfo => sanitizer_common}/Makefile.mk | 10 +- lib/sanitizer_common/sanitizer_allocator.cc | 59 + lib/sanitizer_common/sanitizer_allocator64.h | 488 +++++ lib/sanitizer_common/sanitizer_atomic.h | 65 + lib/sanitizer_common/sanitizer_atomic_clang.h | 122 ++ lib/sanitizer_common/sanitizer_atomic_msvc.h | 112 ++ lib/sanitizer_common/sanitizer_common.cc | 100 ++ lib/sanitizer_common/sanitizer_common.h | 123 ++ lib/sanitizer_common/sanitizer_flags.cc | 82 + lib/sanitizer_common/sanitizer_flags.h | 27 + .../sanitizer_interface_defs.h | 56 + .../sanitizer_internal_defs.h | 163 ++ lib/sanitizer_common/sanitizer_libc.cc | 182 ++ lib/sanitizer_common/sanitizer_libc.h | 69 + lib/sanitizer_common/sanitizer_linux.cc | 348 ++++ lib/sanitizer_common/sanitizer_list.h | 120 ++ lib/sanitizer_common/sanitizer_mac.cc | 243 +++ lib/sanitizer_common/sanitizer_mutex.h | 100 ++ .../sanitizer_placement_new.h | 33 + lib/sanitizer_common/sanitizer_posix.cc | 164 ++ lib/sanitizer_common/sanitizer_printf.cc | 185 ++ lib/sanitizer_common/sanitizer_procmaps.h | 82 + lib/sanitizer_common/sanitizer_symbolizer.cc | 144 ++ lib/sanitizer_common/sanitizer_symbolizer.h | 100 ++ lib/sanitizer_common/sanitizer_win.cc | 200 +++ .../tests/sanitizer_allocator64_test.cc | 257 +++ .../tests/sanitizer_allocator64_testlib.cc | 99 + .../tests/sanitizer_allocator_test.cc | 56 + .../tests/sanitizer_common_test.cc | 66 + .../tests/sanitizer_flags_test.cc | 72 + .../tests/sanitizer_list_test.cc | 157 ++ lib/subdf3.c | 2 +- lib/subsf3.c | 2 +- lib/subvti3.c | 4 +- lib/truncdfsf2.c | 2 +- lib/tsan/CMakeLists.txt | 8 + lib/tsan/Makefile.mk | 18 + lib/tsan/Makefile.old | 106 ++ lib/tsan/analyze_libtsan.sh | 43 + lib/tsan/benchmarks/mini_bench_local.cc | 49 + lib/tsan/benchmarks/mini_bench_shared.cc | 51 + lib/tsan/benchmarks/start_many_threads.cc | 52 + lib/tsan/benchmarks/vts_many_threads_bench.cc | 120 ++ lib/tsan/check_analyze.sh | 43 + lib/tsan/go/buildgo.sh | 78 + lib/tsan/go/test.c | 51 + lib/tsan/go/tsan_go.cc | 185 ++ lib/tsan/output_tests/free_race.c | 43 + lib/tsan/output_tests/free_race2.c | 26 + lib/tsan/output_tests/heap_race.cc | 19 + lib/tsan/output_tests/memcpy_race.cc | 40 + lib/tsan/output_tests/mop_with_offset.cc | 36 + lib/tsan/output_tests/mop_with_offset2.cc | 36 + lib/tsan/output_tests/race_on_barrier.c | 31 + lib/tsan/output_tests/race_on_barrier2.c | 30 + lib/tsan/output_tests/race_on_mutex.c | 41 + .../output_tests/race_with_finished_thread.cc | 43 + lib/tsan/output_tests/simple_race.c | 25 + lib/tsan/output_tests/simple_race.cc | 24 + lib/tsan/output_tests/simple_stack.c | 65 + lib/tsan/output_tests/simple_stack2.cc | 46 + lib/tsan/output_tests/static_init1.cc | 25 + lib/tsan/output_tests/static_init2.cc | 31 + lib/tsan/output_tests/static_init3.cc | 46 + lib/tsan/output_tests/static_init4.cc | 35 + lib/tsan/output_tests/static_init5.cc | 40 + .../output_tests/suppress_same_address.cc | 27 + lib/tsan/output_tests/suppress_same_stacks.cc | 27 + lib/tsan/output_tests/test_output.sh | 49 + lib/tsan/output_tests/thread_leak.c | 15 + lib/tsan/output_tests/thread_leak2.c | 15 + lib/tsan/output_tests/thread_leak3.c | 14 + lib/tsan/output_tests/tiny_race.c | 14 + .../virtual_inheritance_compile_bug.cc | 13 + lib/tsan/output_tests/vptr_benign_race.cc | 50 + lib/tsan/output_tests/vptr_harmful_race.cc | 48 + lib/tsan/rtl/Makefile.mk | 23 + lib/tsan/rtl/Makefile.old | 59 + lib/tsan/rtl/tsan_clock.cc | 118 ++ lib/tsan/rtl/tsan_clock.h | 82 + lib/tsan/rtl/tsan_defs.h | 139 ++ lib/tsan/rtl/tsan_flags.cc | 79 + lib/tsan/rtl/tsan_flags.h | 71 + lib/tsan/rtl/tsan_interceptors.cc | 1596 +++++++++++++++++ lib/tsan/rtl/tsan_interface.cc | 42 + lib/tsan/rtl/tsan_interface.h | 51 + lib/tsan/rtl/tsan_interface_ann.cc | 352 ++++ lib/tsan/rtl/tsan_interface_ann.h | 31 + lib/tsan/rtl/tsan_interface_atomic.cc | 321 ++++ lib/tsan/rtl/tsan_interface_atomic.h | 121 ++ lib/tsan/rtl/tsan_interface_inl.h | 65 + lib/tsan/rtl/tsan_md5.cc | 245 +++ lib/tsan/rtl/tsan_mman.cc | 123 ++ lib/tsan/rtl/tsan_mman.h | 114 ++ lib/tsan/rtl/tsan_mutex.cc | 259 +++ lib/tsan/rtl/tsan_mutex.h | 78 + lib/tsan/rtl/tsan_platform.h | 101 ++ lib/tsan/rtl/tsan_platform_linux.cc | 238 +++ lib/tsan/rtl/tsan_platform_mac.cc | 112 ++ lib/tsan/rtl/tsan_printf.cc | 39 + lib/tsan/rtl/tsan_report.cc | 167 ++ lib/tsan/rtl/tsan_report.h | 102 ++ lib/tsan/rtl/tsan_rtl.cc | 534 ++++++ lib/tsan/rtl/tsan_rtl.h | 491 +++++ lib/tsan/rtl/tsan_rtl_amd64.S | 71 + lib/tsan/rtl/tsan_rtl_mutex.cc | 220 +++ lib/tsan/rtl/tsan_rtl_report.cc | 372 ++++ lib/tsan/rtl/tsan_rtl_thread.cc | 394 ++++ lib/tsan/rtl/tsan_stat.cc | 249 +++ lib/tsan/rtl/tsan_stat.h | 254 +++ lib/tsan/rtl/tsan_suppressions.cc | 163 ++ lib/tsan/rtl/tsan_suppressions.h | 43 + lib/tsan/rtl/tsan_symbolize.cc | 78 + lib/tsan/rtl/tsan_symbolize.h | 31 + .../rtl/tsan_symbolize_addr2line_linux.cc | 193 ++ lib/tsan/rtl/tsan_sync.cc | 219 +++ lib/tsan/rtl/tsan_sync.h | 106 ++ lib/tsan/rtl/tsan_trace.h | 71 + lib/tsan/rtl/tsan_update_shadow_word_inl.h | 79 + lib/tsan/rtl/tsan_vector.h | 110 ++ lib/tsan/rtl_tests/tsan_bench.cc | 105 ++ lib/tsan/rtl_tests/tsan_mop.cc | 233 +++ lib/tsan/rtl_tests/tsan_mutex.cc | 221 +++ lib/tsan/rtl_tests/tsan_posix.cc | 146 ++ lib/tsan/rtl_tests/tsan_string.cc | 82 + lib/tsan/rtl_tests/tsan_test.cc | 44 + lib/tsan/rtl_tests/tsan_test_util.h | 122 ++ lib/tsan/rtl_tests/tsan_test_util_linux.cc | 465 +++++ lib/tsan/rtl_tests/tsan_thread.cc | 59 + lib/tsan/unit_tests/tsan_clock_test.cc | 123 ++ lib/tsan/unit_tests/tsan_flags_test.cc | 38 + lib/tsan/unit_tests/tsan_mman_test.cc | 109 ++ lib/tsan/unit_tests/tsan_mutex_test.cc | 126 ++ lib/tsan/unit_tests/tsan_platform_test.cc | 88 + lib/tsan/unit_tests/tsan_printf_test.cc | 106 ++ lib/tsan/unit_tests/tsan_shadow_test.cc | 47 + lib/tsan/unit_tests/tsan_suppressions_test.cc | 128 ++ lib/tsan/unit_tests/tsan_sync_test.cc | 65 + lib/tsan/unit_tests/tsan_vector_test.cc | 45 + lib/ucmpti2.c | 4 +- lib/udivmoddi4.c | 2 - lib/udivmodti4.c | 4 +- lib/udivsi3.c | 3 +- lib/udivti3.c | 4 +- lib/umodti3.c | 4 +- lib/x86_64/CMakeLists.txt | 5 - make/config.mk | 2 +- make/platform/clang_darwin.mk | 99 +- make/platform/clang_linux.mk | 19 +- make/subdir.mk | 4 +- test/CMakeLists.txt | 122 -- test/Unit/clear_cache_test.c | 29 +- test/Unit/enable_execute_stack_test.c | 25 +- test/Unit/endianness.h | 2 +- test/Unit/ppc/CMakeLists.txt | 9 - test/timing/CMakeLists.txt | 17 - 356 files changed, 23042 insertions(+), 5322 deletions(-) delete mode 100644 BlocksRuntime/CMakeLists.txt delete mode 100644 cmake/ConfigureChecks.cmake delete mode 100644 cmake/Modules/DefineCompilerFlags.cmake delete mode 100644 cmake/Modules/MacroAddCheckTest.cmake delete mode 100644 cmake/Modules/MacroEnsureOutOfSourceBuild.cmake delete mode 100644 cmake/config.h.cmake delete mode 100644 lib/arm/CMakeLists.txt create mode 100644 lib/arm/aeabi_idivmod.S create mode 100644 lib/arm/aeabi_ldivmod.S create mode 100644 lib/arm/aeabi_memcmp.S create mode 100644 lib/arm/aeabi_memcpy.S create mode 100644 lib/arm/aeabi_memmove.S create mode 100644 lib/arm/aeabi_memset.S create mode 100644 lib/arm/aeabi_uidivmod.S create mode 100644 lib/arm/aeabi_uldivmod.S create mode 100644 lib/asan/CMakeLists.txt create mode 100644 lib/asan/asan_flags.h create mode 100644 lib/asan/asan_malloc_win.cc create mode 100644 lib/asan/asan_new_delete.cc create mode 100644 lib/asan/asan_posix.cc create mode 100644 lib/asan/asan_win.cc create mode 100644 lib/asan/output_tests/clone_test.cc create mode 100644 lib/asan/output_tests/deep_tail_call.cc create mode 100644 lib/asan/output_tests/default_options.cc rename lib/asan/{tests => output_tests}/dlclose-test-so.cc (91%) rename lib/asan/{tests => output_tests}/dlclose-test.cc (95%) rename lib/asan/{tests => output_tests}/global-overflow.cc (52%) create mode 100644 lib/asan/output_tests/heap-overflow.cc create mode 100644 lib/asan/output_tests/interception_failure_test-linux.cc create mode 100644 lib/asan/output_tests/interception_malloc_test-linux.cc create mode 100644 lib/asan/output_tests/interception_test-linux.cc create mode 100644 lib/asan/output_tests/large_func_test.cc create mode 100644 lib/asan/output_tests/memcmp_test.cc create mode 100644 lib/asan/output_tests/null_deref.cc rename lib/asan/{tests => output_tests}/shared-lib-test-so.cc (87%) rename lib/asan/{tests => output_tests}/shared-lib-test.cc (73%) create mode 100644 lib/asan/output_tests/stack-overflow.cc rename lib/asan/{tests/stack-use-after-return.cc => output_tests/stack-use-after-return.cc.disabled} (65%) create mode 100644 lib/asan/output_tests/strncpy-overflow.cc create mode 100755 lib/asan/output_tests/test_output.sh rename lib/asan/{tests => output_tests}/use-after-free.c (61%) create mode 100644 lib/asan/output_tests/use-after-free.cc delete mode 100644 lib/asan/sysinfo/LICENSE.TXT delete mode 100644 lib/asan/sysinfo/basictypes.h delete mode 100644 lib/asan/sysinfo/sysinfo.cc delete mode 100644 lib/asan/sysinfo/sysinfo.h create mode 100644 lib/asan/tests/CMakeLists.txt delete mode 100644 lib/asan/tests/asan_interface_test.cc create mode 100644 lib/asan/tests/asan_racy_double_free_test.cc delete mode 100644 lib/asan/tests/dlclose-test.tmpl delete mode 100644 lib/asan/tests/global-overflow.tmpl delete mode 100644 lib/asan/tests/heap-overflow.cc delete mode 100644 lib/asan/tests/heap-overflow.tmpl delete mode 100644 lib/asan/tests/heap-overflow.tmpl.Darwin delete mode 100644 lib/asan/tests/large_func_test.cc delete mode 100644 lib/asan/tests/large_func_test.tmpl delete mode 100755 lib/asan/tests/match_output.py delete mode 100644 lib/asan/tests/null_deref.cc delete mode 100644 lib/asan/tests/null_deref.tmpl delete mode 100644 lib/asan/tests/shared-lib-test.tmpl delete mode 100644 lib/asan/tests/stack-overflow.cc delete mode 100644 lib/asan/tests/stack-overflow.tmpl delete mode 100644 lib/asan/tests/stack-use-after-return.disabled delete mode 100644 lib/asan/tests/strncpy-overflow.cc delete mode 100644 lib/asan/tests/strncpy-overflow.tmpl delete mode 100755 lib/asan/tests/test_output.sh delete mode 100644 lib/asan/tests/use-after-free.cc delete mode 100644 lib/asan/tests/use-after-free.tmpl create mode 100644 lib/atomic.c delete mode 100644 lib/i386/CMakeLists.txt create mode 100644 lib/interception/CMakeLists.txt create mode 100644 lib/interception/Makefile.mk create mode 100644 lib/interception/interception.h create mode 100644 lib/interception/interception_linux.cc create mode 100644 lib/interception/interception_linux.h create mode 100644 lib/interception/interception_mac.cc create mode 100644 lib/interception/interception_mac.h create mode 100644 lib/interception/interception_win.cc create mode 100644 lib/interception/interception_win.h rename lib/{asan => interception}/mach_override/LICENSE.TXT (100%) rename lib/{asan => interception}/mach_override/Makefile.mk (70%) rename lib/{asan => interception}/mach_override/README.txt (100%) rename lib/{asan => interception}/mach_override/mach_override.c (89%) rename lib/{asan => interception}/mach_override/mach_override.h (91%) delete mode 100644 lib/ppc/CMakeLists.txt create mode 100644 lib/sanitizer_common/CMakeLists.txt rename lib/{asan/sysinfo => sanitizer_common}/Makefile.mk (65%) create mode 100644 lib/sanitizer_common/sanitizer_allocator.cc create mode 100644 lib/sanitizer_common/sanitizer_allocator64.h create mode 100644 lib/sanitizer_common/sanitizer_atomic.h create mode 100644 lib/sanitizer_common/sanitizer_atomic_clang.h create mode 100644 lib/sanitizer_common/sanitizer_atomic_msvc.h create mode 100644 lib/sanitizer_common/sanitizer_common.cc create mode 100644 lib/sanitizer_common/sanitizer_common.h create mode 100644 lib/sanitizer_common/sanitizer_flags.cc create mode 100644 lib/sanitizer_common/sanitizer_flags.h create mode 100644 lib/sanitizer_common/sanitizer_interface_defs.h create mode 100644 lib/sanitizer_common/sanitizer_internal_defs.h create mode 100644 lib/sanitizer_common/sanitizer_libc.cc create mode 100644 lib/sanitizer_common/sanitizer_libc.h create mode 100644 lib/sanitizer_common/sanitizer_linux.cc create mode 100644 lib/sanitizer_common/sanitizer_list.h create mode 100644 lib/sanitizer_common/sanitizer_mac.cc create mode 100644 lib/sanitizer_common/sanitizer_mutex.h create mode 100644 lib/sanitizer_common/sanitizer_placement_new.h create mode 100644 lib/sanitizer_common/sanitizer_posix.cc create mode 100644 lib/sanitizer_common/sanitizer_printf.cc create mode 100644 lib/sanitizer_common/sanitizer_procmaps.h create mode 100644 lib/sanitizer_common/sanitizer_symbolizer.cc create mode 100644 lib/sanitizer_common/sanitizer_symbolizer.h create mode 100644 lib/sanitizer_common/sanitizer_win.cc create mode 100644 lib/sanitizer_common/tests/sanitizer_allocator64_test.cc create mode 100644 lib/sanitizer_common/tests/sanitizer_allocator64_testlib.cc create mode 100644 lib/sanitizer_common/tests/sanitizer_allocator_test.cc create mode 100644 lib/sanitizer_common/tests/sanitizer_common_test.cc create mode 100644 lib/sanitizer_common/tests/sanitizer_flags_test.cc create mode 100644 lib/sanitizer_common/tests/sanitizer_list_test.cc create mode 100644 lib/tsan/CMakeLists.txt create mode 100644 lib/tsan/Makefile.mk create mode 100644 lib/tsan/Makefile.old create mode 100755 lib/tsan/analyze_libtsan.sh create mode 100644 lib/tsan/benchmarks/mini_bench_local.cc create mode 100644 lib/tsan/benchmarks/mini_bench_shared.cc create mode 100644 lib/tsan/benchmarks/start_many_threads.cc create mode 100644 lib/tsan/benchmarks/vts_many_threads_bench.cc create mode 100755 lib/tsan/check_analyze.sh create mode 100755 lib/tsan/go/buildgo.sh create mode 100644 lib/tsan/go/test.c create mode 100644 lib/tsan/go/tsan_go.cc create mode 100644 lib/tsan/output_tests/free_race.c create mode 100644 lib/tsan/output_tests/free_race2.c create mode 100644 lib/tsan/output_tests/heap_race.cc create mode 100644 lib/tsan/output_tests/memcpy_race.cc create mode 100644 lib/tsan/output_tests/mop_with_offset.cc create mode 100644 lib/tsan/output_tests/mop_with_offset2.cc create mode 100644 lib/tsan/output_tests/race_on_barrier.c create mode 100644 lib/tsan/output_tests/race_on_barrier2.c create mode 100644 lib/tsan/output_tests/race_on_mutex.c create mode 100644 lib/tsan/output_tests/race_with_finished_thread.cc create mode 100644 lib/tsan/output_tests/simple_race.c create mode 100644 lib/tsan/output_tests/simple_race.cc create mode 100644 lib/tsan/output_tests/simple_stack.c create mode 100644 lib/tsan/output_tests/simple_stack2.cc create mode 100644 lib/tsan/output_tests/static_init1.cc create mode 100644 lib/tsan/output_tests/static_init2.cc create mode 100644 lib/tsan/output_tests/static_init3.cc create mode 100644 lib/tsan/output_tests/static_init4.cc create mode 100644 lib/tsan/output_tests/static_init5.cc create mode 100644 lib/tsan/output_tests/suppress_same_address.cc create mode 100644 lib/tsan/output_tests/suppress_same_stacks.cc create mode 100755 lib/tsan/output_tests/test_output.sh create mode 100644 lib/tsan/output_tests/thread_leak.c create mode 100644 lib/tsan/output_tests/thread_leak2.c create mode 100644 lib/tsan/output_tests/thread_leak3.c create mode 100644 lib/tsan/output_tests/tiny_race.c create mode 100644 lib/tsan/output_tests/virtual_inheritance_compile_bug.cc create mode 100644 lib/tsan/output_tests/vptr_benign_race.cc create mode 100644 lib/tsan/output_tests/vptr_harmful_race.cc create mode 100644 lib/tsan/rtl/Makefile.mk create mode 100644 lib/tsan/rtl/Makefile.old create mode 100644 lib/tsan/rtl/tsan_clock.cc create mode 100644 lib/tsan/rtl/tsan_clock.h create mode 100644 lib/tsan/rtl/tsan_defs.h create mode 100644 lib/tsan/rtl/tsan_flags.cc create mode 100644 lib/tsan/rtl/tsan_flags.h create mode 100644 lib/tsan/rtl/tsan_interceptors.cc create mode 100644 lib/tsan/rtl/tsan_interface.cc create mode 100644 lib/tsan/rtl/tsan_interface.h create mode 100644 lib/tsan/rtl/tsan_interface_ann.cc create mode 100644 lib/tsan/rtl/tsan_interface_ann.h create mode 100644 lib/tsan/rtl/tsan_interface_atomic.cc create mode 100644 lib/tsan/rtl/tsan_interface_atomic.h create mode 100644 lib/tsan/rtl/tsan_interface_inl.h create mode 100644 lib/tsan/rtl/tsan_md5.cc create mode 100644 lib/tsan/rtl/tsan_mman.cc create mode 100644 lib/tsan/rtl/tsan_mman.h create mode 100644 lib/tsan/rtl/tsan_mutex.cc create mode 100644 lib/tsan/rtl/tsan_mutex.h create mode 100644 lib/tsan/rtl/tsan_platform.h create mode 100644 lib/tsan/rtl/tsan_platform_linux.cc create mode 100644 lib/tsan/rtl/tsan_platform_mac.cc create mode 100644 lib/tsan/rtl/tsan_printf.cc create mode 100644 lib/tsan/rtl/tsan_report.cc create mode 100644 lib/tsan/rtl/tsan_report.h create mode 100644 lib/tsan/rtl/tsan_rtl.cc create mode 100644 lib/tsan/rtl/tsan_rtl.h create mode 100644 lib/tsan/rtl/tsan_rtl_amd64.S create mode 100644 lib/tsan/rtl/tsan_rtl_mutex.cc create mode 100644 lib/tsan/rtl/tsan_rtl_report.cc create mode 100644 lib/tsan/rtl/tsan_rtl_thread.cc create mode 100644 lib/tsan/rtl/tsan_stat.cc create mode 100644 lib/tsan/rtl/tsan_stat.h create mode 100644 lib/tsan/rtl/tsan_suppressions.cc create mode 100644 lib/tsan/rtl/tsan_suppressions.h create mode 100644 lib/tsan/rtl/tsan_symbolize.cc create mode 100644 lib/tsan/rtl/tsan_symbolize.h create mode 100644 lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc create mode 100644 lib/tsan/rtl/tsan_sync.cc create mode 100644 lib/tsan/rtl/tsan_sync.h create mode 100644 lib/tsan/rtl/tsan_trace.h create mode 100644 lib/tsan/rtl/tsan_update_shadow_word_inl.h create mode 100644 lib/tsan/rtl/tsan_vector.h create mode 100644 lib/tsan/rtl_tests/tsan_bench.cc create mode 100644 lib/tsan/rtl_tests/tsan_mop.cc create mode 100644 lib/tsan/rtl_tests/tsan_mutex.cc create mode 100644 lib/tsan/rtl_tests/tsan_posix.cc create mode 100644 lib/tsan/rtl_tests/tsan_string.cc create mode 100644 lib/tsan/rtl_tests/tsan_test.cc create mode 100644 lib/tsan/rtl_tests/tsan_test_util.h create mode 100644 lib/tsan/rtl_tests/tsan_test_util_linux.cc create mode 100644 lib/tsan/rtl_tests/tsan_thread.cc create mode 100644 lib/tsan/unit_tests/tsan_clock_test.cc create mode 100644 lib/tsan/unit_tests/tsan_flags_test.cc create mode 100644 lib/tsan/unit_tests/tsan_mman_test.cc create mode 100644 lib/tsan/unit_tests/tsan_mutex_test.cc create mode 100644 lib/tsan/unit_tests/tsan_platform_test.cc create mode 100644 lib/tsan/unit_tests/tsan_printf_test.cc create mode 100644 lib/tsan/unit_tests/tsan_shadow_test.cc create mode 100644 lib/tsan/unit_tests/tsan_suppressions_test.cc create mode 100644 lib/tsan/unit_tests/tsan_sync_test.cc create mode 100644 lib/tsan/unit_tests/tsan_vector_test.cc delete mode 100644 lib/x86_64/CMakeLists.txt delete mode 100644 test/CMakeLists.txt delete mode 100644 test/Unit/ppc/CMakeLists.txt delete mode 100644 test/timing/CMakeLists.txt diff --git a/BlocksRuntime/CMakeLists.txt b/BlocksRuntime/CMakeLists.txt deleted file mode 100644 index 5b4686b83a69..000000000000 --- a/BlocksRuntime/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -PROJECT( BlocksRuntime C ) - -SET( SRCS - runtime.c - data.c - ) - -ADD_LIBRARY( ${PROJECT_NAME} SHARED ${SRCS}) -SET_TARGET_PROPERTIES( ${PROJECT_NAME} PROPERTIES - INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/lib ) - -INSTALL( TARGETS ${PROJECT_NAME} DESTINATION lib ) -INSTALL( FILES Block.h Block_private.h DESTINATION include ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 483b673773a7..97835a1e945e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,55 +1,68 @@ -# See docs/CMake.html for instructions about how to build Compiler-RT with CMake. +# CMake build for CompilerRT. +# +# This build assumes that CompilerRT is checked out into the +# 'projects/compiler-rt' inside of an LLVM tree, it is not a stand-alone build +# system. +# +# An important constraint of the build is that it only produces libraries +# based on the ability of the host toolchain to target various platforms. -PROJECT( CompilerRT C ) -CMAKE_MINIMUM_REQUIRED( VERSION 2.6 ) +include(LLVMParseArguments) -set(PACKAGE_NAME compiler-rt) -set(PACKAGE_VERSION 1.0svn) -set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") -set(PACKAGE_BUGREPORT "llvmbugs@cs.uiuc.edu") +# The CompilerRT build system requires CMake version 2.8.8 or higher in order +# to use its support for building convenience "libraries" as a collection of +# .o files. This is particularly useful in producing larger, more complex +# runtime libraries. +cmake_minimum_required(VERSION 2.8.8) -SET( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules ) +# FIXME: Below we assume that the target build of LLVM/Clang is x86, which is +# not at all valid. Much of this can be fixed just by switching to use +# a just-built-clang binary for the compiles. -# add definitions -include(DefineCompilerFlags) +# Detect whether the current target platform is 32-bit or 64-bit, and setup +# the correct commandline flags needed to attempt to target 32-bit and 64-bit. +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(TARGET_X86_64_CFLAGS "-m64") + set(TARGET_I386_CFLAGS "") +else() + if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8) + message(FATAL_ERROR "Please use a sane architecture with 4 or 8 byte pointers.") + endif() + set(TARGET_X86_64_CFLAGS "") + set(TARGET_I386_CFLAGS "-m32") +endif() -# Disallow in-source build -INCLUDE( MacroEnsureOutOfSourceBuild ) -MACRO_ENSURE_OUT_OF_SOURCE_BUILD( - "${PROJECT_NAME} requires an out of source build. Please create a separate build directory and run 'cmake /path/to/${PROJECT_NAME} [options]' there." - ) +# Try to compile a very simple source file to ensure we can target the given +# platform. We use the results of these tests to build only the various target +# runtime libraries supported by our current compilers cross-compiling +# abilities. +set(SIMPLE_SOURCE64 ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple64.c) +file(WRITE ${SIMPLE_SOURCE64} "#include \nint main() {}") +try_compile(CAN_TARGET_X86_64 ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE64} + COMPILE_DEFINITIONS "${TARGET_X86_64_CFLAGS}" + CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_X86_64_CFLAGS}") -INCLUDE( ${CMAKE_SOURCE_DIR}/cmake/ConfigureChecks.cmake ) -CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/cmake/config.h.cmake - ${CMAKE_CURRENT_BINARY_DIR}/config.h ) +set(SIMPLE_SOURCE32 ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple32.c) +file(WRITE ${SIMPLE_SOURCE32} "#include \nint main() {}") +try_compile(CAN_TARGET_I386 ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE32} + COMPILE_DEFINITIONS "${TARGET_I386_CFLAGS}" + CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_I386_CFLAGS}") -INCLUDE_DIRECTORIES( - ${CMAKE_CURRENT_BINARY_DIR} -) +# Because compiler-rt spends a lot of time setting up custom compile flags, +# define a handy helper function for it. The compile flags setting in CMake +# has serious issues that make its syntax challenging at best. +function(set_target_compile_flags target) + foreach(arg ${ARGN}) + set(argstring "${argstring} ${arg}") + endforeach() + set_property(TARGET ${target} PROPERTY COMPILE_FLAGS "${argstring}") +endfunction() -SET( Achitectures - i386 x86_64 ppc arm - ) +add_subdirectory(lib) -SET( Configurations - Debug Release Profile - ) - -# Only build Blocks Runtime if the compiler has enough support -IF( WIN32 OR MSVC OR HAVE_OSATOMIC_COMPARE_AND_SWAP_INT OR HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT ) - SET(BUILD_BLOCKS_RUNTIME TRUE) -ELSE( WIN32 OR MSVC OR HAVE_OSATOMIC_COMPARE_AND_SWAP_INT OR HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT ) - SET(BUILD_BLOCKS_RUNTIME FALSE) -ENDIF( WIN32 OR MSVC OR HAVE_OSATOMIC_COMPARE_AND_SWAP_INT OR HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT ) - -IF( BUILD_BLOCKS_RUNTIME ) - ADD_SUBDIRECTORY( BlocksRuntime ) -ELSE( BUILD_BLOCKS_RUNTIME ) - MESSAGE(STATUS "No suitable atomic operation routines detected, skipping Blocks Runtime") -ENDIF( BUILD_BLOCKS_RUNTIME ) - -ADD_SUBDIRECTORY( lib ) - -# Enable Test Suit: -INCLUDE( MacroAddCheckTest ) -ADD_SUBDIRECTORY( test ) +if(LLVM_INCLUDE_TESTS) + # Currently the tests have not been ported to CMake, so disable this + # directory. + # + #add_subdirectory(test) +endif() diff --git a/LICENSE.TXT b/LICENSE.TXT index c41925e7340f..f7179425605e 100644 --- a/LICENSE.TXT +++ b/LICENSE.TXT @@ -94,5 +94,4 @@ licenses, and/or restrictions: Program Directory ------- --------- -sysinfo lib/asan/sysinfo -mach_override lib/asan/mach_override +mach_override lib/interception/mach_override diff --git a/SDKs/darwin/usr/include/stdlib.h b/SDKs/darwin/usr/include/stdlib.h index cf65df4cf46b..c18c2e49a329 100644 --- a/SDKs/darwin/usr/include/stdlib.h +++ b/SDKs/darwin/usr/include/stdlib.h @@ -22,6 +22,7 @@ typedef __SIZE_TYPE__ size_t; void abort(void) __attribute__((__noreturn__)); +int atoi(const char *); void free(void *); char *getenv(const char *); void *malloc(size_t); diff --git a/SDKs/darwin/usr/include/string.h b/SDKs/darwin/usr/include/string.h index 5e91109378ed..bee9d46cddc4 100644 --- a/SDKs/darwin/usr/include/string.h +++ b/SDKs/darwin/usr/include/string.h @@ -19,6 +19,8 @@ typedef __SIZE_TYPE__ size_t; +int memcmp(const void *, const void *, size_t); +void *memcpy(void *, const void *, size_t); char *strcat(char *, const char *); char *strcpy(char *, const char *); char *strdup(const char *); diff --git a/SDKs/linux/usr/include/stdlib.h b/SDKs/linux/usr/include/stdlib.h index b3755dff1fbc..2a6617ae3cf1 100644 --- a/SDKs/linux/usr/include/stdlib.h +++ b/SDKs/linux/usr/include/stdlib.h @@ -22,6 +22,7 @@ typedef __SIZE_TYPE__ size_t; void abort(void) __attribute__((__nothrow__)) __attribute__((__noreturn__)); +int atoi(const char *) __attribute__((__nothrow__)); void free(void *) __attribute__((__nothrow__)); char *getenv(const char *) __attribute__((__nothrow__)) __attribute__((__nonnull__(1))); diff --git a/SDKs/linux/usr/include/string.h b/SDKs/linux/usr/include/string.h index 5e91109378ed..bee9d46cddc4 100644 --- a/SDKs/linux/usr/include/string.h +++ b/SDKs/linux/usr/include/string.h @@ -19,6 +19,8 @@ typedef __SIZE_TYPE__ size_t; +int memcmp(const void *, const void *, size_t); +void *memcpy(void *, const void *, size_t); char *strcat(char *, const char *); char *strcpy(char *, const char *); char *strdup(const char *); diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake deleted file mode 100644 index b72a390af8cf..000000000000 --- a/cmake/ConfigureChecks.cmake +++ /dev/null @@ -1,38 +0,0 @@ -INCLUDE( CheckIncludeFile ) -INCLUDE( CheckFunctionExists ) -INCLUDE( CheckSymbolExists ) -INCLUDE( CheckCSourceCompiles ) - -SET( PACKAGE ${PACKAGE_NAME} ) -SET( VERSION ${PACKAGE_VERSION} ) - -SET( BINARYDIR ${CMAKE_BINARY_DIR} ) -SET( SOURCEDIR ${CMAKE_SOURCE_DIR} ) - -# HEADER FILES -CHECK_INCLUDE_FILE( sys/byteorder.h HAVE_SYS_BYTEORDER_H ) -CHECK_INCLUDE_FILE( AvailabilityMacros.h HAVE_AVAILABILITY_MACROS_H ) -CHECK_INCLUDE_FILE( TargetConditionals.h HAVE_TARGET_CONDITIONALS_H ) -CHECK_INCLUDE_FILE( libkern/OSAtomic.h HAVE_LIBKERN_OSATOMIC_H ) - -# FUNCTIONS -CHECK_FUNCTION_EXISTS( sysconf HAVE_SYSCONF ) -CHECK_SYMBOL_EXISTS( OSAtomicCompareAndSwapInt libkern/OSAtomic.h HAVE_OSATOMIC_COMPARE_AND_SWAP_INT ) -CHECK_SYMBOL_EXISTS( OSAtomicCompareAndSwapLong libkern/OSAtomic.h HAVE_OSATOMIC_COMPARE_AND_SWAP_LONG ) - -# BUILTIN -CHECK_C_SOURCE_COMPILES( " -volatile int a; -int main(int argc, char *argv[]) { - (void)__sync_bool_compare_and_swap(&a, 1, 2); - return 0; -} -" HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT ) - -CHECK_C_SOURCE_COMPILES( " -volatile long a; -int main(int argc, char *argv[]) { - (void)__sync_bool_compare_and_swap(&a, 1, 2); - return 0; -} -" HAVE_SYNC_BOOL_COMPARE_AND_SWAP_LONG ) diff --git a/cmake/Modules/DefineCompilerFlags.cmake b/cmake/Modules/DefineCompilerFlags.cmake deleted file mode 100644 index 9e262b94bc4b..000000000000 --- a/cmake/Modules/DefineCompilerFlags.cmake +++ /dev/null @@ -1,6 +0,0 @@ -# Define compiler flags - -if( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX ) - #ADD_DEFINITIONS( -Wall -W -Werror -pedantic ) - ADD_DEFINITIONS( -std=c99 -Wall -Wextra -W -pedantic -Wno-unused-parameter ) -endif( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX ) diff --git a/cmake/Modules/MacroAddCheckTest.cmake b/cmake/Modules/MacroAddCheckTest.cmake deleted file mode 100644 index a13912188716..000000000000 --- a/cmake/Modules/MacroAddCheckTest.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# - macro_add_check_test(test_name test_source linklib1 ... linklibN) - -ENABLE_TESTING() -include(CTest) -set(CMAKE_C_FLAGS_PROFILING "-g -pg") - -macro (MACRO_ADD_CHECK_TEST _testName _testSource) - add_executable(${_testName} ${_testSource}) - target_link_libraries(${_testName} ${ARGN}) - get_target_property(_targetLocation ${_testName} LOCATION) - add_test(${_testName} ${_targetLocation}) -endmacro (MACRO_ADD_CHECK_TEST) diff --git a/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake b/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake deleted file mode 100644 index a0669365bf99..000000000000 --- a/cmake/Modules/MacroEnsureOutOfSourceBuild.cmake +++ /dev/null @@ -1,18 +0,0 @@ -# MACRO_ENSURE_OUT_OF_SOURCE_BUILD() - -macro( MACRO_ENSURE_OUT_OF_SOURCE_BUILD _errorMessage ) - -string( COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" _insource ) -if( _insource ) - message( SEND_ERROR "${_errorMessage}" ) - message( FATAL_ERROR - "In-source builds are not allowed. - CMake would overwrite the makefiles distributed with Compiler-RT. - Please create a directory and run cmake from there, passing the path - to this source directory as the last argument. - This process created the file `CMakeCache.txt' and the directory `CMakeFiles'. - Please delete them." - ) -endif( _insource ) - -endmacro( MACRO_ENSURE_OUT_OF_SOURCE_BUILD ) diff --git a/cmake/config.h.cmake b/cmake/config.h.cmake deleted file mode 100644 index 307e277872ac..000000000000 --- a/cmake/config.h.cmake +++ /dev/null @@ -1,12 +0,0 @@ -#cmakedefine HAVE_SYS_BYTEORDER_H ${HAVE_SYS_BYTEORDER} -#cmakedefine HAVE_AVAILABILITY_MACROS_H ${HAVE_AVAILABILITY_MACROS_H} -#cmakedefine HAVE_TARGET_CONDITIONALS_H ${HAVE_TARGET_CONDITIONALS_H} -#cmakedefine HAVE_LIBKERN_OSATOMIC_H ${HAVE_LIBKERN_OSATOMIC_H} - -#cmakedefine HAVE_SYSCONF ${HAVE_SYSCONF} - -#cmakedefine HAVE_OSATOMIC_COMPARE_AND_SWAP_INT ${HAVE_OSATOMIC_COMPARE_AND_SWAP_INT} -#cmakedefine HAVE_OSATOMIC_COMPARE_AND_SWAP_LONG ${HAVE_OSATOMIC_COMPARE_AND_SWAP_LONG} - -#cmakedefine HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT ${HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT} -#cmakedefine HAVE_SYNC_BOOL_COMPARE_AND_SWAP_LONG ${HAVE_SYNC_BOOL_COMPARE_AND_SWAP_LONG} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e29474a1da6d..67019655332a 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,81 +1,197 @@ -# -# Create a library called "CompilerRT" which includes the source files. +# Compute the Clang version from the LLVM version. +# FIXME: We should be able to reuse CLANG_VERSION variable calculated +# in Clang cmake files, instead of copying the rules here. +string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" CLANG_VERSION + ${PACKAGE_VERSION}) -#INCLUDE_DIRECTORIES( -# ${CMAKE_CURRENT_BINARY_DIR} -#) +# Call add_clang_runtime_static_library() to make +# sure that static is built in the directory +# where Clang driver expects to find it. +if (APPLE) + set(CLANG_RUNTIME_LIB_DIR + ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/lib/darwin) +elseif (UNIX) + # Assume Linux. + set(CLANG_RUNTIME_LIB_DIR + ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/lib/linux) +endif() +function(add_clang_runtime_static_library target_name) + set_target_properties(${target_name} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CLANG_RUNTIME_LIB_DIR}) +endfunction() -# Generic functions needed for each architecture -# libcompiler_rt.Generic.a libcompiler_rt.Optimized.a +# First, add the subdirectories which contain feature-based runtime libraries +# and several convenience helper libraries. +add_subdirectory(asan) +add_subdirectory(interception) +add_subdirectory(sanitizer_common) -# Generic -SET( Generic_SRCS - absvdi2.c absvsi2.c addvdi3.c addvsi3.c ashldi3.c ashrdi3.c - clzdi2.c clzsi2.c cmpdi2.c ctzdi2.c ctzsi2.c - divdc3.c divdi3.c divsc3.c ffsdi2.c - fixdfdi.c fixsfdi.c fixunsdfdi.c fixunsdfsi.c fixunssfdi.c - fixunssfsi.c floatdidf.c floatdisf.c floatundidf.c floatundisf.c - gcc_personality_v0.c lshrdi3.c moddi3.c muldc3.c muldi3.c - mulsc3.c mulvdi3.c mulvsi3.c negdi2.c negvdi2.c negvsi2.c - paritydi2.c paritysi2.c popcountdi2.c popcountsi2.c powidf2.c - powisf2.c subvdi3.c subvsi3.c ucmpdi2.c udivdi3.c - udivmoddi4.c umoddi3.c apple_versioning.c eprintf.c - ) +# FIXME: Add support for the profile library. -# Optimized functions for each architecture -# Commenting out for the min until the basics are working first. -# ADD_SUBDIRECTORY( ppc ) -# ADD_SUBDIRECTORY( x86_64 ) -# ADD_SUBDIRECTORY( i386 ) -# ADD_SUBDIRECTORY( arm ) +# The top-level lib directory contains a large amount of C code which provides +# generic implementations of the core runtime library along with optimized +# architecture-specific code in various subdirectories. -# List of functions needed for each architecture. -SET( i386_Functions - divxc3.c fixunsxfdi.c fixunsxfsi.c fixxfdi.c floatdixf.c - floatundixf.c mulxc3.c powixf2.c clear_cache.c enable_execute_stack.c - ) - -SET( x86_64_Functions - absvti2.c addvti3.c ashlti3.c ashrti3.c clzti2.c cmpti2.c - ctzti2.c divti3.c divxc3.c ffsti2.c fixdfti.c fixsfti.c - fixunsdfti.c fixunssfti.c fixunsxfdi.c fixunsxfsi.c - fixunsxfti.c fixxfdi.c fixxfti.c floatdixf.c floattidf.c - floattisf.c floattixf.c floatundixf.c floatuntidf.c - floatuntisf.c floatuntixf.c lshrti3.c modti3.c multi3.c - mulvti3.c mulxc3.c negti2.c negvti2.c parityti2.c - popcountti2.c powixf2.c subvti3.c ucmpti2.c udivmodti4.c - udivti3.c umodti3.c clear_cache.c enable_execute_stack.c - ) - -SET( PPC_Functions - divtc3.c fixtfdi.c fixunstfdi.c floatditf.c floatunditf.c - gcc_qadd.c gcc_qdiv.c gcc_qmul.c gcc_qsub.c multc3.c - powitf2.c restFP.c saveFP.c trampoline_setup.c - clear_cache.c enable_execute_stack.c - ) - -SET( ARM_Functions - adddf3vfp.c addsf3vfp.c bswapdi2.c bswapsi2.c divdf3vfp.c - divsf3vfp.c eqdf2vfp.c eqsf2vfp.c extendsfdf2vfp.c - fixdfsivfp.c fixsfsivfp.c fixunsdfsivfp.c fixunssfsivfp.c - floatsidfvfp.c floatsisfvfp.c floatunssidfvfp.c floatunssisfvfp.c - gedf2vfp.c gesf2vfp.c gtdf2vfp.c gtsf2vfp.c - ledf2vfp.c lesf2vfp.c ltdf2vfp.c ltsf2vfp.c - muldf3vfp.c mulsf3vfp.c - nedf2vfp.c negdf2vfp.c negsf2vfp.c nesf2vfp.c - subdf3vfp.c subsf3vfp.c truncdfsf2vfp.c unorddf2vfp.c unordsf2vfp.c - modsi3.c umodsi3.c udivsi3.c divsi3.c switch.c - ) - -#FOREACH( LOOP_VAR ${Achitectures} ) -# See ARCHIVE_OUTPUT_DIRECTORY docs. -#${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${LOOP_VAR} -#ENDFOREACH - -ADD_LIBRARY( ${PROJECT_NAME}-Generic STATIC ${Generic_SRCS} ) -#ADD_LIBRARY( ${PROJECT_NAME}-i386 STATIC ${i386_Functions} ) - -# [[debug|optimized|general] -#TARGET_LINK_LIBRARIES( ${PROJECT_NAME} ${PROJECT_NAME}-Common optimized ${PROJECT_NAME}-i386 ) +set(GENERIC_SOURCES + absvdi2.c + absvsi2.c + absvti2.c + adddf3.c + addsf3.c + addvdi3.c + addvsi3.c + addvti3.c + apple_versioning.c + ashldi3.c + ashlti3.c + ashrdi3.c + ashrti3.c + clear_cache.c + clzdi2.c + clzsi2.c + clzti2.c + cmpdi2.c + cmpti2.c + comparedf2.c + comparesf2.c + ctzdi2.c + ctzsi2.c + ctzti2.c + divdc3.c + divdf3.c + divdi3.c + divmoddi4.c + divmodsi4.c + divsc3.c + divsf3.c + divsi3.c + divti3.c + divxc3.c + enable_execute_stack.c + eprintf.c + extendsfdf2.c + ffsdi2.c + ffsti2.c + fixdfdi.c + fixdfsi.c + fixdfti.c + fixsfdi.c + fixsfsi.c + fixsfti.c + fixunsdfdi.c + fixunsdfsi.c + fixunsdfti.c + fixunssfdi.c + fixunssfsi.c + fixunssfti.c + fixunsxfdi.c + fixunsxfsi.c + fixunsxfti.c + fixxfdi.c + fixxfti.c + floatdidf.c + floatdisf.c + floatdixf.c + floatsidf.c + floatsisf.c + floattidf.c + floattisf.c + floattixf.c + floatundidf.c + floatundisf.c + floatundixf.c + floatunsidf.c + floatunsisf.c + floatuntidf.c + floatuntisf.c + floatuntixf.c + gcc_personality_v0.c + int_util.c + lshrdi3.c + lshrti3.c + moddi3.c + modsi3.c + modti3.c + muldc3.c + muldf3.c + muldi3.c + mulodi4.c + mulosi4.c + muloti4.c + mulsc3.c + mulsf3.c + multi3.c + mulvdi3.c + mulvsi3.c + mulvti3.c + mulxc3.c + negdf2.c + negdi2.c + negsf2.c + negti2.c + negvdi2.c + negvsi2.c + negvti2.c + paritydi2.c + paritysi2.c + parityti2.c + popcountdi2.c + popcountsi2.c + popcountti2.c + powidf2.c + powisf2.c + powitf2.c + powixf2.c + subdf3.c + subsf3.c + subvdi3.c + subvsi3.c + subvti3.c + trampoline_setup.c + truncdfsf2.c + ucmpdi2.c + ucmpti2.c + udivdi3.c + udivmoddi4.c + udivmodsi4.c + udivmodti4.c + udivsi3.c + udivti3.c + umoddi3.c + umodsi3.c + umodti3.c + ) +if(CAN_TARGET_X86_64) + add_library(clang_rt.x86_64 STATIC + x86_64/floatdidf.c + x86_64/floatdisf.c + x86_64/floatdixf.c + x86_64/floatundidf.S + x86_64/floatundisf.S + x86_64/floatundixf.S + ${GENERIC_SOURCES} + ) + set_target_properties(clang_rt.x86_64 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_X86_64_CFLAGS}") +endif() +if(CAN_TARGET_I386) + add_library(clang_rt.i386 STATIC + i386/ashldi3.S + i386/ashrdi3.S + i386/divdi3.S + i386/floatdidf.S + i386/floatdisf.S + i386/floatdixf.S + i386/floatundidf.S + i386/floatundisf.S + i386/floatundixf.S + i386/lshrdi3.S + i386/moddi3.S + i386/muldi3.S + i386/udivdi3.S + i386/umoddi3.S + ${GENERIC_SOURCES} + ) + set_target_properties(clang_rt.i386 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_I386_CFLAGS}") +endif() diff --git a/lib/Makefile.mk b/lib/Makefile.mk index 8394af3a8ace..791921a80068 100644 --- a/lib/Makefile.mk +++ b/lib/Makefile.mk @@ -15,10 +15,18 @@ SubDirs += i386 ppc x86_64 arm # Add other submodules. SubDirs += asan +SubDirs += interception SubDirs += profile +SubDirs += sanitizer_common +SubDirs += tsan + +# FIXME: We don't currently support building an atomic library, and as it must +# be a separate library from the runtime library, we need to remove its source +# code from the source files list. +ExcludedSources := atomic.c # Define the variables for this specific directory. -Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file))) +Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(filter-out $(ExcludedSources),$(notdir $(file)))) ObjNames := $(Sources:%.c=%.o) Implementation := Generic diff --git a/lib/absvti2.c b/lib/absvti2.c index 8f2bddcb3b27..c1c7277986ab 100644 --- a/lib/absvti2.c +++ b/lib/absvti2.c @@ -12,10 +12,10 @@ * ===----------------------------------------------------------------------=== */ -#if __x86_64 - #include "int_lib.h" +#if __x86_64 + /* Returns: absolute value */ /* Effects: aborts if abs(x) < 0 */ diff --git a/lib/adddf3.c b/lib/adddf3.c index 7eb40a15d2ae..a55e82d21179 100644 --- a/lib/adddf3.c +++ b/lib/adddf3.c @@ -15,7 +15,7 @@ #define DOUBLE_PRECISION #include "fp_lib.h" -ARM_EABI_FNALIAS(dadd, adddf3); +ARM_EABI_FNALIAS(dadd, adddf3) COMPILER_RT_ABI fp_t __adddf3(fp_t a, fp_t b) { @@ -85,7 +85,7 @@ __adddf3(fp_t a, fp_t b) { // Shift the significand of b by the difference in exponents, with a sticky // bottom bit to get rounding correct. - const int align = aExponent - bExponent; + const unsigned int align = aExponent - bExponent; if (align) { if (align < typeWidth) { const bool sticky = bSignificand << (typeWidth - align); diff --git a/lib/addsf3.c b/lib/addsf3.c index e57270a1e28d..0268324deaab 100644 --- a/lib/addsf3.c +++ b/lib/addsf3.c @@ -15,7 +15,7 @@ #define SINGLE_PRECISION #include "fp_lib.h" -ARM_EABI_FNALIAS(fadd, addsf3); +ARM_EABI_FNALIAS(fadd, addsf3) fp_t __addsf3(fp_t a, fp_t b) { @@ -84,7 +84,7 @@ fp_t __addsf3(fp_t a, fp_t b) { // Shift the significand of b by the difference in exponents, with a sticky // bottom bit to get rounding correct. - const int align = aExponent - bExponent; + const unsigned int align = aExponent - bExponent; if (align) { if (align < typeWidth) { const bool sticky = bSignificand << (typeWidth - align); diff --git a/lib/addvti3.c b/lib/addvti3.c index 9105c178994e..2efcf3b408e4 100644 --- a/lib/addvti3.c +++ b/lib/addvti3.c @@ -12,10 +12,10 @@ * ===----------------------------------------------------------------------=== */ -#if __x86_64 - #include "int_lib.h" +#if __x86_64 + /* Returns: a + b */ /* Effects: aborts if a + b overflows */ diff --git a/lib/arm/CMakeLists.txt b/lib/arm/CMakeLists.txt deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/lib/arm/aeabi_idivmod.S b/lib/arm/aeabi_idivmod.S new file mode 100644 index 000000000000..0237f2221d7b --- /dev/null +++ b/lib/arm/aeabi_idivmod.S @@ -0,0 +1,27 @@ +//===-- aeabi_idivmod.S - EABI idivmod implementation ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// struct { int quot, int rem} __aeabi_idivmod(int numerator, int denominator) { +// int rem, quot; +// quot = __divmodsi4(numerator, denominator, &rem); +// return {quot, rem}; +// } + + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_idivmod) + push { lr } + sub sp, sp, #4 + mov r2, sp + bl SYMBOL_NAME(__divmodsi4) + ldr r1, [sp] + add sp, sp, #4 + pop { pc } diff --git a/lib/arm/aeabi_ldivmod.S b/lib/arm/aeabi_ldivmod.S new file mode 100644 index 000000000000..197c459eb381 --- /dev/null +++ b/lib/arm/aeabi_ldivmod.S @@ -0,0 +1,30 @@ +//===-- aeabi_ldivmod.S - EABI ldivmod implementation ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// struct { int64_t quot, int64_t rem} +// __aeabi_ldivmod(int64_t numerator, int64_t denominator) { +// int64_t rem, quot; +// quot = __divmoddi4(numerator, denominator, &rem); +// return {quot, rem}; +// } + + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod) + push {r11, lr} + sub sp, sp, #16 + add r12, sp, #8 + str r12, [sp] + bl SYMBOL_NAME(__divmoddi4) + ldr r2, [sp, #8] + ldr r3, [sp, #12] + add sp, sp, #16 + pop {r11, pc} diff --git a/lib/arm/aeabi_memcmp.S b/lib/arm/aeabi_memcmp.S new file mode 100644 index 000000000000..ca29c10c65b6 --- /dev/null +++ b/lib/arm/aeabi_memcmp.S @@ -0,0 +1,19 @@ +//===-- aeabi_memcmp.S - EABI memcmp implementation -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// void __aeabi_memcmp(void *dest, void *src, size_t n) { memcmp(dest, src, n); } + + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_memcmp) + b memcmp + +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcmp4, __aeabi_memcmp) +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcmp8, __aeabi_memcmp) diff --git a/lib/arm/aeabi_memcpy.S b/lib/arm/aeabi_memcpy.S new file mode 100644 index 000000000000..8b9c7fdf5f19 --- /dev/null +++ b/lib/arm/aeabi_memcpy.S @@ -0,0 +1,19 @@ +//===-- aeabi_memcpy.S - EABI memcpy implementation -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// void __aeabi_memcpy(void *dest, void *src, size_t n) { memcpy(dest, src, n); } + + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_memcpy) + b memcpy + +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcpy4, __aeabi_memcpy) +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memcpy8, __aeabi_memcpy) diff --git a/lib/arm/aeabi_memmove.S b/lib/arm/aeabi_memmove.S new file mode 100644 index 000000000000..c94ed2b210e8 --- /dev/null +++ b/lib/arm/aeabi_memmove.S @@ -0,0 +1,19 @@ +//===-- aeabi_memmove.S - EABI memmove implementation --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include "../assembly.h" + +// void __aeabi_memmove(void *dest, void *src, size_t n) { memmove(dest, src, n); } + + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_memmove) + b memmove + +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memmove4, __aeabi_memmove) +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memmove8, __aeabi_memmove) diff --git a/lib/arm/aeabi_memset.S b/lib/arm/aeabi_memset.S new file mode 100644 index 000000000000..30ab4bae5a5d --- /dev/null +++ b/lib/arm/aeabi_memset.S @@ -0,0 +1,32 @@ +//===-- aeabi_memset.S - EABI memset implementation -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// void __aeabi_memset(void *dest, size_t n, int c) { memset(dest, c, n); } +// void __aeabi_memclr(void *dest, size_t n) { __aeabi_memset(dest, n, 0); } + + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_memset) + mov r3, r1 + mov r1, r2 + mov r2, r3 + b memset + +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memset4, __aeabi_memset) +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memset8, __aeabi_memset) + +DEFINE_COMPILERRT_FUNCTION(__aeabi_memclr) + mov r2, r1 + mov r1, #0 + b memset + +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memclr4, __aeabi_memclr) +DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_memclr8, __aeabi_memclr) + diff --git a/lib/arm/aeabi_uidivmod.S b/lib/arm/aeabi_uidivmod.S new file mode 100644 index 000000000000..f7e1d2ebed1a --- /dev/null +++ b/lib/arm/aeabi_uidivmod.S @@ -0,0 +1,28 @@ +//===-- aeabi_uidivmod.S - EABI uidivmod implementation -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// struct { unsigned quot, unsigned rem} +// __aeabi_uidivmod(unsigned numerator, unsigned denominator) { +// unsigned rem, quot; +// quot = __udivmodsi4(numerator, denominator, &rem); +// return {quot, rem}; +// } + + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_uidivmod) + push { lr } + sub sp, sp, #4 + mov r2, sp + bl SYMBOL_NAME(__udivmodsi4) + ldr r1, [sp] + add sp, sp, #4 + pop { pc } diff --git a/lib/arm/aeabi_uldivmod.S b/lib/arm/aeabi_uldivmod.S new file mode 100644 index 000000000000..724049dd9ccd --- /dev/null +++ b/lib/arm/aeabi_uldivmod.S @@ -0,0 +1,30 @@ +//===-- aeabi_uldivmod.S - EABI uldivmod implementation -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// struct { uint64_t quot, uint64_t rem} +// __aeabi_uldivmod(uint64_t numerator, uint64_t denominator) { +// uint64_t rem, quot; +// quot = __udivmoddi4(numerator, denominator, &rem); +// return {quot, rem}; +// } + + .syntax unified + .align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_uldivmod) + push {r11, lr} + sub sp, sp, #16 + add r12, sp, #8 + str r12, [sp] + bl SYMBOL_NAME(__udivmoddi4) + ldr r2, [sp, #8] + ldr r3, [sp, #12] + add sp, sp, #16 + pop {r11, pc} \ No newline at end of file diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt new file mode 100644 index 000000000000..ce985f528172 --- /dev/null +++ b/lib/asan/CMakeLists.txt @@ -0,0 +1,82 @@ +# Build for the AddressSanitizer runtime support library. + +set(ASAN_SOURCES + asan_allocator.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_printf.cc + asan_rtl.cc + asan_stack.cc + asan_stats.cc + asan_thread.cc + asan_thread_registry.cc + asan_win.cc + ) + +include_directories(..) + +set(ASAN_CFLAGS + -fPIC + -fno-exceptions + -funwind-tables + -fvisibility=hidden + -fno-builtin + -fomit-frame-pointer + -O3 + ) +if (SUPPORTS_NO_VARIADIC_MACROS_FLAG) + list(APPEND ASAN_CFLAGS -Wno-variadic-macros) +endif () + +if (APPLE) + list(APPEND ASAN_CFLAGS -mmacosx-version-min=10.5) +endif() + +set(ASAN_COMMON_DEFINITIONS + ASAN_HAS_EXCEPTIONS=1 + ASAN_NEEDS_SEGV=1 + ) + +# FIXME: We need to build universal binaries on OS X instead of +# two arch-specific binaries. + +if(CAN_TARGET_X86_64) + add_library(clang_rt.asan-x86_64 STATIC + ${ASAN_SOURCES} + $ + $ + ) + set_target_compile_flags(clang_rt.asan-x86_64 + ${ASAN_CFLAGS} + ${TARGET_X86_64_CFLAGS} + ) + set_property(TARGET clang_rt.asan-x86_64 APPEND PROPERTY COMPILE_DEFINITIONS + ${ASAN_COMMON_DEFINITIONS}) + add_clang_runtime_static_library(clang_rt.asan-x86_64) +endif() +if(CAN_TARGET_I386) + add_library(clang_rt.asan-i386 STATIC + ${ASAN_SOURCES} + $ + $ + ) + set_target_compile_flags(clang_rt.asan-i386 + ${ASAN_CFLAGS} + ${TARGET_I386_CFLAGS} + ) + set_property(TARGET clang_rt.asan-i386 APPEND PROPERTY COMPILE_DEFINITIONS + ${ASAN_COMMON_DEFINITIONS}) + add_clang_runtime_static_library(clang_rt.asan-i386) +endif() + +if(LLVM_INCLUDE_TESTS) + add_subdirectory(tests) +endif() diff --git a/lib/asan/Makefile.mk b/lib/asan/Makefile.mk index 4d9e58d6746f..9d1a2e8a9a28 100644 --- a/lib/asan/Makefile.mk +++ b/lib/asan/Makefile.mk @@ -8,7 +8,7 @@ #===------------------------------------------------------------------------===# ModuleName := asan -SubDirs := mach_override sysinfo +SubDirs := Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) ObjNames := $(Sources:%.cc=%.o) @@ -17,6 +17,8 @@ Implementation := Generic # FIXME: use automatic dependencies? Dependencies := $(wildcard $(Dir)/*.h) +Dependencies += $(wildcard $(Dir)/interception/*.h) +Dependencies += $(wildcard $(Dir)/interception/mach_override/*.h) # Define a convenience variable for all the asan functions. AsanFunctions := $(Sources:%.cc=%) diff --git a/lib/asan/Makefile.old b/lib/asan/Makefile.old index a96ff424fa0d..4ab80e20bcb1 100644 --- a/lib/asan/Makefile.old +++ b/lib/asan/Makefile.old @@ -58,9 +58,11 @@ ifeq ($(ARCH), arm) endif CLANG_FLAGS= +CLANG_VERSION=3.2 CLANG_BUILD=$(ROOT)/../../../../build/Release+Asserts CLANG_CC=$(CLANG_BUILD)/bin/clang $(CLANG_FLAGS) CLANG_CXX=$(CLANG_BUILD)/bin/clang++ $(CLANG_FLAGS) +FILE_CHECK=$(CLANG_BUILD)/bin/FileCheck CC=$(CLANG_CC) CXX=$(CLANG_CXX) @@ -77,7 +79,6 @@ ARCH=x86_64 ASAN_STACK=1 ASAN_GLOBALS=1 -ASAN_USE_CALL=1 ASAN_SCALE=0 # default will be used ASAN_OFFSET=-1 #default will be used ASAN_UAR=0 @@ -120,8 +121,8 @@ endif # This will build libasan on linux for both x86_64 and i386 in the # desired location. The Mac library is already build by the clang's make. -# $(CLANG_BUILD)/lib/clang/3.1/lib/$(OS)/libclang_rt.asan-$(ARCH).a -LIBASAN_INST_DIR=$(CLANG_BUILD)/lib/clang/3.1/lib/$(OS) +# $(CLANG_BUILD)/lib/clang/$(CLANG_VERSION)/lib/$(OS)/libclang_rt.asan-$(ARCH).a +LIBASAN_INST_DIR=$(CLANG_BUILD)/lib/clang/$(CLANG_VERSION)/lib/$(OS) LIBASAN_A=$(LIBASAN_INST_DIR)/libclang_rt.asan-$(ARCH).a BLACKLIST= @@ -140,7 +141,6 @@ CLANG_ASAN_CXX=$(CLANG_CXX) \ $(BLACKLIST) \ -mllvm -asan-stack=$(ASAN_STACK) \ -mllvm -asan-globals=$(ASAN_GLOBALS) \ - -mllvm -asan-use-call=$(ASAN_USE_CALL) \ -mllvm -asan-mapping-scale=$(ASAN_SCALE) \ -mllvm -asan-mapping-offset-log=$(ASAN_OFFSET) \ -mllvm -asan-use-after-return=$(ASAN_UAR) \ @@ -169,37 +169,25 @@ ifeq ($(ASAN_COMPILER), gcc) ASAN_LD_TAIL=$(LIBASAN_A) endif -RTL_HDR=asan_allocator.h \ - asan_internal.h \ - asan_interceptors.h \ - asan_interface.h \ - asan_lock.h \ - asan_mac.h \ - asan_mapping.h \ - asan_stack.h \ - asan_stats.h \ - asan_thread.h \ - asan_thread_registry.h \ - mach_override/mach_override.h \ - sysinfo/basictypes.h \ - sysinfo/sysinfo.h +INTERCEPTION=../interception +MACH_OVERRIDE=$(INTERCEPTION)/mach_override +COMMON=../sanitizer_common -LIBASAN_OBJ=$(BIN)/asan_rtl$(SUFF).o \ - $(BIN)/asan_allocator$(SUFF).o \ - $(BIN)/asan_globals$(SUFF).o \ - $(BIN)/asan_interceptors$(SUFF).o \ - $(BIN)/asan_linux$(SUFF).o \ - $(BIN)/asan_mac$(SUFF).o \ - $(BIN)/asan_malloc_linux$(SUFF).o \ - $(BIN)/asan_malloc_mac$(SUFF).o \ - $(BIN)/asan_poisoning$(SUFF).o \ - $(BIN)/asan_printf$(SUFF).o \ - $(BIN)/asan_stack$(SUFF).o \ - $(BIN)/asan_stats$(SUFF).o \ - $(BIN)/asan_thread$(SUFF).o \ - $(BIN)/asan_thread_registry$(SUFF).o \ - $(BIN)/mach_override/mach_override$(SUFF).o \ - $(BIN)/sysinfo/sysinfo$(SUFF).o +RTL_HDR=$(wildcard *.h) \ + $(wildcard $(INTERCEPTION)/*.h) \ + $(wildcard $(MACH_OVERRIDE)/*.h) \ + $(wildcard $(COMMON)/*.h) + +LIBTSAN_SRC=$(wildcard *.cc) +INTERCEPTION_SRC=$(wildcard $(INTERCEPTION)/*.cc) +MACH_OVERRIDE_SRC=$(wildcard $(MACH_OVERRIDE)/*.c) +COMMON_SRC=$(wildcard $(COMMON)/*.cc) + + +LIBASAN_OBJ=$(patsubst %.cc,$(BIN)/%$(SUFF).o,$(LIBTSAN_SRC)) \ + $(patsubst $(INTERCEPTION)/%.cc,$(BIN)/%$(SUFF).o,$(INTERCEPTION_SRC)) \ + $(patsubst $(COMMON)/%.cc,$(BIN)/%$(SUFF).o,$(COMMON_SRC)) \ + $(patsubst $(MACH_OVERRIDE)/%.c,$(BIN)/%$(SUFF).o,$(MACH_OVERRIDE_SRC)) GTEST_ROOT=third_party/googletest GTEST_INCLUDE=-I$(GTEST_ROOT)/include @@ -209,29 +197,28 @@ GTEST_LIB=$(GTEST_MAKE_DIR)/gtest-all.o all: b64 b32 test: t64 t32 output_tests lint + @echo "ALL TESTS PASSED" output_tests: b32 b64 - cd tests && ./test_output.sh $(CLANG_CXX) $(CLANG_CC) + cd output_tests && ./test_output.sh $(CLANG_CXX) $(CLANG_CC) $(FILE_CHECK) t64: b64 $(BIN)/asan_test64 t32: b32 $(BIN)/asan_test32 -b64: | $(BIN) +b64: | mk_bin_dir $(MAKE) -f $(MAKEFILE) ARCH=x86_64 asan_test asan_benchmarks -b32: | $(BIN) +b32: | mk_bin_dir $(MAKE) -f $(MAKEFILE) ARCH=i386 asan_test asan_benchmarks lib64: - $(MAKE) $(MAKEFILE) ARCH=x86_64 lib + $(MAKE) -f $(MAKEFILE) ARCH=x86_64 lib lib32: - $(MAKE) $(MAKEFILE) ARCH=i386 lib + $(MAKE) -f $(MAKEFILE) ARCH=i386 lib -$(BIN): +mk_bin_dir: mkdir -p $(BIN) - mkdir -p $(BIN)/sysinfo - mkdir -p $(BIN)/mach_override clang: cd ../ && llvm/rebuild_clang_and_asan.sh > /dev/null @@ -250,29 +237,37 @@ install_clang: | $(INSTALL_DIR) # cp -v $(CLANG_BUILD)/lib/libasan*.a $(INSTALL_DIR)/lib $(BIN)/asan_noinst_test$(SUFF).o: tests/asan_noinst_test.cc $(RTL_HDR) $(MAKEFILE) - $(CLEANROOM_CXX) $(PIE) $(CFLAGS) $(GTEST_INCLUDE) -I. -g -c $< -O2 -o $@ + $(CLEANROOM_CXX) $(PIE) $(CFLAGS) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@ $(BIN)/asan_break_optimization$(SUFF).o: tests/asan_break_optimization.cc $(MAKEFILE) $(CLEANROOM_CXX) $(PIE) $(CFLAGS) -c $< -O0 -o $@ $(BIN)/%_test$(SUFF).o: tests/%_test.cc $(RTL_HDR) $(MAKEFILE) - $(ASAN_CXX) $(GTEST_INCLUDE) -I. -g -c $< -O2 -o $@ $(PIE) $(CFLAGS) + $(ASAN_CXX) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@ $(PIE) $(CFLAGS) $(BIN)/%_test$(SUFF).o: tests/%_test.mm $(RTL_HDR) $(MAKEFILE) - $(ASAN_CXX) $(GTEST_INCLUDE) -I. -g -c $< -O2 -o $@ -ObjC $(PIE) $(CFLAGS) + $(ASAN_CXX) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@ -ObjC $(PIE) $(CFLAGS) + +RTL_COMMON_FLAGS=$(PIE) $(CFLAGS) -fPIC -c -O2 -fno-exceptions -funwind-tables \ + -Ithird_party -I.. $(ASAN_FLAGS) + +$(BIN)/%$(SUFF).o: $(INTERCEPTION)/%.cc $(RTL_HDR) $(MAKEFILE) + $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $< + +$(BIN)/%$(SUFF).o: $(COMMON)/%.cc $(RTL_HDR) $(MAKEFILE) + $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $< + +$(BIN)/%$(SUFF).o: $(MACH_OVERRIDE)/%.c $(RTL_HDR) $(MAKEFILE) + $(CC) $(RTL_COMMON_FLAGS) -o $@ -g $< $(BIN)/%$(SUFF).o: %.cc $(RTL_HDR) $(MAKEFILE) - $(CXX) $(PIE) $(CFLAGS) -fPIC -c -O2 -fno-exceptions -funwind-tables \ - -o $@ -g $< -Ithird_party \ - -DASAN_USE_SYSINFO=1 \ + $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $< \ -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \ -DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \ - -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=$(ASAN_FLEXIBLE_MAPPING_AND_OFFSET) \ - $(ASAN_FLAGS) + -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=$(ASAN_FLEXIBLE_MAPPING_AND_OFFSET) $(BIN)/%$(SUFF).o: %.c $(RTL_HDR) $(MAKEFILE) $(CC) $(PIE) $(CFLAGS) -fPIC -c -O2 -o $@ -g $< -Ithird_party \ - -DASAN_USE_SYSINFO=1 \ $(ASAN_FLAGS) ifeq ($(OS),darwin) @@ -283,17 +278,16 @@ endif lib: $(LIBASAN_A) -$(LIBASAN_A): $(BIN) $(LIBASAN_OBJ) $(MAKEFILE) +$(LIBASAN_A): mk_bin_dir $(LIBASAN_OBJ) $(MAKEFILE) mkdir -p $(LIBASAN_INST_DIR) ar ru $@ $(LIBASAN_OBJ) $(CXX) -shared $(CFLAGS) $(LIBASAN_OBJ) $(LD_FLAGS) -o $(BIN)/libasan$(SUFF).so TEST_OBJECTS_COMMON=\ - $(BIN)/asan_test$(SUFF).o \ $(BIN)/asan_globals_test$(SUFF).o \ $(BIN)/asan_break_optimization$(SUFF).o \ $(BIN)/asan_noinst_test$(SUFF).o \ - $(BIN)/asan_interface_test$(SUFF).o + $(BIN)/asan_test$(SUFF).o BENCHMARK_OBJECTS=\ $(BIN)/asan_benchmarks_test$(SUFF).o \ @@ -323,11 +317,11 @@ $(GTEST_LIB): mkdir -p $(GTEST_MAKE_DIR) && \ cd $(GTEST_MAKE_DIR) && \ $(MAKE) -f ../make/Makefile CXXFLAGS="$(PIE) $(CFLAGS) -g -w" \ - CXX="$(CLANG_ASAN_CXX)" + CXX="$(CLANG_CXX)" -RTL_LINT_FITLER=-readability/casting,-readability/check,-build/include,-build/header_guard,-build/class,-legal/copyright +RTL_LINT_FILTER=-readability/casting,-readability/check,-build/include,-build/header_guard,-build/class,-legal/copyright,-build/namespaces # TODO(kcc): remove these filters one by one -TEST_LINT_FITLER=-readability/casting,-build/include,-legal/copyright,-whitespace/newline,-runtime/sizeof,-runtime/int,-runtime/printf +TEST_LINT_FILTER=-readability/casting,-build/include,-legal/copyright,-whitespace/newline,-runtime/sizeof,-runtime/int,-runtime/printf LLVM_LINT_FILTER=-,+whitespace @@ -335,8 +329,10 @@ ADDRESS_SANITIZER_CPP=../../../../lib/Transforms/Instrumentation/AddressSanitize lint: third_party/cpplint/cpplint.py --filter=$(LLVM_LINT_FILTER) $(ADDRESS_SANITIZER_CPP) - third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FITLER) asan_*.cc asan_*.h - third_party/cpplint/cpplint.py --filter=$(TEST_LINT_FITLER) tests/*.cc + third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) asan_*.cc asan_*.h + third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) $(INTERCEPTION)/interception*.h $(INTERCEPTION)/interception*.cc + third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) $(COMMON)/sanitizer_*.h $(COMMON)/sanitizer_*.cc + third_party/cpplint/cpplint.py --filter=$(TEST_LINT_FILTER) tests/*.cc output_tests/*.cc get_third_party: rm -rf third_party @@ -348,5 +344,6 @@ get_third_party: clean: rm -f *.o *.ll *.S *.a *.log asan_test64* asan_test32* a.out perf.data log + rm -f $(LIBASAN_INST_DIR)/libclang_rt.asan-*.a rm -rf $(BIN) rm -rf $(GTEST_ROOT)/make-* diff --git a/lib/asan/README.txt b/lib/asan/README.txt index 00ae3c48f745..5e6600489a69 100644 --- a/lib/asan/README.txt +++ b/lib/asan/README.txt @@ -10,7 +10,6 @@ Makefile.mk : Currently a stub for a proper makefile. not usable. Makefile.old : Old out-of-tree makefile, the only usable one so far. asan_*.{cc,h} : Sources of the asan run-time lirbary. mach_override/* : Utility to override functions on Darwin (MIT License). -sysinfo/* : Portable utility to iterate over /proc/maps (BSD License). scripts/* : Helper scripts. Temporary build instructions (verified on linux): diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc index f86dc0b0205d..352cce00fbee 100644 --- a/lib/asan/asan_allocator.cc +++ b/lib/asan/asan_allocator.cc @@ -1,4 +1,4 @@ -//===-- asan_allocator.cc ---------------------------------------*- C++ -*-===// +//===-- asan_allocator.cc -------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -34,50 +34,68 @@ #include "asan_stats.h" #include "asan_thread.h" #include "asan_thread_registry.h" +#include "sanitizer_common/sanitizer_atomic.h" -#include -#include -#include +#if defined(_WIN32) && !defined(__clang__) +#include +#endif namespace __asan { -#define REDZONE FLAG_redzone -static const size_t kMinAllocSize = REDZONE * 2; -static const size_t kMinMmapSize = 4UL << 20; // 4M -static const uint64_t kMaxAvailableRam = 128ULL << 30; // 128G -static const size_t kMaxThreadLocalQuarantine = 1 << 20; // 1M -static const size_t kMaxSizeForThreadLocalFreeList = 1 << 17; +#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 size_t kMallocSizeClassStepLog = 26; -static const size_t kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog; +static const uptr kMallocSizeClassStepLog = 26; +static const uptr kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog; -#if __WORDSIZE == 32 -static const size_t kMaxAllowedMallocSize = 3UL << 30; // 3G -#else -static const size_t kMaxAllowedMallocSize = 8UL << 30; // 8G -#endif +static const uptr kMaxAllowedMallocSize = + (__WORDSIZE == 32) ? 3UL << 30 : 8UL << 30; -static inline bool IsAligned(uintptr_t a, uintptr_t alignment) { +static inline bool IsAligned(uptr a, uptr alignment) { return (a & (alignment - 1)) == 0; } -static inline size_t Log2(size_t x) { +static inline uptr Log2(uptr x) { CHECK(IsPowerOfTwo(x)); +#if !defined(_WIN32) || defined(__clang__) return __builtin_ctzl(x); +#elif defined(_WIN64) + unsigned long ret; // NOLINT + _BitScanForward64(&ret, x); + return ret; +#else + unsigned long ret; // NOLINT + _BitScanForward(&ret, x); + return ret; +#endif } -static inline size_t RoundUpToPowerOfTwo(size_t size) { +static inline uptr RoundUpToPowerOfTwo(uptr size) { CHECK(size); if (IsPowerOfTwo(size)) return size; - size_t up = __WORDSIZE - __builtin_clzl(size); - CHECK(size < (1ULL << up)); - CHECK(size > (1ULL << (up - 1))); - return 1UL << up; + + unsigned long up; // NOLINT +#if !defined(_WIN32) || defined(__clang__) + up = __WORDSIZE - 1 - __builtin_clzl(size); +#elif defined(_WIN64) + _BitScanReverse64(&up, size); +#else + _BitScanReverse(&up, size); +#endif + CHECK(size < (1ULL << (up + 1))); + CHECK(size > (1ULL << up)); + return 1UL << (up + 1); } -static inline size_t SizeClassToSize(uint8_t size_class) { +static inline uptr SizeClassToSize(u8 size_class) { CHECK(size_class < kNumberOfSizeClasses); if (size_class <= kMallocSizeClassStepLog) { return 1UL << size_class; @@ -86,10 +104,10 @@ static inline size_t SizeClassToSize(uint8_t size_class) { } } -static inline uint8_t SizeToSizeClass(size_t size) { - uint8_t res = 0; +static inline u8 SizeToSizeClass(uptr size) { + u8 res = 0; if (size <= kMallocSizeClassStep) { - size_t rounded = RoundUpToPowerOfTwo(size); + uptr rounded = RoundUpToPowerOfTwo(size); res = Log2(rounded); } else { res = ((size + kMallocSizeClassStep - 1) / kMallocSizeClassStep) @@ -102,7 +120,7 @@ static inline uint8_t SizeToSizeClass(size_t size) { // Given REDZONE bytes, we need to mark first size bytes // as addressable and the rest REDZONE-size bytes as unaddressable. -static void PoisonHeapPartialRightRedzone(uintptr_t mem, size_t size) { +static void PoisonHeapPartialRightRedzone(uptr mem, uptr size) { CHECK(size <= REDZONE); CHECK(IsAligned(mem, REDZONE)); CHECK(IsPowerOfTwo(SHADOW_GRANULARITY)); @@ -112,11 +130,11 @@ static void PoisonHeapPartialRightRedzone(uintptr_t mem, size_t size) { kAsanHeapRightRedzoneMagic); } -static uint8_t *MmapNewPagesAndPoisonShadow(size_t size) { +static u8 *MmapNewPagesAndPoisonShadow(uptr size) { CHECK(IsAligned(size, kPageSize)); - uint8_t *res = (uint8_t*)AsanMmapSomewhereOrDie(size, __FUNCTION__); - PoisonShadow((uintptr_t)res, size, kAsanHeapLeftRedzoneMagic); - if (FLAG_debug) { + 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; @@ -128,103 +146,114 @@ static uint8_t *MmapNewPagesAndPoisonShadow(size_t size) { // 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 case 'next' contains the address -// of the AsanChunk). +// 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 = 0x573B, - CHUNK_ALLOCATED = 0x3204, - CHUNK_QUARANTINE = 0x1978, - CHUNK_MEMALIGN = 0xDC68, + CHUNK_AVAILABLE = 0x57, + CHUNK_ALLOCATED = 0x32, + CHUNK_QUARANTINE = 0x19, + CHUNK_MEMALIGN = 0xDC }; struct ChunkBase { - uint16_t chunk_state; - uint8_t size_class; - uint32_t offset; // User-visible memory starts at this+offset (beg()). - int32_t alloc_tid; - int32_t free_tid; - size_t used_size; // Size requested by the user. + // 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 used_size : FIRST_32_SECOND_64(32, 56); // 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; - uintptr_t beg() { return (uintptr_t)this + offset; } - size_t Size() { return SizeClassToSize(size_class); } - uint8_t SizeClass() { return size_class; } + // 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 { - uint32_t *compressed_alloc_stack() { - CHECK(REDZONE >= sizeof(ChunkBase)); - return (uint32_t*)((uintptr_t)this + sizeof(ChunkBase)); + u32 *compressed_alloc_stack() { + return (u32*)((uptr)this + sizeof(ChunkBase)); } - uint32_t *compressed_free_stack() { - CHECK(REDZONE >= sizeof(ChunkBase)); - return (uint32_t*)((uintptr_t)this + REDZONE); + 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. - size_t compressed_alloc_stack_size() { - return (REDZONE - sizeof(ChunkBase)) / sizeof(uint32_t); + uptr compressed_alloc_stack_size() { + if (REDZONE < sizeof(ChunkBase)) return 0; + return (REDZONE - sizeof(ChunkBase)) / sizeof(u32); } - size_t compressed_free_stack_size() { - return (REDZONE) / sizeof(uint32_t); + uptr compressed_free_stack_size() { + if (REDZONE < sizeof(ChunkBase)) return 0; + return (REDZONE) / sizeof(u32); } - bool AddrIsInside(uintptr_t addr, size_t access_size, size_t *offset) { - if (addr >= beg() && (addr + access_size) <= (beg() + used_size)) { - *offset = addr - beg(); + bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) { + if (addr >= Beg() && (addr + access_size) <= (Beg() + used_size)) { + *offset = addr - Beg(); return true; } return false; } - bool AddrIsAtLeft(uintptr_t addr, size_t access_size, size_t *offset) { - if (addr < beg()) { - *offset = beg() - addr; + bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) { + if (addr < Beg()) { + *offset = Beg() - addr; return true; } return false; } - bool AddrIsAtRight(uintptr_t addr, size_t access_size, size_t *offset) { - if (addr + access_size >= beg() + used_size) { - if (addr <= beg() + used_size) + bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) { + if (addr + access_size >= Beg() + used_size) { + if (addr <= Beg() + used_size) *offset = 0; else - *offset = addr - (beg() + used_size); + *offset = addr - (Beg() + used_size); return true; } return false; } - void DescribeAddress(uintptr_t addr, size_t access_size) { - size_t offset; - Printf("%p is located ", addr); + void DescribeAddress(uptr addr, uptr access_size) { + uptr offset; + AsanPrintf("%p is located ", (void*)addr); if (AddrIsInside(addr, access_size, &offset)) { - Printf("%ld bytes inside of", offset); + AsanPrintf("%zu bytes inside of", offset); } else if (AddrIsAtLeft(addr, access_size, &offset)) { - Printf("%ld bytes to the left of", offset); + AsanPrintf("%zu bytes to the left of", offset); } else if (AddrIsAtRight(addr, access_size, &offset)) { - Printf("%ld bytes to the right of", offset); + AsanPrintf("%zu bytes to the right of", offset); } else { - Printf(" somewhere around (this is AddressSanitizer bug!)"); + AsanPrintf(" somewhere around (this is AddressSanitizer bug!)"); } - Printf(" %lu-byte region [%p,%p)\n", - used_size, beg(), beg() + used_size); + AsanPrintf(" %zu-byte region [%p,%p)\n", + used_size, (void*)Beg(), (void*)(Beg() + used_size)); } }; -static AsanChunk *PtrToChunk(uintptr_t ptr) { +static AsanChunk *PtrToChunk(uptr ptr) { AsanChunk *m = (AsanChunk*)(ptr - REDZONE); if (m->chunk_state == CHUNK_MEMALIGN) { - m = m->next; + m = (AsanChunk*)((uptr)m - m->used_size); } return m; } void AsanChunkFifoList::PushList(AsanChunkFifoList *q) { + CHECK(q->size() > 0); if (last_) { CHECK(first_); CHECK(!last_->next); @@ -234,13 +263,16 @@ void AsanChunkFifoList::PushList(AsanChunkFifoList *q) { CHECK(!first_); last_ = q->last_; first_ = q->first_; + CHECK(first_); } + CHECK(last_); + CHECK(!last_->next); size_ += q->size(); q->clear(); } void AsanChunkFifoList::Push(AsanChunk *n) { - CHECK(n->next == NULL); + CHECK(n->next == 0); if (last_) { CHECK(first_); CHECK(!last_->next); @@ -260,8 +292,8 @@ AsanChunk *AsanChunkFifoList::Pop() { CHECK(first_); AsanChunk *res = first_; first_ = first_->next; - if (first_ == NULL) - last_ = NULL; + if (first_ == 0) + last_ = 0; CHECK(size_ >= res->Size()); size_ -= res->Size(); if (last_) { @@ -272,11 +304,11 @@ AsanChunk *AsanChunkFifoList::Pop() { // All pages we ever allocated. struct PageGroup { - uintptr_t beg; - uintptr_t end; - size_t size_of_chunk; - uintptr_t last_chunk; - bool InRange(uintptr_t addr) { + uptr beg; + uptr end; + uptr size_of_chunk; + uptr last_chunk; + bool InRange(uptr addr) { return addr >= beg && addr < end; } }; @@ -286,12 +318,12 @@ class MallocInfo { explicit MallocInfo(LinkerInitialized x) : mu_(x) { } - AsanChunk *AllocateChunks(uint8_t size_class, size_t n_chunks) { - AsanChunk *m = NULL; + AsanChunk *AllocateChunks(u8 size_class, uptr n_chunks) { + AsanChunk *m = 0; AsanChunk **fl = &free_lists_[size_class]; { ScopedLock lock(&mu_); - for (size_t i = 0; i < n_chunks; i++) { + for (uptr i = 0; i < n_chunks; i++) { if (!(*fl)) { *fl = GetNewChunks(size_class); } @@ -307,17 +339,17 @@ class MallocInfo { void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x, bool eat_free_lists) { - CHECK(FLAG_quarantine_size > 0); + CHECK(flags()->quarantine_size > 0); ScopedLock lock(&mu_); AsanChunkFifoList *q = &x->quarantine_; if (q->size() > 0) { quarantine_.PushList(q); - while (quarantine_.size() > FLAG_quarantine_size) { + while (quarantine_.size() > (uptr)flags()->quarantine_size) { QuarantinePop(); } } if (eat_free_lists) { - for (size_t size_class = 0; size_class < kNumberOfSizeClasses; + for (uptr size_class = 0; size_class < kNumberOfSizeClasses; size_class++) { AsanChunk *m = x->free_lists_[size_class]; while (m) { @@ -336,15 +368,13 @@ class MallocInfo { quarantine_.Push(chunk); } - AsanChunk *FindMallocedOrFreed(uintptr_t addr, size_t access_size) { + AsanChunk *FindMallocedOrFreed(uptr addr, uptr access_size) { ScopedLock lock(&mu_); return FindChunkByAddr(addr); } - // TODO(glider): AllocationSize() may become very slow if the size of - // page_groups_ grows. This can be fixed by increasing kMinMmapSize, - // but a better solution is to speed up the search somehow. - size_t AllocationSize(uintptr_t ptr) { + uptr AllocationSize(uptr ptr) { + if (!ptr) return 0; ScopedLock lock(&mu_); // first, check if this is our memory @@ -368,40 +398,60 @@ class MallocInfo { void PrintStatus() { ScopedLock lock(&mu_); - size_t malloced = 0; + uptr malloced = 0; - Printf(" MallocInfo: in quarantine: %ld malloced: %ld; ", + Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ", quarantine_.size() >> 20, malloced >> 20); - for (size_t j = 1; j < kNumberOfSizeClasses; j++) { + for (uptr j = 1; j < kNumberOfSizeClasses; j++) { AsanChunk *i = free_lists_[j]; if (!i) continue; - size_t t = 0; + uptr t = 0; for (; i; i = i->next) { t += i->Size(); } - Printf("%ld:%ld ", j, t >> 20); + Printf("%zu:%zu ", j, t >> 20); } Printf("\n"); } - PageGroup *FindPageGroup(uintptr_t addr) { + PageGroup *FindPageGroup(uptr addr) { ScopedLock lock(&mu_); return FindPageGroupUnlocked(addr); } private: - PageGroup *FindPageGroupUnlocked(uintptr_t addr) { - for (int i = 0; i < n_page_groups_; i++) { - PageGroup *g = page_groups_[i]; - if (g->InRange(addr)) { - return g; + 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; } } - return NULL; + 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(uintptr_t addr, + 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 && @@ -411,7 +461,7 @@ class MallocInfo { left_chunk->chunk_state != CHUNK_AVAILABLE) return left_chunk; // Choose based on offset. - size_t l_offset = 0, r_offset = 0; + uptr l_offset = 0, r_offset = 0; CHECK(left_chunk->AddrIsAtRight(addr, 1, &l_offset)); CHECK(right_chunk->AddrIsAtLeft(addr, 1, &r_offset)); if (l_offset < r_offset) @@ -419,33 +469,33 @@ class MallocInfo { return right_chunk; } - AsanChunk *FindChunkByAddr(uintptr_t addr) { + AsanChunk *FindChunkByAddr(uptr addr) { PageGroup *g = FindPageGroupUnlocked(addr); if (!g) return 0; CHECK(g->size_of_chunk); - uintptr_t offset_from_beg = addr - g->beg; - uintptr_t this_chunk_addr = g->beg + + 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); - size_t offset = 0; + uptr offset = 0; if (m->AddrIsInside(addr, 1, &offset)) return m; if (m->AddrIsAtRight(addr, 1, &offset)) { if (this_chunk_addr == g->last_chunk) // rightmost chunk return m; - uintptr_t right_chunk_addr = this_chunk_addr + g->size_of_chunk; + 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->AddrIsAtLeft(addr, 1, &offset)); if (this_chunk_addr == g->beg) // leftmost chunk return m; - uintptr_t left_chunk_addr = this_chunk_addr - g->size_of_chunk; + 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); } @@ -459,10 +509,11 @@ class MallocInfo { 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); - size_t size_class = m->SizeClass(); + uptr size_class = m->SizeClass(); m->next = free_lists_[size_class]; free_lists_[size_class] = m; @@ -475,12 +526,12 @@ class MallocInfo { } // Get a list of newly allocated chunks. - AsanChunk *GetNewChunks(uint8_t size_class) { - size_t size = SizeClassToSize(size_class); + AsanChunk *GetNewChunks(u8 size_class) { + uptr size = SizeClassToSize(size_class); CHECK(IsPowerOfTwo(kMinMmapSize)); CHECK(size < kMinMmapSize || (size % kMinMmapSize) == 0); - size_t mmap_size = Max(size, kMinMmapSize); - size_t n_chunks = mmap_size / size; + uptr mmap_size = Max(size, kMinMmapSize); + uptr n_chunks = mmap_size / size; CHECK(n_chunks * size == mmap_size); if (size < kPageSize) { // Size is small, just poison the last chunk. @@ -490,7 +541,7 @@ class MallocInfo { mmap_size += kPageSize; } CHECK(n_chunks > 0); - uint8_t *mem = MmapNewPagesAndPoisonShadow(mmap_size); + u8 *mem = MmapNewPagesAndPoisonShadow(mmap_size); // Statistics. AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); @@ -498,8 +549,8 @@ class MallocInfo { thread_stats.mmaped += mmap_size; thread_stats.mmaped_by_size[size_class] += n_chunks; - AsanChunk *res = NULL; - for (size_t i = 0; i < n_chunks; i++) { + 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; @@ -508,13 +559,13 @@ class MallocInfo { } PageGroup *pg = (PageGroup*)(mem + n_chunks * size); // This memory is already poisoned, no need to poison it again. - pg->beg = (uintptr_t)mem; + pg->beg = (uptr)mem; pg->end = pg->beg + mmap_size; pg->size_of_chunk = size; - pg->last_chunk = (uintptr_t)(mem + size * (n_chunks - 1)); - int page_group_idx = AtomicInc(&n_page_groups_) - 1; - CHECK(page_group_idx < (int)ASAN_ARRAY_SIZE(page_groups_)); - page_groups_[page_group_idx] = pg; + pg->last_chunk = (uptr)(mem + size * (n_chunks - 1)); + int idx = atomic_fetch_add(&n_page_groups_, 1, memory_order_relaxed); + CHECK(idx < (int)ASAN_ARRAY_SIZE(page_groups_)); + page_groups_[idx] = pg; return res; } @@ -523,7 +574,8 @@ class MallocInfo { AsanLock mu_; PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize]; - int n_page_groups_; // atomic + atomic_uint32_t n_page_groups_; + int n_sorted_page_groups_; }; static MallocInfo malloc_info(LINKER_INITIALIZED); @@ -532,7 +584,7 @@ void AsanThreadLocalMallocStorage::CommitBack() { malloc_info.SwallowThreadLocalMallocStorage(this, true); } -static void Describe(uintptr_t addr, size_t access_size) { +static void Describe(uptr addr, uptr access_size) { AsanChunk *m = malloc_info.FindMallocedOrFreed(addr, access_size); if (!m) return; m->DescribeAddress(addr, access_size); @@ -544,55 +596,56 @@ static void Describe(uintptr_t addr, size_t access_size) { m->compressed_alloc_stack_size()); AsanThread *t = asanThreadRegistry().GetCurrent(); CHECK(t); - if (m->free_tid >= 0) { + if (m->free_tid != kInvalidTid) { AsanThreadSummary *free_thread = asanThreadRegistry().FindByTid(m->free_tid); - Printf("freed by thread T%d here:\n", free_thread->tid()); + AsanPrintf("freed by thread T%d here:\n", free_thread->tid()); AsanStackTrace free_stack; AsanStackTrace::UncompressStack(&free_stack, m->compressed_free_stack(), m->compressed_free_stack_size()); free_stack.PrintStack(); - Printf("previously allocated by thread T%d here:\n", - alloc_thread->tid()); + AsanPrintf("previously allocated by thread T%d here:\n", + alloc_thread->tid()); alloc_stack.PrintStack(); t->summary()->Announce(); free_thread->Announce(); alloc_thread->Announce(); } else { - Printf("allocated by thread T%d here:\n", alloc_thread->tid()); + AsanPrintf("allocated by thread T%d here:\n", alloc_thread->tid()); alloc_stack.PrintStack(); t->summary()->Announce(); alloc_thread->Announce(); } } -static uint8_t *Allocate(size_t alignment, size_t size, AsanStackTrace *stack) { +static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) { __asan_init(); CHECK(stack); if (size == 0) { size = 1; // TODO(kcc): do something smarter } CHECK(IsPowerOfTwo(alignment)); - size_t rounded_size = RoundUpTo(size, REDZONE); - size_t needed_size = rounded_size + REDZONE; + 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", size); + Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", + (void*)size); return 0; } - uint8_t size_class = SizeToSizeClass(needed_size); - size_t size_to_allocate = SizeClassToSize(size_class); + 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 (FLAG_v >= 2) { - Printf("Allocate align: %ld size: %ld class: %d real: %ld\n", + if (flags()->verbosity >= 3) { + Printf("Allocate align: %zu size: %zu class: %u real: %zu\n", alignment, size, size_class, size_to_allocate); } @@ -604,7 +657,7 @@ static uint8_t *Allocate(size_t alignment, size_t size, AsanStackTrace *stack) { thread_stats.malloced_redzones += size_to_allocate - size; thread_stats.malloced_by_size[size_class]++; - AsanChunk *m = NULL; + AsanChunk *m = 0; if (!t || size_to_allocate >= kMaxSizeForThreadLocalFreeList) { // get directly from global storage. m = malloc_info.AllocateChunks(size_class, 1); @@ -613,7 +666,7 @@ static uint8_t *Allocate(size_t alignment, size_t size, AsanStackTrace *stack) { // get from the thread-local storage. AsanChunk **fl = &t->malloc_storage().free_lists_[size_class]; if (!*fl) { - size_t n_new_chunks = kMaxSizeForThreadLocalFreeList / size_to_allocate; + uptr n_new_chunks = kMaxSizeForThreadLocalFreeList / size_to_allocate; *fl = malloc_info.AllocateChunks(size_class, n_new_chunks); thread_stats.malloc_small_slow++; } @@ -623,24 +676,27 @@ static uint8_t *Allocate(size_t alignment, size_t size, AsanStackTrace *stack) { CHECK(m); CHECK(m->chunk_state == CHUNK_AVAILABLE); m->chunk_state = CHUNK_ALLOCATED; - m->next = NULL; + m->next = 0; CHECK(m->Size() == size_to_allocate); - uintptr_t addr = (uintptr_t)m + REDZONE; - CHECK(addr == (uintptr_t)m->compressed_free_stack()); + 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->next = m; + 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; - m->offset = addr - (uintptr_t)m; - CHECK(m->beg() == addr); + CHECK(m->Beg() == addr); m->alloc_tid = t ? t->tid() : 0; - m->free_tid = AsanThread::kInvalidTid; + m->free_tid = kInvalidTid; AsanStackTrace::CompressStack(stack, m->compressed_alloc_stack(), m->compressed_alloc_stack_size()); PoisonShadow(addr, rounded_size, 0); @@ -648,42 +704,49 @@ static uint8_t *Allocate(size_t alignment, size_t size, AsanStackTrace *stack) { PoisonHeapPartialRightRedzone(addr + rounded_size - REDZONE, size & (REDZONE - 1)); } - if (size <= FLAG_max_malloc_fill_size) { - real_memset((void*)addr, 0, rounded_size); + if (size <= (uptr)(flags()->max_malloc_fill_size)) { + REAL(memset)((void*)addr, 0, rounded_size); } - return (uint8_t*)addr; + return (u8*)addr; } -static void Deallocate(uint8_t *ptr, AsanStackTrace *stack) { +static void Deallocate(u8 *ptr, AsanStackTrace *stack) { if (!ptr) return; CHECK(stack); - if (FLAG_debug) { - CHECK(malloc_info.FindPageGroup((uintptr_t)ptr)); + if (flags()->debug) { + CHECK(malloc_info.FindPageGroup((uptr)ptr)); } // Printf("Deallocate %p\n", ptr); - AsanChunk *m = PtrToChunk((uintptr_t)ptr); - if (m->chunk_state == CHUNK_QUARANTINE) { - Report("ERROR: AddressSanitizer attempting double-free on %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) { + AsanReport("ERROR: AddressSanitizer attempting double-free on %p:\n", ptr); stack->PrintStack(); - m->DescribeAddress((uintptr_t)ptr, 1); + Describe((uptr)ptr, 1); ShowStatsAndAbort(); - } else if (m->chunk_state != CHUNK_ALLOCATED) { - Report("ERROR: AddressSanitizer attempting free on address which was not" - " malloc()-ed: %p\n", ptr); + } else if (old_chunk_state != CHUNK_ALLOCATED) { + AsanReport("ERROR: AddressSanitizer attempting free on address " + "which was not malloc()-ed: %p\n", ptr); stack->PrintStack(); ShowStatsAndAbort(); } - CHECK(m->chunk_state == CHUNK_ALLOCATED); - CHECK(m->free_tid == AsanThread::kInvalidTid); + CHECK(old_chunk_state == CHUNK_ALLOCATED); + // 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; AsanStackTrace::CompressStack(stack, m->compressed_free_stack(), m->compressed_free_stack_size()); - size_t rounded_size = RoundUpTo(m->used_size, REDZONE); - PoisonShadow((uintptr_t)ptr, rounded_size, kAsanHeapFreeMagic); + uptr rounded_size = RoundUpTo(m->used_size, REDZONE); + PoisonShadow((uptr)ptr, rounded_size, kAsanHeapFreeMagic); // Statistics. AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); @@ -691,22 +754,21 @@ static void Deallocate(uint8_t *ptr, AsanStackTrace *stack) { thread_stats.freed += m->used_size; thread_stats.freed_by_size[m->SizeClass()]++; - m->chunk_state = CHUNK_QUARANTINE; + CHECK(m->chunk_state == CHUNK_QUARANTINE); + if (t) { AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); - CHECK(!m->next); ms->quarantine_.Push(m); if (ms->quarantine_.size() > kMaxThreadLocalQuarantine) { malloc_info.SwallowThreadLocalMallocStorage(ms, false); } } else { - CHECK(!m->next); malloc_info.BypassThreadLocalQuarantine(m); } } -static uint8_t *Reallocate(uint8_t *old_ptr, size_t new_size, +static u8 *Reallocate(u8 *old_ptr, uptr new_size, AsanStackTrace *stack) { CHECK(old_ptr && new_size); @@ -715,13 +777,14 @@ static uint8_t *Reallocate(uint8_t *old_ptr, size_t new_size, thread_stats.reallocs++; thread_stats.realloced += new_size; - AsanChunk *m = PtrToChunk((uintptr_t)old_ptr); + AsanChunk *m = PtrToChunk((uptr)old_ptr); CHECK(m->chunk_state == CHUNK_ALLOCATED); - size_t old_size = m->used_size; - size_t memcpy_size = Min(new_size, old_size); - uint8_t *new_ptr = Allocate(0, new_size, stack); + uptr old_size = m->used_size; + uptr memcpy_size = Min(new_size, old_size); + u8 *new_ptr = Allocate(0, new_size, stack); if (new_ptr) { - real_memcpy(new_ptr, old_ptr, memcpy_size); + CHECK(REAL(memcpy) != 0); + REAL(memcpy)(new_ptr, old_ptr, memcpy_size); Deallocate(old_ptr, stack); } return new_ptr; @@ -738,9 +801,9 @@ static uint8_t *Reallocate(uint8_t *old_ptr, size_t new_size, // program must provide implementation of this hook. // If macro is undefined, the hook is no-op. #ifdef ASAN_NEW_HOOK -extern "C" void ASAN_NEW_HOOK(void *ptr, size_t size); +extern "C" void ASAN_NEW_HOOK(void *ptr, uptr size); #else -static inline void ASAN_NEW_HOOK(void *ptr, size_t size) { } +static inline void ASAN_NEW_HOOK(void *ptr, uptr size) { } #endif #ifdef ASAN_DELETE_HOOK @@ -751,7 +814,7 @@ static inline void ASAN_DELETE_HOOK(void *ptr) { } namespace __asan { -void *asan_memalign(size_t alignment, size_t size, AsanStackTrace *stack) { +void *asan_memalign(uptr alignment, uptr size, AsanStackTrace *stack) { void *ptr = (void*)Allocate(alignment, size, stack); ASAN_NEW_HOOK(ptr, size); return ptr; @@ -759,43 +822,43 @@ void *asan_memalign(size_t alignment, size_t size, AsanStackTrace *stack) { void asan_free(void *ptr, AsanStackTrace *stack) { ASAN_DELETE_HOOK(ptr); - Deallocate((uint8_t*)ptr, stack); + Deallocate((u8*)ptr, stack); } -void *asan_malloc(size_t size, AsanStackTrace *stack) { +void *asan_malloc(uptr size, AsanStackTrace *stack) { void *ptr = (void*)Allocate(0, size, stack); ASAN_NEW_HOOK(ptr, size); return ptr; } -void *asan_calloc(size_t nmemb, size_t size, AsanStackTrace *stack) { +void *asan_calloc(uptr nmemb, uptr size, AsanStackTrace *stack) { void *ptr = (void*)Allocate(0, nmemb * size, stack); if (ptr) - real_memset(ptr, 0, nmemb * size); + REAL(memset)(ptr, 0, nmemb * size); ASAN_NEW_HOOK(ptr, nmemb * size); return ptr; } -void *asan_realloc(void *p, size_t size, AsanStackTrace *stack) { - if (p == NULL) { +void *asan_realloc(void *p, uptr size, AsanStackTrace *stack) { + if (p == 0) { void *ptr = (void*)Allocate(0, size, stack); ASAN_NEW_HOOK(ptr, size); return ptr; } else if (size == 0) { ASAN_DELETE_HOOK(p); - Deallocate((uint8_t*)p, stack); - return NULL; + Deallocate((u8*)p, stack); + return 0; } - return Reallocate((uint8_t*)p, size, stack); + return Reallocate((u8*)p, size, stack); } -void *asan_valloc(size_t size, AsanStackTrace *stack) { +void *asan_valloc(uptr size, AsanStackTrace *stack) { void *ptr = (void*)Allocate(kPageSize, size, stack); ASAN_NEW_HOOK(ptr, size); return ptr; } -void *asan_pvalloc(size_t size, AsanStackTrace *stack) { +void *asan_pvalloc(uptr size, AsanStackTrace *stack) { size = RoundUpTo(size, kPageSize); if (size == 0) { // pvalloc(0) should allocate one page. @@ -806,61 +869,76 @@ void *asan_pvalloc(size_t size, AsanStackTrace *stack) { return ptr; } -int asan_posix_memalign(void **memptr, size_t alignment, size_t size, +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, AsanStackTrace *stack) { void *ptr = Allocate(alignment, size, stack); - CHECK(IsAligned((uintptr_t)ptr, alignment)); + CHECK(IsAligned((uptr)ptr, alignment)); ASAN_NEW_HOOK(ptr, size); *memptr = ptr; return 0; } -size_t __asan_mz_size(const void *ptr) { - return malloc_info.AllocationSize((uintptr_t)ptr); +uptr asan_malloc_usable_size(void *ptr, AsanStackTrace *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)) { + AsanReport("ERROR: AddressSanitizer attempting to call " + "malloc_usable_size() for pointer which is " + "not owned: %p\n", ptr); + stack->PrintStack(); + Describe((uptr)ptr, 1); + ShowStatsAndAbort(); + } + return usable_size; } -void DescribeHeapAddress(uintptr_t addr, uintptr_t access_size) { +uptr asan_mz_size(const void *ptr) { + return malloc_info.AllocationSize((uptr)ptr); +} + +void DescribeHeapAddress(uptr addr, uptr access_size) { Describe(addr, access_size); } -void __asan_mz_force_lock() { +void asan_mz_force_lock() { malloc_info.ForceLock(); } -void __asan_mz_force_unlock() { +void asan_mz_force_unlock() { malloc_info.ForceUnlock(); } // ---------------------- Fake stack-------------------- {{{1 FakeStack::FakeStack() { - CHECK(real_memset); - real_memset(this, 0, sizeof(*this)); + CHECK(REAL(memset) != 0); + REAL(memset)(this, 0, sizeof(*this)); } -bool FakeStack::AddrIsInSizeClass(uintptr_t addr, size_t size_class) { - uintptr_t mem = allocated_size_classes_[size_class]; - uintptr_t size = ClassMmapSize(size_class); +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; } -uintptr_t FakeStack::AddrIsInFakeStack(uintptr_t addr) { - for (size_t i = 0; i < kNumberOfSizeClasses; i++) { +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 size_t FakeStack::ComputeSizeClass(size_t alloc_size) { - size_t rounded_size = RoundUpToPowerOfTwo(alloc_size); - size_t log = Log2(rounded_size); +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 %ld log %ld\n", alloc_size, log); + Printf("alloc_size %zu log %zu\n", alloc_size, log); } CHECK(alloc_size > (1UL << (log-1))); - size_t res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog; + uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog; CHECK(res < kNumberOfSizeClasses); CHECK(ClassSize(res) >= rounded_size); return res; @@ -892,36 +970,36 @@ FakeFrame *FakeFrameFifo::FifoPop() { return res; } -void FakeStack::Init(size_t stack_size) { +void FakeStack::Init(uptr stack_size) { stack_size_ = stack_size; alive_ = true; } void FakeStack::Cleanup() { alive_ = false; - for (size_t i = 0; i < kNumberOfSizeClasses; i++) { - uintptr_t mem = allocated_size_classes_[i]; + 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; - AsanUnmapOrDie((void*)mem, ClassMmapSize(i)); + UnmapOrDie((void*)mem, ClassMmapSize(i)); } } } -size_t FakeStack::ClassMmapSize(size_t size_class) { +uptr FakeStack::ClassMmapSize(uptr size_class) { return RoundUpToPowerOfTwo(stack_size_); } -void FakeStack::AllocateOneSizeClass(size_t size_class) { +void FakeStack::AllocateOneSizeClass(uptr size_class) { CHECK(ClassMmapSize(size_class) >= kPageSize); - uintptr_t new_mem = (uintptr_t)AsanMmapSomewhereOrDie( + uptr new_mem = (uptr)MmapOrDie( ClassMmapSize(size_class), __FUNCTION__); - // Printf("T%d new_mem[%ld]: %p-%p mmap %ld\n", + // 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)); - size_t i; + uptr i; for (i = 0; i < ClassMmapSize(size_class); i += ClassSize(size_class)) { size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i)); @@ -930,10 +1008,10 @@ void FakeStack::AllocateOneSizeClass(size_t size_class) { allocated_size_classes_[size_class] = new_mem; } -uintptr_t FakeStack::AllocateStack(size_t size, size_t real_stack) { +uptr FakeStack::AllocateStack(uptr size, uptr real_stack) { if (!alive_) return real_stack; CHECK(size <= kMaxStackMallocSize && size > 1); - size_t size_class = ComputeSizeClass(size); + uptr size_class = ComputeSizeClass(size); if (!allocated_size_classes_[size_class]) { AllocateOneSizeClass(size_class); } @@ -947,23 +1025,23 @@ uintptr_t FakeStack::AllocateStack(size_t size, size_t real_stack) { DeallocateFrame(top); } call_stack_.LifoPush(fake_frame); - uintptr_t ptr = (uintptr_t)fake_frame; + uptr ptr = (uptr)fake_frame; PoisonShadow(ptr, size, 0); return ptr; } void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { CHECK(alive_); - size_t size = fake_frame->size_minus_one + 1; - size_t size_class = ComputeSizeClass(size); + uptr size = fake_frame->size_minus_one + 1; + uptr size_class = ComputeSizeClass(size); CHECK(allocated_size_classes_[size_class]); - uintptr_t ptr = (uintptr_t)fake_frame; + 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(size_t ptr, size_t size, size_t real_stack) { +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); @@ -976,20 +1054,20 @@ void FakeStack::OnFree(size_t ptr, size_t size, size_t real_stack) { // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT -size_t __asan_stack_malloc(size_t size, size_t real_stack) { - if (!FLAG_use_fake_stack) return real_stack; +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; } - size_t ptr = t->fake_stack().AllocateStack(size, real_stack); - // Printf("__asan_stack_malloc %p %ld %p\n", ptr, 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(size_t ptr, size_t size, size_t real_stack) { - if (!FLAG_use_fake_stack) return; +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); } @@ -997,23 +1075,25 @@ void __asan_stack_free(size_t ptr, size_t size, size_t real_stack) { // ASan allocator doesn't reserve extra bytes, so normally we would // just return "size". -size_t __asan_get_estimated_allocated_size(size_t 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 (p == NULL) || - (malloc_info.AllocationSize((uintptr_t)p) > 0); + return malloc_info.AllocationSize((uptr)p) > 0; } -size_t __asan_get_allocated_size(const void *p) { - if (p == NULL) return 0; - size_t allocated_size = malloc_info.AllocationSize((uintptr_t)p); +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) { - Printf("__asan_get_allocated_size failed, ptr=%p is not owned\n", p); + AsanReport("ERROR: AddressSanitizer attempting to call " + "__asan_get_allocated_size() for pointer which is " + "not owned: %p\n", p); PRINT_CURRENT_STACK(); + Describe((uptr)p, 1); ShowStatsAndAbort(); } return allocated_size; diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h index 9b691e04ff40..2aed59853868 100644 --- a/lib/asan/asan_allocator.h +++ b/lib/asan/asan_allocator.h @@ -20,7 +20,7 @@ namespace __asan { -static const size_t kNumberOfSizeClasses = 255; +static const uptr kNumberOfSizeClasses = 255; struct AsanChunk; class AsanChunkFifoList { @@ -30,23 +30,23 @@ class AsanChunkFifoList { void Push(AsanChunk *n); void PushList(AsanChunkFifoList *q); AsanChunk *Pop(); - size_t size() { return size_; } + uptr size() { return size_; } void clear() { - first_ = last_ = NULL; + first_ = last_ = 0; size_ = 0; } private: AsanChunk *first_; AsanChunk *last_; - size_t size_; + uptr size_; }; struct AsanThreadLocalMallocStorage { explicit AsanThreadLocalMallocStorage(LinkerInitialized x) : quarantine_(x) { } AsanThreadLocalMallocStorage() { - CHECK(real_memset); - real_memset(this, 0, sizeof(AsanThreadLocalMallocStorage)); + CHECK(REAL(memset)); + REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage)); } AsanChunkFifoList quarantine_; @@ -57,11 +57,11 @@ struct AsanThreadLocalMallocStorage { // Fake stack frame contains local variables of one function. // This struct should fit into a stack redzone (32 bytes). struct FakeFrame { - uintptr_t magic; // Modified by the instrumented code. - uintptr_t descr; // Modified by the instrumented code. + uptr magic; // Modified by the instrumented code. + uptr descr; // Modified by the instrumented code. FakeFrame *next; - uint64_t real_stack : 48; - uint64_t size_minus_one : 16; + u64 real_stack : 48; + u64 size_minus_one : 16; }; struct FakeFrameFifo { @@ -100,58 +100,60 @@ class FakeStack { public: FakeStack(); explicit FakeStack(LinkerInitialized) {} - void Init(size_t stack_size); + void Init(uptr stack_size); void StopUsingFakeStack() { alive_ = false; } void Cleanup(); - uintptr_t AllocateStack(size_t size, size_t real_stack); - static void OnFree(size_t ptr, size_t size, size_t real_stack); + uptr AllocateStack(uptr size, uptr real_stack); + static void OnFree(uptr ptr, uptr size, uptr real_stack); // Return the bottom of the maped region. - uintptr_t AddrIsInFakeStack(uintptr_t addr); + uptr AddrIsInFakeStack(uptr addr); + bool StackSize() { return stack_size_; } private: - static const size_t kMinStackFrameSizeLog = 9; // Min frame is 512B. - static const size_t kMaxStackFrameSizeLog = 16; // Max stack frame is 64K. - static const size_t kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; - static const size_t kNumberOfSizeClasses = + 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(uintptr_t addr, size_t size_class); + bool AddrIsInSizeClass(uptr addr, uptr size_class); // Each size class should be large enough to hold all frames. - size_t ClassMmapSize(size_t size_class); + uptr ClassMmapSize(uptr size_class); - size_t ClassSize(size_t size_class) { + uptr ClassSize(uptr size_class) { return 1UL << (size_class + kMinStackFrameSizeLog); } void DeallocateFrame(FakeFrame *fake_frame); - size_t ComputeSizeClass(size_t alloc_size); - void AllocateOneSizeClass(size_t size_class); + uptr ComputeSizeClass(uptr alloc_size); + void AllocateOneSizeClass(uptr size_class); - size_t stack_size_; + uptr stack_size_; bool alive_; - uintptr_t allocated_size_classes_[kNumberOfSizeClasses]; + uptr allocated_size_classes_[kNumberOfSizeClasses]; FakeFrameFifo size_classes_[kNumberOfSizeClasses]; FakeFrameLifo call_stack_; }; -void *asan_memalign(size_t alignment, size_t size, AsanStackTrace *stack); +void *asan_memalign(uptr alignment, uptr size, AsanStackTrace *stack); void asan_free(void *ptr, AsanStackTrace *stack); -void *asan_malloc(size_t size, AsanStackTrace *stack); -void *asan_calloc(size_t nmemb, size_t size, AsanStackTrace *stack); -void *asan_realloc(void *p, size_t size, AsanStackTrace *stack); -void *asan_valloc(size_t size, AsanStackTrace *stack); -void *asan_pvalloc(size_t size, AsanStackTrace *stack); +void *asan_malloc(uptr size, AsanStackTrace *stack); +void *asan_calloc(uptr nmemb, uptr size, AsanStackTrace *stack); +void *asan_realloc(void *p, uptr size, AsanStackTrace *stack); +void *asan_valloc(uptr size, AsanStackTrace *stack); +void *asan_pvalloc(uptr size, AsanStackTrace *stack); -int asan_posix_memalign(void **memptr, size_t alignment, size_t size, +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, AsanStackTrace *stack); +uptr asan_malloc_usable_size(void *ptr, AsanStackTrace *stack); -size_t __asan_mz_size(const void *ptr); -void __asan_mz_force_lock(); -void __asan_mz_force_unlock(); -void DescribeHeapAddress(uintptr_t addr, size_t access_size); +uptr asan_mz_size(const void *ptr); +void asan_mz_force_lock(); +void asan_mz_force_unlock(); +void DescribeHeapAddress(uptr addr, uptr access_size); } // namespace __asan #endif // ASAN_ALLOCATOR_H diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h new file mode 100644 index 000000000000..ca9cf84ba6c5 --- /dev/null +++ b/lib/asan/asan_flags.h @@ -0,0 +1,97 @@ +//===-- asan_flags.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// 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 runtime flags. +//===----------------------------------------------------------------------===// + +#ifndef ASAN_FLAGS_H +#define ASAN_FLAGS_H + +#include "sanitizer_common/sanitizer_interface_defs.h" + +// ASan flag values can be defined in three ways: +// 1) initialized with default values at startup. +// 2) overriden from string returned by user-specified function +// __asan_default_options(). +// 3) overriden from env variable ASAN_OPTIONS. + +extern "C" { +// Can be overriden by user. +const char *__asan_default_options() SANITIZER_WEAK_ATTRIBUTE; +} // extern "C" + +namespace __asan { + +struct Flags { + // Size (in bytes) of quarantine used to detect use-after-free errors. + // Lower value may reduce memory usage but increase the chance of + // false negatives. + int quarantine_size; + // 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. + // Requirement: redzone >= 32, is a power of two. + int redzone; + // If set, prints some debugging information and does additional checks. + bool debug; + // Controls the way to handle globals (0 - don't detect buffer overflow + // on globals, 1 - detect buffer overflow, 2 - print data about registered + // globals). + int report_globals; + // Max number of stack frames kept for each allocation. + int malloc_context_size; + // If set, uses custom wrappers and replacements for libc string functions + // to find more errors. + bool replace_str; + // If set, uses custom wrappers for memset/memcpy/memmove intinsics. + bool replace_intrin; + // Used on Mac only. See comments in asan_mac.cc and asan_malloc_mac.cc. + bool replace_cfallocator; + // 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; + // Override exit status if something was reported. + int exitcode; + // If set, user may manually mark memory regions as poisoned or unpoisoned. + bool allow_user_poisoning; + // Number of seconds to sleep between printing an error report and + // terminating application. Useful for debug purposes (when one needs + // to attach gdb, for example). + int sleep_before_dying; + // If set, registers ASan custom segv handler. + bool handle_segv; + // If set, uses alternate stack for signal handling. + bool use_sigaltstack; + // Allow the users to work around the bug in Nvidia drivers prior to 295.*. + bool check_malloc_usable_size; + // If set, explicitly unmaps (huge) shadow at exit. + bool unmap_shadow_on_exit; + // If set, calls abort() instead of _exit() after printing an error report. + bool abort_on_error; + // If set, prints ASan exit stats even after program terminates successfully. + bool atexit; + // By default, disable core dumper on 64-bit - it makes little sense + // to dump 16T+ core. + bool disable_core; +}; + +Flags *flags(); +void InitializeFlags(Flags *f, const char *env); + +} // namespace __asan + +#endif // ASAN_FLAGS_H diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc index f53bf38db4c2..f8c4040b8e86 100644 --- a/lib/asan/asan_globals.cc +++ b/lib/asan/asan_globals.cc @@ -1,4 +1,4 @@ -//===-- asan_globals.cc -----------------------------------------*- C++ -*-===// +//===-- asan_globals.cc ---------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -36,16 +36,16 @@ static ListOfGlobals *list_of_globals; static LowLevelAllocator allocator_for_globals(LINKER_INITIALIZED); void PoisonRedZones(const Global &g) { - size_t shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE; + uptr shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE; CHECK(shadow_rz_size == 1 || shadow_rz_size == 2 || shadow_rz_size == 4); // full right redzone - size_t g_aligned_size = kGlobalAndStackRedzone * + uptr g_aligned_size = kGlobalAndStackRedzone * ((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone); PoisonShadow(g.beg + g_aligned_size, kGlobalAndStackRedzone, kAsanGlobalRedzoneMagic); if ((g.size % kGlobalAndStackRedzone) != 0) { // partial right redzone - uint64_t g_aligned_down_size = kGlobalAndStackRedzone * + u64 g_aligned_down_size = kGlobalAndStackRedzone * (g.size / kGlobalAndStackRedzone); CHECK(g_aligned_down_size == g_aligned_size - kGlobalAndStackRedzone); PoisonShadowPartialRightRedzone(g.beg + g_aligned_down_size, @@ -55,47 +55,47 @@ void PoisonRedZones(const Global &g) { } } -static size_t GetAlignedSize(size_t size) { +static uptr GetAlignedSize(uptr size) { return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone) * kGlobalAndStackRedzone; } // Check if the global is a zero-terminated ASCII string. If so, print it. void PrintIfASCII(const Global &g) { - for (size_t p = g.beg; p < g.beg + g.size - 1; p++) { + for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { if (!isascii(*(char*)p)) return; } if (*(char*)(g.beg + g.size - 1) != 0) return; - Printf(" '%s' is ascii string '%s'\n", g.name, g.beg); + AsanPrintf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg); } -bool DescribeAddrIfMyRedZone(const Global &g, uintptr_t addr) { +bool DescribeAddrIfMyRedZone(const Global &g, uptr addr) { if (addr < g.beg - kGlobalAndStackRedzone) return false; if (addr >= g.beg + g.size_with_redzone) return false; - Printf("%p is located ", addr); + AsanPrintf("%p is located ", (void*)addr); if (addr < g.beg) { - Printf("%d bytes to the left", g.beg - addr); + AsanPrintf("%zd bytes to the left", g.beg - addr); } else if (addr >= g.beg + g.size) { - Printf("%d bytes to the right", addr - (g.beg + g.size)); + AsanPrintf("%zd bytes to the right", addr - (g.beg + g.size)); } else { - Printf("%d bytes inside", addr - g.beg); // Can it happen? + AsanPrintf("%zd bytes inside", addr - g.beg); // Can it happen? } - Printf(" of global variable '%s' (0x%lx) of size %ld\n", - g.name, g.beg, g.size); + AsanPrintf(" of global variable '%s' (0x%zx) of size %zu\n", + g.name, g.beg, g.size); PrintIfASCII(g); return true; } -bool DescribeAddrIfGlobal(uintptr_t addr) { - if (!FLAG_report_globals) return false; +bool DescribeAddrIfGlobal(uptr addr) { + if (!flags()->report_globals) return false; ScopedLock lock(&mu_for_globals); bool res = false; for (ListOfGlobals *l = list_of_globals; l; l = l->next) { const Global &g = *l->g; - if (FLAG_report_globals >= 2) - Printf("Search Global: beg=%p size=%ld name=%s\n", - g.beg, g.size, g.name); + if (flags()->report_globals >= 2) + AsanPrintf("Search Global: beg=%p size=%zu name=%s\n", + (void*)g.beg, g.size, (char*)g.name); res |= DescribeAddrIfMyRedZone(g, addr); } return res; @@ -106,7 +106,7 @@ bool DescribeAddrIfGlobal(uintptr_t addr) { // so we store the globals in a map. static void RegisterGlobal(const Global *g) { CHECK(asan_inited); - CHECK(FLAG_report_globals); + CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); @@ -116,14 +116,14 @@ static void RegisterGlobal(const Global *g) { l->g = g; l->next = list_of_globals; list_of_globals = l; - if (FLAG_report_globals >= 2) - Report("Added Global: beg=%p size=%ld name=%s\n", - g->beg, g->size, g->name); + if (flags()->report_globals >= 2) + Report("Added Global: beg=%p size=%zu name=%s\n", + (void*)g->beg, g->size, g->name); } static void UnregisterGlobal(const Global *g) { CHECK(asan_inited); - CHECK(FLAG_report_globals); + CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); @@ -139,9 +139,9 @@ static void UnregisterGlobal(const Global *g) { using namespace __asan; // NOLINT // Register one global with a default redzone. -void __asan_register_global(uintptr_t addr, size_t size, +void __asan_register_global(uptr addr, uptr size, const char *name) { - if (!FLAG_report_globals) return; + if (!flags()->report_globals) return; ScopedLock lock(&mu_for_globals); Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global)); g->beg = addr; @@ -152,20 +152,20 @@ void __asan_register_global(uintptr_t addr, size_t size, } // Register an array of globals. -void __asan_register_globals(__asan_global *globals, size_t n) { - if (!FLAG_report_globals) return; +void __asan_register_globals(__asan_global *globals, uptr n) { + if (!flags()->report_globals) return; ScopedLock lock(&mu_for_globals); - for (size_t i = 0; i < n; i++) { + for (uptr i = 0; i < n; i++) { RegisterGlobal(&globals[i]); } } // Unregister an array of globals. // We must do it when a shared objects gets dlclosed. -void __asan_unregister_globals(__asan_global *globals, size_t n) { - if (!FLAG_report_globals) return; +void __asan_unregister_globals(__asan_global *globals, uptr n) { + if (!flags()->report_globals) return; ScopedLock lock(&mu_for_globals); - for (size_t i = 0; i < n; i++) { + for (uptr i = 0; i < n; i++) { UnregisterGlobal(&globals[i]); } } diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc index 53ef91ae857b..2ce5826e1495 100644 --- a/lib/asan/asan_interceptors.cc +++ b/lib/asan/asan_interceptors.cc @@ -1,4 +1,4 @@ -//===-- asan_interceptors.cc ------------------------------------*- C++ -*-===// +//===-- asan_interceptors.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -9,7 +9,7 @@ // // This file is a part of AddressSanitizer, an address sanity checker. // -// Intercept various libc functions to catch buggy memory accesses there. +// Intercept various libc functions. //===----------------------------------------------------------------------===// #include "asan_interceptors.h" @@ -19,40 +19,107 @@ #include "asan_mapping.h" #include "asan_stack.h" #include "asan_stats.h" +#include "asan_thread_registry.h" +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_libc.h" -#include -#include -#include -#include +// Use macro to describe if specific function should be +// intercepted on a given platform. +#if !defined(_WIN32) +# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1 +#else +# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0 +#endif + +#if !defined(__APPLE__) +# define ASAN_INTERCEPT_STRNLEN 1 +#else +# define ASAN_INTERCEPT_STRNLEN 0 +#endif + +#if defined(ANDROID) || defined(_WIN32) +# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0 +#else +# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1 +#endif + +// 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; +int sigaction(int sig, const struct sigaction *act, + struct sigaction *oldact); +void *signal(int signum, void *handler); +# endif + +// setjmp.h +void longjmp(void* env, int value); +# if !defined(_WIN32) +void _longjmp(void *env, int value); +# endif + +// string.h / strings.h +int memcmp(const void *a1, const void *a2, uptr size); +void* memmove(void *to, const void *from, uptr size); +void* memcpy(void *to, const void *from, uptr size); +void* memset(void *block, int c, uptr size); +char* strchr(const char *str, int c); +# if defined(__APPLE__) +char* index(const char *string, int c); +# endif +char* strcat(char *to, const char* from); // NOLINT +char *strncat(char *to, const char* from, uptr size); +char* strcpy(char *to, const char* from); // NOLINT +char* strncpy(char *to, const char* from, uptr size); +int strcmp(const char *s1, const char* s2); +int strncmp(const char *s1, const char* s2, uptr size); +# if !defined(_WIN32) +int strcasecmp(const char *s1, const char *s2); +int strncasecmp(const char *s1, const char *s2, uptr n); +char* strdup(const char *s); +# endif +uptr strlen(const char *s); +# if ASAN_INTERCEPT_STRNLEN +uptr strnlen(const char *s, uptr maxlen); +# endif + +// stdlib.h +int atoi(const char *nptr); +long atol(const char *nptr); // NOLINT +long strtol(const char *nptr, char **endptr, int base); // NOLINT +# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL +long long atoll(const char *nptr); // NOLINT +long long strtoll(const char *nptr, char **endptr, int base); // NOLINT +# 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 !defined(_WIN32) +int pthread_create(void *thread, void *attr, void *(*start_routine)(void*), + void *arg); +# endif +} // extern "C" +#endif namespace __asan { -index_f real_index; -memcmp_f real_memcmp; -memcpy_f real_memcpy; -memmove_f real_memmove; -memset_f real_memset; -strcasecmp_f real_strcasecmp; -strcat_f real_strcat; -strchr_f real_strchr; -strcmp_f real_strcmp; -strcpy_f real_strcpy; -strdup_f real_strdup; -strlen_f real_strlen; -strncasecmp_f real_strncasecmp; -strncmp_f real_strncmp; -strncpy_f real_strncpy; -strnlen_f real_strnlen; - // Instruments read/write access to a single byte in memory. // On error calls __asan_report_error, which aborts the program. -__attribute__((noinline)) -static void AccessAddress(uintptr_t address, bool isWrite) { - if (__asan_address_is_poisoned((void*)address)) { - GET_BP_PC_SP; - __asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1); - } -} +#define ACCESS_ADDRESS(address, isWrite) do { \ + if (!AddrIsInMem(address) || AddressIsPoisoned(address)) { \ + GET_CURRENT_PC_BP_SP; \ + __asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1); \ + } \ +} while (0) // We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE, // and ASAN_WRITE_RANGE as macro instead of function so @@ -64,9 +131,9 @@ static void AccessAddress(uintptr_t address, bool isWrite) { // checking the first and the last byte of a range. #define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ if (size > 0) { \ - uintptr_t ptr = (uintptr_t)(offset); \ - AccessAddress(ptr, isWrite); \ - AccessAddress(ptr + (size) - 1, isWrite); \ + uptr ptr = (uptr)(offset); \ + ACCESS_ADDRESS(ptr, isWrite); \ + ACCESS_ADDRESS(ptr + (size) - 1, isWrite); \ } \ } while (0) @@ -81,17 +148,17 @@ static void AccessAddress(uintptr_t address, bool isWrite) { // Behavior of functions like "memcpy" or "strcpy" is undefined // if memory intervals overlap. We report error in this case. // Macro is used to avoid creation of new frames. -static inline bool RangesOverlap(const char *offset1, size_t length1, - const char *offset2, size_t length2) { +static inline bool RangesOverlap(const char *offset1, uptr length1, + const char *offset2, uptr length2) { return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1)); } #define CHECK_RANGES_OVERLAP(name, _offset1, length1, _offset2, length2) do { \ const char *offset1 = (const char*)_offset1; \ const char *offset2 = (const char*)_offset2; \ if (RangesOverlap(offset1, length1, offset2, length2)) { \ - Report("ERROR: AddressSanitizer %s-param-overlap: " \ - "memory ranges [%p,%p) and [%p, %p) overlap\n", \ - name, offset1, offset1 + length1, offset2, offset2 + length2); \ + AsanReport("ERROR: AddressSanitizer %s-param-overlap: " \ + "memory ranges [%p,%p) and [%p, %p) overlap\n", \ + name, offset1, offset1 + length1, offset2, offset2 + length2); \ PRINT_CURRENT_STACK(); \ ShowStatsAndAbort(); \ } \ @@ -104,64 +171,13 @@ static inline bool RangesOverlap(const char *offset1, size_t length1, } \ } while (0) -size_t internal_strlen(const char *s) { - size_t i = 0; - while (s[i]) i++; - return i; -} - -size_t internal_strnlen(const char *s, size_t maxlen) { - if (real_strnlen != NULL) { - return real_strnlen(s, maxlen); +static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { +#if ASAN_INTERCEPT_STRNLEN + if (REAL(strnlen) != 0) { + return REAL(strnlen)(s, maxlen); } - size_t i = 0; - while (i < maxlen && s[i]) i++; - return i; -} - -void* internal_memchr(const void* s, int c, size_t n) { - const char* t = (char*)s; - for (size_t i = 0; i < n; ++i, ++t) - if (*t == c) - return (void*)t; - return NULL; -} - -int internal_memcmp(const void* s1, const void* s2, size_t n) { - const char* t1 = (char*)s1; - const char* t2 = (char*)s2; - for (size_t i = 0; i < n; ++i, ++t1, ++t2) - if (*t1 != *t2) - return *t1 < *t2 ? -1 : 1; - return 0; -} - -void InitializeAsanInterceptors() { -#ifndef __APPLE__ - INTERCEPT_FUNCTION(index); -#else - OVERRIDE_FUNCTION(index, WRAP(strchr)); #endif - INTERCEPT_FUNCTION(memcmp); - INTERCEPT_FUNCTION(memcpy); - INTERCEPT_FUNCTION(memmove); - INTERCEPT_FUNCTION(memset); - INTERCEPT_FUNCTION(strcasecmp); - INTERCEPT_FUNCTION(strcat); // NOLINT - INTERCEPT_FUNCTION(strchr); - INTERCEPT_FUNCTION(strcmp); - INTERCEPT_FUNCTION(strcpy); // NOLINT - INTERCEPT_FUNCTION(strdup); - INTERCEPT_FUNCTION(strlen); - INTERCEPT_FUNCTION(strncasecmp); - INTERCEPT_FUNCTION(strncmp); - INTERCEPT_FUNCTION(strncpy); -#ifndef __APPLE__ - INTERCEPT_FUNCTION(strnlen); -#endif - if (FLAG_v > 0) { - Printf("AddressSanitizer: libc interceptors initialized\n"); - } + return internal_strnlen(s, maxlen); } } // namespace __asan @@ -169,22 +185,125 @@ void InitializeAsanInterceptors() { // ---------------------- Wrappers ---------------- {{{1 using namespace __asan; // NOLINT +static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { + AsanThread *t = (AsanThread*)arg; + asanThreadRegistry().SetCurrent(t); + return t->ThreadStart(); +} + +#ifndef _WIN32 +INTERCEPTOR(int, pthread_create, void *thread, + void *attr, void *(*start_routine)(void*), void *arg) { + GET_STACK_TRACE_HERE(kStackTraceMax); + u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); + asanThreadRegistry().RegisterThread(t); + return REAL(pthread_create)(thread, attr, asan_thread_start, t); +} +#endif // !_WIN32 + +#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION +INTERCEPTOR(void*, signal, int signum, void *handler) { + if (!AsanInterceptsSignal(signum)) { + return REAL(signal)(signum, handler); + } + return 0; +} + +INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact) { + if (!AsanInterceptsSignal(signum)) { + return REAL(sigaction)(signum, act, oldact); + } + return 0; +} +#elif ASAN_POSIX +// We need to have defined REAL(sigaction) on posix systems. +DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact); +#endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION + +INTERCEPTOR(void, longjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(longjmp)(env, val); +} + +#if !defined(_WIN32) +INTERCEPTOR(void, _longjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(_longjmp)(env, val); +} + +INTERCEPTOR(void, siglongjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(siglongjmp)(env, val); +} +#endif + +#if ASAN_HAS_EXCEPTIONS == 1 +#ifdef __APPLE__ +extern "C" void __cxa_throw(void *a, void *b, void *c); +#endif // __APPLE__ + +INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { + CHECK(REAL(__cxa_throw)); + __asan_handle_no_return(); + REAL(__cxa_throw)(a, b, c); +} +#endif + +// intercept mlock and friends. +// Since asan maps 16T of RAM, mlock is completely unfriendly to asan. +// All functions return 0 (success). +static void MlockIsUnsupported() { + static bool printed = 0; + if (printed) return; + printed = true; + Printf("INFO: AddressSanitizer ignores mlock/mlockall/munlock/munlockall\n"); +} + +extern "C" { +INTERCEPTOR_ATTRIBUTE +int mlock(const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR_ATTRIBUTE +int munlock(const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR_ATTRIBUTE +int mlockall(int flags) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR_ATTRIBUTE +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); + int c1_low = ToLower(c1); + int c2_low = ToLower(c2); return c1_low - c2_low; } -int WRAP(memcmp)(const void *a1, const void *a2, size_t size) { +INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr 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; - size_t i; + uptr i; for (i = 0; i < size; i++) { c1 = s1[i]; c2 = s2[i]; @@ -195,65 +314,70 @@ int WRAP(memcmp)(const void *a1, const void *a2, size_t size) { return CharCmp(c1, c2); } -void *WRAP(memcpy)(void *to, const void *from, size_t size) { +INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { // memcpy is called during __asan_init() from the internals // of printf(...). if (asan_init_is_running) { - return real_memcpy(to, from, size); + return REAL(memcpy)(to, from, size); } ENSURE_ASAN_INITED(); - if (FLAG_replace_intrin) { - CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); + if (flags()->replace_intrin) { + if (to != from) { + // We do not treat memcpy with to==from as a bug. + // See http://llvm.org/bugs/show_bug.cgi?id=11763. + CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); + } ASAN_WRITE_RANGE(from, size); ASAN_READ_RANGE(to, size); } - return real_memcpy(to, from, size); + return REAL(memcpy)(to, from, size); } -void *WRAP(memmove)(void *to, const void *from, size_t size) { - ENSURE_ASAN_INITED(); - if (FLAG_replace_intrin) { - ASAN_WRITE_RANGE(from, size); - ASAN_READ_RANGE(to, size); - } - return real_memmove(to, from, size); -} - -void *WRAP(memset)(void *block, int c, size_t size) { - // memset is called inside INTERCEPT_FUNCTION on Mac. +INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { if (asan_init_is_running) { - return real_memset(block, c, size); + return REAL(memmove)(to, from, size); } ENSURE_ASAN_INITED(); - if (FLAG_replace_intrin) { + if (flags()->replace_intrin) { + ASAN_WRITE_RANGE(from, size); + ASAN_READ_RANGE(to, size); + } + return REAL(memmove)(to, from, size); +} + +INTERCEPTOR(void*, memset, void *block, int c, uptr size) { + // memset is called inside Printf. + if (asan_init_is_running) { + return REAL(memset)(block, c, size); + } + ENSURE_ASAN_INITED(); + if (flags()->replace_intrin) { ASAN_WRITE_RANGE(block, size); } - return real_memset(block, c, size); + return REAL(memset)(block, c, size); } -// Note that on Linux index and strchr are definined differently depending on -// the compiler (gcc vs clang). -// see __CORRECT_ISO_CPP_STRING_H_PROTO in /usr/include/string.h - -#ifndef __APPLE__ -char *WRAP(index)(const char *str, int c) - __attribute__((alias(WRAPPER_NAME(strchr)))); -#endif - -char *WRAP(strchr)(const char *str, int c) { +INTERCEPTOR(char*, strchr, const char *str, int c) { ENSURE_ASAN_INITED(); - char *result = real_strchr(str, c); - if (FLAG_replace_str) { - size_t bytes_read = (result ? result - str : real_strlen(str)) + 1; + char *result = REAL(strchr)(str, c); + if (flags()->replace_str) { + uptr bytes_read = (result ? result - str : REAL(strlen)(str)) + 1; ASAN_READ_RANGE(str, bytes_read); } return result; } -int WRAP(strcasecmp)(const char *s1, const char *s2) { +#ifdef __linux__ +INTERCEPTOR(char*, index, const char *string, int c) + ALIAS(WRAPPER_NAME(strchr)); +#else +DEFINE_REAL(char*, index, const char *string, int c) +#endif + +INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { ENSURE_ASAN_INITED(); unsigned char c1, c2; - size_t i; + uptr i; for (i = 0; ; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; @@ -264,29 +388,44 @@ int WRAP(strcasecmp)(const char *s1, const char *s2) { return CharCaseCmp(c1, c2); } -char *WRAP(strcat)(char *to, const char *from) { // NOLINT +INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT ENSURE_ASAN_INITED(); - if (FLAG_replace_str) { - size_t from_length = real_strlen(from); + if (flags()->replace_str) { + uptr from_length = REAL(strlen)(from); ASAN_READ_RANGE(from, from_length + 1); if (from_length > 0) { - size_t to_length = real_strlen(to); + uptr to_length = REAL(strlen)(to); ASAN_READ_RANGE(to, to_length); ASAN_WRITE_RANGE(to + to_length, from_length + 1); CHECK_RANGES_OVERLAP("strcat", to, to_length + 1, from, from_length + 1); } } - return real_strcat(to, from); + return REAL(strcat)(to, from); // NOLINT } -int WRAP(strcmp)(const char *s1, const char *s2) { - // strcmp is called from malloc_default_purgeable_zone() - // in __asan::ReplaceSystemAlloc() on Mac. - if (asan_init_is_running) { - return real_strcmp(s1, s2); +INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { + ENSURE_ASAN_INITED(); + if (flags()->replace_str && size > 0) { + uptr from_length = MaybeRealStrnlen(from, size); + ASAN_READ_RANGE(from, Min(size, from_length + 1)); + uptr to_length = REAL(strlen)(to); + ASAN_READ_RANGE(to, to_length); + ASAN_WRITE_RANGE(to + to_length, from_length + 1); + if (from_length > 0) { + CHECK_RANGES_OVERLAP("strncat", to, to_length + 1, + from, Min(size, from_length + 1)); + } } + return REAL(strncat)(to, from, size); +} + +INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { + if (!asan_inited) { + return internal_strcmp(s1, s2); + } + ENSURE_ASAN_INITED(); unsigned char c1, c2; - size_t i; + uptr i; for (i = 0; ; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; @@ -297,67 +436,68 @@ int WRAP(strcmp)(const char *s1, const char *s2) { return CharCmp(c1, c2); } -char *WRAP(strcpy)(char *to, const char *from) { // NOLINT +INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT // strcpy is called from malloc_default_purgeable_zone() // in __asan::ReplaceSystemAlloc() on Mac. if (asan_init_is_running) { - return real_strcpy(to, from); + return REAL(strcpy)(to, from); // NOLINT } ENSURE_ASAN_INITED(); - if (FLAG_replace_str) { - size_t from_size = real_strlen(from) + 1; + if (flags()->replace_str) { + uptr from_size = REAL(strlen)(from) + 1; CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size); ASAN_READ_RANGE(from, from_size); ASAN_WRITE_RANGE(to, from_size); } - return real_strcpy(to, from); + return REAL(strcpy)(to, from); // NOLINT } -char *WRAP(strdup)(const char *s) { +INTERCEPTOR(char*, strdup, const char *s) { ENSURE_ASAN_INITED(); - if (FLAG_replace_str) { - size_t length = real_strlen(s); + if (flags()->replace_str) { + uptr length = REAL(strlen)(s); ASAN_READ_RANGE(s, length + 1); } - return real_strdup(s); + return REAL(strdup)(s); } -size_t WRAP(strlen)(const char *s) { +INTERCEPTOR(uptr, strlen, const char *s) { // strlen is called from malloc_default_purgeable_zone() // in __asan::ReplaceSystemAlloc() on Mac. if (asan_init_is_running) { - return real_strlen(s); + return REAL(strlen)(s); } ENSURE_ASAN_INITED(); - size_t length = real_strlen(s); - if (FLAG_replace_str) { + uptr length = REAL(strlen)(s); + if (flags()->replace_str) { ASAN_READ_RANGE(s, length + 1); } return length; } -int WRAP(strncasecmp)(const char *s1, const char *s2, size_t size) { +INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr n) { ENSURE_ASAN_INITED(); unsigned char c1 = 0, c2 = 0; - size_t i; - for (i = 0; i < size; i++) { + 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, size)); - ASAN_READ_RANGE(s2, Min(i + 1, size)); + ASAN_READ_RANGE(s1, Min(i + 1, n)); + ASAN_READ_RANGE(s2, Min(i + 1, n)); return CharCaseCmp(c1, c2); } -int WRAP(strncmp)(const char *s1, const char *s2, size_t size) { +INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr 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); + return REAL(strncmp)(s1, s2, size); } + ENSURE_ASAN_INITED(); unsigned char c1 = 0, c2 = 0; - size_t i; + uptr i; for (i = 0; i < size; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; @@ -368,24 +508,234 @@ int WRAP(strncmp)(const char *s1, const char *s2, size_t size) { return CharCmp(c1, c2); } -char *WRAP(strncpy)(char *to, const char *from, size_t size) { +INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { ENSURE_ASAN_INITED(); - if (FLAG_replace_str) { - size_t from_size = Min(size, internal_strnlen(from, size) + 1); + if (flags()->replace_str) { + uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size); ASAN_READ_RANGE(from, from_size); ASAN_WRITE_RANGE(to, size); } - return real_strncpy(to, from, size); + return REAL(strncpy)(to, from, size); } -#ifndef __APPLE__ -size_t WRAP(strnlen)(const char *s, size_t maxlen) { +#if ASAN_INTERCEPT_STRNLEN +INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) { ENSURE_ASAN_INITED(); - size_t length = real_strnlen(s, maxlen); - if (FLAG_replace_str) { + uptr length = REAL(strnlen)(s, maxlen); + if (flags()->replace_str) { ASAN_READ_RANGE(s, Min(length + 1, maxlen)); } return length; } +#endif // ASAN_INTERCEPT_STRNLEN + +static inline bool IsValidStrtolBase(int base) { + return (base == 0) || (2 <= base && base <= 36); +} + +static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { + CHECK(endptr != 0); + if (nptr == *endptr) { + // No digits were found at strtol call, we need to find out the last + // symbol accessed by strtoll on our own. + // We get this symbol by skipping leading blanks and optional +/- sign. + while (IsSpace(*nptr)) nptr++; + if (*nptr == '+' || *nptr == '-') nptr++; + *endptr = (char*)nptr; + } + CHECK(*endptr >= nptr); +} + +INTERCEPTOR(long, strtol, const char *nptr, // NOLINT + char **endptr, int base) { + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(strtol)(nptr, endptr, base); + } + char *real_endptr; + long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT + if (endptr != 0) { + *endptr = real_endptr; + } + if (IsValidStrtolBase(base)) { + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + } + return result; +} + +INTERCEPTOR(int, atoi, const char *nptr) { + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(atoi)(nptr); + } + char *real_endptr; + // "man atoi" tells that behavior of atoi(nptr) is the same as + // strtol(nptr, 0, 10), i.e. it sets errno to ERANGE if the + // parsed integer can't be stored in *long* type (even if it's + // different from int). So, we just imitate this behavior. + int result = REAL(strtol)(nptr, &real_endptr, 10); + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + return result; +} + +INTERCEPTOR(long, atol, const char *nptr) { // NOLINT + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(atol)(nptr); + } + char *real_endptr; + long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + return result; +} + +#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL +INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT + char **endptr, int base) { + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(strtoll)(nptr, endptr, base); + } + char *real_endptr; + long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT + if (endptr != 0) { + *endptr = real_endptr; + } + // If base has unsupported value, strtoll can exit with EINVAL + // without reading any characters. So do additional checks only + // if base is valid. + if (IsValidStrtolBase(base)) { + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + } + return result; +} + +INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(atoll)(nptr); + } + char *real_endptr; + long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + return result; +} +#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL + +#define ASAN_INTERCEPT_FUNC(name) do { \ + if (!INTERCEPT_FUNCTION(name) && flags()->verbosity > 0) \ + Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ + } while (0) + +#if defined(_WIN32) +INTERCEPTOR_WINAPI(DWORD, CreateThread, + void* security, uptr stack_size, + DWORD (__stdcall *start_routine)(void*), void* arg, + DWORD flags, void* tid) { + GET_STACK_TRACE_HERE(kStackTraceMax); + u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); + asanThreadRegistry().RegisterThread(t); + return REAL(CreateThread)(security, stack_size, + asan_thread_start, t, flags, tid); +} + +namespace __asan { +void InitializeWindowsInterceptors() { + ASAN_INTERCEPT_FUNC(CreateThread); +} + +} // namespace __asan #endif + +// ---------------------- InitializeAsanInterceptors ---------------- {{{1 +namespace __asan { +void InitializeAsanInterceptors() { + static bool was_called_once; + CHECK(was_called_once == false); + was_called_once = true; + // Intercept mem* functions. + ASAN_INTERCEPT_FUNC(memcmp); + ASAN_INTERCEPT_FUNC(memmove); + ASAN_INTERCEPT_FUNC(memset); + if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { + ASAN_INTERCEPT_FUNC(memcpy); + } else { + REAL(memcpy) = REAL(memmove); + } + + // 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(strncat); + ASAN_INTERCEPT_FUNC(strncmp); + ASAN_INTERCEPT_FUNC(strncpy); +#if !defined(_WIN32) + ASAN_INTERCEPT_FUNC(strcasecmp); + ASAN_INTERCEPT_FUNC(strdup); + ASAN_INTERCEPT_FUNC(strncasecmp); +# ifndef __APPLE__ + ASAN_INTERCEPT_FUNC(index); +# else + CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr))); +# endif +#endif +#if ASAN_INTERCEPT_STRNLEN + ASAN_INTERCEPT_FUNC(strnlen); +#endif + + ASAN_INTERCEPT_FUNC(atoi); + ASAN_INTERCEPT_FUNC(atol); + ASAN_INTERCEPT_FUNC(strtol); +#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL + ASAN_INTERCEPT_FUNC(atoll); + ASAN_INTERCEPT_FUNC(strtoll); +#endif + + // Intecept signal- and jump-related functions. + ASAN_INTERCEPT_FUNC(longjmp); +#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION + ASAN_INTERCEPT_FUNC(sigaction); + ASAN_INTERCEPT_FUNC(signal); +#endif + +#if !defined(_WIN32) + ASAN_INTERCEPT_FUNC(_longjmp); + INTERCEPT_FUNCTION(__cxa_throw); +# if !defined(__APPLE__) + // On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it + // there. + ASAN_INTERCEPT_FUNC(siglongjmp); +# endif +#endif + + // Intercept threading-related functions +#if !defined(_WIN32) + ASAN_INTERCEPT_FUNC(pthread_create); +#endif + + // Some Windows-specific interceptors. +#if defined(_WIN32) + InitializeWindowsInterceptors(); +#endif + + // Some Mac-specific interceptors. +#if defined(__APPLE__) + InitializeMacInterceptors(); +#endif + + if (flags()->verbosity > 0) { + Report("AddressSanitizer: libc interceptors initialized\n"); + } +} + +} // namespace __asan diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h index 07b94208197d..32816920f7a3 100644 --- a/lib/asan/asan_interceptors.h +++ b/lib/asan/asan_interceptors.h @@ -15,119 +15,25 @@ #define ASAN_INTERCEPTORS_H #include "asan_internal.h" +#include "interception/interception.h" -// To replace weak system functions on Linux we just need to declare functions -// with same names in our library and then obtain the real function pointers -// using dlsym(). 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 -// libraries in Chromium were noticed when doing so. -// Instead we use mach_override, a handy framework for patching functions at -// runtime. To avoid possible name clashes, our replacement functions have -// the "wrap_" prefix on Mac. -// -// After interception, the calls to system functions will be substituted by -// calls to our interceptors. We store pointers to system function f() -// in __asan::real_f(). -// -// TODO(glider): mach_override_ptr() tends to spend too much time -// in allocateBranchIsland(). This should be ok for real-word -// application, but slows down our tests which fork too many children. -#ifdef __APPLE__ -#include "mach_override/mach_override.h" -#define WRAP(x) wrap_##x -#define WRAPPER_NAME(x) "wrap_"#x - -#define OVERRIDE_FUNCTION(oldfunc, newfunc) \ - CHECK(0 == __asan_mach_override_ptr((void*)(oldfunc), \ - (void*)(newfunc), \ - (void**)&real_##oldfunc)); \ - CHECK(real_##oldfunc != NULL); - -#define OVERRIDE_FUNCTION_IF_EXISTS(oldfunc, newfunc) \ - do { __asan_mach_override_ptr((void*)(oldfunc), \ - (void*)(newfunc), \ - (void**)&real_##oldfunc); } while (0) - -#define INTERCEPT_FUNCTION(func) \ - OVERRIDE_FUNCTION(func, WRAP(func)) - -#define INTERCEPT_FUNCTION_IF_EXISTS(func) \ - OVERRIDE_FUNCTION_IF_EXISTS(func, WRAP(func)) - -#else // __linux__ -#define WRAP(x) x -#define WRAPPER_NAME(x) #x - -#define INTERCEPT_FUNCTION(func) \ - CHECK((real_##func = (func##_f)dlsym(RTLD_NEXT, #func))); - -#define INTERCEPT_FUNCTION_IF_EXISTS(func) \ - do { real_##func = (func##_f)dlsym(RTLD_NEXT, #func); } while (0) -#endif - -#ifdef __APPLE__ -int WRAP(memcmp)(const void *a1, const void *a2, size_t size); -void *WRAP(memcpy)(void *to, const void *from, size_t size); -void *WRAP(memmove)(void *to, const void *from, size_t size); -void *WRAP(memset)(void *block, int c, size_t size); -int WRAP(strcasecmp)(const char *s1, const char *s2); -char *WRAP(strcat)(char *to, const char *from); // NOLINT -char *WRAP(strchr)(const char *string, int c); -int WRAP(strcmp)(const char *s1, const char *s2); -char *WRAP(strcpy)(char *to, const char *from); // NOLINT -char *WRAP(strdup)(const char *s); -size_t WRAP(strlen)(const char *s); -int WRAP(strncasecmp)(const char *s1, const char *s2, size_t n); -int WRAP(strncmp)(const char *s1, const char *s2, size_t size); -char *WRAP(strncpy)(char *to, const char *from, size_t size); -#endif +DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size) +DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size) +DECLARE_REAL(void*, memset, void *block, int c, uptr size) +DECLARE_REAL(char*, strchr, const char *str, int c) +DECLARE_REAL(uptr, strlen, const char *s) +DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size) +DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen) +struct sigaction; +DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact) namespace __asan { -typedef void* (*index_f)(const char *string, int c); -typedef int (*memcmp_f)(const void *a1, const void *a2, size_t size); -typedef void* (*memcpy_f)(void *to, const void *from, size_t size); -typedef void* (*memmove_f)(void *to, const void *from, size_t size); -typedef void* (*memset_f)(void *block, int c, size_t size); -typedef int (*strcasecmp_f)(const char *s1, const char *s2); -typedef char* (*strcat_f)(char *to, const char *from); -typedef char* (*strchr_f)(const char *str, int c); -typedef int (*strcmp_f)(const char *s1, const char *s2); -typedef char* (*strcpy_f)(char *to, const char *from); -typedef char* (*strdup_f)(const char *s); -typedef size_t (*strlen_f)(const char *s); -typedef int (*strncasecmp_f)(const char *s1, const char *s2, size_t n); -typedef int (*strncmp_f)(const char *s1, const char *s2, size_t size); -typedef char* (*strncpy_f)(char *to, const char *from, size_t size); -typedef size_t (*strnlen_f)(const char *s, size_t maxlen); - -// __asan::real_X() holds pointer to library implementation of X(). -extern index_f real_index; -extern memcmp_f real_memcmp; -extern memcpy_f real_memcpy; -extern memmove_f real_memmove; -extern memset_f real_memset; -extern strcasecmp_f real_strcasecmp; -extern strcat_f real_strcat; -extern strchr_f real_strchr; -extern strcmp_f real_strcmp; -extern strcpy_f real_strcpy; -extern strdup_f real_strdup; -extern strlen_f real_strlen; -extern strncasecmp_f real_strncasecmp; -extern strncmp_f real_strncmp; -extern strncpy_f real_strncpy; -extern strnlen_f real_strnlen; - -// __asan::internal_X() is the implementation of X() for use in RTL. -size_t internal_strlen(const char *s); -size_t internal_strnlen(const char *s, size_t maxlen); -void* internal_memchr(const void* s, int c, size_t n); -int internal_memcmp(const void* s1, const void* s2, size_t n); - -// Initializes pointers to str*/mem* functions. void InitializeAsanInterceptors(); +#if defined(__APPLE__) +void InitializeMacInterceptors(); +#endif // __APPLE__ } // namespace __asan diff --git a/lib/asan/asan_interface.h b/lib/asan/asan_interface.h index 7506586fefeb..c625a6217c0c 100644 --- a/lib/asan/asan_interface.h +++ b/lib/asan/asan_interface.h @@ -15,46 +15,46 @@ #ifndef ASAN_INTERFACE_H #define ASAN_INTERFACE_H -#include // for __WORDSIZE -#include // for size_t - +#include "sanitizer_common/sanitizer_interface_defs.h" +// ----------- ATTENTION ------------- // This header should NOT include any other headers from ASan runtime. // All functions in this header are extern "C" and start with __asan_. +using __sanitizer::uptr; + extern "C" { // This function should be called at the very beginning of the process, // before any instrumented code is executed and before any call to malloc. - void __asan_init() - __attribute__((visibility("default"))); + void __asan_init() SANITIZER_INTERFACE_ATTRIBUTE; // This function should be called by the instrumented code. // 'addr' is the address of a global variable called 'name' of 'size' bytes. - void __asan_register_global(uintptr_t addr, size_t size, const char *name) - __attribute__((visibility("default"))); + void __asan_register_global(uptr addr, uptr size, const char *name) + SANITIZER_INTERFACE_ATTRIBUTE; // This structure describes an instrumented global variable. struct __asan_global { - size_t beg; // The address of the global. - size_t size; // The original size of the global. - size_t size_with_redzone; // The size with the redzone. + uptr beg; // The address of the global. + 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. }; // 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, size_t n) - __attribute__((visibility("default"))); - void __asan_unregister_globals(__asan_global *globals, size_t n) - __attribute__((visibility("default"))); + void __asan_register_globals(__asan_global *globals, uptr n) + SANITIZER_INTERFACE_ATTRIBUTE; + void __asan_unregister_globals(__asan_global *globals, uptr n) + 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. - size_t __asan_stack_malloc(size_t size, size_t real_stack) - __attribute__((visibility("default"))); - void __asan_stack_free(size_t ptr, size_t size, size_t real_stack) - __attribute__((visibility("default"))); + 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; // Marks memory region [addr, addr+size) as unaddressable. // This memory must be previously allocated by the user program. Accessing @@ -64,7 +64,8 @@ extern "C" { // to ASan alignment restrictions. // Method is NOT thread-safe in the sense that no two threads can // (un)poison memory in the same memory region simultaneously. - void __asan_poison_memory_region(void const volatile *addr, size_t size); + void __asan_poison_memory_region(void const volatile *addr, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; // Marks memory region [addr, addr+size) as addressable. // This memory must be previously allocated by the user program. Accessing // addresses in this region is allowed until this region is poisoned again. @@ -72,10 +73,15 @@ extern "C" { // ASan alignment restrictions. // Method is NOT thread-safe in the sense that no two threads can // (un)poison memory in the same memory region simultaneously. - void __asan_unpoison_memory_region(void const volatile *addr, size_t size); + void __asan_unpoison_memory_region(void const volatile *addr, uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + + // 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; // User code should use macro instead of functions. -#if defined(__has_feature) && __has_feature(address_sanitizer) +#if __has_feature(address_sanitizer) #define ASAN_POISON_MEMORY_REGION(addr, size) \ __asan_poison_memory_region((addr), (size)) #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ @@ -89,47 +95,65 @@ extern "C" { // Returns true iff addr is poisoned (i.e. 1-byte read/write access to this // address will result in error report from AddressSanitizer). - bool __asan_address_is_poisoned(void const volatile *addr); + bool __asan_address_is_poisoned(void const volatile *addr) + SANITIZER_INTERFACE_ATTRIBUTE; // This is an internal function that is called to report an error. // However it is still a part of the interface because users may want to // set a breakpoint on this function in a debugger. - void __asan_report_error(uintptr_t pc, uintptr_t bp, uintptr_t sp, - uintptr_t addr, bool is_write, size_t access_size) - __attribute__((visibility("default"))); + void __asan_report_error(uptr pc, uptr bp, uptr sp, + uptr addr, bool is_write, uptr access_size) + SANITIZER_INTERFACE_ATTRIBUTE; // Sets the exit code to use when reporting an error. // Returns the old value. - int __asan_set_error_exit_code(int exit_code); + int __asan_set_error_exit_code(int exit_code) + SANITIZER_INTERFACE_ATTRIBUTE; + + // Sets the callback to be called right before death on error. + // Passing 0 will unset the callback. + void __asan_set_death_callback(void (*callback)(void)) + SANITIZER_INTERFACE_ATTRIBUTE; + + void __asan_set_error_report_callback(void (*callback)(const char*)) + SANITIZER_INTERFACE_ATTRIBUTE; // Returns the estimated number of bytes that will be reserved by allocator // for request of "size" bytes. If ASan allocator can't allocate that much // memory, returns the maximal possible allocation size, otherwise returns // "size". - size_t __asan_get_estimated_allocated_size(size_t size); - // Returns true if p is NULL or if p was returned by the ASan allocator and + uptr __asan_get_estimated_allocated_size(uptr size) + SANITIZER_INTERFACE_ATTRIBUTE; + // Returns true if p was returned by the ASan allocator and // is not yet freed. - bool __asan_get_ownership(const void *p); + bool __asan_get_ownership(const void *p) + SANITIZER_INTERFACE_ATTRIBUTE; // Returns the number of bytes reserved for the pointer p. - // Requires (get_ownership(p) == true). - size_t __asan_get_allocated_size(const void *p); + // Requires (get_ownership(p) == true) or (p == 0). + uptr __asan_get_allocated_size(const void *p) + SANITIZER_INTERFACE_ATTRIBUTE; // Number of bytes, allocated and not yet freed by the application. - size_t __asan_get_current_allocated_bytes(); + uptr __asan_get_current_allocated_bytes() + SANITIZER_INTERFACE_ATTRIBUTE; // Number of bytes, mmaped by asan allocator to fulfill allocation requests. // Generally, for request of X bytes, allocator can reserve and add to free // lists a large number of chunks of size X to use them for future requests. // All these chunks count toward the heap size. Currently, allocator never // releases memory to OS (instead, it just puts freed chunks to free lists). - size_t __asan_get_heap_size(); + uptr __asan_get_heap_size() + SANITIZER_INTERFACE_ATTRIBUTE; // Number of bytes, mmaped by asan allocator, which can be used to fulfill // allocation requests. When a user program frees memory chunk, it can first // fall into quarantine and will count toward __asan_get_free_bytes() later. - size_t __asan_get_free_bytes(); + uptr __asan_get_free_bytes() + SANITIZER_INTERFACE_ATTRIBUTE; // Number of bytes in unmapped pages, that are released to OS. Currently, // always returns 0. - size_t __asan_get_unmapped_bytes(); + uptr __asan_get_unmapped_bytes() + SANITIZER_INTERFACE_ATTRIBUTE; // Prints accumulated stats to stderr. Used for debugging. - void __asan_print_accumulated_stats(); + void __asan_print_accumulated_stats() + SANITIZER_INTERFACE_ATTRIBUTE; } // namespace #endif // ASAN_INTERFACE_H diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h index d0790a7c8b7b..8c1f32028f2c 100644 --- a/lib/asan/asan_internal.h +++ b/lib/asan/asan_internal.h @@ -14,40 +14,49 @@ #ifndef ASAN_INTERNAL_H #define ASAN_INTERNAL_H -#if !defined(__linux__) && !defined(__APPLE__) +#include "asan_flags.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_libc.h" + +#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) # error "This operating system is not supported by AddressSanitizer" #endif -#include // for __WORDSIZE -#include // for size_t -#include // for _exit +#if defined(_WIN32) +extern "C" void* _ReturnAddress(void); +# pragma intrinsic(_ReturnAddress) +#endif // defined(_WIN32) -// If __WORDSIZE was undefined by the platform, define it in terms of the -// compiler built-in __LP64__. -#ifndef __WORDSIZE -#if __LP64__ -#define __WORDSIZE 64 +#define ASAN_DEFAULT_FAILURE_EXITCODE 1 + +#if defined(__linux__) +# define ASAN_LINUX 1 #else -#define __WORDSIZE 32 -#endif +# define ASAN_LINUX 0 #endif -#ifdef ANDROID -#include +#if defined(__APPLE__) +# define ASAN_MAC 1 +#else +# define ASAN_MAC 0 #endif -#if defined(__has_feature) && __has_feature(address_sanitizer) +#if defined(_WIN32) +# define ASAN_WINDOWS 1 +#else +# define ASAN_WINDOWS 0 +#endif + +#define ASAN_POSIX (ASAN_LINUX || ASAN_MAC) + +#if __has_feature(address_sanitizer) # error "The AddressSanitizer run-time should not be" " instrumented by AddressSanitizer" #endif // Build-time configuration options. -// If set, sysinfo/sysinfo.h will be used to iterate over /proc/maps. -#ifndef ASAN_USE_SYSINFO -# define ASAN_USE_SYSINFO 1 -#endif - // If set, asan will install its own SEGV signal handler. #ifndef ASAN_NEEDS_SEGV # define ASAN_NEEDS_SEGV 1 @@ -64,6 +73,12 @@ # define ASAN_FLEXIBLE_MAPPING_AND_OFFSET 0 #endif +// If set, values like allocator chunk size, as well as defaults for some flags +// will be changed towards less memory overhead. +#ifndef ASAN_LOW_MEMORY +# define ASAN_LOW_MEMORY 0 +#endif + // All internal functions in asan reside inside the __asan namespace // to avoid namespace collisions with the user programs. // Seperate namespace also makes it simpler to distinguish the asan run-time @@ -74,109 +89,81 @@ class AsanThread; struct AsanStackTrace; // asan_rtl.cc -void CheckFailed(const char *cond, const char *file, int line); -void ShowStatsAndAbort(); +void NORETURN ShowStatsAndAbort(); // asan_globals.cc -bool DescribeAddrIfGlobal(uintptr_t addr); +bool DescribeAddrIfGlobal(uptr addr); +void ReplaceOperatorsNewAndDelete(); // asan_malloc_linux.cc / asan_malloc_mac.cc void ReplaceSystemMalloc(); -void OutOfMemoryMessageAndDie(const char *mem_type, size_t size); - -// asan_linux.cc / asan_mac.cc +// asan_linux.cc / asan_mac.cc / asan_win.cc void *AsanDoesNotSupportStaticLinkage(); -int AsanOpenReadonly(const char* filename); -void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size); -void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size); -void *AsanMprotect(uintptr_t fixed_addr, size_t size); -void *AsanMmapSomewhereOrDie(size_t size, const char *where); -void AsanUnmapOrDie(void *ptr, size_t size); +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); -ssize_t AsanRead(int fd, void *buf, size_t count); -ssize_t AsanWrite(int fd, const void *buf, size_t count); -int AsanClose(int fd); +bool AsanInterceptsSignal(int signum); +void SetAlternateSignalStack(); +void UnsetAlternateSignalStack(); +void InstallSignalHandlers(); +void AsanPlatformThreadInit(); +// Wrapper for TLS/TSD. +void AsanTSDInit(void (*destructor)(void *tsd)); +void *AsanTSDGet(); +void AsanTSDSet(void *tsd); + +void AppendToErrorMessageBuffer(const char *buffer); // asan_printf.cc -void RawWrite(const char *buffer); -int SNPrint(char *buffer, size_t length, const char *format, ...); -void Printf(const char *format, ...); -void Report(const char *format, ...); - -// Don't use std::min and std::max, to minimize dependency on libstdc++. -template T Min(T a, T b) { return a < b ? a : b; } -template T Max(T a, T b) { return a > b ? a : b; } +void AsanPrintf(const char *format, ...); +void AsanReport(const char *format, ...); // asan_poisoning.cc // Poisons the shadow memory for "size" bytes starting from "addr". -void PoisonShadow(uintptr_t addr, size_t size, uint8_t value); +void PoisonShadow(uptr addr, uptr size, u8 value); // Poisons the shadow memory for "redzone_size" bytes starting from // "addr + size". -void PoisonShadowPartialRightRedzone(uintptr_t addr, - uintptr_t size, - uintptr_t redzone_size, - uint8_t value); +void PoisonShadowPartialRightRedzone(uptr addr, + uptr size, + uptr redzone_size, + u8 value); -extern size_t FLAG_quarantine_size; -extern int FLAG_demangle; -extern bool FLAG_symbolize; -extern int FLAG_v; -extern size_t FLAG_redzone; -extern int FLAG_debug; -extern bool FLAG_poison_shadow; -extern int FLAG_report_globals; -extern size_t FLAG_malloc_context_size; -extern bool FLAG_replace_str; -extern bool FLAG_replace_intrin; -extern bool FLAG_replace_cfallocator; -extern bool FLAG_fast_unwind; -extern bool FLAG_use_fake_stack; -extern size_t FLAG_max_malloc_fill_size; -extern int FLAG_exitcode; -extern bool FLAG_allow_user_poisoning; +// Platfrom-specific options. +#ifdef __APPLE__ +bool PlatformHasDifferentMemcpyAndMemmove(); +# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \ + (PlatformHasDifferentMemcpyAndMemmove()) +#else +# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true +#endif // __APPLE__ extern int asan_inited; // Used to avoid infinite recursion in __asan_init(). extern bool asan_init_is_running; +extern void (*death_callback)(void); enum LinkerInitialized { LINKER_INITIALIZED = 0 }; -#ifndef ASAN_DIE -#define ASAN_DIE _exit(FLAG_exitcode) -#endif // ASAN_DIE - -#define CHECK(cond) do { if (!(cond)) { \ - CheckFailed(#cond, __FILE__, __LINE__); \ -}}while(0) - -#define RAW_CHECK_MSG(expr, msg) do { \ - if (!(expr)) { \ - RawWrite(msg); \ - ASAN_DIE; \ - } \ -} while (0) - -#define RAW_CHECK(expr) RAW_CHECK_MSG(expr, #expr) - -#define UNIMPLEMENTED() CHECK("unimplemented" && 0) - #define ASAN_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) -const size_t kWordSize = __WORDSIZE / 8; -const size_t kWordSizeInBits = 8 * kWordSize; -const size_t kPageSizeBits = 12; -const size_t kPageSize = 1UL << kPageSizeBits; +#if !defined(_WIN32) || defined(__clang__) +# define GET_CALLER_PC() (uptr)__builtin_return_address(0) +# define GET_CURRENT_FRAME() (uptr)__builtin_frame_address(0) +#else +# define GET_CALLER_PC() (uptr)_ReturnAddress() +// CaptureStackBackTrace doesn't need to know BP on Windows. +// FIXME: This macro is still used when printing error reports though it's not +// clear if the BP value is needed in the ASan reports on Windows. +# define GET_CURRENT_FRAME() (uptr)0xDEADBEEF +#endif -#define GET_CALLER_PC() (uintptr_t)__builtin_return_address(0) -#define GET_CURRENT_FRAME() (uintptr_t)__builtin_frame_address(0) - -#define GET_BP_PC_SP \ - uintptr_t bp = GET_CURRENT_FRAME(); \ - uintptr_t pc = GET_CALLER_PC(); \ - uintptr_t local_stack; \ - uintptr_t sp = (uintptr_t)&local_stack; +#ifdef _WIN32 +# ifndef ASAN_USE_EXTERNAL_SYMBOLIZER +# define ASAN_USE_EXTERNAL_SYMBOLIZER __asan_WinSymbolize +bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size); +# endif +#endif // _WIN32 // These magic values are written to shadow for better error reporting. const int kAsanHeapLeftRedzoneMagic = 0xfa; @@ -191,18 +178,8 @@ const int kAsanUserPoisonedMemoryMagic = 0xf7; const int kAsanGlobalRedzoneMagic = 0xf9; const int kAsanInternalHeapMagic = 0xfe; -static const uintptr_t kCurrentStackFrameMagic = 0x41B58AB3; -static const uintptr_t kRetiredStackFrameMagic = 0x45E0360E; - -// --------------------------- Bit twiddling ------- {{{1 -inline bool IsPowerOfTwo(size_t x) { - return (x & (x - 1)) == 0; -} - -inline size_t RoundUpTo(size_t size, size_t boundary) { - CHECK(IsPowerOfTwo(boundary)); - return (size + boundary - 1) & ~(boundary - 1); -} +static const uptr kCurrentStackFrameMagic = 0x41B58AB3; +static const uptr kRetiredStackFrameMagic = 0x45E0360E; // -------------------------- LowLevelAllocator ----- {{{1 // A simple low-level memory allocator for internal use. @@ -211,29 +188,12 @@ class LowLevelAllocator { explicit LowLevelAllocator(LinkerInitialized) {} // 'size' must be a power of two. // Requires an external lock. - void *Allocate(size_t size); + void *Allocate(uptr size); private: char *allocated_end_; char *allocated_current_; }; -// -------------------------- Atomic ---------------- {{{1 -static inline int AtomicInc(int *a) { -#ifdef ANDROID - return __atomic_inc(a) + 1; -#else - return __sync_add_and_fetch(a, 1); -#endif -} - -static inline int AtomicDec(int *a) { -#ifdef ANDROID - return __atomic_dec(a) - 1; -#else - return __sync_add_and_fetch(a, -1); -#endif -} - } // namespace __asan #endif // ASAN_INTERNAL_H diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc index 26a08ae8f5b6..9a3d6bdb15a6 100644 --- a/lib/asan/asan_linux.cc +++ b/lib/asan/asan_linux.cc @@ -13,87 +13,131 @@ //===----------------------------------------------------------------------===// #ifdef __linux__ +#include "asan_interceptors.h" #include "asan_internal.h" +#include "asan_lock.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" +#include +#include #include #include #include #include +#include +#include #include +#include -extern char _DYNAMIC[]; +#ifndef ANDROID +// FIXME: where to get ucontext on Android? +#include +#endif + +extern "C" void* _DYNAMIC; namespace __asan { void *AsanDoesNotSupportStaticLinkage() { // This will fail to link with -static. - return &_DYNAMIC; + return &_DYNAMIC; // defined in link.h } -static void *asan_mmap(void *addr, size_t length, int prot, int flags, - int fd, uint64_t offset) { -# if __WORDSIZE == 64 - return (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset); -# else - return (void *)syscall(__NR_mmap2, addr, length, prot, flags, fd, offset); -# endif +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { +#ifdef ANDROID + *pc = *sp = *bp = 0; +#elif defined(__arm__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.arm_pc; + *bp = ucontext->uc_mcontext.arm_fp; + *sp = ucontext->uc_mcontext.arm_sp; +# elif defined(__x86_64__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[REG_RIP]; + *bp = ucontext->uc_mcontext.gregs[REG_RBP]; + *sp = ucontext->uc_mcontext.gregs[REG_RSP]; +# elif defined(__i386__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[REG_EIP]; + *bp = ucontext->uc_mcontext.gregs[REG_EBP]; + *sp = ucontext->uc_mcontext.gregs[REG_ESP]; +#else +# error "Unsupported arch" +#endif } -void *AsanMmapSomewhereOrDie(size_t size, const char *mem_type) { - size = RoundUpTo(size, kPageSize); - void *res = asan_mmap(0, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, -1, 0); - if (res == (void*)-1) { - OutOfMemoryMessageAndDie(mem_type, size); +bool AsanInterceptsSignal(int signum) { + return signum == SIGSEGV && flags()->handle_segv; +} + +void AsanPlatformThreadInit() { + // Nothing here for now. +} + +AsanLock::AsanLock(LinkerInitialized) { + // We assume that pthread_mutex_t initialized to all zeroes is a valid + // unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers + // a gcc warning: + // extended initializer lists only available with -std=c++0x or -std=gnu++0x +} + +void AsanLock::Lock() { + CHECK(sizeof(pthread_mutex_t) <= sizeof(opaque_storage_)); + pthread_mutex_lock((pthread_mutex_t*)&opaque_storage_); + CHECK(!owner_); + owner_ = (uptr)pthread_self(); +} + +void AsanLock::Unlock() { + CHECK(owner_ == (uptr)pthread_self()); + owner_ = 0; + pthread_mutex_unlock((pthread_mutex_t*)&opaque_storage_); +} + +#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) { + AsanStackTrace *b = (AsanStackTrace*)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; +} + +void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) { + size = 0; + trace[0] = pc; + if ((max_s) > 1) { + max_size = max_s; +#ifdef __arm__ + _Unwind_Backtrace(Unwind_Trace, this); +#else + FastUnwindStack(pc, bp); +#endif } - return res; -} - -void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size) { - return asan_mmap((void*)fixed_addr, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, - 0, 0); -} - -void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size) { - return asan_mmap((void*)fixed_addr, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - 0, 0); -} - -void *AsanMprotect(uintptr_t fixed_addr, size_t size) { - return asan_mmap((void*)fixed_addr, size, - PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, - 0, 0); -} - -void AsanUnmapOrDie(void *addr, size_t size) { - if (!addr || !size) return; - int res = syscall(__NR_munmap, addr, size); - if (res != 0) { - Report("Failed to unmap\n"); - ASAN_DIE; - } -} - -ssize_t AsanWrite(int fd, const void *buf, size_t count) { - return (ssize_t)syscall(__NR_write, fd, buf, count); -} - -int AsanOpenReadonly(const char* filename) { - return open(filename, O_RDONLY); -} - -ssize_t AsanRead(int fd, void *buf, size_t count) { - return (ssize_t)syscall(__NR_read, fd, buf, count); -} - -int AsanClose(int fd) { - return close(fd); } } // namespace __asan diff --git a/lib/asan/asan_lock.h b/lib/asan/asan_lock.h index 030fae613c23..edee49adf6a7 100644 --- a/lib/asan/asan_lock.h +++ b/lib/asan/asan_lock.h @@ -14,86 +14,28 @@ #ifndef ASAN_LOCK_H #define ASAN_LOCK_H +#include "sanitizer_common/sanitizer_mutex.h" #include "asan_internal.h" // The locks in ASan are global objects and they are never destroyed to avoid // at-exit races (that is, a lock is being used by other threads while the main // thread is doing atexit destructors). +// We define the class using opaque storage to avoid including system headers. -#ifdef __APPLE__ -#include - -#include namespace __asan { + class AsanLock { public: - explicit AsanLock(LinkerInitialized) : - mu_(OS_SPINLOCK_INIT), - owner_(0), - is_locked_(false) {} - - void Lock() { - CHECK(owner_ != pthread_self()); - OSSpinLockLock(&mu_); - is_locked_ = true; - owner_ = pthread_self(); - } - void Unlock() { - owner_ = 0; - is_locked_ = false; - OSSpinLockUnlock(&mu_); - } - - bool IsLocked() { - // This is not atomic, e.g. one thread may get different values if another - // one is about to release the lock. - return is_locked_; - } + explicit AsanLock(LinkerInitialized); + void Lock(); + void Unlock(); + bool IsLocked() { return owner_ != 0; } private: - OSSpinLock mu_; - volatile pthread_t owner_; // for debugging purposes - bool is_locked_; // for silly malloc_introspection_t interface + uptr opaque_storage_[10]; + uptr owner_; // for debugging and for malloc_introspection_t interface }; -} // namespace __asan -#else // assume linux -#include -namespace __asan { -class AsanLock { - public: - explicit AsanLock(LinkerInitialized) { - // We assume that pthread_mutex_t initialized to all zeroes is a valid - // unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers - // a gcc warning: - // extended initializer lists only available with -std=c++0x or -std=gnu++0x - } - void Lock() { - pthread_mutex_lock(&mu_); - // pthread_spin_lock(&mu_); - } - void Unlock() { - pthread_mutex_unlock(&mu_); - // pthread_spin_unlock(&mu_); - } - private: - pthread_mutex_t mu_; - // pthread_spinlock_t mu_; -}; -} // namespace __asan -#endif - -namespace __asan { -class ScopedLock { - public: - explicit ScopedLock(AsanLock *mu) : mu_(mu) { - mu_->Lock(); - } - ~ScopedLock() { - mu_->Unlock(); - } - private: - AsanLock *mu_; -}; +typedef GenericScopedLock ScopedLock; } // namespace __asan diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc index b202e639b511..a3d39e7ae05c 100644 --- a/lib/asan/asan_mac.cc +++ b/lib/asan/asan_mac.cc @@ -14,93 +14,165 @@ #ifdef __APPLE__ -#include "asan_mac.h" - +#include "asan_interceptors.h" #include "asan_internal.h" +#include "asan_mac.h" +#include "asan_mapping.h" #include "asan_stack.h" #include "asan_thread.h" #include "asan_thread_registry.h" +#include "sanitizer_common/sanitizer_libc.h" +#include // for _NSGetEnviron +#include +#include #include +#include +#include +#include #include +#include +#include // for free() #include - -#include +#include +#include namespace __asan { -extern dispatch_async_f_f real_dispatch_async_f; -extern dispatch_sync_f_f real_dispatch_sync_f; -extern dispatch_after_f_f real_dispatch_after_f; -extern dispatch_barrier_async_f_f real_dispatch_barrier_async_f; -extern dispatch_group_async_f_f real_dispatch_group_async_f; -extern pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np; +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { + ucontext_t *ucontext = (ucontext_t*)context; +# if __WORDSIZE == 64 + *pc = ucontext->uc_mcontext->__ss.__rip; + *bp = ucontext->uc_mcontext->__ss.__rbp; + *sp = ucontext->uc_mcontext->__ss.__rsp; +# else + *pc = ucontext->uc_mcontext->__ss.__eip; + *bp = ucontext->uc_mcontext->__ss.__ebp; + *sp = ucontext->uc_mcontext->__ss.__esp; +# endif // __WORDSIZE +} + +int GetMacosVersion() { + 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); + switch (version[0]) { + case '9': return MACOS_VERSION_LEOPARD; + case '1': { + switch (version[1]) { + case '0': return MACOS_VERSION_SNOW_LEOPARD; + case '1': return MACOS_VERSION_LION; + default: return MACOS_VERSION_UNKNOWN; + } + } + default: return MACOS_VERSION_UNKNOWN; + } +} + +bool PlatformHasDifferentMemcpyAndMemmove() { + // On OS X 10.7 memcpy() and memmove() are both resolved + // into memmove$VARIANT$sse42. + // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34. + // TODO(glider): need to check dynamically that memcpy() and memmove() are + // actually the same function. + return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD; +} // No-op. Mac does not support static linkage anyway. void *AsanDoesNotSupportStaticLinkage() { - return NULL; + return 0; } -static void *asan_mmap(void *addr, size_t length, int prot, int flags, - int fd, uint64_t offset) { - return mmap(addr, length, prot, flags, fd, offset); +bool AsanInterceptsSignal(int signum) { + return (signum == SIGSEGV || signum == SIGBUS) && flags()->handle_segv; } -ssize_t AsanWrite(int fd, const void *buf, size_t count) { - return write(fd, buf, count); +void AsanPlatformThreadInit() { + ReplaceCFAllocator(); } -void *AsanMmapSomewhereOrDie(size_t size, const char *mem_type) { - size = RoundUpTo(size, kPageSize); - void *res = asan_mmap(0, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, -1, 0); - if (res == (void*)-1) { - OutOfMemoryMessageAndDie(mem_type, size); - } - return res; +AsanLock::AsanLock(LinkerInitialized) { + // We assume that OS_SPINLOCK_INIT is zero } -void *AsanMmapFixedNoReserve(uintptr_t fixed_addr, size_t size) { - return asan_mmap((void*)fixed_addr, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, - 0, 0); +void AsanLock::Lock() { + CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_)); + CHECK(OS_SPINLOCK_INIT == 0); + CHECK(owner_ != (uptr)pthread_self()); + OSSpinLockLock((OSSpinLock*)&opaque_storage_); + CHECK(!owner_); + owner_ = (uptr)pthread_self(); } -void *AsanMmapFixedReserve(uintptr_t fixed_addr, size_t size) { - return asan_mmap((void*)fixed_addr, size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - 0, 0); +void AsanLock::Unlock() { + CHECK(owner_ == (uptr)pthread_self()); + owner_ = 0; + OSSpinLockUnlock((OSSpinLock*)&opaque_storage_); } -void *AsanMprotect(uintptr_t fixed_addr, size_t size) { - return asan_mmap((void*)fixed_addr, size, - PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, - 0, 0); -} - -void AsanUnmapOrDie(void *addr, size_t size) { - if (!addr || !size) return; - int res = munmap(addr, size); - if (res != 0) { - Report("Failed to unmap\n"); - ASAN_DIE; +void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) { + size = 0; + trace[0] = pc; + if ((max_s) > 1) { + max_size = max_s; + FastUnwindStack(pc, bp); } } -int AsanOpenReadonly(const char* filename) { - return open(filename, O_RDONLY); +// The range of pages to be used for escape islands. +// TODO(glider): instead of mapping a fixed range we must find a range of +// unmapped pages in vmmap and take them. +// These constants were chosen empirically and may not work if the shadow +// memory layout changes. Unfortunately they do necessarily depend on +// kHighMemBeg or kHighMemEnd. +static void *island_allocator_pos = 0; + +#if __WORDSIZE == 32 +# define kIslandEnd (0xffdf0000 - kPageSize) +# define kIslandBeg (kIslandEnd - 256 * kPageSize) +#else +# define kIslandEnd (0x7fffffdf0000 - kPageSize) +# define kIslandBeg (kIslandEnd - 256 * kPageSize) +#endif + +extern "C" +mach_error_t __interception_allocate_island(void **ptr, + uptr unused_size, + void *unused_hint) { + if (!island_allocator_pos) { + island_allocator_pos = + internal_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, 0); + if (island_allocator_pos != (void*)kIslandBeg) { + return KERN_NO_SPACE; + } + if (flags()->verbosity) { + Report("Mapped pages %p--%p for branch islands.\n", + (void*)kIslandBeg, (void*)kIslandEnd); + } + // Should not be very performance-critical. + internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg); + }; + *ptr = island_allocator_pos; + island_allocator_pos = (char*)island_allocator_pos + kPageSize; + if (flags()->verbosity) { + Report("Branch island allocated at %p\n", *ptr); + } + return err_none; } -ssize_t AsanRead(int fd, void *buf, size_t count) { - return read(fd, buf, count); -} - -int AsanClose(int fd) { - return close(fd); +extern "C" +mach_error_t __interception_deallocate_island(void *ptr) { + // Do nothing. + // TODO(glider): allow to free and reuse the island memory. + return err_none; } // Support for the following functions from libdispatch on Mac OS: @@ -132,34 +204,56 @@ int AsanClose(int fd) { // The implementation details are at // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c +typedef void* pthread_workqueue_t; +typedef void* pthread_workitem_handle_t; + +typedef void* dispatch_group_t; +typedef void* dispatch_queue_t; +typedef u64 dispatch_time_t; +typedef void (*dispatch_function_t)(void *block); +typedef void* (*worker_t)(void *block); + +// A wrapper for the ObjC blocks used to support libdispatch. +typedef struct { + void *block; + dispatch_function_t func; + 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); +int pthread_workqueue_additem_np(pthread_workqueue_t workq, + void *(*workitem_func)(void *), void * workitem_arg, + pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp); +} // extern "C" + extern "C" void asan_dispatch_call_block_and_release(void *block) { - GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); + GET_STACK_TRACE_HERE(kStackTraceMax); asan_block_context_t *context = (asan_block_context_t*)block; - if (FLAG_v >= 2) { + if (flags()->verbosity >= 2) { Report("asan_dispatch_call_block_and_release(): " "context: %p, pthread_self: %p\n", block, pthread_self()); } AsanThread *t = asanThreadRegistry().GetCurrent(); - if (t) { - // We've already executed a job on this worker thread. Let's reuse the - // AsanThread object. - if (t != asanThreadRegistry().GetMain()) { - // Flush the statistics and update the current thread's tid. - asanThreadRegistry().UnregisterThread(t); - asanThreadRegistry().RegisterThread(t, context->parent_tid, &stack); - } - // Otherwise the worker is being executed on the main thread -- we are - // draining the dispatch queue. - // TODO(glider): any checks for that? - } else { - // It's incorrect to assert that the current thread is not dying: at least - // the callbacks from dispatch_sync() are sometimes called after the TSD is - // destroyed. - t = (AsanThread*)asan_malloc(sizeof(AsanThread), &stack); - new(t) AsanThread(context->parent_tid, - /*start_routine*/NULL, /*arg*/NULL, &stack); + if (!t) { + t = AsanThread::Create(context->parent_tid, 0, 0, &stack); + asanThreadRegistry().RegisterThread(t); t->Init(); asanThreadRegistry().SetCurrent(t); } @@ -181,94 +275,75 @@ 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; - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); - if (FLAG_debug) { - // Sometimes at Chromium teardown this assertion is violated: - // -- a task is created via dispatch_async() on the "CFMachPort" - // thread while doing _dispatch_queue_drain(); - // -- a task is created via dispatch_async_f() on the - // "com.apple.root.default-overcommit-priority" thread while doing - // _dispatch_dispose(). - // TODO(glider): find out what's going on. - CHECK(curr_thread || asanThreadRegistry().IsCurrentThreadDying()); - } - asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne(); + asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); return asan_ctxt; } // TODO(glider): can we reduce code duplication by introducing a macro? -extern "C" -int WRAP(dispatch_async_f)(dispatch_queue_t dq, - void *ctxt, - dispatch_function_t func) { - GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); +INTERCEPTOR(void, dispatch_async_f, dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { + GET_STACK_TRACE_HERE(kStackTraceMax); asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (FLAG_v >= 2) { + if (flags()->verbosity >= 2) { Report("dispatch_async_f(): context: %p, pthread_self: %p\n", asan_ctxt, pthread_self()); PRINT_CURRENT_STACK(); } - return real_dispatch_async_f(dq, (void*)asan_ctxt, - asan_dispatch_call_block_and_release); + return REAL(dispatch_async_f)(dq, (void*)asan_ctxt, + asan_dispatch_call_block_and_release); } -extern "C" -int WRAP(dispatch_sync_f)(dispatch_queue_t dq, - void *ctxt, - dispatch_function_t func) { - GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); +INTERCEPTOR(void, dispatch_sync_f, dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { + GET_STACK_TRACE_HERE(kStackTraceMax); asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (FLAG_v >= 2) { + if (flags()->verbosity >= 2) { Report("dispatch_sync_f(): context: %p, pthread_self: %p\n", asan_ctxt, pthread_self()); PRINT_CURRENT_STACK(); } - return real_dispatch_sync_f(dq, (void*)asan_ctxt, - asan_dispatch_call_block_and_release); -} - -extern "C" -int WRAP(dispatch_after_f)(dispatch_time_t when, - dispatch_queue_t dq, - void *ctxt, - dispatch_function_t func) { - GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); - asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (FLAG_v >= 2) { - Report("dispatch_after_f: %p\n", asan_ctxt); - PRINT_CURRENT_STACK(); - } - return real_dispatch_after_f(when, dq, (void*)asan_ctxt, + return REAL(dispatch_sync_f)(dq, (void*)asan_ctxt, asan_dispatch_call_block_and_release); } -extern "C" -void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq, - void *ctxt, dispatch_function_t func) { - GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); +INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, + dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { + GET_STACK_TRACE_HERE(kStackTraceMax); asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (FLAG_v >= 2) { + if (flags()->verbosity >= 2) { + Report("dispatch_after_f: %p\n", asan_ctxt); + PRINT_CURRENT_STACK(); + } + return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt, + asan_dispatch_call_block_and_release); +} + +INTERCEPTOR(void, dispatch_barrier_async_f, dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { + GET_STACK_TRACE_HERE(kStackTraceMax); + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); + if (flags()->verbosity >= 2) { Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n", asan_ctxt, pthread_self()); PRINT_CURRENT_STACK(); } - real_dispatch_barrier_async_f(dq, (void*)asan_ctxt, - asan_dispatch_call_block_and_release); + REAL(dispatch_barrier_async_f)(dq, (void*)asan_ctxt, + asan_dispatch_call_block_and_release); } -extern "C" -void WRAP(dispatch_group_async_f)(dispatch_group_t group, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func) { - GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); +INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, + dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { + GET_STACK_TRACE_HERE(kStackTraceMax); asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (FLAG_v >= 2) { + if (flags()->verbosity >= 2) { Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", asan_ctxt, pthread_self()); PRINT_CURRENT_STACK(); } - real_dispatch_group_async_f(group, dq, (void*)asan_ctxt, - asan_dispatch_call_block_and_release); + REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt, + asan_dispatch_call_block_and_release); } // The following stuff has been extremely helpful while looking for the @@ -279,33 +354,90 @@ void WRAP(dispatch_group_async_f)(dispatch_group_t group, // libdispatch API. extern "C" void *wrap_workitem_func(void *arg) { - if (FLAG_v >= 2) { + if (flags()->verbosity >= 2) { Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self()); } asan_block_context_t *ctxt = (asan_block_context_t*)arg; worker_t fn = (worker_t)(ctxt->func); void *result = fn(ctxt->block); - GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); + GET_STACK_TRACE_HERE(kStackTraceMax); asan_free(arg, &stack); return result; } -extern "C" -int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq, +INTERCEPTOR(int, pthread_workqueue_additem_np, pthread_workqueue_t workq, void *(*workitem_func)(void *), void * workitem_arg, pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) { - GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); + GET_STACK_TRACE_HERE(kStackTraceMax); asan_block_context_t *asan_ctxt = (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack); asan_ctxt->block = workitem_arg; asan_ctxt->func = (dispatch_function_t)workitem_func; - asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrMinusOne(); - if (FLAG_v >= 2) { + asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + if (flags()->verbosity >= 2) { Report("pthread_workqueue_additem_np: %p\n", asan_ctxt); PRINT_CURRENT_STACK(); } - return real_pthread_workqueue_additem_np(workq, wrap_workitem_func, asan_ctxt, - itemhandlep, gencountp); + return REAL(pthread_workqueue_additem_np)(workq, wrap_workitem_func, + asan_ctxt, itemhandlep, + gencountp); } +// See http://opensource.apple.com/source/CF/CF-635.15/CFString.c +int __CFStrIsConstant(CFStringRef str) { + CFRuntimeBase *base = (CFRuntimeBase*)str; +#if __LP64__ + return base->_rc == 0; +#else + return (base->_cfinfo[CF_RC_BITS]) == 0; +#endif +} + +INTERCEPTOR(CFStringRef, CFStringCreateCopy, CFAllocatorRef alloc, + CFStringRef str) { + if (__CFStrIsConstant(str)) { + return str; + } else { + return REAL(CFStringCreateCopy)(alloc, str); + } +} + +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) + +extern "C" +void __CFInitialize(); +DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize) + +namespace __asan { + +void InitializeMacInterceptors() { + CHECK(INTERCEPT_FUNCTION(dispatch_async_f)); + CHECK(INTERCEPT_FUNCTION(dispatch_sync_f)); + CHECK(INTERCEPT_FUNCTION(dispatch_after_f)); + CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f)); + CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f)); + // We don't need to intercept pthread_workqueue_additem_np() to support the + // libdispatch API, but it helps us to debug the unsupported functions. Let's + // intercept it only during verbose runs. + if (flags()->verbosity >= 2) { + CHECK(INTERCEPT_FUNCTION(pthread_workqueue_additem_np)); + } + // Normally CFStringCreateCopy should not copy constant CF strings. + // Replacing the default CFAllocator causes constant strings to be copied + // rather than just returned, which leads to bugs in big applications like + // Chromium and WebKit, see + // http://code.google.com/p/address-sanitizer/issues/detail?id=10 + // Until this problem is fixed we need to check that the string is + // non-constant before calling CFStringCreateCopy. + CHECK(INTERCEPT_FUNCTION(CFStringCreateCopy)); + // Some of the library functions call free() directly, so we have to + // intercept it. + CHECK(INTERCEPT_FUNCTION(free)); + if (flags()->replace_cfallocator) { + CHECK(INTERCEPT_FUNCTION(__CFInitialize)); + } +} + +} // namespace __asan + #endif // __APPLE__ diff --git a/lib/asan/asan_mac.h b/lib/asan/asan_mac.h index 32739e766cc7..6c65765a804c 100644 --- a/lib/asan/asan_mac.h +++ b/lib/asan/asan_mac.h @@ -9,79 +9,45 @@ // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_mac.cc +// Mac-specific ASan definitions. //===----------------------------------------------------------------------===// -#ifdef __APPLE__ - #ifndef ASAN_MAC_H #define ASAN_MAC_H -#include "asan_interceptors.h" +// CF_RC_BITS, the layout of CFRuntimeBase and __CFStrIsConstant are internal +// and subject to change in further CoreFoundation versions. Apple does not +// guarantee any binary compatibility from release to release. -// TODO(glider): need to check if the OS X version is 10.6 or greater. -#include -#include +// See http://opensource.apple.com/source/CF/CF-635.15/CFInternal.h +#if defined(__BIG_ENDIAN__) +#define CF_RC_BITS 0 +#endif -typedef void* pthread_workqueue_t; -typedef void* pthread_workitem_handle_t; +#if defined(__LITTLE_ENDIAN__) +#define CF_RC_BITS 3 +#endif -typedef void (*dispatch_function_t)(void *block); -typedef void* (*worker_t)(void *block); -typedef int (*dispatch_async_f_f)(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -typedef int (*dispatch_sync_f_f)(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -typedef int (*dispatch_after_f_f)(dispatch_time_t when, - dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -typedef void (*dispatch_barrier_async_f_f)(dispatch_queue_t dq, - void *ctxt, - dispatch_function_t func); -typedef void (*dispatch_group_async_f_f)(dispatch_group_t group, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -typedef int (*pthread_workqueue_additem_np_f)(pthread_workqueue_t workq, - void *(*workitem_func)(void *), void * workitem_arg, - pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp); +// See http://opensource.apple.com/source/CF/CF-635.15/CFRuntime.h +typedef struct __CFRuntimeBase { + uptr _cfisa; + u8 _cfinfo[4]; +#if __LP64__ + u32 _rc; +#endif +} CFRuntimeBase; +enum { + MACOS_VERSION_UNKNOWN = 0, + MACOS_VERSION_LEOPARD, + MACOS_VERSION_SNOW_LEOPARD, + MACOS_VERSION_LION +}; -// A wrapper for the ObjC blocks used to support libdispatch. -typedef struct { - void *block; - dispatch_function_t func; - int parent_tid; -} asan_block_context_t; +namespace __asan { +int GetMacosVersion(); +void ReplaceCFAllocator(); -extern "C" { -// dispatch_barrier_async_f() is not declared in . -void dispatch_barrier_async_f(dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -// Neither is pthread_workqueue_additem_np(). -int pthread_workqueue_additem_np(pthread_workqueue_t workq, - void *(*workitem_func)(void *), void * workitem_arg, - pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp); - -int WRAP(dispatch_async_f)(dispatch_queue_t dq, - void *ctxt, - dispatch_function_t func); -int WRAP(dispatch_sync_f)(dispatch_queue_t dq, - void *ctxt, - dispatch_function_t func); -int WRAP(dispatch_after_f)(dispatch_time_t when, - dispatch_queue_t dq, - void *ctxt, - dispatch_function_t func); -void WRAP(dispatch_barrier_async_f)(dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -void WRAP(dispatch_group_async_f)(dispatch_group_t group, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -int WRAP(pthread_workqueue_additem_np)(pthread_workqueue_t workq, - void *(*workitem_func)(void *), void * workitem_arg, - pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp); -} +} // namespace __asan #endif // ASAN_MAC_H - -#endif // __APPLE__ diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc index 9dbc7a127fcc..1046f4c843a8 100644 --- a/lib/asan/asan_malloc_linux.cc +++ b/lib/asan/asan_malloc_linux.cc @@ -1,4 +1,4 @@ -//===-- asan_malloc_linux.cc ------------------------------------*- C++ -*-===// +//===-- asan_malloc_linux.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -20,20 +20,16 @@ #include "asan_internal.h" #include "asan_stack.h" -#include - -#define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default"))) - #ifdef ANDROID struct MallocDebug { - void* (*malloc)(size_t bytes); + void* (*malloc)(uptr bytes); void (*free)(void* mem); - void* (*calloc)(size_t n_elements, size_t elem_size); - void* (*realloc)(void* oldMem, size_t bytes); - void* (*memalign)(size_t alignment, size_t bytes); + void* (*calloc)(uptr n_elements, uptr elem_size); + void* (*realloc)(void* oldMem, uptr bytes); + void* (*memalign)(uptr alignment, uptr bytes); }; -const MallocDebug asan_malloc_dispatch __attribute__((aligned(32))) = { +const MallocDebug asan_malloc_dispatch ALIGNED(32) = { malloc, free, calloc, realloc, memalign }; @@ -56,33 +52,28 @@ void ReplaceSystemMalloc() { // ---------------------- Replacement functions ---------------- {{{1 using namespace __asan; // NOLINT -extern "C" { -INTERCEPTOR_ATTRIBUTE -void free(void *ptr) { +INTERCEPTOR(void, free, void *ptr) { GET_STACK_TRACE_HERE_FOR_FREE(ptr); asan_free(ptr, &stack); } -INTERCEPTOR_ATTRIBUTE -void cfree(void *ptr) { +INTERCEPTOR(void, cfree, void *ptr) { GET_STACK_TRACE_HERE_FOR_FREE(ptr); asan_free(ptr, &stack); } -INTERCEPTOR_ATTRIBUTE -void *malloc(size_t size) { +INTERCEPTOR(void*, malloc, uptr size) { GET_STACK_TRACE_HERE_FOR_MALLOC; return asan_malloc(size, &stack); } -INTERCEPTOR_ATTRIBUTE -void *calloc(size_t nmemb, size_t size) { +INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { if (!asan_inited) { - // Hack: dlsym calls calloc before real_calloc is retrieved from dlsym. - const size_t kCallocPoolSize = 1024; - static uintptr_t calloc_memory_for_dlsym[kCallocPoolSize]; - static size_t allocated; - size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const uptr kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static uptr allocated; + uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; void *mem = (void*)&calloc_memory_for_dlsym[allocated]; allocated += size_in_words; CHECK(allocated < kCallocPoolSize); @@ -92,51 +83,56 @@ void *calloc(size_t nmemb, size_t size) { return asan_calloc(nmemb, size, &stack); } -INTERCEPTOR_ATTRIBUTE -void *realloc(void *ptr, size_t size) { +INTERCEPTOR(void*, realloc, void *ptr, uptr size) { GET_STACK_TRACE_HERE_FOR_MALLOC; return asan_realloc(ptr, size, &stack); } -INTERCEPTOR_ATTRIBUTE -void *memalign(size_t boundary, size_t size) { +INTERCEPTOR(void*, memalign, uptr boundary, uptr size) { GET_STACK_TRACE_HERE_FOR_MALLOC; return asan_memalign(boundary, size, &stack); } -void* __libc_memalign(size_t align, size_t s) - __attribute__((alias("memalign"))); +INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s) + ALIAS("memalign"); -INTERCEPTOR_ATTRIBUTE -struct mallinfo mallinfo() { - struct mallinfo res; - real_memset(&res, 0, sizeof(res)); +INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc_usable_size(ptr, &stack); +} + +// We avoid including malloc.h for portability reasons. +// man mallinfo says the fields are "long", but the implementation uses int. +// It doesn't matter much -- we just need to make sure that the libc's mallinfo +// is not called. +struct fake_mallinfo { + int x[10]; +}; + +INTERCEPTOR(struct fake_mallinfo, mallinfo, void) { + struct fake_mallinfo res; + REAL(memset)(&res, 0, sizeof(res)); return res; } -INTERCEPTOR_ATTRIBUTE -int mallopt(int cmd, int value) { +INTERCEPTOR(int, mallopt, int cmd, int value) { return -1; } -INTERCEPTOR_ATTRIBUTE -int posix_memalign(void **memptr, size_t alignment, size_t size) { +INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { GET_STACK_TRACE_HERE_FOR_MALLOC; - // Printf("posix_memalign: %lx %ld\n", alignment, size); + // Printf("posix_memalign: %zx %zu\n", alignment, size); return asan_posix_memalign(memptr, alignment, size, &stack); } -INTERCEPTOR_ATTRIBUTE -void *valloc(size_t size) { +INTERCEPTOR(void*, valloc, uptr size) { GET_STACK_TRACE_HERE_FOR_MALLOC; return asan_valloc(size, &stack); } -INTERCEPTOR_ATTRIBUTE -void *pvalloc(size_t size) { +INTERCEPTOR(void*, pvalloc, uptr size) { GET_STACK_TRACE_HERE_FOR_MALLOC; return asan_pvalloc(size, &stack); } -} // extern "C" #endif // __linux__ diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc index 8a6f1bc41ff8..1a6c84052a0a 100644 --- a/lib/asan/asan_malloc_mac.cc +++ b/lib/asan/asan_malloc_mac.cc @@ -1,4 +1,4 @@ -//===-- asan_rtl.cc ---------------------------------------------*- C++ -*-===// +//===-- asan_rtl.cc -------------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -16,12 +16,14 @@ #include #include +#include #include #include #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_internal.h" +#include "asan_mac.h" #include "asan_stack.h" // Similar code is used in Google Perftools, @@ -30,12 +32,26 @@ // ---------------------- Replacement functions ---------------- {{{1 using namespace __asan; // NOLINT +// TODO(glider): do we need both zones? +static malloc_zone_t *system_malloc_zone = 0; +static malloc_zone_t *system_purgeable_zone = 0; +static malloc_zone_t asan_zone; +CFAllocatorRef cf_asan = 0; + // The free() implementation provided by OS X calls malloc_zone_from_ptr() -// to find the owner of |ptr|. If the result is NULL, an invalid free() is +// to find the owner of |ptr|. If the result is 0, an invalid free() is // reported. Our implementation falls back to asan_free() in this case // in order to print an ASan-style report. -extern "C" -void free(void *ptr) { +// +// For the objects created by _CFRuntimeCreateInstance a CFAllocatorRef is +// placed at the beginning of the allocated chunk and the pointer returned by +// our allocator is off by sizeof(CFAllocatorRef). This pointer can be then +// passed directly to free(), which will lead to errors. +// To overcome this we're checking whether |ptr-sizeof(CFAllocatorRef)| +// contains a pointer to our CFAllocator (assuming no other allocator is used). +// See http://code.google.com/p/address-sanitizer/issues/detail?id=70 for more +// info. +INTERCEPTOR(void, free, void *ptr) { malloc_zone_t *zone = malloc_zone_from_ptr(ptr); if (zone) { #if defined(MAC_OS_X_VERSION_10_6) && \ @@ -49,27 +65,48 @@ void free(void *ptr) { malloc_zone_free(zone, ptr); #endif } else { + if (flags()->replace_cfallocator) { + // Make sure we're not hitting the previous page. This may be incorrect + // if ASan's malloc returns an address ending with 0xFF8, which will be + // then padded to a page boundary with a CFAllocatorRef. + uptr arith_ptr = (uptr)ptr; + if ((arith_ptr & 0xFFF) > sizeof(CFAllocatorRef)) { + CFAllocatorRef *saved = + (CFAllocatorRef*)(arith_ptr - sizeof(CFAllocatorRef)); + if ((*saved == cf_asan) && asan_mz_size(saved)) ptr = (void*)saved; + } + } GET_STACK_TRACE_HERE_FOR_FREE(ptr); asan_free(ptr, &stack); } } -// TODO(glider): do we need both zones? -static malloc_zone_t *system_malloc_zone = NULL; -static malloc_zone_t *system_purgeable_zone = NULL; +namespace __asan { + void ReplaceCFAllocator(); +} + +// We can't always replace the default CFAllocator with cf_asan right in +// ReplaceSystemMalloc(), because it is sometimes called before +// __CFInitialize(), when the default allocator is invalid and replacing it may +// crash the program. Instead we wait for the allocator to initialize and jump +// in just after __CFInitialize(). Nobody is going to allocate memory using +// CFAllocators before that, so we won't miss anything. +// +// See http://code.google.com/p/address-sanitizer/issues/detail?id=87 +// and http://opensource.apple.com/source/CF/CF-550.43/CFRuntime.c +INTERCEPTOR(void, __CFInitialize) { + CHECK(flags()->replace_cfallocator); + CHECK(asan_inited); + REAL(__CFInitialize)(); + if (!cf_asan) ReplaceCFAllocator(); +} -// We need to provide wrappers around all the libc functions. namespace { + // TODO(glider): the mz_* functions should be united with the Linux wrappers, // as they are basically copied from there. size_t mz_size(malloc_zone_t* zone, const void* ptr) { - // Fast path: check whether this pointer belongs to the original malloc zone. - // We cannot just call malloc_zone_from_ptr(), because it in turn - // calls our mz_size(). - if (system_malloc_zone) { - if ((system_malloc_zone->size)(system_malloc_zone, ptr)) return 0; - } - return __asan_mz_size(ptr); + return asan_mz_size(ptr); } void *mz_malloc(malloc_zone_t *zone, size_t size) { @@ -92,9 +129,9 @@ void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) { void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { if (!asan_inited) { - // Hack: dlsym calls calloc before real_calloc is retrieved from dlsym. + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. const size_t kCallocPoolSize = 1024; - static uintptr_t calloc_memory_for_dlsym[kCallocPoolSize]; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; static size_t allocated; size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; void *mem = (void*)&calloc_memory_for_dlsym[allocated]; @@ -119,64 +156,41 @@ void print_zone_for_ptr(void *ptr) { malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr); if (orig_zone) { if (orig_zone->zone_name) { - Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n", - ptr, orig_zone, orig_zone->zone_name); + AsanPrintf("malloc_zone_from_ptr(%p) = %p, which is %s\n", + ptr, orig_zone, orig_zone->zone_name); } else { - Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n", - ptr, orig_zone); + AsanPrintf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n", + ptr, orig_zone); } } else { - Printf("malloc_zone_from_ptr(%p) = NULL\n", ptr); + AsanPrintf("malloc_zone_from_ptr(%p) = 0\n", ptr); + } +} + +void ALWAYS_INLINE free_common(void *context, void *ptr) { + if (!ptr) return; + if (!flags()->mac_ignore_invalid_free || asan_mz_size(ptr)) { + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + asan_free(ptr, &stack); + } else { + // Let us just leak this memory for now. + AsanPrintf("free_common(%p) -- attempting to free unallocated memory.\n" + "AddressSanitizer is ignoring this error on Mac OS now.\n", + ptr); + print_zone_for_ptr(ptr); + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + stack.PrintStack(); + return; } } // TODO(glider): the allocation callbacks need to be refactored. void mz_free(malloc_zone_t *zone, void *ptr) { - if (!ptr) return; - malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr); - // For some reason Chromium calls mz_free() for pointers that belong to - // DefaultPurgeableMallocZone instead of asan_zone. We might want to - // fix this someday. - if (orig_zone == system_purgeable_zone) { - system_purgeable_zone->free(system_purgeable_zone, ptr); - return; - } - if (__asan_mz_size(ptr)) { - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - asan_free(ptr, &stack); - } else { - // Let us just leak this memory for now. - Printf("mz_free(%p) -- attempting to free unallocated memory.\n" - "AddressSanitizer is ignoring this error on Mac OS now.\n", ptr); - print_zone_for_ptr(ptr); - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - stack.PrintStack(); - return; - } + free_common(zone, ptr); } void cf_free(void *ptr, void *info) { - if (!ptr) return; - malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr); - // For some reason Chromium calls mz_free() for pointers that belong to - // DefaultPurgeableMallocZone instead of asan_zone. We might want to - // fix this someday. - if (orig_zone == system_purgeable_zone) { - system_purgeable_zone->free(system_purgeable_zone, ptr); - return; - } - if (__asan_mz_size(ptr)) { - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - asan_free(ptr, &stack); - } else { - // Let us just leak this memory for now. - Printf("cf_free(%p) -- attempting to free unallocated memory.\n" - "AddressSanitizer is ignoring this error on Mac OS now.\n", ptr); - print_zone_for_ptr(ptr); - GET_STACK_TRACE_HERE_FOR_FREE(ptr); - stack.PrintStack(); - return; - } + free_common(info, ptr); } void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { @@ -184,20 +198,21 @@ void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { GET_STACK_TRACE_HERE_FOR_MALLOC; return asan_malloc(size, &stack); } else { - if (__asan_mz_size(ptr)) { + if (asan_mz_size(ptr)) { GET_STACK_TRACE_HERE_FOR_MALLOC; return asan_realloc(ptr, size, &stack); } else { // We can't recover from reallocating an unknown address, because // this would require reading at most |size| bytes from // potentially unaccessible memory. - Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" - "This is an unrecoverable problem, exiting now.\n", ptr); + AsanPrintf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" + "This is an unrecoverable problem, exiting now.\n", + ptr); print_zone_for_ptr(ptr); GET_STACK_TRACE_HERE_FOR_FREE(ptr); stack.PrintStack(); ShowStatsAndAbort(); - return NULL; // unreachable + return 0; // unreachable } } } @@ -207,27 +222,28 @@ void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) { GET_STACK_TRACE_HERE_FOR_MALLOC; return asan_malloc(size, &stack); } else { - if (__asan_mz_size(ptr)) { + if (asan_mz_size(ptr)) { GET_STACK_TRACE_HERE_FOR_MALLOC; return asan_realloc(ptr, size, &stack); } else { // We can't recover from reallocating an unknown address, because // this would require reading at most |size| bytes from // potentially unaccessible memory. - Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n" - "This is an unrecoverable problem, exiting now.\n", ptr); + AsanPrintf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n" + "This is an unrecoverable problem, exiting now.\n", + ptr); print_zone_for_ptr(ptr); GET_STACK_TRACE_HERE_FOR_FREE(ptr); stack.PrintStack(); ShowStatsAndAbort(); - return NULL; // unreachable + return 0; // unreachable } } } void mz_destroy(malloc_zone_t* zone) { // A no-op -- we will not be destroyed! - Printf("mz_destroy() called -- ignoring\n"); + AsanPrintf("mz_destroy() called -- ignoring\n"); } // from AvailabilityMacros.h #if defined(MAC_OS_X_VERSION_10_6) && \ @@ -279,11 +295,11 @@ void mi_log(malloc_zone_t *zone, void *address) { } void mi_force_lock(malloc_zone_t *zone) { - __asan_mz_force_lock(); + asan_mz_force_lock(); } void mi_force_unlock(malloc_zone_t *zone) { - __asan_mz_force_unlock(); + asan_mz_force_unlock(); } // This function is currently unused, and we build with -Werror. @@ -298,19 +314,38 @@ void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { } #endif +#if defined(MAC_OS_X_VERSION_10_6) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 boolean_t mi_zone_locked(malloc_zone_t *zone) { // UNIMPLEMENTED(); return false; } +#endif } // unnamed namespace -extern bool kCFUseCollectableAllocator; // is GC on? +extern int __CFRuntimeClassTableSize; namespace __asan { +void ReplaceCFAllocator() { + static CFAllocatorContext asan_context = { + /*version*/ 0, /*info*/ &asan_zone, + /*retain*/ 0, /*release*/ 0, + /*copyDescription*/0, + /*allocate*/ &cf_malloc, + /*reallocate*/ &cf_realloc, + /*deallocate*/ &cf_free, + /*preferredSize*/ 0 }; + if (!cf_asan) + cf_asan = CFAllocatorCreate(kCFAllocatorUseContext, &asan_context); + if (CFAllocatorGetDefault() != cf_asan) + CFAllocatorSetDefault(cf_asan); +} + void ReplaceSystemMalloc() { static malloc_introspection_t asan_introspection; - __asan::real_memset(&asan_introspection, 0, sizeof(asan_introspection)); + // Ok to use internal_memset, these places are not performance-critical. + internal_memset(&asan_introspection, 0, sizeof(asan_introspection)); asan_introspection.enumerator = &mi_enumerator; asan_introspection.good_size = &mi_good_size; @@ -320,8 +355,7 @@ void ReplaceSystemMalloc() { asan_introspection.force_lock = &mi_force_lock; asan_introspection.force_unlock = &mi_force_unlock; - static malloc_zone_t asan_zone; - __asan::real_memset(&asan_zone, 0, sizeof(malloc_zone_t)); + internal_memset(&asan_zone, 0, sizeof(malloc_zone_t)); // Start with a version 4 zone which is used for OS X 10.4 and 10.5. asan_zone.version = 4; @@ -333,8 +367,8 @@ void ReplaceSystemMalloc() { asan_zone.free = &mz_free; asan_zone.realloc = &mz_realloc; asan_zone.destroy = &mz_destroy; - asan_zone.batch_malloc = NULL; - asan_zone.batch_free = NULL; + asan_zone.batch_malloc = 0; + asan_zone.batch_free = 0; asan_zone.introspect = &asan_introspection; // from AvailabilityMacros.h @@ -371,18 +405,16 @@ void ReplaceSystemMalloc() { // Make sure the default allocator was replaced. CHECK(malloc_default_zone() == &asan_zone); - if (FLAG_replace_cfallocator) { - static CFAllocatorContext asan_context = - { /*version*/ 0, /*info*/ &asan_zone, - /*retain*/ NULL, /*release*/ NULL, - /*copyDescription*/NULL, - /*allocate*/ &cf_malloc, - /*reallocate*/ &cf_realloc, - /*deallocate*/ &cf_free, - /*preferredSize*/ NULL }; - CFAllocatorRef cf_asan = - CFAllocatorCreate(kCFAllocatorUseContext, &asan_context); - CFAllocatorSetDefault(cf_asan); + if (flags()->replace_cfallocator) { + // If __CFInitialize() hasn't been called yet, cf_asan will be created and + // installed as the default allocator after __CFInitialize() finishes (see + // the interceptor for __CFInitialize() above). Otherwise install cf_asan + // right now. On both Snow Leopard and Lion __CFInitialize() calls + // __CFAllocatorInitialize(), which initializes the _base._cfisa field of + // the default allocators we check here. + if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) { + ReplaceCFAllocator(); + } } } } // namespace __asan diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc new file mode 100644 index 000000000000..6c00e77caae8 --- /dev/null +++ b/lib/asan/asan_malloc_win.cc @@ -0,0 +1,141 @@ +//===-- asan_malloc_win.cc ------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// 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. +// +// Windows-specific malloc interception. +//===----------------------------------------------------------------------===// +#ifdef _WIN32 + +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_stack.h" + +#include "interception/interception.h" + +// ---------------------- Replacement functions ---------------- {{{1 +using namespace __asan; // NOLINT + +// FIXME: Simply defining functions with the same signature in *.obj +// files overrides the standard functions in *.lib +// This works well for simple helloworld-like tests but might need to be +// revisited in the future. + +extern "C" { +void free(void *ptr) { + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + return asan_free(ptr, &stack); +} + +void _free_dbg(void* ptr, int) { + free(ptr); +} + +void cfree(void *ptr) { + CHECK(!"cfree() should not be used on Windows?"); +} + +void *malloc(size_t size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc(size, &stack); +} + +void* _malloc_dbg(size_t size, int , const char*, int) { + return malloc(size); +} + +void *calloc(size_t nmemb, size_t size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { + return calloc(n, size); +} + +void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { + return calloc(nmemb, size); +} + +void *realloc(void *ptr, size_t size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_realloc(ptr, size, &stack); +} + +void *_realloc_dbg(void *ptr, size_t size, int) { + CHECK(!"_realloc_dbg should not exist!"); + return 0; +} + +void* _recalloc(void* p, size_t n, size_t elem_size) { + if (!p) + return calloc(n, elem_size); + const size_t size = n * elem_size; + if (elem_size != 0 && size / elem_size != n) + return 0; + return realloc(p, size); +} + +size_t _msize(void *ptr) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc_usable_size(ptr, &stack); +} + +int _CrtDbgReport(int, const char*, int, + const char*, const char*, ...) { + ShowStatsAndAbort(); +} + +int _CrtDbgReportW(int reportType, const wchar_t*, int, + const wchar_t*, const wchar_t*, ...) { + ShowStatsAndAbort(); +} + +int _CrtSetReportMode(int, int) { + return 0; +} +} // extern "C" + +using __interception::GetRealFunctionAddress; + +// We don't want to include "windows.h" in this file to avoid extra attributes +// set on malloc/free etc (e.g. dllimport), so declare a few things manually: +extern "C" int __stdcall VirtualProtect(void* addr, size_t size, + DWORD prot, DWORD *old_prot); +const int PAGE_EXECUTE_READWRITE = 0x40; + +namespace __asan { +void ReplaceSystemMalloc() { +#if defined(_DLL) +# ifdef _WIN64 +# error ReplaceSystemMalloc was not tested on x64 +# endif + char *crt_malloc; + if (GetRealFunctionAddress("malloc", (void**)&crt_malloc)) { + // Replace malloc in the CRT dll with a jump to our malloc. + DWORD old_prot, unused; + CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot)); + REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16); // just in case. + + ptrdiff_t jmp_offset = (char*)malloc - (char*)crt_malloc - 5; + crt_malloc[0] = 0xE9; // jmp, should be followed by an offset. + REAL(memcpy)(crt_malloc + 1, &jmp_offset, sizeof(jmp_offset)); + + CHECK(VirtualProtect(crt_malloc, 16, old_prot, &unused)); + + // FYI: FlushInstructionCache is needed on Itanium etc but not on x86/x64. + } + + // FIXME: investigate whether anything else is needed. +#endif +} +} // namespace __asan + +#endif // _WIN32 diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h index 63aba10a22f6..8e0c6ec5db20 100644 --- a/lib/asan/asan_mapping.h +++ b/lib/asan/asan_mapping.h @@ -20,26 +20,32 @@ // http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm #if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 -extern __attribute__((visibility("default"))) uintptr_t __asan_mapping_scale; -extern __attribute__((visibility("default"))) uintptr_t __asan_mapping_offset; -#define SHADOW_SCALE (__asan_mapping_scale) -#define SHADOW_OFFSET (__asan_mapping_offset) +extern __attribute__((visibility("default"))) uptr __asan_mapping_scale; +extern __attribute__((visibility("default"))) uptr __asan_mapping_offset; +# define SHADOW_SCALE (__asan_mapping_scale) +# define SHADOW_OFFSET (__asan_mapping_offset) #else -#define SHADOW_SCALE (3) -#if __WORDSIZE == 32 -#define SHADOW_OFFSET (1 << 29) -#else -#define SHADOW_OFFSET (1ULL << 44) -#endif +# ifdef ANDROID +# define SHADOW_SCALE (3) +# define SHADOW_OFFSET (0) +# else +# define SHADOW_SCALE (3) +# if __WORDSIZE == 32 +# define SHADOW_OFFSET (1 << 29) +# else +# define SHADOW_OFFSET (1ULL << 44) +# endif +# endif #endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET #define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE) #define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET)) +#define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE) #if __WORDSIZE == 64 - static const size_t kHighMemEnd = 0x00007fffffffffffUL; + static const uptr kHighMemEnd = 0x00007fffffffffffUL; #else // __WORDSIZE == 32 - static const size_t kHighMemEnd = 0xffffffff; + static const uptr kHighMemEnd = 0xffffffff; #endif // __WORDSIZE @@ -62,39 +68,55 @@ extern __attribute__((visibility("default"))) uintptr_t __asan_mapping_offset; namespace __asan { -static inline bool AddrIsInLowMem(uintptr_t a) { +static inline bool AddrIsInLowMem(uptr a) { return a < kLowMemEnd; } -static inline bool AddrIsInLowShadow(uintptr_t a) { +static inline bool AddrIsInLowShadow(uptr a) { return a >= kLowShadowBeg && a <= kLowShadowEnd; } -static inline bool AddrIsInHighMem(uintptr_t a) { +static inline bool AddrIsInHighMem(uptr a) { return a >= kHighMemBeg && a <= kHighMemEnd; } -static inline bool AddrIsInMem(uintptr_t a) { +static inline bool AddrIsInMem(uptr a) { return AddrIsInLowMem(a) || AddrIsInHighMem(a); } -static inline uintptr_t MemToShadow(uintptr_t p) { +static inline uptr MemToShadow(uptr p) { CHECK(AddrIsInMem(p)); return MEM_TO_SHADOW(p); } -static inline bool AddrIsInHighShadow(uintptr_t a) { +static inline bool AddrIsInHighShadow(uptr a) { return a >= kHighShadowBeg && a <= kHighMemEnd; } -static inline bool AddrIsInShadow(uintptr_t a) { +static inline bool AddrIsInShadow(uptr a) { return AddrIsInLowShadow(a) || AddrIsInHighShadow(a); } -static inline bool AddrIsAlignedByGranularity(uintptr_t a) { +static inline bool AddrIsInShadowGap(uptr a) { + return a >= kShadowGapBeg && a <= kShadowGapEnd; +} + +static inline bool AddrIsAlignedByGranularity(uptr a) { return (a & (SHADOW_GRANULARITY - 1)) == 0; } +static inline bool AddressIsPoisoned(uptr a) { + const uptr kAccessSize = 1; + u8 *shadow_address = (u8*)MemToShadow(a); + s8 shadow_value = *shadow_address; + if (shadow_value) { + u8 last_accessed_byte = (a & (SHADOW_GRANULARITY - 1)) + + kAccessSize - 1; + return (last_accessed_byte >= shadow_value); + } + return false; +} + } // namespace __asan #endif // ASAN_MAPPING_H diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc new file mode 100644 index 000000000000..4a7275842f08 --- /dev/null +++ b/lib/asan/asan_new_delete.cc @@ -0,0 +1,56 @@ +//===-- asan_interceptors.cc ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// 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. +// +// Interceptors for operators new and delete. +//===----------------------------------------------------------------------===// + +#include "asan_allocator.h" +#include "asan_internal.h" +#include "asan_stack.h" + +#include +#include + +namespace __asan { +// This function is a no-op. We need it to make sure that object file +// with our replacements will actually be loaded from static ASan +// run-time library at link-time. +void ReplaceOperatorsNewAndDelete() { } +} + +using namespace __asan; // NOLINT + +#define OPERATOR_NEW_BODY \ + GET_STACK_TRACE_HERE_FOR_MALLOC;\ + return asan_memalign(0, size, &stack); + +#ifdef ANDROID +void *operator new(size_t size) { OPERATOR_NEW_BODY; } +void *operator new[](size_t size) { OPERATOR_NEW_BODY; } +#else +void *operator new(size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; } +void *operator new[](size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; } +void *operator new(size_t size, std::nothrow_t const&) throw() +{ OPERATOR_NEW_BODY; } +void *operator new[](size_t size, std::nothrow_t const&) throw() +{ OPERATOR_NEW_BODY; } +#endif + +#define OPERATOR_DELETE_BODY \ + GET_STACK_TRACE_HERE_FOR_FREE(ptr);\ + asan_free(ptr, &stack); + +void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; } +void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; } +void operator delete(void *ptr, std::nothrow_t const&) throw() +{ OPERATOR_DELETE_BODY; } +void operator delete[](void *ptr, std::nothrow_t const&) throw() +{ OPERATOR_DELETE_BODY; } diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc index daa1ad68e46c..3b9d9f6795ca 100644 --- a/lib/asan/asan_poisoning.cc +++ b/lib/asan/asan_poisoning.cc @@ -1,4 +1,4 @@ -//===-- asan_poisoning.cc ---------------------------------------*- C++ -*-===// +//===-- asan_poisoning.cc -------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -19,21 +19,22 @@ namespace __asan { -void PoisonShadow(uintptr_t addr, size_t size, uint8_t value) { +void PoisonShadow(uptr addr, uptr size, u8 value) { CHECK(AddrIsAlignedByGranularity(addr)); CHECK(AddrIsAlignedByGranularity(addr + size)); - uintptr_t shadow_beg = MemToShadow(addr); - uintptr_t shadow_end = MemToShadow(addr + size); - real_memset((void*)shadow_beg, value, shadow_end - shadow_beg); + uptr shadow_beg = MemToShadow(addr); + uptr shadow_end = MemToShadow(addr + size); + CHECK(REAL(memset) != 0); + REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); } -void PoisonShadowPartialRightRedzone(uintptr_t addr, - uintptr_t size, - uintptr_t redzone_size, - uint8_t value) { +void PoisonShadowPartialRightRedzone(uptr addr, + uptr size, + uptr redzone_size, + u8 value) { CHECK(AddrIsAlignedByGranularity(addr)); - uint8_t *shadow = (uint8_t*)MemToShadow(addr); - for (uintptr_t i = 0; i < redzone_size; + 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 @@ -47,12 +48,12 @@ void PoisonShadowPartialRightRedzone(uintptr_t addr, struct ShadowSegmentEndpoint { - uint8_t *chunk; - int8_t offset; // in [0, SHADOW_GRANULARITY) - int8_t value; // = *chunk; + u8 *chunk; + s8 offset; // in [0, SHADOW_GRANULARITY) + s8 value; // = *chunk; - explicit ShadowSegmentEndpoint(uintptr_t address) { - chunk = (uint8_t*)MemToShadow(address); + explicit ShadowSegmentEndpoint(uptr address) { + chunk = (u8*)MemToShadow(address); offset = address & (SHADOW_GRANULARITY - 1); value = *chunk; } @@ -73,18 +74,19 @@ using namespace __asan; // NOLINT // at least [left, AlignDown(right)). // * if user asks to unpoison region [left, right), the program unpoisons // at most [AlignDown(left), right). -void __asan_poison_memory_region(void const volatile *addr, size_t size) { - if (!FLAG_allow_user_poisoning || size == 0) return; - uintptr_t beg_addr = (uintptr_t)addr; - uintptr_t end_addr = beg_addr + size; - if (FLAG_v >= 1) { - Printf("Trying to poison memory region [%p, %p)\n", beg_addr, end_addr); +void __asan_poison_memory_region(void const volatile *addr, uptr size) { + if (!flags()->allow_user_poisoning || size == 0) return; + uptr beg_addr = (uptr)addr; + uptr end_addr = beg_addr + size; + if (flags()->verbosity >= 1) { + Printf("Trying to poison memory region [%p, %p)\n", + (void*)beg_addr, (void*)end_addr); } ShadowSegmentEndpoint beg(beg_addr); ShadowSegmentEndpoint end(end_addr); if (beg.chunk == end.chunk) { CHECK(beg.offset < end.offset); - int8_t value = beg.value; + s8 value = beg.value; CHECK(value == end.value); // We can only poison memory if the byte in end.offset is unaddressable. // No need to re-poison memory if it is poisoned already. @@ -107,25 +109,26 @@ void __asan_poison_memory_region(void const volatile *addr, size_t size) { } beg.chunk++; } - real_memset(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk); + REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk); // Poison if byte in end.offset is unaddressable. if (end.value > 0 && end.value <= end.offset) { *end.chunk = kAsanUserPoisonedMemoryMagic; } } -void __asan_unpoison_memory_region(void const volatile *addr, size_t size) { - if (!FLAG_allow_user_poisoning || size == 0) return; - uintptr_t beg_addr = (uintptr_t)addr; - uintptr_t end_addr = beg_addr + size; - if (FLAG_v >= 1) { - Printf("Trying to unpoison memory region [%p, %p)\n", beg_addr, end_addr); +void __asan_unpoison_memory_region(void const volatile *addr, uptr size) { + if (!flags()->allow_user_poisoning || size == 0) return; + uptr beg_addr = (uptr)addr; + uptr end_addr = beg_addr + size; + if (flags()->verbosity >= 1) { + Printf("Trying to unpoison memory region [%p, %p)\n", + (void*)beg_addr, (void*)end_addr); } ShadowSegmentEndpoint beg(beg_addr); ShadowSegmentEndpoint end(end_addr); if (beg.chunk == end.chunk) { CHECK(beg.offset < end.offset); - int8_t value = beg.value; + s8 value = beg.value; CHECK(value == end.value); // We unpoison memory bytes up to enbytes up to end.offset if it is not // unpoisoned already. @@ -139,21 +142,12 @@ void __asan_unpoison_memory_region(void const volatile *addr, size_t size) { *beg.chunk = 0; beg.chunk++; } - real_memset(beg.chunk, 0, end.chunk - beg.chunk); + REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk); if (end.offset > 0 && end.value != 0) { *end.chunk = Max(end.value, end.offset); } } bool __asan_address_is_poisoned(void const volatile *addr) { - const size_t kAccessSize = 1; - uintptr_t address = (uintptr_t)addr; - uint8_t *shadow_address = (uint8_t*)MemToShadow(address); - int8_t shadow_value = *shadow_address; - if (shadow_value) { - uint8_t last_accessed_byte = (address & (SHADOW_GRANULARITY - 1)) - + kAccessSize - 1; - return (last_accessed_byte >= shadow_value); - } - return false; + return __asan::AddressIsPoisoned((uptr)addr); } diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc new file mode 100644 index 000000000000..061bb193034d --- /dev/null +++ b/lib/asan/asan_posix.cc @@ -0,0 +1,126 @@ +//===-- asan_linux.cc -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// 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. +// +// Posix-specific details. +//===----------------------------------------------------------------------===// +#if defined(__linux__) || defined(__APPLE__) + +#include "asan_internal.h" +#include "asan_interceptors.h" +#include "asan_mapping.h" +#include "asan_stack.h" +#include "asan_thread_registry.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" + +#include +#include +#include +#include +#include +#include + +static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough. + +namespace __asan { + +static void MaybeInstallSigaction(int signum, + void (*handler)(int, siginfo_t *, void *)) { + if (!AsanInterceptsSignal(signum)) + return; + struct sigaction sigact; + REAL(memset)(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = handler; + sigact.sa_flags = SA_SIGINFO; + if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; + CHECK(0 == REAL(sigaction)(signum, &sigact, 0)); + if (flags()->verbosity >= 1) { + Report("Installed the sigaction for signal %d\n", signum); + } +} + +static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) { + uptr addr = (uptr)siginfo->si_addr; + // Write the first message using the bullet-proof write. + if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die(); + uptr pc, sp, bp; + GetPcSpBp(context, &pc, &sp, &bp); + AsanReport("ERROR: AddressSanitizer crashed on unknown address %p" + " (pc %p sp %p bp %p T%d)\n", + (void*)addr, (void*)pc, (void*)sp, (void*)bp, + asanThreadRegistry().GetCurrentTidOrInvalid()); + AsanPrintf("AddressSanitizer can not provide additional info. ABORTING\n"); + GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp); + stack.PrintStack(); + ShowStatsAndAbort(); +} + +void SetAlternateSignalStack() { + stack_t altstack, oldstack; + CHECK(0 == sigaltstack(0, &oldstack)); + // If the alternate stack is already in place, do nothing. + if ((oldstack.ss_flags & SS_DISABLE) == 0) return; + // TODO(glider): the mapped stack should have the MAP_STACK flag in the + // future. It is not required by man 2 sigaltstack now (they're using + // malloc()). + void* base = MmapOrDie(kAltStackSize, __FUNCTION__); + altstack.ss_sp = base; + altstack.ss_flags = 0; + altstack.ss_size = kAltStackSize; + CHECK(0 == sigaltstack(&altstack, 0)); + if (flags()->verbosity > 0) { + Report("Alternative stack for T%d set: [%p,%p)\n", + asanThreadRegistry().GetCurrentTidOrInvalid(), + altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size); + } +} + +void UnsetAlternateSignalStack() { + stack_t altstack, oldstack; + altstack.ss_sp = 0; + altstack.ss_flags = SS_DISABLE; + altstack.ss_size = 0; + CHECK(0 == sigaltstack(&altstack, &oldstack)); + UnmapOrDie(oldstack.ss_sp, oldstack.ss_size); +} + +void InstallSignalHandlers() { + // Set the alternate signal stack for the main thread. + // This will cause SetAlternateSignalStack to be called twice, but the stack + // will be actually set only once. + if (flags()->use_sigaltstack) SetAlternateSignalStack(); + MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV); + MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV); +} + +// ---------------------- TSD ---------------- {{{1 + +static pthread_key_t tsd_key; +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)); +} + +void *AsanTSDGet() { + CHECK(tsd_key_inited); + return pthread_getspecific(tsd_key); +} + +void AsanTSDSet(void *tsd) { + CHECK(tsd_key_inited); + pthread_setspecific(tsd_key, tsd); +} + +} // namespace __asan + +#endif // __linux__ || __APPLE_ diff --git a/lib/asan/asan_printf.cc b/lib/asan/asan_printf.cc index a3d06ff67ad2..e1304f0fbe3f 100644 --- a/lib/asan/asan_printf.cc +++ b/lib/asan/asan_printf.cc @@ -1,4 +1,4 @@ -//===-- asan_printf.cc ------------------------------------------*- C++ -*-===// +//===-- asan_printf.cc ----------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -16,131 +16,19 @@ #include "asan_internal.h" #include "asan_interceptors.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_common.h" #include +#include + +namespace __sanitizer { +int VSNPrintf(char *buff, int buff_length, const char *format, va_list args); +} // namespace __sanitizer namespace __asan { -void RawWrite(const char *buffer) { - static const char *kRawWriteError = "RawWrite can't output requested buffer!"; - ssize_t length = (ssize_t)internal_strlen(buffer); - if (length != AsanWrite(2, buffer, length)) { - AsanWrite(2, kRawWriteError, internal_strlen(kRawWriteError)); - ASAN_DIE; - } -} - -static inline int AppendChar(char **buff, const char *buff_end, char c) { - if (*buff < buff_end) { - **buff = c; - (*buff)++; - } - return 1; -} - -// 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, uint64_t num, - uint8_t base, uint8_t minimal_num_length) { - size_t const kMaxLen = 30; - RAW_CHECK(base == 10 || base == 16); - RAW_CHECK(minimal_num_length < kMaxLen); - size_t num_buffer[kMaxLen]; - size_t pos = 0; - do { - RAW_CHECK_MSG(pos < kMaxLen, "appendNumber buffer overflow"); - num_buffer[pos++] = num % base; - num /= base; - } while (num > 0); - while (pos < minimal_num_length) num_buffer[pos++] = 0; - int result = 0; - while (pos-- > 0) { - size_t digit = num_buffer[pos]; - result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit - : 'a' + digit - 10); - } - return result; -} - -static inline int AppendSignedDecimal(char **buff, const char *buff_end, - int64_t num) { - int result = 0; - if (num < 0) { - result += AppendChar(buff, buff_end, '-'); - num = -num; - } - result += AppendUnsigned(buff, buff_end, (uint64_t)num, 10, 0); - return result; -} - -static inline int AppendString(char **buff, const char *buff_end, - const char *s) { - // Avoid library functions like stpcpy here. - RAW_CHECK(s); - int result = 0; - for (; *s; s++) { - result += AppendChar(buff, buff_end, *s); - } - return result; -} - -static inline int AppendPointer(char **buff, const char *buff_end, - uint64_t ptr_value) { - int result = 0; - result += AppendString(buff, buff_end, "0x"); - result += AppendUnsigned(buff, buff_end, ptr_value, 16, - (__WORDSIZE == 64) ? 12 : 8); - return result; -} - -static int VSNPrintf(char *buff, int buff_length, - const char *format, va_list args) { - static const char *kPrintfFormatsHelp = "Supported Printf formats: " - "%%[l]{d,u,x}; %%p; %%s"; - RAW_CHECK(format); - RAW_CHECK(buff_length > 0); - const char *buff_end = &buff[buff_length - 1]; - const char *cur = format; - int result = 0; - for (; *cur; cur++) { - if (*cur == '%') { - cur++; - bool have_l = (*cur == 'l'); - cur += have_l; - int64_t dval; - uint64_t uval, xval; - switch (*cur) { - case 'd': dval = have_l ? va_arg(args, intptr_t) - : va_arg(args, int); - result += AppendSignedDecimal(&buff, buff_end, dval); - break; - case 'u': uval = have_l ? va_arg(args, uintptr_t) - : va_arg(args, unsigned int); - result += AppendUnsigned(&buff, buff_end, uval, 10, 0); - break; - case 'x': xval = have_l ? va_arg(args, uintptr_t) - : va_arg(args, unsigned int); - result += AppendUnsigned(&buff, buff_end, xval, 16, 0); - break; - case 'p': RAW_CHECK_MSG(!have_l, kPrintfFormatsHelp); - result += AppendPointer(&buff, buff_end, - va_arg(args, uintptr_t)); - break; - case 's': RAW_CHECK_MSG(!have_l, kPrintfFormatsHelp); - result += AppendString(&buff, buff_end, va_arg(args, char*)); - break; - default: RAW_CHECK_MSG(false, kPrintfFormatsHelp); - } - } else { - result += AppendChar(&buff, buff_end, *cur); - } - } - RAW_CHECK(buff <= buff_end); - AppendChar(&buff, buff_end + 1, '\0'); - return result; -} - -void Printf(const char *format, ...) { +void AsanPrintf(const char *format, ...) { const int kLen = 1024 * 4; char buffer[kLen]; va_list args; @@ -149,25 +37,14 @@ void Printf(const char *format, ...) { va_end(args); RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n"); RawWrite(buffer); + AppendToErrorMessageBuffer(buffer); } -// Writes at most "length" symbols to "buffer" (including trailing '\0'). -// Returns the number of symbols that should have been written to buffer -// (not including trailing '\0'). Thus, the string is truncated -// iff return value is not less than "length". -int SNPrintf(char *buffer, size_t length, const char *format, ...) { - va_list args; - va_start(args, format); - int needed_length = VSNPrintf(buffer, length, format, args); - va_end(args); - return needed_length; -} - -// Like Printf, but prints the current PID before the output string. -void Report(const char *format, ...) { +// Like AsanPrintf, but prints the current PID before the output string. +void AsanReport(const char *format, ...) { const int kLen = 1024 * 4; char buffer[kLen]; - int needed_length = SNPrintf(buffer, kLen, "==%d== ", getpid()); + int needed_length = internal_snprintf(buffer, kLen, "==%d== ", GetPid()); RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); va_list args; va_start(args, format); @@ -176,6 +53,7 @@ void Report(const char *format, ...) { va_end(args); RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); RawWrite(buffer); + AppendToErrorMessageBuffer(buffer); } } // namespace __asan diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index c876f6d97f73..34324fa16d08 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -1,4 +1,4 @@ -//===-- asan_rtl.cc ---------------------------------------------*- C++ -*-===// +//===-- asan_rtl.cc -------------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -16,254 +16,276 @@ #include "asan_interface.h" #include "asan_internal.h" #include "asan_lock.h" -#include "asan_mac.h" #include "asan_mapping.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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef ANDROID -#include -#endif -#include -#include -#include -// must not include on Linux +namespace __sanitizer { +using namespace __asan; -namespace __asan { - -// -------------------------- Flags ------------------------- {{{1 -static const size_t kMallocContextSize = 30; -static int FLAG_atexit; -bool FLAG_fast_unwind = true; - -size_t FLAG_redzone; // power of two, >= 32 -size_t FLAG_quarantine_size; -int FLAG_demangle; -bool FLAG_symbolize; -int FLAG_v; -int FLAG_debug; -bool FLAG_poison_shadow; -int FLAG_report_globals; -size_t FLAG_malloc_context_size = kMallocContextSize; -uintptr_t FLAG_large_malloc; -bool FLAG_lazy_shadow; -bool FLAG_handle_segv; -bool FLAG_handle_sigill; -bool FLAG_replace_str; -bool FLAG_replace_intrin; -bool FLAG_replace_cfallocator; // Used on Mac only. -size_t FLAG_max_malloc_fill_size = 0; -bool FLAG_use_fake_stack; -int FLAG_exitcode = EXIT_FAILURE; -bool FLAG_allow_user_poisoning; - -// -------------------------- Globals --------------------- {{{1 -int asan_inited; -bool asan_init_is_running; - -// -------------------------- Interceptors ---------------- {{{1 -typedef int (*sigaction_f)(int signum, const struct sigaction *act, - struct sigaction *oldact); -typedef sig_t (*signal_f)(int signum, sig_t handler); -typedef void (*longjmp_f)(void *env, int val); -typedef longjmp_f _longjmp_f; -typedef longjmp_f siglongjmp_f; -typedef void (*__cxa_throw_f)(void *, void *, void *); -typedef int (*pthread_create_f)(pthread_t *thread, const pthread_attr_t *attr, - void *(*start_routine) (void *), void *arg); -#ifdef __APPLE__ -dispatch_async_f_f real_dispatch_async_f; -dispatch_sync_f_f real_dispatch_sync_f; -dispatch_after_f_f real_dispatch_after_f; -dispatch_barrier_async_f_f real_dispatch_barrier_async_f; -dispatch_group_async_f_f real_dispatch_group_async_f; -pthread_workqueue_additem_np_f real_pthread_workqueue_additem_np; -#endif - -sigaction_f real_sigaction; -signal_f real_signal; -longjmp_f real_longjmp; -_longjmp_f real__longjmp; -siglongjmp_f real_siglongjmp; -__cxa_throw_f real___cxa_throw; -pthread_create_f real_pthread_create; - -// -------------------------- Misc ---------------- {{{1 -void ShowStatsAndAbort() { - __asan_print_accumulated_stats(); - ASAN_DIE; -} - -static void PrintBytes(const char *before, uintptr_t *a) { - uint8_t *bytes = (uint8_t*)a; - size_t byte_num = (__WORDSIZE) / 8; - Printf("%s%p:", before, (uintptr_t)a); - for (size_t i = 0; i < byte_num; i++) { - Printf(" %lx%lx", bytes[i] >> 4, bytes[i] & 15); +void Die() { + static atomic_uint32_t num_calls; + if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { + // Don't die twice - run a busy loop. + while (1) { } } - Printf("\n"); -} - -// Opens the file 'file_name" and reads up to 'max_len' bytes. -// The resulting buffer is mmaped and stored in '*buff'. -// Returns the number of read bytes or -1 if file can not be opened. -static ssize_t ReadFileToBuffer(const char *file_name, char **buff, - size_t max_len) { - const size_t kMinFileLen = kPageSize; - ssize_t read_len = -1; - *buff = 0; - size_t maped_size = 0; - // The files we usually open are not seekable, so try different buffer sizes. - for (size_t size = kMinFileLen; size <= max_len; size *= 2) { - int fd = AsanOpenReadonly(file_name); - if (fd < 0) return -1; - AsanUnmapOrDie(*buff, maped_size); - maped_size = size; - *buff = (char*)AsanMmapSomewhereOrDie(size, __FUNCTION__); - read_len = AsanRead(fd, *buff, size); - AsanClose(fd); - if (read_len < size) // We've read the whole file. - break; + if (flags()->sleep_before_dying) { + Report("Sleeping for %zd second(s)\n", flags()->sleep_before_dying); + SleepForSeconds(flags()->sleep_before_dying); } - return read_len; + if (flags()->unmap_shadow_on_exit) + UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); + if (death_callback) + death_callback(); + if (flags()->abort_on_error) + Abort(); + Exit(flags()->exitcode); } -// Like getenv, but reads env directly from /proc and does not use libc. -// This function should be called first inside __asan_init. -static const char* GetEnvFromProcSelfEnviron(const char* name) { - static char *environ; - static ssize_t len; - static bool inited; - if (!inited) { - inited = true; - len = ReadFileToBuffer("/proc/self/environ", &environ, 1 << 20); - } - if (!environ || len <= 0) return NULL; - size_t namelen = internal_strlen(name); - const char *p = environ; - while (*p != '\0') { // will happen at the \0\0 that terminates the buffer - // proc file has the format NAME=value\0NAME=value\0NAME=value\0... - const char* endp = - (char*)internal_memchr(p, '\0', len - (p - environ)); - if (endp == NULL) // this entry isn't NUL terminated - return NULL; - else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match. - return p + namelen + 1; // point after = - p = endp + 1; - } - return NULL; // Not found. -} - -// ---------------------- Thread ------------------------- {{{1 -static void *asan_thread_start(void *arg) { - AsanThread *t= (AsanThread*)arg; - asanThreadRegistry().SetCurrent(t); - return t->ThreadStart(); -} - -// ---------------------- mmap -------------------- {{{1 -void OutOfMemoryMessageAndDie(const char *mem_type, size_t size) { - Report("ERROR: AddressSanitizer failed to allocate " - "0x%lx (%ld) bytes of %s\n", - size, size, mem_type); +void CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { + AsanReport("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", + file, line, cond, (uptr)v1, (uptr)v2); PRINT_CURRENT_STACK(); ShowStatsAndAbort(); } +} // namespace __sanitizer + +namespace __asan { + +// -------------------------- Flags ------------------------- {{{1 +static const int kMallocContextSize = 30; + +static Flags asan_flags; + +Flags *flags() { + return &asan_flags; +} + +static void ParseFlagsFromString(Flags *f, const char *str) { + 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(IsPowerOfTwo(f->redzone)); + + ParseFlag(str, &f->debug, "debug"); + ParseFlag(str, &f->report_globals, "report_globals"); + ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); + CHECK(f->malloc_context_size <= kMallocContextSize); + + ParseFlag(str, &f->replace_str, "replace_str"); + ParseFlag(str, &f->replace_intrin, "replace_intrin"); + ParseFlag(str, &f->replace_cfallocator, "replace_cfallocator"); + ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free"); + ParseFlag(str, &f->use_fake_stack, "use_fake_stack"); + ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size"); + 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->use_sigaltstack, "use_sigaltstack"); + ParseFlag(str, &f->check_malloc_usable_size, "check_malloc_usable_size"); + ParseFlag(str, &f->unmap_shadow_on_exit, "unmap_shadow_on_exit"); + ParseFlag(str, &f->abort_on_error, "abort_on_error"); + ParseFlag(str, &f->atexit, "atexit"); + ParseFlag(str, &f->disable_core, "disable_core"); +} + +extern "C" { +const char* WEAK __asan_default_options() { return ""; } +} // extern "C" + +void InitializeFlags(Flags *f, const char *env) { + internal_memset(f, 0, sizeof(*f)); + + f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 24 : 1UL << 28; + f->symbolize = false; + f->verbosity = 0; + f->redzone = (ASAN_LOW_MEMORY) ? 64 : 128; + f->debug = false; + f->report_globals = 1; + f->malloc_context_size = kMallocContextSize; + f->replace_str = true; + f->replace_intrin = true; + f->replace_cfallocator = true; + f->mac_ignore_invalid_free = false; + f->use_fake_stack = true; + f->max_malloc_fill_size = 0; + f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE; + f->allow_user_poisoning = true; + f->sleep_before_dying = 0; + f->handle_segv = ASAN_NEEDS_SEGV; + f->use_sigaltstack = false; + f->check_malloc_usable_size = true; + f->unmap_shadow_on_exit = false; + f->abort_on_error = false; + f->atexit = false; + f->disable_core = (__WORDSIZE == 64); + + // Override from user-specified string. + ParseFlagsFromString(f, __asan_default_options()); + if (flags()->verbosity) { + Report("Using the defaults from __asan_default_options: %s\n", + __asan_default_options()); + } + + // Override from command line. + ParseFlagsFromString(f, env); +} + +// -------------------------- Globals --------------------- {{{1 +int asan_inited; +bool asan_init_is_running; +void (*death_callback)(void); +static void (*error_report_callback)(const char*); +char *error_message_buffer = 0; +uptr error_message_buffer_pos = 0; +uptr error_message_buffer_size = 0; + +// -------------------------- Misc ---------------- {{{1 +void ShowStatsAndAbort() { + __asan_print_accumulated_stats(); + Die(); +} + +static void PrintBytes(const char *before, uptr *a) { + u8 *bytes = (u8*)a; + uptr byte_num = (__WORDSIZE) / 8; + AsanPrintf("%s%p:", before, (void*)a); + for (uptr i = 0; i < byte_num; i++) { + AsanPrintf(" %x%x", bytes[i] >> 4, bytes[i] & 15); + } + AsanPrintf("\n"); +} + +void AppendToErrorMessageBuffer(const char *buffer) { + if (error_message_buffer) { + uptr length = internal_strlen(buffer); + CHECK_GE(error_message_buffer_size, error_message_buffer_pos); + uptr remaining = error_message_buffer_size - error_message_buffer_pos; + internal_strncpy(error_message_buffer + error_message_buffer_pos, + buffer, remaining); + error_message_buffer[error_message_buffer_size - 1] = '\0'; + // FIXME: reallocate the buffer instead of truncating the message. + error_message_buffer_pos += remaining > length ? length : remaining; + } +} + +// ---------------------- mmap -------------------- {{{1 // Reserve memory range [beg, end]. -static void ReserveShadowMemoryRange(uintptr_t beg, uintptr_t end) { +static void ReserveShadowMemoryRange(uptr beg, uptr end) { CHECK((beg % kPageSize) == 0); CHECK(((end + 1) % kPageSize) == 0); - size_t size = end - beg + 1; - void *res = AsanMmapFixedNoReserve(beg, size); + uptr size = end - beg + 1; + void *res = MmapFixedNoReserve(beg, size); CHECK(res == (void*)beg && "ReserveShadowMemoryRange failed"); } // ---------------------- LowLevelAllocator ------------- {{{1 -void *LowLevelAllocator::Allocate(size_t size) { +void *LowLevelAllocator::Allocate(uptr size) { CHECK((size & (size - 1)) == 0 && "size must be a power of two"); - if (allocated_end_ - allocated_current_ < size) { - size_t size_to_allocate = Max(size, kPageSize); + if (allocated_end_ - allocated_current_ < (sptr)size) { + uptr size_to_allocate = Max(size, kPageSize); allocated_current_ = - (char*)AsanMmapSomewhereOrDie(size_to_allocate, __FUNCTION__); + (char*)MmapOrDie(size_to_allocate, __FUNCTION__); allocated_end_ = allocated_current_ + size_to_allocate; - PoisonShadow((uintptr_t)allocated_current_, size_to_allocate, + PoisonShadow((uptr)allocated_current_, size_to_allocate, kAsanInternalHeapMagic); } - CHECK(allocated_end_ - allocated_current_ >= size); + CHECK(allocated_end_ - allocated_current_ >= (sptr)size); void *res = allocated_current_; allocated_current_ += size; return res; } // ---------------------- DescribeAddress -------------------- {{{1 -static bool DescribeStackAddress(uintptr_t addr, uintptr_t access_size) { +static bool DescribeStackAddress(uptr addr, uptr access_size) { AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr); if (!t) return false; - const intptr_t kBufSize = 4095; + const sptr kBufSize = 4095; char buf[kBufSize]; - uintptr_t offset = 0; + uptr offset = 0; const char *frame_descr = t->GetFrameNameByAddr(addr, &offset); // This string is created by the compiler and has the following form: // "FunctioName 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 = real_strchr(frame_descr, ' '); + const char *name_end = internal_strchr(frame_descr, ' '); CHECK(name_end); buf[0] = 0; - strncat(buf, frame_descr, - Min(kBufSize, static_cast(name_end - frame_descr))); - Printf("Address %p is located at offset %ld " - "in frame <%s> of T%d's stack:\n", - addr, offset, buf, t->tid()); + internal_strncat(buf, frame_descr, + Min(kBufSize, + static_cast(name_end - frame_descr))); + AsanPrintf("Address %p is located at offset %zu " + "in frame <%s> of T%d's stack:\n", + (void*)addr, offset, buf, t->tid()); // Report the number of stack objects. char *p; - size_t n_objects = strtol(name_end, &p, 10); + uptr n_objects = internal_simple_strtoll(name_end, &p, 10); CHECK(n_objects > 0); - Printf(" This frame has %ld object(s):\n", n_objects); + AsanPrintf(" This frame has %zu object(s):\n", n_objects); // Report all objects in this frame. - for (size_t i = 0; i < n_objects; i++) { - size_t beg, size; - intptr_t len; - beg = strtol(p, &p, 10); - size = strtol(p, &p, 10); - len = strtol(p, &p, 10); + 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 != ' ') { - Printf("AddressSanitizer can't parse the stack frame descriptor: |%s|\n", - frame_descr); + AsanPrintf("AddressSanitizer can't parse the stack frame " + "descriptor: |%s|\n", frame_descr); break; } p++; buf[0] = 0; - strncat(buf, p, Min(kBufSize, len)); + internal_strncat(buf, p, Min(kBufSize, len)); p += len; - Printf(" [%ld, %ld) '%s'\n", beg, beg + size, buf); + AsanPrintf(" [%zu, %zu) '%s'\n", beg, beg + size, buf); } - Printf("HINT: this may be a false positive if your program uses " - "some custom stack unwind mechanism\n" - " (longjmp and C++ exceptions *are* supported)\n"); + AsanPrintf("HINT: this may be a false positive if your program uses " + "some custom stack unwind mechanism\n" + " (longjmp and C++ exceptions *are* supported)\n"); t->summary()->Announce(); return true; } -__attribute__((noinline)) -static void DescribeAddress(uintptr_t addr, uintptr_t access_size) { +static bool DescribeAddrIfShadow(uptr addr) { + if (AddrIsInMem(addr)) + return false; + static const char kAddrInShadowReport[] = + "Address %p is located in the %s.\n"; + if (AddrIsInShadowGap(addr)) { + AsanPrintf(kAddrInShadowReport, addr, "shadow gap area"); + return true; + } + if (AddrIsInHighShadow(addr)) { + AsanPrintf(kAddrInShadowReport, addr, "high shadow area"); + return true; + } + if (AddrIsInLowShadow(addr)) { + AsanPrintf(kAddrInShadowReport, addr, "low shadow area"); + return true; + } + + CHECK(0); // Unreachable. + return false; +} + +static NOINLINE void DescribeAddress(uptr addr, uptr access_size) { + // Check if this is shadow or shadow gap. + if (DescribeAddrIfShadow(addr)) + return; + + CHECK(AddrIsInMem(addr)); + // Check if this is a global. if (DescribeAddrIfGlobal(addr)) return; @@ -276,92 +298,12 @@ static void DescribeAddress(uintptr_t addr, uintptr_t access_size) { } // -------------------------- Run-time entry ------------------- {{{1 -void GetPcSpBpAx(void *context, - uintptr_t *pc, uintptr_t *sp, uintptr_t *bp, uintptr_t *ax) { -#ifndef ANDROID - ucontext_t *ucontext = (ucontext_t*)context; -#endif -#ifdef __APPLE__ -# if __WORDSIZE == 64 - *pc = ucontext->uc_mcontext->__ss.__rip; - *bp = ucontext->uc_mcontext->__ss.__rbp; - *sp = ucontext->uc_mcontext->__ss.__rsp; - *ax = ucontext->uc_mcontext->__ss.__rax; -# else - *pc = ucontext->uc_mcontext->__ss.__eip; - *bp = ucontext->uc_mcontext->__ss.__ebp; - *sp = ucontext->uc_mcontext->__ss.__esp; - *ax = ucontext->uc_mcontext->__ss.__eax; -# endif // __WORDSIZE -#else // assume linux -# if defined (ANDROID) - *pc = *sp = *bp = *ax = 0; -# elif defined(__arm__) - *pc = ucontext->uc_mcontext.arm_pc; - *bp = ucontext->uc_mcontext.arm_fp; - *sp = ucontext->uc_mcontext.arm_sp; - *ax = ucontext->uc_mcontext.arm_r0; -# elif __WORDSIZE == 64 - *pc = ucontext->uc_mcontext.gregs[REG_RIP]; - *bp = ucontext->uc_mcontext.gregs[REG_RBP]; - *sp = ucontext->uc_mcontext.gregs[REG_RSP]; - *ax = ucontext->uc_mcontext.gregs[REG_RAX]; -# else - *pc = ucontext->uc_mcontext.gregs[REG_EIP]; - *bp = ucontext->uc_mcontext.gregs[REG_EBP]; - *sp = ucontext->uc_mcontext.gregs[REG_ESP]; - *ax = ucontext->uc_mcontext.gregs[REG_EAX]; -# endif // __WORDSIZE -#endif -} - -static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) { - uintptr_t addr = (uintptr_t)siginfo->si_addr; - if (AddrIsInShadow(addr) && FLAG_lazy_shadow) { - // We traped on access to a shadow address. Just map a large chunk around - // this address. - const uintptr_t chunk_size = kPageSize << 10; // 4M - uintptr_t chunk = addr & ~(chunk_size - 1); - AsanMmapFixedReserve(chunk, chunk_size); - return; - } - // Write the first message using the bullet-proof write. - if (13 != AsanWrite(2, "ASAN:SIGSEGV\n", 13)) ASAN_DIE; - uintptr_t pc, sp, bp, ax; - GetPcSpBpAx(context, &pc, &sp, &bp, &ax); - Report("ERROR: AddressSanitizer crashed on unknown address %p" - " (pc %p sp %p bp %p ax %p T%d)\n", - addr, pc, sp, bp, ax, - asanThreadRegistry().GetCurrentTidOrMinusOne()); - Printf("AddressSanitizer can not provide additional info. ABORTING\n"); - GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, false, pc, bp); - stack.PrintStack(); - ShowStatsAndAbort(); -} - -static void ASAN_OnSIGILL(int, siginfo_t *siginfo, void *context) { - // Write the first message using the bullet-proof write. - if (12 != AsanWrite(2, "ASAN:SIGILL\n", 12)) ASAN_DIE; - uintptr_t pc, sp, bp, ax; - GetPcSpBpAx(context, &pc, &sp, &bp, &ax); - - uintptr_t addr = ax; - - uint8_t *insn = (uint8_t*)pc; - CHECK(insn[0] == 0x0f && insn[1] == 0x0b); // ud2 - unsigned access_size_and_type = insn[2] - 0x50; - CHECK(access_size_and_type < 16); - bool is_write = access_size_and_type & 8; - int access_size = 1 << (access_size_and_type & 7); - __asan_report_error(pc, bp, sp, addr, is_write, access_size); -} - // exported functions #define ASAN_REPORT_ERROR(type, is_write, size) \ -extern "C" void __asan_report_ ## type ## size(uintptr_t addr) \ - __attribute__((visibility("default"))) __attribute__((noinline)); \ -extern "C" void __asan_report_ ## type ## size(uintptr_t addr) { \ - GET_BP_PC_SP; \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## size(uptr addr); \ +void __asan_report_ ## type ## size(uptr addr) { \ + GET_CALLER_PC_BP_SP; \ __asan_report_error(pc, bp, sp, addr, is_write, size); \ } @@ -381,210 +323,85 @@ ASAN_REPORT_ERROR(store, true, 16) // dynamic libraries access the symbol even if it is not used by the executable // itself. This should help if the build system is removing dead code at link // time. -static void force_interface_symbols() { +static NOINLINE void force_interface_symbols() { volatile int fake_condition = 0; // prevent dead condition elimination. if (fake_condition) { - __asan_report_load1(NULL); - __asan_report_load2(NULL); - __asan_report_load4(NULL); - __asan_report_load8(NULL); - __asan_report_load16(NULL); - __asan_report_store1(NULL); - __asan_report_store2(NULL); - __asan_report_store4(NULL); - __asan_report_store8(NULL); - __asan_report_store16(NULL); - __asan_register_global(0, 0, NULL); - __asan_register_globals(NULL, 0); - __asan_unregister_globals(NULL, 0); + __asan_report_load1(0); + __asan_report_load2(0); + __asan_report_load4(0); + __asan_report_load8(0); + __asan_report_load16(0); + __asan_report_store1(0); + __asan_report_store2(0); + __asan_report_store4(0); + __asan_report_store8(0); + __asan_report_store16(0); + __asan_register_global(0, 0, 0); + __asan_register_globals(0, 0); + __asan_unregister_globals(0, 0); + __asan_set_death_callback(0); + __asan_set_error_report_callback(0); + __asan_handle_no_return(); } } // -------------------------- Init ------------------- {{{1 -static int64_t IntFlagValue(const char *flags, const char *flag, - int64_t default_val) { - if (!flags) return default_val; - const char *str = strstr(flags, flag); - if (!str) return default_val; - return atoll(str + internal_strlen(flag)); -} - static void asan_atexit() { - Printf("AddressSanitizer exit stats:\n"); + AsanPrintf("AddressSanitizer exit stats:\n"); __asan_print_accumulated_stats(); } -void CheckFailed(const char *cond, const char *file, int line) { - Report("CHECK failed: %s at %s:%d, pthread_self=%p\n", - cond, file, line, pthread_self()); - PRINT_CURRENT_STACK(); - ShowStatsAndAbort(); -} - } // namespace __asan -// -------------------------- Interceptors ------------------- {{{1 +// ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT -#define OPERATOR_NEW_BODY \ - GET_STACK_TRACE_HERE_FOR_MALLOC;\ - return asan_memalign(0, size, &stack); - -#ifdef ANDROID -void *operator new(size_t size) { OPERATOR_NEW_BODY; } -void *operator new[](size_t size) { OPERATOR_NEW_BODY; } -#else -void *operator new(size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; } -void *operator new[](size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; } -void *operator new(size_t size, std::nothrow_t const&) throw() -{ OPERATOR_NEW_BODY; } -void *operator new[](size_t size, std::nothrow_t const&) throw() -{ OPERATOR_NEW_BODY; } -#endif - -#define OPERATOR_DELETE_BODY \ - GET_STACK_TRACE_HERE_FOR_FREE(ptr);\ - asan_free(ptr, &stack); - -void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; } -void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; } -void operator delete(void *ptr, std::nothrow_t const&) throw() -{ OPERATOR_DELETE_BODY; } -void operator delete[](void *ptr, std::nothrow_t const&) throw() -{ OPERATOR_DELETE_BODY;} - -extern "C" -#ifndef __APPLE__ -__attribute__((visibility("default"))) -#endif -int WRAP(pthread_create)(pthread_t *thread, const pthread_attr_t *attr, - void *(*start_routine) (void *), void *arg) { - GET_STACK_TRACE_HERE(kStackTraceMax, /*fast_unwind*/false); - AsanThread *t = (AsanThread*)asan_malloc(sizeof(AsanThread), &stack); - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); - CHECK(curr_thread || asanThreadRegistry().IsCurrentThreadDying()); - new(t) AsanThread(asanThreadRegistry().GetCurrentTidOrMinusOne(), - start_routine, arg, &stack); - return real_pthread_create(thread, attr, asan_thread_start, t); -} - -static bool MySignal(int signum) { - if (FLAG_handle_sigill && signum == SIGILL) return true; - if (FLAG_handle_segv && signum == SIGSEGV) return true; -#ifdef __APPLE__ - if (FLAG_handle_segv && signum == SIGBUS) return true; -#endif - return false; -} - -static void MaybeInstallSigaction(int signum, - void (*handler)(int, siginfo_t *, void *)) { - if (!MySignal(signum)) - return; - struct sigaction sigact; - real_memset(&sigact, 0, sizeof(sigact)); - sigact.sa_sigaction = handler; - sigact.sa_flags = SA_SIGINFO; - CHECK(0 == real_sigaction(signum, &sigact, 0)); -} - -extern "C" -sig_t WRAP(signal)(int signum, sig_t handler) { - if (!MySignal(signum)) { - return real_signal(signum, handler); - } - return NULL; -} - -extern "C" -int WRAP(sigaction)(int signum, const struct sigaction *act, - struct sigaction *oldact) { - if (!MySignal(signum)) { - return real_sigaction(signum, act, oldact); - } - return 0; -} - - -static void UnpoisonStackFromHereToTop() { - int local_stack; - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); - CHECK(curr_thread); - uintptr_t top = curr_thread->stack_top(); - uintptr_t bottom = ((uintptr_t)&local_stack - kPageSize) & ~(kPageSize-1); - PoisonShadow(bottom, top - bottom, 0); -} - -extern "C" void WRAP(longjmp)(void *env, int val) { - UnpoisonStackFromHereToTop(); - real_longjmp(env, val); -} - -extern "C" void WRAP(_longjmp)(void *env, int val) { - UnpoisonStackFromHereToTop(); - real__longjmp(env, val); -} - -extern "C" void WRAP(siglongjmp)(void *env, int val) { - UnpoisonStackFromHereToTop(); - real_siglongjmp(env, val); -} - -extern "C" void __cxa_throw(void *a, void *b, void *c); - -#if ASAN_HAS_EXCEPTIONS == 1 -extern "C" void WRAP(__cxa_throw)(void *a, void *b, void *c) { - CHECK(&real___cxa_throw); - UnpoisonStackFromHereToTop(); - real___cxa_throw(a, b, c); -} -#endif - -extern "C" { -// intercept mlock and friends. -// Since asan maps 16T of RAM, mlock is completely unfriendly to asan. -// All functions return 0 (success). -static void MlockIsUnsupported() { - static bool printed = 0; - if (printed) return; - printed = true; - Printf("INFO: AddressSanitizer ignores mlock/mlockall/munlock/munlockall\n"); -} -int mlock(const void *addr, size_t len) { - MlockIsUnsupported(); - return 0; -} -int munlock(const void *addr, size_t len) { - MlockIsUnsupported(); - return 0; -} -int mlockall(int flags) { - MlockIsUnsupported(); - return 0; -} -int munlockall(void) { - MlockIsUnsupported(); - return 0; -} -} // extern "C" - -// ---------------------- Interface ---------------- {{{1 int __asan_set_error_exit_code(int exit_code) { - int old = FLAG_exitcode; - FLAG_exitcode = exit_code; + int old = flags()->exitcode; + flags()->exitcode = exit_code; return old; } -void __asan_report_error(uintptr_t pc, uintptr_t bp, uintptr_t sp, - uintptr_t addr, bool is_write, size_t access_size) { - // Do not print more than one report, otherwise they will mix up. - static int num_calls = 0; - if (AtomicInc(&num_calls) > 1) return; +void NOINLINE __asan_handle_no_return() { + int local_stack; + AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); + CHECK(curr_thread); + uptr top = curr_thread->stack_top(); + uptr bottom = ((uptr)&local_stack - kPageSize) & ~(kPageSize-1); + PoisonShadow(bottom, top - bottom, 0); +} - Printf("=================================================================\n"); +void NOINLINE __asan_set_death_callback(void (*callback)(void)) { + death_callback = callback; +} + +void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { + error_report_callback = callback; + if (callback) { + error_message_buffer_size = 1 << 16; + error_message_buffer = + (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__); + error_message_buffer_pos = 0; + } +} + +void __asan_report_error(uptr pc, uptr bp, uptr sp, + uptr addr, bool is_write, uptr access_size) { + static atomic_uint32_t num_calls; + if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { + // Do not print more than one report, otherwise they will mix up. + // We can not return here because the function is marked as never-return. + AsanPrintf("AddressSanitizer: while reporting a bug found another one." + "Ignoring.\n"); + SleepForSeconds(5); + Die(); + } + + AsanPrintf("====================================================" + "=============\n"); const char *bug_descr = "unknown-crash"; if (AddrIsInMem(addr)) { - uint8_t *shadow_addr = (uint8_t*)MemToShadow(addr); + u8 *shadow_addr = (u8*)MemToShadow(addr); // If we are accessing 16 bytes, look at the second shadow byte. if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) shadow_addr++; @@ -620,7 +437,7 @@ void __asan_report_error(uintptr_t pc, uintptr_t bp, uintptr_t sp, } AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); - int curr_tid = asanThreadRegistry().GetCurrentTidOrMinusOne(); + u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); if (curr_thread) { // We started reporting an error message. Stop using the fake stack @@ -628,47 +445,49 @@ void __asan_report_error(uintptr_t pc, uintptr_t bp, uintptr_t sp, curr_thread->fake_stack().StopUsingFakeStack(); } - Report("ERROR: AddressSanitizer %s on address " - "%p at pc 0x%lx bp 0x%lx sp 0x%lx\n", - bug_descr, addr, pc, bp, sp); + AsanReport("ERROR: AddressSanitizer %s on address " + "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n", + bug_descr, (void*)addr, pc, bp, sp); - Printf("%s of size %d at %p thread T%d\n", - access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", - access_size, addr, curr_tid); + AsanPrintf("%s of size %zu at %p thread T%d\n", + access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", + access_size, (void*)addr, curr_tid); - if (FLAG_debug) { - PrintBytes("PC: ", (uintptr_t*)pc); + if (flags()->debug) { + PrintBytes("PC: ", (uptr*)pc); } - GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, - false, // FLAG_fast_unwind, - pc, bp); + GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp); stack.PrintStack(); - CHECK(AddrIsInMem(addr)); - DescribeAddress(addr, access_size); - uintptr_t shadow_addr = MemToShadow(addr); - Report("ABORTING\n"); - __asan_print_accumulated_stats(); - Printf("Shadow byte and word:\n"); - Printf(" %p: %x\n", shadow_addr, *(unsigned char*)shadow_addr); - uintptr_t aligned_shadow = shadow_addr & ~(kWordSize - 1); - PrintBytes(" ", (uintptr_t*)(aligned_shadow)); - Printf("More shadow bytes:\n"); - PrintBytes(" ", (uintptr_t*)(aligned_shadow-4*kWordSize)); - PrintBytes(" ", (uintptr_t*)(aligned_shadow-3*kWordSize)); - PrintBytes(" ", (uintptr_t*)(aligned_shadow-2*kWordSize)); - PrintBytes(" ", (uintptr_t*)(aligned_shadow-1*kWordSize)); - PrintBytes("=>", (uintptr_t*)(aligned_shadow+0*kWordSize)); - PrintBytes(" ", (uintptr_t*)(aligned_shadow+1*kWordSize)); - PrintBytes(" ", (uintptr_t*)(aligned_shadow+2*kWordSize)); - PrintBytes(" ", (uintptr_t*)(aligned_shadow+3*kWordSize)); - PrintBytes(" ", (uintptr_t*)(aligned_shadow+4*kWordSize)); - ASAN_DIE; + if (AddrIsInMem(addr)) { + uptr shadow_addr = MemToShadow(addr); + AsanReport("ABORTING\n"); + __asan_print_accumulated_stats(); + AsanPrintf("Shadow byte and word:\n"); + AsanPrintf(" %p: %x\n", (void*)shadow_addr, *(unsigned char*)shadow_addr); + uptr aligned_shadow = shadow_addr & ~(kWordSize - 1); + PrintBytes(" ", (uptr*)(aligned_shadow)); + AsanPrintf("More shadow bytes:\n"); + PrintBytes(" ", (uptr*)(aligned_shadow-4*kWordSize)); + PrintBytes(" ", (uptr*)(aligned_shadow-3*kWordSize)); + PrintBytes(" ", (uptr*)(aligned_shadow-2*kWordSize)); + PrintBytes(" ", (uptr*)(aligned_shadow-1*kWordSize)); + PrintBytes("=>", (uptr*)(aligned_shadow+0*kWordSize)); + PrintBytes(" ", (uptr*)(aligned_shadow+1*kWordSize)); + PrintBytes(" ", (uptr*)(aligned_shadow+2*kWordSize)); + PrintBytes(" ", (uptr*)(aligned_shadow+3*kWordSize)); + PrintBytes(" ", (uptr*)(aligned_shadow+4*kWordSize)); + } + if (error_report_callback) { + error_report_callback(error_message_buffer); + } + Die(); } + void __asan_init() { if (asan_inited) return; asan_init_is_running = true; @@ -676,125 +495,75 @@ void __asan_init() { // Make sure we are not statically linked. AsanDoesNotSupportStaticLinkage(); - // flags - const char *options = GetEnvFromProcSelfEnviron("ASAN_OPTIONS"); - FLAG_malloc_context_size = - IntFlagValue(options, "malloc_context_size=", kMallocContextSize); - CHECK(FLAG_malloc_context_size <= kMallocContextSize); + // Initialize flags. + const char *options = GetEnv("ASAN_OPTIONS"); + InitializeFlags(flags(), options); - FLAG_max_malloc_fill_size = - IntFlagValue(options, "max_malloc_fill_size=", 0); - - FLAG_v = IntFlagValue(options, "verbosity=", 0); - - FLAG_redzone = IntFlagValue(options, "redzone=", 128); - CHECK(FLAG_redzone >= 32); - CHECK((FLAG_redzone & (FLAG_redzone - 1)) == 0); - - FLAG_atexit = IntFlagValue(options, "atexit=", 0); - FLAG_poison_shadow = IntFlagValue(options, "poison_shadow=", 1); - FLAG_report_globals = IntFlagValue(options, "report_globals=", 1); - FLAG_lazy_shadow = IntFlagValue(options, "lazy_shadow=", 0); - FLAG_handle_segv = IntFlagValue(options, "handle_segv=", ASAN_NEEDS_SEGV); - FLAG_handle_sigill = IntFlagValue(options, "handle_sigill=", 0); - FLAG_symbolize = IntFlagValue(options, "symbolize=", 1); - FLAG_demangle = IntFlagValue(options, "demangle=", 1); - FLAG_debug = IntFlagValue(options, "debug=", 0); - FLAG_replace_cfallocator = IntFlagValue(options, "replace_cfallocator=", 1); - FLAG_fast_unwind = IntFlagValue(options, "fast_unwind=", 1); - FLAG_replace_str = IntFlagValue(options, "replace_str=", 1); - FLAG_replace_intrin = IntFlagValue(options, "replace_intrin=", 1); - FLAG_use_fake_stack = IntFlagValue(options, "use_fake_stack=", 1); - FLAG_exitcode = IntFlagValue(options, "exitcode=", EXIT_FAILURE); - FLAG_allow_user_poisoning = IntFlagValue(options, - "allow_user_poisoning=", 1); - - if (FLAG_atexit) { - atexit(asan_atexit); + if (flags()->verbosity && options) { + Report("Parsed ASAN_OPTIONS: %s\n", options); } - FLAG_quarantine_size = - IntFlagValue(options, "quarantine_size=", 1UL << 28); + if (flags()->atexit) { + Atexit(asan_atexit); + } // interceptors InitializeAsanInterceptors(); ReplaceSystemMalloc(); + ReplaceOperatorsNewAndDelete(); - INTERCEPT_FUNCTION(sigaction); - INTERCEPT_FUNCTION(signal); - INTERCEPT_FUNCTION(longjmp); - INTERCEPT_FUNCTION(_longjmp); - INTERCEPT_FUNCTION_IF_EXISTS(__cxa_throw); - INTERCEPT_FUNCTION(pthread_create); -#ifdef __APPLE__ - INTERCEPT_FUNCTION(dispatch_async_f); - INTERCEPT_FUNCTION(dispatch_sync_f); - INTERCEPT_FUNCTION(dispatch_after_f); - INTERCEPT_FUNCTION(dispatch_barrier_async_f); - INTERCEPT_FUNCTION(dispatch_group_async_f); - // We don't need to intercept pthread_workqueue_additem_np() to support the - // libdispatch API, but it helps us to debug the unsupported functions. Let's - // intercept it only during verbose runs. - if (FLAG_v >= 2) { - INTERCEPT_FUNCTION(pthread_workqueue_additem_np); - } -#else - // On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it - // there. - INTERCEPT_FUNCTION(siglongjmp); -#endif - - MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV); - MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV); - MaybeInstallSigaction(SIGILL, ASAN_OnSIGILL); - - if (FLAG_v) { - Printf("|| `[%p, %p]` || HighMem ||\n", kHighMemBeg, kHighMemEnd); + if (flags()->verbosity) { + Printf("|| `[%p, %p]` || HighMem ||\n", + (void*)kHighMemBeg, (void*)kHighMemEnd); Printf("|| `[%p, %p]` || HighShadow ||\n", - kHighShadowBeg, kHighShadowEnd); + (void*)kHighShadowBeg, (void*)kHighShadowEnd); Printf("|| `[%p, %p]` || ShadowGap ||\n", - kShadowGapBeg, kShadowGapEnd); + (void*)kShadowGapBeg, (void*)kShadowGapEnd); Printf("|| `[%p, %p]` || LowShadow ||\n", - kLowShadowBeg, kLowShadowEnd); - Printf("|| `[%p, %p]` || LowMem ||\n", kLowMemBeg, kLowMemEnd); + (void*)kLowShadowBeg, (void*)kLowShadowEnd); + Printf("|| `[%p, %p]` || LowMem ||\n", + (void*)kLowMemBeg, (void*)kLowMemEnd); Printf("MemToShadow(shadow): %p %p %p %p\n", - MEM_TO_SHADOW(kLowShadowBeg), - MEM_TO_SHADOW(kLowShadowEnd), - MEM_TO_SHADOW(kHighShadowBeg), - MEM_TO_SHADOW(kHighShadowEnd)); - Printf("red_zone=%ld\n", FLAG_redzone); - Printf("malloc_context_size=%ld\n", (int)FLAG_malloc_context_size); - Printf("fast_unwind=%d\n", (int)FLAG_fast_unwind); + (void*)MEM_TO_SHADOW(kLowShadowBeg), + (void*)MEM_TO_SHADOW(kLowShadowEnd), + (void*)MEM_TO_SHADOW(kHighShadowBeg), + (void*)MEM_TO_SHADOW(kHighShadowEnd)); + Printf("red_zone=%zu\n", (uptr)flags()->redzone); + Printf("malloc_context_size=%zu\n", (uptr)flags()->malloc_context_size); - Printf("SHADOW_SCALE: %lx\n", SHADOW_SCALE); - Printf("SHADOW_GRANULARITY: %lx\n", SHADOW_GRANULARITY); - Printf("SHADOW_OFFSET: %lx\n", SHADOW_OFFSET); + Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE); + Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY); + Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET); CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7); } - if (__WORDSIZE == 64) { - // Disable core dumper -- it makes little sense to dump 16T+ core. - struct rlimit nocore; - nocore.rlim_cur = 0; - nocore.rlim_max = 0; - setrlimit(RLIMIT_CORE, &nocore); + if (flags()->disable_core) { + DisableCoreDumper(); } - { - if (!FLAG_lazy_shadow) { - if (kLowShadowBeg != kLowShadowEnd) { - // mmap the low shadow plus one page. - ReserveShadowMemoryRange(kLowShadowBeg - kPageSize, kLowShadowEnd); - } - // mmap the high shadow. - ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); + uptr shadow_start = kLowShadowBeg; + if (kLowShadowBeg > 0) shadow_start -= kMmapGranularity; + uptr shadow_end = kHighShadowEnd; + if (MemoryRangeIsAvailable(shadow_start, shadow_end)) { + if (kLowShadowBeg != kLowShadowEnd) { + // mmap the low shadow plus at least one page. + ReserveShadowMemoryRange(kLowShadowBeg - kMmapGranularity, kLowShadowEnd); } + // mmap the high shadow. + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); // protect the gap - void *prot = AsanMprotect(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + void *prot = Mprotect(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); CHECK(prot == (void*)kShadowGapBeg); + } else { + Report("Shadow memory range interleaves with an existing memory mapping. " + "ASan cannot proceed correctly. ABORTING.\n"); + DumpProcessMap(); + Die(); } + InstallSignalHandlers(); + // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited // should be set to 1 prior to initializing the threads. asan_inited = 1; @@ -804,7 +573,21 @@ void __asan_init() { asanThreadRegistry().GetMain()->ThreadStart(); force_interface_symbols(); // no-op. - if (FLAG_v) { + if (flags()->verbosity) { Report("AddressSanitizer Init done\n"); } } + +#if defined(ASAN_USE_PREINIT_ARRAY) + // 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? + __attribute__((section(".preinit_array"))) + typeof(__asan_init) *__asan_preinit =__asan_init; +#elif defined(_WIN32) && defined(_DLL) + // On Windows, when using dynamic CRT (/MD), we can put a pointer + // to __asan_init into the global list of C initializers. + // See crt0dat.c in the CRT sources for the details. + #pragma section(".CRT$XIB", long, read) // NOLINT + __declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init; +#endif diff --git a/lib/asan/asan_stack.cc b/lib/asan/asan_stack.cc index 8163983bce7d..d6103c2c98fa 100644 --- a/lib/asan/asan_stack.cc +++ b/lib/asan/asan_stack.cc @@ -1,4 +1,4 @@ -//===-- asan_stack.cc -------------------------------------------*- C++ -*-===// +//===-- asan_stack.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -16,12 +16,8 @@ #include "asan_stack.h" #include "asan_thread.h" #include "asan_thread_registry.h" - -#include - -#if ASAN_USE_SYSINFO == 1 -#include "sysinfo/sysinfo.h" -#endif +#include "sanitizer_common/sanitizer_procmaps.h" +#include "sanitizer_common/sanitizer_symbolizer.h" #ifdef ASAN_USE_EXTERNAL_SYMBOLIZER extern bool @@ -30,183 +26,139 @@ ASAN_USE_EXTERNAL_SYMBOLIZER(const void *pc, char *out, int out_size); namespace __asan { -// ----------------------- ProcSelfMaps ----------------------------- {{{1 -#if ASAN_USE_SYSINFO == 1 -class ProcSelfMaps { - public: - void Init() { - ScopedLock lock(&mu_); - if (map_size_ != 0) return; // already inited - if (FLAG_v >= 2) { - Printf("ProcSelfMaps::Init()\n"); - } - ProcMapsIterator it(0, &proc_self_maps_); // 0 means "current pid" - - uint64 start, end, offset; - int64 inode; - char *flags, *filename; - CHECK(map_size_ == 0); - while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) { - CHECK(map_size_ < kMaxProcSelfMapsSize); - Mapping &mapping = memory_map[map_size_]; - mapping.beg = start; - mapping.end = end; - mapping.offset = offset; - real_strncpy(mapping.name, - filename, ASAN_ARRAY_SIZE(mapping.name)); - mapping.name[ASAN_ARRAY_SIZE(mapping.name) - 1] = 0; - if (FLAG_v >= 2) { - Printf("[%ld] [%p,%p] off %p %s\n", map_size_, - mapping.beg, mapping.end, mapping.offset, mapping.name); - } - map_size_++; - } - } - - void Print() { - Printf("%s\n", proc_self_maps_); - } - - void PrintPc(uintptr_t pc, int idx) { - for (size_t i = 0; i < map_size_; i++) { - Mapping &m = memory_map[i]; - if (pc >= m.beg && pc < m.end) { - uintptr_t offset = pc - m.beg; - if (i == 0) offset = pc; - Printf(" #%d 0x%lx (%s+0x%lx)\n", idx, pc, m.name, offset); - return; - } - } - Printf(" #%d 0x%lx\n", idx, pc); - } - - private: - void copy_until_new_line(const char *str, char *dest, size_t max_size) { - size_t i = 0; - for (; str[i] && str[i] != '\n' && i < max_size - 1; i++) { - dest[i] = str[i]; - } - dest[i] = 0; - } - - - struct Mapping { - uintptr_t beg, end, offset; - char name[1000]; - }; - static const size_t kMaxNumMapEntries = 4096; - static const size_t kMaxProcSelfMapsSize = 1 << 20; - ProcMapsIterator::Buffer proc_self_maps_; - size_t map_size_; - Mapping memory_map[kMaxNumMapEntries]; - - static AsanLock mu_; -}; - -static ProcSelfMaps proc_self_maps; -AsanLock ProcSelfMaps::mu_(LINKER_INITIALIZED); - - -void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) { - proc_self_maps.Init(); - for (size_t i = 0; i < size && addr[i]; i++) { - uintptr_t pc = addr[i]; - // int line; - proc_self_maps.PrintPc(pc, i); - // Printf(" #%ld 0x%lx %s\n", i, pc, rtn.c_str()); - } +// ----------------------- AsanStackTrace ----------------------------- {{{1 +// PCs in stack traces are actually the return addresses, that is, +// addresses of the next instructions after the call. That's why we +// decrement them. +static uptr patch_pc(uptr pc) { +#ifdef __arm__ + // Cancel Thumb bit. + pc = pc & (~1); +#endif + return pc - 1; } -#elif defined(ASAN_USE_EXTERNAL_SYMBOLIZER) -void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) { - for (size_t i = 0; i < size && addr[i]; i++) { - uintptr_t pc = addr[i]; + +#if defined(ASAN_USE_EXTERNAL_SYMBOLIZER) +void AsanStackTrace::PrintStack(uptr *addr, uptr size) { + for (uptr i = 0; i < size && addr[i]; i++) { + uptr pc = addr[i]; + if (i < size - 1 && addr[i + 1]) + pc = patch_pc(pc); char buff[4096]; ASAN_USE_EXTERNAL_SYMBOLIZER((void*)pc, buff, sizeof(buff)); - Printf(" #%ld 0x%lx %s\n", i, pc, buff); + AsanPrintf(" #%zu 0x%zx %s\n", i, pc, buff); } } -#else // ASAN_USE_SYSINFO -void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) { - for (size_t i = 0; i < size && addr[i]; i++) { - uintptr_t pc = addr[i]; - Printf(" #%ld 0x%lx\n", i, pc); +#else // ASAN_USE_EXTERNAL_SYMBOLIZER +void AsanStackTrace::PrintStack(uptr *addr, uptr size) { + ProcessMaps proc_maps; + uptr frame_num = 0; + for (uptr i = 0; i < size && addr[i]; i++) { + uptr pc = addr[i]; + if (i < size - 1 && addr[i + 1]) + pc = patch_pc(pc); + AddressInfo addr_frames[64]; + uptr addr_frames_num = 0; + if (flags()->symbolize) { + addr_frames_num = SymbolizeCode(pc, addr_frames, + ASAN_ARRAY_SIZE(addr_frames)); + } + if (addr_frames_num > 0) { + for (uptr j = 0; j < addr_frames_num; j++) { + AddressInfo &info = addr_frames[j]; + AsanPrintf(" #%zu 0x%zx", frame_num, pc); + if (info.function) { + AsanPrintf(" in %s", info.function); + } + if (info.file) { + AsanPrintf(" %s:%d:%d", info.file, info.line, info.column); + } else if (info.module) { + AsanPrintf(" (%s+0x%zx)", info.module, info.module_offset); + } + AsanPrintf("\n"); + info.Clear(); + frame_num++; + } + } else { + uptr offset; + char filename[4096]; + if (proc_maps.GetObjectNameAndOffset(pc, &offset, + filename, sizeof(filename))) { + AsanPrintf(" #%zu 0x%zx (%s+0x%zx)\n", frame_num, pc, filename, + offset); + } else { + AsanPrintf(" #%zu 0x%zx\n", frame_num, pc); + } + frame_num++; + } } } -#endif // ASAN_USE_SYSINFO +#endif // ASAN_USE_EXTERNAL_SYMBOLIZER -#ifdef __arm__ -#define UNWIND_STOP _URC_END_OF_STACK -#define UNWIND_CONTINUE _URC_OK -#else -#define UNWIND_STOP _URC_NORMAL_STOP -#define UNWIND_CONTINUE _URC_NO_REASON -#endif - -// ----------------------- AsanStackTrace ----------------------------- {{{1 -uintptr_t AsanStackTrace::GetCurrentPc() { +uptr AsanStackTrace::GetCurrentPc() { return GET_CALLER_PC(); } -void AsanStackTrace::FastUnwindStack(uintptr_t pc, uintptr_t bp) { +void AsanStackTrace::FastUnwindStack(uptr pc, uptr bp) { CHECK(size == 0 && trace[0] == pc); size = 1; if (!asan_inited) return; AsanThread *t = asanThreadRegistry().GetCurrent(); if (!t) return; - uintptr_t *frame = (uintptr_t*)bp; - uintptr_t *prev_frame = frame; - uintptr_t *top = (uintptr_t*)t->stack_top(); - uintptr_t *bottom = (uintptr_t*)t->stack_bottom(); + uptr *frame = (uptr*)bp; + uptr *prev_frame = frame; + uptr *top = (uptr*)t->stack_top(); + uptr *bottom = (uptr*)t->stack_bottom(); while (frame >= prev_frame && - frame < top && + frame < top - 2 && frame > bottom && size < max_size) { - uintptr_t pc1 = frame[1]; + uptr pc1 = frame[1]; if (pc1 != pc) { trace[size++] = pc1; } prev_frame = frame; - frame = (uintptr_t*)frame[0]; + frame = (uptr*)frame[0]; } } // On 32-bits we don't compress stack traces. // On 64-bits we compress stack traces: if a given pc differes slightly from // the previous one, we record a 31-bit offset instead of the full pc. -size_t AsanStackTrace::CompressStack(AsanStackTrace *stack, - uint32_t *compressed, size_t size) { +uptr AsanStackTrace::CompressStack(AsanStackTrace *stack, + u32 *compressed, uptr size) { #if __WORDSIZE == 32 // Don't compress, just copy. - size_t res = 0; - for (size_t i = 0; i < stack->size && i < size; i++) { + uptr res = 0; + for (uptr i = 0; i < stack->size && i < size; i++) { compressed[i] = stack->trace[i]; res++; } if (stack->size < size) compressed[stack->size] = 0; #else // 64 bits, compress. - uintptr_t prev_pc = 0; - const uintptr_t kMaxOffset = (1ULL << 30) - 1; - uintptr_t c_index = 0; - size_t res = 0; - for (size_t i = 0, n = stack->size; i < n; i++) { - uintptr_t pc = stack->trace[i]; + uptr prev_pc = 0; + const uptr kMaxOffset = (1ULL << 30) - 1; + uptr c_index = 0; + uptr res = 0; + for (uptr i = 0, n = stack->size; i < n; i++) { + uptr pc = stack->trace[i]; if (!pc) break; - if ((int64_t)pc < 0) break; - // Printf("C pc[%ld] %lx\n", i, pc); + if ((s64)pc < 0) break; + // Printf("C pc[%zu] %zx\n", i, pc); if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) { - uintptr_t offset = (int64_t)(pc - prev_pc); + uptr offset = (s64)(pc - prev_pc); offset |= (1U << 31); if (c_index >= size) break; - // Printf("C co[%ld] offset %lx\n", i, offset); + // Printf("C co[%zu] offset %zx\n", i, offset); compressed[c_index++] = offset; } else { - uintptr_t hi = pc >> 32; - uintptr_t lo = (pc << 32) >> 32; + uptr hi = pc >> 32; + uptr lo = (pc << 32) >> 32; CHECK((hi & (1 << 31)) == 0); if (c_index + 1 >= size) break; - // Printf("C co[%ld] hi/lo: %lx %lx\n", c_index, hi, lo); + // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo); compressed[c_index++] = hi; compressed[c_index++] = lo; } @@ -224,53 +176,53 @@ size_t AsanStackTrace::CompressStack(AsanStackTrace *stack, AsanStackTrace check_stack; UncompressStack(&check_stack, compressed, size); if (res < check_stack.size) { - Printf("res %ld check_stack.size %ld; c_size %ld\n", res, + Printf("res %zu check_stack.size %zu; c_size %zu\n", res, check_stack.size, size); } // |res| may be greater than check_stack.size, because // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames. CHECK(res >= check_stack.size); - CHECK(0 == real_memcmp(check_stack.trace, stack->trace, - check_stack.size * sizeof(uintptr_t))); + CHECK(0 == REAL(memcmp)(check_stack.trace, stack->trace, + check_stack.size * sizeof(uptr))); #endif return res; } void AsanStackTrace::UncompressStack(AsanStackTrace *stack, - uint32_t *compressed, size_t size) { + u32 *compressed, uptr size) { #if __WORDSIZE == 32 // Don't uncompress, just copy. stack->size = 0; - for (size_t i = 0; i < size && i < kStackTraceMax; i++) { + for (uptr i = 0; i < size && i < kStackTraceMax; i++) { if (!compressed[i]) break; stack->size++; stack->trace[i] = compressed[i]; } #else // 64 bits, uncompress - uintptr_t prev_pc = 0; + uptr prev_pc = 0; stack->size = 0; - for (size_t i = 0; i < size && stack->size < kStackTraceMax; i++) { - uint32_t x = compressed[i]; - uintptr_t pc = 0; + for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) { + u32 x = compressed[i]; + uptr pc = 0; if (x & (1U << 31)) { - // Printf("U co[%ld] offset: %x\n", i, x); + // Printf("U co[%zu] offset: %x\n", i, x); // this is an offset - int32_t offset = x; + s32 offset = x; offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend. pc = prev_pc + offset; CHECK(pc); } else { // CHECK(i + 1 < size); if (i + 1 >= size) break; - uintptr_t hi = x; - uintptr_t lo = compressed[i+1]; - // Printf("U co[%ld] hi/lo: %lx %lx\n", i, hi, lo); + uptr hi = x; + uptr lo = compressed[i+1]; + // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo); i++; pc = (hi << 32) | lo; if (!pc) break; } - // Printf("U pc[%ld] %lx\n", stack->size, pc); + // Printf("U pc[%zu] %zx\n", stack->size, pc); stack->trace[stack->size++] = pc; prev_pc = pc; } diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h index 97aefd6d4218..6ca9a0b28bfc 100644 --- a/lib/asan/asan_stack.h +++ b/lib/asan/asan_stack.h @@ -18,77 +18,87 @@ namespace __asan { -static const size_t kStackTraceMax = 64; +static const uptr kStackTraceMax = 64; struct AsanStackTrace { - size_t size; - size_t max_size; - uintptr_t trace[kStackTraceMax]; - static void PrintStack(uintptr_t *addr, size_t size); + uptr size; + uptr max_size; + uptr trace[kStackTraceMax]; + static void PrintStack(uptr *addr, uptr size); void PrintStack() { PrintStack(this->trace, this->size); } - void CopyTo(uintptr_t *dst, size_t dst_size) { - for (size_t i = 0; i < size && i < dst_size; i++) + void CopyTo(uptr *dst, uptr dst_size) { + for (uptr i = 0; i < size && i < dst_size; i++) dst[i] = trace[i]; - for (size_t i = size; i < dst_size; i++) + for (uptr i = size; i < dst_size; i++) dst[i] = 0; } - void CopyFrom(uintptr_t *src, size_t src_size) { + void CopyFrom(uptr *src, uptr src_size) { size = src_size; if (size > kStackTraceMax) size = kStackTraceMax; - for (size_t i = 0; i < size; i++) { + for (uptr i = 0; i < size; i++) { trace[i] = src[i]; } } - void FastUnwindStack(uintptr_t pc, uintptr_t bp); -// static _Unwind_Reason_Code Unwind_Trace( -// struct _Unwind_Context *ctx, void *param); - static uintptr_t GetCurrentPc(); + void GetStackTrace(uptr max_s, uptr pc, uptr bp); - static size_t CompressStack(AsanStackTrace *stack, - uint32_t *compressed, size_t size); + void FastUnwindStack(uptr pc, uptr bp); + + static uptr GetCurrentPc(); + + static uptr CompressStack(AsanStackTrace *stack, + u32 *compressed, uptr size); static void UncompressStack(AsanStackTrace *stack, - uint32_t *compressed, size_t size); - size_t full_frame_count; + u32 *compressed, uptr size); }; } // namespace __asan +// Use this macro if you want to print stack trace with the caller +// of the current function in the top frame. +#define GET_CALLER_PC_BP_SP \ + uptr bp = GET_CURRENT_FRAME(); \ + uptr pc = GET_CALLER_PC(); \ + uptr local_stack; \ + uptr sp = (uptr)&local_stack + +// Use this macro if you want to print stack trace with the current +// function in the top frame. +#define GET_CURRENT_PC_BP_SP \ + uptr bp = GET_CURRENT_FRAME(); \ + uptr pc = AsanStackTrace::GetCurrentPc(); \ + uptr local_stack; \ + uptr sp = (uptr)&local_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, fast_unwind, pc, bp) \ - AsanStackTrace stack; \ - { \ - uintptr_t saved_pc = pc; \ - uintptr_t saved_bp = bp; \ - stack.size = 0; \ - stack.full_frame_count = 0; \ - stack.trace[0] = saved_pc; \ - if ((max_s) > 1) { \ - stack.max_size = max_s; \ - stack.FastUnwindStack(saved_pc, saved_bp); \ - } \ - } \ +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp) \ + AsanStackTrace stack; \ + stack.GetStackTrace(max_s, pc, bp) -#define GET_STACK_TRACE_HERE(max_size, fast_unwind) \ - GET_STACK_TRACE_WITH_PC_AND_BP(max_size, fast_unwind, \ - AsanStackTrace::GetCurrentPc(), GET_CURRENT_FRAME()) \ +// NOTE: A Rule of thumb is to retrieve stack trace in the interceptors +// as early as possible (in functions exposed to the user), as we generally +// don't want stack trace to contain functions from ASan internals. -#define GET_STACK_TRACE_HERE_FOR_MALLOC \ - GET_STACK_TRACE_HERE(FLAG_malloc_context_size, FLAG_fast_unwind) +#define GET_STACK_TRACE_HERE(max_size) \ + GET_STACK_TRACE_WITH_PC_AND_BP(max_size, \ + AsanStackTrace::GetCurrentPc(), GET_CURRENT_FRAME()) -#define GET_STACK_TRACE_HERE_FOR_FREE(ptr) \ - GET_STACK_TRACE_HERE(FLAG_malloc_context_size, FLAG_fast_unwind) +#define GET_STACK_TRACE_HERE_FOR_MALLOC \ + GET_STACK_TRACE_HERE(flags()->malloc_context_size) + +#define GET_STACK_TRACE_HERE_FOR_FREE(ptr) \ + GET_STACK_TRACE_HERE(flags()->malloc_context_size) #define PRINT_CURRENT_STACK() \ { \ - GET_STACK_TRACE_HERE(kStackTraceMax, false); \ + GET_STACK_TRACE_HERE(kStackTraceMax); \ stack.PrintStack(); \ - } \ + } #endif // ASAN_STACK_H diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc index 3e4d1b4f52d9..ef5e53a8bf33 100644 --- a/lib/asan/asan_stats.cc +++ b/lib/asan/asan_stats.cc @@ -1,4 +1,4 @@ -//===-- asan_stats.cc -------------------------------------------*- C++ -*-===// +//===-- asan_stats.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -21,36 +21,36 @@ namespace __asan { AsanStats::AsanStats() { - CHECK(real_memset != NULL); - real_memset(this, 0, sizeof(AsanStats)); + CHECK(REAL(memset) != 0); + REAL(memset)(this, 0, sizeof(AsanStats)); } static void PrintMallocStatsArray(const char *prefix, - size_t (&array)[kNumberOfSizeClasses]) { - Printf("%s", prefix); - for (size_t i = 0; i < kNumberOfSizeClasses; i++) { + uptr (&array)[kNumberOfSizeClasses]) { + AsanPrintf("%s", prefix); + for (uptr i = 0; i < kNumberOfSizeClasses; i++) { if (!array[i]) continue; - Printf("%ld:%ld; ", i, array[i]); + AsanPrintf("%zu:%zu; ", i, array[i]); } - Printf("\n"); + AsanPrintf("\n"); } void AsanStats::Print() { - Printf("Stats: %ldM malloced (%ldM for red zones) by %ld calls\n", - malloced>>20, malloced_redzones>>20, mallocs); - Printf("Stats: %ldM realloced by %ld calls\n", realloced>>20, reallocs); - Printf("Stats: %ldM freed by %ld calls\n", freed>>20, frees); - Printf("Stats: %ldM really freed by %ld calls\n", - really_freed>>20, real_frees); - Printf("Stats: %ldM (%ld full pages) mmaped in %ld calls\n", - mmaped>>20, mmaped / kPageSize, mmaps); + AsanPrintf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n", + malloced>>20, malloced_redzones>>20, mallocs); + AsanPrintf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs); + AsanPrintf("Stats: %zuM freed by %zu calls\n", freed>>20, frees); + AsanPrintf("Stats: %zuM really freed by %zu calls\n", + really_freed>>20, real_frees); + AsanPrintf("Stats: %zuM (%zu full pages) mmaped in %zu calls\n", + mmaped>>20, mmaped / kPageSize, mmaps); PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size); PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size); PrintMallocStatsArray(" frees by size class: ", freed_by_size); PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size); - Printf("Stats: malloc large: %ld small slow: %ld\n", - malloc_large, malloc_small_slow); + AsanPrintf("Stats: malloc large: %zu small slow: %zu\n", + malloc_large, malloc_small_slow); } static AsanLock print_lock(LINKER_INITIALIZED); @@ -67,19 +67,19 @@ static void PrintAccumulatedStats() { // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT -size_t __asan_get_current_allocated_bytes() { +uptr __asan_get_current_allocated_bytes() { return asanThreadRegistry().GetCurrentAllocatedBytes(); } -size_t __asan_get_heap_size() { +uptr __asan_get_heap_size() { return asanThreadRegistry().GetHeapSize(); } -size_t __asan_get_free_bytes() { +uptr __asan_get_free_bytes() { return asanThreadRegistry().GetFreeBytes(); } -size_t __asan_get_unmapped_bytes() { +uptr __asan_get_unmapped_bytes() { return 0; } diff --git a/lib/asan/asan_stats.h b/lib/asan/asan_stats.h index d6dd084c013d..b4c63f44fc68 100644 --- a/lib/asan/asan_stats.h +++ b/lib/asan/asan_stats.h @@ -23,27 +23,27 @@ namespace __asan { // Each AsanThread has its own AsanStats, which are sometimes flushed // to the accumulated AsanStats. struct AsanStats { - // AsanStats must be a struct consisting of size_t fields only. - // When merging two AsanStats structs, we treat them as arrays of size_t. - size_t mallocs; - size_t malloced; - size_t malloced_redzones; - size_t frees; - size_t freed; - size_t real_frees; - size_t really_freed; - size_t really_freed_redzones; - size_t reallocs; - size_t realloced; - size_t mmaps; - size_t mmaped; - size_t mmaped_by_size[kNumberOfSizeClasses]; - size_t malloced_by_size[kNumberOfSizeClasses]; - size_t freed_by_size[kNumberOfSizeClasses]; - size_t really_freed_by_size[kNumberOfSizeClasses]; + // AsanStats must be a struct consisting of uptr fields only. + // When merging two AsanStats structs, we treat them as arrays of uptr. + uptr mallocs; + uptr malloced; + uptr malloced_redzones; + uptr frees; + uptr freed; + uptr real_frees; + uptr really_freed; + uptr really_freed_redzones; + uptr reallocs; + uptr realloced; + uptr mmaps; + uptr mmaped; + uptr mmaped_by_size[kNumberOfSizeClasses]; + uptr malloced_by_size[kNumberOfSizeClasses]; + uptr freed_by_size[kNumberOfSizeClasses]; + uptr really_freed_by_size[kNumberOfSizeClasses]; - size_t malloc_large; - size_t malloc_small_slow; + uptr malloc_large; + uptr malloc_small_slow; // Ctor for global AsanStats (accumulated stats and main thread stats). explicit AsanStats(LinkerInitialized) { } diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc index 329197dd4e25..05a41ea9685a 100644 --- a/lib/asan/asan_thread.cc +++ b/lib/asan/asan_thread.cc @@ -1,4 +1,4 @@ -//===-- asan_thread.cc ------------------------------------------*- C++ -*-===// +//===-- asan_thread.cc ----------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -13,19 +13,11 @@ //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_interceptors.h" +#include "asan_stack.h" #include "asan_thread.h" #include "asan_thread_registry.h" #include "asan_mapping.h" - -#if ASAN_USE_SYSINFO == 1 -#include "sysinfo/sysinfo.h" -#endif - -#include -#include -#include -#include -#include +#include "sanitizer_common/sanitizer_common.h" namespace __asan { @@ -34,67 +26,105 @@ AsanThread::AsanThread(LinkerInitialized x) malloc_storage_(x), stats_(x) { } -AsanThread::AsanThread(int parent_tid, void *(*start_routine) (void *), - void *arg, AsanStackTrace *stack) - : start_routine_(start_routine), - arg_(arg) { - asanThreadRegistry().RegisterThread(this, parent_tid, stack); +static AsanLock mu_for_thread_summary(LINKER_INITIALIZED); +static LowLevelAllocator allocator_for_thread_summary(LINKER_INITIALIZED); + +AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine, + void *arg, AsanStackTrace *stack) { + uptr size = RoundUpTo(sizeof(AsanThread), kPageSize); + AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__); + thread->start_routine_ = start_routine; + thread->arg_ = arg; + + const uptr kSummaryAllocSize = 1024; + CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize); + AsanThreadSummary *summary; + { + ScopedLock lock(&mu_for_thread_summary); + summary = (AsanThreadSummary*) + allocator_for_thread_summary.Allocate(kSummaryAllocSize); + } + summary->Init(parent_tid, stack); + summary->set_thread(thread); + thread->set_summary(summary); + + return thread; } -AsanThread::~AsanThread() { +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::Destroy() { + if (flags()->verbosity >= 1) { + Report("T%d exited\n", tid()); + } + asanThreadRegistry().UnregisterThread(this); - fake_stack().Cleanup(); + CHECK(summary()->thread() == 0); // 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(); -} - -void AsanThread::ClearShadowForThreadStack() { - uintptr_t shadow_bot = MemToShadow(stack_bottom_); - uintptr_t shadow_top = MemToShadow(stack_top_); - real_memset((void*)shadow_bot, 0, shadow_top - shadow_bot); + fake_stack().Cleanup(); + uptr size = RoundUpTo(sizeof(AsanThread), kPageSize); + UnmapOrDie(this, size); } void AsanThread::Init() { SetThreadStackTopAndBottom(); - fake_stack_.Init(stack_size()); - if (FLAG_v >= 1) { - int local = 0; - Report("T%d: stack [%p,%p) size 0x%lx; local=%p, pthread_self=%p\n", - tid(), stack_bottom_, stack_top_, - stack_top_ - stack_bottom_, &local, pthread_self()); - } - CHECK(AddrIsInMem(stack_bottom_)); CHECK(AddrIsInMem(stack_top_)); - ClearShadowForThreadStack(); + 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()); + AsanPlatformThreadInit(); } -void *AsanThread::ThreadStart() { +thread_return_t AsanThread::ThreadStart() { Init(); + if (flags()->use_sigaltstack) SetAlternateSignalStack(); if (!start_routine_) { - // start_routine_ == NULL if we're on the main thread or on one of the + // 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); return 0; } - void *res = start_routine_(arg_); + thread_return_t res = start_routine_(arg_); malloc_storage().CommitBack(); + if (flags()->use_sigaltstack) UnsetAlternateSignalStack(); - if (FLAG_v >= 1) { - Report("T%d exited\n", tid()); - } + this->Destroy(); return res; } -const char *AsanThread::GetFrameNameByAddr(uintptr_t addr, uintptr_t *offset) { - uintptr_t bottom = 0; +void AsanThread::SetThreadStackTopAndBottom() { + GetThreadStackTopAndBottom(tid() == 0, &stack_top_, &stack_bottom_); + int local; + CHECK(AddrIsInStack((uptr)&local)); +} + +void AsanThread::ClearShadowForThreadStack() { + PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); +} + +const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { + uptr bottom = 0; bool is_fake_stack = false; if (AddrIsInStack(addr)) { bottom = stack_bottom(); @@ -103,76 +133,30 @@ const char *AsanThread::GetFrameNameByAddr(uintptr_t addr, uintptr_t *offset) { CHECK(bottom); is_fake_stack = true; } - uintptr_t aligned_addr = addr & ~(__WORDSIZE/8 - 1); // align addr. - uintptr_t *ptr = (uintptr_t*)aligned_addr; - while (ptr >= (uintptr_t*)bottom) { - if (ptr[0] == kCurrentStackFrameMagic || - (is_fake_stack && ptr[0] == kRetiredStackFrameMagic)) { - *offset = addr - (uintptr_t)ptr; - return (const char*)ptr[1]; - } - ptr--; + uptr aligned_addr = addr & ~(__WORDSIZE/8 - 1); // align addr. + u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); + u8 *shadow_bottom = (u8*)MemToShadow(bottom); + + while (shadow_ptr >= shadow_bottom && + *shadow_ptr != kAsanStackLeftRedzoneMagic) { + shadow_ptr--; } - *offset = 0; - return "UNKNOWN"; -} -void AsanThread::SetThreadStackTopAndBottom() { -#ifdef __APPLE__ - size_t stacksize = pthread_get_stacksize_np(pthread_self()); - void *stackaddr = pthread_get_stackaddr_np(pthread_self()); - stack_top_ = (uintptr_t)stackaddr; - stack_bottom_ = stack_top_ - stacksize; - int local; - CHECK(AddrIsInStack((uintptr_t)&local)); -#else -#if ASAN_USE_SYSINFO == 1 - if (tid() == 0) { - // This is the main thread. Libpthread may not be initialized yet. - struct rlimit rl; - CHECK(getrlimit(RLIMIT_STACK, &rl) == 0); - - // Find the mapping that contains a stack variable. - ProcMapsIterator it(0); - uint64_t start, end; - uint64_t prev_end = 0; - while (it.Next(&start, &end, NULL, NULL, NULL, NULL)) { - if ((uintptr_t)&rl < end) - break; - prev_end = end; - } - CHECK((uintptr_t)&rl >= start && (uintptr_t)&rl < end); - - // Get stacksize from rlimit, but clip it so that it does not overlap - // with other mappings. - size_t stacksize = rl.rlim_cur; - if (stacksize > end - prev_end) - stacksize = end - prev_end; - if (stacksize > kMaxThreadStackSize) - stacksize = kMaxThreadStackSize; - stack_top_ = end; - stack_bottom_ = end - stacksize; - CHECK(AddrIsInStack((uintptr_t)&rl)); - return; + while (shadow_ptr >= shadow_bottom && + *shadow_ptr == kAsanStackLeftRedzoneMagic) { + shadow_ptr--; } -#endif - pthread_attr_t attr; - CHECK(pthread_getattr_np(pthread_self(), &attr) == 0); - size_t stacksize = 0; - void *stackaddr = NULL; - pthread_attr_getstack(&attr, &stackaddr, &stacksize); - pthread_attr_destroy(&attr); - stack_top_ = (uintptr_t)stackaddr + stacksize; - stack_bottom_ = (uintptr_t)stackaddr; - // 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 subrocesses with unlimited stack. - if (stacksize > kMaxThreadStackSize) { - stack_bottom_ = stack_top_ - kMaxThreadStackSize; + if (shadow_ptr < shadow_bottom) { + *offset = 0; + return "UNKNOWN"; } - CHECK(AddrIsInStack((uintptr_t)&attr)); -#endif + + uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1)); + CHECK((ptr[0] == kCurrentStackFrameMagic) || + (is_fake_stack && ptr[0] == kRetiredStackFrameMagic)); + *offset = addr - (uptr)ptr; + return (const char*)ptr[1]; } } // namespace __asan diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h index c382c85a6a3c..9a032fe3e66c 100644 --- a/lib/asan/asan_thread.h +++ b/lib/asan/asan_thread.h @@ -18,10 +18,11 @@ #include "asan_internal.h" #include "asan_stack.h" #include "asan_stats.h" +#include "sanitizer_common/sanitizer_libc.h" namespace __asan { -const size_t kMaxThreadStackSize = 16 * (1 << 20); // 16M +const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits. class AsanThread; @@ -30,12 +31,12 @@ class AsanThread; class AsanThreadSummary { public: explicit AsanThreadSummary(LinkerInitialized) { } // for T0. - AsanThreadSummary(int tid, int parent_tid, AsanStackTrace *stack) - : tid_(tid), - parent_tid_(parent_tid), - announced_(false) { + void Init(u32 parent_tid, AsanStackTrace *stack) { + parent_tid_ = parent_tid; + announced_ = false; + tid_ = kInvalidTid; if (stack) { - stack_ = *stack; + internal_memcpy(&stack_, stack, sizeof(*stack)); } thread_ = 0; } @@ -43,16 +44,19 @@ class AsanThreadSummary { if (tid_ == 0) return; // no need to announce the main thread. if (!announced_) { announced_ = true; - Printf("Thread T%d created by T%d here:\n", tid_, parent_tid_); + AsanPrintf("Thread T%d created by T%d here:\n", tid_, parent_tid_); stack_.PrintStack(); } } - int tid() { return tid_; } + u32 tid() { return tid_; } + void set_tid(u32 tid) { tid_ = tid; } AsanThread *thread() { return thread_; } void set_thread(AsanThread *thread) { thread_ = thread; } + static void TSDDtor(void *tsd); + private: - int tid_; - int parent_tid_; + u32 tid_; + u32 parent_tid_; bool announced_; AsanStackTrace stack_; AsanThread *thread_; @@ -62,23 +66,23 @@ class AsanThreadSummary { class AsanThread { public: explicit AsanThread(LinkerInitialized); // for T0. - AsanThread(int parent_tid, void *(*start_routine) (void *), - void *arg, AsanStackTrace *stack); - ~AsanThread(); + static AsanThread *Create(u32 parent_tid, thread_callback_t start_routine, + void *arg, AsanStackTrace *stack); + void Destroy(); void Init(); // Should be called from the thread itself. - void *ThreadStart(); + thread_return_t ThreadStart(); - uintptr_t stack_top() { return stack_top_; } - uintptr_t stack_bottom() { return stack_bottom_; } - size_t stack_size() { return stack_top_ - stack_bottom_; } - int tid() { return summary_->tid(); } + 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; } - const char *GetFrameNameByAddr(uintptr_t addr, uintptr_t *offset); + const char *GetFrameNameByAddr(uptr addr, uptr *offset); - bool AddrIsInStack(uintptr_t addr) { + bool AddrIsInStack(uptr addr) { return addr >= stack_bottom_ && addr < stack_top_; } @@ -86,17 +90,15 @@ class AsanThread { AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } AsanStats &stats() { return stats_; } - static const int kInvalidTid = -1; - private: void SetThreadStackTopAndBottom(); void ClearShadowForThreadStack(); AsanThreadSummary *summary_; - void *(*start_routine_) (void *param); + thread_callback_t start_routine_; void *arg_; - uintptr_t stack_top_; - uintptr_t stack_bottom_; + uptr stack_top_; + uptr stack_bottom_; FakeStack fake_stack_; AsanThreadLocalMallocStorage malloc_storage_; diff --git a/lib/asan/asan_thread_registry.cc b/lib/asan/asan_thread_registry.cc index 39fba4401a2b..4540d589c552 100644 --- a/lib/asan/asan_thread_registry.cc +++ b/lib/asan/asan_thread_registry.cc @@ -1,4 +1,4 @@ -//===-- asan_thread_registry.cc ---------------------------------*- C++ -*-===// +//===-- asan_thread_registry.cc -------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -16,8 +16,7 @@ #include "asan_stack.h" #include "asan_thread.h" #include "asan_thread_registry.h" - -#include +#include "sanitizer_common/sanitizer_common.h" namespace __asan { @@ -27,48 +26,6 @@ AsanThreadRegistry &asanThreadRegistry() { return asan_thread_registry; } -#ifdef ANDROID -#ifndef PTHREAD_DESTRUCTOR_ITERATIONS -#define PTHREAD_DESTRUCTOR_ITERATIONS 4 -#endif -#endif - -// Dark magic below. In order to be able to notice that we're not handling -// some thread creation routines (e.g. on Mac OS) we want to distinguish the -// thread that used to have a corresponding AsanThread object from the thread -// that never had one. That's why upon AsanThread destruction we set the -// pthread_key value to some odd number (that's not a valid pointer), instead -// of NULL. -// Because the TSD destructor for a non-NULL key value is called iteratively, -// we increase the value by two, keeping it an invalid pointer. -// Because the TSD implementations are allowed to call such a destructor -// infinitely (see -// http://pubs.opengroup.org/onlinepubs/009604499/functions/pthread_key_create.html -// ), we exit the program after a certain number of iterations. -static void DestroyAsanTsd(void *tsd) { - intptr_t iter = (intptr_t)tsd; - if (iter % 2 == 0) { - // The pointer is valid. - AsanThread *t = (AsanThread*)tsd; - if (t != asanThreadRegistry().GetMain()) { - delete t; - } - iter = 1; - } else { - // The pointer is invalid -- we've already destroyed the TSD before. - // If |iter| is too big, we're in the infinite loop. This should be - // impossible on the systems AddressSanitizer was tested on. - CHECK(iter < 4 * PTHREAD_DESTRUCTOR_ITERATIONS); - iter += 2; - } - CHECK(0 == pthread_setspecific(asanThreadRegistry().GetTlsKey(), - (void*)iter)); - if (FLAG_v >= 2) { - Report("DestroyAsanTsd: writing %p to the TSD slot of thread %p\n", - (void*)iter, pthread_self()); - } -} - AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x) : main_thread_(x), main_thread_summary_(x), @@ -76,26 +33,25 @@ AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x) mu_(x) { } void AsanThreadRegistry::Init() { - CHECK(0 == pthread_key_create(&tls_key_, DestroyAsanTsd)); - tls_key_created_ = true; - SetCurrent(&main_thread_); + AsanTSDInit(AsanThreadSummary::TSDDtor); main_thread_.set_summary(&main_thread_summary_); main_thread_summary_.set_thread(&main_thread_); - thread_summaries_[0] = &main_thread_summary_; - n_threads_ = 1; + RegisterThread(&main_thread_); + SetCurrent(&main_thread_); + // At this point only one thread exists. + inited_ = true; } -void AsanThreadRegistry::RegisterThread(AsanThread *thread, int parent_tid, - AsanStackTrace *stack) { +void AsanThreadRegistry::RegisterThread(AsanThread *thread) { ScopedLock lock(&mu_); - CHECK(n_threads_ > 0); - int tid = n_threads_; + u32 tid = n_threads_; n_threads_++; CHECK(n_threads_ < kMaxNumberOfThreads); - AsanThreadSummary *summary = new AsanThreadSummary(tid, parent_tid, stack); - summary->set_thread(thread); + + AsanThreadSummary *summary = thread->summary(); + CHECK(summary != 0); + summary->set_tid(tid); thread_summaries_[tid] = summary; - thread->set_summary(summary); } void AsanThreadRegistry::UnregisterThread(AsanThread *thread) { @@ -103,7 +59,7 @@ void AsanThreadRegistry::UnregisterThread(AsanThread *thread) { FlushToAccumulatedStatsUnlocked(&thread->stats()); AsanThreadSummary *summary = thread->summary(); CHECK(summary); - summary->set_thread(NULL); + summary->set_thread(0); } AsanThread *AsanThreadRegistry::GetMain() { @@ -111,45 +67,35 @@ AsanThread *AsanThreadRegistry::GetMain() { } AsanThread *AsanThreadRegistry::GetCurrent() { - CHECK(tls_key_created_); - AsanThread *thread = (AsanThread*)pthread_getspecific(tls_key_); - if ((!thread || (intptr_t)thread % 2) && FLAG_v >= 2) { - Report("GetCurrent: %p for thread %p\n", thread, pthread_self()); - } - if ((intptr_t)thread % 2) { - // Invalid pointer -- we've deleted the AsanThread already. Return NULL as - // if the TSD was empty. - // TODO(glider): if the code in the client TSD destructor calls - // pthread_create(), we'll set the parent tid of the spawned thread to NULL, - // although the creation stack will belong to the current thread. This may - // confuse the user, but is quite unlikely. - return NULL; - } else { - // NULL or valid pointer to AsanThread. - return thread; + AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet(); + if (!summary) { +#ifdef 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) { - if (FLAG_v >=2) { - Report("SetCurrent: %p for thread %p\n", t, pthread_self()); + 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. - intptr_t old_key = (intptr_t)pthread_getspecific(tls_key_); - CHECK(!old_key || old_key % 2); - CHECK(0 == pthread_setspecific(tls_key_, t)); - CHECK(pthread_getspecific(tls_key_) == t); -} - -pthread_key_t AsanThreadRegistry::GetTlsKey() { - return tls_key_; -} - -// Returns true iff DestroyAsanTsd() was already called for this thread. -bool AsanThreadRegistry::IsCurrentThreadDying() { - CHECK(tls_key_created_); - intptr_t thread = (intptr_t)pthread_getspecific(tls_key_); - return (bool)(thread % 2); + CHECK(AsanTSDGet() == 0); + AsanTSDSet(t->summary()); + CHECK(AsanTSDGet() == t->summary()); } AsanStats &AsanThreadRegistry::GetCurrentThreadStats() { @@ -163,19 +109,19 @@ AsanStats AsanThreadRegistry::GetAccumulatedStats() { return accumulated_stats_; } -size_t AsanThreadRegistry::GetCurrentAllocatedBytes() { +uptr AsanThreadRegistry::GetCurrentAllocatedBytes() { ScopedLock lock(&mu_); UpdateAccumulatedStatsUnlocked(); return accumulated_stats_.malloced - accumulated_stats_.freed; } -size_t AsanThreadRegistry::GetHeapSize() { +uptr AsanThreadRegistry::GetHeapSize() { ScopedLock lock(&mu_); UpdateAccumulatedStatsUnlocked(); return accumulated_stats_.mmaped; } -size_t AsanThreadRegistry::GetFreeBytes() { +uptr AsanThreadRegistry::GetFreeBytes() { ScopedLock lock(&mu_); UpdateAccumulatedStatsUnlocked(); return accumulated_stats_.mmaped @@ -185,18 +131,17 @@ size_t AsanThreadRegistry::GetFreeBytes() { + accumulated_stats_.really_freed_redzones; } -AsanThreadSummary *AsanThreadRegistry::FindByTid(int tid) { - CHECK(tid >= 0); +AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) { CHECK(tid < n_threads_); CHECK(thread_summaries_[tid]); return thread_summaries_[tid]; } -AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uintptr_t addr) { +AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) { ScopedLock lock(&mu_); - for (int tid = 0; tid < n_threads_; tid++) { + for (u32 tid = 0; tid < n_threads_; tid++) { AsanThread *t = thread_summaries_[tid]->thread(); - if (!t) continue; + if (!t || !(t->fake_stack().StackSize())) continue; if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) { return t; } @@ -205,20 +150,20 @@ AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uintptr_t addr) { } void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() { - for (int tid = 0; tid < n_threads_; tid++) { + for (u32 tid = 0; tid < n_threads_; tid++) { AsanThread *t = thread_summaries_[tid]->thread(); - if (t != NULL) { + if (t != 0) { FlushToAccumulatedStatsUnlocked(&t->stats()); } } } void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) { - // AsanStats consists of variables of type size_t only. - size_t *dst = (size_t*)&accumulated_stats_; - size_t *src = (size_t*)stats; - size_t num_fields = sizeof(AsanStats) / sizeof(size_t); - for (size_t i = 0; i < num_fields; i++) { + // 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; } diff --git a/lib/asan/asan_thread_registry.h b/lib/asan/asan_thread_registry.h index b80dd4da4df7..7037b9edc161 100644 --- a/lib/asan/asan_thread_registry.h +++ b/lib/asan/asan_thread_registry.h @@ -30,34 +30,32 @@ class AsanThreadRegistry { public: explicit AsanThreadRegistry(LinkerInitialized); void Init(); - void RegisterThread(AsanThread *thread, int parent_tid, - AsanStackTrace *stack); + void RegisterThread(AsanThread *thread); void UnregisterThread(AsanThread *thread); AsanThread *GetMain(); - // Get the current thread. May return NULL. + // Get the current thread. May return 0. AsanThread *GetCurrent(); void SetCurrent(AsanThread *t); - pthread_key_t GetTlsKey(); - bool IsCurrentThreadDying(); - int GetCurrentTidOrMinusOne() { + u32 GetCurrentTidOrInvalid() { + if (!inited_) return 0; AsanThread *t = GetCurrent(); - return t ? t->tid() : -1; + return t ? t->tid() : kInvalidTid; } // Returns stats for GetCurrent(), or stats for - // T0 if GetCurrent() returns NULL. + // T0 if GetCurrent() returns 0. AsanStats &GetCurrentThreadStats(); // Flushes all thread-local stats to accumulated stats, and returns // a copy of accumulated stats. AsanStats GetAccumulatedStats(); - size_t GetCurrentAllocatedBytes(); - size_t GetHeapSize(); - size_t GetFreeBytes(); + uptr GetCurrentAllocatedBytes(); + uptr GetHeapSize(); + uptr GetFreeBytes(); - AsanThreadSummary *FindByTid(int tid); - AsanThread *FindThreadByStackAddress(uintptr_t addr); + AsanThreadSummary *FindByTid(u32 tid); + AsanThread *FindThreadByStackAddress(uptr addr); private: void UpdateAccumulatedStatsUnlocked(); @@ -65,19 +63,14 @@ class AsanThreadRegistry { // and fills "stats" with zeroes. void FlushToAccumulatedStatsUnlocked(AsanStats *stats); - static const int kMaxNumberOfThreads = (1 << 22); // 4M + static const u32 kMaxNumberOfThreads = (1 << 22); // 4M AsanThreadSummary *thread_summaries_[kMaxNumberOfThreads]; AsanThread main_thread_; AsanThreadSummary main_thread_summary_; AsanStats accumulated_stats_; - int n_threads_; + u32 n_threads_; AsanLock mu_; - // For each thread tls_key_ stores the pointer to the corresponding - // AsanThread. - pthread_key_t tls_key_; - // This flag is updated only once at program startup, and then read - // by concurrent threads. - bool tls_key_created_; + bool inited_; }; // Returns a single instance of registry. diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc new file mode 100644 index 000000000000..9e899d5865fa --- /dev/null +++ b/lib/asan/asan_win.cc @@ -0,0 +1,181 @@ +//===-- asan_win.cc -------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// 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. +// +// Windows-specific details. +//===----------------------------------------------------------------------===// +#ifdef _WIN32 +#include + +#include +#include + +#include // FIXME: temporarily needed for placement new in AsanLock. + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_lock.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_libc.h" + +namespace __asan { + +// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1 +static AsanLock dbghelp_lock(LINKER_INITIALIZED); +static bool dbghelp_initialized = false; +#pragma comment(lib, "dbghelp.lib") + +void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) { + 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, max_size, tmp, 0), + 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; + } + + size = cs_ret - offset; + for (uptr i = 0; i < size; i++) + trace[i] = (uptr)tmp[i + offset]; +} + +bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size) { + ScopedLock lock(&dbghelp_lock); + if (!dbghelp_initialized) { + SymSetOptions(SYMOPT_DEFERRED_LOADS | + SYMOPT_UNDNAME | + SYMOPT_LOAD_LINES); + CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE)); + // FIXME: We don't call SymCleanup() on exit yet - should we? + dbghelp_initialized = true; + } + + // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + DWORD64 offset = 0; + BOOL got_objname = SymFromAddr(GetCurrentProcess(), + (DWORD64)addr, &offset, symbol); + if (!got_objname) + return false; + + DWORD unused; + IMAGEHLP_LINE64 info; + info.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), + (DWORD64)addr, &unused, &info); + int written = 0; + out_buffer[0] = '\0'; + // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too. + if (got_fileline) { + written += internal_snprintf(out_buffer + written, buffer_size - written, + " %s %s:%d", symbol->Name, + info.FileName, info.LineNumber); + } else { + written += internal_snprintf(out_buffer + written, buffer_size - written, + " %s+0x%p", symbol->Name, offset); + } + return true; +} + +// ---------------------- AsanLock ---------------- {{{1 +enum LockState { + LOCK_UNINITIALIZED = 0, + LOCK_READY = -1, +}; + +AsanLock::AsanLock(LinkerInitialized li) { + // FIXME: see comments in AsanLock::Lock() for the details. + CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED); + + CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); + InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); + owner_ = LOCK_READY; +} + +void AsanLock::Lock() { + if (owner_ == LOCK_UNINITIALIZED) { + // FIXME: hm, global AsanLock objects are not initialized?!? + // This might be a side effect of the clang+cl+link Frankenbuild... + new(this) AsanLock((LinkerInitialized)(LINKER_INITIALIZED + 1)); + + // FIXME: If it turns out the linker doesn't invoke our + // constructors, we should probably manually Lock/Unlock all the global + // locks while we're starting in one thread to avoid double-init races. + } + EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_); + CHECK(owner_ == LOCK_READY); + owner_ = GetThreadSelf(); +} + +void AsanLock::Unlock() { + CHECK(owner_ == GetThreadSelf()); + owner_ = LOCK_READY; + LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); +} + +// ---------------------- TSD ---------------- {{{1 +static bool tsd_key_inited = false; + +static __declspec(thread) void *fake_tsd = 0; + +void AsanTSDInit(void (*destructor)(void *tsd)) { + // FIXME: we're ignoring the destructor for now. + tsd_key_inited = true; +} + +void *AsanTSDGet() { + CHECK(tsd_key_inited); + return fake_tsd; +} + +void AsanTSDSet(void *tsd) { + CHECK(tsd_key_inited); + fake_tsd = tsd; +} + +// ---------------------- Various stuff ---------------- {{{1 +void *AsanDoesNotSupportStaticLinkage() { +#if defined(_DEBUG) +#error Please build the runtime with a non-debug CRT: /MD or /MT +#endif + return 0; +} + +void SetAlternateSignalStack() { + // FIXME: Decide what to do on Windows. +} + +void UnsetAlternateSignalStack() { + // FIXME: Decide what to do on Windows. +} + +void InstallSignalHandlers() { + // FIXME: Decide what to do on Windows. +} + +void AsanPlatformThreadInit() { + // Nothing here for now. +} + +} // namespace __asan + +#endif // _WIN32 diff --git a/lib/asan/output_tests/clone_test.cc b/lib/asan/output_tests/clone_test.cc new file mode 100644 index 000000000000..b18d2550bc7c --- /dev/null +++ b/lib/asan/output_tests/clone_test.cc @@ -0,0 +1,34 @@ +#ifdef __linux__ +#include +#include +#include +#include +#include +#include + +int Child(void *arg) { + char x[32] = {0}; // Stack gets poisoned. + printf("Child: %p\n", x); + _exit(1); // NoReturn, stack will remain unpoisoned unless we do something. +} + +int main(int argc, char **argv) { + const int kStackSize = 1 << 20; + char child_stack[kStackSize + 1]; + char *sp = child_stack + kStackSize; // Stack grows down. + printf("Parent: %p\n", sp); + pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0); + waitpid(clone_pid, NULL, 0); + for (int i = 0; i < kStackSize; i++) + child_stack[i] = i; + int ret = child_stack[argc - 1]; + printf("PASSED\n"); + return ret; +} +#else // not __linux__ +#include +int main() { + printf("PASSED\n"); + // Check-Common: PASSED +} +#endif diff --git a/lib/asan/output_tests/deep_tail_call.cc b/lib/asan/output_tests/deep_tail_call.cc new file mode 100644 index 000000000000..cb69e8925197 --- /dev/null +++ b/lib/asan/output_tests/deep_tail_call.cc @@ -0,0 +1,15 @@ +// Check-Common: AddressSanitizer global-buffer-overflow +int global[10]; +// Check-Common: {{#0.*call4}} +void __attribute__((noinline)) call4(int i) { global[i+10]++; } +// Check-Common: {{#1.*call3}} +void __attribute__((noinline)) call3(int i) { call4(i); } +// Check-Common: {{#2.*call2}} +void __attribute__((noinline)) call2(int i) { call3(i); } +// Check-Common: {{#3.*call1}} +void __attribute__((noinline)) call1(int i) { call2(i); } +// Check-Common: {{#4.*main}} +int main(int argc, char **argv) { + call1(argc); + return global[0]; +} diff --git a/lib/asan/output_tests/default_options.cc b/lib/asan/output_tests/default_options.cc new file mode 100644 index 000000000000..d6c70291e6ff --- /dev/null +++ b/lib/asan/output_tests/default_options.cc @@ -0,0 +1,12 @@ +const char *kAsanDefaultOptions="verbosity=1 foo=bar"; + +extern "C" +__attribute__((no_address_safety_analysis)) +const char *__asan_default_options() { + return kAsanDefaultOptions; +} + +int main() { + // Check-Common: foo=bar + return 0; +} diff --git a/lib/asan/tests/dlclose-test-so.cc b/lib/asan/output_tests/dlclose-test-so.cc similarity index 91% rename from lib/asan/tests/dlclose-test-so.cc rename to lib/asan/output_tests/dlclose-test-so.cc index fae2f813abb7..73e00507358a 100644 --- a/lib/asan/tests/dlclose-test-so.cc +++ b/lib/asan/output_tests/dlclose-test-so.cc @@ -1,4 +1,4 @@ -//===-- asan_rtl.cc ------------*- C++ -*-===// +//===----------- dlclose-test-so.cc -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // diff --git a/lib/asan/tests/dlclose-test.cc b/lib/asan/output_tests/dlclose-test.cc similarity index 95% rename from lib/asan/tests/dlclose-test.cc rename to lib/asan/output_tests/dlclose-test.cc index 307886667b15..16126eb9042f 100644 --- a/lib/asan/tests/dlclose-test.cc +++ b/lib/asan/output_tests/dlclose-test.cc @@ -1,4 +1,4 @@ -//===-- asan_rtl.cc ------------*- C++ -*-===// +//===----------- dlclose-test.cc --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -69,5 +69,6 @@ int main(int argc, char *argv[]) { } addr[1] = 2; // BOOM (if the bug is not fixed). printf("PASS\n"); + // Check-Common: PASS return 0; } diff --git a/lib/asan/tests/global-overflow.cc b/lib/asan/output_tests/global-overflow.cc similarity index 52% rename from lib/asan/tests/global-overflow.cc rename to lib/asan/output_tests/global-overflow.cc index b85c4d2a18bf..a63eb733365f 100644 --- a/lib/asan/tests/global-overflow.cc +++ b/lib/asan/output_tests/global-overflow.cc @@ -7,6 +7,10 @@ int main(int argc, char **argv) { memset(YYY, 0, 10); memset(ZZZ, 0, 10); int res = YYY[argc * 10]; // BOOOM + // Check-Common: {{READ of size 1 at 0x.* thread T0}} + // Check-Common: {{ #0 0x.* in main .*global-overflow.cc:9}} + // Check-Common: {{0x.* is located 0 bytes to the right of global variable}} + // Check-Common: {{.*YYY.* of size 10}} res += XXX[argc] + ZZZ[argc]; return res; } diff --git a/lib/asan/output_tests/heap-overflow.cc b/lib/asan/output_tests/heap-overflow.cc new file mode 100644 index 000000000000..534fbe00b355 --- /dev/null +++ b/lib/asan/output_tests/heap-overflow.cc @@ -0,0 +1,22 @@ +#include +#include +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + free(x); + return res; +} + +// Check-Common: {{READ of size 1 at 0x.* thread T0}} +// Check-Common: {{ #0 0x.* in main .*heap-overflow.cc:6}} +// Check-Common: {{0x.* is located 0 bytes to the right of 10-byte region}} +// Check-Common: {{allocated by thread T0 here:}} + +// Check-Linux: {{ #0 0x.* in .*malloc}} +// Check-Linux: {{ #1 0x.* in main .*heap-overflow.cc:4}} + +// Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}} +// Check-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}} +// Check-Darwin: {{ #2 0x.* in malloc.*}} +// Check-Darwin: {{ #3 0x.* in main heap-overflow.cc:4}} diff --git a/lib/asan/output_tests/interception_failure_test-linux.cc b/lib/asan/output_tests/interception_failure_test-linux.cc new file mode 100644 index 000000000000..9e8b7536906b --- /dev/null +++ b/lib/asan/output_tests/interception_failure_test-linux.cc @@ -0,0 +1,17 @@ +#include +#include + +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return 0; +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); +} + +// Check-Common: my_strtol_interceptor +// CHECK-NOT: heap-use-after-free + diff --git a/lib/asan/output_tests/interception_malloc_test-linux.cc b/lib/asan/output_tests/interception_malloc_test-linux.cc new file mode 100644 index 000000000000..4bb3bd66de33 --- /dev/null +++ b/lib/asan/output_tests/interception_malloc_test-linux.cc @@ -0,0 +1,19 @@ +#include +#include +#include + +extern "C" void *__interceptor_malloc(size_t size); +extern "C" void *malloc(size_t size) { + write(2, "malloc call\n", sizeof("malloc call\n") - 1); + return __interceptor_malloc(size); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); +} + +// Check-Common: malloc call +// Check-Common: heap-use-after-free + diff --git a/lib/asan/output_tests/interception_test-linux.cc b/lib/asan/output_tests/interception_test-linux.cc new file mode 100644 index 000000000000..0523510465a1 --- /dev/null +++ b/lib/asan/output_tests/interception_test-linux.cc @@ -0,0 +1,18 @@ +#include +#include + +extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base); +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return __interceptor_strtol(nptr, endptr, base); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); +} + +// Check-Common: my_strtol_interceptor +// Check-Common: heap-use-after-free + diff --git a/lib/asan/output_tests/large_func_test.cc b/lib/asan/output_tests/large_func_test.cc new file mode 100644 index 000000000000..49751b39277a --- /dev/null +++ b/lib/asan/output_tests/large_func_test.cc @@ -0,0 +1,48 @@ +#include +__attribute__((noinline)) +static void LargeFunction(int *x, int zero) { + x[0]++; + x[1]++; + x[2]++; + x[3]++; + x[4]++; + x[5]++; + x[6]++; + x[7]++; + x[8]++; + x[9]++; + + x[zero + 111]++; // we should report this exact line + + x[10]++; + x[11]++; + x[12]++; + x[13]++; + x[14]++; + x[15]++; + x[16]++; + x[17]++; + x[18]++; + x[19]++; +} + +int main(int argc, char **argv) { + int *x = new int[100]; + LargeFunction(x, argc - 1); + delete x; +} + +// Check-Common: {{.*ERROR: AddressSanitizer heap-buffer-overflow on address}} +// Check-Common: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} +// Check-Common: {{READ of size 4 at 0x.* thread T0}} + +// atos incorrectly extracts the symbol name for the static functions on +// Darwin. +// Check-Linux: {{ #0 0x.* in LargeFunction.*large_func_test.cc:15}} +// Check-Darwin: {{ #0 0x.* in .*LargeFunction.*large_func_test.cc:15}} + +// Check-Common: {{ #1 0x.* in main .*large_func_test.cc:31}} +// Check-Common: {{0x.* is located 44 bytes to the right of 400-byte region}} +// Check-Common: {{allocated by thread T0 here:}} +// Check-Common: {{ #0 0x.* in operator new.*}} +// Check-Common: {{ #1 0x.* in main .*large_func_test.cc:30}} diff --git a/lib/asan/output_tests/memcmp_test.cc b/lib/asan/output_tests/memcmp_test.cc new file mode 100644 index 000000000000..d0e5a43b4355 --- /dev/null +++ b/lib/asan/output_tests/memcmp_test.cc @@ -0,0 +1,10 @@ +#include +int main(int argc, char **argv) { + char a1[] = {argc, 2, 3, 4}; + char a2[] = {1, 2*argc, 3, 4}; +// Check-Common: AddressSanitizer stack-buffer-overflow +// Check-Common: {{#0.*memcmp}} +// Check-Common: {{#1.*main}} + int res = memcmp(a1, a2, 4 + argc); // BOOM + return res; +} diff --git a/lib/asan/output_tests/null_deref.cc b/lib/asan/output_tests/null_deref.cc new file mode 100644 index 000000000000..c152a4202e33 --- /dev/null +++ b/lib/asan/output_tests/null_deref.cc @@ -0,0 +1,17 @@ +__attribute__((noinline)) +static void NullDeref(int *ptr) { + ptr[10]++; +} +int main() { + NullDeref((int*)0); +} + +// Check-Common: {{.*ERROR: AddressSanitizer crashed on unknown address}} +// Check-Common: {{0x0*00028 .*pc 0x.*}} +// Check-Common: {{AddressSanitizer can not provide additional info. ABORTING}} + +// atos on Mac cannot extract the symbol name correctly. +// Check-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:3}} +// Check-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:3}} + +// Check-Common: {{ #1 0x.* in main.*null_deref.cc:6}} diff --git a/lib/asan/tests/shared-lib-test-so.cc b/lib/asan/output_tests/shared-lib-test-so.cc similarity index 87% rename from lib/asan/tests/shared-lib-test-so.cc rename to lib/asan/output_tests/shared-lib-test-so.cc index c3b3bc22aed2..686a24578082 100644 --- a/lib/asan/tests/shared-lib-test-so.cc +++ b/lib/asan/output_tests/shared-lib-test-so.cc @@ -1,4 +1,4 @@ -//===-- asan_rtl.cc ------------*- C++ -*-===// +//===----------- shared-lib-test-so.cc --------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // diff --git a/lib/asan/tests/shared-lib-test.cc b/lib/asan/output_tests/shared-lib-test.cc similarity index 73% rename from lib/asan/tests/shared-lib-test.cc rename to lib/asan/output_tests/shared-lib-test.cc index e492572c7280..060fcde35f0d 100644 --- a/lib/asan/tests/shared-lib-test.cc +++ b/lib/asan/output_tests/shared-lib-test.cc @@ -1,4 +1,4 @@ -//===-- asan_rtl.cc ------------*- C++ -*-===// +//===----------- shared-lib-test.cc -----------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -32,6 +32,11 @@ int main(int argc, char *argv[]) { if (!inc) return 1; printf("ok\n"); inc(1); - inc(-1); + inc(-1); // BOOM return 0; } + +// Check-Common: {{.*ERROR: AddressSanitizer global-buffer-overflow}} +// Check-Common: {{READ of size 4 at 0x.* thread T0}} +// Check-Common: {{ #0 0x.*}} +// Check-Common: {{ #1 0x.* in main .*shared-lib-test.cc:35}} diff --git a/lib/asan/output_tests/stack-overflow.cc b/lib/asan/output_tests/stack-overflow.cc new file mode 100644 index 000000000000..35fa8a66c34c --- /dev/null +++ b/lib/asan/output_tests/stack-overflow.cc @@ -0,0 +1,11 @@ +#include +int main(int argc, char **argv) { + char x[10]; + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + return res; +} + +// Check-Common: {{READ of size 1 at 0x.* thread T0}} +// Check-Common: {{ #0 0x.* in main .*stack-overflow.cc:5}} +// Check-Common: {{Address 0x.* is .* frame
}} diff --git a/lib/asan/tests/stack-use-after-return.cc b/lib/asan/output_tests/stack-use-after-return.cc.disabled similarity index 65% rename from lib/asan/tests/stack-use-after-return.cc rename to lib/asan/output_tests/stack-use-after-return.cc.disabled index 9098edf0adb8..f49715737430 100644 --- a/lib/asan/tests/stack-use-after-return.cc +++ b/lib/asan/output_tests/stack-use-after-return.cc.disabled @@ -16,6 +16,9 @@ __attribute__((noinline)) void Func2(char *x) { fprintf(stderr, "2: %p\n", x); *x = 1; + // Check-Common: {{WRITE of size 1 .* thread T0}} + // Check-Common: {{ #0.*Func2.*stack-use-after-return.cc:18}} + // Check-Common: {{is located in frame <.*Func1.*> of T0's stack}} } int main(int argc, char **argv) { diff --git a/lib/asan/output_tests/strncpy-overflow.cc b/lib/asan/output_tests/strncpy-overflow.cc new file mode 100644 index 000000000000..66d5810b7040 --- /dev/null +++ b/lib/asan/output_tests/strncpy-overflow.cc @@ -0,0 +1,24 @@ +#include +#include +int main(int argc, char **argv) { + char *hello = (char*)malloc(6); + strcpy(hello, "hello"); + char *short_buffer = (char*)malloc(9); + strncpy(short_buffer, hello, 10); // BOOM + return short_buffer[8]; +} + +// Check-Common: {{WRITE of size 1 at 0x.* thread T0}} +// Check-Linux: {{ #0 0x.* in .*strncpy}} +// Check-Darwin: {{ #0 0x.* in wrap_strncpy}} +// Check-Common: {{ #1 0x.* in main .*strncpy-overflow.cc:7}} +// Check-Common: {{0x.* is located 0 bytes to the right of 9-byte region}} +// Check-Common: {{allocated by thread T0 here:}} + +// Check-Linux: {{ #0 0x.* in .*malloc}} +// Check-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:6}} + +// Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}} +// Check-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}} +// Check-Darwin: {{ #2 0x.* in malloc.*}} +// Check-Darwin: {{ #3 0x.* in main .*strncpy-overflow.cc:6}} diff --git a/lib/asan/output_tests/test_output.sh b/lib/asan/output_tests/test_output.sh new file mode 100755 index 000000000000..6510043396e4 --- /dev/null +++ b/lib/asan/output_tests/test_output.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +set -e # fail on any error + +OS=`uname` +CXX=$1 +CC=$2 +FILE_CHECK=$3 +CXXFLAGS="-mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -g" +SYMBOLIZER=../scripts/asan_symbolize.py +TMP_ASAN_REPORT=asan_report.tmp + +run_program() { + ./$1 2>&1 | $SYMBOLIZER 2> /dev/null | c++filt > $TMP_ASAN_REPORT +} + +# check_program exe_file source_file check_prefixf +check_program() { + run_program $1 + $FILE_CHECK $2 --check-prefix=$3 < $TMP_ASAN_REPORT + rm -f $TMP_ASAN_REPORT +} + +C_TEST=use-after-free +echo "Sanity checking a test in pure C" +$CC -g -faddress-sanitizer -O2 $C_TEST.c +check_program a.out $C_TEST.c CHECK +rm ./a.out + +echo "Sanity checking a test in pure C with -pie" +$CC -g -faddress-sanitizer -O2 $C_TEST.c -pie +check_program a.out $C_TEST.c CHECK +rm ./a.out + +echo "Testing sleep_before_dying" +$CC -g -faddress-sanitizer -O2 $C_TEST.c +export ASAN_OPTIONS="sleep_before_dying=1" +check_program a.out $C_TEST.c CHECKSLEEP +export ASAN_OPTIONS="" +rm ./a.out + +# FIXME: some tests do not need to be ran for all the combinations of arch +# and optimization mode. +for t in *.cc; do + for b in 32 64; do + for O in 0 1 2 3; do + c=`basename $t .cc` + if [[ "$c" == *"-so" ]]; then + continue + fi + if [[ "$c" == *"-linux" ]]; then + if [[ "$OS" != "Linux" ]]; then + continue + fi + fi + c_so=$c-so + exe=$c.$b.O$O + so=$c.$b.O$O-so.so + echo testing $exe + build_command="$CXX $CXXFLAGS -m$b -faddress-sanitizer -O$O $c.cc -o $exe" + [ "$DEBUG" == "1" ] && echo $build_command + $build_command + [ -e "$c_so.cc" ] && $CXX $CXXFLAGS -m$b -faddress-sanitizer -O$O $c_so.cc -fPIC -shared -o $so + run_program $exe + # Check common expected lines for OS. + $FILE_CHECK $c.cc --check-prefix="Check-Common" < $TMP_ASAN_REPORT + # Check OS-specific lines. + if [ `grep -c "Check-$OS" $c.cc` -gt 0 ] + then + $FILE_CHECK $c.cc --check-prefix="Check-$OS" < $TMP_ASAN_REPORT + fi + rm ./$exe + rm ./$TMP_ASAN_REPORT + [ -e "$so" ] && rm ./$so + done + done +done + +exit 0 diff --git a/lib/asan/tests/use-after-free.c b/lib/asan/output_tests/use-after-free.c similarity index 61% rename from lib/asan/tests/use-after-free.c rename to lib/asan/output_tests/use-after-free.c index 60626bff778a..801d3f68a466 100644 --- a/lib/asan/tests/use-after-free.c +++ b/lib/asan/output_tests/use-after-free.c @@ -4,3 +4,6 @@ int main() { free(x); return x[5]; } + +// CHECK: heap-use-after-free +// CHECKSLEEP: Sleeping for 1 second diff --git a/lib/asan/output_tests/use-after-free.cc b/lib/asan/output_tests/use-after-free.cc new file mode 100644 index 000000000000..c3e9dbe6db75 --- /dev/null +++ b/lib/asan/output_tests/use-after-free.cc @@ -0,0 +1,31 @@ +#include +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; +} + +// Check-Common: {{.*ERROR: AddressSanitizer heap-use-after-free on address}} +// Check-Common: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} +// Check-Common: {{READ of size 1 at 0x.* thread T0}} +// Check-Common: {{ #0 0x.* in main .*use-after-free.cc:5}} +// Check-Common: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}} +// Check-Common: {{freed by thread T0 here:}} + +// Check-Linux: {{ #0 0x.* in .*free}} +// Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:4}} + +// Check-Darwin: {{ #0 0x.* in .*mz_free.*}} +// We override free() on Darwin, thus no malloc_zone_free +// Check-Darwin: {{ #1 0x.* in wrap_free}} +// Check-Darwin: {{ #2 0x.* in main .*use-after-free.cc:4}} + +// Check-Common: {{previously allocated by thread T0 here:}} + +// Check-Linux: {{ #0 0x.* in .*malloc}} +// Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:3}} + +// Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}} +// Check-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}} +// Check-Darwin: {{ #2 0x.* in malloc.*}} +// Check-Darwin: {{ #3 0x.* in main .*use-after-free.cc:3}} diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py index 80b592725161..e4897d0c7649 100755 --- a/lib/asan/scripts/asan_symbolize.py +++ b/lib/asan/scripts/asan_symbolize.py @@ -14,21 +14,16 @@ import subprocess pipes = {} +filetypes = {} +DEBUG=False + +def fix_filename(file_name): + for path_to_cut in sys.argv[1:]: + file_name = re.sub(".*" + path_to_cut, "", file_name) + file_name = re.sub(".*asan_[a-z_]*.cc:[0-9]*", "_asan_rtl_", file_name) + file_name = re.sub(".*crtstuff.c:0", "???:0", file_name) + return file_name -def patch_address(frameno, addr_s): - ''' Subtracts 1 or 2 from the top frame's address. - Top frame is normally the return address from asan_report* - call, which is not expected to return at all. Because of that, this - address often belongs to the next source code line, or even to a different - function. ''' - if frameno == '0': - addr = int(addr_s, 16) - if os.uname()[4].startswith('arm'): - # Cancel the Thumb bit - addr = addr & (~1) - addr -= 1 - return hex(addr) - return addr_s # TODO(glider): need some refactoring here def symbolize_addr2line(line): @@ -38,7 +33,6 @@ def symbolize_addr2line(line): frameno = match.group(2) binary = match.group(3) addr = match.group(4) - addr = patch_address(frameno, addr) if not pipes.has_key(binary): pipes[binary] = subprocess.Popen(["addr2line", "-f", "-e", binary], stdin=subprocess.PIPE, stdout=subprocess.PIPE) @@ -50,15 +44,25 @@ def symbolize_addr2line(line): except: function_name = "" file_name = "" - for path_to_cut in sys.argv[1:]: - file_name = re.sub(".*" + path_to_cut, "", file_name) - file_name = re.sub(".*asan_[a-z_]*.cc:[0-9]*", "_asan_rtl_", file_name) - file_name = re.sub(".*crtstuff.c:0", "???:0", file_name) + file_name = fix_filename(file_name) print match.group(1), "in", function_name, file_name else: print line.rstrip() + +def get_macho_filetype(binary): + if not filetypes.has_key(binary): + otool_pipe = subprocess.Popen(["otool", "-Vh", binary], + stdin=subprocess.PIPE, stdout=subprocess.PIPE) + otool_line = "".join(otool_pipe.stdout.readlines()) + for t in ["DYLIB", "EXECUTE"]: + if t in otool_line: + filetypes[binary] = t + otool_pipe.stdin.close() + return filetypes[binary] + + def symbolize_atos(line): #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) match = re.match('^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line) @@ -66,27 +70,49 @@ def symbolize_atos(line): #print line prefix = match.group(1) frameno = match.group(2) - addr = match.group(3) + orig_addr = match.group(3) binary = match.group(4) offset = match.group(5) - addr = patch_address(frameno, addr) - load_addr = int(addr, 16) - int(offset, 16) + addr = orig_addr + load_addr = hex(int(orig_addr, 16) - int(offset, 16)) + filetype = get_macho_filetype(binary) + if not pipes.has_key(binary): - #print "atos -o %s -l %s" % (binary, hex(load_addr)) - pipes[binary] = subprocess.Popen(["atos", "-o", binary], - stdin=subprocess.PIPE, stdout=subprocess.PIPE,) + # Guess which arch we're running. 10 = len("0x") + 8 hex digits. + if len(addr) > 10: + arch = "x86_64" + else: + arch = "i386" + + if filetype == "DYLIB": + load_addr = "0x0" + if DEBUG: + print "atos -o %s -arch %s -l %s" % (binary, arch, load_addr) + cmd = ["atos", "-o", binary, "-arch", arch, "-l", load_addr] + pipes[binary] = subprocess.Popen(cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) p = pipes[binary] - # TODO(glider): how to tell if the address is absolute? - if ".app/" in binary and not ".framework" in binary: - print >>p.stdin, "%s" % addr - else: + if filetype == "DYLIB": print >>p.stdin, "%s" % offset + else: + print >>p.stdin, "%s" % addr # TODO(glider): it's more efficient to make a batch atos run for each binary. p.stdin.close() atos_line = p.stdout.readline().rstrip() + # A well-formed atos response looks like this: + # foo(type1, type2) (in object.name) (filename.cc:80) + match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line) + #print "atos_line: ", atos_line + if match: + function_name = match.group(1) + function_name = re.sub("\(.*?\)", "", function_name) + file_name = fix_filename(match.group(3)) + print "%s%s in %s %s" % (prefix, addr, function_name, file_name) + else: + print "%s%s in %s" % (prefix, addr, atos_line) del pipes[binary] - - print "%s%s in %s" % (prefix, addr, atos_line) else: print line.rstrip() diff --git a/lib/asan/sysinfo/LICENSE.TXT b/lib/asan/sysinfo/LICENSE.TXT deleted file mode 100644 index b519af5aedd3..000000000000 --- a/lib/asan/sysinfo/LICENSE.TXT +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (c) 2005, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/lib/asan/sysinfo/basictypes.h b/lib/asan/sysinfo/basictypes.h deleted file mode 100644 index ac21f8c29833..000000000000 --- a/lib/asan/sysinfo/basictypes.h +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright (c) 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef _BASICTYPES_H_ -#define _BASICTYPES_H_ - -#include // uint16_t might be here; PRId64 too. -#include // to get uint16_t (ISO naming madness) -#include // our last best hope for uint16_t - -// Standard typedefs -// All Google code is compiled with -funsigned-char to make "char" -// unsigned. Google code therefore doesn't need a "uchar" type. -// TODO(csilvers): how do we make sure unsigned-char works on non-gcc systems? -typedef signed char schar; -typedef int8_t int8; -typedef int16_t int16; -typedef int32_t int32; -typedef int64_t int64; - -// NOTE: unsigned types are DANGEROUS in loops and other arithmetical -// places. Use the signed types unless your variable represents a bit -// pattern (eg a hash value) or you really need the extra bit. Do NOT -// use 'unsigned' to express "this value should always be positive"; -// use assertions for this. - -typedef uint8_t uint8; -typedef uint16_t uint16; -typedef uint32_t uint32; -typedef uint64_t uint64; - -const uint16 kuint16max = ( (uint16) 0xFFFF); -const uint32 kuint32max = ( (uint32) 0xFFFFFFFF); -const uint64 kuint64max = ( (((uint64) kuint32max) << 32) | kuint32max ); - -const int8 kint8max = ( ( int8) 0x7F); -const int16 kint16max = ( ( int16) 0x7FFF); -const int32 kint32max = ( ( int32) 0x7FFFFFFF); -const int64 kint64max = ( ((( int64) kint32max) << 32) | kuint32max ); - -const int8 kint8min = ( ( int8) 0x80); -const int16 kint16min = ( ( int16) 0x8000); -const int32 kint32min = ( ( int32) 0x80000000); -const int64 kint64min = ( ((( int64) kint32min) << 32) | 0 ); - -// Define the "portable" printf and scanf macros, if they're not -// already there (via the inttypes.h we #included above, hopefully). -// Mostly it's old systems that don't support inttypes.h, so we assume -// they're 32 bit. -#ifndef PRIx64 -#define PRIx64 "llx" -#endif -#ifndef SCNx64 -#define SCNx64 "llx" -#endif -#ifndef PRId64 -#define PRId64 "lld" -#endif -#ifndef SCNd64 -#define SCNd64 "lld" -#endif -#ifndef PRIu64 -#define PRIu64 "llu" -#endif -#ifndef PRIxPTR -#define PRIxPTR "lx" -#endif - -// Also allow for printing of a pthread_t. -#define GPRIuPTHREAD "lu" -#define GPRIxPTHREAD "lx" -#if defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) -#define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast(pthreadt) -#else -#define PRINTABLE_PTHREAD(pthreadt) pthreadt -#endif - -// A macro to disallow the evil copy constructor and operator= functions -// This should be used in the private: declarations for a class -#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - -// An alternate name that leaves out the moral judgment... :-) -#define DISALLOW_COPY_AND_ASSIGN(TypeName) DISALLOW_EVIL_CONSTRUCTORS(TypeName) - -// The COMPILE_ASSERT macro can be used to verify that a compile time -// expression is true. For example, you could use it to verify the -// size of a static array: -// -// COMPILE_ASSERT(sizeof(num_content_type_names) == sizeof(int), -// content_type_names_incorrect_size); -// -// or to make sure a struct is smaller than a certain size: -// -// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); -// -// The second argument to the macro is the name of the variable. If -// the expression is false, most compilers will issue a warning/error -// containing the name of the variable. -// -// Implementation details of COMPILE_ASSERT: -// -// - COMPILE_ASSERT works by defining an array type that has -1 -// elements (and thus is invalid) when the expression is false. -// -// - The simpler definition -// -// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] -// -// does not work, as gcc supports variable-length arrays whose sizes -// are determined at run-time (this is gcc's extension and not part -// of the C++ standard). As a result, gcc fails to reject the -// following code with the simple definition: -// -// int foo; -// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is -// // not a compile-time constant. -// -// - By using the type CompileAssert<(bool(expr))>, we ensures that -// expr is a compile-time constant. (Template arguments must be -// determined at compile-time.) -// -// - The outter parentheses in CompileAssert<(bool(expr))> are necessary -// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written -// -// CompileAssert -// -// instead, these compilers will refuse to compile -// -// COMPILE_ASSERT(5 > 0, some_message); -// -// (They seem to think the ">" in "5 > 0" marks the end of the -// template argument list.) -// -// - The array size is (bool(expr) ? 1 : -1), instead of simply -// -// ((expr) ? 1 : -1). -// -// This is to avoid running into a bug in MS VC 7.1, which -// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. - -template -struct CompileAssert { -}; - -#define COMPILE_ASSERT(expr, msg) \ - typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] - -#define arraysize(a) (sizeof(a) / sizeof(*(a))) - -#define OFFSETOF_MEMBER(strct, field) \ - (reinterpret_cast(&reinterpret_cast(16)->field) - \ - reinterpret_cast(16)) - -#ifdef HAVE___ATTRIBUTE__ -# define ATTRIBUTE_WEAK __attribute__((weak)) -# define ATTRIBUTE_NOINLINE __attribute__((noinline)) -#else -# define ATTRIBUTE_WEAK -# define ATTRIBUTE_NOINLINE -#endif - -// Section attributes are supported for both ELF and Mach-O, but in -// very different ways. Here's the API we provide: -// 1) ATTRIBUTE_SECTION: put this with the declaration of all functions -// you want to be in the same linker section -// 2) DEFINE_ATTRIBUTE_SECTION_VARS: must be called once per unique -// name. You want to make sure this is executed before any -// DECLARE_ATTRIBUTE_SECTION_VARS; the easiest way is to put them -// in the same .cc file. Put this call at the global level. -// 3) INIT_ATTRIBUTE_SECTION_VARS: you can scatter calls to this in -// multiple places to help ensure execution before any -// DECLARE_ATTRIBUTE_SECTION_VARS. You must have at least one -// DEFINE, but you can have many INITs. Put each in its own scope. -// 4) DECLARE_ATTRIBUTE_SECTION_VARS: must be called before using -// ATTRIBUTE_SECTION_START or ATTRIBUTE_SECTION_STOP on a name. -// Put this call at the global level. -// 5) ATTRIBUTE_SECTION_START/ATTRIBUTE_SECTION_STOP: call this to say -// where in memory a given section is. All functions declared with -// ATTRIBUTE_SECTION are guaranteed to be between START and STOP. - -#if defined(HAVE___ATTRIBUTE__) && defined(__ELF__) -# define ATTRIBUTE_SECTION(name) __attribute__ ((section (#name))) - - // Weak section declaration to be used as a global declaration - // for ATTRIBUTE_SECTION_START|STOP(name) to compile and link - // even without functions with ATTRIBUTE_SECTION(name). -# define DECLARE_ATTRIBUTE_SECTION_VARS(name) \ - extern char __start_##name[] ATTRIBUTE_WEAK; \ - extern char __stop_##name[] ATTRIBUTE_WEAK -# define INIT_ATTRIBUTE_SECTION_VARS(name) // no-op for ELF -# define DEFINE_ATTRIBUTE_SECTION_VARS(name) // no-op for ELF - - // Return void* pointers to start/end of a section of code with functions - // having ATTRIBUTE_SECTION(name), or 0 if no such function exists. - // One must DECLARE_ATTRIBUTE_SECTION(name) for this to compile and link. -# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast(__start_##name)) -# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast(__stop_##name)) -# define HAVE_ATTRIBUTE_SECTION_START 1 - -#elif defined(HAVE___ATTRIBUTE__) && defined(__MACH__) -# define ATTRIBUTE_SECTION(name) __attribute__ ((section ("__TEXT, " #name))) - -#include -#include -class AssignAttributeStartEnd { - public: - AssignAttributeStartEnd(const char* name, char** pstart, char** pend) { - // Find out what dynamic library name is defined in - if (_dyld_present()) { - for (int i = _dyld_image_count() - 1; i >= 0; --i) { - const mach_header* hdr = _dyld_get_image_header(i); -#ifdef MH_MAGIC_64 - if (hdr->magic == MH_MAGIC_64) { - uint64_t len; - *pstart = getsectdatafromheader_64((mach_header_64*)hdr, - "__TEXT", name, &len); - if (*pstart) { // NULL if not defined in this dynamic library - *pstart += _dyld_get_image_vmaddr_slide(i); // correct for reloc - *pend = *pstart + len; - return; - } - } -#endif - if (hdr->magic == MH_MAGIC) { - uint32_t len; - *pstart = getsectdatafromheader(hdr, "__TEXT", name, &len); - if (*pstart) { // NULL if not defined in this dynamic library - *pstart += _dyld_get_image_vmaddr_slide(i); // correct for reloc - *pend = *pstart + len; - return; - } - } - } - } - // If we get here, not defined in a dll at all. See if defined statically. - unsigned long len; // don't ask me why this type isn't uint32_t too... - *pstart = getsectdata("__TEXT", name, &len); - *pend = *pstart + len; - } -}; - -#define DECLARE_ATTRIBUTE_SECTION_VARS(name) \ - extern char* __start_##name; \ - extern char* __stop_##name - -#define INIT_ATTRIBUTE_SECTION_VARS(name) \ - DECLARE_ATTRIBUTE_SECTION_VARS(name); \ - static const AssignAttributeStartEnd __assign_##name( \ - #name, &__start_##name, &__stop_##name) - -#define DEFINE_ATTRIBUTE_SECTION_VARS(name) \ - char* __start_##name, *__stop_##name; \ - INIT_ATTRIBUTE_SECTION_VARS(name) - -# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast(__start_##name)) -# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast(__stop_##name)) -# define HAVE_ATTRIBUTE_SECTION_START 1 - -#else // not HAVE___ATTRIBUTE__ && __ELF__, nor HAVE___ATTRIBUTE__ && __MACH__ -# define ATTRIBUTE_SECTION(name) -# define DECLARE_ATTRIBUTE_SECTION_VARS(name) -# define INIT_ATTRIBUTE_SECTION_VARS(name) -# define DEFINE_ATTRIBUTE_SECTION_VARS(name) -# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast(0)) -# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast(0)) - -#endif // HAVE___ATTRIBUTE__ and __ELF__ or __MACH__ - -#if defined(HAVE___ATTRIBUTE__) && (defined(__i386__) || defined(__x86_64__)) -# define CACHELINE_SIZE 64 -# define CACHELINE_ALIGNED __attribute__((aligned(CACHELINE_SIZE))) -#else -# define CACHELINE_ALIGNED -#endif // defined(HAVE___ATTRIBUTE__) && (__i386__ || __x86_64__) - - -// The following enum should be used only as a constructor argument to indicate -// that the variable has static storage class, and that the constructor should -// do nothing to its state. It indicates to the reader that it is legal to -// declare a static nistance of the class, provided the constructor is given -// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a -// static variable that has a constructor or a destructor because invocation -// order is undefined. However, IF the type can be initialized by filling with -// zeroes (which the loader does for static variables), AND the destructor also -// does nothing to the storage, then a constructor declared as -// explicit MyClass(base::LinkerInitialized x) {} -// and invoked as -// static MyClass my_variable_name(base::LINKER_INITIALIZED); -namespace base { -enum LinkerInitialized { LINKER_INITIALIZED }; -} - -#endif // _BASICTYPES_H_ diff --git a/lib/asan/sysinfo/sysinfo.cc b/lib/asan/sysinfo/sysinfo.cc deleted file mode 100644 index ee0673526402..000000000000 --- a/lib/asan/sysinfo/sysinfo.cc +++ /dev/null @@ -1,617 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include // for getenv() -#include // for snprintf(), sscanf() -#include // for memmove(), memchr(), etc. -#include // for open() -#include // for errno -#include // for read() -#if defined __MACH__ // Mac OS X, almost certainly -#include // for iterating over dll's in ProcMapsIter -#include // for iterating over dll's in ProcMapsIter -#include -#include // how we figure out numcpu's on OS X -#elif defined __FreeBSD__ -#include -#elif defined __sun__ // Solaris -#include // for, e.g., prmap_t -#elif defined(PLATFORM_WINDOWS) -#include // for getpid() (actually, _getpid()) -#include // for SHGetValueA() -#include // for Module32First() -#endif -#include "sysinfo.h" - -#ifdef PLATFORM_WINDOWS -#ifdef MODULEENTRY32 -// In a change from the usual W-A pattern, there is no A variant of -// MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A. -// In unicode mode, tlhelp32.h #defines MODULEENTRY32 to be -// MODULEENTRY32W. These #undefs are the only way I see to get back -// access to the original, ascii struct (and related functions). -#undef MODULEENTRY32 -#undef Module32First -#undef Module32Next -#undef PMODULEENTRY32 -#undef LPMODULEENTRY32 -#endif /* MODULEENTRY32 */ -// MinGW doesn't seem to define this, perhaps some windowsen don't either. -#ifndef TH32CS_SNAPMODULE32 -#define TH32CS_SNAPMODULE32 0 -#endif /* TH32CS_SNAPMODULE32 */ -#endif /* PLATFORM_WINDOWS */ - -// Re-run fn until it doesn't cause EINTR. -#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR) - -// open/read/close can set errno, which may be illegal at this -// time, so prefer making the syscalls directly if we can. -#ifdef HAVE_SYS_SYSCALL_H -# include -# define safeopen(filename, mode) syscall(SYS_open, filename, mode) -# define saferead(fd, buffer, size) syscall(SYS_read, fd, buffer, size) -# define safeclose(fd) syscall(SYS_close, fd) -#else -# define safeopen(filename, mode) open(filename, mode) -# define saferead(fd, buffer, size) read(fd, buffer, size) -# define safeclose(fd) close(fd) -#endif - - -// ---------------------------------------------------------------------- -// HasPosixThreads() -// Return true if we're running POSIX (e.g., NPTL on Linux) -// threads, as opposed to a non-POSIX thread libary. The thing -// that we care about is whether a thread's pid is the same as -// the thread that spawned it. If so, this function returns -// true. -// ---------------------------------------------------------------------- -bool HasPosixThreads() { -#if defined(__linux__) and !defined(ANDROID) -#ifndef _CS_GNU_LIBPTHREAD_VERSION -#define _CS_GNU_LIBPTHREAD_VERSION 3 -#endif - char buf[32]; - // We assume that, if confstr() doesn't know about this name, then - // the same glibc is providing LinuxThreads. - if (confstr(_CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf)) == 0) - return false; - return strncmp(buf, "NPTL", 4) == 0; -#elif defined(PLATFORM_WINDOWS) || defined(__CYGWIN__) || defined(__CYGWIN32__) - return false; -#else // other OS - return true; // Assume that everything else has Posix -#endif // else OS_LINUX -} - -// ---------------------------------------------------------------------- - -#define CHECK_LT(x, y) do { assert((x) < (y)); } while (0) - -#if defined __linux__ || defined __FreeBSD__ || defined __sun__ || defined __CYGWIN__ || defined __CYGWIN32__ -static void ConstructFilename(const char* spec, pid_t pid, - char* buf, int buf_size) { - CHECK_LT(snprintf(buf, buf_size, - spec, - static_cast(pid ? pid : getpid())), buf_size); -} -#endif - -// A templatized helper function instantiated for Mach (OS X) only. -// It can handle finding info for both 32 bits and 64 bits. -// Returns true if it successfully handled the hdr, false else. -#ifdef __MACH__ // Mac OS X, almost certainly -template -static bool NextExtMachHelper(const mach_header* hdr, - int current_image, int current_load_cmd, - uint64 *start, uint64 *end, char **flags, - uint64 *offset, int64 *inode, char **filename, - uint64 *file_mapping, uint64 *file_pages, - uint64 *anon_mapping, uint64 *anon_pages, - dev_t *dev) { - static char kDefaultPerms[5] = "r-xp"; - if (hdr->magic != kMagic) - return false; - const char* lc = (const char *)hdr + sizeof(MachHeader); - // TODO(csilvers): make this not-quadradic (increment and hold state) - for (int j = 0; j < current_load_cmd; j++) // advance to *our* load_cmd - lc += ((const load_command *)lc)->cmdsize; - if (((const load_command *)lc)->cmd == kLCSegment) { - const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image); - const SegmentCommand* sc = (const SegmentCommand *)lc; - if (start) *start = sc->vmaddr + dlloff; - if (end) *end = sc->vmaddr + sc->vmsize + dlloff; - if (flags) *flags = kDefaultPerms; // can we do better? - if (offset) *offset = sc->fileoff; - if (inode) *inode = 0; - if (filename) - *filename = const_cast(_dyld_get_image_name(current_image)); - if (file_mapping) *file_mapping = 0; - if (file_pages) *file_pages = 0; // could we use sc->filesize? - if (anon_mapping) *anon_mapping = 0; - if (anon_pages) *anon_pages = 0; - if (dev) *dev = 0; - return true; - } - - return false; -} -#endif - -ProcMapsIterator::ProcMapsIterator(pid_t pid) { - Init(pid, NULL, false); -} - -ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer) { - Init(pid, buffer, false); -} - -ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer, - bool use_maps_backing) { - Init(pid, buffer, use_maps_backing); -} - -void ProcMapsIterator::Init(pid_t pid, Buffer *buffer, - bool use_maps_backing) { - pid_ = pid; - using_maps_backing_ = use_maps_backing; - dynamic_buffer_ = NULL; - if (!buffer) { - // If the user didn't pass in any buffer storage, allocate it - // now. This is the normal case; the signal handler passes in a - // static buffer. - buffer = dynamic_buffer_ = new Buffer; - } else { - dynamic_buffer_ = NULL; - } - - ibuf_ = buffer->buf_; - - stext_ = etext_ = nextline_ = ibuf_; - ebuf_ = ibuf_ + Buffer::kBufSize - 1; - nextline_ = ibuf_; - -#if defined(__linux__) || defined(__CYGWIN__) || defined(__CYGWIN32__) - if (use_maps_backing) { // don't bother with clever "self" stuff in this case - ConstructFilename("/proc/%d/maps_backing", pid, ibuf_, Buffer::kBufSize); - } else if (pid == 0) { - // We have to kludge a bit to deal with the args ConstructFilename - // expects. The 1 is never used -- it's only impt. that it's not 0. - ConstructFilename("/proc/self/maps", 1, ibuf_, Buffer::kBufSize); - } else { - ConstructFilename("/proc/%d/maps", pid, ibuf_, Buffer::kBufSize); - } - // No error logging since this can be called from the crash dump - // handler at awkward moments. Users should call Valid() before - // using. - NO_INTR(fd_ = open(ibuf_, O_RDONLY)); -#elif defined(__FreeBSD__) - // We don't support maps_backing on freebsd - if (pid == 0) { - ConstructFilename("/proc/curproc/map", 1, ibuf_, Buffer::kBufSize); - } else { - ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize); - } - NO_INTR(fd_ = open(ibuf_, O_RDONLY)); -#elif defined(__sun__) - if (pid == 0) { - ConstructFilename("/proc/self/map", 1, ibuf_, Buffer::kBufSize); - } else { - ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize); - } - NO_INTR(fd_ = open(ibuf_, O_RDONLY)); -#elif defined(__MACH__) - current_image_ = _dyld_image_count(); // count down from the top - current_load_cmd_ = -1; -#elif defined(PLATFORM_WINDOWS) - snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | - TH32CS_SNAPMODULE32, - GetCurrentProcessId()); - memset(&module_, 0, sizeof(module_)); -#else - fd_ = -1; // so Valid() is always false -#endif - -} - -ProcMapsIterator::~ProcMapsIterator() { -#if defined(PLATFORM_WINDOWS) - if (snapshot_ != INVALID_HANDLE_VALUE) CloseHandle(snapshot_); -#elif defined(__MACH__) - // no cleanup necessary! -#else - if (fd_ >= 0) NO_INTR(close(fd_)); -#endif - delete dynamic_buffer_; -} - -bool ProcMapsIterator::Valid() const { -#if defined(PLATFORM_WINDOWS) - return snapshot_ != INVALID_HANDLE_VALUE; -#elif defined(__MACH__) - return 1; -#else - return fd_ != -1; -#endif -} - -bool ProcMapsIterator::Next(uint64 *start, uint64 *end, char **flags, - uint64 *offset, int64 *inode, char **filename) { - return NextExt(start, end, flags, offset, inode, filename, NULL, NULL, - NULL, NULL, NULL); -} - -// This has too many arguments. It should really be building -// a map object and returning it. The problem is that this is called -// when the memory allocator state is undefined, hence the arguments. -bool ProcMapsIterator::NextExt(uint64 *start, uint64 *end, char **flags, - uint64 *offset, int64 *inode, char **filename, - uint64 *file_mapping, uint64 *file_pages, - uint64 *anon_mapping, uint64 *anon_pages, - dev_t *dev) { - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__CYGWIN__) || defined(__CYGWIN32__) - do { - // Advance to the start of the next line - stext_ = nextline_; - - // See if we have a complete line in the buffer already - nextline_ = static_cast(memchr (stext_, '\n', etext_ - stext_)); - if (!nextline_) { - // Shift/fill the buffer so we do have a line - int count = etext_ - stext_; - - // Move the current text to the start of the buffer - memmove(ibuf_, stext_, count); - stext_ = ibuf_; - etext_ = ibuf_ + count; - - int nread = 0; // fill up buffer with text - while (etext_ < ebuf_) { - NO_INTR(nread = read(fd_, etext_, ebuf_ - etext_)); - if (nread > 0) - etext_ += nread; - else - break; - } - - // Zero out remaining characters in buffer at EOF to avoid returning - // garbage from subsequent calls. - if (etext_ != ebuf_ && nread == 0) { - memset(etext_, 0, ebuf_ - etext_); - } - *etext_ = '\n'; // sentinel; safe because ibuf extends 1 char beyond ebuf - nextline_ = static_cast(memchr (stext_, '\n', etext_ + 1 - stext_)); - } - *nextline_ = 0; // turn newline into nul - nextline_ += ((nextline_ < etext_)? 1 : 0); // skip nul if not end of text - // stext_ now points at a nul-terminated line - uint64 tmpstart, tmpend, tmpoffset; - int64 tmpinode; - int major, minor; - unsigned filename_offset = 0; -#if defined(__linux__) - // for now, assume all linuxes have the same format - if (sscanf(stext_, "%"SCNx64"-%"SCNx64" %4s %"SCNx64" %x:%x %"SCNd64" %n", - (unsigned long long *)(start ? start : &tmpstart), - (unsigned long long *)(end ? end : &tmpend), - flags_, - (unsigned long long *)(offset ? offset : &tmpoffset), - &major, &minor, - (unsigned long long *)(inode ? inode : &tmpinode), - &filename_offset) != 7) continue; -#elif defined(__CYGWIN__) || defined(__CYGWIN32__) - // cygwin is like linux, except the third field is the "entry point" - // rather than the offset (see format_process_maps at - // http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/fhandler_process.cc?rev=1.89&content-type=text/x-cvsweb-markup&cvsroot=src - // Offset is always be 0 on cygwin: cygwin implements an mmap - // by loading the whole file and then calling NtMapViewOfSection. - // Cygwin also seems to set its flags kinda randomly; use windows default. - char tmpflags[5]; - if (offset) - *offset = 0; - strcpy(flags_, "r-xp"); - if (sscanf(stext_, "%llx-%llx %4s %llx %x:%x %lld %n", - start ? start : &tmpstart, - end ? end : &tmpend, - tmpflags, - &tmpoffset, - &major, &minor, - inode ? inode : &tmpinode, &filename_offset) != 7) continue; -#elif defined(__FreeBSD__) - // For the format, see http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/fs/procfs/procfs_map.c?rev=1.31&content-type=text/x-cvsweb-markup - tmpstart = tmpend = tmpoffset = 0; - tmpinode = 0; - major = minor = 0; // can't get this info in freebsd - if (inode) - *inode = 0; // nor this - if (offset) - *offset = 0; // seems like this should be in there, but maybe not - // start end resident privateresident obj(?) prot refcnt shadowcnt - // flags copy_on_write needs_copy type filename: - // 0x8048000 0x804a000 2 0 0xc104ce70 r-x 1 0 0x0 COW NC vnode /bin/cat - if (sscanf(stext_, "0x%"SCNx64" 0x%"SCNx64" %*d %*d %*p %3s %*d %*d 0x%*x %*s %*s %*s %n", - start ? start : &tmpstart, - end ? end : &tmpend, - flags_, - &filename_offset) != 3) continue; -#endif - - // Depending on the Linux kernel being used, there may or may not be a space - // after the inode if there is no filename. sscanf will in such situations - // nondeterministically either fill in filename_offset or not (the results - // differ on multiple calls in the same run even with identical arguments). - // We don't want to wander off somewhere beyond the end of the string. - size_t stext_length = strlen(stext_); - if (filename_offset == 0 || filename_offset > stext_length) - filename_offset = stext_length; - - // We found an entry - if (flags) *flags = flags_; - if (filename) *filename = stext_ + filename_offset; - if (dev) *dev = minor | (major << 8); - - if (using_maps_backing_) { - // Extract and parse physical page backing info. - char *backing_ptr = stext_ + filename_offset + - strlen(stext_+filename_offset); - - // find the second '(' - int paren_count = 0; - while (--backing_ptr > stext_) { - if (*backing_ptr == '(') { - ++paren_count; - if (paren_count >= 2) { - uint64 tmp_file_mapping; - uint64 tmp_file_pages; - uint64 tmp_anon_mapping; - uint64 tmp_anon_pages; - - sscanf(backing_ptr+1, "F %"SCNx64" %"SCNd64") (A %"SCNx64" %"SCNd64")", - (unsigned long long *)(file_mapping ? - file_mapping : &tmp_file_mapping), - (unsigned long long *)(file_pages ? - file_pages : &tmp_file_pages), - (unsigned long long *)(anon_mapping - ? anon_mapping : &tmp_anon_mapping), - (unsigned long long *)(anon_pages - ? anon_pages : &tmp_anon_pages)); - // null terminate the file name (there is a space - // before the first (. - backing_ptr[-1] = 0; - break; - } - } - } - } - - return true; - } while (etext_ > ibuf_); -#elif defined(__sun__) - // This is based on MA_READ == 4, MA_WRITE == 2, MA_EXEC == 1 - static char kPerms[8][4] = { "---", "--x", "-w-", "-wx", - "r--", "r-x", "rw-", "rwx" }; - COMPILE_ASSERT(MA_READ == 4, solaris_ma_read_must_equal_4); - COMPILE_ASSERT(MA_WRITE == 2, solaris_ma_write_must_equal_2); - COMPILE_ASSERT(MA_EXEC == 1, solaris_ma_exec_must_equal_1); - Buffer object_path; - int nread = 0; // fill up buffer with text - NO_INTR(nread = read(fd_, ibuf_, sizeof(prmap_t))); - if (nread == sizeof(prmap_t)) { - long inode_from_mapname = 0; - prmap_t* mapinfo = reinterpret_cast(ibuf_); - // Best-effort attempt to get the inode from the filename. I think the - // two middle ints are major and minor device numbers, but I'm not sure. - sscanf(mapinfo->pr_mapname, "ufs.%*d.%*d.%ld", &inode_from_mapname); - - if (pid_ == 0) { - CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize, - "/proc/self/path/%s", mapinfo->pr_mapname), - Buffer::kBufSize); - } else { - CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize, - "/proc/%d/path/%s", - static_cast(pid_), mapinfo->pr_mapname), - Buffer::kBufSize); - } - ssize_t len = readlink(object_path.buf_, current_filename_, PATH_MAX); - CHECK_LT(len, PATH_MAX); - if (len < 0) - len = 0; - current_filename_[len] = '\0'; - - if (start) *start = mapinfo->pr_vaddr; - if (end) *end = mapinfo->pr_vaddr + mapinfo->pr_size; - if (flags) *flags = kPerms[mapinfo->pr_mflags & 7]; - if (offset) *offset = mapinfo->pr_offset; - if (inode) *inode = inode_from_mapname; - if (filename) *filename = current_filename_; - if (file_mapping) *file_mapping = 0; - if (file_pages) *file_pages = 0; - if (anon_mapping) *anon_mapping = 0; - if (anon_pages) *anon_pages = 0; - if (dev) *dev = 0; - return true; - } -#elif defined(__MACH__) - // We return a separate entry for each segment in the DLL. (TODO(csilvers): - // can we do better?) A DLL ("image") has load-commands, some of which - // talk about segment boundaries. - // cf image_for_address from http://svn.digium.com/view/asterisk/team/oej/minivoicemail/dlfcn.c?revision=53912 - for (; current_image_ >= 0; current_image_--) { - const mach_header* hdr = _dyld_get_image_header(current_image_); - if (!hdr) continue; - if (current_load_cmd_ < 0) // set up for this image - current_load_cmd_ = hdr->ncmds; // again, go from the top down - - // We start with the next load command (we've already looked at this one). - for (current_load_cmd_--; current_load_cmd_ >= 0; current_load_cmd_--) { -#ifdef MH_MAGIC_64 - if (NextExtMachHelper( - hdr, current_image_, current_load_cmd_, - start, end, flags, offset, inode, filename, - file_mapping, file_pages, anon_mapping, - anon_pages, dev)) { - return true; - } -#endif - if (NextExtMachHelper( - hdr, current_image_, current_load_cmd_, - start, end, flags, offset, inode, filename, - file_mapping, file_pages, anon_mapping, - anon_pages, dev)) { - return true; - } - } - // If we get here, no more load_cmd's in this image talk about - // segments. Go on to the next image. - } -#elif defined(PLATFORM_WINDOWS) - static char kDefaultPerms[5] = "r-xp"; - BOOL ok; - if (module_.dwSize == 0) { // only possible before first call - module_.dwSize = sizeof(module_); - ok = Module32First(snapshot_, &module_); - } else { - ok = Module32Next(snapshot_, &module_); - } - if (ok) { - uint64 base_addr = reinterpret_cast(module_.modBaseAddr); - if (start) *start = base_addr; - if (end) *end = base_addr + module_.modBaseSize; - if (flags) *flags = kDefaultPerms; - if (offset) *offset = 0; - if (inode) *inode = 0; - if (filename) *filename = module_.szExePath; - if (file_mapping) *file_mapping = 0; - if (file_pages) *file_pages = 0; - if (anon_mapping) *anon_mapping = 0; - if (anon_pages) *anon_pages = 0; - if (dev) *dev = 0; - return true; - } -#endif - - // We didn't find anything - return false; -} - -int ProcMapsIterator::FormatLine(char* buffer, int bufsize, - uint64 start, uint64 end, const char *flags, - uint64 offset, int64 inode, - const char *filename, dev_t dev) { - // We assume 'flags' looks like 'rwxp' or 'rwx'. - char r = (flags && flags[0] == 'r') ? 'r' : '-'; - char w = (flags && flags[0] && flags[1] == 'w') ? 'w' : '-'; - char x = (flags && flags[0] && flags[1] && flags[2] == 'x') ? 'x' : '-'; - // p always seems set on linux, so we set the default to 'p', not '-' - char p = (flags && flags[0] && flags[1] && flags[2] && flags[3] != 'p') - ? '-' : 'p'; - - const int rc = snprintf(buffer, bufsize, - "%08"PRIx64"-%08"PRIx64" %c%c%c%c %08"PRIx64" %02x:%02x %-11"PRId64" %s\n", - (unsigned long long)start, (unsigned long long)end, r,w,x,p, - (unsigned long long)offset, - static_cast(dev/256), static_cast(dev%256), - (unsigned long long)inode, filename); - return (rc < 0 || rc >= bufsize) ? 0 : rc; -} - -// Helper to add the list of mapped shared libraries to a profile. -// Fill formatted "/proc/self/maps" contents into buffer 'buf' of size 'size' -// and return the actual size occupied in 'buf'. We fill wrote_all to true -// if we successfully wrote all proc lines to buf, false else. -// We do not provision for 0-terminating 'buf'. -int FillProcSelfMaps(char buf[], int size, bool* wrote_all) { - ProcMapsIterator::Buffer iterbuf; - ProcMapsIterator it(0, &iterbuf); // 0 means "current pid" - - uint64 start, end, offset; - int64 inode; - char *flags, *filename; - int bytes_written = 0; - *wrote_all = true; - while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) { - const int line_length = it.FormatLine(buf + bytes_written, - size - bytes_written, - start, end, flags, offset, - inode, filename, 0); - if (line_length == 0) - *wrote_all = false; // failed to write this line out - else - bytes_written += line_length; - - } - return bytes_written; -} - -// Dump the same data as FillProcSelfMaps reads to fd. -// It seems easier to repeat parts of FillProcSelfMaps here than to -// reuse it via a call. -void DumpProcSelfMaps(RawFD fd) { - ProcMapsIterator::Buffer iterbuf; - ProcMapsIterator it(0, &iterbuf); // 0 means "current pid" - - uint64 start, end, offset; - int64 inode; - char *flags, *filename; - ProcMapsIterator::Buffer linebuf; - while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) { - int written = it.FormatLine(linebuf.buf_, sizeof(linebuf.buf_), - start, end, flags, offset, inode, filename, - 0); - RawWrite(fd, linebuf.buf_, written); - } -} - -// Re-run fn until it doesn't cause EINTR. -#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR) - -RawFD RawOpenForWriting(const char* filename) { - return open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664); -} - -void RawWrite(RawFD fd, const char* buf, size_t len) { - while (len > 0) { - ssize_t r; - NO_INTR(r = write(fd, buf, len)); - if (r <= 0) break; - buf += r; - len -= r; - } -} - -void RawClose(RawFD fd) { - NO_INTR(close(fd)); -} diff --git a/lib/asan/sysinfo/sysinfo.h b/lib/asan/sysinfo/sysinfo.h deleted file mode 100644 index 707687e037ac..000000000000 --- a/lib/asan/sysinfo/sysinfo.h +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// All functions here are thread-hostile due to file caching unless -// commented otherwise. - -#ifndef _SYSINFO_H_ -#define _SYSINFO_H_ - -#include -#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__)) -#include // for DWORD -#include // for CreateToolhelp32Snapshot -#endif -#ifdef HAVE_UNISTD_H -#include // for pid_t -#endif -#include // for size_t -#include // for PATH_MAX -#include "basictypes.h" - -// This getenv function is safe to call before the C runtime is initialized. -// On Windows, it utilizes GetEnvironmentVariable() and on unix it uses -// /proc/self/environ instead calling getenv(). It's intended to be used in -// routines that run before main(), when the state required for getenv() may -// not be set up yet. In particular, errno isn't set up until relatively late -// (after the pthreads library has a chance to make it threadsafe), and -// getenv() doesn't work until then. -// On some platforms, this call will utilize the same, static buffer for -// repeated GetenvBeforeMain() calls. Callers should not expect pointers from -// this routine to be long lived. -// Note that on unix, /proc only has the environment at the time the -// application was started, so this routine ignores setenv() calls/etc. Also -// note it only reads the first 16K of the environment. -extern const char* GetenvBeforeMain(const char* name); - -// This takes as an argument an environment-variable name (like -// CPUPROFILE) whose value is supposed to be a file-path, and sets -// path to that path, and returns true. Non-trivial for surprising -// reasons, as documented in sysinfo.cc. path must have space PATH_MAX. -extern bool GetUniquePathFromEnv(const char* env_name, char* path); - -extern int NumCPUs(); - -// processor cycles per second of each processor. Thread-safe. -extern double CyclesPerSecond(void); - - -// Return true if we're running POSIX (e.g., NPTL on Linux) threads, -// as opposed to a non-POSIX thread libary. The thing that we care -// about is whether a thread's pid is the same as the thread that -// spawned it. If so, this function returns true. -// Thread-safe. -// Note: We consider false negatives to be OK. -bool HasPosixThreads(); - -#ifndef SWIG // SWIG doesn't like struct Buffer and variable arguments. - -// A ProcMapsIterator abstracts access to /proc/maps for a given -// process. Needs to be stack-allocatable and avoid using stdio/malloc -// so it can be used in the google stack dumper, heap-profiler, etc. -// -// On Windows and Mac OS X, this iterator iterates *only* over DLLs -// mapped into this process space. For Linux, FreeBSD, and Solaris, -// it iterates over *all* mapped memory regions, including anonymous -// mmaps. For other O/Ss, it is unlikely to work at all, and Valid() -// will always return false. Also note: this routine only works on -// FreeBSD if procfs is mounted: make sure this is in your /etc/fstab: -// proc /proc procfs rw 0 0 -class ProcMapsIterator { - public: - struct Buffer { -#ifdef __FreeBSD__ - // FreeBSD requires us to read all of the maps file at once, so - // we have to make a buffer that's "always" big enough - static const size_t kBufSize = 102400; -#else // a one-line buffer is good enough - static const size_t kBufSize = PATH_MAX + 1024; -#endif - char buf_[kBufSize]; - }; - - - // Create a new iterator for the specified pid. pid can be 0 for "self". - explicit ProcMapsIterator(pid_t pid); - - // Create an iterator with specified storage (for use in signal - // handler). "buffer" should point to a ProcMapsIterator::Buffer - // buffer can be NULL in which case a bufer will be allocated. - ProcMapsIterator(pid_t pid, Buffer *buffer); - - // Iterate through maps_backing instead of maps if use_maps_backing - // is true. Otherwise the same as above. buffer can be NULL and - // it will allocate a buffer itself. - ProcMapsIterator(pid_t pid, Buffer *buffer, - bool use_maps_backing); - - // Returns true if the iterator successfully initialized; - bool Valid() const; - - // Returns a pointer to the most recently parsed line. Only valid - // after Next() returns true, and until the iterator is destroyed or - // Next() is called again. This may give strange results on non-Linux - // systems. Prefer FormatLine() if that may be a concern. - const char *CurrentLine() const { return stext_; } - - // Writes the "canonical" form of the /proc/xxx/maps info for a single - // line to the passed-in buffer. Returns the number of bytes written, - // or 0 if it was not able to write the complete line. (To guarantee - // success, buffer should have size at least Buffer::kBufSize.) - // Takes as arguments values set via a call to Next(). The - // "canonical" form of the line (taken from linux's /proc/xxx/maps): - // - + - // : Note: the - // eg - // 08048000-0804c000 r-xp 00000000 03:01 3793678 /bin/cat - // If you don't have the dev_t (dev), feel free to pass in 0. - // (Next() doesn't return a dev_t, though NextExt does.) - // - // Note: if filename and flags were obtained via a call to Next(), - // then the output of this function is only valid if Next() returned - // true, and only until the iterator is destroyed or Next() is - // called again. (Since filename, at least, points into CurrentLine.) - static int FormatLine(char* buffer, int bufsize, - uint64 start, uint64 end, const char *flags, - uint64 offset, int64 inode, const char *filename, - dev_t dev); - - // Find the next entry in /proc/maps; return true if found or false - // if at the end of the file. - // - // Any of the result pointers can be NULL if you're not interested - // in those values. - // - // If "flags" and "filename" are passed, they end up pointing to - // storage within the ProcMapsIterator that is valid only until the - // iterator is destroyed or Next() is called again. The caller may - // modify the contents of these strings (up as far as the first NUL, - // and only until the subsequent call to Next()) if desired. - - // The offsets are all uint64 in order to handle the case of a - // 32-bit process running on a 64-bit kernel - // - // IMPORTANT NOTE: see top-of-class notes for details about what - // mapped regions Next() iterates over, depending on O/S. - // TODO(csilvers): make flags and filename const. - bool Next(uint64 *start, uint64 *end, char **flags, - uint64 *offset, int64 *inode, char **filename); - - bool NextExt(uint64 *start, uint64 *end, char **flags, - uint64 *offset, int64 *inode, char **filename, - uint64 *file_mapping, uint64 *file_pages, - uint64 *anon_mapping, uint64 *anon_pages, - dev_t *dev); - - ~ProcMapsIterator(); - - private: - void Init(pid_t pid, Buffer *buffer, bool use_maps_backing); - - char *ibuf_; // input buffer - char *stext_; // start of text - char *etext_; // end of text - char *nextline_; // start of next line - char *ebuf_; // end of buffer (1 char for a nul) -#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__)) - HANDLE snapshot_; // filehandle on dll info - // In a change from the usual W-A pattern, there is no A variant of - // MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A. - // We want the original A variants, and this #undef is the only - // way I see to get them. Redefining it when we're done prevents us - // from affecting other .cc files. -# ifdef MODULEENTRY32 // Alias of W -# undef MODULEENTRY32 - MODULEENTRY32 module_; // info about current dll (and dll iterator) -# define MODULEENTRY32 MODULEENTRY32W -# else // It's the ascii, the one we want. - MODULEENTRY32 module_; // info about current dll (and dll iterator) -# endif -#elif defined(__MACH__) - int current_image_; // dll's are called "images" in macos parlance - int current_load_cmd_; // the segment of this dll we're examining -#elif defined(__sun__) // Solaris - int fd_; - char current_filename_[PATH_MAX]; -#else - int fd_; // filehandle on /proc/*/maps -#endif - pid_t pid_; - char flags_[10]; - Buffer* dynamic_buffer_; // dynamically-allocated Buffer - bool using_maps_backing_; // true if we are looking at maps_backing instead of maps. -}; - -#endif /* #ifndef SWIG */ - -// Helper routines -typedef int RawFD; -const RawFD kIllegalRawFD = -1; // what open returns if it fails - -RawFD RawOpenForWriting(const char* filename); // uses default permissions -void RawWrite(RawFD fd, const char* buf, size_t len); -void RawClose(RawFD fd); - -int FillProcSelfMaps(char buf[], int size, bool* wrote_all); -void DumpProcSelfMaps(RawFD fd); - -#endif /* #ifndef _SYSINFO_H_ */ diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt new file mode 100644 index 000000000000..d409d50b995e --- /dev/null +++ b/lib/asan/tests/CMakeLists.txt @@ -0,0 +1,118 @@ +# Testing rules for AddressSanitizer. +# +# These are broken into two buckets. One set of tests directly interacts with +# the runtime library and checks its functionality. These are the +# no-instrumentation tests. +# +# Another group of tests relies upon the ability to compile the test with +# address sanitizer instrumentation pass. These tests form "integration" tests +# and have some elements of version skew -- they test the *host* compiler's +# instrumentation against the just-built runtime library. + +include(CheckCXXCompilerFlag) + +include_directories(..) +include_directories(../..) + +set(ASAN_UNITTEST_COMMON_CFLAGS + -Wall + -Wno-format + -fvisibility=hidden +) +# Support 64-bit and 32-bit builds. +if(LLVM_BUILD_32_BITS) + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m32) +else() + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m64) +endif() + +set(ASAN_GTEST_INCLUDE_CFLAGS + -I${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include + -I${LLVM_MAIN_SRC_DIR}/include + -I${LLVM_BINARY_DIR}/include + -D__STDC_CONSTANT_MACROS + -D__STDC_LIMIT_MACROS +) + +set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS + ${ASAN_UNITTEST_COMMON_CFLAGS} + ${ASAN_GTEST_INCLUDE_CFLAGS} + -faddress-sanitizer + -O2 + -g + -mllvm "-asan-blacklist=${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore" + -DASAN_HAS_BLACKLIST=1 + -DASAN_HAS_EXCEPTIONS=1 + -DASAN_NEEDS_SEGV=1 + -DASAN_UAR=0 +) + +add_custom_target(AsanTests) +set_target_properties(AsanTests PROPERTIES FOLDER "ASan tests") +function(add_asan_test testname) + add_unittest(AsanTests ${testname} ${ARGN}) + if(LLVM_BUILD_32_BITS) + target_link_libraries(${testname} clang_rt.asan-i386) + else() + target_link_libraries(${testname} clang_rt.asan-x86_64) + endif() + if (APPLE) + # Darwin-specific linker flags. + set_property(TARGET ${testname} APPEND PROPERTY + LINK_FLAGS "-framework Foundation") + elseif (UNIX) + # Linux-specific linker flags. + set_property(TARGET ${testname} APPEND PROPERTY + LINK_FLAGS "-lpthread -ldl -export-dynamic") + endif() + set(add_compile_flags "") + get_property(compile_flags TARGET ${testname} PROPERTY COMPILE_FLAGS) + foreach(arg ${ASAN_UNITTEST_COMMON_CFLAGS}) + set(add_compile_flags "${add_compile_flags} ${arg}") + endforeach(arg ${ASAN_UNITTEST_COMMON_CFLAGS}) + set_property(TARGET ${testname} PROPERTY COMPILE_FLAGS + "${compile_flags} ${add_compile_flags}") +endfunction() + +set(ASAN_NOINST_TEST_SOURCES + asan_noinst_test.cc + asan_break_optimization.cc +) + +set(ASAN_INST_TEST_OBJECTS) + +# We only support building instrumented tests when we're not cross compiling +# and targeting a unix-like system where we can predict viable compilation and +# linking strategies. +if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX) + + # This function is a custom routine to manage manually compiling source files + # for unit tests with the just-built Clang binary, using the ASan + # instrumentation, and linking them into a test executable. + function(add_asan_compile_command source extra_cflags) + set(output_obj "${source}.asan.o") + add_custom_command( + OUTPUT ${output_obj} + COMMAND clang + ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} + ${extra_cflags} + -c -o "${output_obj}" + ${CMAKE_CURRENT_SOURCE_DIR}/${source} + MAIN_DEPENDENCY ${source} + DEPENDS clang clang_rt.asan-i386 clang_rt.asan-x86_64 ${ARGN} + ) + endfunction() + + add_asan_compile_command(asan_globals_test.cc "") + add_asan_compile_command(asan_test.cc "") + list(APPEND ASAN_INST_TEST_OBJECTS asan_globals_test.cc.asan.o + asan_test.cc.asan.o) + if (APPLE) + add_asan_compile_command(asan_mac_test.mm "-ObjC") + list(APPEND ASAN_INST_TEST_OBJECTS asan_mac_test.mm.asan.o) + endif() + +endif() + +add_asan_test(AsanTest ${ASAN_NOINST_TEST_SOURCES} + ${ASAN_INST_TEST_OBJECTS}) diff --git a/lib/asan/tests/asan_benchmarks_test.cc b/lib/asan/tests/asan_benchmarks_test.cc index b72cc3fbe14b..a142fd23e1b8 100644 --- a/lib/asan/tests/asan_benchmarks_test.cc +++ b/lib/asan/tests/asan_benchmarks_test.cc @@ -1,4 +1,4 @@ -//===-- asan_benchmarks_test.cc ------------*- C++ -*-===// +//===-- asan_benchmarks_test.cc ----------------------===// // // The LLVM Compiler Infrastructure // diff --git a/lib/asan/tests/asan_break_optimization.cc b/lib/asan/tests/asan_break_optimization.cc index acd042701e1e..022a9f8b8505 100644 --- a/lib/asan/tests/asan_break_optimization.cc +++ b/lib/asan/tests/asan_break_optimization.cc @@ -1,4 +1,4 @@ -//===-- asan_break_optimization.cc ------------*- C++ -*-===// +//===-- asan_break_optimization.cc ----------------------===// // // The LLVM Compiler Infrastructure // @@ -15,4 +15,5 @@ // Have this function in a separate file to avoid inlining. // (Yes, we know about cross-file inlining, but let's assume we don't use it). extern "C" void break_optimization(void *x) { + (void)x; } diff --git a/lib/asan/tests/asan_globals_test.cc b/lib/asan/tests/asan_globals_test.cc index 2303f8bdc43b..6467524ca32c 100644 --- a/lib/asan/tests/asan_globals_test.cc +++ b/lib/asan/tests/asan_globals_test.cc @@ -1,4 +1,4 @@ -//===-- asan_globals_test.cc ------------*- C++ -*-===// +//===-- asan_globals_test.cc ----------------------===// // // The LLVM Compiler Infrastructure // diff --git a/lib/asan/tests/asan_interface_test.cc b/lib/asan/tests/asan_interface_test.cc deleted file mode 100644 index c26ed92468b3..000000000000 --- a/lib/asan/tests/asan_interface_test.cc +++ /dev/null @@ -1,334 +0,0 @@ -//===-- asan_interface_test.cc ------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// 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. -// -//===----------------------------------------------------------------------===// -#include -#include -#include - -#include "asan_test_config.h" -#include "asan_test_utils.h" -#include "asan_interface.h" - -TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { - EXPECT_EQ(1, __asan_get_estimated_allocated_size(0)); - const size_t sizes[] = { 1, 30, 1<<30 }; - for (size_t i = 0; i < 3; i++) { - EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i])); - } -} - -static const char* kGetAllocatedSizeErrorMsg = - "__asan_get_allocated_size failed"; - -TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { - const size_t kArraySize = 100; - char *array = Ident((char*)malloc(kArraySize)); - int *int_ptr = Ident(new int); - - // Allocated memory is owned by allocator. Allocated size should be - // equal to requested size. - EXPECT_EQ(true, __asan_get_ownership(array)); - EXPECT_EQ(kArraySize, __asan_get_allocated_size(array)); - EXPECT_EQ(true, __asan_get_ownership(int_ptr)); - EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr)); - - // We cannot call GetAllocatedSize from the memory we didn't map, - // and from the interior pointers (not returned by previous malloc). - void *wild_addr = (void*)0x1; - EXPECT_EQ(false, __asan_get_ownership(wild_addr)); - EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg); - EXPECT_EQ(false, __asan_get_ownership(array + kArraySize / 2)); - EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2), - kGetAllocatedSizeErrorMsg); - - // NULL is a valid argument and is owned. - EXPECT_EQ(true, __asan_get_ownership(NULL)); - EXPECT_EQ(0, __asan_get_allocated_size(NULL)); - - // When memory is freed, it's not owned, and call to GetAllocatedSize - // is forbidden. - free(array); - EXPECT_EQ(false, __asan_get_ownership(array)); - EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg); - - delete int_ptr; -} - -TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) { - size_t before_malloc, after_malloc, after_free; - char *array; - const size_t kMallocSize = 100; - before_malloc = __asan_get_current_allocated_bytes(); - - array = Ident((char*)malloc(kMallocSize)); - after_malloc = __asan_get_current_allocated_bytes(); - EXPECT_EQ(before_malloc + kMallocSize, after_malloc); - - free(array); - after_free = __asan_get_current_allocated_bytes(); - EXPECT_EQ(before_malloc, after_free); -} - -static void DoDoubleFree() { - int *x = Ident(new int); - delete Ident(x); - delete Ident(x); -} - -// This test is run in a separate process, so that large malloced -// chunk won't remain in the free lists after the test. -// Note: use ASSERT_* instead of EXPECT_* here. -static void RunGetHeapSizeTestAndDie() { - size_t old_heap_size, new_heap_size, heap_growth; - // We unlikely have have chunk of this size in free list. - static const size_t kLargeMallocSize = 1 << 29; // 512M - old_heap_size = __asan_get_heap_size(); - fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); - free(Ident(malloc(kLargeMallocSize))); - new_heap_size = __asan_get_heap_size(); - heap_growth = new_heap_size - old_heap_size; - fprintf(stderr, "heap growth after first malloc: %zu\n", heap_growth); - ASSERT_GE(heap_growth, kLargeMallocSize); - ASSERT_LE(heap_growth, 2 * kLargeMallocSize); - - // Now large chunk should fall into free list, and can be - // allocated without increasing heap size. - old_heap_size = new_heap_size; - free(Ident(malloc(kLargeMallocSize))); - heap_growth = __asan_get_heap_size() - old_heap_size; - fprintf(stderr, "heap growth after second malloc: %zu\n", heap_growth); - ASSERT_LT(heap_growth, kLargeMallocSize); - - // Test passed. Now die with expected double-free. - DoDoubleFree(); -} - -TEST(AddressSanitizerInterface, GetHeapSizeTest) { - EXPECT_DEATH(RunGetHeapSizeTestAndDie(), "double-free"); -} - -// Note: use ASSERT_* instead of EXPECT_* here. -static void DoLargeMallocForGetFreeBytesTestAndDie() { - size_t old_free_bytes, new_free_bytes; - static const size_t kLargeMallocSize = 1 << 29; // 512M - // If we malloc and free a large memory chunk, it will not fall - // into quarantine and will be available for future requests. - old_free_bytes = __asan_get_free_bytes(); - fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); - fprintf(stderr, "free bytes before malloc: %zu\n", old_free_bytes); - free(Ident(malloc(kLargeMallocSize))); - new_free_bytes = __asan_get_free_bytes(); - fprintf(stderr, "free bytes after malloc and free: %zu\n", new_free_bytes); - ASSERT_GE(new_free_bytes, old_free_bytes + kLargeMallocSize); - // Test passed. - DoDoubleFree(); -} - -TEST(AddressSanitizerInterface, GetFreeBytesTest) { - static const size_t kNumOfChunks = 100; - static const size_t kChunkSize = 100; - char *chunks[kNumOfChunks]; - size_t i; - size_t old_free_bytes, new_free_bytes; - // Allocate a small chunk. Now allocator probably has a lot of these - // chunks to fulfill future requests. So, future requests will decrease - // the number of free bytes. - chunks[0] = Ident((char*)malloc(kChunkSize)); - old_free_bytes = __asan_get_free_bytes(); - for (i = 1; i < kNumOfChunks; i++) { - chunks[i] = Ident((char*)malloc(kChunkSize)); - new_free_bytes = __asan_get_free_bytes(); - EXPECT_LT(new_free_bytes, old_free_bytes); - old_free_bytes = new_free_bytes; - } - // Deleting these chunks will move them to quarantine, number of free - // bytes won't increase. - for (i = 0; i < kNumOfChunks; i++) { - free(chunks[i]); - EXPECT_EQ(old_free_bytes, __asan_get_free_bytes()); - } - EXPECT_DEATH(DoLargeMallocForGetFreeBytesTestAndDie(), "double-free"); -} - -static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<20, 357}; -static const size_t kManyThreadsIterations = 250; -static const size_t kManyThreadsNumThreads = 200; - -void *ManyThreadsWithStatsWorker(void *arg) { - for (size_t iter = 0; iter < kManyThreadsIterations; iter++) { - for (size_t size_index = 0; size_index < 4; size_index++) { - free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); - } - } - return 0; -} - -TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { - size_t before_test, after_test, i; - pthread_t threads[kManyThreadsNumThreads]; - before_test = __asan_get_current_allocated_bytes(); - for (i = 0; i < kManyThreadsNumThreads; i++) { - pthread_create(&threads[i], 0, - (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); - } - for (i = 0; i < kManyThreadsNumThreads; i++) { - pthread_join(threads[i], 0); - } - after_test = __asan_get_current_allocated_bytes(); - // ASan stats also reflect memory usage of internal ASan RTL structs, - // so we can't check for equality here. - EXPECT_LT(after_test, before_test + (1UL<<20)); -} - -TEST(AddressSanitizerInterface, ExitCode) { - int original_exit_code = __asan_set_error_exit_code(7); - EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), ""); - EXPECT_EQ(7, __asan_set_error_exit_code(8)); - EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), ""); - EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code)); - EXPECT_EXIT(DoDoubleFree(), - ::testing::ExitedWithCode(original_exit_code), ""); -} - -static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; - -#define ACCESS(ptr, offset) Ident(*(ptr + offset)) - -#define DIE_ON_ACCESS(ptr, offset) \ - EXPECT_DEATH(Ident(*(ptr + offset)), kUseAfterPoisonErrorMessage) - -TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { - char *array = Ident((char*)malloc(120)); - // poison array[40..80) - ASAN_POISON_MEMORY_REGION(array + 40, 40); - ACCESS(array, 39); - ACCESS(array, 80); - DIE_ON_ACCESS(array, 40); - DIE_ON_ACCESS(array, 60); - DIE_ON_ACCESS(array, 79); - ASAN_UNPOISON_MEMORY_REGION(array + 40, 40); - // access previously poisoned memory. - ACCESS(array, 40); - ACCESS(array, 79); - free(array); -} - -TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { - char *array = Ident((char*)malloc(120)); - // Poison [0..40) and [80..120) - ASAN_POISON_MEMORY_REGION(array, 40); - ASAN_POISON_MEMORY_REGION(array + 80, 40); - DIE_ON_ACCESS(array, 20); - ACCESS(array, 60); - DIE_ON_ACCESS(array, 100); - // Poison whole array - [0..120) - ASAN_POISON_MEMORY_REGION(array, 120); - DIE_ON_ACCESS(array, 60); - // Unpoison [24..96) - ASAN_UNPOISON_MEMORY_REGION(array + 24, 72); - DIE_ON_ACCESS(array, 23); - ACCESS(array, 24); - ACCESS(array, 60); - ACCESS(array, 95); - DIE_ON_ACCESS(array, 96); - free(array); -} - -TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { - // Vector of capacity 20 - char *vec = Ident((char*)malloc(20)); - ASAN_POISON_MEMORY_REGION(vec, 20); - for (size_t i = 0; i < 7; i++) { - // Simulate push_back. - ASAN_UNPOISON_MEMORY_REGION(vec + i, 1); - ACCESS(vec, i); - DIE_ON_ACCESS(vec, i + 1); - } - for (size_t i = 7; i > 0; i--) { - // Simulate pop_back. - ASAN_POISON_MEMORY_REGION(vec + i - 1, 1); - DIE_ON_ACCESS(vec, i - 1); - if (i > 1) ACCESS(vec, i - 2); - } - free(vec); -} - -// Make sure that each aligned block of size "2^granularity" doesn't have -// "true" value before "false" value. -static void MakeShadowValid(bool *shadow, int length, int granularity) { - bool can_be_poisoned = true; - for (int i = length - 1; i >= 0; i--) { - can_be_poisoned &= shadow[i]; - shadow[i] &= can_be_poisoned; - if (i % (1 << granularity) == 0) { - can_be_poisoned = true; - } - } -} - -TEST(AddressSanitizerInterface, PoisoningStressTest) { - const size_t kSize = 24; - bool expected[kSize]; - char *arr = Ident((char*)malloc(kSize)); - for (size_t l1 = 0; l1 < kSize; l1++) { - for (size_t s1 = 1; l1 + s1 <= kSize; s1++) { - for (size_t l2 = 0; l2 < kSize; l2++) { - for (size_t s2 = 1; l2 + s2 <= kSize; s2++) { - // Poison [l1, l1+s1), [l2, l2+s2) and check result. - ASAN_UNPOISON_MEMORY_REGION(arr, kSize); - ASAN_POISON_MEMORY_REGION(arr + l1, s1); - ASAN_POISON_MEMORY_REGION(arr + l2, s2); - memset(expected, false, kSize); - memset(expected + l1, true, s1); - MakeShadowValid(expected, 24, /*granularity*/ 3); - memset(expected + l2, true, s2); - MakeShadowValid(expected, 24, /*granularity*/ 3); - for (size_t i = 0; i < kSize; i++) { - ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); - } - // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result. - ASAN_POISON_MEMORY_REGION(arr, kSize); - ASAN_UNPOISON_MEMORY_REGION(arr + l1, s1); - ASAN_UNPOISON_MEMORY_REGION(arr + l2, s2); - memset(expected, true, kSize); - memset(expected + l1, false, s1); - MakeShadowValid(expected, 24, /*granularity*/ 3); - memset(expected + l2, false, s2); - MakeShadowValid(expected, 24, /*granularity*/ 3); - for (size_t i = 0; i < kSize; i++) { - ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); - } - } - } - } - } -} - -static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; -static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; - -TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { - char *array = Ident((char*)malloc(120)); - ASAN_UNPOISON_MEMORY_REGION(array, 120); - // Try to unpoison not owned memory - EXPECT_DEATH(ASAN_UNPOISON_MEMORY_REGION(array, 121), - kInvalidUnpoisonMessage); - EXPECT_DEATH(ASAN_UNPOISON_MEMORY_REGION(array - 1, 120), - kInvalidUnpoisonMessage); - - ASAN_POISON_MEMORY_REGION(array, 120); - // Try to poison not owned memory. - EXPECT_DEATH(ASAN_POISON_MEMORY_REGION(array, 121), kInvalidPoisonMessage); - EXPECT_DEATH(ASAN_POISON_MEMORY_REGION(array - 1, 120), - kInvalidPoisonMessage); - free(array); -} diff --git a/lib/asan/tests/asan_mac_test.h b/lib/asan/tests/asan_mac_test.h index e3ad8273ae14..441547a5a3dc 100644 --- a/lib/asan/tests/asan_mac_test.h +++ b/lib/asan/tests/asan_mac_test.h @@ -1,5 +1,5 @@ extern "C" { - void CFAllocatorDefaultDoubleFree(); + void *CFAllocatorDefaultDoubleFree(void *unused); void CFAllocatorSystemDefaultDoubleFree(); void CFAllocatorMallocDoubleFree(); void CFAllocatorMallocZoneDoubleFree(); @@ -13,4 +13,7 @@ extern "C" { void TestGCDSourceEvent(); void TestGCDSourceCancel(); void TestGCDGroupAsync(); + void TestOOBNSObjects(); + void TestNSURLDeallocation(); + void TestPassCFMemoryToAnotherThread(); } diff --git a/lib/asan/tests/asan_mac_test.mm b/lib/asan/tests/asan_mac_test.mm index b5dbbde4f315..4e5873b74485 100644 --- a/lib/asan/tests/asan_mac_test.mm +++ b/lib/asan/tests/asan_mac_test.mm @@ -7,11 +7,14 @@ #import #import +#import -void CFAllocatorDefaultDoubleFree() { +// This is a (void*)(void*) function so it can be passed to pthread_create. +void *CFAllocatorDefaultDoubleFree(void *unused) { void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0); CFAllocatorDeallocate(kCFAllocatorDefault, mem); CFAllocatorDeallocate(kCFAllocatorDefault, mem); + return 0; } void CFAllocatorSystemDefaultDoubleFree() { @@ -32,6 +35,10 @@ void CFAllocatorMallocZoneDoubleFree() { CFAllocatorDeallocate(kCFAllocatorMallocZone, mem); } +__attribute__((noinline)) +void access_memory(char *a) { + *a = 0; +} // Test the +load instrumentation. // Because the +load methods are invoked before anything else is initialized, @@ -51,7 +58,7 @@ char kStartupStr[] = +(void) load { for (int i = 0; i < strlen(kStartupStr); i++) { - volatile char ch = kStartupStr[i]; // make sure no optimizations occur. + access_memory(&kStartupStr[i]); // make sure no optimizations occur. } // Don't print anything here not to interfere with the death tests. } @@ -66,7 +73,7 @@ void worker_do_alloc(int size) { void worker_do_crash(int size) { char * volatile mem = malloc(size); - mem[size] = 0; // BOOM + access_memory(&mem[size]); // BOOM free(mem); } @@ -162,7 +169,7 @@ void TestGCDSourceEvent() { dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0); char * volatile mem = malloc(10); dispatch_source_set_event_handler(timer, ^{ - mem[10] = 1; + access_memory(&mem[10]); }); dispatch_resume(timer); sleep(2); @@ -186,7 +193,7 @@ void TestGCDSourceCancel() { dispatch_source_cancel(timer); }); dispatch_source_set_cancel_handler(timer, ^{ - mem[10] = 1; + access_memory(&mem[10]); }); dispatch_resume(timer); sleep(2); @@ -197,7 +204,34 @@ void TestGCDGroupAsync() { dispatch_group_t group = dispatch_group_create(); char * volatile mem = malloc(10); dispatch_group_async(group, queue, ^{ - mem[10] = 1; + access_memory(&mem[10]); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); } + +@interface FixedArray : NSObject { + int items[10]; +} +@end + +@implementation FixedArray +-(int) access: (int)index { + return items[index]; +} +@end + +void TestOOBNSObjects() { + id anObject = [FixedArray new]; + [anObject access:1]; + [anObject access:11]; + [anObject release]; +} + +void TestNSURLDeallocation() { + NSURL *base = + [[NSURL alloc] initWithString:@"file://localhost/Users/glider/Library/"]; + volatile NSURL *u = + [[NSURL alloc] initWithString:@"Saved Application State" + relativeToURL:base]; + [u release]; +} diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc index 204c0dacc342..44d4c3c845b2 100644 --- a/lib/asan/tests/asan_noinst_test.cc +++ b/lib/asan/tests/asan_noinst_test.cc @@ -1,4 +1,4 @@ -//===-- asan_noinst_test.cc ------------*- C++ -*-===// +//===-- asan_noinst_test.cc ----------------------===// // // The LLVM Compiler Infrastructure // @@ -21,17 +21,18 @@ #include #include #include -#include +#include // for memset() #include +#include #include "gtest/gtest.h" // Simple stand-alone pseudorandom number generator. // Current algorithm is ANSI C linear congruential PRNG. -static inline uint32_t my_rand(uint32_t* state) { +static inline u32 my_rand(u32* state) { return (*state = *state * 1103515245 + 12345) >> 16; } -static uint32_t global_seed = 0; +static u32 global_seed = 0; TEST(AddressSanitizer, InternalSimpleDeathTest) { @@ -39,7 +40,7 @@ TEST(AddressSanitizer, InternalSimpleDeathTest) { } static void MallocStress(size_t n) { - uint32_t seed = my_rand(&global_seed); + u32 seed = my_rand(&global_seed); __asan::AsanStackTrace stack1; stack1.trace[0] = 0xa123; stack1.trace[1] = 0xa456; @@ -92,16 +93,16 @@ TEST(AddressSanitizer, NoInstMallocTest) { #endif } -static void PrintShadow(const char *tag, uintptr_t ptr, size_t size) { +static void PrintShadow(const char *tag, uptr ptr, size_t size) { fprintf(stderr, "%s shadow: %lx size % 3ld: ", tag, (long)ptr, (long)size); - uintptr_t prev_shadow = 0; - for (intptr_t i = -32; i < (intptr_t)size + 32; i++) { - uintptr_t shadow = __asan::MemToShadow(ptr + i); - if (i == 0 || i == (intptr_t)size) + uptr prev_shadow = 0; + for (sptr i = -32; i < (sptr)size + 32; i++) { + uptr shadow = __asan::MemToShadow(ptr + i); + if (i == 0 || i == (sptr)size) fprintf(stderr, "."); if (shadow != prev_shadow) { prev_shadow = shadow; - fprintf(stderr, "%02x", (int)*(uint8_t*)shadow); + fprintf(stderr, "%02x", (int)*(u8*)shadow); } } fprintf(stderr, "\n"); @@ -110,13 +111,13 @@ static void PrintShadow(const char *tag, uintptr_t ptr, size_t size) { TEST(AddressSanitizer, DISABLED_InternalPrintShadow) { for (size_t size = 1; size <= 513; size++) { char *ptr = new char[size]; - PrintShadow("m", (uintptr_t)ptr, size); + PrintShadow("m", (uptr)ptr, size); delete [] ptr; - PrintShadow("f", (uintptr_t)ptr, size); + PrintShadow("f", (uptr)ptr, size); } } -static uintptr_t pc_array[] = { +static uptr pc_array[] = { #if __WORDSIZE == 64 0x7effbf756068ULL, 0x7effbf75e5abULL, @@ -207,19 +208,20 @@ static uintptr_t pc_array[] = { }; void CompressStackTraceTest(size_t n_iter) { - uint32_t seed = my_rand(&global_seed); + u32 seed = my_rand(&global_seed); const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array); - uint32_t compressed[2 * kNumPcs]; + u32 compressed[2 * kNumPcs]; for (size_t iter = 0; iter < n_iter; iter++) { std::random_shuffle(pc_array, pc_array + kNumPcs); __asan::AsanStackTrace stack0, stack1; stack0.CopyFrom(pc_array, kNumPcs); - stack0.size = std::max((size_t)1, (size_t)my_rand(&seed) % stack0.size); + stack0.size = std::max((size_t)1, (size_t)(my_rand(&seed) % stack0.size)); size_t compress_size = std::max((size_t)2, (size_t)my_rand(&seed) % (2 * kNumPcs)); size_t n_frames = __asan::AsanStackTrace::CompressStack(&stack0, compressed, compress_size); + Ident(n_frames); assert(n_frames <= stack0.size); __asan::AsanStackTrace::UncompressStack(&stack1, compressed, compress_size); assert(stack1.size == n_frames); @@ -235,7 +237,7 @@ TEST(AddressSanitizer, CompressStackTraceTest) { void CompressStackTraceBenchmark(size_t n_iter) { const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array); - uint32_t compressed[2 * kNumPcs]; + u32 compressed[2 * kNumPcs]; std::random_shuffle(pc_array, pc_array + kNumPcs); __asan::AsanStackTrace stack0; @@ -274,7 +276,8 @@ TEST(AddressSanitizer, QuarantineTest) { } void *ThreadedQuarantineTestWorker(void *unused) { - uint32_t seed = my_rand(&global_seed); + (void)unused; + u32 seed = my_rand(&global_seed); __asan::AsanStackTrace stack; stack.trace[0] = 0x890; stack.size = 1; @@ -301,6 +304,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) { } void *ThreadedOneSizeMallocStress(void *unused) { + (void)unused; __asan::AsanStackTrace stack; stack.trace[0] = 0x890; stack.size = 1; @@ -327,3 +331,371 @@ TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) { pthread_join(t[i], 0); } } + +TEST(AddressSanitizer, MemsetWildAddressTest) { + typedef void*(*memset_p)(void*, int, size_t); + // Prevent inlining of memset(). + volatile memset_p libc_memset = (memset_p)memset; + EXPECT_DEATH(libc_memset((void*)(kLowShadowBeg + kPageSize), 0, 100), + "unknown-crash.*low shadow"); + EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + kPageSize), 0, 100), + "unknown-crash.*shadow gap"); + EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + kPageSize), 0, 100), + "unknown-crash.*high shadow"); +} + +TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { + EXPECT_EQ(1U, __asan_get_estimated_allocated_size(0)); + const size_t sizes[] = { 1, 30, 1<<30 }; + for (size_t i = 0; i < 3; i++) { + EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i])); + } +} + +static const char* kGetAllocatedSizeErrorMsg = + "attempting to call __asan_get_allocated_size()"; + +TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { + const size_t kArraySize = 100; + char *array = Ident((char*)malloc(kArraySize)); + int *int_ptr = Ident(new int); + + // Allocated memory is owned by allocator. Allocated size should be + // equal to requested size. + EXPECT_EQ(true, __asan_get_ownership(array)); + EXPECT_EQ(kArraySize, __asan_get_allocated_size(array)); + EXPECT_EQ(true, __asan_get_ownership(int_ptr)); + EXPECT_EQ(sizeof(int), __asan_get_allocated_size(int_ptr)); + + // We cannot call GetAllocatedSize from the memory we didn't map, + // and from the interior pointers (not returned by previous malloc). + void *wild_addr = (void*)0x1; + EXPECT_EQ(false, __asan_get_ownership(wild_addr)); + EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg); + EXPECT_EQ(false, __asan_get_ownership(array + kArraySize / 2)); + EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2), + kGetAllocatedSizeErrorMsg); + + // NULL is not owned, but is a valid argument for __asan_get_allocated_size(). + EXPECT_EQ(false, __asan_get_ownership(NULL)); + EXPECT_EQ(0U, __asan_get_allocated_size(NULL)); + + // When memory is freed, it's not owned, and call to GetAllocatedSize + // is forbidden. + free(array); + EXPECT_EQ(false, __asan_get_ownership(array)); + EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg); + + delete int_ptr; +} + +TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) { + size_t before_malloc, after_malloc, after_free; + char *array; + const size_t kMallocSize = 100; + before_malloc = __asan_get_current_allocated_bytes(); + + array = Ident((char*)malloc(kMallocSize)); + after_malloc = __asan_get_current_allocated_bytes(); + EXPECT_EQ(before_malloc + kMallocSize, after_malloc); + + free(array); + after_free = __asan_get_current_allocated_bytes(); + EXPECT_EQ(before_malloc, after_free); +} + +static void DoDoubleFree() { + int *x = Ident(new int); + delete Ident(x); + delete Ident(x); +} + +// This test is run in a separate process, so that large malloced +// chunk won't remain in the free lists after the test. +// Note: use ASSERT_* instead of EXPECT_* here. +static void RunGetHeapSizeTestAndDie() { + size_t old_heap_size, new_heap_size, heap_growth; + // We unlikely have have chunk of this size in free list. + static const size_t kLargeMallocSize = 1 << 29; // 512M + old_heap_size = __asan_get_heap_size(); + fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); + free(Ident(malloc(kLargeMallocSize))); + new_heap_size = __asan_get_heap_size(); + heap_growth = new_heap_size - old_heap_size; + fprintf(stderr, "heap growth after first malloc: %zu\n", heap_growth); + ASSERT_GE(heap_growth, kLargeMallocSize); + ASSERT_LE(heap_growth, 2 * kLargeMallocSize); + + // Now large chunk should fall into free list, and can be + // allocated without increasing heap size. + old_heap_size = new_heap_size; + free(Ident(malloc(kLargeMallocSize))); + heap_growth = __asan_get_heap_size() - old_heap_size; + fprintf(stderr, "heap growth after second malloc: %zu\n", heap_growth); + ASSERT_LT(heap_growth, kLargeMallocSize); + + // Test passed. Now die with expected double-free. + DoDoubleFree(); +} + +TEST(AddressSanitizerInterface, GetHeapSizeTest) { + EXPECT_DEATH(RunGetHeapSizeTestAndDie(), "double-free"); +} + +// Note: use ASSERT_* instead of EXPECT_* here. +static void DoLargeMallocForGetFreeBytesTestAndDie() { + size_t old_free_bytes, new_free_bytes; + static const size_t kLargeMallocSize = 1 << 29; // 512M + // If we malloc and free a large memory chunk, it will not fall + // into quarantine and will be available for future requests. + old_free_bytes = __asan_get_free_bytes(); + fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); + fprintf(stderr, "free bytes before malloc: %zu\n", old_free_bytes); + free(Ident(malloc(kLargeMallocSize))); + new_free_bytes = __asan_get_free_bytes(); + fprintf(stderr, "free bytes after malloc and free: %zu\n", new_free_bytes); + ASSERT_GE(new_free_bytes, old_free_bytes + kLargeMallocSize); + // Test passed. + DoDoubleFree(); +} + +TEST(AddressSanitizerInterface, GetFreeBytesTest) { + static const size_t kNumOfChunks = 100; + static const size_t kChunkSize = 100; + char *chunks[kNumOfChunks]; + size_t i; + size_t old_free_bytes, new_free_bytes; + // Allocate a small chunk. Now allocator probably has a lot of these + // chunks to fulfill future requests. So, future requests will decrease + // the number of free bytes. + chunks[0] = Ident((char*)malloc(kChunkSize)); + old_free_bytes = __asan_get_free_bytes(); + for (i = 1; i < kNumOfChunks; i++) { + chunks[i] = Ident((char*)malloc(kChunkSize)); + new_free_bytes = __asan_get_free_bytes(); + EXPECT_LT(new_free_bytes, old_free_bytes); + old_free_bytes = new_free_bytes; + } + EXPECT_DEATH(DoLargeMallocForGetFreeBytesTestAndDie(), "double-free"); +} + +static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<20, 357}; +static const size_t kManyThreadsIterations = 250; +static const size_t kManyThreadsNumThreads = (__WORDSIZE == 32) ? 40 : 200; + +void *ManyThreadsWithStatsWorker(void *arg) { + (void)arg; + for (size_t iter = 0; iter < kManyThreadsIterations; iter++) { + for (size_t size_index = 0; size_index < 4; size_index++) { + free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); + } + } + return 0; +} + +TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { + size_t before_test, after_test, i; + pthread_t threads[kManyThreadsNumThreads]; + before_test = __asan_get_current_allocated_bytes(); + for (i = 0; i < kManyThreadsNumThreads; i++) { + pthread_create(&threads[i], 0, + (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); + } + for (i = 0; i < kManyThreadsNumThreads; i++) { + pthread_join(threads[i], 0); + } + after_test = __asan_get_current_allocated_bytes(); + // ASan stats also reflect memory usage of internal ASan RTL structs, + // so we can't check for equality here. + EXPECT_LT(after_test, before_test + (1UL<<20)); +} + +TEST(AddressSanitizerInterface, ExitCode) { + int original_exit_code = __asan_set_error_exit_code(7); + EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), ""); + EXPECT_EQ(7, __asan_set_error_exit_code(8)); + EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), ""); + EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code)); + EXPECT_EXIT(DoDoubleFree(), + ::testing::ExitedWithCode(original_exit_code), ""); +} + +static void MyDeathCallback() { + fprintf(stderr, "MyDeathCallback\n"); +} + +TEST(AddressSanitizerInterface, DeathCallbackTest) { + __asan_set_death_callback(MyDeathCallback); + EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback"); + __asan_set_death_callback(NULL); +} + +static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; + +#define GOOD_ACCESS(ptr, offset) \ + EXPECT_FALSE(__asan::AddressIsPoisoned((uptr)(ptr + offset))) + +#define BAD_ACCESS(ptr, offset) \ + EXPECT_TRUE(__asan::AddressIsPoisoned((uptr)(ptr + offset))) + +TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { + char *array = Ident((char*)malloc(120)); + // poison array[40..80) + __asan_poison_memory_region(array + 40, 40); + GOOD_ACCESS(array, 39); + GOOD_ACCESS(array, 80); + BAD_ACCESS(array, 40); + BAD_ACCESS(array, 60); + BAD_ACCESS(array, 79); + EXPECT_DEATH(__asan_report_error(0, 0, 0, (uptr)(array + 40), true, 1), + kUseAfterPoisonErrorMessage); + __asan_unpoison_memory_region(array + 40, 40); + // access previously poisoned memory. + GOOD_ACCESS(array, 40); + GOOD_ACCESS(array, 79); + free(array); +} + +TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { + char *array = Ident((char*)malloc(120)); + // Poison [0..40) and [80..120) + __asan_poison_memory_region(array, 40); + __asan_poison_memory_region(array + 80, 40); + BAD_ACCESS(array, 20); + GOOD_ACCESS(array, 60); + BAD_ACCESS(array, 100); + // Poison whole array - [0..120) + __asan_poison_memory_region(array, 120); + BAD_ACCESS(array, 60); + // Unpoison [24..96) + __asan_unpoison_memory_region(array + 24, 72); + BAD_ACCESS(array, 23); + GOOD_ACCESS(array, 24); + GOOD_ACCESS(array, 60); + GOOD_ACCESS(array, 95); + BAD_ACCESS(array, 96); + free(array); +} + +TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { + // Vector of capacity 20 + char *vec = Ident((char*)malloc(20)); + __asan_poison_memory_region(vec, 20); + for (size_t i = 0; i < 7; i++) { + // Simulate push_back. + __asan_unpoison_memory_region(vec + i, 1); + GOOD_ACCESS(vec, i); + BAD_ACCESS(vec, i + 1); + } + for (size_t i = 7; i > 0; i--) { + // Simulate pop_back. + __asan_poison_memory_region(vec + i - 1, 1); + BAD_ACCESS(vec, i - 1); + if (i > 1) GOOD_ACCESS(vec, i - 2); + } + free(vec); +} + +// Make sure that each aligned block of size "2^granularity" doesn't have +// "true" value before "false" value. +static void MakeShadowValid(bool *shadow, int length, int granularity) { + bool can_be_poisoned = true; + for (int i = length - 1; i >= 0; i--) { + if (!shadow[i]) + can_be_poisoned = false; + if (!can_be_poisoned) + shadow[i] = false; + if (i % (1 << granularity) == 0) { + can_be_poisoned = true; + } + } +} + +TEST(AddressSanitizerInterface, PoisoningStressTest) { + const size_t kSize = 24; + bool expected[kSize]; + char *arr = Ident((char*)malloc(kSize)); + for (size_t l1 = 0; l1 < kSize; l1++) { + for (size_t s1 = 1; l1 + s1 <= kSize; s1++) { + for (size_t l2 = 0; l2 < kSize; l2++) { + for (size_t s2 = 1; l2 + s2 <= kSize; s2++) { + // Poison [l1, l1+s1), [l2, l2+s2) and check result. + __asan_unpoison_memory_region(arr, kSize); + __asan_poison_memory_region(arr + l1, s1); + __asan_poison_memory_region(arr + l2, s2); + memset(expected, false, kSize); + memset(expected + l1, true, s1); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + memset(expected + l2, true, s2); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + for (size_t i = 0; i < kSize; i++) { + ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); + } + // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result. + __asan_poison_memory_region(arr, kSize); + __asan_unpoison_memory_region(arr + l1, s1); + __asan_unpoison_memory_region(arr + l2, s2); + memset(expected, true, kSize); + memset(expected + l1, false, s1); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + memset(expected + l2, false, s2); + MakeShadowValid(expected, kSize, /*granularity*/ 3); + for (size_t i = 0; i < kSize; i++) { + ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); + } + } + } + } + } +} + +static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; +static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; + +TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { + char *array = Ident((char*)malloc(120)); + __asan_unpoison_memory_region(array, 120); + // Try to unpoison not owned memory + EXPECT_DEATH(__asan_unpoison_memory_region(array, 121), + kInvalidUnpoisonMessage); + EXPECT_DEATH(__asan_unpoison_memory_region(array - 1, 120), + kInvalidUnpoisonMessage); + + __asan_poison_memory_region(array, 120); + // Try to poison not owned memory. + EXPECT_DEATH(__asan_poison_memory_region(array, 121), kInvalidPoisonMessage); + EXPECT_DEATH(__asan_poison_memory_region(array - 1, 120), + kInvalidPoisonMessage); + free(array); +} + +static void ErrorReportCallbackOneToZ(const char *report) { + write(2, "ABCDEF", 6); +} + +TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) { + __asan_set_error_report_callback(ErrorReportCallbackOneToZ); + EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1), "ABCDEF"); + __asan_set_error_report_callback(NULL); +} + +TEST(AddressSanitizerInterface, GetOwnershipStressTest) { + std::vector pointers; + std::vector sizes; + const size_t kNumMallocs = + (__WORDSIZE <= 32 || ASAN_LOW_MEMORY) ? 1 << 10 : 1 << 14; + for (size_t i = 0; i < kNumMallocs; i++) { + size_t size = i * 100 + 1; + pointers.push_back((char*)malloc(size)); + sizes.push_back(size); + } + for (size_t i = 0; i < 4000000; i++) { + EXPECT_FALSE(__asan_get_ownership(&pointers)); + EXPECT_FALSE(__asan_get_ownership((void*)0x1234)); + size_t idx = i % kNumMallocs; + EXPECT_TRUE(__asan_get_ownership(pointers[idx])); + EXPECT_EQ(sizes[idx], __asan_get_allocated_size(pointers[idx])); + } + for (size_t i = 0, n = pointers.size(); i < n; i++) + free(pointers[i]); +} diff --git a/lib/asan/tests/asan_racy_double_free_test.cc b/lib/asan/tests/asan_racy_double_free_test.cc new file mode 100644 index 000000000000..deeeb4f45e2f --- /dev/null +++ b/lib/asan/tests/asan_racy_double_free_test.cc @@ -0,0 +1,32 @@ +#include +#include +#include + +const int N = 1000; +void *x[N]; + +void *Thread1(void *unused) { + for (int i = 0; i < N; i++) { + fprintf(stderr, "%s %d\n", __FUNCTION__, i); + free(x[i]); + } + return NULL; +} + +void *Thread2(void *unused) { + for (int i = 0; i < N; i++) { + fprintf(stderr, "%s %d\n", __FUNCTION__, i); + free(x[i]); + } + return NULL; +} + +int main() { + for (int i = 0; i < N; i++) + x[i] = malloc(128); + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + pthread_create(&t[1], 0, Thread2, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); +} diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc index 0ff72d3cf6d8..8e967e929899 100644 --- a/lib/asan/tests/asan_test.cc +++ b/lib/asan/tests/asan_test.cc @@ -1,4 +1,4 @@ -//===-- asan_test.cc ------------*- C++ -*-===// +//===-- asan_test.cc ----------------------===// // // The LLVM Compiler Infrastructure // @@ -20,7 +20,7 @@ #include #include -#if defined(__i386__) or defined(__x86_64__) +#if defined(__i386__) || defined(__x86_64__) #include #endif @@ -29,13 +29,10 @@ #ifndef __APPLE__ #include -#endif // __APPLE__ - -#ifdef __APPLE__ -static bool APPLE = true; #else -static bool APPLE = false; -#endif +#include // For MAC_OS_X_VERSION_* +#include +#endif // __APPLE__ #if ASAN_HAS_EXCEPTIONS # define ASAN_THROW(x) throw (x) @@ -61,99 +58,14 @@ static inline uint32_t my_rand(uint32_t* state) { static uint32_t global_seed = 0; -class ObjdumpOfMyself { - public: - explicit ObjdumpOfMyself(const string &binary) { - is_correct = true; - string objdump_name = APPLE ? "gobjdump" : "objdump"; - string prog = objdump_name + " -d " + binary; - // TODO(glider): popen() succeeds even if the file does not exist. - FILE *pipe = popen(prog.c_str(), "r"); - string objdump; - if (pipe) { - const int kBuffSize = 4096; - char buff[kBuffSize+1]; - int read_bytes; - while ((read_bytes = fread(buff, 1, kBuffSize, pipe)) > 0) { - buff[read_bytes] = 0; - objdump.append(buff); - } - pclose(pipe); - } else { - is_correct = false; - } - // cut the objdump into functions - string fn, next_fn; - size_t next_start; - for (size_t start = fn_start(objdump, 0, &fn); - start != string::npos; - start = next_start, fn = next_fn) { - next_start = fn_start(objdump, start, &next_fn); - // fprintf(stderr, "start: %d next_start = %d fn: %s\n", - // (int)start, (int)next_start, fn.c_str()); - // Mac OS adds the "_" prefix to function names. - if (fn.find(APPLE ? "_Disasm" : "Disasm") == string::npos) { - continue; - } - string fn_body = objdump.substr(start, next_start - start); - // fprintf(stderr, "%s:\n%s", fn.c_str(), fn_body.c_str()); - functions_[fn] = fn_body; - } - } - - string &GetFuncDisasm(const string &fn) { - return functions_[fn]; - } - - int CountInsnInFunc(const string &fn, const vector &insns) { - // Mac OS adds the "_" prefix to function names. - string fn_ref = APPLE ? "_" + fn : fn; - const string &disasm = GetFuncDisasm(fn_ref); - if (disasm.empty()) return -1; - size_t counter = 0; - for (size_t i = 0; i < insns.size(); i++) { - size_t pos = 0; - while ((pos = disasm.find(insns[i], pos)) != string::npos) { - counter++; - pos++; - } - } - return counter; - } - - bool IsCorrect() { return is_correct; } - - private: - size_t fn_start(const string &objdump, size_t start_pos, string *fn) { - size_t pos = objdump.find(">:\n", start_pos); - if (pos == string::npos) - return string::npos; - size_t beg = pos; - while (beg > 0 && objdump[beg - 1] != '<') - beg--; - *fn = objdump.substr(beg, pos - beg); - return pos + 3; - } - - map functions_; - bool is_correct; -}; - -static ObjdumpOfMyself *objdump_of_myself() { - static ObjdumpOfMyself *o = new ObjdumpOfMyself(progname); - return o; -} - const size_t kLargeMalloc = 1 << 24; -template -__attribute__((noinline)) -void asan_write(T *a) { +template +NOINLINE void asan_write(T *a) { *a = 0; } -__attribute__((noinline)) -void asan_write_sized_aligned(uint8_t *p, size_t size) { +NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) { EXPECT_EQ(0, ((uintptr_t)p % size)); if (size == 1) asan_write((uint8_t*)p); else if (size == 2) asan_write((uint16_t*)p); @@ -161,45 +73,41 @@ void asan_write_sized_aligned(uint8_t *p, size_t size) { else if (size == 8) asan_write((uint64_t*)p); } -__attribute__((noinline)) void *malloc_fff(size_t size) { +NOINLINE void *malloc_fff(size_t size) { void *res = malloc/**/(size); break_optimization(0); return res;} -__attribute__((noinline)) void *malloc_eee(size_t size) { +NOINLINE void *malloc_eee(size_t size) { void *res = malloc_fff(size); break_optimization(0); return res;} -__attribute__((noinline)) void *malloc_ddd(size_t size) { +NOINLINE void *malloc_ddd(size_t size) { void *res = malloc_eee(size); break_optimization(0); return res;} -__attribute__((noinline)) void *malloc_ccc(size_t size) { +NOINLINE void *malloc_ccc(size_t size) { void *res = malloc_ddd(size); break_optimization(0); return res;} -__attribute__((noinline)) void *malloc_bbb(size_t size) { +NOINLINE void *malloc_bbb(size_t size) { void *res = malloc_ccc(size); break_optimization(0); return res;} -__attribute__((noinline)) void *malloc_aaa(size_t size) { +NOINLINE void *malloc_aaa(size_t size) { void *res = malloc_bbb(size); break_optimization(0); return res;} #ifndef __APPLE__ -__attribute__((noinline)) void *memalign_fff(size_t alignment, size_t size) { +NOINLINE void *memalign_fff(size_t alignment, size_t size) { void *res = memalign/**/(alignment, size); break_optimization(0); return res;} -__attribute__((noinline)) void *memalign_eee(size_t alignment, size_t size) { +NOINLINE void *memalign_eee(size_t alignment, size_t size) { void *res = memalign_fff(alignment, size); break_optimization(0); return res;} -__attribute__((noinline)) void *memalign_ddd(size_t alignment, size_t size) { +NOINLINE void *memalign_ddd(size_t alignment, size_t size) { void *res = memalign_eee(alignment, size); break_optimization(0); return res;} -__attribute__((noinline)) void *memalign_ccc(size_t alignment, size_t size) { +NOINLINE void *memalign_ccc(size_t alignment, size_t size) { void *res = memalign_ddd(alignment, size); break_optimization(0); return res;} -__attribute__((noinline)) void *memalign_bbb(size_t alignment, size_t size) { +NOINLINE void *memalign_bbb(size_t alignment, size_t size) { void *res = memalign_ccc(alignment, size); break_optimization(0); return res;} -__attribute__((noinline)) void *memalign_aaa(size_t alignment, size_t size) { +NOINLINE void *memalign_aaa(size_t alignment, size_t size) { void *res = memalign_bbb(alignment, size); break_optimization(0); return res;} #endif // __APPLE__ -__attribute__((noinline)) - void free_ccc(void *p) { free(p); break_optimization(0);} -__attribute__((noinline)) - void free_bbb(void *p) { free_ccc(p); break_optimization(0);} -__attribute__((noinline)) - void free_aaa(void *p) { free_bbb(p); break_optimization(0);} +NOINLINE void free_ccc(void *p) { free(p); break_optimization(0);} +NOINLINE void free_bbb(void *p) { free_ccc(p); break_optimization(0);} +NOINLINE void free_aaa(void *p) { free_bbb(p); break_optimization(0);} -template -__attribute__((noinline)) -void oob_test(int size, int off) { +template +NOINLINE void oob_test(int size, int off) { char *p = (char*)malloc_aaa(size); // fprintf(stderr, "writing %d byte(s) into [%p,%p) with offset %d\n", // sizeof(T), p, p + size, off); @@ -208,9 +116,8 @@ void oob_test(int size, int off) { } -template -__attribute__((noinline)) -void uaf_test(int size, int off) { +template +NOINLINE void uaf_test(int size, int off) { char *p = (char *)malloc_aaa(size); free_aaa(p); for (int i = 1; i < 100; i++) @@ -255,13 +162,15 @@ TEST(AddressSanitizer, VariousMallocsTest) { *c = 0; delete c; -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(ANDROID) // fprintf(stderr, "posix_memalign\n"); int *pm; int pm_res = posix_memalign((void**)&pm, kPageSize, kPageSize); EXPECT_EQ(0, pm_res); free(pm); +#endif +#if !defined(__APPLE__) int *ma = (int*)memalign(kPageSize, kPageSize); EXPECT_EQ(0, (uintptr_t)ma % kPageSize); ma[123] = 0; @@ -295,48 +204,6 @@ TEST(AddressSanitizer, PvallocTest) { } #endif // __APPLE__ -void NoOpSignalHandler(int unused) { - fprintf(stderr, "NoOpSignalHandler (should not happen). Aborting\n"); - abort(); -} - -void NoOpSigaction(int, siginfo_t *siginfo, void *context) { - fprintf(stderr, "NoOpSigaction (should not happen). Aborting\n"); - abort(); -} - -TEST(AddressSanitizer, SignalTest) { - signal(SIGSEGV, NoOpSignalHandler); - signal(SIGILL, NoOpSignalHandler); - // If asan did not intercept sigaction NoOpSigaction will fire. - char *x = Ident((char*)malloc(5)); - EXPECT_DEATH(x[6]++, "is located 1 bytes to the right"); - free(Ident(x)); -} - -TEST(AddressSanitizer, SigactionTest) { - { - struct sigaction sigact; - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_sigaction = NoOpSigaction;; - sigact.sa_flags = SA_SIGINFO; - sigaction(SIGSEGV, &sigact, 0); - } - - { - struct sigaction sigact; - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_sigaction = NoOpSigaction;; - sigact.sa_flags = SA_SIGINFO; - sigaction(SIGILL, &sigact, 0); - } - - // If asan did not intercept sigaction NoOpSigaction will fire. - char *x = Ident((char*)malloc(5)); - EXPECT_DEATH(x[6]++, "is located 1 bytes to the right"); - free(Ident(x)); -} - void *TSDWorker(void *test_key) { if (test_key) { pthread_setspecific(*(pthread_key_t*)test_key, (void*)0xfeedface); @@ -367,7 +234,7 @@ TEST(AddressSanitizer, DISABLED_TSDTest) { pthread_key_delete(test_key); } -template +template void OOBTest() { char expected_str[100]; for (int size = sizeof(T); size < 20; size += 5) { @@ -531,7 +398,7 @@ static void MallocStress(size_t n) { } TEST(AddressSanitizer, MallocStressTest) { - MallocStress(200000); + MallocStress((ASAN_LOW_MEMORY) ? 20000 : 200000); } static void TestLargeMalloc(size_t size) { @@ -546,24 +413,29 @@ TEST(AddressSanitizer, LargeMallocTest) { } } +#if ASAN_LOW_MEMORY != 1 TEST(AddressSanitizer, HugeMallocTest) { #ifdef __APPLE__ // It was empirically found out that 1215 megabytes is the maximum amount of - // memory available to the process under AddressSanitizer on Darwin. + // memory available to the process under AddressSanitizer on 32-bit Mac 10.6. + // 32-bit Mac 10.7 gives even less (< 1G). // (the libSystem malloc() allows allocating up to 2300 megabytes without // ASan). - size_t n_megs = __WORDSIZE == 32 ? 1200 : 4100; + size_t n_megs = __WORDSIZE == 32 ? 500 : 4100; #else size_t n_megs = __WORDSIZE == 32 ? 2600 : 4100; #endif TestLargeMalloc(n_megs << 20); } +#endif TEST(AddressSanitizer, ThreadedMallocStressTest) { const int kNumThreads = 4; + const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000; pthread_t t[kNumThreads]; for (int i = 0; i < kNumThreads; i++) { - pthread_create(&t[i], 0, (void* (*)(void *x))MallocStress, (void*)100000); + pthread_create(&t[i], 0, (void* (*)(void *x))MallocStress, + (void*)kNumIterations); } for (int i = 0; i < kNumThreads; i++) { pthread_join(t[i], 0); @@ -601,6 +473,25 @@ TEST(AddressSanitizer, ReallocTest) { } } +#ifndef __APPLE__ +static const char *kMallocUsableSizeErrorMsg = + "AddressSanitizer attempting to call malloc_usable_size()"; + +TEST(AddressSanitizer, MallocUsableSizeTest) { + const size_t kArraySize = 100; + char *array = Ident((char*)malloc(kArraySize)); + int *int_ptr = Ident(new int); + EXPECT_EQ(0, malloc_usable_size(NULL)); + EXPECT_EQ(kArraySize, malloc_usable_size(array)); + EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr)); + EXPECT_DEATH(malloc_usable_size((void*)0x123), kMallocUsableSizeErrorMsg); + EXPECT_DEATH(malloc_usable_size(array + kArraySize / 2), + kMallocUsableSizeErrorMsg); + free(array); + EXPECT_DEATH(malloc_usable_size(array), kMallocUsableSizeErrorMsg); +} +#endif + void WrongFree() { int *x = (int*)malloc(100 * sizeof(int)); // Use the allocated memory, otherwise Clang will optimize it out. @@ -623,12 +514,15 @@ void DoubleFree() { } TEST(AddressSanitizer, DoubleFreeTest) { - EXPECT_DEATH(DoubleFree(), "ERROR: AddressSanitizer attempting double-free"); + EXPECT_DEATH(DoubleFree(), ASAN_PCRE_DOTALL + "ERROR: AddressSanitizer attempting double-free" + ".*is located 0 bytes inside of 400-byte region" + ".*freed by thread T0 here" + ".*previously allocated by thread T0 here"); } template -__attribute__((noinline)) -void SizedStackTest() { +NOINLINE void SizedStackTest() { char a[kSize]; char *A = Ident((char*)&a); for (size_t i = 0; i < kSize; i++) @@ -669,8 +563,7 @@ TEST(AddressSanitizer, ManyStackObjectsTest) { EXPECT_DEATH(Ident(ZZZ)[-1] = 0, ASAN_PCRE_DOTALL "XXX.*YYY.*ZZZ"); } -__attribute__((noinline)) -static void Frame0(int frame, char *a, char *b, char *c) { +NOINLINE static void Frame0(int frame, char *a, char *b, char *c) { char d[4] = {0}; char *D = Ident(d); switch (frame) { @@ -680,15 +573,15 @@ static void Frame0(int frame, char *a, char *b, char *c) { case 0: D[5]++; break; } } -__attribute__((noinline)) static void Frame1(int frame, char *a, char *b) { +NOINLINE static void Frame1(int frame, char *a, char *b) { char c[4] = {0}; Frame0(frame, a, b, c); break_optimization(0); } -__attribute__((noinline)) static void Frame2(int frame, char *a) { +NOINLINE static void Frame2(int frame, char *a) { char b[4] = {0}; Frame1(frame, a, b); break_optimization(0); } -__attribute__((noinline)) static void Frame3(int frame) { +NOINLINE static void Frame3(int frame) { char a[4] = {0}; Frame2(frame, a); break_optimization(0); } @@ -706,8 +599,7 @@ TEST(AddressSanitizer, GuiltyStackFrame3Test) { EXPECT_DEATH(Frame3(3), "located .*in frame <.*Frame3"); } -__attribute__((noinline)) -void LongJmpFunc1(jmp_buf buf) { +NOINLINE void LongJmpFunc1(jmp_buf buf) { // create three red zones for these two stack objects. int a; int b; @@ -718,8 +610,7 @@ void LongJmpFunc1(jmp_buf buf) { longjmp(buf, 1); } -__attribute__((noinline)) -void UnderscopeLongJmpFunc1(jmp_buf buf) { +NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) { // create three red zones for these two stack objects. int a; int b; @@ -730,8 +621,7 @@ void UnderscopeLongJmpFunc1(jmp_buf buf) { _longjmp(buf, 1); } -__attribute__((noinline)) -void SigLongJmpFunc1(sigjmp_buf buf) { +NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) { // create three red zones for these two stack objects. int a; int b; @@ -743,8 +633,7 @@ void SigLongJmpFunc1(sigjmp_buf buf) { } -__attribute__((noinline)) -void TouchStackFunc() { +NOINLINE void TouchStackFunc() { int a[100]; // long array will intersect with redzones from LongJmpFunc1. int *A = Ident(a); for (int i = 0; i < 100; i++) @@ -780,8 +669,7 @@ TEST(AddressSanitizer, SigLongJmpTest) { } #ifdef __EXCEPTIONS -__attribute__((noinline)) -void ThrowFunc() { +NOINLINE void ThrowFunc() { // create three red zones for these two stack objects. int a; int b; @@ -828,7 +716,7 @@ TEST(AddressSanitizer, ThreadStackReuseTest) { pthread_join(t, 0); } -#if defined(__i386__) or defined(__x86_64__) +#if defined(__i386__) || defined(__x86_64__) TEST(AddressSanitizer, Store128Test) { char *a = Ident((char*)malloc(Ident(12))); char *p = a; @@ -860,7 +748,7 @@ static string LeftOOBErrorMessage(int oob_distance) { return string(expected_str); } -template +template void MemSetOOBTestTemplate(size_t length) { if (length == 0) return; size_t size = Ident(sizeof(T) * length); @@ -917,7 +805,7 @@ TEST(AddressSanitizer, MemSetOOBTest) { } // Same test for memcpy and memmove functions -template +template void MemTransferOOBTestTemplate(size_t length) { if (length == 0) return; size_t size = Ident(sizeof(T) * length); @@ -1051,11 +939,14 @@ TEST(AddressSanitizer, StrLenOOBTest) { free(heap_string); } -static inline char* MallocAndMemsetString(size_t size) { +static inline char* MallocAndMemsetString(size_t size, char ch) { char *s = Ident((char*)malloc(size)); - memset(s, 'z', size); + memset(s, ch, size); return s; } +static inline char* MallocAndMemsetString(size_t size) { + return MallocAndMemsetString(size, 'z'); +} #ifndef __APPLE__ TEST(AddressSanitizer, StrNLenOOBTest) { @@ -1355,6 +1246,47 @@ TEST(AddressSanitizer, StrCatOOBTest) { EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0)); // length of "to" is just enough. strcat(to, from + 1); + + free(to); + free(from); +} + +TEST(AddressSanitizer, StrNCatOOBTest) { + size_t to_size = Ident(100); + char *to = MallocAndMemsetString(to_size); + to[0] = '\0'; + size_t from_size = Ident(20); + char *from = MallocAndMemsetString(from_size); + // Normal strncat calls. + strncat(to, from, 0); + strncat(to, from, from_size); + from[from_size - 1] = '\0'; + strncat(to, from, 2 * from_size); + // Catenating empty string is not an error. + strncat(to - 1, from, 0); + strncat(to, from + from_size - 1, 10); + // One of arguments points to not allocated memory. + EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBErrorMessage(1)); + EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBErrorMessage(1)); + EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBErrorMessage(0)); + EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBErrorMessage(0)); + + memset(from, 'z', from_size); + memset(to, 'z', to_size); + to[0] = '\0'; + // "from" is too short. + EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBErrorMessage(0)); + // "to" is not zero-terminated. + EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBErrorMessage(0)); + // "to" is too short to fit "from". + to[0] = 'z'; + to[to_size - from_size + 1] = '\0'; + EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBErrorMessage(0)); + // "to" is just enough. + strncat(to, from, from_size - 2); + + free(to); + free(from); } static string OverlapErrorMessage(const string &func) { @@ -1365,14 +1297,22 @@ TEST(AddressSanitizer, StrArgsOverlapTest) { size_t size = Ident(100); char *str = Ident((char*)malloc(size)); +// Do not check memcpy() on OS X 10.7 and later, where it actually aliases +// memmove(). +#if !defined(__APPLE__) || !defined(MAC_OS_X_VERSION_10_7) || \ + (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7) // Check "memcpy". Use Ident() to avoid inlining. memset(str, 'z', size); Ident(memcpy)(str + 1, str + 11, 10); Ident(memcpy)(str, str, 0); EXPECT_DEATH(Ident(memcpy)(str, str + 14, 15), OverlapErrorMessage("memcpy")); EXPECT_DEATH(Ident(memcpy)(str + 14, str, 15), OverlapErrorMessage("memcpy")); - EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1), - OverlapErrorMessage("memcpy")); +#endif + + // We do not treat memcpy with to==from as a bug. + // See http://llvm.org/bugs/show_bug.cgi?id=11763. + // EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1), + // OverlapErrorMessage("memcpy")); // Check "strcpy". memset(str, 'z', size); @@ -1403,9 +1343,117 @@ TEST(AddressSanitizer, StrArgsOverlapTest) { EXPECT_DEATH(strcat(str + 9, str), OverlapErrorMessage("strcat")); EXPECT_DEATH(strcat(str + 10, str), OverlapErrorMessage("strcat")); + // Check "strncat". + memset(str, 'z', size); + str[10] = '\0'; + strncat(str, str + 10, 10); // from is empty + strncat(str, str + 11, 10); + str[10] = '\0'; + str[20] = '\0'; + strncat(str + 5, str, 5); + str[10] = '\0'; + EXPECT_DEATH(strncat(str + 5, str, 6), OverlapErrorMessage("strncat")); + EXPECT_DEATH(strncat(str, str + 9, 10), OverlapErrorMessage("strncat")); + free(str); } +void CallAtoi(const char *nptr) { + Ident(atoi(nptr)); +} +void CallAtol(const char *nptr) { + Ident(atol(nptr)); +} +void CallAtoll(const char *nptr) { + Ident(atoll(nptr)); +} +typedef void(*PointerToCallAtoi)(const char*); + +void RunAtoiOOBTest(PointerToCallAtoi Atoi) { + char *array = MallocAndMemsetString(10, '1'); + // Invalid pointer to the string. + EXPECT_DEATH(Atoi(array + 11), RightOOBErrorMessage(1)); + EXPECT_DEATH(Atoi(array - 1), LeftOOBErrorMessage(1)); + // Die if a buffer doesn't have terminating NULL. + EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0)); + // Make last symbol a terminating NULL or other non-digit. + array[9] = '\0'; + Atoi(array); + array[9] = 'a'; + Atoi(array); + Atoi(array + 9); + // Sometimes we need to detect overflow if no digits are found. + memset(array, ' ', 10); + EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0)); + array[9] = '-'; + EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0)); + EXPECT_DEATH(Atoi(array + 9), RightOOBErrorMessage(0)); + array[8] = '-'; + Atoi(array); + delete array; +} + +TEST(AddressSanitizer, AtoiAndFriendsOOBTest) { + RunAtoiOOBTest(&CallAtoi); + RunAtoiOOBTest(&CallAtol); + RunAtoiOOBTest(&CallAtoll); +} + +void CallStrtol(const char *nptr, char **endptr, int base) { + Ident(strtol(nptr, endptr, base)); +} +void CallStrtoll(const char *nptr, char **endptr, int base) { + Ident(strtoll(nptr, endptr, base)); +} +typedef void(*PointerToCallStrtol)(const char*, char**, int); + +void RunStrtolOOBTest(PointerToCallStrtol Strtol) { + char *array = MallocAndMemsetString(3); + char *endptr = NULL; + array[0] = '1'; + array[1] = '2'; + array[2] = '3'; + // Invalid pointer to the string. + EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBErrorMessage(0)); + EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBErrorMessage(1)); + // Buffer overflow if there is no terminating null (depends on base). + Strtol(array, &endptr, 3); + EXPECT_EQ(array + 2, endptr); + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0)); + array[2] = 'z'; + Strtol(array, &endptr, 35); + EXPECT_EQ(array + 2, endptr); + EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBErrorMessage(0)); + // Add terminating zero to get rid of overflow. + array[2] = '\0'; + Strtol(array, NULL, 36); + // Don't check for overflow if base is invalid. + Strtol(array - 1, NULL, -1); + Strtol(array + 3, NULL, 1); + // Sometimes we need to detect overflow if no digits are found. + array[0] = array[1] = array[2] = ' '; + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0)); + array[2] = '+'; + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0)); + array[2] = '-'; + EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0)); + array[1] = '+'; + Strtol(array, NULL, 0); + array[1] = array[2] = 'z'; + Strtol(array, &endptr, 0); + EXPECT_EQ(array, endptr); + Strtol(array + 2, NULL, 0); + EXPECT_EQ(array, endptr); + delete array; +} + +TEST(AddressSanitizer, StrtollOOBTest) { + RunStrtolOOBTest(&CallStrtoll); +} +TEST(AddressSanitizer, StrtolOOBTest) { + RunStrtolOOBTest(&CallStrtol); +} + // At the moment we instrument memcpy/memove/memset calls at compile time so we // can't handle OOB error if these functions are called by pointer, see disabled // MemIntrinsicCallByPointerTest below @@ -1446,8 +1494,7 @@ TEST(AddressSanitizer, DISABLED_MemIntrinsicUnalignedAccessTest) { // TODO(samsonov): Add a test with malloc(0) // TODO(samsonov): Add tests for str* and mem* functions. -__attribute__((noinline)) -static int LargeFunction(bool do_bad_access) { +NOINLINE static int LargeFunction(bool do_bad_access) { int *x = new int[100]; x[0]++; x[1]++; @@ -1544,8 +1591,7 @@ TEST(AddressSanitizer, ShadowGapTest) { #endif // ASAN_NEEDS_SEGV extern "C" { -__attribute__((noinline)) -static void UseThenFreeThenUse() { +NOINLINE static void UseThenFreeThenUse() { char *x = Ident((char*)malloc(8)); *x = 1; free_aaa(x); @@ -1561,76 +1607,6 @@ TEST(AddressSanitizer, StrDupTest) { free(strdup(Ident("123"))); } -TEST(AddressSanitizer, ObjdumpTest) { - ObjdumpOfMyself *o = objdump_of_myself(); - EXPECT_TRUE(o->IsCorrect()); -} - -extern "C" { -__attribute__((noinline)) -static void DisasmSimple() { - Ident(0); -} - -__attribute__((noinline)) -static void DisasmParamWrite(int *a) { - *a = 1; -} - -__attribute__((noinline)) -static void DisasmParamInc(int *a) { - (*a)++; -} - -__attribute__((noinline)) -static void DisasmParamReadIfWrite(int *a) { - if (*a) - *a = 1; -} - -__attribute__((noinline)) -static int DisasmParamIfReadWrite(int *a, int cond) { - int res = 0; - if (cond) - res = *a; - *a = 0; - return res; -} - -static int GLOBAL; - -__attribute__((noinline)) -static void DisasmWriteGlob() { - GLOBAL = 1; -} -} // extern "C" - -TEST(AddressSanitizer, DisasmTest) { - int a; - DisasmSimple(); - DisasmParamWrite(&a); - DisasmParamInc(&a); - Ident(DisasmWriteGlob)(); - DisasmParamReadIfWrite(&a); - - a = 7; - EXPECT_EQ(7, DisasmParamIfReadWrite(&a, Ident(1))); - EXPECT_EQ(0, a); - - ObjdumpOfMyself *o = objdump_of_myself(); - vector insns; - insns.push_back("ud2"); - insns.push_back("__asan_report_"); - EXPECT_EQ(0, o->CountInsnInFunc("DisasmSimple", insns)); - EXPECT_EQ(1, o->CountInsnInFunc("DisasmParamWrite", insns)); - EXPECT_EQ(1, o->CountInsnInFunc("DisasmParamInc", insns)); - EXPECT_EQ(0, o->CountInsnInFunc("DisasmWriteGlob", insns)); - - // TODO(kcc): implement these (needs just one __asan_report). - EXPECT_EQ(2, o->CountInsnInFunc("DisasmParamReadIfWrite", insns)); - EXPECT_EQ(2, o->CountInsnInFunc("DisasmParamIfReadWrite", insns)); -} - // Currently we create and poison redzone at right of global variables. char glob5[5]; static char static110[110]; @@ -1718,8 +1694,7 @@ TEST(AddressSanitizer, LocalReferenceReturnTest) { #endif template -__attribute__((noinline)) -static void FuncWithStack() { +NOINLINE static void FuncWithStack() { char x[kSize]; Ident(x)[0] = 0; Ident(x)[kSize-1] = 0; @@ -1758,9 +1733,21 @@ TEST(AddressSanitizer, ThreadedStressStackReuseTest) { } } +static void *PthreadExit(void *a) { + pthread_exit(0); + return 0; +} + +TEST(AddressSanitizer, PthreadExitTest) { + pthread_t t; + for (int i = 0; i < 1000; i++) { + pthread_create(&t, 0, PthreadExit, 0); + pthread_join(t, 0); + } +} + #ifdef __EXCEPTIONS -__attribute__((noinline)) -static void StackReuseAndException() { +NOINLINE static void StackReuseAndException() { int large_stack[1000]; Ident(large_stack); ASAN_THROW(1); @@ -1784,6 +1771,28 @@ TEST(AddressSanitizer, MlockTest) { EXPECT_EQ(0, munlock((void*)0x987, 0x654)); } +struct LargeStruct { + int foo[100]; +}; + +// Test for bug http://llvm.org/bugs/show_bug.cgi?id=11763. +// Struct copy should not cause asan warning even if lhs == rhs. +TEST(AddressSanitizer, LargeStructCopyTest) { + LargeStruct a; + *Ident(&a) = *Ident(&a); +} + +__attribute__((no_address_safety_analysis)) +static void NoAddressSafety() { + char *foo = new char[10]; + Ident(foo)[10] = 0; + delete [] foo; +} + +TEST(AddressSanitizer, AttributeNoAddressSafetyTest) { + Ident(NoAddressSafety)(); +} + // ------------------ demo tests; run each one-by-one ------------- // e.g. --gtest_filter=*DemoOOBLeftHigh --gtest_also_run_disabled_tests TEST(AddressSanitizer, DISABLED_DemoThreadedTest) { @@ -1873,23 +1882,75 @@ TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) { } } +// http://code.google.com/p/address-sanitizer/issues/detail?id=66 +TEST(AddressSanitizer, BufferOverflowAfterManyFrees) { + for (int i = 0; i < 1000000; i++) { + delete [] (Ident(new char [8644])); + } + char *x = new char[8192]; + EXPECT_DEATH(x[Ident(8192)] = 0, "AddressSanitizer heap-buffer-overflow"); + delete [] Ident(x); +} + #ifdef __APPLE__ #include "asan_mac_test.h" -// TODO(glider): figure out whether we still need these tests. Is it correct -// to intercept CFAllocator? -TEST(AddressSanitizerMac, DISABLED_CFAllocatorDefaultDoubleFree) { +TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) { EXPECT_DEATH( - CFAllocatorDefaultDoubleFree(), + CFAllocatorDefaultDoubleFree(NULL), "attempting double-free"); } +void CFAllocator_DoubleFreeOnPthread() { + pthread_t child; + pthread_create(&child, NULL, CFAllocatorDefaultDoubleFree, NULL); + pthread_join(child, NULL); // Shouldn't be reached. +} + +TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) { + EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free"); +} + +namespace { + +void *GLOB; + +void *CFAllocatorAllocateToGlob(void *unused) { + GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0); + return NULL; +} + +void *CFAllocatorDeallocateFromGlob(void *unused) { + char *p = (char*)GLOB; + p[100] = 'A'; // ASan should report an error here. + CFAllocatorDeallocate(NULL, GLOB); + return NULL; +} + +void CFAllocator_PassMemoryToAnotherThread() { + pthread_t th1, th2; + pthread_create(&th1, NULL, CFAllocatorAllocateToGlob, NULL); + pthread_join(th1, NULL); + pthread_create(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL); + pthread_join(th2, NULL); +} + +TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) { + EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(), + "heap-buffer-overflow"); +} + +} // namespace + +// TODO(glider): figure out whether we still need these tests. Is it correct +// to intercept the non-default CFAllocators? TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) { EXPECT_DEATH( CFAllocatorSystemDefaultDoubleFree(), "attempting double-free"); } -TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocDoubleFree) { +// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan. +TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) { EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free"); } @@ -2012,8 +2073,37 @@ TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) { pthread_join(th, NULL); pthread_key_delete(test_key); } + +// Test that CFStringCreateCopy does not copy constant strings. +TEST(AddressSanitizerMac, CFStringCreateCopy) { + CFStringRef str = CFSTR("Hello world!\n"); + CFStringRef str2 = CFStringCreateCopy(0, str); + EXPECT_EQ(str, str2); +} + +TEST(AddressSanitizerMac, NSObjectOOB) { + // Make sure that our allocators are used for NSObjects. + EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow"); +} + +// Make sure that correct pointer is passed to free() when deallocating a +// NSURL object. +// See http://code.google.com/p/address-sanitizer/issues/detail?id=70. +TEST(AddressSanitizerMac, NSURLDeallocation) { + TestNSURLDeallocation(); +} #endif // __APPLE__ +// Test that instrumentation of stack allocations takes into account +// AllocSize of a type, and not its StoreSize (16 vs 10 bytes for long double). +// See http://llvm.org/bugs/show_bug.cgi?id=12047 for more details. +TEST(AddressSanitizer, LongDoubleNegativeTest) { + long double a, b; + static long double c; + memcpy(Ident(&a), Ident(&b), sizeof(long double)); + memcpy(Ident(&c), Ident(&b), sizeof(long double)); +}; + int main(int argc, char **argv) { progname = argv[0]; testing::GTEST_FLAG(death_test_style) = "threadsafe"; diff --git a/lib/asan/tests/asan_test_config.h b/lib/asan/tests/asan_test_config.h index de4ae95bd244..6cf0e6958484 100644 --- a/lib/asan/tests/asan_test_config.h +++ b/lib/asan/tests/asan_test_config.h @@ -39,6 +39,10 @@ using std::map; # error "please define ASAN_NEEDS_SEGV" #endif +#ifndef ASAN_LOW_MEMORY +#define ASAN_LOW_MEMORY 0 +#endif + #define ASAN_PCRE_DOTALL "" #endif // ASAN_TEST_CONFIG_H diff --git a/lib/asan/tests/asan_test_utils.h b/lib/asan/tests/asan_test_utils.h index a4809812dcf0..fb509cc43e30 100644 --- a/lib/asan/tests/asan_test_utils.h +++ b/lib/asan/tests/asan_test_utils.h @@ -14,13 +14,39 @@ #ifndef ASAN_TEST_UTILS_H #define ASAN_TEST_UTILS_H +#if defined(_WIN32) +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +# define NOINLINE __declspec(noinline) +#else // defined(_WIN32) +# define NOINLINE __attribute__((noinline)) +#endif // defined(_WIN32) + +#if !defined(__has_feature) +#define __has_feature(x) 0 +#endif + +#ifndef __WORDSIZE +#if __LP64__ || defined(_WIN64) +#define __WORDSIZE 64 +#else +#define __WORDSIZE 32 +#endif +#endif + // Make the compiler think that something is going on there. extern "C" void break_optimization(void *); // This function returns its parameter but in such a way that compiler // can not prove it. template -__attribute__((noinline)) +NOINLINE static T Ident(T t) { T ret = t; break_optimization(&ret); diff --git a/lib/asan/tests/dlclose-test.tmpl b/lib/asan/tests/dlclose-test.tmpl deleted file mode 100644 index 7ef22e9a431a..000000000000 --- a/lib/asan/tests/dlclose-test.tmpl +++ /dev/null @@ -1 +0,0 @@ -PASS diff --git a/lib/asan/tests/global-overflow.tmpl b/lib/asan/tests/global-overflow.tmpl deleted file mode 100644 index c5d54428143b..000000000000 --- a/lib/asan/tests/global-overflow.tmpl +++ /dev/null @@ -1,3 +0,0 @@ -READ of size 1 at 0x.* thread T0 - #0 0x.* in main .*global-overflow.cc:9 -0x.* is located 0 bytes to the right of global variable .*YYY.* of size 10 diff --git a/lib/asan/tests/heap-overflow.cc b/lib/asan/tests/heap-overflow.cc deleted file mode 100644 index 475d1637385a..000000000000 --- a/lib/asan/tests/heap-overflow.cc +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include -int main(int argc, char **argv) { - char *x = (char*)malloc(10 * sizeof(char)); - memset(x, 0, 10); - int res = x[argc * 10]; // BOOOM - free(x); - return res; -} diff --git a/lib/asan/tests/heap-overflow.tmpl b/lib/asan/tests/heap-overflow.tmpl deleted file mode 100644 index e2ab65f17d82..000000000000 --- a/lib/asan/tests/heap-overflow.tmpl +++ /dev/null @@ -1,6 +0,0 @@ -READ of size 1 at 0x.* thread T0 - #0 0x.* in main .*heap-overflow.cc:6 -0x.* is located 0 bytes to the right of 10-byte region -allocated by thread T0 here: - #0 0x.* in malloc - #1 0x.* in main .*heap-overflow.cc:[45] diff --git a/lib/asan/tests/heap-overflow.tmpl.Darwin b/lib/asan/tests/heap-overflow.tmpl.Darwin deleted file mode 100644 index e4611d067abe..000000000000 --- a/lib/asan/tests/heap-overflow.tmpl.Darwin +++ /dev/null @@ -1,8 +0,0 @@ -READ of size 1 at 0x.* thread T0 - #0 0x.* in main .*heap-overflow.cc:6 -0x.* is located 0 bytes to the right of 10-byte region -allocated by thread T0 here: - #0 0x.* in .*mz_malloc.* _asan_rtl_ - #1 0x.* in malloc_zone_malloc.* - #2 0x.* in malloc.* - #3 0x.* in main heap-overflow.cc:4 diff --git a/lib/asan/tests/large_func_test.cc b/lib/asan/tests/large_func_test.cc deleted file mode 100644 index 70bc36f40b87..000000000000 --- a/lib/asan/tests/large_func_test.cc +++ /dev/null @@ -1,33 +0,0 @@ -#include -__attribute__((noinline)) -static void LargeFunction(int *x, int zero) { - x[0]++; - x[1]++; - x[2]++; - x[3]++; - x[4]++; - x[5]++; - x[6]++; - x[7]++; - x[8]++; - x[9]++; - - x[zero + 111]++; // we should report this exact line - - x[10]++; - x[11]++; - x[12]++; - x[13]++; - x[14]++; - x[15]++; - x[16]++; - x[17]++; - x[18]++; - x[19]++; -} - -int main(int argc, char **argv) { - int *x = new int[100]; - LargeFunction(x, argc - 1); - delete x; -} diff --git a/lib/asan/tests/large_func_test.tmpl b/lib/asan/tests/large_func_test.tmpl deleted file mode 100644 index 45a13d0bc5bd..000000000000 --- a/lib/asan/tests/large_func_test.tmpl +++ /dev/null @@ -1,8 +0,0 @@ -.*ERROR: AddressSanitizer heap-buffer-overflow on address 0x.* at pc 0x.* bp 0x.* sp 0x.* -READ of size 4 at 0x.* thread T0 - #0 0x.* in LargeFunction .*large_func_test.cc:15 - #1 0x.* in main .*large_func_test.cc:3[012] -0x.* is located 44 bytes to the right of 400-byte region -allocated by thread T0 here: - #0 0x.* in operator new.* - #1 0x.* in main .*large_func_test.cc:30 diff --git a/lib/asan/tests/match_output.py b/lib/asan/tests/match_output.py deleted file mode 100755 index 31095f3f62f2..000000000000 --- a/lib/asan/tests/match_output.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/python - -import re -import sys - -def matchFile(f, f_re): - for line_re in f_re: - line_re = line_re.rstrip() - if not line_re: - continue - if line_re[0] == '#': - continue - match = False - for line in f: - line = line.rstrip() - # print line - if re.search(line_re, line): - match = True - #print 'match: %s =~ %s' % (line, line_re) - break - if not match: - print 'no match for: %s' % (line_re) - return False - return True - -if len(sys.argv) != 2: - print >>sys.stderr, 'Usage: %s