From: Martin Liska Date: Wed, 14 Aug 2019 08:47:11 +0000 (+0200) Subject: Libsanitizer merge from trunk r368656. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=b667dd7017a8f9d36d3ab266f22290d75fa527b0;p=gcc.git Libsanitizer merge from trunk r368656. 2019-08-14 Martin Liska PR sanitizer/89832 PR sanitizer/91325 * All source files: Merge from upstream 368656. From-SVN: r274426 --- diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog index 52c119ff690..1049000c4a3 100644 --- a/libsanitizer/ChangeLog +++ b/libsanitizer/ChangeLog @@ -1,3 +1,9 @@ +2019-08-14 Martin Liska + + PR sanitizer/89832 + PR sanitizer/91325 + * All source files: Merge from upstream 368656. + 2019-06-26 Rainer Orth * sanitizer_common/sanitizer_posix_libcdep.cc: Cherry-pick diff --git a/libsanitizer/MERGE b/libsanitizer/MERGE index 8f02e230649..bb1b045f488 100644 --- a/libsanitizer/MERGE +++ b/libsanitizer/MERGE @@ -1,4 +1,4 @@ -345033 +368656 The first line of this file holds the svn revision number of the last merge done from the master library sources. diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in index 0d789b3a59d..8f59d804790 100644 --- a/libsanitizer/Makefile.in +++ b/libsanitizer/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15.1 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2017 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -435,8 +435,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(top_srcdir)/../multilib.am $(am__empty): diff --git a/libsanitizer/aclocal.m4 b/libsanitizer/aclocal.m4 index dba827747d7..6c7c3d5f941 100644 --- a/libsanitizer/aclocal.m4 +++ b/libsanitizer/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.15.1 -*- Autoconf -*- +# generated automatically by aclocal 1.16.1 -*- Autoconf -*- -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -20,7 +20,7 @@ You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) -# Copyright (C) 2002-2017 Free Software Foundation, Inc. +# Copyright (C) 2002-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -32,10 +32,10 @@ To do so, use the procedure documented by the package, typically 'autoreconf'.]) # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], -[am__api_version='1.15' +[am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.15.1], [], +m4_if([$1], [1.16.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -51,14 +51,14 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.15.1])dnl +[AM_AUTOMAKE_VERSION([1.16.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # Figure out how to run the assembler. -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -78,7 +78,7 @@ _AM_IF_OPTION([no-dependencies],, [_AM_DEPENDENCIES([CCAS])])dnl # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -130,7 +130,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd` # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -161,7 +161,7 @@ AC_CONFIG_COMMANDS_PRE( Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -352,13 +352,12 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. - # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], @@ -366,49 +365,41 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + AS_CASE([$CONFIG_FILES], + [*\'*], [eval set x "$CONFIG_FILES"], + [*], [set x $CONFIG_FILES]) shift - for mf + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf do # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line + am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`AS_DIRNAME("$mf")` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`AS_DIRNAME(["$file"])` - AS_MKDIR_P([$dirpart/$fdir]) - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`AS_DIRNAME(["$am_mf"])` + am_filepart=`AS_BASENAME(["$am_mf"])` + AM_RUN_LOG([cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles]) || am_rc=$? done + if test $am_rc -ne 0; then + AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. Try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking).]) + fi + AS_UNSET([am_dirpart]) + AS_UNSET([am_filepart]) + AS_UNSET([am_mf]) + AS_UNSET([am_rc]) + rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS @@ -417,18 +408,17 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # -# This code is only required when automatic dependency tracking -# is enabled. FIXME. This creates each '.P' file that we will -# need in order to bootstrap the dependency handling code. +# This code is only required when automatic dependency tracking is enabled. +# This creates each '.Po' and '.Plo' makefile fragment that we'll need in +# order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], - [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) -]) + [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -515,8 +505,8 @@ AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: -# -# +# +# AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. @@ -583,7 +573,7 @@ END Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . +that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM @@ -625,7 +615,7 @@ for _am_header in $config_headers :; do done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -649,7 +639,7 @@ AC_SUBST([install_sh])]) # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -684,7 +674,7 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -692,49 +682,42 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) # AM_MAKE_INCLUDE() # ----------------- -# Check to see how make treats includes. +# Check whether make has an 'include' directive that can support all +# the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], -[am_make=${MAKE-make} -cat > confinc << 'END' +[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) +cat > confinc.mk << 'END' am__doit: - @echo this is the am__doit target + @echo this is the am__doit target >confinc.out .PHONY: am__doit END -# If we don't find an include directive, just comment out the code. -AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD - ;; - esac -fi -AC_SUBST([am__include]) -AC_SUBST([am__quote]) -AC_MSG_RESULT([$_am_result]) -rm -f confinc confmf -]) +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) + AS_CASE([$?:`cat confinc.out 2>/dev/null`], + ['0:this is the am__doit target'], + [AS_CASE([$s], + [BSD], [am__include='.include' am__quote='"'], + [am__include='include' am__quote=''])]) + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +AC_MSG_RESULT([${_am_result}]) +AC_SUBST([am__include])]) +AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -773,7 +756,7 @@ fi # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -802,7 +785,7 @@ AC_DEFUN([_AM_SET_OPTIONS], AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -849,7 +832,7 @@ AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -868,7 +851,7 @@ AC_DEFUN([AM_RUN_LOG], # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -949,7 +932,7 @@ AC_CONFIG_COMMANDS_PRE( rm -f conftest.file ]) -# Copyright (C) 2009-2017 Free Software Foundation, Inc. +# Copyright (C) 2009-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1009,7 +992,7 @@ AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1037,7 +1020,7 @@ fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2017 Free Software Foundation, Inc. +# Copyright (C) 2006-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1056,7 +1039,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2017 Free Software Foundation, Inc. +# Copyright (C) 2004-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff --git a/libsanitizer/asan/Makefile.am b/libsanitizer/asan/Makefile.am index b18ab2a9211..7bba555b171 100644 --- a/libsanitizer/asan/Makefile.am +++ b/libsanitizer/asan/Makefile.am @@ -17,37 +17,38 @@ toolexeclib_LTLIBRARIES = libasan.la nodist_toolexeclib_HEADERS = libasan_preinit.o asan_files = \ - asan_activation.cc \ - asan_allocator.cc \ - asan_debugging.cc \ - asan_descriptions.cc \ - asan_errors.cc \ - asan_fake_stack.cc \ - asan_flags.cc \ - asan_globals.cc \ - asan_interceptors.cc \ - asan_interceptors_memintrinsics.cc \ - asan_linux.cc \ - asan_mac.cc \ - asan_malloc_linux.cc \ - asan_malloc_mac.cc \ - asan_malloc_win.cc \ - asan_memory_profile.cc \ - asan_new_delete.cc \ - asan_poisoning.cc \ - asan_posix.cc \ - asan_premap_shadow.cc \ - asan_report.cc \ - asan_rtems.cc \ - asan_rtl.cc \ - asan_shadow_setup.cc \ - asan_stack.cc \ - asan_stats.cc \ - asan_suppressions.cc \ - asan_thread.cc \ - asan_win.cc \ - asan_win_dll_thunk.cc \ - asan_win_dynamic_runtime_thunk.cc + asan_activation.cpp \ + asan_allocator.cpp \ + asan_debugging.cpp \ + asan_descriptions.cpp \ + asan_errors.cpp \ + asan_fake_stack.cpp \ + asan_flags.cpp \ + asan_globals.cpp \ + asan_interceptors.cpp \ + asan_interceptors_memintrinsics.cpp \ + asan_linux.cpp \ + asan_mac.cpp \ + asan_malloc_linux.cpp \ + asan_malloc_mac.cpp \ + asan_malloc_win.cpp \ + asan_memory_profile.cpp \ + asan_new_delete.cpp \ + asan_poisoning.cpp \ + asan_posix.cpp \ + asan_premap_shadow.cpp \ + asan_report.cpp \ + asan_rtems.cpp \ + asan_rtl.cpp \ + asan_shadow_setup.cpp \ + asan_stack.cpp \ + asan_stats.cpp \ + asan_suppressions.cpp \ + asan_thread.cpp \ + asan_win.cpp \ + asan_win_dll_thunk.cpp \ + asan_win_dynamic_runtime_thunk.cpp \ + asan_interceptors_vfork.S libasan_la_SOURCES = $(asan_files) libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in index 41bace42383..40041575394 100644 --- a/libsanitizer/asan/Makefile.in +++ b/libsanitizer/asan/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15.1 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2017 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -158,7 +158,8 @@ am__objects_1 = asan_activation.lo asan_allocator.lo asan_debugging.lo \ asan_posix.lo asan_premap_shadow.lo asan_report.lo \ asan_rtems.lo asan_rtl.lo asan_shadow_setup.lo asan_stack.lo \ asan_stats.lo asan_suppressions.lo asan_thread.lo asan_win.lo \ - asan_win_dll_thunk.lo asan_win_dynamic_runtime_thunk.lo + asan_win_dll_thunk.lo asan_win_dynamic_runtime_thunk.lo \ + asan_interceptors_vfork.lo am_libasan_la_OBJECTS = $(am__objects_1) libasan_la_OBJECTS = $(am_libasan_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) @@ -182,8 +183,38 @@ am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/../depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/asan_activation.Plo \ + ./$(DEPDIR)/asan_allocator.Plo ./$(DEPDIR)/asan_debugging.Plo \ + ./$(DEPDIR)/asan_descriptions.Plo ./$(DEPDIR)/asan_errors.Plo \ + ./$(DEPDIR)/asan_fake_stack.Plo ./$(DEPDIR)/asan_flags.Plo \ + ./$(DEPDIR)/asan_globals.Plo ./$(DEPDIR)/asan_interceptors.Plo \ + ./$(DEPDIR)/asan_interceptors_memintrinsics.Plo \ + ./$(DEPDIR)/asan_interceptors_vfork.Plo \ + ./$(DEPDIR)/asan_linux.Plo ./$(DEPDIR)/asan_mac.Plo \ + ./$(DEPDIR)/asan_malloc_linux.Plo \ + ./$(DEPDIR)/asan_malloc_mac.Plo \ + ./$(DEPDIR)/asan_malloc_win.Plo \ + ./$(DEPDIR)/asan_memory_profile.Plo \ + ./$(DEPDIR)/asan_new_delete.Plo ./$(DEPDIR)/asan_poisoning.Plo \ + ./$(DEPDIR)/asan_posix.Plo ./$(DEPDIR)/asan_premap_shadow.Plo \ + ./$(DEPDIR)/asan_report.Plo ./$(DEPDIR)/asan_rtems.Plo \ + ./$(DEPDIR)/asan_rtl.Plo ./$(DEPDIR)/asan_shadow_setup.Plo \ + ./$(DEPDIR)/asan_stack.Plo ./$(DEPDIR)/asan_stats.Plo \ + ./$(DEPDIR)/asan_suppressions.Plo ./$(DEPDIR)/asan_thread.Plo \ + ./$(DEPDIR)/asan_win.Plo ./$(DEPDIR)/asan_win_dll_thunk.Plo \ + ./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo am__mv = mv -f +CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS) +LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CCASFLAGS) $(CCASFLAGS) +AM_V_CPPAS = $(am__v_CPPAS_@AM_V@) +am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@) +am__v_CPPAS_0 = @echo " CPPAS " $@; +am__v_CPPAS_1 = CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ @@ -202,6 +233,24 @@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = SOURCES = $(libasan_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ @@ -256,8 +305,8 @@ CYGPATH_W = @CYGPATH_W@ DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS \ -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS \ -DASAN_HAS_EXCEPTIONS=1 -DASAN_NEEDS_SEGV=1 \ - -DCAN_SANITIZE_UB=0 \ - -DASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION=0 $(am__append_1) + -DCAN_SANITIZE_UB=0 -DASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION=0 \ + $(am__append_1) DEPDIR = @DEPDIR@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ @@ -394,37 +443,38 @@ ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config toolexeclib_LTLIBRARIES = libasan.la nodist_toolexeclib_HEADERS = libasan_preinit.o asan_files = \ - asan_activation.cc \ - asan_allocator.cc \ - asan_debugging.cc \ - asan_descriptions.cc \ - asan_errors.cc \ - asan_fake_stack.cc \ - asan_flags.cc \ - asan_globals.cc \ - asan_interceptors.cc \ - asan_interceptors_memintrinsics.cc \ - asan_linux.cc \ - asan_mac.cc \ - asan_malloc_linux.cc \ - asan_malloc_mac.cc \ - asan_malloc_win.cc \ - asan_memory_profile.cc \ - asan_new_delete.cc \ - asan_poisoning.cc \ - asan_posix.cc \ - asan_premap_shadow.cc \ - asan_report.cc \ - asan_rtems.cc \ - asan_rtl.cc \ - asan_shadow_setup.cc \ - asan_stack.cc \ - asan_stats.cc \ - asan_suppressions.cc \ - asan_thread.cc \ - asan_win.cc \ - asan_win_dll_thunk.cc \ - asan_win_dynamic_runtime_thunk.cc + asan_activation.cpp \ + asan_allocator.cpp \ + asan_debugging.cpp \ + asan_descriptions.cpp \ + asan_errors.cpp \ + asan_fake_stack.cpp \ + asan_flags.cpp \ + asan_globals.cpp \ + asan_interceptors.cpp \ + asan_interceptors_memintrinsics.cpp \ + asan_linux.cpp \ + asan_mac.cpp \ + asan_malloc_linux.cpp \ + asan_malloc_mac.cpp \ + asan_malloc_win.cpp \ + asan_memory_profile.cpp \ + asan_new_delete.cpp \ + asan_poisoning.cpp \ + asan_posix.cpp \ + asan_premap_shadow.cpp \ + asan_report.cpp \ + asan_rtems.cpp \ + asan_rtl.cpp \ + asan_shadow_setup.cpp \ + asan_stack.cpp \ + asan_stats.cpp \ + asan_suppressions.cpp \ + asan_thread.cpp \ + asan_win.cpp \ + asan_win_dll_thunk.cpp \ + asan_win_dynamic_runtime_thunk.cpp \ + asan_interceptors_vfork.S libasan_la_SOURCES = $(asan_files) libasan_la_LIBADD = \ @@ -475,7 +525,7 @@ MAKEOVERRIDES = all: all-am .SUFFIXES: -.SUFFIXES: .cc .lo .o .obj +.SUFFIXES: .S .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -493,8 +543,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -550,53 +600,81 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_debugging.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_descriptions.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_errors.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_flags.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors_memintrinsics.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_linux.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_mac.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_linux.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_mac.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_win.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_memory_profile.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_new_delete.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_poisoning.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_posix.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_premap_shadow.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_report.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtems.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtl.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_shadow_setup.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stack.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_suppressions.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dll_thunk.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo@am__quote@ - -.cc.o: +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_debugging.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_descriptions.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_errors.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_flags.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors_memintrinsics.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors_vfork.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_mac.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_mac.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_win.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_memory_profile.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_new_delete.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_poisoning.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_posix.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_premap_shadow.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_report.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtems.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_shadow_setup.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stack.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_suppressions.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dll_thunk.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.S.o: +@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $< + +.S.obj: +@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.S.lo: +@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $< + +.cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< -.cc.obj: +.cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` -.cc.lo: +.cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @@ -724,7 +802,38 @@ clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \ mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/asan_activation.Plo + -rm -f ./$(DEPDIR)/asan_allocator.Plo + -rm -f ./$(DEPDIR)/asan_debugging.Plo + -rm -f ./$(DEPDIR)/asan_descriptions.Plo + -rm -f ./$(DEPDIR)/asan_errors.Plo + -rm -f ./$(DEPDIR)/asan_fake_stack.Plo + -rm -f ./$(DEPDIR)/asan_flags.Plo + -rm -f ./$(DEPDIR)/asan_globals.Plo + -rm -f ./$(DEPDIR)/asan_interceptors.Plo + -rm -f ./$(DEPDIR)/asan_interceptors_memintrinsics.Plo + -rm -f ./$(DEPDIR)/asan_interceptors_vfork.Plo + -rm -f ./$(DEPDIR)/asan_linux.Plo + -rm -f ./$(DEPDIR)/asan_mac.Plo + -rm -f ./$(DEPDIR)/asan_malloc_linux.Plo + -rm -f ./$(DEPDIR)/asan_malloc_mac.Plo + -rm -f ./$(DEPDIR)/asan_malloc_win.Plo + -rm -f ./$(DEPDIR)/asan_memory_profile.Plo + -rm -f ./$(DEPDIR)/asan_new_delete.Plo + -rm -f ./$(DEPDIR)/asan_poisoning.Plo + -rm -f ./$(DEPDIR)/asan_posix.Plo + -rm -f ./$(DEPDIR)/asan_premap_shadow.Plo + -rm -f ./$(DEPDIR)/asan_report.Plo + -rm -f ./$(DEPDIR)/asan_rtems.Plo + -rm -f ./$(DEPDIR)/asan_rtl.Plo + -rm -f ./$(DEPDIR)/asan_shadow_setup.Plo + -rm -f ./$(DEPDIR)/asan_stack.Plo + -rm -f ./$(DEPDIR)/asan_stats.Plo + -rm -f ./$(DEPDIR)/asan_suppressions.Plo + -rm -f ./$(DEPDIR)/asan_thread.Plo + -rm -f ./$(DEPDIR)/asan_win.Plo + -rm -f ./$(DEPDIR)/asan_win_dll_thunk.Plo + -rm -f ./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -771,7 +880,38 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/asan_activation.Plo + -rm -f ./$(DEPDIR)/asan_allocator.Plo + -rm -f ./$(DEPDIR)/asan_debugging.Plo + -rm -f ./$(DEPDIR)/asan_descriptions.Plo + -rm -f ./$(DEPDIR)/asan_errors.Plo + -rm -f ./$(DEPDIR)/asan_fake_stack.Plo + -rm -f ./$(DEPDIR)/asan_flags.Plo + -rm -f ./$(DEPDIR)/asan_globals.Plo + -rm -f ./$(DEPDIR)/asan_interceptors.Plo + -rm -f ./$(DEPDIR)/asan_interceptors_memintrinsics.Plo + -rm -f ./$(DEPDIR)/asan_interceptors_vfork.Plo + -rm -f ./$(DEPDIR)/asan_linux.Plo + -rm -f ./$(DEPDIR)/asan_mac.Plo + -rm -f ./$(DEPDIR)/asan_malloc_linux.Plo + -rm -f ./$(DEPDIR)/asan_malloc_mac.Plo + -rm -f ./$(DEPDIR)/asan_malloc_win.Plo + -rm -f ./$(DEPDIR)/asan_memory_profile.Plo + -rm -f ./$(DEPDIR)/asan_new_delete.Plo + -rm -f ./$(DEPDIR)/asan_poisoning.Plo + -rm -f ./$(DEPDIR)/asan_posix.Plo + -rm -f ./$(DEPDIR)/asan_premap_shadow.Plo + -rm -f ./$(DEPDIR)/asan_report.Plo + -rm -f ./$(DEPDIR)/asan_rtems.Plo + -rm -f ./$(DEPDIR)/asan_rtl.Plo + -rm -f ./$(DEPDIR)/asan_shadow_setup.Plo + -rm -f ./$(DEPDIR)/asan_stack.Plo + -rm -f ./$(DEPDIR)/asan_stats.Plo + -rm -f ./$(DEPDIR)/asan_suppressions.Plo + -rm -f ./$(DEPDIR)/asan_thread.Plo + -rm -f ./$(DEPDIR)/asan_win.Plo + -rm -f ./$(DEPDIR)/asan_win_dll_thunk.Plo + -rm -f ./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -793,20 +933,21 @@ uninstall-am: uninstall-nodist_toolexeclibHEADERS \ .MAKE: install-am install-strip -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ - clean-libtool clean-toolexeclibLTLIBRARIES cscopelist-am ctags \ - ctags-am distclean distclean-compile distclean-generic \ - distclean-libtool distclean-tags dvi dvi-am html html-am info \ - info-am install install-am install-data install-data-am \ - install-dvi install-dvi-am install-exec install-exec-am \ - install-html install-html-am install-info install-info-am \ - install-man install-nodist_toolexeclibHEADERS install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - install-toolexeclibLTLIBRARIES installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ - uninstall-am uninstall-nodist_toolexeclibHEADERS \ +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags dvi dvi-am \ + html html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-nodist_toolexeclibHEADERS \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip install-toolexeclibLTLIBRARIES installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am \ + uninstall-nodist_toolexeclibHEADERS \ uninstall-toolexeclibLTLIBRARIES .PRECIOUS: Makefile diff --git a/libsanitizer/asan/asan_activation.cc b/libsanitizer/asan/asan_activation.cc deleted file mode 100644 index 6f69f700c06..00000000000 --- a/libsanitizer/asan/asan_activation.cc +++ /dev/null @@ -1,142 +0,0 @@ -//===-- asan_activation.cc --------------------------------------*- C++ -*-===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// ASan activation/deactivation logic. -//===----------------------------------------------------------------------===// - -#include "asan_activation.h" -#include "asan_allocator.h" -#include "asan_flags.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "asan_poisoning.h" -#include "asan_stack.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_flags.h" - -namespace __asan { - -static struct AsanDeactivatedFlags { - AllocatorOptions allocator_options; - int malloc_context_size; - bool poison_heap; - bool coverage; - const char *coverage_dir; - - void RegisterActivationFlags(FlagParser *parser, Flags *f, CommonFlags *cf) { -#define ASAN_ACTIVATION_FLAG(Type, Name) \ - RegisterFlag(parser, #Name, "", &f->Name); -#define COMMON_ACTIVATION_FLAG(Type, Name) \ - RegisterFlag(parser, #Name, "", &cf->Name); -#include "asan_activation_flags.inc" -#undef ASAN_ACTIVATION_FLAG -#undef COMMON_ACTIVATION_FLAG - - RegisterIncludeFlags(parser, cf); - } - - void OverrideFromActivationFlags() { - Flags f; - CommonFlags cf; - FlagParser parser; - RegisterActivationFlags(&parser, &f, &cf); - - cf.SetDefaults(); - // Copy the current activation flags. - allocator_options.CopyTo(&f, &cf); - cf.malloc_context_size = malloc_context_size; - f.poison_heap = poison_heap; - cf.coverage = coverage; - cf.coverage_dir = coverage_dir; - cf.verbosity = Verbosity(); - cf.help = false; // this is activation-specific help - - // Check if activation flags need to be overriden. - if (const char *env = GetEnv("ASAN_ACTIVATION_OPTIONS")) { - parser.ParseString(env); - } - - InitializeCommonFlags(&cf); - - if (Verbosity()) ReportUnrecognizedFlags(); - - if (cf.help) parser.PrintFlagDescriptions(); - - allocator_options.SetFrom(&f, &cf); - malloc_context_size = cf.malloc_context_size; - poison_heap = f.poison_heap; - coverage = cf.coverage; - coverage_dir = cf.coverage_dir; - } - - void Print() { - Report( - "quarantine_size_mb %d, thread_local_quarantine_size_kb %d, " - "max_redzone %d, poison_heap %d, malloc_context_size %d, " - "alloc_dealloc_mismatch %d, allocator_may_return_null %d, coverage %d, " - "coverage_dir %s, allocator_release_to_os_interval_ms %d\n", - allocator_options.quarantine_size_mb, - allocator_options.thread_local_quarantine_size_kb, - allocator_options.max_redzone, poison_heap, malloc_context_size, - allocator_options.alloc_dealloc_mismatch, - allocator_options.may_return_null, coverage, coverage_dir, - allocator_options.release_to_os_interval_ms); - } -} asan_deactivated_flags; - -static bool asan_is_deactivated; - -void AsanDeactivate() { - CHECK(!asan_is_deactivated); - VReport(1, "Deactivating ASan\n"); - - // Stash runtime state. - GetAllocatorOptions(&asan_deactivated_flags.allocator_options); - asan_deactivated_flags.malloc_context_size = GetMallocContextSize(); - asan_deactivated_flags.poison_heap = CanPoisonMemory(); - asan_deactivated_flags.coverage = common_flags()->coverage; - asan_deactivated_flags.coverage_dir = common_flags()->coverage_dir; - - // Deactivate the runtime. - SetCanPoisonMemory(false); - SetMallocContextSize(1); - - AllocatorOptions disabled = asan_deactivated_flags.allocator_options; - disabled.quarantine_size_mb = 0; - disabled.thread_local_quarantine_size_kb = 0; - // Redzone must be at least Max(16, granularity) bytes long. - disabled.min_redzone = Max(16, (int)SHADOW_GRANULARITY); - disabled.max_redzone = disabled.min_redzone; - disabled.alloc_dealloc_mismatch = false; - disabled.may_return_null = true; - ReInitializeAllocator(disabled); - - asan_is_deactivated = true; -} - -void AsanActivate() { - if (!asan_is_deactivated) return; - VReport(1, "Activating ASan\n"); - - UpdateProcessName(); - - asan_deactivated_flags.OverrideFromActivationFlags(); - - SetCanPoisonMemory(asan_deactivated_flags.poison_heap); - SetMallocContextSize(asan_deactivated_flags.malloc_context_size); - ReInitializeAllocator(asan_deactivated_flags.allocator_options); - - asan_is_deactivated = false; - if (Verbosity()) { - Report("Activated with flags:\n"); - asan_deactivated_flags.Print(); - } -} - -} // namespace __asan diff --git a/libsanitizer/asan/asan_activation.cpp b/libsanitizer/asan/asan_activation.cpp new file mode 100644 index 00000000000..795df95a541 --- /dev/null +++ b/libsanitizer/asan/asan_activation.cpp @@ -0,0 +1,143 @@ +//===-- asan_activation.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan activation/deactivation logic. +//===----------------------------------------------------------------------===// + +#include "asan_activation.h" +#include "asan_allocator.h" +#include "asan_flags.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" + +namespace __asan { + +static struct AsanDeactivatedFlags { + AllocatorOptions allocator_options; + int malloc_context_size; + bool poison_heap; + bool coverage; + const char *coverage_dir; + + void RegisterActivationFlags(FlagParser *parser, Flags *f, CommonFlags *cf) { +#define ASAN_ACTIVATION_FLAG(Type, Name) \ + RegisterFlag(parser, #Name, "", &f->Name); +#define COMMON_ACTIVATION_FLAG(Type, Name) \ + RegisterFlag(parser, #Name, "", &cf->Name); +#include "asan_activation_flags.inc" +#undef ASAN_ACTIVATION_FLAG +#undef COMMON_ACTIVATION_FLAG + + RegisterIncludeFlags(parser, cf); + } + + void OverrideFromActivationFlags() { + Flags f; + CommonFlags cf; + FlagParser parser; + RegisterActivationFlags(&parser, &f, &cf); + + cf.SetDefaults(); + // Copy the current activation flags. + allocator_options.CopyTo(&f, &cf); + cf.malloc_context_size = malloc_context_size; + f.poison_heap = poison_heap; + cf.coverage = coverage; + cf.coverage_dir = coverage_dir; + cf.verbosity = Verbosity(); + cf.help = false; // this is activation-specific help + + // Check if activation flags need to be overriden. + if (const char *env = GetEnv("ASAN_ACTIVATION_OPTIONS")) { + parser.ParseString(env); + } + + InitializeCommonFlags(&cf); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (cf.help) parser.PrintFlagDescriptions(); + + allocator_options.SetFrom(&f, &cf); + malloc_context_size = cf.malloc_context_size; + poison_heap = f.poison_heap; + coverage = cf.coverage; + coverage_dir = cf.coverage_dir; + } + + void Print() { + Report( + "quarantine_size_mb %d, thread_local_quarantine_size_kb %d, " + "max_redzone %d, poison_heap %d, malloc_context_size %d, " + "alloc_dealloc_mismatch %d, allocator_may_return_null %d, coverage %d, " + "coverage_dir %s, allocator_release_to_os_interval_ms %d\n", + allocator_options.quarantine_size_mb, + allocator_options.thread_local_quarantine_size_kb, + allocator_options.max_redzone, poison_heap, malloc_context_size, + allocator_options.alloc_dealloc_mismatch, + allocator_options.may_return_null, coverage, coverage_dir, + allocator_options.release_to_os_interval_ms); + } +} asan_deactivated_flags; + +static bool asan_is_deactivated; + +void AsanDeactivate() { + CHECK(!asan_is_deactivated); + VReport(1, "Deactivating ASan\n"); + + // Stash runtime state. + GetAllocatorOptions(&asan_deactivated_flags.allocator_options); + asan_deactivated_flags.malloc_context_size = GetMallocContextSize(); + asan_deactivated_flags.poison_heap = CanPoisonMemory(); + asan_deactivated_flags.coverage = common_flags()->coverage; + asan_deactivated_flags.coverage_dir = common_flags()->coverage_dir; + + // Deactivate the runtime. + SetCanPoisonMemory(false); + SetMallocContextSize(1); + + AllocatorOptions disabled = asan_deactivated_flags.allocator_options; + disabled.quarantine_size_mb = 0; + disabled.thread_local_quarantine_size_kb = 0; + // Redzone must be at least Max(16, granularity) bytes long. + disabled.min_redzone = Max(16, (int)SHADOW_GRANULARITY); + disabled.max_redzone = disabled.min_redzone; + disabled.alloc_dealloc_mismatch = false; + disabled.may_return_null = true; + ReInitializeAllocator(disabled); + + asan_is_deactivated = true; +} + +void AsanActivate() { + if (!asan_is_deactivated) return; + VReport(1, "Activating ASan\n"); + + UpdateProcessName(); + + asan_deactivated_flags.OverrideFromActivationFlags(); + + SetCanPoisonMemory(asan_deactivated_flags.poison_heap); + SetMallocContextSize(asan_deactivated_flags.malloc_context_size); + ReInitializeAllocator(asan_deactivated_flags.allocator_options); + + asan_is_deactivated = false; + if (Verbosity()) { + Report("Activated with flags:\n"); + asan_deactivated_flags.Print(); + } +} + +} // namespace __asan diff --git a/libsanitizer/asan/asan_activation.h b/libsanitizer/asan/asan_activation.h index 162a5ebcea9..93c290c2ae2 100644 --- a/libsanitizer/asan/asan_activation.h +++ b/libsanitizer/asan/asan_activation.h @@ -1,7 +1,8 @@ //===-- asan_activation.h ---------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/asan/asan_activation_flags.inc b/libsanitizer/asan/asan_activation_flags.inc index e71abb96e5b..e0fdffc82ac 100644 --- a/libsanitizer/asan/asan_activation_flags.inc +++ b/libsanitizer/asan/asan_activation_flags.inc @@ -1,7 +1,8 @@ //===-- asan_activation_flags.inc -------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc deleted file mode 100644 index c2b31a543e7..00000000000 --- a/libsanitizer/asan/asan_allocator.cc +++ /dev/null @@ -1,1107 +0,0 @@ -//===-- asan_allocator.cc -------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Implementation of ASan's memory allocator, 2-nd version. -// This variant uses the allocator from sanitizer_common, i.e. the one shared -// with ThreadSanitizer and MemorySanitizer. -// -//===----------------------------------------------------------------------===// - -#include "asan_allocator.h" -#include "asan_mapping.h" -#include "asan_poisoning.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_allocator_checks.h" -#include "sanitizer_common/sanitizer_allocator_interface.h" -#include "sanitizer_common/sanitizer_errno.h" -#include "sanitizer_common/sanitizer_flags.h" -#include "sanitizer_common/sanitizer_internal_defs.h" -#include "sanitizer_common/sanitizer_list.h" -#include "sanitizer_common/sanitizer_stackdepot.h" -#include "sanitizer_common/sanitizer_quarantine.h" -#include "lsan/lsan_common.h" - -namespace __asan { - -// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits. -// We use adaptive redzones: for larger allocation larger redzones are used. -static u32 RZLog2Size(u32 rz_log) { - CHECK_LT(rz_log, 8); - return 16 << rz_log; -} - -static u32 RZSize2Log(u32 rz_size) { - CHECK_GE(rz_size, 16); - CHECK_LE(rz_size, 2048); - CHECK(IsPowerOfTwo(rz_size)); - u32 res = Log2(rz_size) - 4; - CHECK_EQ(rz_size, RZLog2Size(res)); - return res; -} - -static AsanAllocator &get_allocator(); - -// The memory chunk allocated from the underlying allocator looks like this: -// L L L L L L H H U U U U U U R R -// L -- left redzone words (0 or more bytes) -// H -- ChunkHeader (16 bytes), which is also a part of the left redzone. -// U -- user memory. -// R -- right redzone (0 or more bytes) -// ChunkBase consists of ChunkHeader and other bytes that overlap with user -// memory. - -// If the left redzone is greater than the ChunkHeader size we store a magic -// value in the first uptr word of the memory block and store the address of -// ChunkBase in the next uptr. -// M B L L L L L L L L L H H U U U U U U -// | ^ -// ---------------------| -// M -- magic value kAllocBegMagic -// B -- address of ChunkHeader pointing to the first 'H' -static const uptr kAllocBegMagic = 0xCC6E96B9; - -struct ChunkHeader { - // 1-st 8 bytes. - u32 chunk_state : 8; // Must be first. - u32 alloc_tid : 24; - - u32 free_tid : 24; - u32 from_memalign : 1; - u32 alloc_type : 2; - u32 rz_log : 3; - u32 lsan_tag : 2; - // 2-nd 8 bytes - // This field is used for small sizes. For large sizes it is equal to - // SizeClassMap::kMaxSize and the actual size is stored in the - // SecondaryAllocator's metadata. - u32 user_requested_size : 29; - // align < 8 -> 0 - // else -> log2(min(align, 512)) - 2 - u32 user_requested_alignment_log : 3; - u32 alloc_context_id; -}; - -struct ChunkBase : ChunkHeader { - // Header2, intersects with user memory. - u32 free_context_id; -}; - -static const uptr kChunkHeaderSize = sizeof(ChunkHeader); -static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize; -COMPILER_CHECK(kChunkHeaderSize == 16); -COMPILER_CHECK(kChunkHeader2Size <= 16); - -// Every chunk of memory allocated by this allocator can be in one of 3 states: -// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. -// CHUNK_ALLOCATED: the chunk is allocated and not yet freed. -// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone. -enum { - CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it. - CHUNK_ALLOCATED = 2, - CHUNK_QUARANTINE = 3 -}; - -struct AsanChunk: ChunkBase { - uptr Beg() { return reinterpret_cast(this) + kChunkHeaderSize; } - uptr UsedSize(bool locked_version = false) { - if (user_requested_size != SizeClassMap::kMaxSize) - return user_requested_size; - return *reinterpret_cast( - get_allocator().GetMetaData(AllocBeg(locked_version))); - } - void *AllocBeg(bool locked_version = false) { - if (from_memalign) { - if (locked_version) - return get_allocator().GetBlockBeginFastLocked( - reinterpret_cast(this)); - return get_allocator().GetBlockBegin(reinterpret_cast(this)); - } - return reinterpret_cast(Beg() - RZLog2Size(rz_log)); - } - bool AddrIsInside(uptr addr, bool locked_version = false) { - return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); - } -}; - -struct QuarantineCallback { - QuarantineCallback(AllocatorCache *cache, BufferedStackTrace *stack) - : cache_(cache), - stack_(stack) { - } - - void Recycle(AsanChunk *m) { - CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); - atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed); - CHECK_NE(m->alloc_tid, kInvalidTid); - CHECK_NE(m->free_tid, kInvalidTid); - PoisonShadow(m->Beg(), - RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), - kAsanHeapLeftRedzoneMagic); - void *p = reinterpret_cast(m->AllocBeg()); - if (p != m) { - uptr *alloc_magic = reinterpret_cast(p); - CHECK_EQ(alloc_magic[0], kAllocBegMagic); - // Clear the magic value, as allocator internals may overwrite the - // contents of deallocated chunk, confusing GetAsanChunk lookup. - alloc_magic[0] = 0; - CHECK_EQ(alloc_magic[1], reinterpret_cast(m)); - } - - // Statistics. - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.real_frees++; - thread_stats.really_freed += m->UsedSize(); - - get_allocator().Deallocate(cache_, p); - } - - void *Allocate(uptr size) { - void *res = get_allocator().Allocate(cache_, size, 1); - // TODO(alekseys): Consider making quarantine OOM-friendly. - if (UNLIKELY(!res)) - ReportOutOfMemory(size, stack_); - return res; - } - - void Deallocate(void *p) { - get_allocator().Deallocate(cache_, p); - } - - private: - AllocatorCache* const cache_; - BufferedStackTrace* const stack_; -}; - -typedef Quarantine AsanQuarantine; -typedef AsanQuarantine::Cache QuarantineCache; - -void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const { - PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); - // Statistics. - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.mmaps++; - thread_stats.mmaped += size; -} -void AsanMapUnmapCallback::OnUnmap(uptr p, uptr size) const { - PoisonShadow(p, size, 0); - // We are about to unmap a chunk of user memory. - // Mark the corresponding shadow memory as not needed. - FlushUnneededASanShadowMemory(p, size); - // Statistics. - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.munmaps++; - thread_stats.munmaped += size; -} - -// We can not use THREADLOCAL because it is not supported on some of the -// platforms we care about (OSX 10.6, Android). -// static THREADLOCAL AllocatorCache cache; -AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) { - CHECK(ms); - return &ms->allocator_cache; -} - -QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) { - CHECK(ms); - CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache)); - return reinterpret_cast(ms->quarantine_cache); -} - -void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) { - quarantine_size_mb = f->quarantine_size_mb; - thread_local_quarantine_size_kb = f->thread_local_quarantine_size_kb; - min_redzone = f->redzone; - max_redzone = f->max_redzone; - may_return_null = cf->allocator_may_return_null; - alloc_dealloc_mismatch = f->alloc_dealloc_mismatch; - release_to_os_interval_ms = cf->allocator_release_to_os_interval_ms; -} - -void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) { - f->quarantine_size_mb = quarantine_size_mb; - f->thread_local_quarantine_size_kb = thread_local_quarantine_size_kb; - f->redzone = min_redzone; - f->max_redzone = max_redzone; - cf->allocator_may_return_null = may_return_null; - f->alloc_dealloc_mismatch = alloc_dealloc_mismatch; - cf->allocator_release_to_os_interval_ms = release_to_os_interval_ms; -} - -struct Allocator { - static const uptr kMaxAllowedMallocSize = - FIRST_32_SECOND_64(3UL << 30, 1ULL << 40); - - AsanAllocator allocator; - AsanQuarantine quarantine; - StaticSpinMutex fallback_mutex; - AllocatorCache fallback_allocator_cache; - QuarantineCache fallback_quarantine_cache; - - atomic_uint8_t rss_limit_exceeded; - - // ------------------- Options -------------------------- - atomic_uint16_t min_redzone; - atomic_uint16_t max_redzone; - atomic_uint8_t alloc_dealloc_mismatch; - - // ------------------- Initialization ------------------------ - explicit Allocator(LinkerInitialized) - : quarantine(LINKER_INITIALIZED), - fallback_quarantine_cache(LINKER_INITIALIZED) {} - - void CheckOptions(const AllocatorOptions &options) const { - CHECK_GE(options.min_redzone, 16); - CHECK_GE(options.max_redzone, options.min_redzone); - CHECK_LE(options.max_redzone, 2048); - CHECK(IsPowerOfTwo(options.min_redzone)); - CHECK(IsPowerOfTwo(options.max_redzone)); - } - - void SharedInitCode(const AllocatorOptions &options) { - CheckOptions(options); - quarantine.Init((uptr)options.quarantine_size_mb << 20, - (uptr)options.thread_local_quarantine_size_kb << 10); - atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch, - memory_order_release); - atomic_store(&min_redzone, options.min_redzone, memory_order_release); - atomic_store(&max_redzone, options.max_redzone, memory_order_release); - } - - void InitLinkerInitialized(const AllocatorOptions &options) { - SetAllocatorMayReturnNull(options.may_return_null); - allocator.InitLinkerInitialized(options.release_to_os_interval_ms); - SharedInitCode(options); - } - - bool RssLimitExceeded() { - return atomic_load(&rss_limit_exceeded, memory_order_relaxed); - } - - void SetRssLimitExceeded(bool limit_exceeded) { - atomic_store(&rss_limit_exceeded, limit_exceeded, memory_order_relaxed); - } - - void RePoisonChunk(uptr chunk) { - // This could be a user-facing chunk (with redzones), or some internal - // housekeeping chunk, like TransferBatch. Start by assuming the former. - AsanChunk *ac = GetAsanChunk((void *)chunk); - uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac); - uptr beg = ac->Beg(); - uptr end = ac->Beg() + ac->UsedSize(true); - uptr chunk_end = chunk + allocated_size; - if (chunk < beg && beg < end && end <= chunk_end && - ac->chunk_state == CHUNK_ALLOCATED) { - // Looks like a valid AsanChunk in use, poison redzones only. - PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic); - uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY); - FastPoisonShadowPartialRightRedzone( - end_aligned_down, end - end_aligned_down, - chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic); - } else { - // This is either not an AsanChunk or freed or quarantined AsanChunk. - // In either case, poison everything. - PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic); - } - } - - void ReInitialize(const AllocatorOptions &options) { - SetAllocatorMayReturnNull(options.may_return_null); - allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms); - SharedInitCode(options); - - // Poison all existing allocation's redzones. - if (CanPoisonMemory()) { - allocator.ForceLock(); - allocator.ForEachChunk( - [](uptr chunk, void *alloc) { - ((Allocator *)alloc)->RePoisonChunk(chunk); - }, - this); - allocator.ForceUnlock(); - } - } - - void GetOptions(AllocatorOptions *options) const { - options->quarantine_size_mb = quarantine.GetSize() >> 20; - options->thread_local_quarantine_size_kb = quarantine.GetCacheSize() >> 10; - options->min_redzone = atomic_load(&min_redzone, memory_order_acquire); - options->max_redzone = atomic_load(&max_redzone, memory_order_acquire); - options->may_return_null = AllocatorMayReturnNull(); - options->alloc_dealloc_mismatch = - atomic_load(&alloc_dealloc_mismatch, memory_order_acquire); - options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs(); - } - - // -------------------- Helper methods. ------------------------- - uptr ComputeRZLog(uptr user_requested_size) { - u32 rz_log = - user_requested_size <= 64 - 16 ? 0 : - user_requested_size <= 128 - 32 ? 1 : - user_requested_size <= 512 - 64 ? 2 : - user_requested_size <= 4096 - 128 ? 3 : - user_requested_size <= (1 << 14) - 256 ? 4 : - user_requested_size <= (1 << 15) - 512 ? 5 : - user_requested_size <= (1 << 16) - 1024 ? 6 : 7; - u32 min_rz = atomic_load(&min_redzone, memory_order_acquire); - u32 max_rz = atomic_load(&max_redzone, memory_order_acquire); - return Min(Max(rz_log, RZSize2Log(min_rz)), RZSize2Log(max_rz)); - } - - static uptr ComputeUserRequestedAlignmentLog(uptr user_requested_alignment) { - if (user_requested_alignment < 8) - return 0; - if (user_requested_alignment > 512) - user_requested_alignment = 512; - return Log2(user_requested_alignment) - 2; - } - - static uptr ComputeUserAlignment(uptr user_requested_alignment_log) { - if (user_requested_alignment_log == 0) - return 0; - return 1LL << (user_requested_alignment_log + 2); - } - - // We have an address between two chunks, and we want to report just one. - AsanChunk *ChooseChunk(uptr addr, AsanChunk *left_chunk, - AsanChunk *right_chunk) { - // Prefer an allocated chunk over freed chunk and freed chunk - // over available chunk. - if (left_chunk->chunk_state != right_chunk->chunk_state) { - if (left_chunk->chunk_state == CHUNK_ALLOCATED) - return left_chunk; - if (right_chunk->chunk_state == CHUNK_ALLOCATED) - return right_chunk; - if (left_chunk->chunk_state == CHUNK_QUARANTINE) - return left_chunk; - if (right_chunk->chunk_state == CHUNK_QUARANTINE) - return right_chunk; - } - // Same chunk_state: choose based on offset. - sptr l_offset = 0, r_offset = 0; - CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); - CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); - if (l_offset < r_offset) - return left_chunk; - return right_chunk; - } - - // -------------------- Allocation/Deallocation routines --------------- - void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack, - AllocType alloc_type, bool can_fill) { - if (UNLIKELY(!asan_inited)) - AsanInitFromRtl(); - if (RssLimitExceeded()) { - if (AllocatorMayReturnNull()) - return nullptr; - ReportRssLimitExceeded(stack); - } - Flags &fl = *flags(); - CHECK(stack); - const uptr min_alignment = SHADOW_GRANULARITY; - const uptr user_requested_alignment_log = - ComputeUserRequestedAlignmentLog(alignment); - if (alignment < min_alignment) - alignment = min_alignment; - if (size == 0) { - // We'd be happy to avoid allocating memory for zero-size requests, but - // some programs/tests depend on this behavior and assume that malloc - // would not return NULL even for zero-size allocations. Moreover, it - // looks like operator new should never return NULL, and results of - // consecutive "new" calls must be different even if the allocated size - // is zero. - size = 1; - } - CHECK(IsPowerOfTwo(alignment)); - uptr rz_log = ComputeRZLog(size); - uptr rz_size = RZLog2Size(rz_log); - uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); - uptr needed_size = rounded_size + rz_size; - if (alignment > min_alignment) - needed_size += alignment; - bool using_primary_allocator = true; - // If we are allocating from the secondary allocator, there will be no - // automatic right redzone, so add the right redzone manually. - if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) { - needed_size += rz_size; - using_primary_allocator = false; - } - CHECK(IsAligned(needed_size, min_alignment)); - if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { - if (AllocatorMayReturnNull()) { - Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n", - (void*)size); - return nullptr; - } - ReportAllocationSizeTooBig(size, needed_size, kMaxAllowedMallocSize, - stack); - } - - AsanThread *t = GetCurrentThread(); - void *allocated; - if (t) { - AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); - allocated = allocator.Allocate(cache, needed_size, 8); - } else { - SpinMutexLock l(&fallback_mutex); - AllocatorCache *cache = &fallback_allocator_cache; - allocated = allocator.Allocate(cache, needed_size, 8); - } - if (UNLIKELY(!allocated)) { - SetAllocatorOutOfMemory(); - if (AllocatorMayReturnNull()) - return nullptr; - ReportOutOfMemory(size, stack); - } - - if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) { - // Heap poisoning is enabled, but the allocator provides an unpoisoned - // chunk. This is possible if CanPoisonMemory() was false for some - // time, for example, due to flags()->start_disabled. - // Anyway, poison the block before using it for anything else. - uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated); - PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic); - } - - uptr alloc_beg = reinterpret_cast(allocated); - uptr alloc_end = alloc_beg + needed_size; - uptr beg_plus_redzone = alloc_beg + rz_size; - uptr user_beg = beg_plus_redzone; - if (!IsAligned(user_beg, alignment)) - user_beg = RoundUpTo(user_beg, alignment); - uptr user_end = user_beg + size; - CHECK_LE(user_end, alloc_end); - uptr chunk_beg = user_beg - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast(chunk_beg); - m->alloc_type = alloc_type; - m->rz_log = rz_log; - u32 alloc_tid = t ? t->tid() : 0; - m->alloc_tid = alloc_tid; - CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield? - m->free_tid = kInvalidTid; - m->from_memalign = user_beg != beg_plus_redzone; - if (alloc_beg != chunk_beg) { - CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); - reinterpret_cast(alloc_beg)[0] = kAllocBegMagic; - reinterpret_cast(alloc_beg)[1] = chunk_beg; - } - if (using_primary_allocator) { - CHECK(size); - m->user_requested_size = size; - CHECK(allocator.FromPrimary(allocated)); - } else { - CHECK(!allocator.FromPrimary(allocated)); - m->user_requested_size = SizeClassMap::kMaxSize; - uptr *meta = reinterpret_cast(allocator.GetMetaData(allocated)); - meta[0] = size; - meta[1] = chunk_beg; - } - m->user_requested_alignment_log = user_requested_alignment_log; - - m->alloc_context_id = StackDepotPut(*stack); - - uptr size_rounded_down_to_granularity = - RoundDownTo(size, SHADOW_GRANULARITY); - // Unpoison the bulk of the memory region. - if (size_rounded_down_to_granularity) - PoisonShadow(user_beg, size_rounded_down_to_granularity, 0); - // Deal with the end of the region if size is not aligned to granularity. - if (size != size_rounded_down_to_granularity && CanPoisonMemory()) { - u8 *shadow = - (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity); - *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; - } - - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.mallocs++; - thread_stats.malloced += size; - thread_stats.malloced_redzones += needed_size - size; - if (needed_size > SizeClassMap::kMaxSize) - thread_stats.malloc_large++; - else - thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++; - - void *res = reinterpret_cast(user_beg); - if (can_fill && fl.max_malloc_fill_size) { - uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); - REAL(memset)(res, fl.malloc_fill_byte, fill_size); - } -#if CAN_SANITIZE_LEAKS - m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored - : __lsan::kDirectlyLeaked; -#endif - // Must be the last mutation of metadata in this function. - atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); - ASAN_MALLOC_HOOK(res, size); - return res; - } - - // Set quarantine flag if chunk is allocated, issue ASan error report on - // available and quarantined chunks. Return true on success, false otherwise. - bool AtomicallySetQuarantineFlagIfAllocated(AsanChunk *m, void *ptr, - BufferedStackTrace *stack) { - u8 old_chunk_state = CHUNK_ALLOCATED; - // Flip the chunk_state atomically to avoid race on double-free. - if (!atomic_compare_exchange_strong((atomic_uint8_t *)m, &old_chunk_state, - CHUNK_QUARANTINE, - memory_order_acquire)) { - ReportInvalidFree(ptr, old_chunk_state, stack); - // It's not safe to push a chunk in quarantine on invalid free. - return false; - } - CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); - return true; - } - - // Expects the chunk to already be marked as quarantined by using - // AtomicallySetQuarantineFlagIfAllocated. - void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack) { - CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); - CHECK_GE(m->alloc_tid, 0); - if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area. - CHECK_EQ(m->free_tid, kInvalidTid); - AsanThread *t = GetCurrentThread(); - m->free_tid = t ? t->tid() : 0; - m->free_context_id = StackDepotPut(*stack); - - Flags &fl = *flags(); - if (fl.max_free_fill_size > 0) { - // We have to skip the chunk header, it contains free_context_id. - uptr scribble_start = (uptr)m + kChunkHeaderSize + kChunkHeader2Size; - if (m->UsedSize() >= kChunkHeader2Size) { // Skip Header2 in user area. - uptr size_to_fill = m->UsedSize() - kChunkHeader2Size; - size_to_fill = Min(size_to_fill, (uptr)fl.max_free_fill_size); - REAL(memset)((void *)scribble_start, fl.free_fill_byte, size_to_fill); - } - } - - // Poison the region. - PoisonShadow(m->Beg(), - RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), - kAsanHeapFreeMagic); - - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.frees++; - thread_stats.freed += m->UsedSize(); - - // Push into quarantine. - if (t) { - AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); - AllocatorCache *ac = GetAllocatorCache(ms); - quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac, stack), m, - m->UsedSize()); - } else { - SpinMutexLock l(&fallback_mutex); - AllocatorCache *ac = &fallback_allocator_cache; - quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac, stack), - m, m->UsedSize()); - } - } - - void Deallocate(void *ptr, uptr delete_size, uptr delete_alignment, - BufferedStackTrace *stack, AllocType alloc_type) { - uptr p = reinterpret_cast(ptr); - if (p == 0) return; - - uptr chunk_beg = p - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast(chunk_beg); - - // On Windows, uninstrumented DLLs may allocate memory before ASan hooks - // malloc. Don't report an invalid free in this case. - if (SANITIZER_WINDOWS && - !get_allocator().PointerIsMine(ptr)) { - if (!IsSystemHeapAddress(p)) - ReportFreeNotMalloced(p, stack); - return; - } - - ASAN_FREE_HOOK(ptr); - - // Must mark the chunk as quarantined before any changes to its metadata. - // Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag. - if (!AtomicallySetQuarantineFlagIfAllocated(m, ptr, stack)) return; - - if (m->alloc_type != alloc_type) { - if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) { - ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type, - (AllocType)alloc_type); - } - } else { - if (flags()->new_delete_type_mismatch && - (alloc_type == FROM_NEW || alloc_type == FROM_NEW_BR) && - ((delete_size && delete_size != m->UsedSize()) || - ComputeUserRequestedAlignmentLog(delete_alignment) != - m->user_requested_alignment_log)) { - ReportNewDeleteTypeMismatch(p, delete_size, delete_alignment, stack); - } - } - - QuarantineChunk(m, ptr, stack); - } - - void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) { - CHECK(old_ptr && new_size); - uptr p = reinterpret_cast(old_ptr); - uptr chunk_beg = p - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast(chunk_beg); - - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.reallocs++; - thread_stats.realloced += new_size; - - void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); - if (new_ptr) { - u8 chunk_state = m->chunk_state; - if (chunk_state != CHUNK_ALLOCATED) - ReportInvalidFree(old_ptr, chunk_state, stack); - CHECK_NE(REAL(memcpy), nullptr); - uptr memcpy_size = Min(new_size, m->UsedSize()); - // If realloc() races with free(), we may start copying freed memory. - // However, we will report racy double-free later anyway. - REAL(memcpy)(new_ptr, old_ptr, memcpy_size); - Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC); - } - return new_ptr; - } - - void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { - if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { - if (AllocatorMayReturnNull()) - return nullptr; - ReportCallocOverflow(nmemb, size, stack); - } - void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); - // If the memory comes from the secondary allocator no need to clear it - // as it comes directly from mmap. - if (ptr && allocator.FromPrimary(ptr)) - REAL(memset)(ptr, 0, nmemb * size); - return ptr; - } - - void ReportInvalidFree(void *ptr, u8 chunk_state, BufferedStackTrace *stack) { - if (chunk_state == CHUNK_QUARANTINE) - ReportDoubleFree((uptr)ptr, stack); - else - ReportFreeNotMalloced((uptr)ptr, stack); - } - - void CommitBack(AsanThreadLocalMallocStorage *ms, BufferedStackTrace *stack) { - AllocatorCache *ac = GetAllocatorCache(ms); - quarantine.Drain(GetQuarantineCache(ms), QuarantineCallback(ac, stack)); - allocator.SwallowCache(ac); - } - - // -------------------------- Chunk lookup ---------------------- - - // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). - AsanChunk *GetAsanChunk(void *alloc_beg) { - if (!alloc_beg) return nullptr; - if (!allocator.FromPrimary(alloc_beg)) { - uptr *meta = reinterpret_cast(allocator.GetMetaData(alloc_beg)); - AsanChunk *m = reinterpret_cast(meta[1]); - return m; - } - uptr *alloc_magic = reinterpret_cast(alloc_beg); - if (alloc_magic[0] == kAllocBegMagic) - return reinterpret_cast(alloc_magic[1]); - return reinterpret_cast(alloc_beg); - } - - AsanChunk *GetAsanChunkByAddr(uptr p) { - void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast(p)); - return GetAsanChunk(alloc_beg); - } - - // Allocator must be locked when this function is called. - AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { - void *alloc_beg = - allocator.GetBlockBeginFastLocked(reinterpret_cast(p)); - return GetAsanChunk(alloc_beg); - } - - uptr AllocationSize(uptr p) { - AsanChunk *m = GetAsanChunkByAddr(p); - if (!m) return 0; - if (m->chunk_state != CHUNK_ALLOCATED) return 0; - if (m->Beg() != p) return 0; - return m->UsedSize(); - } - - AsanChunkView FindHeapChunkByAddress(uptr addr) { - AsanChunk *m1 = GetAsanChunkByAddr(addr); - if (!m1) return AsanChunkView(m1); - sptr offset = 0; - if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) { - // The address is in the chunk's left redzone, so maybe it is actually - // a right buffer overflow from the other chunk to the left. - // Search a bit to the left to see if there is another chunk. - AsanChunk *m2 = nullptr; - for (uptr l = 1; l < GetPageSizeCached(); l++) { - m2 = GetAsanChunkByAddr(addr - l); - if (m2 == m1) continue; // Still the same chunk. - break; - } - if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset)) - m1 = ChooseChunk(addr, m2, m1); - } - return AsanChunkView(m1); - } - - void Purge(BufferedStackTrace *stack) { - AsanThread *t = GetCurrentThread(); - if (t) { - AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); - quarantine.DrainAndRecycle(GetQuarantineCache(ms), - QuarantineCallback(GetAllocatorCache(ms), - stack)); - } - { - SpinMutexLock l(&fallback_mutex); - quarantine.DrainAndRecycle(&fallback_quarantine_cache, - QuarantineCallback(&fallback_allocator_cache, - stack)); - } - - allocator.ForceReleaseToOS(); - } - - void PrintStats() { - allocator.PrintStats(); - quarantine.PrintStats(); - } - - void ForceLock() { - allocator.ForceLock(); - fallback_mutex.Lock(); - } - - void ForceUnlock() { - fallback_mutex.Unlock(); - allocator.ForceUnlock(); - } -}; - -static Allocator instance(LINKER_INITIALIZED); - -static AsanAllocator &get_allocator() { - return instance.allocator; -} - -bool AsanChunkView::IsValid() const { - return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE; -} -bool AsanChunkView::IsAllocated() const { - return chunk_ && chunk_->chunk_state == CHUNK_ALLOCATED; -} -bool AsanChunkView::IsQuarantined() const { - return chunk_ && chunk_->chunk_state == CHUNK_QUARANTINE; -} -uptr AsanChunkView::Beg() const { return chunk_->Beg(); } -uptr AsanChunkView::End() const { return Beg() + UsedSize(); } -uptr AsanChunkView::UsedSize() const { return chunk_->UsedSize(); } -u32 AsanChunkView::UserRequestedAlignment() const { - return Allocator::ComputeUserAlignment(chunk_->user_requested_alignment_log); -} -uptr AsanChunkView::AllocTid() const { return chunk_->alloc_tid; } -uptr AsanChunkView::FreeTid() const { return chunk_->free_tid; } -AllocType AsanChunkView::GetAllocType() const { - return (AllocType)chunk_->alloc_type; -} - -static StackTrace GetStackTraceFromId(u32 id) { - CHECK(id); - StackTrace res = StackDepotGet(id); - CHECK(res.trace); - return res; -} - -u32 AsanChunkView::GetAllocStackId() const { return chunk_->alloc_context_id; } -u32 AsanChunkView::GetFreeStackId() const { return chunk_->free_context_id; } - -StackTrace AsanChunkView::GetAllocStack() const { - return GetStackTraceFromId(GetAllocStackId()); -} - -StackTrace AsanChunkView::GetFreeStack() const { - return GetStackTraceFromId(GetFreeStackId()); -} - -void InitializeAllocator(const AllocatorOptions &options) { - instance.InitLinkerInitialized(options); -} - -void ReInitializeAllocator(const AllocatorOptions &options) { - instance.ReInitialize(options); -} - -void GetAllocatorOptions(AllocatorOptions *options) { - instance.GetOptions(options); -} - -AsanChunkView FindHeapChunkByAddress(uptr addr) { - return instance.FindHeapChunkByAddress(addr); -} -AsanChunkView FindHeapChunkByAllocBeg(uptr addr) { - return AsanChunkView(instance.GetAsanChunk(reinterpret_cast(addr))); -} - -void AsanThreadLocalMallocStorage::CommitBack() { - GET_STACK_TRACE_MALLOC; - instance.CommitBack(this, &stack); -} - -void PrintInternalAllocatorStats() { - instance.PrintStats(); -} - -void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) { - instance.Deallocate(ptr, 0, 0, stack, alloc_type); -} - -void asan_delete(void *ptr, uptr size, uptr alignment, - BufferedStackTrace *stack, AllocType alloc_type) { - instance.Deallocate(ptr, size, alignment, stack, alloc_type); -} - -void *asan_malloc(uptr size, BufferedStackTrace *stack) { - return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true)); -} - -void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { - return SetErrnoOnNull(instance.Calloc(nmemb, size, stack)); -} - -void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { - if (!p) - return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true)); - if (size == 0) { - if (flags()->allocator_frees_and_returns_null_on_realloc_zero) { - instance.Deallocate(p, 0, 0, stack, FROM_MALLOC); - return nullptr; - } - // Allocate a size of 1 if we shouldn't free() on Realloc to 0 - size = 1; - } - return SetErrnoOnNull(instance.Reallocate(p, size, stack)); -} - -void *asan_valloc(uptr size, BufferedStackTrace *stack) { - return SetErrnoOnNull( - instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true)); -} - -void *asan_pvalloc(uptr size, BufferedStackTrace *stack) { - uptr PageSize = GetPageSizeCached(); - if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) { - errno = errno_ENOMEM; - if (AllocatorMayReturnNull()) - return nullptr; - ReportPvallocOverflow(size, stack); - } - // pvalloc(0) should allocate one page. - size = size ? RoundUpTo(size, PageSize) : PageSize; - return SetErrnoOnNull( - instance.Allocate(size, PageSize, stack, FROM_MALLOC, true)); -} - -void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, - AllocType alloc_type) { - if (UNLIKELY(!IsPowerOfTwo(alignment))) { - errno = errno_EINVAL; - if (AllocatorMayReturnNull()) - return nullptr; - ReportInvalidAllocationAlignment(alignment, stack); - } - return SetErrnoOnNull( - instance.Allocate(size, alignment, stack, alloc_type, true)); -} - -void *asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack) { - if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) { - errno = errno_EINVAL; - if (AllocatorMayReturnNull()) - return nullptr; - ReportInvalidAlignedAllocAlignment(size, alignment, stack); - } - return SetErrnoOnNull( - instance.Allocate(size, alignment, stack, FROM_MALLOC, true)); -} - -int asan_posix_memalign(void **memptr, uptr alignment, uptr size, - BufferedStackTrace *stack) { - if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) { - if (AllocatorMayReturnNull()) - return errno_EINVAL; - ReportInvalidPosixMemalignAlignment(alignment, stack); - } - void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC, true); - if (UNLIKELY(!ptr)) - // OOM error is already taken care of by Allocate. - return errno_ENOMEM; - CHECK(IsAligned((uptr)ptr, alignment)); - *memptr = ptr; - return 0; -} - -uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) { - if (!ptr) return 0; - uptr usable_size = instance.AllocationSize(reinterpret_cast(ptr)); - if (flags()->check_malloc_usable_size && (usable_size == 0)) { - GET_STACK_TRACE_FATAL(pc, bp); - ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); - } - return usable_size; -} - -uptr asan_mz_size(const void *ptr) { - return instance.AllocationSize(reinterpret_cast(ptr)); -} - -void asan_mz_force_lock() { - instance.ForceLock(); -} - -void asan_mz_force_unlock() { - instance.ForceUnlock(); -} - -void AsanSoftRssLimitExceededCallback(bool limit_exceeded) { - instance.SetRssLimitExceeded(limit_exceeded); -} - -} // namespace __asan - -// --- Implementation of LSan-specific functions --- {{{1 -namespace __lsan { -void LockAllocator() { - __asan::get_allocator().ForceLock(); -} - -void UnlockAllocator() { - __asan::get_allocator().ForceUnlock(); -} - -void GetAllocatorGlobalRange(uptr *begin, uptr *end) { - *begin = (uptr)&__asan::get_allocator(); - *end = *begin + sizeof(__asan::get_allocator()); -} - -uptr PointsIntoChunk(void* p) { - uptr addr = reinterpret_cast(p); - __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(addr); - if (!m) return 0; - uptr chunk = m->Beg(); - if (m->chunk_state != __asan::CHUNK_ALLOCATED) - return 0; - if (m->AddrIsInside(addr, /*locked_version=*/true)) - return chunk; - if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true), - addr)) - return chunk; - return 0; -} - -uptr GetUserBegin(uptr chunk) { - __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk); - CHECK(m); - return m->Beg(); -} - -LsanMetadata::LsanMetadata(uptr chunk) { - metadata_ = reinterpret_cast(chunk - __asan::kChunkHeaderSize); -} - -bool LsanMetadata::allocated() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->chunk_state == __asan::CHUNK_ALLOCATED; -} - -ChunkTag LsanMetadata::tag() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return static_cast(m->lsan_tag); -} - -void LsanMetadata::set_tag(ChunkTag value) { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - m->lsan_tag = value; -} - -uptr LsanMetadata::requested_size() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->UsedSize(/*locked_version=*/true); -} - -u32 LsanMetadata::stack_trace_id() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->alloc_context_id; -} - -void ForEachChunk(ForEachChunkCallback callback, void *arg) { - __asan::get_allocator().ForEachChunk(callback, arg); -} - -IgnoreObjectResult IgnoreObjectLocked(const void *p) { - uptr addr = reinterpret_cast(p); - __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddr(addr); - if (!m) return kIgnoreObjectInvalid; - if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) { - if (m->lsan_tag == kIgnored) - return kIgnoreObjectAlreadyIgnored; - m->lsan_tag = __lsan::kIgnored; - return kIgnoreObjectSuccess; - } else { - return kIgnoreObjectInvalid; - } -} -} // namespace __lsan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -// ASan allocator doesn't reserve extra bytes, so normally we would -// just return "size". We don't want to expose our redzone sizes, etc here. -uptr __sanitizer_get_estimated_allocated_size(uptr size) { - return size; -} - -int __sanitizer_get_ownership(const void *p) { - uptr ptr = reinterpret_cast(p); - return instance.AllocationSize(ptr) > 0; -} - -uptr __sanitizer_get_allocated_size(const void *p) { - if (!p) return 0; - uptr ptr = reinterpret_cast(p); - uptr allocated_size = instance.AllocationSize(ptr); - // Die if p is not malloced or if it is already freed. - if (allocated_size == 0) { - GET_STACK_TRACE_FATAL_HERE; - ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack); - } - return allocated_size; -} - -void __sanitizer_purge_allocator() { - GET_STACK_TRACE_MALLOC; - instance.Purge(&stack); -} - -#if !SANITIZER_SUPPORTS_WEAK_HOOKS -// Provide default (no-op) implementation of malloc hooks. -SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook, - void *ptr, uptr size) { - (void)ptr; - (void)size; -} - -SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) { - (void)ptr; -} -#endif diff --git a/libsanitizer/asan/asan_allocator.cpp b/libsanitizer/asan/asan_allocator.cpp new file mode 100644 index 00000000000..b58116e17b7 --- /dev/null +++ b/libsanitizer/asan/asan_allocator.cpp @@ -0,0 +1,1119 @@ +//===-- asan_allocator.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Implementation of ASan's memory allocator, 2-nd version. +// This variant uses the allocator from sanitizer_common, i.e. the one shared +// with ThreadSanitizer and MemorySanitizer. +// +//===----------------------------------------------------------------------===// + +#include "asan_allocator.h" +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_allocator_checks.h" +#include "sanitizer_common/sanitizer_allocator_interface.h" +#include "sanitizer_common/sanitizer_errno.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_list.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_quarantine.h" +#include "lsan/lsan_common.h" + +namespace __asan { + +// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits. +// We use adaptive redzones: for larger allocation larger redzones are used. +static u32 RZLog2Size(u32 rz_log) { + CHECK_LT(rz_log, 8); + return 16 << rz_log; +} + +static u32 RZSize2Log(u32 rz_size) { + CHECK_GE(rz_size, 16); + CHECK_LE(rz_size, 2048); + CHECK(IsPowerOfTwo(rz_size)); + u32 res = Log2(rz_size) - 4; + CHECK_EQ(rz_size, RZLog2Size(res)); + return res; +} + +static AsanAllocator &get_allocator(); + +// The memory chunk allocated from the underlying allocator looks like this: +// L L L L L L H H U U U U U U R R +// L -- left redzone words (0 or more bytes) +// H -- ChunkHeader (16 bytes), which is also a part of the left redzone. +// U -- user memory. +// R -- right redzone (0 or more bytes) +// ChunkBase consists of ChunkHeader and other bytes that overlap with user +// memory. + +// If the left redzone is greater than the ChunkHeader size we store a magic +// value in the first uptr word of the memory block and store the address of +// ChunkBase in the next uptr. +// M B L L L L L L L L L H H U U U U U U +// | ^ +// ---------------------| +// M -- magic value kAllocBegMagic +// B -- address of ChunkHeader pointing to the first 'H' +static const uptr kAllocBegMagic = 0xCC6E96B9; + +struct ChunkHeader { + // 1-st 8 bytes. + u32 chunk_state : 8; // Must be first. + u32 alloc_tid : 24; + + u32 free_tid : 24; + u32 from_memalign : 1; + u32 alloc_type : 2; + u32 rz_log : 3; + u32 lsan_tag : 2; + // 2-nd 8 bytes + // This field is used for small sizes. For large sizes it is equal to + // SizeClassMap::kMaxSize and the actual size is stored in the + // SecondaryAllocator's metadata. + u32 user_requested_size : 29; + // align < 8 -> 0 + // else -> log2(min(align, 512)) - 2 + u32 user_requested_alignment_log : 3; + u32 alloc_context_id; +}; + +struct ChunkBase : ChunkHeader { + // Header2, intersects with user memory. + u32 free_context_id; +}; + +static const uptr kChunkHeaderSize = sizeof(ChunkHeader); +static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize; +COMPILER_CHECK(kChunkHeaderSize == 16); +COMPILER_CHECK(kChunkHeader2Size <= 16); + +// Every chunk of memory allocated by this allocator can be in one of 3 states: +// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. +// CHUNK_ALLOCATED: the chunk is allocated and not yet freed. +// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone. +enum { + CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it. + CHUNK_ALLOCATED = 2, + CHUNK_QUARANTINE = 3 +}; + +struct AsanChunk: ChunkBase { + uptr Beg() { return reinterpret_cast(this) + kChunkHeaderSize; } + uptr UsedSize(bool locked_version = false) { + if (user_requested_size != SizeClassMap::kMaxSize) + return user_requested_size; + return *reinterpret_cast( + get_allocator().GetMetaData(AllocBeg(locked_version))); + } + void *AllocBeg(bool locked_version = false) { + if (from_memalign) { + if (locked_version) + return get_allocator().GetBlockBeginFastLocked( + reinterpret_cast(this)); + return get_allocator().GetBlockBegin(reinterpret_cast(this)); + } + return reinterpret_cast(Beg() - RZLog2Size(rz_log)); + } + bool AddrIsInside(uptr addr, bool locked_version = false) { + return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); + } +}; + +struct QuarantineCallback { + QuarantineCallback(AllocatorCache *cache, BufferedStackTrace *stack) + : cache_(cache), + stack_(stack) { + } + + void Recycle(AsanChunk *m) { + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed); + CHECK_NE(m->alloc_tid, kInvalidTid); + CHECK_NE(m->free_tid, kInvalidTid); + PoisonShadow(m->Beg(), + RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), + kAsanHeapLeftRedzoneMagic); + void *p = reinterpret_cast(m->AllocBeg()); + if (p != m) { + uptr *alloc_magic = reinterpret_cast(p); + CHECK_EQ(alloc_magic[0], kAllocBegMagic); + // Clear the magic value, as allocator internals may overwrite the + // contents of deallocated chunk, confusing GetAsanChunk lookup. + alloc_magic[0] = 0; + CHECK_EQ(alloc_magic[1], reinterpret_cast(m)); + } + + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.real_frees++; + thread_stats.really_freed += m->UsedSize(); + + get_allocator().Deallocate(cache_, p); + } + + void *Allocate(uptr size) { + void *res = get_allocator().Allocate(cache_, size, 1); + // TODO(alekseys): Consider making quarantine OOM-friendly. + if (UNLIKELY(!res)) + ReportOutOfMemory(size, stack_); + return res; + } + + void Deallocate(void *p) { + get_allocator().Deallocate(cache_, p); + } + + private: + AllocatorCache* const cache_; + BufferedStackTrace* const stack_; +}; + +typedef Quarantine AsanQuarantine; +typedef AsanQuarantine::Cache QuarantineCache; + +void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const { + PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.mmaps++; + thread_stats.mmaped += size; +} +void AsanMapUnmapCallback::OnUnmap(uptr p, uptr size) const { + PoisonShadow(p, size, 0); + // We are about to unmap a chunk of user memory. + // Mark the corresponding shadow memory as not needed. + FlushUnneededASanShadowMemory(p, size); + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.munmaps++; + thread_stats.munmaped += size; +} + +// We can not use THREADLOCAL because it is not supported on some of the +// platforms we care about (OSX 10.6, Android). +// static THREADLOCAL AllocatorCache cache; +AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) { + CHECK(ms); + return &ms->allocator_cache; +} + +QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) { + CHECK(ms); + CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache)); + return reinterpret_cast(ms->quarantine_cache); +} + +void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) { + quarantine_size_mb = f->quarantine_size_mb; + thread_local_quarantine_size_kb = f->thread_local_quarantine_size_kb; + min_redzone = f->redzone; + max_redzone = f->max_redzone; + may_return_null = cf->allocator_may_return_null; + alloc_dealloc_mismatch = f->alloc_dealloc_mismatch; + release_to_os_interval_ms = cf->allocator_release_to_os_interval_ms; +} + +void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) { + f->quarantine_size_mb = quarantine_size_mb; + f->thread_local_quarantine_size_kb = thread_local_quarantine_size_kb; + f->redzone = min_redzone; + f->max_redzone = max_redzone; + cf->allocator_may_return_null = may_return_null; + f->alloc_dealloc_mismatch = alloc_dealloc_mismatch; + cf->allocator_release_to_os_interval_ms = release_to_os_interval_ms; +} + +struct Allocator { + static const uptr kMaxAllowedMallocSize = + FIRST_32_SECOND_64(3UL << 30, 1ULL << 40); + + AsanAllocator allocator; + AsanQuarantine quarantine; + StaticSpinMutex fallback_mutex; + AllocatorCache fallback_allocator_cache; + QuarantineCache fallback_quarantine_cache; + + atomic_uint8_t rss_limit_exceeded; + + // ------------------- Options -------------------------- + atomic_uint16_t min_redzone; + atomic_uint16_t max_redzone; + atomic_uint8_t alloc_dealloc_mismatch; + + // ------------------- Initialization ------------------------ + explicit Allocator(LinkerInitialized) + : quarantine(LINKER_INITIALIZED), + fallback_quarantine_cache(LINKER_INITIALIZED) {} + + void CheckOptions(const AllocatorOptions &options) const { + CHECK_GE(options.min_redzone, 16); + CHECK_GE(options.max_redzone, options.min_redzone); + CHECK_LE(options.max_redzone, 2048); + CHECK(IsPowerOfTwo(options.min_redzone)); + CHECK(IsPowerOfTwo(options.max_redzone)); + } + + void SharedInitCode(const AllocatorOptions &options) { + CheckOptions(options); + quarantine.Init((uptr)options.quarantine_size_mb << 20, + (uptr)options.thread_local_quarantine_size_kb << 10); + atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch, + memory_order_release); + atomic_store(&min_redzone, options.min_redzone, memory_order_release); + atomic_store(&max_redzone, options.max_redzone, memory_order_release); + } + + void InitLinkerInitialized(const AllocatorOptions &options) { + SetAllocatorMayReturnNull(options.may_return_null); + allocator.InitLinkerInitialized(options.release_to_os_interval_ms); + SharedInitCode(options); + } + + bool RssLimitExceeded() { + return atomic_load(&rss_limit_exceeded, memory_order_relaxed); + } + + void SetRssLimitExceeded(bool limit_exceeded) { + atomic_store(&rss_limit_exceeded, limit_exceeded, memory_order_relaxed); + } + + void RePoisonChunk(uptr chunk) { + // This could be a user-facing chunk (with redzones), or some internal + // housekeeping chunk, like TransferBatch. Start by assuming the former. + AsanChunk *ac = GetAsanChunk((void *)chunk); + uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac); + uptr beg = ac->Beg(); + uptr end = ac->Beg() + ac->UsedSize(true); + uptr chunk_end = chunk + allocated_size; + if (chunk < beg && beg < end && end <= chunk_end && + ac->chunk_state == CHUNK_ALLOCATED) { + // Looks like a valid AsanChunk in use, poison redzones only. + PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic); + uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY); + FastPoisonShadowPartialRightRedzone( + end_aligned_down, end - end_aligned_down, + chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic); + } else { + // This is either not an AsanChunk or freed or quarantined AsanChunk. + // In either case, poison everything. + PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic); + } + } + + void ReInitialize(const AllocatorOptions &options) { + SetAllocatorMayReturnNull(options.may_return_null); + allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms); + SharedInitCode(options); + + // Poison all existing allocation's redzones. + if (CanPoisonMemory()) { + allocator.ForceLock(); + allocator.ForEachChunk( + [](uptr chunk, void *alloc) { + ((Allocator *)alloc)->RePoisonChunk(chunk); + }, + this); + allocator.ForceUnlock(); + } + } + + void GetOptions(AllocatorOptions *options) const { + options->quarantine_size_mb = quarantine.GetSize() >> 20; + options->thread_local_quarantine_size_kb = quarantine.GetCacheSize() >> 10; + options->min_redzone = atomic_load(&min_redzone, memory_order_acquire); + options->max_redzone = atomic_load(&max_redzone, memory_order_acquire); + options->may_return_null = AllocatorMayReturnNull(); + options->alloc_dealloc_mismatch = + atomic_load(&alloc_dealloc_mismatch, memory_order_acquire); + options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs(); + } + + // -------------------- Helper methods. ------------------------- + uptr ComputeRZLog(uptr user_requested_size) { + u32 rz_log = + user_requested_size <= 64 - 16 ? 0 : + user_requested_size <= 128 - 32 ? 1 : + user_requested_size <= 512 - 64 ? 2 : + user_requested_size <= 4096 - 128 ? 3 : + user_requested_size <= (1 << 14) - 256 ? 4 : + user_requested_size <= (1 << 15) - 512 ? 5 : + user_requested_size <= (1 << 16) - 1024 ? 6 : 7; + u32 min_rz = atomic_load(&min_redzone, memory_order_acquire); + u32 max_rz = atomic_load(&max_redzone, memory_order_acquire); + return Min(Max(rz_log, RZSize2Log(min_rz)), RZSize2Log(max_rz)); + } + + static uptr ComputeUserRequestedAlignmentLog(uptr user_requested_alignment) { + if (user_requested_alignment < 8) + return 0; + if (user_requested_alignment > 512) + user_requested_alignment = 512; + return Log2(user_requested_alignment) - 2; + } + + static uptr ComputeUserAlignment(uptr user_requested_alignment_log) { + if (user_requested_alignment_log == 0) + return 0; + return 1LL << (user_requested_alignment_log + 2); + } + + // We have an address between two chunks, and we want to report just one. + AsanChunk *ChooseChunk(uptr addr, AsanChunk *left_chunk, + AsanChunk *right_chunk) { + // Prefer an allocated chunk over freed chunk and freed chunk + // over available chunk. + if (left_chunk->chunk_state != right_chunk->chunk_state) { + if (left_chunk->chunk_state == CHUNK_ALLOCATED) + return left_chunk; + if (right_chunk->chunk_state == CHUNK_ALLOCATED) + return right_chunk; + if (left_chunk->chunk_state == CHUNK_QUARANTINE) + return left_chunk; + if (right_chunk->chunk_state == CHUNK_QUARANTINE) + return right_chunk; + } + // Same chunk_state: choose based on offset. + sptr l_offset = 0, r_offset = 0; + CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); + CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); + if (l_offset < r_offset) + return left_chunk; + return right_chunk; + } + + // -------------------- Allocation/Deallocation routines --------------- + void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack, + AllocType alloc_type, bool can_fill) { + if (UNLIKELY(!asan_inited)) + AsanInitFromRtl(); + if (RssLimitExceeded()) { + if (AllocatorMayReturnNull()) + return nullptr; + ReportRssLimitExceeded(stack); + } + Flags &fl = *flags(); + CHECK(stack); + const uptr min_alignment = SHADOW_GRANULARITY; + const uptr user_requested_alignment_log = + ComputeUserRequestedAlignmentLog(alignment); + if (alignment < min_alignment) + alignment = min_alignment; + if (size == 0) { + // We'd be happy to avoid allocating memory for zero-size requests, but + // some programs/tests depend on this behavior and assume that malloc + // would not return NULL even for zero-size allocations. Moreover, it + // looks like operator new should never return NULL, and results of + // consecutive "new" calls must be different even if the allocated size + // is zero. + size = 1; + } + CHECK(IsPowerOfTwo(alignment)); + uptr rz_log = ComputeRZLog(size); + uptr rz_size = RZLog2Size(rz_log); + uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); + uptr needed_size = rounded_size + rz_size; + if (alignment > min_alignment) + needed_size += alignment; + bool using_primary_allocator = true; + // If we are allocating from the secondary allocator, there will be no + // automatic right redzone, so add the right redzone manually. + if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) { + needed_size += rz_size; + using_primary_allocator = false; + } + CHECK(IsAligned(needed_size, min_alignment)); + if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { + if (AllocatorMayReturnNull()) { + Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n", + (void*)size); + return nullptr; + } + ReportAllocationSizeTooBig(size, needed_size, kMaxAllowedMallocSize, + stack); + } + + AsanThread *t = GetCurrentThread(); + void *allocated; + if (t) { + AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); + allocated = allocator.Allocate(cache, needed_size, 8); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *cache = &fallback_allocator_cache; + allocated = allocator.Allocate(cache, needed_size, 8); + } + if (UNLIKELY(!allocated)) { + SetAllocatorOutOfMemory(); + if (AllocatorMayReturnNull()) + return nullptr; + ReportOutOfMemory(size, stack); + } + + if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) { + // Heap poisoning is enabled, but the allocator provides an unpoisoned + // chunk. This is possible if CanPoisonMemory() was false for some + // time, for example, due to flags()->start_disabled. + // Anyway, poison the block before using it for anything else. + uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated); + PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic); + } + + uptr alloc_beg = reinterpret_cast(allocated); + uptr alloc_end = alloc_beg + needed_size; + uptr beg_plus_redzone = alloc_beg + rz_size; + uptr user_beg = beg_plus_redzone; + if (!IsAligned(user_beg, alignment)) + user_beg = RoundUpTo(user_beg, alignment); + uptr user_end = user_beg + size; + CHECK_LE(user_end, alloc_end); + uptr chunk_beg = user_beg - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + m->alloc_type = alloc_type; + m->rz_log = rz_log; + u32 alloc_tid = t ? t->tid() : 0; + m->alloc_tid = alloc_tid; + CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield? + m->free_tid = kInvalidTid; + m->from_memalign = user_beg != beg_plus_redzone; + if (alloc_beg != chunk_beg) { + CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); + reinterpret_cast(alloc_beg)[0] = kAllocBegMagic; + reinterpret_cast(alloc_beg)[1] = chunk_beg; + } + if (using_primary_allocator) { + CHECK(size); + m->user_requested_size = size; + CHECK(allocator.FromPrimary(allocated)); + } else { + CHECK(!allocator.FromPrimary(allocated)); + m->user_requested_size = SizeClassMap::kMaxSize; + uptr *meta = reinterpret_cast(allocator.GetMetaData(allocated)); + meta[0] = size; + meta[1] = chunk_beg; + } + m->user_requested_alignment_log = user_requested_alignment_log; + + m->alloc_context_id = StackDepotPut(*stack); + + uptr size_rounded_down_to_granularity = + RoundDownTo(size, SHADOW_GRANULARITY); + // Unpoison the bulk of the memory region. + if (size_rounded_down_to_granularity) + PoisonShadow(user_beg, size_rounded_down_to_granularity, 0); + // Deal with the end of the region if size is not aligned to granularity. + if (size != size_rounded_down_to_granularity && CanPoisonMemory()) { + u8 *shadow = + (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity); + *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; + } + + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.mallocs++; + thread_stats.malloced += size; + thread_stats.malloced_redzones += needed_size - size; + if (needed_size > SizeClassMap::kMaxSize) + thread_stats.malloc_large++; + else + thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++; + + void *res = reinterpret_cast(user_beg); + if (can_fill && fl.max_malloc_fill_size) { + uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); + REAL(memset)(res, fl.malloc_fill_byte, fill_size); + } +#if CAN_SANITIZE_LEAKS + m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored + : __lsan::kDirectlyLeaked; +#endif + // Must be the last mutation of metadata in this function. + atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); + ASAN_MALLOC_HOOK(res, size); + return res; + } + + // Set quarantine flag if chunk is allocated, issue ASan error report on + // available and quarantined chunks. Return true on success, false otherwise. + bool AtomicallySetQuarantineFlagIfAllocated(AsanChunk *m, void *ptr, + BufferedStackTrace *stack) { + u8 old_chunk_state = CHUNK_ALLOCATED; + // Flip the chunk_state atomically to avoid race on double-free. + if (!atomic_compare_exchange_strong((atomic_uint8_t *)m, &old_chunk_state, + CHUNK_QUARANTINE, + memory_order_acquire)) { + ReportInvalidFree(ptr, old_chunk_state, stack); + // It's not safe to push a chunk in quarantine on invalid free. + return false; + } + CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); + return true; + } + + // Expects the chunk to already be marked as quarantined by using + // AtomicallySetQuarantineFlagIfAllocated. + void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack) { + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + CHECK_GE(m->alloc_tid, 0); + if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area. + CHECK_EQ(m->free_tid, kInvalidTid); + AsanThread *t = GetCurrentThread(); + m->free_tid = t ? t->tid() : 0; + m->free_context_id = StackDepotPut(*stack); + + Flags &fl = *flags(); + if (fl.max_free_fill_size > 0) { + // We have to skip the chunk header, it contains free_context_id. + uptr scribble_start = (uptr)m + kChunkHeaderSize + kChunkHeader2Size; + if (m->UsedSize() >= kChunkHeader2Size) { // Skip Header2 in user area. + uptr size_to_fill = m->UsedSize() - kChunkHeader2Size; + size_to_fill = Min(size_to_fill, (uptr)fl.max_free_fill_size); + REAL(memset)((void *)scribble_start, fl.free_fill_byte, size_to_fill); + } + } + + // Poison the region. + PoisonShadow(m->Beg(), + RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), + kAsanHeapFreeMagic); + + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.frees++; + thread_stats.freed += m->UsedSize(); + + // Push into quarantine. + if (t) { + AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); + AllocatorCache *ac = GetAllocatorCache(ms); + quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac, stack), m, + m->UsedSize()); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *ac = &fallback_allocator_cache; + quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac, stack), + m, m->UsedSize()); + } + } + + void Deallocate(void *ptr, uptr delete_size, uptr delete_alignment, + BufferedStackTrace *stack, AllocType alloc_type) { + uptr p = reinterpret_cast(ptr); + if (p == 0) return; + + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + + // On Windows, uninstrumented DLLs may allocate memory before ASan hooks + // malloc. Don't report an invalid free in this case. + if (SANITIZER_WINDOWS && + !get_allocator().PointerIsMine(ptr)) { + if (!IsSystemHeapAddress(p)) + ReportFreeNotMalloced(p, stack); + return; + } + + ASAN_FREE_HOOK(ptr); + + // Must mark the chunk as quarantined before any changes to its metadata. + // Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag. + if (!AtomicallySetQuarantineFlagIfAllocated(m, ptr, stack)) return; + + if (m->alloc_type != alloc_type) { + if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) { + ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type, + (AllocType)alloc_type); + } + } else { + if (flags()->new_delete_type_mismatch && + (alloc_type == FROM_NEW || alloc_type == FROM_NEW_BR) && + ((delete_size && delete_size != m->UsedSize()) || + ComputeUserRequestedAlignmentLog(delete_alignment) != + m->user_requested_alignment_log)) { + ReportNewDeleteTypeMismatch(p, delete_size, delete_alignment, stack); + } + } + + QuarantineChunk(m, ptr, stack); + } + + void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) { + CHECK(old_ptr && new_size); + uptr p = reinterpret_cast(old_ptr); + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.reallocs++; + thread_stats.realloced += new_size; + + void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); + if (new_ptr) { + u8 chunk_state = m->chunk_state; + if (chunk_state != CHUNK_ALLOCATED) + ReportInvalidFree(old_ptr, chunk_state, stack); + CHECK_NE(REAL(memcpy), nullptr); + uptr memcpy_size = Min(new_size, m->UsedSize()); + // If realloc() races with free(), we may start copying freed memory. + // However, we will report racy double-free later anyway. + REAL(memcpy)(new_ptr, old_ptr, memcpy_size); + Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC); + } + return new_ptr; + } + + void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { + if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { + if (AllocatorMayReturnNull()) + return nullptr; + ReportCallocOverflow(nmemb, size, stack); + } + void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); + // If the memory comes from the secondary allocator no need to clear it + // as it comes directly from mmap. + if (ptr && allocator.FromPrimary(ptr)) + REAL(memset)(ptr, 0, nmemb * size); + return ptr; + } + + void ReportInvalidFree(void *ptr, u8 chunk_state, BufferedStackTrace *stack) { + if (chunk_state == CHUNK_QUARANTINE) + ReportDoubleFree((uptr)ptr, stack); + else + ReportFreeNotMalloced((uptr)ptr, stack); + } + + void CommitBack(AsanThreadLocalMallocStorage *ms, BufferedStackTrace *stack) { + AllocatorCache *ac = GetAllocatorCache(ms); + quarantine.Drain(GetQuarantineCache(ms), QuarantineCallback(ac, stack)); + allocator.SwallowCache(ac); + } + + // -------------------------- Chunk lookup ---------------------- + + // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). + AsanChunk *GetAsanChunk(void *alloc_beg) { + if (!alloc_beg) return nullptr; + if (!allocator.FromPrimary(alloc_beg)) { + uptr *meta = reinterpret_cast(allocator.GetMetaData(alloc_beg)); + AsanChunk *m = reinterpret_cast(meta[1]); + return m; + } + uptr *alloc_magic = reinterpret_cast(alloc_beg); + if (alloc_magic[0] == kAllocBegMagic) + return reinterpret_cast(alloc_magic[1]); + return reinterpret_cast(alloc_beg); + } + + AsanChunk *GetAsanChunkByAddr(uptr p) { + void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast(p)); + return GetAsanChunk(alloc_beg); + } + + // Allocator must be locked when this function is called. + AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { + void *alloc_beg = + allocator.GetBlockBeginFastLocked(reinterpret_cast(p)); + return GetAsanChunk(alloc_beg); + } + + uptr AllocationSize(uptr p) { + AsanChunk *m = GetAsanChunkByAddr(p); + if (!m) return 0; + if (m->chunk_state != CHUNK_ALLOCATED) return 0; + if (m->Beg() != p) return 0; + return m->UsedSize(); + } + + AsanChunkView FindHeapChunkByAddress(uptr addr) { + AsanChunk *m1 = GetAsanChunkByAddr(addr); + if (!m1) return AsanChunkView(m1); + sptr offset = 0; + if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) { + // The address is in the chunk's left redzone, so maybe it is actually + // a right buffer overflow from the other chunk to the left. + // Search a bit to the left to see if there is another chunk. + AsanChunk *m2 = nullptr; + for (uptr l = 1; l < GetPageSizeCached(); l++) { + m2 = GetAsanChunkByAddr(addr - l); + if (m2 == m1) continue; // Still the same chunk. + break; + } + if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset)) + m1 = ChooseChunk(addr, m2, m1); + } + return AsanChunkView(m1); + } + + void Purge(BufferedStackTrace *stack) { + AsanThread *t = GetCurrentThread(); + if (t) { + AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); + quarantine.DrainAndRecycle(GetQuarantineCache(ms), + QuarantineCallback(GetAllocatorCache(ms), + stack)); + } + { + SpinMutexLock l(&fallback_mutex); + quarantine.DrainAndRecycle(&fallback_quarantine_cache, + QuarantineCallback(&fallback_allocator_cache, + stack)); + } + + allocator.ForceReleaseToOS(); + } + + void PrintStats() { + allocator.PrintStats(); + quarantine.PrintStats(); + } + + void ForceLock() { + allocator.ForceLock(); + fallback_mutex.Lock(); + } + + void ForceUnlock() { + fallback_mutex.Unlock(); + allocator.ForceUnlock(); + } +}; + +static Allocator instance(LINKER_INITIALIZED); + +static AsanAllocator &get_allocator() { + return instance.allocator; +} + +bool AsanChunkView::IsValid() const { + return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE; +} +bool AsanChunkView::IsAllocated() const { + return chunk_ && chunk_->chunk_state == CHUNK_ALLOCATED; +} +bool AsanChunkView::IsQuarantined() const { + return chunk_ && chunk_->chunk_state == CHUNK_QUARANTINE; +} +uptr AsanChunkView::Beg() const { return chunk_->Beg(); } +uptr AsanChunkView::End() const { return Beg() + UsedSize(); } +uptr AsanChunkView::UsedSize() const { return chunk_->UsedSize(); } +u32 AsanChunkView::UserRequestedAlignment() const { + return Allocator::ComputeUserAlignment(chunk_->user_requested_alignment_log); +} +uptr AsanChunkView::AllocTid() const { return chunk_->alloc_tid; } +uptr AsanChunkView::FreeTid() const { return chunk_->free_tid; } +AllocType AsanChunkView::GetAllocType() const { + return (AllocType)chunk_->alloc_type; +} + +static StackTrace GetStackTraceFromId(u32 id) { + CHECK(id); + StackTrace res = StackDepotGet(id); + CHECK(res.trace); + return res; +} + +u32 AsanChunkView::GetAllocStackId() const { return chunk_->alloc_context_id; } +u32 AsanChunkView::GetFreeStackId() const { return chunk_->free_context_id; } + +StackTrace AsanChunkView::GetAllocStack() const { + return GetStackTraceFromId(GetAllocStackId()); +} + +StackTrace AsanChunkView::GetFreeStack() const { + return GetStackTraceFromId(GetFreeStackId()); +} + +void InitializeAllocator(const AllocatorOptions &options) { + instance.InitLinkerInitialized(options); +} + +void ReInitializeAllocator(const AllocatorOptions &options) { + instance.ReInitialize(options); +} + +void GetAllocatorOptions(AllocatorOptions *options) { + instance.GetOptions(options); +} + +AsanChunkView FindHeapChunkByAddress(uptr addr) { + return instance.FindHeapChunkByAddress(addr); +} +AsanChunkView FindHeapChunkByAllocBeg(uptr addr) { + return AsanChunkView(instance.GetAsanChunk(reinterpret_cast(addr))); +} + +void AsanThreadLocalMallocStorage::CommitBack() { + GET_STACK_TRACE_MALLOC; + instance.CommitBack(this, &stack); +} + +void PrintInternalAllocatorStats() { + instance.PrintStats(); +} + +void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) { + instance.Deallocate(ptr, 0, 0, stack, alloc_type); +} + +void asan_delete(void *ptr, uptr size, uptr alignment, + BufferedStackTrace *stack, AllocType alloc_type) { + instance.Deallocate(ptr, size, alignment, stack, alloc_type); +} + +void *asan_malloc(uptr size, BufferedStackTrace *stack) { + return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true)); +} + +void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { + return SetErrnoOnNull(instance.Calloc(nmemb, size, stack)); +} + +void *asan_reallocarray(void *p, uptr nmemb, uptr size, + BufferedStackTrace *stack) { + if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { + errno = errno_ENOMEM; + if (AllocatorMayReturnNull()) + return nullptr; + ReportReallocArrayOverflow(nmemb, size, stack); + } + return asan_realloc(p, nmemb * size, stack); +} + +void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { + if (!p) + return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true)); + if (size == 0) { + if (flags()->allocator_frees_and_returns_null_on_realloc_zero) { + instance.Deallocate(p, 0, 0, stack, FROM_MALLOC); + return nullptr; + } + // Allocate a size of 1 if we shouldn't free() on Realloc to 0 + size = 1; + } + return SetErrnoOnNull(instance.Reallocate(p, size, stack)); +} + +void *asan_valloc(uptr size, BufferedStackTrace *stack) { + return SetErrnoOnNull( + instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true)); +} + +void *asan_pvalloc(uptr size, BufferedStackTrace *stack) { + uptr PageSize = GetPageSizeCached(); + if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) { + errno = errno_ENOMEM; + if (AllocatorMayReturnNull()) + return nullptr; + ReportPvallocOverflow(size, stack); + } + // pvalloc(0) should allocate one page. + size = size ? RoundUpTo(size, PageSize) : PageSize; + return SetErrnoOnNull( + instance.Allocate(size, PageSize, stack, FROM_MALLOC, true)); +} + +void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, + AllocType alloc_type) { + if (UNLIKELY(!IsPowerOfTwo(alignment))) { + errno = errno_EINVAL; + if (AllocatorMayReturnNull()) + return nullptr; + ReportInvalidAllocationAlignment(alignment, stack); + } + return SetErrnoOnNull( + instance.Allocate(size, alignment, stack, alloc_type, true)); +} + +void *asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack) { + if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) { + errno = errno_EINVAL; + if (AllocatorMayReturnNull()) + return nullptr; + ReportInvalidAlignedAllocAlignment(size, alignment, stack); + } + return SetErrnoOnNull( + instance.Allocate(size, alignment, stack, FROM_MALLOC, true)); +} + +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, + BufferedStackTrace *stack) { + if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) { + if (AllocatorMayReturnNull()) + return errno_EINVAL; + ReportInvalidPosixMemalignAlignment(alignment, stack); + } + void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC, true); + if (UNLIKELY(!ptr)) + // OOM error is already taken care of by Allocate. + return errno_ENOMEM; + CHECK(IsAligned((uptr)ptr, alignment)); + *memptr = ptr; + return 0; +} + +uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) { + if (!ptr) return 0; + uptr usable_size = instance.AllocationSize(reinterpret_cast(ptr)); + if (flags()->check_malloc_usable_size && (usable_size == 0)) { + GET_STACK_TRACE_FATAL(pc, bp); + ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); + } + return usable_size; +} + +uptr asan_mz_size(const void *ptr) { + return instance.AllocationSize(reinterpret_cast(ptr)); +} + +void asan_mz_force_lock() { + instance.ForceLock(); +} + +void asan_mz_force_unlock() { + instance.ForceUnlock(); +} + +void AsanSoftRssLimitExceededCallback(bool limit_exceeded) { + instance.SetRssLimitExceeded(limit_exceeded); +} + +} // namespace __asan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +void LockAllocator() { + __asan::get_allocator().ForceLock(); +} + +void UnlockAllocator() { + __asan::get_allocator().ForceUnlock(); +} + +void GetAllocatorGlobalRange(uptr *begin, uptr *end) { + *begin = (uptr)&__asan::get_allocator(); + *end = *begin + sizeof(__asan::get_allocator()); +} + +uptr PointsIntoChunk(void* p) { + uptr addr = reinterpret_cast(p); + __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(addr); + if (!m) return 0; + uptr chunk = m->Beg(); + if (m->chunk_state != __asan::CHUNK_ALLOCATED) + return 0; + if (m->AddrIsInside(addr, /*locked_version=*/true)) + return chunk; + if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true), + addr)) + return chunk; + return 0; +} + +uptr GetUserBegin(uptr chunk) { + __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk); + CHECK(m); + return m->Beg(); +} + +LsanMetadata::LsanMetadata(uptr chunk) { + metadata_ = reinterpret_cast(chunk - __asan::kChunkHeaderSize); +} + +bool LsanMetadata::allocated() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->chunk_state == __asan::CHUNK_ALLOCATED; +} + +ChunkTag LsanMetadata::tag() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return static_cast(m->lsan_tag); +} + +void LsanMetadata::set_tag(ChunkTag value) { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + m->lsan_tag = value; +} + +uptr LsanMetadata::requested_size() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->UsedSize(/*locked_version=*/true); +} + +u32 LsanMetadata::stack_trace_id() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->alloc_context_id; +} + +void ForEachChunk(ForEachChunkCallback callback, void *arg) { + __asan::get_allocator().ForEachChunk(callback, arg); +} + +IgnoreObjectResult IgnoreObjectLocked(const void *p) { + uptr addr = reinterpret_cast(p); + __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddr(addr); + if (!m) return kIgnoreObjectInvalid; + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) { + if (m->lsan_tag == kIgnored) + return kIgnoreObjectAlreadyIgnored; + m->lsan_tag = __lsan::kIgnored; + return kIgnoreObjectSuccess; + } else { + return kIgnoreObjectInvalid; + } +} +} // namespace __lsan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +// ASan allocator doesn't reserve extra bytes, so normally we would +// just return "size". We don't want to expose our redzone sizes, etc here. +uptr __sanitizer_get_estimated_allocated_size(uptr size) { + return size; +} + +int __sanitizer_get_ownership(const void *p) { + uptr ptr = reinterpret_cast(p); + return instance.AllocationSize(ptr) > 0; +} + +uptr __sanitizer_get_allocated_size(const void *p) { + if (!p) return 0; + uptr ptr = reinterpret_cast(p); + uptr allocated_size = instance.AllocationSize(ptr); + // Die if p is not malloced or if it is already freed. + if (allocated_size == 0) { + GET_STACK_TRACE_FATAL_HERE; + ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack); + } + return allocated_size; +} + +void __sanitizer_purge_allocator() { + GET_STACK_TRACE_MALLOC; + instance.Purge(&stack); +} + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +// Provide default (no-op) implementation of malloc hooks. +SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook, + void *ptr, uptr size) { + (void)ptr; + (void)size; +} + +SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) { + (void)ptr; +} +#endif diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h index 1f58bb16859..b37d8ef4e8d 100644 --- a/libsanitizer/asan/asan_allocator.h +++ b/libsanitizer/asan/asan_allocator.h @@ -1,13 +1,14 @@ //===-- asan_allocator.h ----------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_allocator.cc. +// ASan-private header for asan_allocator.cpp. //===----------------------------------------------------------------------===// #ifndef ASAN_ALLOCATOR_H @@ -137,9 +138,9 @@ typedef VeryCompactSizeClassMap SizeClassMap; const uptr kAllocatorSpace = 0x10000000000ULL; const uptr kAllocatorSize = 0x10000000000ULL; // 3T. typedef DefaultSizeClassMap SizeClassMap; -# elif defined(__sparc__) +#elif defined(__sparc__) const uptr kAllocatorSpace = ~(uptr)0; -const uptr kAllocatorSize = 0x20000000000ULL; // 2T. +const uptr kAllocatorSize = 0x20000000000ULL; // 2T. typedef DefaultSizeClassMap SizeClassMap; # elif SANITIZER_WINDOWS const uptr kAllocatorSpace = ~(uptr)0; @@ -150,6 +151,7 @@ const uptr kAllocatorSpace = 0x600000000000ULL; const uptr kAllocatorSize = 0x40000000000ULL; // 4T. typedef DefaultSizeClassMap SizeClassMap; # endif +template struct AP64 { // Allocator64 parameters. Deliberately using a short name. static const uptr kSpaceBeg = kAllocatorSpace; static const uptr kSpaceSize = kAllocatorSize; @@ -157,37 +159,37 @@ struct AP64 { // Allocator64 parameters. Deliberately using a short name. typedef __asan::SizeClassMap SizeClassMap; typedef AsanMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; + using AddressSpaceView = AddressSpaceViewTy; }; -typedef SizeClassAllocator64 PrimaryAllocator; +template +using PrimaryAllocatorASVT = SizeClassAllocator64>; +using PrimaryAllocator = PrimaryAllocatorASVT; #else // Fallback to SizeClassAllocator32. -static const uptr kRegionSizeLog = 20; -static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; -# if SANITIZER_WORDSIZE == 32 -typedef FlatByteMap ByteMap; -# elif SANITIZER_WORDSIZE == 64 -typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; -# endif typedef CompactSizeClassMap SizeClassMap; +template struct AP32 { static const uptr kSpaceBeg = 0; static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE; static const uptr kMetadataSize = 16; typedef __asan::SizeClassMap SizeClassMap; - static const uptr kRegionSizeLog = __asan::kRegionSizeLog; - typedef __asan::ByteMap ByteMap; + static const uptr kRegionSizeLog = 20; + using AddressSpaceView = AddressSpaceViewTy; typedef AsanMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; -typedef SizeClassAllocator32 PrimaryAllocator; +template +using PrimaryAllocatorASVT = SizeClassAllocator32 >; +using PrimaryAllocator = PrimaryAllocatorASVT; #endif // SANITIZER_CAN_USE_ALLOCATOR64 static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses; -typedef SizeClassAllocatorLocalCache AllocatorCache; -typedef LargeMmapAllocator SecondaryAllocator; -typedef CombinedAllocator AsanAllocator; +template +using AsanAllocatorASVT = + CombinedAllocator>; +using AsanAllocator = AsanAllocatorASVT; +using AllocatorCache = AsanAllocator::AllocatorCache; struct AsanThreadLocalMallocStorage { uptr quarantine_cache[16]; @@ -207,6 +209,8 @@ void asan_delete(void *ptr, uptr size, uptr alignment, void *asan_malloc(uptr size, BufferedStackTrace *stack); void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack); void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack); +void *asan_reallocarray(void *p, uptr nmemb, uptr size, + BufferedStackTrace *stack); void *asan_valloc(uptr size, BufferedStackTrace *stack); void *asan_pvalloc(uptr size, BufferedStackTrace *stack); diff --git a/libsanitizer/asan/asan_debugging.cc b/libsanitizer/asan/asan_debugging.cc deleted file mode 100644 index 075af3375cd..00000000000 --- a/libsanitizer/asan/asan_debugging.cc +++ /dev/null @@ -1,145 +0,0 @@ -//===-- asan_debugging.cc -------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// This file contains various functions that are generally useful to call when -// using a debugger (LLDB, GDB). -//===----------------------------------------------------------------------===// - -#include "asan_allocator.h" -#include "asan_descriptions.h" -#include "asan_flags.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "asan_report.h" -#include "asan_thread.h" - -namespace { -using namespace __asan; - -static void FindInfoForStackVar(uptr addr, const char *frame_descr, uptr offset, - char *name, uptr name_size, - uptr ®ion_address, uptr ®ion_size) { - InternalMmapVector vars; - vars.reserve(16); - if (!ParseFrameDescription(frame_descr, &vars)) { - return; - } - - for (uptr i = 0; i < vars.size(); i++) { - if (offset <= vars[i].beg + vars[i].size) { - // We use name_len + 1 because strlcpy will guarantee a \0 at the end, so - // if we're limiting the copy due to name_len, we add 1 to ensure we copy - // the whole name and then terminate with '\0'. - internal_strlcpy(name, vars[i].name_pos, - Min(name_size, vars[i].name_len + 1)); - region_address = addr - (offset - vars[i].beg); - region_size = vars[i].size; - return; - } - } -} - -uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id, - bool alloc_stack) { - AsanChunkView chunk = FindHeapChunkByAddress(addr); - if (!chunk.IsValid()) return 0; - - StackTrace stack(nullptr, 0); - if (alloc_stack) { - if (chunk.AllocTid() == kInvalidTid) return 0; - stack = chunk.GetAllocStack(); - if (thread_id) *thread_id = chunk.AllocTid(); - } else { - if (chunk.FreeTid() == kInvalidTid) return 0; - stack = chunk.GetFreeStack(); - if (thread_id) *thread_id = chunk.FreeTid(); - } - - if (trace && size) { - size = Min(size, Min(stack.size, kStackTraceMax)); - for (uptr i = 0; i < size; i++) - trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]); - - return size; - } - - return 0; -} - -} // namespace - -SANITIZER_INTERFACE_ATTRIBUTE -const char *__asan_locate_address(uptr addr, char *name, uptr name_size, - uptr *region_address_ptr, - uptr *region_size_ptr) { - AddressDescription descr(addr); - uptr region_address = 0; - uptr region_size = 0; - const char *region_kind = nullptr; - if (name && name_size > 0) name[0] = 0; - - if (auto shadow = descr.AsShadow()) { - // region_{address,size} are already 0 - switch (shadow->kind) { - case kShadowKindLow: - region_kind = "low shadow"; - break; - case kShadowKindGap: - region_kind = "shadow gap"; - break; - case kShadowKindHigh: - region_kind = "high shadow"; - break; - } - } else if (auto heap = descr.AsHeap()) { - region_kind = "heap"; - region_address = heap->chunk_access.chunk_begin; - region_size = heap->chunk_access.chunk_size; - } else if (auto stack = descr.AsStack()) { - region_kind = "stack"; - if (!stack->frame_descr) { - // region_{address,size} are already 0 - } else { - FindInfoForStackVar(addr, stack->frame_descr, stack->offset, name, - name_size, region_address, region_size); - } - } else if (auto global = descr.AsGlobal()) { - region_kind = "global"; - auto &g = global->globals[0]; - internal_strlcpy(name, g.name, name_size); - region_address = g.beg; - region_size = g.size; - } else { - // region_{address,size} are already 0 - region_kind = "heap-invalid"; - } - - CHECK(region_kind); - if (region_address_ptr) *region_address_ptr = region_address; - if (region_size_ptr) *region_size_ptr = region_size; - return region_kind; -} - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) { - return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true); -} - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) { - return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ false); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset) { - if (shadow_scale) - *shadow_scale = SHADOW_SCALE; - if (shadow_offset) - *shadow_offset = SHADOW_OFFSET; -} diff --git a/libsanitizer/asan/asan_debugging.cpp b/libsanitizer/asan/asan_debugging.cpp new file mode 100644 index 00000000000..3fc15adf7b8 --- /dev/null +++ b/libsanitizer/asan/asan_debugging.cpp @@ -0,0 +1,146 @@ +//===-- asan_debugging.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file contains various functions that are generally useful to call when +// using a debugger (LLDB, GDB). +//===----------------------------------------------------------------------===// + +#include "asan_allocator.h" +#include "asan_descriptions.h" +#include "asan_flags.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_thread.h" + +namespace { +using namespace __asan; + +static void FindInfoForStackVar(uptr addr, const char *frame_descr, uptr offset, + char *name, uptr name_size, + uptr ®ion_address, uptr ®ion_size) { + InternalMmapVector vars; + vars.reserve(16); + if (!ParseFrameDescription(frame_descr, &vars)) { + return; + } + + for (uptr i = 0; i < vars.size(); i++) { + if (offset <= vars[i].beg + vars[i].size) { + // We use name_len + 1 because strlcpy will guarantee a \0 at the end, so + // if we're limiting the copy due to name_len, we add 1 to ensure we copy + // the whole name and then terminate with '\0'. + internal_strlcpy(name, vars[i].name_pos, + Min(name_size, vars[i].name_len + 1)); + region_address = addr - (offset - vars[i].beg); + region_size = vars[i].size; + return; + } + } +} + +uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id, + bool alloc_stack) { + AsanChunkView chunk = FindHeapChunkByAddress(addr); + if (!chunk.IsValid()) return 0; + + StackTrace stack(nullptr, 0); + if (alloc_stack) { + if (chunk.AllocTid() == kInvalidTid) return 0; + stack = chunk.GetAllocStack(); + if (thread_id) *thread_id = chunk.AllocTid(); + } else { + if (chunk.FreeTid() == kInvalidTid) return 0; + stack = chunk.GetFreeStack(); + if (thread_id) *thread_id = chunk.FreeTid(); + } + + if (trace && size) { + size = Min(size, Min(stack.size, kStackTraceMax)); + for (uptr i = 0; i < size; i++) + trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]); + + return size; + } + + return 0; +} + +} // namespace + +SANITIZER_INTERFACE_ATTRIBUTE +const char *__asan_locate_address(uptr addr, char *name, uptr name_size, + uptr *region_address_ptr, + uptr *region_size_ptr) { + AddressDescription descr(addr); + uptr region_address = 0; + uptr region_size = 0; + const char *region_kind = nullptr; + if (name && name_size > 0) name[0] = 0; + + if (auto shadow = descr.AsShadow()) { + // region_{address,size} are already 0 + switch (shadow->kind) { + case kShadowKindLow: + region_kind = "low shadow"; + break; + case kShadowKindGap: + region_kind = "shadow gap"; + break; + case kShadowKindHigh: + region_kind = "high shadow"; + break; + } + } else if (auto heap = descr.AsHeap()) { + region_kind = "heap"; + region_address = heap->chunk_access.chunk_begin; + region_size = heap->chunk_access.chunk_size; + } else if (auto stack = descr.AsStack()) { + region_kind = "stack"; + if (!stack->frame_descr) { + // region_{address,size} are already 0 + } else { + FindInfoForStackVar(addr, stack->frame_descr, stack->offset, name, + name_size, region_address, region_size); + } + } else if (auto global = descr.AsGlobal()) { + region_kind = "global"; + auto &g = global->globals[0]; + internal_strlcpy(name, g.name, name_size); + region_address = g.beg; + region_size = g.size; + } else { + // region_{address,size} are already 0 + region_kind = "heap-invalid"; + } + + CHECK(region_kind); + if (region_address_ptr) *region_address_ptr = region_address; + if (region_size_ptr) *region_size_ptr = region_size; + return region_kind; +} + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) { + return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true); +} + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) { + return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ false); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset) { + if (shadow_scale) + *shadow_scale = SHADOW_SCALE; + if (shadow_offset) + *shadow_offset = SHADOW_OFFSET; +} diff --git a/libsanitizer/asan/asan_descriptions.cc b/libsanitizer/asan/asan_descriptions.cc deleted file mode 100644 index 99f226da899..00000000000 --- a/libsanitizer/asan/asan_descriptions.cc +++ /dev/null @@ -1,500 +0,0 @@ -//===-- asan_descriptions.cc ------------------------------------*- C++ -*-===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// ASan functions for getting information about an address and/or printing it. -//===----------------------------------------------------------------------===// - -#include "asan_descriptions.h" -#include "asan_mapping.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "sanitizer_common/sanitizer_stackdepot.h" - -namespace __asan { - -AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) { - Init(t->tid, t->name); -} - -AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) { - if (tid == kInvalidTid) { - Init(tid, ""); - } else { - asanThreadRegistry().CheckLocked(); - AsanThreadContext *t = GetThreadContextByTidLocked(tid); - Init(tid, t->name); - } -} - -void AsanThreadIdAndName::Init(u32 tid, const char *tname) { - int len = internal_snprintf(name, sizeof(name), "T%d", tid); - CHECK(((unsigned int)len) < sizeof(name)); - if (tname[0] != '\0') - internal_snprintf(&name[len], sizeof(name) - len, " (%s)", tname); -} - -void DescribeThread(AsanThreadContext *context) { - CHECK(context); - asanThreadRegistry().CheckLocked(); - // No need to announce the main thread. - if (context->tid == 0 || context->announced) { - return; - } - context->announced = true; - InternalScopedString str(1024); - str.append("Thread %s", AsanThreadIdAndName(context).c_str()); - if (context->parent_tid == kInvalidTid) { - str.append(" created by unknown thread\n"); - Printf("%s", str.data()); - return; - } - str.append(" created by %s here:\n", - AsanThreadIdAndName(context->parent_tid).c_str()); - Printf("%s", str.data()); - StackDepotGet(context->stack_id).Print(); - // Recursively described parent thread if needed. - if (flags()->print_full_thread_history) { - AsanThreadContext *parent_context = - GetThreadContextByTidLocked(context->parent_tid); - DescribeThread(parent_context); - } -} - -// Shadow descriptions -static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) { - CHECK(!AddrIsInMem(addr)); - if (AddrIsInShadowGap(addr)) { - *shadow_kind = kShadowKindGap; - } else if (AddrIsInHighShadow(addr)) { - *shadow_kind = kShadowKindHigh; - } else if (AddrIsInLowShadow(addr)) { - *shadow_kind = kShadowKindLow; - } else { - CHECK(0 && "Address is not in memory and not in shadow?"); - return false; - } - return true; -} - -bool DescribeAddressIfShadow(uptr addr) { - ShadowAddressDescription descr; - if (!GetShadowAddressInformation(addr, &descr)) return false; - descr.Print(); - return true; -} - -bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) { - if (AddrIsInMem(addr)) return false; - ShadowKind shadow_kind; - if (!GetShadowKind(addr, &shadow_kind)) return false; - if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr; - descr->addr = addr; - descr->kind = shadow_kind; - return true; -} - -// Heap descriptions -static void GetAccessToHeapChunkInformation(ChunkAccess *descr, - AsanChunkView chunk, uptr addr, - uptr access_size) { - descr->bad_addr = addr; - if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) { - descr->access_type = kAccessTypeLeft; - } else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) { - descr->access_type = kAccessTypeRight; - if (descr->offset < 0) { - descr->bad_addr -= descr->offset; - descr->offset = 0; - } - } else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) { - descr->access_type = kAccessTypeInside; - } else { - descr->access_type = kAccessTypeUnknown; - } - descr->chunk_begin = chunk.Beg(); - descr->chunk_size = chunk.UsedSize(); - descr->user_requested_alignment = chunk.UserRequestedAlignment(); - descr->alloc_type = chunk.GetAllocType(); -} - -static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) { - Decorator d; - InternalScopedString str(4096); - str.append("%s", d.Location()); - switch (descr.access_type) { - case kAccessTypeLeft: - str.append("%p is located %zd bytes to the left of", - (void *)descr.bad_addr, descr.offset); - break; - case kAccessTypeRight: - str.append("%p is located %zd bytes to the right of", - (void *)descr.bad_addr, descr.offset); - break; - case kAccessTypeInside: - str.append("%p is located %zd bytes inside of", (void *)descr.bad_addr, - descr.offset); - break; - case kAccessTypeUnknown: - str.append( - "%p is located somewhere around (this is AddressSanitizer bug!)", - (void *)descr.bad_addr); - } - str.append(" %zu-byte region [%p,%p)\n", descr.chunk_size, - (void *)descr.chunk_begin, - (void *)(descr.chunk_begin + descr.chunk_size)); - str.append("%s", d.Default()); - Printf("%s", str.data()); -} - -bool GetHeapAddressInformation(uptr addr, uptr access_size, - HeapAddressDescription *descr) { - AsanChunkView chunk = FindHeapChunkByAddress(addr); - if (!chunk.IsValid()) { - return false; - } - descr->addr = addr; - GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr, - access_size); - CHECK_NE(chunk.AllocTid(), kInvalidTid); - descr->alloc_tid = chunk.AllocTid(); - descr->alloc_stack_id = chunk.GetAllocStackId(); - descr->free_tid = chunk.FreeTid(); - if (descr->free_tid != kInvalidTid) - descr->free_stack_id = chunk.GetFreeStackId(); - return true; -} - -static StackTrace GetStackTraceFromId(u32 id) { - CHECK(id); - StackTrace res = StackDepotGet(id); - CHECK(res.trace); - return res; -} - -bool DescribeAddressIfHeap(uptr addr, uptr access_size) { - HeapAddressDescription descr; - if (!GetHeapAddressInformation(addr, access_size, &descr)) { - Printf( - "AddressSanitizer can not describe address in more detail " - "(wild memory access suspected).\n"); - return false; - } - descr.Print(); - return true; -} - -// Stack descriptions -bool GetStackAddressInformation(uptr addr, uptr access_size, - StackAddressDescription *descr) { - AsanThread *t = FindThreadByStackAddress(addr); - if (!t) return false; - - descr->addr = addr; - descr->tid = t->tid(); - // Try to fetch precise stack frame for this access. - AsanThread::StackFrameAccess access; - if (!t->GetStackFrameAccessByAddr(addr, &access)) { - descr->frame_descr = nullptr; - return true; - } - - descr->offset = access.offset; - descr->access_size = access_size; - descr->frame_pc = access.frame_pc; - descr->frame_descr = access.frame_descr; - -#if SANITIZER_PPC64V1 - // On PowerPC64 ELFv1, the address of a function actually points to a - // three-doubleword data structure with the first field containing - // the address of the function's code. - descr->frame_pc = *reinterpret_cast(descr->frame_pc); -#endif - descr->frame_pc += 16; - - return true; -} - -static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, - uptr access_size, uptr prev_var_end, - uptr next_var_beg) { - uptr var_end = var.beg + var.size; - uptr addr_end = addr + access_size; - const char *pos_descr = nullptr; - // If the variable [var.beg, var_end) is the nearest variable to the - // current memory access, indicate it in the log. - if (addr >= var.beg) { - if (addr_end <= var_end) - pos_descr = "is inside"; // May happen if this is a use-after-return. - else if (addr < var_end) - pos_descr = "partially overflows"; - else if (addr_end <= next_var_beg && - next_var_beg - addr_end >= addr - var_end) - pos_descr = "overflows"; - } else { - if (addr_end > var.beg) - pos_descr = "partially underflows"; - else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end) - pos_descr = "underflows"; - } - InternalScopedString str(1024); - str.append(" [%zd, %zd)", var.beg, var_end); - // Render variable name. - str.append(" '"); - for (uptr i = 0; i < var.name_len; ++i) { - str.append("%c", var.name_pos[i]); - } - str.append("'"); - if (var.line > 0) { - str.append(" (line %d)", var.line); - } - if (pos_descr) { - Decorator d; - // FIXME: we may want to also print the size of the access here, - // but in case of accesses generated by memset it may be confusing. - str.append("%s <== Memory access at offset %zd %s this variable%s\n", - d.Location(), addr, pos_descr, d.Default()); - } else { - str.append("\n"); - } - Printf("%s", str.data()); -} - -bool DescribeAddressIfStack(uptr addr, uptr access_size) { - StackAddressDescription descr; - if (!GetStackAddressInformation(addr, access_size, &descr)) return false; - descr.Print(); - return true; -} - -// Global descriptions -static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, - const __asan_global &g) { - InternalScopedString str(4096); - Decorator d; - str.append("%s", d.Location()); - if (addr < g.beg) { - str.append("%p is located %zd bytes to the left", (void *)addr, - g.beg - addr); - } else if (addr + access_size > g.beg + g.size) { - if (addr < g.beg + g.size) addr = g.beg + g.size; - str.append("%p is located %zd bytes to the right", (void *)addr, - addr - (g.beg + g.size)); - } else { - // Can it happen? - str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg); - } - str.append(" of global variable '%s' defined in '", - MaybeDemangleGlobalName(g.name)); - PrintGlobalLocation(&str, g); - str.append("' (0x%zx) of size %zu\n", g.beg, g.size); - str.append("%s", d.Default()); - PrintGlobalNameIfASCII(&str, g); - Printf("%s", str.data()); -} - -bool GetGlobalAddressInformation(uptr addr, uptr access_size, - GlobalAddressDescription *descr) { - descr->addr = addr; - int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites, - ARRAY_SIZE(descr->globals)); - descr->size = globals_num; - descr->access_size = access_size; - return globals_num != 0; -} - -bool DescribeAddressIfGlobal(uptr addr, uptr access_size, - const char *bug_type) { - GlobalAddressDescription descr; - if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false; - - descr.Print(bug_type); - return true; -} - -void ShadowAddressDescription::Print() const { - Printf("Address %p is located in the %s area.\n", addr, ShadowNames[kind]); -} - -void GlobalAddressDescription::Print(const char *bug_type) const { - for (int i = 0; i < size; i++) { - DescribeAddressRelativeToGlobal(addr, access_size, globals[i]); - if (bug_type && - 0 == internal_strcmp(bug_type, "initialization-order-fiasco") && - reg_sites[i]) { - Printf(" registered at:\n"); - StackDepotGet(reg_sites[i]).Print(); - } - } -} - -bool GlobalAddressDescription::PointsInsideTheSameVariable( - const GlobalAddressDescription &other) const { - if (size == 0 || other.size == 0) return false; - - for (uptr i = 0; i < size; i++) { - const __asan_global &a = globals[i]; - for (uptr j = 0; j < other.size; j++) { - const __asan_global &b = other.globals[j]; - if (a.beg == b.beg && - a.beg <= addr && - b.beg <= other.addr && - (addr + access_size) < (a.beg + a.size) && - (other.addr + other.access_size) < (b.beg + b.size)) - return true; - } - } - - return false; -} - -void StackAddressDescription::Print() const { - Decorator d; - Printf("%s", d.Location()); - Printf("Address %p is located in stack of thread %s", addr, - AsanThreadIdAndName(tid).c_str()); - - if (!frame_descr) { - Printf("%s\n", d.Default()); - return; - } - Printf(" at offset %zu in frame%s\n", offset, d.Default()); - - // Now we print the frame where the alloca has happened. - // We print this frame as a stack trace with one element. - // The symbolizer may print more than one frame if inlining was involved. - // The frame numbers may be different than those in the stack trace printed - // previously. That's unfortunate, but I have no better solution, - // especially given that the alloca may be from entirely different place - // (e.g. use-after-scope, or different thread's stack). - Printf("%s", d.Default()); - StackTrace alloca_stack(&frame_pc, 1); - alloca_stack.Print(); - - InternalMmapVector vars; - vars.reserve(16); - if (!ParseFrameDescription(frame_descr, &vars)) { - Printf( - "AddressSanitizer can't parse the stack frame " - "descriptor: |%s|\n", - frame_descr); - // 'addr' is a stack address, so return true even if we can't parse frame - return; - } - uptr n_objects = vars.size(); - // Report the number of stack objects. - Printf(" This frame has %zu object(s):\n", n_objects); - - // Report all objects in this frame. - for (uptr i = 0; i < n_objects; i++) { - uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; - uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); - PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end, - next_var_beg); - } - Printf( - "HINT: this may be a false positive if your program uses " - "some custom stack unwind mechanism, swapcontext or vfork\n"); - if (SANITIZER_WINDOWS) - Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n"); - else - Printf(" (longjmp and C++ exceptions *are* supported)\n"); - - DescribeThread(GetThreadContextByTidLocked(tid)); -} - -void HeapAddressDescription::Print() const { - PrintHeapChunkAccess(addr, chunk_access); - - asanThreadRegistry().CheckLocked(); - AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid); - StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id); - - Decorator d; - AsanThreadContext *free_thread = nullptr; - if (free_tid != kInvalidTid) { - free_thread = GetThreadContextByTidLocked(free_tid); - Printf("%sfreed by thread %s here:%s\n", d.Allocation(), - AsanThreadIdAndName(free_thread).c_str(), d.Default()); - StackTrace free_stack = GetStackTraceFromId(free_stack_id); - free_stack.Print(); - Printf("%spreviously allocated by thread %s here:%s\n", d.Allocation(), - AsanThreadIdAndName(alloc_thread).c_str(), d.Default()); - } else { - Printf("%sallocated by thread %s here:%s\n", d.Allocation(), - AsanThreadIdAndName(alloc_thread).c_str(), d.Default()); - } - alloc_stack.Print(); - DescribeThread(GetCurrentThread()); - if (free_thread) DescribeThread(free_thread); - DescribeThread(alloc_thread); -} - -AddressDescription::AddressDescription(uptr addr, uptr access_size, - bool shouldLockThreadRegistry) { - if (GetShadowAddressInformation(addr, &data.shadow)) { - data.kind = kAddressKindShadow; - return; - } - if (GetHeapAddressInformation(addr, access_size, &data.heap)) { - data.kind = kAddressKindHeap; - return; - } - - bool isStackMemory = false; - if (shouldLockThreadRegistry) { - ThreadRegistryLock l(&asanThreadRegistry()); - isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack); - } else { - isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack); - } - if (isStackMemory) { - data.kind = kAddressKindStack; - return; - } - - if (GetGlobalAddressInformation(addr, access_size, &data.global)) { - data.kind = kAddressKindGlobal; - return; - } - data.kind = kAddressKindWild; - addr = 0; -} - -void PrintAddressDescription(uptr addr, uptr access_size, - const char *bug_type) { - ShadowAddressDescription shadow_descr; - if (GetShadowAddressInformation(addr, &shadow_descr)) { - shadow_descr.Print(); - return; - } - - GlobalAddressDescription global_descr; - if (GetGlobalAddressInformation(addr, access_size, &global_descr)) { - global_descr.Print(bug_type); - return; - } - - StackAddressDescription stack_descr; - if (GetStackAddressInformation(addr, access_size, &stack_descr)) { - stack_descr.Print(); - return; - } - - HeapAddressDescription heap_descr; - if (GetHeapAddressInformation(addr, access_size, &heap_descr)) { - heap_descr.Print(); - return; - } - - // We exhausted our possibilities. Bail out. - Printf( - "AddressSanitizer can not describe address in more detail " - "(wild memory access suspected).\n"); -} -} // namespace __asan diff --git a/libsanitizer/asan/asan_descriptions.cpp b/libsanitizer/asan/asan_descriptions.cpp new file mode 100644 index 00000000000..153c874a4e7 --- /dev/null +++ b/libsanitizer/asan/asan_descriptions.cpp @@ -0,0 +1,501 @@ +//===-- asan_descriptions.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan functions for getting information about an address and/or printing it. +//===----------------------------------------------------------------------===// + +#include "asan_descriptions.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + +namespace __asan { + +AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) { + Init(t->tid, t->name); +} + +AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) { + if (tid == kInvalidTid) { + Init(tid, ""); + } else { + asanThreadRegistry().CheckLocked(); + AsanThreadContext *t = GetThreadContextByTidLocked(tid); + Init(tid, t->name); + } +} + +void AsanThreadIdAndName::Init(u32 tid, const char *tname) { + int len = internal_snprintf(name, sizeof(name), "T%d", tid); + CHECK(((unsigned int)len) < sizeof(name)); + if (tname[0] != '\0') + internal_snprintf(&name[len], sizeof(name) - len, " (%s)", tname); +} + +void DescribeThread(AsanThreadContext *context) { + CHECK(context); + asanThreadRegistry().CheckLocked(); + // No need to announce the main thread. + if (context->tid == 0 || context->announced) { + return; + } + context->announced = true; + InternalScopedString str(1024); + str.append("Thread %s", AsanThreadIdAndName(context).c_str()); + if (context->parent_tid == kInvalidTid) { + str.append(" created by unknown thread\n"); + Printf("%s", str.data()); + return; + } + str.append(" created by %s here:\n", + AsanThreadIdAndName(context->parent_tid).c_str()); + Printf("%s", str.data()); + StackDepotGet(context->stack_id).Print(); + // Recursively described parent thread if needed. + if (flags()->print_full_thread_history) { + AsanThreadContext *parent_context = + GetThreadContextByTidLocked(context->parent_tid); + DescribeThread(parent_context); + } +} + +// Shadow descriptions +static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) { + CHECK(!AddrIsInMem(addr)); + if (AddrIsInShadowGap(addr)) { + *shadow_kind = kShadowKindGap; + } else if (AddrIsInHighShadow(addr)) { + *shadow_kind = kShadowKindHigh; + } else if (AddrIsInLowShadow(addr)) { + *shadow_kind = kShadowKindLow; + } else { + CHECK(0 && "Address is not in memory and not in shadow?"); + return false; + } + return true; +} + +bool DescribeAddressIfShadow(uptr addr) { + ShadowAddressDescription descr; + if (!GetShadowAddressInformation(addr, &descr)) return false; + descr.Print(); + return true; +} + +bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) { + if (AddrIsInMem(addr)) return false; + ShadowKind shadow_kind; + if (!GetShadowKind(addr, &shadow_kind)) return false; + if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr; + descr->addr = addr; + descr->kind = shadow_kind; + return true; +} + +// Heap descriptions +static void GetAccessToHeapChunkInformation(ChunkAccess *descr, + AsanChunkView chunk, uptr addr, + uptr access_size) { + descr->bad_addr = addr; + if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) { + descr->access_type = kAccessTypeLeft; + } else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) { + descr->access_type = kAccessTypeRight; + if (descr->offset < 0) { + descr->bad_addr -= descr->offset; + descr->offset = 0; + } + } else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) { + descr->access_type = kAccessTypeInside; + } else { + descr->access_type = kAccessTypeUnknown; + } + descr->chunk_begin = chunk.Beg(); + descr->chunk_size = chunk.UsedSize(); + descr->user_requested_alignment = chunk.UserRequestedAlignment(); + descr->alloc_type = chunk.GetAllocType(); +} + +static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) { + Decorator d; + InternalScopedString str(4096); + str.append("%s", d.Location()); + switch (descr.access_type) { + case kAccessTypeLeft: + str.append("%p is located %zd bytes to the left of", + (void *)descr.bad_addr, descr.offset); + break; + case kAccessTypeRight: + str.append("%p is located %zd bytes to the right of", + (void *)descr.bad_addr, descr.offset); + break; + case kAccessTypeInside: + str.append("%p is located %zd bytes inside of", (void *)descr.bad_addr, + descr.offset); + break; + case kAccessTypeUnknown: + str.append( + "%p is located somewhere around (this is AddressSanitizer bug!)", + (void *)descr.bad_addr); + } + str.append(" %zu-byte region [%p,%p)\n", descr.chunk_size, + (void *)descr.chunk_begin, + (void *)(descr.chunk_begin + descr.chunk_size)); + str.append("%s", d.Default()); + Printf("%s", str.data()); +} + +bool GetHeapAddressInformation(uptr addr, uptr access_size, + HeapAddressDescription *descr) { + AsanChunkView chunk = FindHeapChunkByAddress(addr); + if (!chunk.IsValid()) { + return false; + } + descr->addr = addr; + GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr, + access_size); + CHECK_NE(chunk.AllocTid(), kInvalidTid); + descr->alloc_tid = chunk.AllocTid(); + descr->alloc_stack_id = chunk.GetAllocStackId(); + descr->free_tid = chunk.FreeTid(); + if (descr->free_tid != kInvalidTid) + descr->free_stack_id = chunk.GetFreeStackId(); + return true; +} + +static StackTrace GetStackTraceFromId(u32 id) { + CHECK(id); + StackTrace res = StackDepotGet(id); + CHECK(res.trace); + return res; +} + +bool DescribeAddressIfHeap(uptr addr, uptr access_size) { + HeapAddressDescription descr; + if (!GetHeapAddressInformation(addr, access_size, &descr)) { + Printf( + "AddressSanitizer can not describe address in more detail " + "(wild memory access suspected).\n"); + return false; + } + descr.Print(); + return true; +} + +// Stack descriptions +bool GetStackAddressInformation(uptr addr, uptr access_size, + StackAddressDescription *descr) { + AsanThread *t = FindThreadByStackAddress(addr); + if (!t) return false; + + descr->addr = addr; + descr->tid = t->tid(); + // Try to fetch precise stack frame for this access. + AsanThread::StackFrameAccess access; + if (!t->GetStackFrameAccessByAddr(addr, &access)) { + descr->frame_descr = nullptr; + return true; + } + + descr->offset = access.offset; + descr->access_size = access_size; + descr->frame_pc = access.frame_pc; + descr->frame_descr = access.frame_descr; + +#if SANITIZER_PPC64V1 + // On PowerPC64 ELFv1, the address of a function actually points to a + // three-doubleword data structure with the first field containing + // the address of the function's code. + descr->frame_pc = *reinterpret_cast(descr->frame_pc); +#endif + descr->frame_pc += 16; + + return true; +} + +static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, + uptr access_size, uptr prev_var_end, + uptr next_var_beg) { + uptr var_end = var.beg + var.size; + uptr addr_end = addr + access_size; + const char *pos_descr = nullptr; + // If the variable [var.beg, var_end) is the nearest variable to the + // current memory access, indicate it in the log. + if (addr >= var.beg) { + if (addr_end <= var_end) + pos_descr = "is inside"; // May happen if this is a use-after-return. + else if (addr < var_end) + pos_descr = "partially overflows"; + else if (addr_end <= next_var_beg && + next_var_beg - addr_end >= addr - var_end) + pos_descr = "overflows"; + } else { + if (addr_end > var.beg) + pos_descr = "partially underflows"; + else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end) + pos_descr = "underflows"; + } + InternalScopedString str(1024); + str.append(" [%zd, %zd)", var.beg, var_end); + // Render variable name. + str.append(" '"); + for (uptr i = 0; i < var.name_len; ++i) { + str.append("%c", var.name_pos[i]); + } + str.append("'"); + if (var.line > 0) { + str.append(" (line %d)", var.line); + } + if (pos_descr) { + Decorator d; + // FIXME: we may want to also print the size of the access here, + // but in case of accesses generated by memset it may be confusing. + str.append("%s <== Memory access at offset %zd %s this variable%s\n", + d.Location(), addr, pos_descr, d.Default()); + } else { + str.append("\n"); + } + Printf("%s", str.data()); +} + +bool DescribeAddressIfStack(uptr addr, uptr access_size) { + StackAddressDescription descr; + if (!GetStackAddressInformation(addr, access_size, &descr)) return false; + descr.Print(); + return true; +} + +// Global descriptions +static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, + const __asan_global &g) { + InternalScopedString str(4096); + Decorator d; + str.append("%s", d.Location()); + if (addr < g.beg) { + str.append("%p is located %zd bytes to the left", (void *)addr, + g.beg - addr); + } else if (addr + access_size > g.beg + g.size) { + if (addr < g.beg + g.size) addr = g.beg + g.size; + str.append("%p is located %zd bytes to the right", (void *)addr, + addr - (g.beg + g.size)); + } else { + // Can it happen? + str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg); + } + str.append(" of global variable '%s' defined in '", + MaybeDemangleGlobalName(g.name)); + PrintGlobalLocation(&str, g); + str.append("' (0x%zx) of size %zu\n", g.beg, g.size); + str.append("%s", d.Default()); + PrintGlobalNameIfASCII(&str, g); + Printf("%s", str.data()); +} + +bool GetGlobalAddressInformation(uptr addr, uptr access_size, + GlobalAddressDescription *descr) { + descr->addr = addr; + int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites, + ARRAY_SIZE(descr->globals)); + descr->size = globals_num; + descr->access_size = access_size; + return globals_num != 0; +} + +bool DescribeAddressIfGlobal(uptr addr, uptr access_size, + const char *bug_type) { + GlobalAddressDescription descr; + if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false; + + descr.Print(bug_type); + return true; +} + +void ShadowAddressDescription::Print() const { + Printf("Address %p is located in the %s area.\n", addr, ShadowNames[kind]); +} + +void GlobalAddressDescription::Print(const char *bug_type) const { + for (int i = 0; i < size; i++) { + DescribeAddressRelativeToGlobal(addr, access_size, globals[i]); + if (bug_type && + 0 == internal_strcmp(bug_type, "initialization-order-fiasco") && + reg_sites[i]) { + Printf(" registered at:\n"); + StackDepotGet(reg_sites[i]).Print(); + } + } +} + +bool GlobalAddressDescription::PointsInsideTheSameVariable( + const GlobalAddressDescription &other) const { + if (size == 0 || other.size == 0) return false; + + for (uptr i = 0; i < size; i++) { + const __asan_global &a = globals[i]; + for (uptr j = 0; j < other.size; j++) { + const __asan_global &b = other.globals[j]; + if (a.beg == b.beg && + a.beg <= addr && + b.beg <= other.addr && + (addr + access_size) < (a.beg + a.size) && + (other.addr + other.access_size) < (b.beg + b.size)) + return true; + } + } + + return false; +} + +void StackAddressDescription::Print() const { + Decorator d; + Printf("%s", d.Location()); + Printf("Address %p is located in stack of thread %s", addr, + AsanThreadIdAndName(tid).c_str()); + + if (!frame_descr) { + Printf("%s\n", d.Default()); + return; + } + Printf(" at offset %zu in frame%s\n", offset, d.Default()); + + // Now we print the frame where the alloca has happened. + // We print this frame as a stack trace with one element. + // The symbolizer may print more than one frame if inlining was involved. + // The frame numbers may be different than those in the stack trace printed + // previously. That's unfortunate, but I have no better solution, + // especially given that the alloca may be from entirely different place + // (e.g. use-after-scope, or different thread's stack). + Printf("%s", d.Default()); + StackTrace alloca_stack(&frame_pc, 1); + alloca_stack.Print(); + + InternalMmapVector vars; + vars.reserve(16); + if (!ParseFrameDescription(frame_descr, &vars)) { + Printf( + "AddressSanitizer can't parse the stack frame " + "descriptor: |%s|\n", + frame_descr); + // 'addr' is a stack address, so return true even if we can't parse frame + return; + } + uptr n_objects = vars.size(); + // Report the number of stack objects. + Printf(" This frame has %zu object(s):\n", n_objects); + + // Report all objects in this frame. + for (uptr i = 0; i < n_objects; i++) { + uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; + uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); + PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end, + next_var_beg); + } + Printf( + "HINT: this may be a false positive if your program uses " + "some custom stack unwind mechanism, swapcontext or vfork\n"); + if (SANITIZER_WINDOWS) + Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n"); + else + Printf(" (longjmp and C++ exceptions *are* supported)\n"); + + DescribeThread(GetThreadContextByTidLocked(tid)); +} + +void HeapAddressDescription::Print() const { + PrintHeapChunkAccess(addr, chunk_access); + + asanThreadRegistry().CheckLocked(); + AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid); + StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id); + + Decorator d; + AsanThreadContext *free_thread = nullptr; + if (free_tid != kInvalidTid) { + free_thread = GetThreadContextByTidLocked(free_tid); + Printf("%sfreed by thread %s here:%s\n", d.Allocation(), + AsanThreadIdAndName(free_thread).c_str(), d.Default()); + StackTrace free_stack = GetStackTraceFromId(free_stack_id); + free_stack.Print(); + Printf("%spreviously allocated by thread %s here:%s\n", d.Allocation(), + AsanThreadIdAndName(alloc_thread).c_str(), d.Default()); + } else { + Printf("%sallocated by thread %s here:%s\n", d.Allocation(), + AsanThreadIdAndName(alloc_thread).c_str(), d.Default()); + } + alloc_stack.Print(); + DescribeThread(GetCurrentThread()); + if (free_thread) DescribeThread(free_thread); + DescribeThread(alloc_thread); +} + +AddressDescription::AddressDescription(uptr addr, uptr access_size, + bool shouldLockThreadRegistry) { + if (GetShadowAddressInformation(addr, &data.shadow)) { + data.kind = kAddressKindShadow; + return; + } + if (GetHeapAddressInformation(addr, access_size, &data.heap)) { + data.kind = kAddressKindHeap; + return; + } + + bool isStackMemory = false; + if (shouldLockThreadRegistry) { + ThreadRegistryLock l(&asanThreadRegistry()); + isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack); + } else { + isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack); + } + if (isStackMemory) { + data.kind = kAddressKindStack; + return; + } + + if (GetGlobalAddressInformation(addr, access_size, &data.global)) { + data.kind = kAddressKindGlobal; + return; + } + data.kind = kAddressKindWild; + addr = 0; +} + +void PrintAddressDescription(uptr addr, uptr access_size, + const char *bug_type) { + ShadowAddressDescription shadow_descr; + if (GetShadowAddressInformation(addr, &shadow_descr)) { + shadow_descr.Print(); + return; + } + + GlobalAddressDescription global_descr; + if (GetGlobalAddressInformation(addr, access_size, &global_descr)) { + global_descr.Print(bug_type); + return; + } + + StackAddressDescription stack_descr; + if (GetStackAddressInformation(addr, access_size, &stack_descr)) { + stack_descr.Print(); + return; + } + + HeapAddressDescription heap_descr; + if (GetHeapAddressInformation(addr, access_size, &heap_descr)) { + heap_descr.Print(); + return; + } + + // We exhausted our possibilities. Bail out. + Printf( + "AddressSanitizer can not describe address in more detail " + "(wild memory access suspected).\n"); +} +} // namespace __asan diff --git a/libsanitizer/asan/asan_descriptions.h b/libsanitizer/asan/asan_descriptions.h index 43d0cbfeff3..28b38100b85 100644 --- a/libsanitizer/asan/asan_descriptions.h +++ b/libsanitizer/asan/asan_descriptions.h @@ -1,13 +1,14 @@ //===-- asan_descriptions.h -------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_descriptions.cc. +// ASan-private header for asan_descriptions.cpp. // TODO(filcab): Most struct definitions should move to the interface headers. //===----------------------------------------------------------------------===// #ifndef ASAN_DESCRIPTIONS_H diff --git a/libsanitizer/asan/asan_errors.cc b/libsanitizer/asan/asan_errors.cc deleted file mode 100644 index 65941f65bf8..00000000000 --- a/libsanitizer/asan/asan_errors.cc +++ /dev/null @@ -1,583 +0,0 @@ -//===-- asan_errors.cc ------------------------------------------*- C++ -*-===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// ASan implementation for error structures. -//===----------------------------------------------------------------------===// - -#include "asan_errors.h" -#include "asan_descriptions.h" -#include "asan_mapping.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "sanitizer_common/sanitizer_stackdepot.h" - -namespace __asan { - -static void OnStackUnwind(const SignalContext &sig, - const void *callback_context, - BufferedStackTrace *stack) { - bool fast = common_flags()->fast_unwind_on_fatal; -#if SANITIZER_FREEBSD || SANITIZER_NETBSD - // On FreeBSD the slow unwinding that leverages _Unwind_Backtrace() - // yields the call stack of the signal's handler and not of the code - // that raised the signal (as it does on Linux). - fast = true; -#endif - // Tests and maybe some users expect that scariness is going to be printed - // just before the stack. As only asan has scariness score we have no - // corresponding code in the sanitizer_common and we use this callback to - // print it. - static_cast(callback_context)->Print(); - GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, sig.context, fast); -} - -void ErrorDeadlySignal::Print() { - ReportDeadlySignal(signal, tid, &OnStackUnwind, &scariness); -} - -void ErrorDoubleFree::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: attempting %s on %p in thread %s:\n", - scariness.GetDescription(), addr_description.addr, - AsanThreadIdAndName(tid).c_str()); - Printf("%s", d.Default()); - scariness.Print(); - GET_STACK_TRACE_FATAL(second_free_stack->trace[0], - second_free_stack->top_frame_bp); - stack.Print(); - addr_description.Print(); - ReportErrorSummary(scariness.GetDescription(), &stack); -} - -void ErrorNewDeleteTypeMismatch::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: %s on %p in thread %s:\n", - scariness.GetDescription(), addr_description.addr, - AsanThreadIdAndName(tid).c_str()); - Printf("%s object passed to delete has wrong type:\n", d.Default()); - if (delete_size != 0) { - Printf( - " size of the allocated type: %zd bytes;\n" - " size of the deallocated type: %zd bytes.\n", - addr_description.chunk_access.chunk_size, delete_size); - } - const uptr user_alignment = - addr_description.chunk_access.user_requested_alignment; - if (delete_alignment != user_alignment) { - char user_alignment_str[32]; - char delete_alignment_str[32]; - internal_snprintf(user_alignment_str, sizeof(user_alignment_str), - "%zd bytes", user_alignment); - internal_snprintf(delete_alignment_str, sizeof(delete_alignment_str), - "%zd bytes", delete_alignment); - static const char *kDefaultAlignment = "default-aligned"; - Printf( - " alignment of the allocated type: %s;\n" - " alignment of the deallocated type: %s.\n", - user_alignment > 0 ? user_alignment_str : kDefaultAlignment, - delete_alignment > 0 ? delete_alignment_str : kDefaultAlignment); - } - CHECK_GT(free_stack->size, 0); - scariness.Print(); - GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); - stack.Print(); - addr_description.Print(); - ReportErrorSummary(scariness.GetDescription(), &stack); - Report( - "HINT: if you don't care about these errors you may set " - "ASAN_OPTIONS=new_delete_type_mismatch=0\n"); -} - -void ErrorFreeNotMalloced::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: attempting free on address " - "which was not malloc()-ed: %p in thread %s\n", - addr_description.Address(), AsanThreadIdAndName(tid).c_str()); - Printf("%s", d.Default()); - CHECK_GT(free_stack->size, 0); - scariness.Print(); - GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); - stack.Print(); - addr_description.Print(); - ReportErrorSummary(scariness.GetDescription(), &stack); -} - -void ErrorAllocTypeMismatch::Print() { - static const char *alloc_names[] = {"INVALID", "malloc", "operator new", - "operator new []"}; - static const char *dealloc_names[] = {"INVALID", "free", "operator delete", - "operator delete []"}; - CHECK_NE(alloc_type, dealloc_type); - Decorator d; - Printf("%s", d.Error()); - Report("ERROR: AddressSanitizer: %s (%s vs %s) on %p\n", - scariness.GetDescription(), alloc_names[alloc_type], - dealloc_names[dealloc_type], addr_description.Address()); - Printf("%s", d.Default()); - CHECK_GT(dealloc_stack->size, 0); - scariness.Print(); - GET_STACK_TRACE_FATAL(dealloc_stack->trace[0], dealloc_stack->top_frame_bp); - stack.Print(); - addr_description.Print(); - ReportErrorSummary(scariness.GetDescription(), &stack); - Report( - "HINT: if you don't care about these errors you may set " - "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); -} - -void ErrorMallocUsableSizeNotOwned::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: attempting to call malloc_usable_size() for " - "pointer which is not owned: %p\n", - addr_description.Address()); - Printf("%s", d.Default()); - stack->Print(); - addr_description.Print(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorSanitizerGetAllocatedSizeNotOwned::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: attempting to call " - "__sanitizer_get_allocated_size() for pointer which is not owned: %p\n", - addr_description.Address()); - Printf("%s", d.Default()); - stack->Print(); - addr_description.Print(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorCallocOverflow::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: calloc parameters overflow: count * size " - "(%zd * %zd) cannot be represented in type size_t (thread %s)\n", - count, size, AsanThreadIdAndName(tid).c_str()); - Printf("%s", d.Default()); - stack->Print(); - PrintHintAllocatorCannotReturnNull(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorPvallocOverflow::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: pvalloc parameters overflow: size 0x%zx " - "rounded up to system page size 0x%zx cannot be represented in type " - "size_t (thread %s)\n", - size, GetPageSizeCached(), AsanThreadIdAndName(tid).c_str()); - Printf("%s", d.Default()); - stack->Print(); - PrintHintAllocatorCannotReturnNull(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorInvalidAllocationAlignment::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: invalid allocation alignment: %zd, " - "alignment must be a power of two (thread %s)\n", - alignment, AsanThreadIdAndName(tid).c_str()); - Printf("%s", d.Default()); - stack->Print(); - PrintHintAllocatorCannotReturnNull(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorInvalidAlignedAllocAlignment::Print() { - Decorator d; - Printf("%s", d.Error()); -#if SANITIZER_POSIX - Report("ERROR: AddressSanitizer: invalid alignment requested in " - "aligned_alloc: %zd, alignment must be a power of two and the " - "requested size 0x%zx must be a multiple of alignment " - "(thread %s)\n", alignment, size, AsanThreadIdAndName(tid).c_str()); -#else - Report("ERROR: AddressSanitizer: invalid alignment requested in " - "aligned_alloc: %zd, the requested size 0x%zx must be a multiple of " - "alignment (thread %s)\n", alignment, size, - AsanThreadIdAndName(tid).c_str()); -#endif - Printf("%s", d.Default()); - stack->Print(); - PrintHintAllocatorCannotReturnNull(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorInvalidPosixMemalignAlignment::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: invalid alignment requested in posix_memalign: " - "%zd, alignment must be a power of two and a multiple of sizeof(void*) " - "== %zd (thread %s)\n", - alignment, sizeof(void*), AsanThreadIdAndName(tid).c_str()); // NOLINT - Printf("%s", d.Default()); - stack->Print(); - PrintHintAllocatorCannotReturnNull(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorAllocationSizeTooBig::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: requested allocation size 0x%zx (0x%zx after " - "adjustments for alignment, red zones etc.) exceeds maximum supported " - "size of 0x%zx (thread %s)\n", - user_size, total_size, max_size, AsanThreadIdAndName(tid).c_str()); - Printf("%s", d.Default()); - stack->Print(); - PrintHintAllocatorCannotReturnNull(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorRssLimitExceeded::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: specified RSS limit exceeded, currently set to " - "soft_rss_limit_mb=%zd\n", common_flags()->soft_rss_limit_mb); - Printf("%s", d.Default()); - stack->Print(); - PrintHintAllocatorCannotReturnNull(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorOutOfMemory::Print() { - Decorator d; - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: allocator is out of memory trying to allocate " - "0x%zx bytes\n", requested_size); - Printf("%s", d.Default()); - stack->Print(); - PrintHintAllocatorCannotReturnNull(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorStringFunctionMemoryRangesOverlap::Print() { - Decorator d; - char bug_type[100]; - internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); - Printf("%s", d.Error()); - Report( - "ERROR: AddressSanitizer: %s: memory ranges [%p,%p) and [%p, %p) " - "overlap\n", - bug_type, addr1_description.Address(), - addr1_description.Address() + length1, addr2_description.Address(), - addr2_description.Address() + length2); - Printf("%s", d.Default()); - scariness.Print(); - stack->Print(); - addr1_description.Print(); - addr2_description.Print(); - ReportErrorSummary(bug_type, stack); -} - -void ErrorStringFunctionSizeOverflow::Print() { - Decorator d; - Printf("%s", d.Error()); - Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", - scariness.GetDescription(), size); - Printf("%s", d.Default()); - scariness.Print(); - stack->Print(); - addr_description.Print(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorBadParamsToAnnotateContiguousContainer::Print() { - Report( - "ERROR: AddressSanitizer: bad parameters to " - "__sanitizer_annotate_contiguous_container:\n" - " beg : %p\n" - " end : %p\n" - " old_mid : %p\n" - " new_mid : %p\n", - beg, end, old_mid, new_mid); - uptr granularity = SHADOW_GRANULARITY; - if (!IsAligned(beg, granularity)) - Report("ERROR: beg is not aligned by %d\n", granularity); - stack->Print(); - ReportErrorSummary(scariness.GetDescription(), stack); -} - -void ErrorODRViolation::Print() { - Decorator d; - Printf("%s", d.Error()); - Report("ERROR: AddressSanitizer: %s (%p):\n", scariness.GetDescription(), - global1.beg); - Printf("%s", d.Default()); - InternalScopedString g1_loc(256), g2_loc(256); - PrintGlobalLocation(&g1_loc, global1); - PrintGlobalLocation(&g2_loc, global2); - Printf(" [1] size=%zd '%s' %s\n", global1.size, - MaybeDemangleGlobalName(global1.name), g1_loc.data()); - Printf(" [2] size=%zd '%s' %s\n", global2.size, - MaybeDemangleGlobalName(global2.name), g2_loc.data()); - if (stack_id1 && stack_id2) { - Printf("These globals were registered at these points:\n"); - Printf(" [1]:\n"); - StackDepotGet(stack_id1).Print(); - Printf(" [2]:\n"); - StackDepotGet(stack_id2).Print(); - } - Report( - "HINT: if you don't care about these errors you may set " - "ASAN_OPTIONS=detect_odr_violation=0\n"); - InternalScopedString error_msg(256); - error_msg.append("%s: global '%s' at %s", scariness.GetDescription(), - MaybeDemangleGlobalName(global1.name), g1_loc.data()); - ReportErrorSummary(error_msg.data()); -} - -void ErrorInvalidPointerPair::Print() { - Decorator d; - Printf("%s", d.Error()); - Report("ERROR: AddressSanitizer: %s: %p %p\n", scariness.GetDescription(), - addr1_description.Address(), addr2_description.Address()); - Printf("%s", d.Default()); - GET_STACK_TRACE_FATAL(pc, bp); - stack.Print(); - addr1_description.Print(); - addr2_description.Print(); - ReportErrorSummary(scariness.GetDescription(), &stack); -} - -static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) { - return s[-1] > 127 && s[1] > 127; -} - -ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr, - bool is_write_, uptr access_size_) - : ErrorBase(tid), - addr_description(addr, access_size_, /*shouldLockThreadRegistry=*/false), - pc(pc_), - bp(bp_), - sp(sp_), - access_size(access_size_), - is_write(is_write_), - shadow_val(0) { - scariness.Clear(); - if (access_size) { - if (access_size <= 9) { - char desr[] = "?-byte"; - desr[0] = '0' + access_size; - scariness.Scare(access_size + access_size / 2, desr); - } else if (access_size >= 10) { - scariness.Scare(15, "multi-byte"); - } - is_write ? scariness.Scare(20, "write") : scariness.Scare(1, "read"); - - // Determine the error type. - bug_descr = "unknown-crash"; - if (AddrIsInMem(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++; - // If we are in the partial right redzone, look at the next shadow byte. - if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++; - bool far_from_bounds = false; - shadow_val = *shadow_addr; - int bug_type_score = 0; - // For use-after-frees reads are almost as bad as writes. - int read_after_free_bonus = 0; - switch (shadow_val) { - case kAsanHeapLeftRedzoneMagic: - case kAsanArrayCookieMagic: - bug_descr = "heap-buffer-overflow"; - bug_type_score = 10; - far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); - break; - case kAsanHeapFreeMagic: - bug_descr = "heap-use-after-free"; - bug_type_score = 20; - if (!is_write) read_after_free_bonus = 18; - break; - case kAsanStackLeftRedzoneMagic: - bug_descr = "stack-buffer-underflow"; - bug_type_score = 25; - far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); - break; - case kAsanInitializationOrderMagic: - bug_descr = "initialization-order-fiasco"; - bug_type_score = 1; - break; - case kAsanStackMidRedzoneMagic: - case kAsanStackRightRedzoneMagic: - bug_descr = "stack-buffer-overflow"; - bug_type_score = 25; - far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); - break; - case kAsanStackAfterReturnMagic: - bug_descr = "stack-use-after-return"; - bug_type_score = 30; - if (!is_write) read_after_free_bonus = 18; - break; - case kAsanUserPoisonedMemoryMagic: - bug_descr = "use-after-poison"; - bug_type_score = 20; - break; - case kAsanContiguousContainerOOBMagic: - bug_descr = "container-overflow"; - bug_type_score = 10; - break; - case kAsanStackUseAfterScopeMagic: - bug_descr = "stack-use-after-scope"; - bug_type_score = 10; - break; - case kAsanGlobalRedzoneMagic: - bug_descr = "global-buffer-overflow"; - bug_type_score = 10; - far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); - break; - case kAsanIntraObjectRedzone: - bug_descr = "intra-object-overflow"; - bug_type_score = 10; - break; - case kAsanAllocaLeftMagic: - case kAsanAllocaRightMagic: - bug_descr = "dynamic-stack-buffer-overflow"; - bug_type_score = 25; - far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); - break; - } - scariness.Scare(bug_type_score + read_after_free_bonus, bug_descr); - if (far_from_bounds) scariness.Scare(10, "far-from-bounds"); - } - } -} - -static void PrintContainerOverflowHint() { - Printf("HINT: if you don't care about these errors you may set " - "ASAN_OPTIONS=detect_container_overflow=0.\n" - "If you suspect a false positive see also: " - "https://github.com/google/sanitizers/wiki/" - "AddressSanitizerContainerOverflow.\n"); -} - -static void PrintShadowByte(InternalScopedString *str, const char *before, - u8 byte, const char *after = "\n") { - PrintMemoryByte(str, before, byte, /*in_shadow*/true, after); -} - -static void PrintLegend(InternalScopedString *str) { - str->append( - "Shadow byte legend (one shadow byte represents %d " - "application bytes):\n", - (int)SHADOW_GRANULARITY); - PrintShadowByte(str, " Addressable: ", 0); - str->append(" Partially addressable: "); - for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " "); - str->append("\n"); - PrintShadowByte(str, " Heap left redzone: ", - kAsanHeapLeftRedzoneMagic); - PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic); - PrintShadowByte(str, " Stack left redzone: ", - kAsanStackLeftRedzoneMagic); - PrintShadowByte(str, " Stack mid redzone: ", - kAsanStackMidRedzoneMagic); - PrintShadowByte(str, " Stack right redzone: ", - kAsanStackRightRedzoneMagic); - PrintShadowByte(str, " Stack after return: ", - kAsanStackAfterReturnMagic); - PrintShadowByte(str, " Stack use after scope: ", - kAsanStackUseAfterScopeMagic); - PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic); - PrintShadowByte(str, " Global init order: ", - kAsanInitializationOrderMagic); - PrintShadowByte(str, " Poisoned by user: ", - kAsanUserPoisonedMemoryMagic); - PrintShadowByte(str, " Container overflow: ", - kAsanContiguousContainerOOBMagic); - PrintShadowByte(str, " Array cookie: ", - kAsanArrayCookieMagic); - PrintShadowByte(str, " Intra object redzone: ", - kAsanIntraObjectRedzone); - PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic); - PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic); - PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic); - PrintShadowByte(str, " Shadow gap: ", kAsanShadowGap); -} - -static void PrintShadowBytes(InternalScopedString *str, const char *before, - u8 *bytes, u8 *guilty, uptr n) { - Decorator d; - if (before) str->append("%s%p:", before, bytes); - for (uptr i = 0; i < n; i++) { - u8 *p = bytes + i; - const char *before = - p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " "; - const char *after = p == guilty ? "]" : ""; - PrintShadowByte(str, before, *p, after); - } - str->append("\n"); -} - -static void PrintShadowMemoryForAddress(uptr addr) { - if (!AddrIsInMem(addr)) return; - uptr shadow_addr = MemToShadow(addr); - const uptr n_bytes_per_row = 16; - uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); - InternalScopedString str(4096 * 8); - str.append("Shadow bytes around the buggy address:\n"); - for (int i = -5; i <= 5; i++) { - uptr row_shadow_addr = aligned_shadow + i * n_bytes_per_row; - // Skip rows that would be outside the shadow range. This can happen when - // the user address is near the bottom, top, or shadow gap of the address - // space. - if (!AddrIsInShadow(row_shadow_addr)) continue; - const char *prefix = (i == 0) ? "=>" : " "; - PrintShadowBytes(&str, prefix, (u8 *)row_shadow_addr, (u8 *)shadow_addr, - n_bytes_per_row); - } - if (flags()->print_legend) PrintLegend(&str); - Printf("%s", str.data()); -} - -void ErrorGeneric::Print() { - Decorator d; - Printf("%s", d.Error()); - uptr addr = addr_description.Address(); - Report("ERROR: AddressSanitizer: %s on address %p at pc %p bp %p sp %p\n", - bug_descr, (void *)addr, pc, bp, sp); - Printf("%s", d.Default()); - - Printf("%s%s of size %zu at %p thread %s%s\n", d.Access(), - access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size, - (void *)addr, AsanThreadIdAndName(tid).c_str(), d.Default()); - - scariness.Print(); - GET_STACK_TRACE_FATAL(pc, bp); - stack.Print(); - - // Pass bug_descr because we have a special case for - // initialization-order-fiasco - addr_description.Print(bug_descr); - if (shadow_val == kAsanContiguousContainerOOBMagic) - PrintContainerOverflowHint(); - ReportErrorSummary(bug_descr, &stack); - PrintShadowMemoryForAddress(addr); -} - -} // namespace __asan diff --git a/libsanitizer/asan/asan_errors.cpp b/libsanitizer/asan/asan_errors.cpp new file mode 100644 index 00000000000..75ee996ceef --- /dev/null +++ b/libsanitizer/asan/asan_errors.cpp @@ -0,0 +1,597 @@ +//===-- asan_errors.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan implementation for error structures. +//===----------------------------------------------------------------------===// + +#include "asan_errors.h" +#include "asan_descriptions.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + +namespace __asan { + +static void OnStackUnwind(const SignalContext &sig, + const void *callback_context, + BufferedStackTrace *stack) { + bool fast = common_flags()->fast_unwind_on_fatal; +#if SANITIZER_FREEBSD || SANITIZER_NETBSD + // On FreeBSD the slow unwinding that leverages _Unwind_Backtrace() + // yields the call stack of the signal's handler and not of the code + // that raised the signal (as it does on Linux). + fast = true; +#endif + // Tests and maybe some users expect that scariness is going to be printed + // just before the stack. As only asan has scariness score we have no + // corresponding code in the sanitizer_common and we use this callback to + // print it. + static_cast(callback_context)->Print(); + stack->Unwind(sig.pc, sig.bp, sig.context, fast); +} + +void ErrorDeadlySignal::Print() { + ReportDeadlySignal(signal, tid, &OnStackUnwind, &scariness); +} + +void ErrorDoubleFree::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: attempting %s on %p in thread %s:\n", + scariness.GetDescription(), addr_description.addr, + AsanThreadIdAndName(tid).c_str()); + Printf("%s", d.Default()); + scariness.Print(); + GET_STACK_TRACE_FATAL(second_free_stack->trace[0], + second_free_stack->top_frame_bp); + stack.Print(); + addr_description.Print(); + ReportErrorSummary(scariness.GetDescription(), &stack); +} + +void ErrorNewDeleteTypeMismatch::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: %s on %p in thread %s:\n", + scariness.GetDescription(), addr_description.addr, + AsanThreadIdAndName(tid).c_str()); + Printf("%s object passed to delete has wrong type:\n", d.Default()); + if (delete_size != 0) { + Printf( + " size of the allocated type: %zd bytes;\n" + " size of the deallocated type: %zd bytes.\n", + addr_description.chunk_access.chunk_size, delete_size); + } + const uptr user_alignment = + addr_description.chunk_access.user_requested_alignment; + if (delete_alignment != user_alignment) { + char user_alignment_str[32]; + char delete_alignment_str[32]; + internal_snprintf(user_alignment_str, sizeof(user_alignment_str), + "%zd bytes", user_alignment); + internal_snprintf(delete_alignment_str, sizeof(delete_alignment_str), + "%zd bytes", delete_alignment); + static const char *kDefaultAlignment = "default-aligned"; + Printf( + " alignment of the allocated type: %s;\n" + " alignment of the deallocated type: %s.\n", + user_alignment > 0 ? user_alignment_str : kDefaultAlignment, + delete_alignment > 0 ? delete_alignment_str : kDefaultAlignment); + } + CHECK_GT(free_stack->size, 0); + scariness.Print(); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + stack.Print(); + addr_description.Print(); + ReportErrorSummary(scariness.GetDescription(), &stack); + Report( + "HINT: if you don't care about these errors you may set " + "ASAN_OPTIONS=new_delete_type_mismatch=0\n"); +} + +void ErrorFreeNotMalloced::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: attempting free on address " + "which was not malloc()-ed: %p in thread %s\n", + addr_description.Address(), AsanThreadIdAndName(tid).c_str()); + Printf("%s", d.Default()); + CHECK_GT(free_stack->size, 0); + scariness.Print(); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + stack.Print(); + addr_description.Print(); + ReportErrorSummary(scariness.GetDescription(), &stack); +} + +void ErrorAllocTypeMismatch::Print() { + static const char *alloc_names[] = {"INVALID", "malloc", "operator new", + "operator new []"}; + static const char *dealloc_names[] = {"INVALID", "free", "operator delete", + "operator delete []"}; + CHECK_NE(alloc_type, dealloc_type); + Decorator d; + Printf("%s", d.Error()); + Report("ERROR: AddressSanitizer: %s (%s vs %s) on %p\n", + scariness.GetDescription(), alloc_names[alloc_type], + dealloc_names[dealloc_type], addr_description.Address()); + Printf("%s", d.Default()); + CHECK_GT(dealloc_stack->size, 0); + scariness.Print(); + GET_STACK_TRACE_FATAL(dealloc_stack->trace[0], dealloc_stack->top_frame_bp); + stack.Print(); + addr_description.Print(); + ReportErrorSummary(scariness.GetDescription(), &stack); + Report( + "HINT: if you don't care about these errors you may set " + "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); +} + +void ErrorMallocUsableSizeNotOwned::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: attempting to call malloc_usable_size() for " + "pointer which is not owned: %p\n", + addr_description.Address()); + Printf("%s", d.Default()); + stack->Print(); + addr_description.Print(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorSanitizerGetAllocatedSizeNotOwned::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: attempting to call " + "__sanitizer_get_allocated_size() for pointer which is not owned: %p\n", + addr_description.Address()); + Printf("%s", d.Default()); + stack->Print(); + addr_description.Print(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorCallocOverflow::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: calloc parameters overflow: count * size " + "(%zd * %zd) cannot be represented in type size_t (thread %s)\n", + count, size, AsanThreadIdAndName(tid).c_str()); + Printf("%s", d.Default()); + stack->Print(); + PrintHintAllocatorCannotReturnNull(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorReallocArrayOverflow::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: reallocarray parameters overflow: count * size " + "(%zd * %zd) cannot be represented in type size_t (thread %s)\n", + count, size, AsanThreadIdAndName(tid).c_str()); + Printf("%s", d.Default()); + stack->Print(); + PrintHintAllocatorCannotReturnNull(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorPvallocOverflow::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: pvalloc parameters overflow: size 0x%zx " + "rounded up to system page size 0x%zx cannot be represented in type " + "size_t (thread %s)\n", + size, GetPageSizeCached(), AsanThreadIdAndName(tid).c_str()); + Printf("%s", d.Default()); + stack->Print(); + PrintHintAllocatorCannotReturnNull(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorInvalidAllocationAlignment::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: invalid allocation alignment: %zd, " + "alignment must be a power of two (thread %s)\n", + alignment, AsanThreadIdAndName(tid).c_str()); + Printf("%s", d.Default()); + stack->Print(); + PrintHintAllocatorCannotReturnNull(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorInvalidAlignedAllocAlignment::Print() { + Decorator d; + Printf("%s", d.Error()); +#if SANITIZER_POSIX + Report("ERROR: AddressSanitizer: invalid alignment requested in " + "aligned_alloc: %zd, alignment must be a power of two and the " + "requested size 0x%zx must be a multiple of alignment " + "(thread %s)\n", alignment, size, AsanThreadIdAndName(tid).c_str()); +#else + Report("ERROR: AddressSanitizer: invalid alignment requested in " + "aligned_alloc: %zd, the requested size 0x%zx must be a multiple of " + "alignment (thread %s)\n", alignment, size, + AsanThreadIdAndName(tid).c_str()); +#endif + Printf("%s", d.Default()); + stack->Print(); + PrintHintAllocatorCannotReturnNull(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorInvalidPosixMemalignAlignment::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: invalid alignment requested in posix_memalign: " + "%zd, alignment must be a power of two and a multiple of sizeof(void*) " + "== %zd (thread %s)\n", + alignment, sizeof(void*), AsanThreadIdAndName(tid).c_str()); // NOLINT + Printf("%s", d.Default()); + stack->Print(); + PrintHintAllocatorCannotReturnNull(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorAllocationSizeTooBig::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: requested allocation size 0x%zx (0x%zx after " + "adjustments for alignment, red zones etc.) exceeds maximum supported " + "size of 0x%zx (thread %s)\n", + user_size, total_size, max_size, AsanThreadIdAndName(tid).c_str()); + Printf("%s", d.Default()); + stack->Print(); + PrintHintAllocatorCannotReturnNull(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorRssLimitExceeded::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: specified RSS limit exceeded, currently set to " + "soft_rss_limit_mb=%zd\n", common_flags()->soft_rss_limit_mb); + Printf("%s", d.Default()); + stack->Print(); + PrintHintAllocatorCannotReturnNull(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorOutOfMemory::Print() { + Decorator d; + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: allocator is out of memory trying to allocate " + "0x%zx bytes\n", requested_size); + Printf("%s", d.Default()); + stack->Print(); + PrintHintAllocatorCannotReturnNull(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorStringFunctionMemoryRangesOverlap::Print() { + Decorator d; + char bug_type[100]; + internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); + Printf("%s", d.Error()); + Report( + "ERROR: AddressSanitizer: %s: memory ranges [%p,%p) and [%p, %p) " + "overlap\n", + bug_type, addr1_description.Address(), + addr1_description.Address() + length1, addr2_description.Address(), + addr2_description.Address() + length2); + Printf("%s", d.Default()); + scariness.Print(); + stack->Print(); + addr1_description.Print(); + addr2_description.Print(); + ReportErrorSummary(bug_type, stack); +} + +void ErrorStringFunctionSizeOverflow::Print() { + Decorator d; + Printf("%s", d.Error()); + Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", + scariness.GetDescription(), size); + Printf("%s", d.Default()); + scariness.Print(); + stack->Print(); + addr_description.Print(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorBadParamsToAnnotateContiguousContainer::Print() { + Report( + "ERROR: AddressSanitizer: bad parameters to " + "__sanitizer_annotate_contiguous_container:\n" + " beg : %p\n" + " end : %p\n" + " old_mid : %p\n" + " new_mid : %p\n", + beg, end, old_mid, new_mid); + uptr granularity = SHADOW_GRANULARITY; + if (!IsAligned(beg, granularity)) + Report("ERROR: beg is not aligned by %d\n", granularity); + stack->Print(); + ReportErrorSummary(scariness.GetDescription(), stack); +} + +void ErrorODRViolation::Print() { + Decorator d; + Printf("%s", d.Error()); + Report("ERROR: AddressSanitizer: %s (%p):\n", scariness.GetDescription(), + global1.beg); + Printf("%s", d.Default()); + InternalScopedString g1_loc(256), g2_loc(256); + PrintGlobalLocation(&g1_loc, global1); + PrintGlobalLocation(&g2_loc, global2); + Printf(" [1] size=%zd '%s' %s\n", global1.size, + MaybeDemangleGlobalName(global1.name), g1_loc.data()); + Printf(" [2] size=%zd '%s' %s\n", global2.size, + MaybeDemangleGlobalName(global2.name), g2_loc.data()); + if (stack_id1 && stack_id2) { + Printf("These globals were registered at these points:\n"); + Printf(" [1]:\n"); + StackDepotGet(stack_id1).Print(); + Printf(" [2]:\n"); + StackDepotGet(stack_id2).Print(); + } + Report( + "HINT: if you don't care about these errors you may set " + "ASAN_OPTIONS=detect_odr_violation=0\n"); + InternalScopedString error_msg(256); + error_msg.append("%s: global '%s' at %s", scariness.GetDescription(), + MaybeDemangleGlobalName(global1.name), g1_loc.data()); + ReportErrorSummary(error_msg.data()); +} + +void ErrorInvalidPointerPair::Print() { + Decorator d; + Printf("%s", d.Error()); + Report("ERROR: AddressSanitizer: %s: %p %p\n", scariness.GetDescription(), + addr1_description.Address(), addr2_description.Address()); + Printf("%s", d.Default()); + GET_STACK_TRACE_FATAL(pc, bp); + stack.Print(); + addr1_description.Print(); + addr2_description.Print(); + ReportErrorSummary(scariness.GetDescription(), &stack); +} + +static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) { + return s[-1] > 127 && s[1] > 127; +} + +ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr, + bool is_write_, uptr access_size_) + : ErrorBase(tid), + addr_description(addr, access_size_, /*shouldLockThreadRegistry=*/false), + pc(pc_), + bp(bp_), + sp(sp_), + access_size(access_size_), + is_write(is_write_), + shadow_val(0) { + scariness.Clear(); + if (access_size) { + if (access_size <= 9) { + char desr[] = "?-byte"; + desr[0] = '0' + access_size; + scariness.Scare(access_size + access_size / 2, desr); + } else if (access_size >= 10) { + scariness.Scare(15, "multi-byte"); + } + is_write ? scariness.Scare(20, "write") : scariness.Scare(1, "read"); + + // Determine the error type. + bug_descr = "unknown-crash"; + if (AddrIsInMem(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++; + // If we are in the partial right redzone, look at the next shadow byte. + if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++; + bool far_from_bounds = false; + shadow_val = *shadow_addr; + int bug_type_score = 0; + // For use-after-frees reads are almost as bad as writes. + int read_after_free_bonus = 0; + switch (shadow_val) { + case kAsanHeapLeftRedzoneMagic: + case kAsanArrayCookieMagic: + bug_descr = "heap-buffer-overflow"; + bug_type_score = 10; + far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); + break; + case kAsanHeapFreeMagic: + bug_descr = "heap-use-after-free"; + bug_type_score = 20; + if (!is_write) read_after_free_bonus = 18; + break; + case kAsanStackLeftRedzoneMagic: + bug_descr = "stack-buffer-underflow"; + bug_type_score = 25; + far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); + break; + case kAsanInitializationOrderMagic: + bug_descr = "initialization-order-fiasco"; + bug_type_score = 1; + break; + case kAsanStackMidRedzoneMagic: + case kAsanStackRightRedzoneMagic: + bug_descr = "stack-buffer-overflow"; + bug_type_score = 25; + far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); + break; + case kAsanStackAfterReturnMagic: + bug_descr = "stack-use-after-return"; + bug_type_score = 30; + if (!is_write) read_after_free_bonus = 18; + break; + case kAsanUserPoisonedMemoryMagic: + bug_descr = "use-after-poison"; + bug_type_score = 20; + break; + case kAsanContiguousContainerOOBMagic: + bug_descr = "container-overflow"; + bug_type_score = 10; + break; + case kAsanStackUseAfterScopeMagic: + bug_descr = "stack-use-after-scope"; + bug_type_score = 10; + break; + case kAsanGlobalRedzoneMagic: + bug_descr = "global-buffer-overflow"; + bug_type_score = 10; + far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); + break; + case kAsanIntraObjectRedzone: + bug_descr = "intra-object-overflow"; + bug_type_score = 10; + break; + case kAsanAllocaLeftMagic: + case kAsanAllocaRightMagic: + bug_descr = "dynamic-stack-buffer-overflow"; + bug_type_score = 25; + far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); + break; + } + scariness.Scare(bug_type_score + read_after_free_bonus, bug_descr); + if (far_from_bounds) scariness.Scare(10, "far-from-bounds"); + } + } +} + +static void PrintContainerOverflowHint() { + Printf("HINT: if you don't care about these errors you may set " + "ASAN_OPTIONS=detect_container_overflow=0.\n" + "If you suspect a false positive see also: " + "https://github.com/google/sanitizers/wiki/" + "AddressSanitizerContainerOverflow.\n"); +} + +static void PrintShadowByte(InternalScopedString *str, const char *before, + u8 byte, const char *after = "\n") { + PrintMemoryByte(str, before, byte, /*in_shadow*/true, after); +} + +static void PrintLegend(InternalScopedString *str) { + str->append( + "Shadow byte legend (one shadow byte represents %d " + "application bytes):\n", + (int)SHADOW_GRANULARITY); + PrintShadowByte(str, " Addressable: ", 0); + str->append(" Partially addressable: "); + for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " "); + str->append("\n"); + PrintShadowByte(str, " Heap left redzone: ", + kAsanHeapLeftRedzoneMagic); + PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic); + PrintShadowByte(str, " Stack left redzone: ", + kAsanStackLeftRedzoneMagic); + PrintShadowByte(str, " Stack mid redzone: ", + kAsanStackMidRedzoneMagic); + PrintShadowByte(str, " Stack right redzone: ", + kAsanStackRightRedzoneMagic); + PrintShadowByte(str, " Stack after return: ", + kAsanStackAfterReturnMagic); + PrintShadowByte(str, " Stack use after scope: ", + kAsanStackUseAfterScopeMagic); + PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic); + PrintShadowByte(str, " Global init order: ", + kAsanInitializationOrderMagic); + PrintShadowByte(str, " Poisoned by user: ", + kAsanUserPoisonedMemoryMagic); + PrintShadowByte(str, " Container overflow: ", + kAsanContiguousContainerOOBMagic); + PrintShadowByte(str, " Array cookie: ", + kAsanArrayCookieMagic); + PrintShadowByte(str, " Intra object redzone: ", + kAsanIntraObjectRedzone); + PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic); + PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic); + PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic); + PrintShadowByte(str, " Shadow gap: ", kAsanShadowGap); +} + +static void PrintShadowBytes(InternalScopedString *str, const char *before, + u8 *bytes, u8 *guilty, uptr n) { + Decorator d; + if (before) str->append("%s%p:", before, bytes); + for (uptr i = 0; i < n; i++) { + u8 *p = bytes + i; + const char *before = + p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " "; + const char *after = p == guilty ? "]" : ""; + PrintShadowByte(str, before, *p, after); + } + str->append("\n"); +} + +static void PrintShadowMemoryForAddress(uptr addr) { + if (!AddrIsInMem(addr)) return; + uptr shadow_addr = MemToShadow(addr); + const uptr n_bytes_per_row = 16; + uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); + InternalScopedString str(4096 * 8); + str.append("Shadow bytes around the buggy address:\n"); + for (int i = -5; i <= 5; i++) { + uptr row_shadow_addr = aligned_shadow + i * n_bytes_per_row; + // Skip rows that would be outside the shadow range. This can happen when + // the user address is near the bottom, top, or shadow gap of the address + // space. + if (!AddrIsInShadow(row_shadow_addr)) continue; + const char *prefix = (i == 0) ? "=>" : " "; + PrintShadowBytes(&str, prefix, (u8 *)row_shadow_addr, (u8 *)shadow_addr, + n_bytes_per_row); + } + if (flags()->print_legend) PrintLegend(&str); + Printf("%s", str.data()); +} + +void ErrorGeneric::Print() { + Decorator d; + Printf("%s", d.Error()); + uptr addr = addr_description.Address(); + Report("ERROR: AddressSanitizer: %s on address %p at pc %p bp %p sp %p\n", + bug_descr, (void *)addr, pc, bp, sp); + Printf("%s", d.Default()); + + Printf("%s%s of size %zu at %p thread %s%s\n", d.Access(), + access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size, + (void *)addr, AsanThreadIdAndName(tid).c_str(), d.Default()); + + scariness.Print(); + GET_STACK_TRACE_FATAL(pc, bp); + stack.Print(); + + // Pass bug_descr because we have a special case for + // initialization-order-fiasco + addr_description.Print(bug_descr); + if (shadow_val == kAsanContiguousContainerOOBMagic) + PrintContainerOverflowHint(); + ReportErrorSummary(bug_descr, &stack); + PrintShadowMemoryForAddress(addr); +} + +} // namespace __asan diff --git a/libsanitizer/asan/asan_errors.h b/libsanitizer/asan/asan_errors.h index b155f2452af..b84f56c1853 100644 --- a/libsanitizer/asan/asan_errors.h +++ b/libsanitizer/asan/asan_errors.h @@ -1,7 +1,8 @@ //===-- asan_errors.h -------------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -162,6 +163,21 @@ struct ErrorCallocOverflow : ErrorBase { void Print(); }; +struct ErrorReallocArrayOverflow : ErrorBase { + const BufferedStackTrace *stack; + uptr count; + uptr size; + + ErrorReallocArrayOverflow() = default; // (*) + ErrorReallocArrayOverflow(u32 tid, BufferedStackTrace *stack_, uptr count_, + uptr size_) + : ErrorBase(tid, 10, "reallocarray-overflow"), + stack(stack_), + count(count_), + size(size_) {} + void Print(); +}; + struct ErrorPvallocOverflow : ErrorBase { const BufferedStackTrace *stack; uptr size; @@ -370,6 +386,7 @@ struct ErrorGeneric : ErrorBase { macro(MallocUsableSizeNotOwned) \ macro(SanitizerGetAllocatedSizeNotOwned) \ macro(CallocOverflow) \ + macro(ReallocArrayOverflow) \ macro(PvallocOverflow) \ macro(InvalidAllocationAlignment) \ macro(InvalidAlignedAllocAlignment) \ @@ -387,8 +404,10 @@ struct ErrorGeneric : ErrorBase { #define ASAN_DEFINE_ERROR_KIND(name) kErrorKind##name, #define ASAN_ERROR_DESCRIPTION_MEMBER(name) Error##name name; -#define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name) \ - ErrorDescription(Error##name const &e) : kind(kErrorKind##name), name(e) {} +#define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name) \ + ErrorDescription(Error##name const &e) : kind(kErrorKind##name) { \ + internal_memcpy(&name, &e, sizeof(name)); \ + } #define ASAN_ERROR_DESCRIPTION_PRINT(name) \ case kErrorKind##name: \ return name.Print(); diff --git a/libsanitizer/asan/asan_fake_stack.cc b/libsanitizer/asan/asan_fake_stack.cc deleted file mode 100644 index f4a5bb75c28..00000000000 --- a/libsanitizer/asan/asan_fake_stack.cc +++ /dev/null @@ -1,281 +0,0 @@ -//===-- asan_fake_stack.cc ------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// FakeStack is used to detect use-after-return bugs. -//===----------------------------------------------------------------------===// - -#include "asan_allocator.h" -#include "asan_poisoning.h" -#include "asan_thread.h" - -namespace __asan { - -static const u64 kMagic1 = kAsanStackAfterReturnMagic; -static const u64 kMagic2 = (kMagic1 << 8) | kMagic1; -static const u64 kMagic4 = (kMagic2 << 16) | kMagic2; -static const u64 kMagic8 = (kMagic4 << 32) | kMagic4; - -static const u64 kAllocaRedzoneSize = 32UL; -static const u64 kAllocaRedzoneMask = 31UL; - -// For small size classes inline PoisonShadow for better performance. -ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { - u64 *shadow = reinterpret_cast(MemToShadow(ptr)); - if (SHADOW_SCALE == 3 && class_id <= 6) { - // This code expects SHADOW_SCALE=3. - for (uptr i = 0; i < (((uptr)1) << class_id); i++) { - shadow[i] = magic; - // Make sure this does not become memset. - SanitizerBreakOptimization(nullptr); - } - } else { - // The size class is too big, it's cheaper to poison only size bytes. - PoisonShadow(ptr, size, static_cast(magic)); - } -} - -FakeStack *FakeStack::Create(uptr stack_size_log) { - static uptr kMinStackSizeLog = 16; - static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28); - if (stack_size_log < kMinStackSizeLog) - stack_size_log = kMinStackSizeLog; - if (stack_size_log > kMaxStackSizeLog) - stack_size_log = kMaxStackSizeLog; - uptr size = RequiredSize(stack_size_log); - FakeStack *res = reinterpret_cast( - flags()->uar_noreserve ? MmapNoReserveOrDie(size, "FakeStack") - : MmapOrDie(size, "FakeStack")); - res->stack_size_log_ = stack_size_log; - u8 *p = reinterpret_cast(res); - VReport(1, "T%d: FakeStack created: %p -- %p stack_size_log: %zd; " - "mmapped %zdK, noreserve=%d \n", - GetCurrentTidOrInvalid(), p, - p + FakeStack::RequiredSize(stack_size_log), stack_size_log, - size >> 10, flags()->uar_noreserve); - return res; -} - -void FakeStack::Destroy(int tid) { - PoisonAll(0); - if (Verbosity() >= 2) { - InternalScopedString str(kNumberOfSizeClasses * 50); - for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) - str.append("%zd: %zd/%zd; ", class_id, hint_position_[class_id], - NumberOfFrames(stack_size_log(), class_id)); - Report("T%d: FakeStack destroyed: %s\n", tid, str.data()); - } - uptr size = RequiredSize(stack_size_log_); - FlushUnneededASanShadowMemory(reinterpret_cast(this), size); - UnmapOrDie(this, size); -} - -void FakeStack::PoisonAll(u8 magic) { - PoisonShadow(reinterpret_cast(this), RequiredSize(stack_size_log()), - magic); -} - -#if !defined(_MSC_VER) || defined(__clang__) -ALWAYS_INLINE USED -#endif -FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, - uptr real_stack) { - CHECK_LT(class_id, kNumberOfSizeClasses); - if (needs_gc_) - GC(real_stack); - uptr &hint_position = hint_position_[class_id]; - const int num_iter = NumberOfFrames(stack_size_log, class_id); - u8 *flags = GetFlags(stack_size_log, class_id); - for (int i = 0; i < num_iter; i++) { - uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++); - // This part is tricky. On one hand, checking and setting flags[pos] - // should be atomic to ensure async-signal safety. But on the other hand, - // if the signal arrives between checking and setting flags[pos], the - // signal handler's fake stack will start from a different hint_position - // and so will not touch this particular byte. So, it is safe to do this - // with regular non-atomic load and store (at least I was not able to make - // this code crash). - if (flags[pos]) continue; - flags[pos] = 1; - FakeFrame *res = reinterpret_cast( - GetFrame(stack_size_log, class_id, pos)); - res->real_stack = real_stack; - *SavedFlagPtr(reinterpret_cast(res), class_id) = &flags[pos]; - return res; - } - return nullptr; // We are out of fake stack. -} - -uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr *frame_beg, uptr *frame_end) { - uptr stack_size_log = this->stack_size_log(); - uptr beg = reinterpret_cast(GetFrame(stack_size_log, 0, 0)); - uptr end = reinterpret_cast(this) + RequiredSize(stack_size_log); - if (ptr < beg || ptr >= end) return 0; - uptr class_id = (ptr - beg) >> stack_size_log; - uptr base = beg + (class_id << stack_size_log); - CHECK_LE(base, ptr); - CHECK_LT(ptr, base + (((uptr)1) << stack_size_log)); - uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id); - uptr res = base + pos * BytesInSizeClass(class_id); - *frame_end = res + BytesInSizeClass(class_id); - *frame_beg = res + sizeof(FakeFrame); - return res; -} - -void FakeStack::HandleNoReturn() { - needs_gc_ = true; -} - -// When throw, longjmp or some such happens we don't call OnFree() and -// as the result may leak one or more fake frames, but the good news is that -// we are notified about all such events by HandleNoReturn(). -// If we recently had such no-return event we need to collect garbage frames. -// We do it based on their 'real_stack' values -- everything that is lower -// than the current real_stack is garbage. -NOINLINE void FakeStack::GC(uptr real_stack) { - uptr collected = 0; - for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { - u8 *flags = GetFlags(stack_size_log(), class_id); - for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; - i++) { - if (flags[i] == 0) continue; // not allocated. - FakeFrame *ff = reinterpret_cast( - GetFrame(stack_size_log(), class_id, i)); - if (ff->real_stack < real_stack) { - flags[i] = 0; - collected++; - } - } - } - needs_gc_ = false; -} - -void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) { - for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { - u8 *flags = GetFlags(stack_size_log(), class_id); - for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; - i++) { - if (flags[i] == 0) continue; // not allocated. - FakeFrame *ff = reinterpret_cast( - GetFrame(stack_size_log(), class_id, i)); - uptr begin = reinterpret_cast(ff); - callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg); - } - } -} - -#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA -static THREADLOCAL FakeStack *fake_stack_tls; - -FakeStack *GetTLSFakeStack() { - return fake_stack_tls; -} -void SetTLSFakeStack(FakeStack *fs) { - fake_stack_tls = fs; -} -#else -FakeStack *GetTLSFakeStack() { return 0; } -void SetTLSFakeStack(FakeStack *fs) { } -#endif // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA - -static FakeStack *GetFakeStack() { - AsanThread *t = GetCurrentThread(); - if (!t) return nullptr; - return t->fake_stack(); -} - -static FakeStack *GetFakeStackFast() { - if (FakeStack *fs = GetTLSFakeStack()) - return fs; - if (!__asan_option_detect_stack_use_after_return) - return nullptr; - return GetFakeStack(); -} - -ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) { - FakeStack *fs = GetFakeStackFast(); - if (!fs) return 0; - uptr local_stack; - uptr real_stack = reinterpret_cast(&local_stack); - FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack); - if (!ff) return 0; // Out of fake stack. - uptr ptr = reinterpret_cast(ff); - SetShadow(ptr, size, class_id, 0); - return ptr; -} - -ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) { - FakeStack::Deallocate(ptr, class_id); - SetShadow(ptr, size, class_id, kMagic8); -} - -} // namespace __asan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; -#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \ - extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \ - __asan_stack_malloc_##class_id(uptr size) { \ - return OnMalloc(class_id, size); \ - } \ - extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \ - uptr ptr, uptr size) { \ - OnFree(ptr, class_id, size); \ - } - -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0) -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1) -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2) -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3) -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4) -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5) -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6) -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7) -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8) -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9) -DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10) -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_get_current_fake_stack() { return GetFakeStackFast(); } - -SANITIZER_INTERFACE_ATTRIBUTE -void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, - void **end) { - FakeStack *fs = reinterpret_cast(fake_stack); - if (!fs) return nullptr; - uptr frame_beg, frame_end; - FakeFrame *frame = reinterpret_cast(fs->AddrIsInFakeStack( - reinterpret_cast(addr), &frame_beg, &frame_end)); - if (!frame) return nullptr; - if (frame->magic != kCurrentStackFrameMagic) - return nullptr; - if (beg) *beg = reinterpret_cast(frame_beg); - if (end) *end = reinterpret_cast(frame_end); - return reinterpret_cast(frame->real_stack); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void __asan_alloca_poison(uptr addr, uptr size) { - uptr LeftRedzoneAddr = addr - kAllocaRedzoneSize; - uptr PartialRzAddr = addr + size; - uptr RightRzAddr = (PartialRzAddr + kAllocaRedzoneMask) & ~kAllocaRedzoneMask; - uptr PartialRzAligned = PartialRzAddr & ~(SHADOW_GRANULARITY - 1); - FastPoisonShadow(LeftRedzoneAddr, kAllocaRedzoneSize, kAsanAllocaLeftMagic); - FastPoisonShadowPartialRightRedzone( - PartialRzAligned, PartialRzAddr % SHADOW_GRANULARITY, - RightRzAddr - PartialRzAligned, kAsanAllocaRightMagic); - FastPoisonShadow(RightRzAddr, kAllocaRedzoneSize, kAsanAllocaRightMagic); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void __asan_allocas_unpoison(uptr top, uptr bottom) { - if ((!top) || (top > bottom)) return; - REAL(memset)(reinterpret_cast(MemToShadow(top)), 0, - (bottom - top) / SHADOW_GRANULARITY); -} -} // extern "C" diff --git a/libsanitizer/asan/asan_fake_stack.cpp b/libsanitizer/asan/asan_fake_stack.cpp new file mode 100644 index 00000000000..295e6debc96 --- /dev/null +++ b/libsanitizer/asan/asan_fake_stack.cpp @@ -0,0 +1,282 @@ +//===-- asan_fake_stack.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// FakeStack is used to detect use-after-return bugs. +//===----------------------------------------------------------------------===// + +#include "asan_allocator.h" +#include "asan_poisoning.h" +#include "asan_thread.h" + +namespace __asan { + +static const u64 kMagic1 = kAsanStackAfterReturnMagic; +static const u64 kMagic2 = (kMagic1 << 8) | kMagic1; +static const u64 kMagic4 = (kMagic2 << 16) | kMagic2; +static const u64 kMagic8 = (kMagic4 << 32) | kMagic4; + +static const u64 kAllocaRedzoneSize = 32UL; +static const u64 kAllocaRedzoneMask = 31UL; + +// For small size classes inline PoisonShadow for better performance. +ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { + u64 *shadow = reinterpret_cast(MemToShadow(ptr)); + if (SHADOW_SCALE == 3 && class_id <= 6) { + // This code expects SHADOW_SCALE=3. + for (uptr i = 0; i < (((uptr)1) << class_id); i++) { + shadow[i] = magic; + // Make sure this does not become memset. + SanitizerBreakOptimization(nullptr); + } + } else { + // The size class is too big, it's cheaper to poison only size bytes. + PoisonShadow(ptr, size, static_cast(magic)); + } +} + +FakeStack *FakeStack::Create(uptr stack_size_log) { + static uptr kMinStackSizeLog = 16; + static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28); + if (stack_size_log < kMinStackSizeLog) + stack_size_log = kMinStackSizeLog; + if (stack_size_log > kMaxStackSizeLog) + stack_size_log = kMaxStackSizeLog; + uptr size = RequiredSize(stack_size_log); + FakeStack *res = reinterpret_cast( + flags()->uar_noreserve ? MmapNoReserveOrDie(size, "FakeStack") + : MmapOrDie(size, "FakeStack")); + res->stack_size_log_ = stack_size_log; + u8 *p = reinterpret_cast(res); + VReport(1, "T%d: FakeStack created: %p -- %p stack_size_log: %zd; " + "mmapped %zdK, noreserve=%d \n", + GetCurrentTidOrInvalid(), p, + p + FakeStack::RequiredSize(stack_size_log), stack_size_log, + size >> 10, flags()->uar_noreserve); + return res; +} + +void FakeStack::Destroy(int tid) { + PoisonAll(0); + if (Verbosity() >= 2) { + InternalScopedString str(kNumberOfSizeClasses * 50); + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) + str.append("%zd: %zd/%zd; ", class_id, hint_position_[class_id], + NumberOfFrames(stack_size_log(), class_id)); + Report("T%d: FakeStack destroyed: %s\n", tid, str.data()); + } + uptr size = RequiredSize(stack_size_log_); + FlushUnneededASanShadowMemory(reinterpret_cast(this), size); + UnmapOrDie(this, size); +} + +void FakeStack::PoisonAll(u8 magic) { + PoisonShadow(reinterpret_cast(this), RequiredSize(stack_size_log()), + magic); +} + +#if !defined(_MSC_VER) || defined(__clang__) +ALWAYS_INLINE USED +#endif +FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, + uptr real_stack) { + CHECK_LT(class_id, kNumberOfSizeClasses); + if (needs_gc_) + GC(real_stack); + uptr &hint_position = hint_position_[class_id]; + const int num_iter = NumberOfFrames(stack_size_log, class_id); + u8 *flags = GetFlags(stack_size_log, class_id); + for (int i = 0; i < num_iter; i++) { + uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++); + // This part is tricky. On one hand, checking and setting flags[pos] + // should be atomic to ensure async-signal safety. But on the other hand, + // if the signal arrives between checking and setting flags[pos], the + // signal handler's fake stack will start from a different hint_position + // and so will not touch this particular byte. So, it is safe to do this + // with regular non-atomic load and store (at least I was not able to make + // this code crash). + if (flags[pos]) continue; + flags[pos] = 1; + FakeFrame *res = reinterpret_cast( + GetFrame(stack_size_log, class_id, pos)); + res->real_stack = real_stack; + *SavedFlagPtr(reinterpret_cast(res), class_id) = &flags[pos]; + return res; + } + return nullptr; // We are out of fake stack. +} + +uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr *frame_beg, uptr *frame_end) { + uptr stack_size_log = this->stack_size_log(); + uptr beg = reinterpret_cast(GetFrame(stack_size_log, 0, 0)); + uptr end = reinterpret_cast(this) + RequiredSize(stack_size_log); + if (ptr < beg || ptr >= end) return 0; + uptr class_id = (ptr - beg) >> stack_size_log; + uptr base = beg + (class_id << stack_size_log); + CHECK_LE(base, ptr); + CHECK_LT(ptr, base + (((uptr)1) << stack_size_log)); + uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id); + uptr res = base + pos * BytesInSizeClass(class_id); + *frame_end = res + BytesInSizeClass(class_id); + *frame_beg = res + sizeof(FakeFrame); + return res; +} + +void FakeStack::HandleNoReturn() { + needs_gc_ = true; +} + +// When throw, longjmp or some such happens we don't call OnFree() and +// as the result may leak one or more fake frames, but the good news is that +// we are notified about all such events by HandleNoReturn(). +// If we recently had such no-return event we need to collect garbage frames. +// We do it based on their 'real_stack' values -- everything that is lower +// than the current real_stack is garbage. +NOINLINE void FakeStack::GC(uptr real_stack) { + uptr collected = 0; + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { + u8 *flags = GetFlags(stack_size_log(), class_id); + for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; + i++) { + if (flags[i] == 0) continue; // not allocated. + FakeFrame *ff = reinterpret_cast( + GetFrame(stack_size_log(), class_id, i)); + if (ff->real_stack < real_stack) { + flags[i] = 0; + collected++; + } + } + } + needs_gc_ = false; +} + +void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) { + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { + u8 *flags = GetFlags(stack_size_log(), class_id); + for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; + i++) { + if (flags[i] == 0) continue; // not allocated. + FakeFrame *ff = reinterpret_cast( + GetFrame(stack_size_log(), class_id, i)); + uptr begin = reinterpret_cast(ff); + callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg); + } + } +} + +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA +static THREADLOCAL FakeStack *fake_stack_tls; + +FakeStack *GetTLSFakeStack() { + return fake_stack_tls; +} +void SetTLSFakeStack(FakeStack *fs) { + fake_stack_tls = fs; +} +#else +FakeStack *GetTLSFakeStack() { return 0; } +void SetTLSFakeStack(FakeStack *fs) { } +#endif // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA + +static FakeStack *GetFakeStack() { + AsanThread *t = GetCurrentThread(); + if (!t) return nullptr; + return t->fake_stack(); +} + +static FakeStack *GetFakeStackFast() { + if (FakeStack *fs = GetTLSFakeStack()) + return fs; + if (!__asan_option_detect_stack_use_after_return) + return nullptr; + return GetFakeStack(); +} + +ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) { + FakeStack *fs = GetFakeStackFast(); + if (!fs) return 0; + uptr local_stack; + uptr real_stack = reinterpret_cast(&local_stack); + FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack); + if (!ff) return 0; // Out of fake stack. + uptr ptr = reinterpret_cast(ff); + SetShadow(ptr, size, class_id, 0); + return ptr; +} + +ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) { + FakeStack::Deallocate(ptr, class_id); + SetShadow(ptr, size, class_id, kMagic8); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; +#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \ + __asan_stack_malloc_##class_id(uptr size) { \ + return OnMalloc(class_id, size); \ + } \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \ + uptr ptr, uptr size) { \ + OnFree(ptr, class_id, size); \ + } + +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10) +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_get_current_fake_stack() { return GetFakeStackFast(); } + +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, + void **end) { + FakeStack *fs = reinterpret_cast(fake_stack); + if (!fs) return nullptr; + uptr frame_beg, frame_end; + FakeFrame *frame = reinterpret_cast(fs->AddrIsInFakeStack( + reinterpret_cast(addr), &frame_beg, &frame_end)); + if (!frame) return nullptr; + if (frame->magic != kCurrentStackFrameMagic) + return nullptr; + if (beg) *beg = reinterpret_cast(frame_beg); + if (end) *end = reinterpret_cast(frame_end); + return reinterpret_cast(frame->real_stack); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_alloca_poison(uptr addr, uptr size) { + uptr LeftRedzoneAddr = addr - kAllocaRedzoneSize; + uptr PartialRzAddr = addr + size; + uptr RightRzAddr = (PartialRzAddr + kAllocaRedzoneMask) & ~kAllocaRedzoneMask; + uptr PartialRzAligned = PartialRzAddr & ~(SHADOW_GRANULARITY - 1); + FastPoisonShadow(LeftRedzoneAddr, kAllocaRedzoneSize, kAsanAllocaLeftMagic); + FastPoisonShadowPartialRightRedzone( + PartialRzAligned, PartialRzAddr % SHADOW_GRANULARITY, + RightRzAddr - PartialRzAligned, kAsanAllocaRightMagic); + FastPoisonShadow(RightRzAddr, kAllocaRedzoneSize, kAsanAllocaRightMagic); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_allocas_unpoison(uptr top, uptr bottom) { + if ((!top) || (top > bottom)) return; + REAL(memset)(reinterpret_cast(MemToShadow(top)), 0, + (bottom - top) / SHADOW_GRANULARITY); +} +} // extern "C" diff --git a/libsanitizer/asan/asan_fake_stack.h b/libsanitizer/asan/asan_fake_stack.h index 6ac61ddd24e..270a19816d6 100644 --- a/libsanitizer/asan/asan_fake_stack.h +++ b/libsanitizer/asan/asan_fake_stack.h @@ -1,13 +1,14 @@ //===-- asan_fake_stack.h ---------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_fake_stack.cc, implements FakeStack. +// ASan-private header for asan_fake_stack.cpp, implements FakeStack. //===----------------------------------------------------------------------===// #ifndef ASAN_FAKE_STACK_H diff --git a/libsanitizer/asan/asan_flags.cc b/libsanitizer/asan/asan_flags.cc deleted file mode 100644 index 522fce30f05..00000000000 --- a/libsanitizer/asan/asan_flags.cc +++ /dev/null @@ -1,213 +0,0 @@ -//===-- asan_flags.cc -------------------------------------------*- C++ -*-===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// ASan flag parsing logic. -//===----------------------------------------------------------------------===// - -#include "asan_activation.h" -#include "asan_flags.h" -#include "asan_interface_internal.h" -#include "asan_stack.h" -#include "lsan/lsan_common.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_flags.h" -#include "sanitizer_common/sanitizer_flag_parser.h" -#include "ubsan/ubsan_flags.h" -#include "ubsan/ubsan_platform.h" - -namespace __asan { - -Flags asan_flags_dont_use_directly; // use via flags(). - -static const char *MaybeCallAsanDefaultOptions() { - return (&__asan_default_options) ? __asan_default_options() : ""; -} - -static const char *MaybeUseAsanDefaultOptionsCompileDefinition() { -#ifdef ASAN_DEFAULT_OPTIONS - return SANITIZER_STRINGIFY(ASAN_DEFAULT_OPTIONS); -#else - return ""; -#endif -} - -void Flags::SetDefaults() { -#define ASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; -#include "asan_flags.inc" -#undef ASAN_FLAG -} - -static void RegisterAsanFlags(FlagParser *parser, Flags *f) { -#define ASAN_FLAG(Type, Name, DefaultValue, Description) \ - RegisterFlag(parser, #Name, Description, &f->Name); -#include "asan_flags.inc" -#undef ASAN_FLAG -} - -void InitializeFlags() { - // Set the default values and prepare for parsing ASan and common flags. - SetCommonFlagsDefaults(); - { - CommonFlags cf; - cf.CopyFrom(*common_flags()); - cf.detect_leaks = cf.detect_leaks && CAN_SANITIZE_LEAKS; - cf.external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); - cf.malloc_context_size = kDefaultMallocContextSize; - cf.intercept_tls_get_addr = true; - cf.exitcode = 1; - OverrideCommonFlags(cf); - } - Flags *f = flags(); - f->SetDefaults(); - - FlagParser asan_parser; - RegisterAsanFlags(&asan_parser, f); - RegisterCommonFlags(&asan_parser); - - // Set the default values and prepare for parsing LSan and UBSan flags - // (which can also overwrite common flags). -#if CAN_SANITIZE_LEAKS - __lsan::Flags *lf = __lsan::flags(); - lf->SetDefaults(); - - FlagParser lsan_parser; - __lsan::RegisterLsanFlags(&lsan_parser, lf); - RegisterCommonFlags(&lsan_parser); -#endif - -#if CAN_SANITIZE_UB - __ubsan::Flags *uf = __ubsan::flags(); - uf->SetDefaults(); - - FlagParser ubsan_parser; - __ubsan::RegisterUbsanFlags(&ubsan_parser, uf); - RegisterCommonFlags(&ubsan_parser); -#endif - - if (SANITIZER_MAC) { - // Support macOS MallocScribble and MallocPreScribble: - // - if (GetEnv("MallocScribble")) { - f->max_free_fill_size = 0x1000; - } - if (GetEnv("MallocPreScribble")) { - f->malloc_fill_byte = 0xaa; - } - } - - // Override from ASan compile definition. - const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); - asan_parser.ParseString(asan_compile_def); - - // Override from user-specified string. - const char *asan_default_options = MaybeCallAsanDefaultOptions(); - asan_parser.ParseString(asan_default_options); -#if CAN_SANITIZE_UB - const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions(); - ubsan_parser.ParseString(ubsan_default_options); -#endif -#if CAN_SANITIZE_LEAKS - const char *lsan_default_options = __lsan::MaybeCallLsanDefaultOptions(); - lsan_parser.ParseString(lsan_default_options); -#endif - - // Override from command line. - asan_parser.ParseString(GetEnv("ASAN_OPTIONS")); -#if CAN_SANITIZE_LEAKS - lsan_parser.ParseString(GetEnv("LSAN_OPTIONS")); -#endif -#if CAN_SANITIZE_UB - ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS")); -#endif - - InitializeCommonFlags(); - - // TODO(eugenis): dump all flags at verbosity>=2? - if (Verbosity()) ReportUnrecognizedFlags(); - - if (common_flags()->help) { - // TODO(samsonov): print all of the flags (ASan, LSan, common). - asan_parser.PrintFlagDescriptions(); - } - - // Flag validation: - if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) { - Report("%s: detect_leaks is not supported on this platform.\n", - SanitizerToolName); - Die(); - } - // Ensure that redzone is at least SHADOW_GRANULARITY. - if (f->redzone < (int)SHADOW_GRANULARITY) - f->redzone = SHADOW_GRANULARITY; - // Make "strict_init_order" imply "check_initialization_order". - // TODO(samsonov): Use a single runtime flag for an init-order checker. - if (f->strict_init_order) { - f->check_initialization_order = true; - } - CHECK_LE((uptr)common_flags()->malloc_context_size, kStackTraceMax); - CHECK_LE(f->min_uar_stack_size_log, f->max_uar_stack_size_log); - CHECK_GE(f->redzone, 16); - CHECK_GE(f->max_redzone, f->redzone); - CHECK_LE(f->max_redzone, 2048); - CHECK(IsPowerOfTwo(f->redzone)); - CHECK(IsPowerOfTwo(f->max_redzone)); - if (SANITIZER_RTEMS) { - CHECK(!f->unmap_shadow_on_exit); - CHECK(!f->protect_shadow_gap); - } - - // quarantine_size is deprecated but we still honor it. - // quarantine_size can not be used together with quarantine_size_mb. - if (f->quarantine_size >= 0 && f->quarantine_size_mb >= 0) { - Report("%s: please use either 'quarantine_size' (deprecated) or " - "quarantine_size_mb, but not both\n", SanitizerToolName); - Die(); - } - if (f->quarantine_size >= 0) - f->quarantine_size_mb = f->quarantine_size >> 20; - if (f->quarantine_size_mb < 0) { - const int kDefaultQuarantineSizeMb = - (ASAN_LOW_MEMORY) ? 1UL << 4 : 1UL << 8; - f->quarantine_size_mb = kDefaultQuarantineSizeMb; - } - if (f->thread_local_quarantine_size_kb < 0) { - const u32 kDefaultThreadLocalQuarantineSizeKb = - // It is not advised to go lower than 64Kb, otherwise quarantine batches - // pushed from thread local quarantine to global one will create too - // much overhead. One quarantine batch size is 8Kb and it holds up to - // 1021 chunk, which amounts to 1/8 memory overhead per batch when - // thread local quarantine is set to 64Kb. - (ASAN_LOW_MEMORY) ? 1 << 6 : FIRST_32_SECOND_64(1 << 8, 1 << 10); - f->thread_local_quarantine_size_kb = kDefaultThreadLocalQuarantineSizeKb; - } - if (f->thread_local_quarantine_size_kb == 0 && f->quarantine_size_mb > 0) { - Report("%s: thread_local_quarantine_size_kb can be set to 0 only when " - "quarantine_size_mb is set to 0\n", SanitizerToolName); - Die(); - } - if (!f->replace_str && common_flags()->intercept_strlen) { - Report("WARNING: strlen interceptor is enabled even though replace_str=0. " - "Use intercept_strlen=0 to disable it."); - } - if (!f->replace_str && common_flags()->intercept_strchr) { - Report("WARNING: strchr* interceptors are enabled even though " - "replace_str=0. Use intercept_strchr=0 to disable them."); - } - if (!f->replace_str && common_flags()->intercept_strndup) { - Report("WARNING: strndup* interceptors are enabled even though " - "replace_str=0. Use intercept_strndup=0 to disable them."); - } -} - -} // namespace __asan - -SANITIZER_INTERFACE_WEAK_DEF(const char*, __asan_default_options, void) { - return ""; -} diff --git a/libsanitizer/asan/asan_flags.cpp b/libsanitizer/asan/asan_flags.cpp new file mode 100644 index 00000000000..c5c70eaed73 --- /dev/null +++ b/libsanitizer/asan/asan_flags.cpp @@ -0,0 +1,214 @@ +//===-- asan_flags.cpp ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan flag parsing logic. +//===----------------------------------------------------------------------===// + +#include "asan_activation.h" +#include "asan_flags.h" +#include "asan_interface_internal.h" +#include "asan_stack.h" +#include "lsan/lsan_common.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" +#include "ubsan/ubsan_flags.h" +#include "ubsan/ubsan_platform.h" + +namespace __asan { + +Flags asan_flags_dont_use_directly; // use via flags(). + +static const char *MaybeCallAsanDefaultOptions() { + return (&__asan_default_options) ? __asan_default_options() : ""; +} + +static const char *MaybeUseAsanDefaultOptionsCompileDefinition() { +#ifdef ASAN_DEFAULT_OPTIONS + return SANITIZER_STRINGIFY(ASAN_DEFAULT_OPTIONS); +#else + return ""; +#endif +} + +void Flags::SetDefaults() { +#define ASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "asan_flags.inc" +#undef ASAN_FLAG +} + +static void RegisterAsanFlags(FlagParser *parser, Flags *f) { +#define ASAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "asan_flags.inc" +#undef ASAN_FLAG +} + +void InitializeFlags() { + // Set the default values and prepare for parsing ASan and common flags. + SetCommonFlagsDefaults(); + { + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.detect_leaks = cf.detect_leaks && CAN_SANITIZE_LEAKS; + cf.external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); + cf.malloc_context_size = kDefaultMallocContextSize; + cf.intercept_tls_get_addr = true; + cf.exitcode = 1; + OverrideCommonFlags(cf); + } + Flags *f = flags(); + f->SetDefaults(); + + FlagParser asan_parser; + RegisterAsanFlags(&asan_parser, f); + RegisterCommonFlags(&asan_parser); + + // Set the default values and prepare for parsing LSan and UBSan flags + // (which can also overwrite common flags). +#if CAN_SANITIZE_LEAKS + __lsan::Flags *lf = __lsan::flags(); + lf->SetDefaults(); + + FlagParser lsan_parser; + __lsan::RegisterLsanFlags(&lsan_parser, lf); + RegisterCommonFlags(&lsan_parser); +#endif + +#if CAN_SANITIZE_UB + __ubsan::Flags *uf = __ubsan::flags(); + uf->SetDefaults(); + + FlagParser ubsan_parser; + __ubsan::RegisterUbsanFlags(&ubsan_parser, uf); + RegisterCommonFlags(&ubsan_parser); +#endif + + if (SANITIZER_MAC) { + // Support macOS MallocScribble and MallocPreScribble: + // + if (GetEnv("MallocScribble")) { + f->max_free_fill_size = 0x1000; + } + if (GetEnv("MallocPreScribble")) { + f->malloc_fill_byte = 0xaa; + } + } + + // Override from ASan compile definition. + const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); + asan_parser.ParseString(asan_compile_def); + + // Override from user-specified string. + const char *asan_default_options = MaybeCallAsanDefaultOptions(); + asan_parser.ParseString(asan_default_options); +#if CAN_SANITIZE_UB + const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions(); + ubsan_parser.ParseString(ubsan_default_options); +#endif +#if CAN_SANITIZE_LEAKS + const char *lsan_default_options = __lsan::MaybeCallLsanDefaultOptions(); + lsan_parser.ParseString(lsan_default_options); +#endif + + // Override from command line. + asan_parser.ParseStringFromEnv("ASAN_OPTIONS"); +#if CAN_SANITIZE_LEAKS + lsan_parser.ParseStringFromEnv("LSAN_OPTIONS"); +#endif +#if CAN_SANITIZE_UB + ubsan_parser.ParseStringFromEnv("UBSAN_OPTIONS"); +#endif + + InitializeCommonFlags(); + + // TODO(eugenis): dump all flags at verbosity>=2? + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) { + // TODO(samsonov): print all of the flags (ASan, LSan, common). + asan_parser.PrintFlagDescriptions(); + } + + // Flag validation: + if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) { + Report("%s: detect_leaks is not supported on this platform.\n", + SanitizerToolName); + Die(); + } + // Ensure that redzone is at least SHADOW_GRANULARITY. + if (f->redzone < (int)SHADOW_GRANULARITY) + f->redzone = SHADOW_GRANULARITY; + // Make "strict_init_order" imply "check_initialization_order". + // TODO(samsonov): Use a single runtime flag for an init-order checker. + if (f->strict_init_order) { + f->check_initialization_order = true; + } + CHECK_LE((uptr)common_flags()->malloc_context_size, kStackTraceMax); + CHECK_LE(f->min_uar_stack_size_log, f->max_uar_stack_size_log); + CHECK_GE(f->redzone, 16); + CHECK_GE(f->max_redzone, f->redzone); + CHECK_LE(f->max_redzone, 2048); + CHECK(IsPowerOfTwo(f->redzone)); + CHECK(IsPowerOfTwo(f->max_redzone)); + if (SANITIZER_RTEMS) { + CHECK(!f->unmap_shadow_on_exit); + CHECK(!f->protect_shadow_gap); + } + + // quarantine_size is deprecated but we still honor it. + // quarantine_size can not be used together with quarantine_size_mb. + if (f->quarantine_size >= 0 && f->quarantine_size_mb >= 0) { + Report("%s: please use either 'quarantine_size' (deprecated) or " + "quarantine_size_mb, but not both\n", SanitizerToolName); + Die(); + } + if (f->quarantine_size >= 0) + f->quarantine_size_mb = f->quarantine_size >> 20; + if (f->quarantine_size_mb < 0) { + const int kDefaultQuarantineSizeMb = + (ASAN_LOW_MEMORY) ? 1UL << 4 : 1UL << 8; + f->quarantine_size_mb = kDefaultQuarantineSizeMb; + } + if (f->thread_local_quarantine_size_kb < 0) { + const u32 kDefaultThreadLocalQuarantineSizeKb = + // It is not advised to go lower than 64Kb, otherwise quarantine batches + // pushed from thread local quarantine to global one will create too + // much overhead. One quarantine batch size is 8Kb and it holds up to + // 1021 chunk, which amounts to 1/8 memory overhead per batch when + // thread local quarantine is set to 64Kb. + (ASAN_LOW_MEMORY) ? 1 << 6 : FIRST_32_SECOND_64(1 << 8, 1 << 10); + f->thread_local_quarantine_size_kb = kDefaultThreadLocalQuarantineSizeKb; + } + if (f->thread_local_quarantine_size_kb == 0 && f->quarantine_size_mb > 0) { + Report("%s: thread_local_quarantine_size_kb can be set to 0 only when " + "quarantine_size_mb is set to 0\n", SanitizerToolName); + Die(); + } + if (!f->replace_str && common_flags()->intercept_strlen) { + Report("WARNING: strlen interceptor is enabled even though replace_str=0. " + "Use intercept_strlen=0 to disable it."); + } + if (!f->replace_str && common_flags()->intercept_strchr) { + Report("WARNING: strchr* interceptors are enabled even though " + "replace_str=0. Use intercept_strchr=0 to disable them."); + } + if (!f->replace_str && common_flags()->intercept_strndup) { + Report("WARNING: strndup* interceptors are enabled even though " + "replace_str=0. Use intercept_strndup=0 to disable them."); + } +} + +} // namespace __asan + +SANITIZER_INTERFACE_WEAK_DEF(const char*, __asan_default_options, void) { + return ""; +} diff --git a/libsanitizer/asan/asan_flags.h b/libsanitizer/asan/asan_flags.h index 6b33789b84c..b55c81f07d4 100644 --- a/libsanitizer/asan/asan_flags.h +++ b/libsanitizer/asan/asan_flags.h @@ -1,7 +1,8 @@ //===-- asan_flags.h -------------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/asan/asan_flags.inc b/libsanitizer/asan/asan_flags.inc index 9cd1f60db60..d360e03ca55 100644 --- a/libsanitizer/asan/asan_flags.inc +++ b/libsanitizer/asan/asan_flags.inc @@ -1,7 +1,8 @@ //===-- asan_flags.inc ------------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -150,8 +151,6 @@ ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.") ASAN_FLAG(bool, halt_on_error, true, "Crash the program after printing the first error report " "(WARNING: USE AT YOUR OWN RISK!)") -ASAN_FLAG(bool, use_odr_indicator, false, - "Use special ODR indicator symbol for ODR violation detection") ASAN_FLAG(bool, allocator_frees_and_returns_null_on_realloc_zero, true, "realloc(p, 0) is equivalent to free(p) by default (Same as the " "POSIX standard). If set to false, realloc(p, 0) will return a " @@ -159,3 +158,5 @@ ASAN_FLAG(bool, allocator_frees_and_returns_null_on_realloc_zero, true, ASAN_FLAG(bool, verify_asan_link_order, true, "Check position of ASan runtime in library list (needs to be disabled" " when other library has to be preloaded system-wide)") +ASAN_FLAG(bool, windows_hook_rtl_allocators, false, + "(Windows only) enable hooking of Rtl(Allocate|Free|Size|ReAllocate)Heap.") diff --git a/libsanitizer/asan/asan_fuchsia.cc b/libsanitizer/asan/asan_fuchsia.cc deleted file mode 100644 index f8207ecccd1..00000000000 --- a/libsanitizer/asan/asan_fuchsia.cc +++ /dev/null @@ -1,216 +0,0 @@ -//===-- asan_fuchsia.cc --------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===---------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Fuchsia-specific details. -//===---------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_fuchsia.h" -#if SANITIZER_FUCHSIA - -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_stack.h" -#include "asan_thread.h" - -#include -#include -#include -#include - -namespace __asan { - -// The system already set up the shadow memory for us. -// __sanitizer::GetMaxUserVirtualAddress has already been called by -// AsanInitInternal->InitializeHighMemEnd (asan_rtl.cc). -// Just do some additional sanity checks here. -void InitializeShadowMemory() { - if (Verbosity()) PrintAddressSpaceLayout(); - - // Make sure SHADOW_OFFSET doesn't use __asan_shadow_memory_dynamic_address. - __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel; - DCHECK(kLowShadowBeg != kDefaultShadowSentinel); - __asan_shadow_memory_dynamic_address = kLowShadowBeg; - - CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); - CHECK_EQ(kHighMemEnd, __sanitizer::ShadowBounds.memory_limit - 1); - CHECK_EQ(kHighMemBeg, __sanitizer::ShadowBounds.shadow_limit); - CHECK_EQ(kHighShadowBeg, __sanitizer::ShadowBounds.shadow_base); - CHECK_EQ(kShadowGapEnd, __sanitizer::ShadowBounds.shadow_base - 1); - CHECK_EQ(kLowShadowEnd, 0); - CHECK_EQ(kLowShadowBeg, 0); -} - -void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { - UNIMPLEMENTED(); -} - -void AsanCheckDynamicRTPrereqs() {} -void AsanCheckIncompatibleRT() {} -void InitializeAsanInterceptors() {} - -void *AsanDoesNotSupportStaticLinkage() { return nullptr; } - -void InitializePlatformExceptionHandlers() {} -void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { - UNIMPLEMENTED(); -} - -// We can use a plain thread_local variable for TSD. -static thread_local void *per_thread; - -void *AsanTSDGet() { return per_thread; } - -void AsanTSDSet(void *tsd) { per_thread = tsd; } - -// There's no initialization needed, and the passed-in destructor -// will never be called. Instead, our own thread destruction hook -// (below) will call AsanThread::TSDDtor directly. -void AsanTSDInit(void (*destructor)(void *tsd)) { - DCHECK(destructor == &PlatformTSDDtor); -} - -void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); } - -static inline size_t AsanThreadMmapSize() { - return RoundUpTo(sizeof(AsanThread), PAGE_SIZE); -} - -struct AsanThread::InitOptions { - uptr stack_bottom, stack_size; -}; - -// Shared setup between thread creation and startup for the initial thread. -static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid, - uptr user_id, bool detached, - const char *name, uptr stack_bottom, - uptr stack_size) { - // In lieu of AsanThread::Create. - AsanThread *thread = (AsanThread *)MmapOrDie(AsanThreadMmapSize(), __func__); - - AsanThreadContext::CreateThreadContextArgs args = {thread, stack}; - u32 tid = - asanThreadRegistry().CreateThread(user_id, detached, parent_tid, &args); - asanThreadRegistry().SetThreadName(tid, name); - - // On other systems, AsanThread::Init() is called from the new - // thread itself. But on Fuchsia we already know the stack address - // range beforehand, so we can do most of the setup right now. - const AsanThread::InitOptions options = {stack_bottom, stack_size}; - thread->Init(&options); - - return thread; -} - -// This gets the same arguments passed to Init by CreateAsanThread, above. -// We're in the creator thread before the new thread is actually started, -// but its stack address range is already known. We don't bother tracking -// the static TLS address range because the system itself already uses an -// ASan-aware allocator for that. -void AsanThread::SetThreadStackAndTls(const AsanThread::InitOptions *options) { - DCHECK_NE(GetCurrentThread(), this); - DCHECK_NE(GetCurrentThread(), nullptr); - CHECK_NE(options->stack_bottom, 0); - CHECK_NE(options->stack_size, 0); - stack_bottom_ = options->stack_bottom; - stack_top_ = options->stack_bottom + options->stack_size; -} - -// Called by __asan::AsanInitInternal (asan_rtl.c). -AsanThread *CreateMainThread() { - thrd_t self = thrd_current(); - char name[ZX_MAX_NAME_LEN]; - CHECK_NE(__sanitizer::MainThreadStackBase, 0); - CHECK_GT(__sanitizer::MainThreadStackSize, 0); - AsanThread *t = CreateAsanThread( - nullptr, 0, reinterpret_cast(self), true, - _zx_object_get_property(thrd_get_zx_handle(self), ZX_PROP_NAME, name, - sizeof(name)) == ZX_OK - ? name - : nullptr, - __sanitizer::MainThreadStackBase, __sanitizer::MainThreadStackSize); - SetCurrentThread(t); - return t; -} - -// This is called before each thread creation is attempted. So, in -// its first call, the calling thread is the initial and sole thread. -static void *BeforeThreadCreateHook(uptr user_id, bool detached, - const char *name, uptr stack_bottom, - uptr stack_size) { - EnsureMainThreadIDIsCorrect(); - // Strict init-order checking is thread-hostile. - if (flags()->strict_init_order) StopInitOrderChecking(); - - GET_STACK_TRACE_THREAD; - u32 parent_tid = GetCurrentTidOrInvalid(); - - return CreateAsanThread(&stack, parent_tid, user_id, detached, name, - stack_bottom, stack_size); -} - -// This is called after creating a new thread (in the creating thread), -// with the pointer returned by BeforeThreadCreateHook (above). -static void ThreadCreateHook(void *hook, bool aborted) { - AsanThread *thread = static_cast(hook); - if (!aborted) { - // The thread was created successfully. - // ThreadStartHook is already running in the new thread. - } else { - // The thread wasn't created after all. - // Clean up everything we set up in BeforeThreadCreateHook. - asanThreadRegistry().FinishThread(thread->tid()); - UnmapOrDie(thread, AsanThreadMmapSize()); - } -} - -// This is called in the newly-created thread before it runs anything else, -// with the pointer returned by BeforeThreadCreateHook (above). -// cf. asan_interceptors.cc:asan_thread_start -static void ThreadStartHook(void *hook, uptr os_id) { - AsanThread *thread = static_cast(hook); - SetCurrentThread(thread); - - // In lieu of AsanThread::ThreadStart. - asanThreadRegistry().StartThread(thread->tid(), os_id, /*workerthread*/ false, - nullptr); -} - -// Each thread runs this just before it exits, -// with the pointer returned by BeforeThreadCreateHook (above). -// All per-thread destructors have already been called. -static void ThreadExitHook(void *hook, uptr os_id) { - AsanThread::TSDDtor(per_thread); -} - -} // namespace __asan - -// These are declared (in extern "C") by . -// The system runtime will call our definitions directly. - -void *__sanitizer_before_thread_create_hook(thrd_t thread, bool detached, - const char *name, void *stack_base, - size_t stack_size) { - return __asan::BeforeThreadCreateHook( - reinterpret_cast(thread), detached, name, - reinterpret_cast(stack_base), stack_size); -} - -void __sanitizer_thread_create_hook(void *hook, thrd_t thread, int error) { - __asan::ThreadCreateHook(hook, error != thrd_success); -} - -void __sanitizer_thread_start_hook(void *hook, thrd_t self) { - __asan::ThreadStartHook(hook, reinterpret_cast(self)); -} - -void __sanitizer_thread_exit_hook(void *hook, thrd_t self) { - __asan::ThreadExitHook(hook, reinterpret_cast(self)); -} - -#endif // SANITIZER_FUCHSIA diff --git a/libsanitizer/asan/asan_fuchsia.cpp b/libsanitizer/asan/asan_fuchsia.cpp new file mode 100644 index 00000000000..f8b2d5f2697 --- /dev/null +++ b/libsanitizer/asan/asan_fuchsia.cpp @@ -0,0 +1,224 @@ +//===-- asan_fuchsia.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===---------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Fuchsia-specific details. +//===---------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_fuchsia.h" +#if SANITIZER_FUCHSIA + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_stack.h" +#include "asan_thread.h" + +#include +#include +#include +#include + +namespace __asan { + +// The system already set up the shadow memory for us. +// __sanitizer::GetMaxUserVirtualAddress has already been called by +// AsanInitInternal->InitializeHighMemEnd (asan_rtl.cpp). +// Just do some additional sanity checks here. +void InitializeShadowMemory() { + if (Verbosity()) PrintAddressSpaceLayout(); + + // Make sure SHADOW_OFFSET doesn't use __asan_shadow_memory_dynamic_address. + __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel; + DCHECK(kLowShadowBeg != kDefaultShadowSentinel); + __asan_shadow_memory_dynamic_address = kLowShadowBeg; + + CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); + CHECK_EQ(kHighMemEnd, __sanitizer::ShadowBounds.memory_limit - 1); + CHECK_EQ(kHighMemBeg, __sanitizer::ShadowBounds.shadow_limit); + CHECK_EQ(kHighShadowBeg, __sanitizer::ShadowBounds.shadow_base); + CHECK_EQ(kShadowGapEnd, __sanitizer::ShadowBounds.shadow_base - 1); + CHECK_EQ(kLowShadowEnd, 0); + CHECK_EQ(kLowShadowBeg, 0); +} + +void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { + UNIMPLEMENTED(); +} + +void AsanCheckDynamicRTPrereqs() {} +void AsanCheckIncompatibleRT() {} +void InitializeAsanInterceptors() {} + +void *AsanDoesNotSupportStaticLinkage() { return nullptr; } + +void InitializePlatformExceptionHandlers() {} +void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { + UNIMPLEMENTED(); +} + +// We can use a plain thread_local variable for TSD. +static thread_local void *per_thread; + +void *AsanTSDGet() { return per_thread; } + +void AsanTSDSet(void *tsd) { per_thread = tsd; } + +// There's no initialization needed, and the passed-in destructor +// will never be called. Instead, our own thread destruction hook +// (below) will call AsanThread::TSDDtor directly. +void AsanTSDInit(void (*destructor)(void *tsd)) { + DCHECK(destructor == &PlatformTSDDtor); +} + +void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); } + +static inline size_t AsanThreadMmapSize() { + return RoundUpTo(sizeof(AsanThread), PAGE_SIZE); +} + +struct AsanThread::InitOptions { + uptr stack_bottom, stack_size; +}; + +// Shared setup between thread creation and startup for the initial thread. +static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid, + uptr user_id, bool detached, + const char *name, uptr stack_bottom, + uptr stack_size) { + // In lieu of AsanThread::Create. + AsanThread *thread = (AsanThread *)MmapOrDie(AsanThreadMmapSize(), __func__); + + AsanThreadContext::CreateThreadContextArgs args = {thread, stack}; + u32 tid = + asanThreadRegistry().CreateThread(user_id, detached, parent_tid, &args); + asanThreadRegistry().SetThreadName(tid, name); + + // On other systems, AsanThread::Init() is called from the new + // thread itself. But on Fuchsia we already know the stack address + // range beforehand, so we can do most of the setup right now. + const AsanThread::InitOptions options = {stack_bottom, stack_size}; + thread->Init(&options); + + return thread; +} + +// This gets the same arguments passed to Init by CreateAsanThread, above. +// We're in the creator thread before the new thread is actually started, +// but its stack address range is already known. We don't bother tracking +// the static TLS address range because the system itself already uses an +// ASan-aware allocator for that. +void AsanThread::SetThreadStackAndTls(const AsanThread::InitOptions *options) { + DCHECK_NE(GetCurrentThread(), this); + DCHECK_NE(GetCurrentThread(), nullptr); + CHECK_NE(options->stack_bottom, 0); + CHECK_NE(options->stack_size, 0); + stack_bottom_ = options->stack_bottom; + stack_top_ = options->stack_bottom + options->stack_size; +} + +// Called by __asan::AsanInitInternal (asan_rtl.c). +AsanThread *CreateMainThread() { + thrd_t self = thrd_current(); + char name[ZX_MAX_NAME_LEN]; + CHECK_NE(__sanitizer::MainThreadStackBase, 0); + CHECK_GT(__sanitizer::MainThreadStackSize, 0); + AsanThread *t = CreateAsanThread( + nullptr, 0, reinterpret_cast(self), true, + _zx_object_get_property(thrd_get_zx_handle(self), ZX_PROP_NAME, name, + sizeof(name)) == ZX_OK + ? name + : nullptr, + __sanitizer::MainThreadStackBase, __sanitizer::MainThreadStackSize); + SetCurrentThread(t); + return t; +} + +// This is called before each thread creation is attempted. So, in +// its first call, the calling thread is the initial and sole thread. +static void *BeforeThreadCreateHook(uptr user_id, bool detached, + const char *name, uptr stack_bottom, + uptr stack_size) { + EnsureMainThreadIDIsCorrect(); + // Strict init-order checking is thread-hostile. + if (flags()->strict_init_order) StopInitOrderChecking(); + + GET_STACK_TRACE_THREAD; + u32 parent_tid = GetCurrentTidOrInvalid(); + + return CreateAsanThread(&stack, parent_tid, user_id, detached, name, + stack_bottom, stack_size); +} + +// This is called after creating a new thread (in the creating thread), +// with the pointer returned by BeforeThreadCreateHook (above). +static void ThreadCreateHook(void *hook, bool aborted) { + AsanThread *thread = static_cast(hook); + if (!aborted) { + // The thread was created successfully. + // ThreadStartHook is already running in the new thread. + } else { + // The thread wasn't created after all. + // Clean up everything we set up in BeforeThreadCreateHook. + asanThreadRegistry().FinishThread(thread->tid()); + UnmapOrDie(thread, AsanThreadMmapSize()); + } +} + +// This is called in the newly-created thread before it runs anything else, +// with the pointer returned by BeforeThreadCreateHook (above). +// cf. asan_interceptors.cpp:asan_thread_start +static void ThreadStartHook(void *hook, uptr os_id) { + AsanThread *thread = static_cast(hook); + SetCurrentThread(thread); + + // In lieu of AsanThread::ThreadStart. + asanThreadRegistry().StartThread(thread->tid(), os_id, ThreadType::Regular, + nullptr); +} + +// Each thread runs this just before it exits, +// with the pointer returned by BeforeThreadCreateHook (above). +// All per-thread destructors have already been called. +static void ThreadExitHook(void *hook, uptr os_id) { + AsanThread::TSDDtor(per_thread); +} + +bool HandleDlopenInit() { + // Not supported on this platform. + static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN, + "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false"); + return false; +} + +} // namespace __asan + +// These are declared (in extern "C") by . +// The system runtime will call our definitions directly. + +void *__sanitizer_before_thread_create_hook(thrd_t thread, bool detached, + const char *name, void *stack_base, + size_t stack_size) { + return __asan::BeforeThreadCreateHook( + reinterpret_cast(thread), detached, name, + reinterpret_cast(stack_base), stack_size); +} + +void __sanitizer_thread_create_hook(void *hook, thrd_t thread, int error) { + __asan::ThreadCreateHook(hook, error != thrd_success); +} + +void __sanitizer_thread_start_hook(void *hook, thrd_t self) { + __asan::ThreadStartHook(hook, reinterpret_cast(self)); +} + +void __sanitizer_thread_exit_hook(void *hook, thrd_t self) { + __asan::ThreadExitHook(hook, reinterpret_cast(self)); +} + +#endif // SANITIZER_FUCHSIA diff --git a/libsanitizer/asan/asan_globals.cc b/libsanitizer/asan/asan_globals.cc deleted file mode 100644 index 10f090940fe..00000000000 --- a/libsanitizer/asan/asan_globals.cc +++ /dev/null @@ -1,445 +0,0 @@ -//===-- asan_globals.cc ---------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Handle globals. -//===----------------------------------------------------------------------===// - -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "asan_poisoning.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "asan_stats.h" -#include "asan_suppressions.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_mutex.h" -#include "sanitizer_common/sanitizer_placement_new.h" -#include "sanitizer_common/sanitizer_stackdepot.h" -#include "sanitizer_common/sanitizer_symbolizer.h" - -namespace __asan { - -typedef __asan_global Global; - -struct ListOfGlobals { - const Global *g; - ListOfGlobals *next; -}; - -static BlockingMutex mu_for_globals(LINKER_INITIALIZED); -static LowLevelAllocator allocator_for_globals; -static ListOfGlobals *list_of_all_globals; - -static const int kDynamicInitGlobalsInitialCapacity = 512; -struct DynInitGlobal { - Global g; - bool initialized; -}; -typedef InternalMmapVector VectorOfGlobals; -// Lazy-initialized and never deleted. -static VectorOfGlobals *dynamic_init_globals; - -// We want to remember where a certain range of globals was registered. -struct GlobalRegistrationSite { - u32 stack_id; - Global *g_first, *g_last; -}; -typedef InternalMmapVector GlobalRegistrationSiteVector; -static GlobalRegistrationSiteVector *global_registration_site_vector; - -ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { - FastPoisonShadow(g->beg, g->size_with_redzone, value); -} - -ALWAYS_INLINE void PoisonRedZones(const Global &g) { - uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); - FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, - kAsanGlobalRedzoneMagic); - if (g.size != aligned_size) { - FastPoisonShadowPartialRightRedzone( - g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), - g.size % SHADOW_GRANULARITY, - SHADOW_GRANULARITY, - kAsanGlobalRedzoneMagic); - } -} - -const uptr kMinimalDistanceFromAnotherGlobal = 64; - -static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) { - if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; - if (addr >= g.beg + g.size_with_redzone) return false; - return true; -} - -static void ReportGlobal(const Global &g, const char *prefix) { - Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n", - prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name, - g.module_name, g.has_dynamic_init); - if (g.location) { - Report(" location (%p): name=%s[%p], %d %d\n", g.location, - g.location->filename, g.location->filename, g.location->line_no, - g.location->column_no); - } -} - -static u32 FindRegistrationSite(const Global *g) { - mu_for_globals.CheckLocked(); - CHECK(global_registration_site_vector); - for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) { - GlobalRegistrationSite &grs = (*global_registration_site_vector)[i]; - if (g >= grs.g_first && g <= grs.g_last) - return grs.stack_id; - } - return 0; -} - -int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites, - int max_globals) { - if (!flags()->report_globals) return 0; - BlockingMutexLock lock(&mu_for_globals); - int res = 0; - for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { - const Global &g = *l->g; - if (flags()->report_globals >= 2) - ReportGlobal(g, "Search"); - if (IsAddressNearGlobal(addr, g)) { -#if defined(__GNUC__) && defined(__sparc__) - internal_memcpy(&globals[res], &g, sizeof(g)); -#else - globals[res] = g; -#endif - if (reg_sites) - reg_sites[res] = FindRegistrationSite(&g); - res++; - if (res == max_globals) break; - } - } - return res; -} - -enum GlobalSymbolState { - UNREGISTERED = 0, - REGISTERED = 1 -}; - -// Check ODR violation for given global G via special ODR indicator. We use -// this method in case compiler instruments global variables through their -// local aliases. -static void CheckODRViolationViaIndicator(const Global *g) { - u8 *odr_indicator = reinterpret_cast(g->odr_indicator); - if (*odr_indicator == UNREGISTERED) { - *odr_indicator = REGISTERED; - return; - } - // If *odr_indicator is DEFINED, some module have already registered - // externally visible symbol with the same name. This is an ODR violation. - for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { - if (g->odr_indicator == l->g->odr_indicator && - (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && - !IsODRViolationSuppressed(g->name)) - ReportODRViolation(g, FindRegistrationSite(g), - l->g, FindRegistrationSite(l->g)); - } -} - -// Clang provides two different ways for global variables protection: -// it can poison the global itself or its private alias. In former -// case we may poison same symbol multiple times, that can help us to -// cheaply detect ODR violation: if we try to poison an already poisoned -// global, we have ODR violation error. -// In latter case, we poison each symbol exactly once, so we use special -// indicator symbol to perform similar check. -// In either case, compiler provides a special odr_indicator field to Global -// structure, that can contain two kinds of values: -// 1) Non-zero value. In this case, odr_indicator is an address of -// corresponding indicator variable for given global. -// 2) Zero. This means that we don't use private aliases for global variables -// and can freely check ODR violation with the first method. -// -// This routine chooses between two different methods of ODR violation -// detection. -static inline bool UseODRIndicator(const Global *g) { - // Use ODR indicator method iff use_odr_indicator flag is set and - // indicator symbol address is not 0. - return flags()->use_odr_indicator && g->odr_indicator > 0; -} - -// Register a global variable. -// This function may be called more than once for every global -// so we store the globals in a map. -static void RegisterGlobal(const Global *g) { - CHECK(asan_inited); - if (flags()->report_globals >= 2) - ReportGlobal(*g, "Added"); - CHECK(flags()->report_globals); - CHECK(AddrIsInMem(g->beg)); - if (!AddrIsAlignedByGranularity(g->beg)) { - Report("The following global variable is not properly aligned.\n"); - Report("This may happen if another global with the same name\n"); - Report("resides in another non-instrumented module.\n"); - Report("Or the global comes from a C file built w/o -fno-common.\n"); - Report("In either case this is likely an ODR violation bug,\n"); - Report("but AddressSanitizer can not provide more details.\n"); - ReportODRViolation(g, FindRegistrationSite(g), g, FindRegistrationSite(g)); - CHECK(AddrIsAlignedByGranularity(g->beg)); - } - CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - if (flags()->detect_odr_violation) { - // Try detecting ODR (One Definition Rule) violation, i.e. the situation - // where two globals with the same name are defined in different modules. - if (UseODRIndicator(g)) - CheckODRViolationViaIndicator(g); - } - if (CanPoisonMemory()) - PoisonRedZones(*g); - ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals; - l->g = g; - l->next = list_of_all_globals; - list_of_all_globals = l; - if (g->has_dynamic_init) { - if (!dynamic_init_globals) { - dynamic_init_globals = - new (allocator_for_globals) VectorOfGlobals; // NOLINT - dynamic_init_globals->reserve(kDynamicInitGlobalsInitialCapacity); - } - DynInitGlobal dyn_global = { *g, false }; - dynamic_init_globals->push_back(dyn_global); - } -} - -static void UnregisterGlobal(const Global *g) { - CHECK(asan_inited); - if (flags()->report_globals >= 2) - ReportGlobal(*g, "Removed"); - CHECK(flags()->report_globals); - CHECK(AddrIsInMem(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - if (CanPoisonMemory()) - PoisonShadowForGlobal(g, 0); - // We unpoison the shadow memory for the global but we do not remove it from - // the list because that would require O(n^2) time with the current list - // implementation. It might not be worth doing anyway. - - // Release ODR indicator. - if (UseODRIndicator(g)) { - u8 *odr_indicator = reinterpret_cast(g->odr_indicator); - *odr_indicator = UNREGISTERED; - } -} - -void StopInitOrderChecking() { - BlockingMutexLock lock(&mu_for_globals); - if (!flags()->check_initialization_order || !dynamic_init_globals) - return; - flags()->check_initialization_order = false; - for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { - DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; - const Global *g = &dyn_g.g; - // Unpoison the whole global. - PoisonShadowForGlobal(g, 0); - // Poison redzones back. - PoisonRedZones(*g); - } -} - -static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; } - -const char *MaybeDemangleGlobalName(const char *name) { - // We can spoil names of globals with C linkage, so use an heuristic - // approach to check if the name should be demangled. - bool should_demangle = false; - if (name[0] == '_' && name[1] == 'Z') - should_demangle = true; - else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?') - should_demangle = true; - - return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name; -} - -// Check if the global is a zero-terminated ASCII string. If so, print it. -void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) { - for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { - unsigned char c = *(unsigned char *)p; - if (c == '\0' || !IsASCII(c)) return; - } - if (*(char *)(g.beg + g.size - 1) != '\0') return; - str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name), - (char *)g.beg); -} - -static const char *GlobalFilename(const __asan_global &g) { - const char *res = g.module_name; - // Prefer the filename from source location, if is available. - if (g.location) res = g.location->filename; - CHECK(res); - return res; -} - -void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g) { - str->append("%s", GlobalFilename(g)); - if (!g.location) return; - if (g.location->line_no) str->append(":%d", g.location->line_no); - if (g.location->column_no) str->append(":%d", g.location->column_no); -} - -} // namespace __asan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - - -// Apply __asan_register_globals to all globals found in the same loaded -// executable or shared library as `flag'. The flag tracks whether globals have -// already been registered or not for this image. -void __asan_register_image_globals(uptr *flag) { - if (*flag) - return; - AsanApplyToGlobals(__asan_register_globals, flag); - *flag = 1; -} - -// This mirrors __asan_register_image_globals. -void __asan_unregister_image_globals(uptr *flag) { - if (!*flag) - return; - AsanApplyToGlobals(__asan_unregister_globals, flag); - *flag = 0; -} - -void __asan_register_elf_globals(uptr *flag, void *start, void *stop) { - if (*flag) return; - if (!start) return; - CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global)); - __asan_global *globals_start = (__asan_global*)start; - __asan_global *globals_stop = (__asan_global*)stop; - __asan_register_globals(globals_start, globals_stop - globals_start); - *flag = 1; -} - -void __asan_unregister_elf_globals(uptr *flag, void *start, void *stop) { - if (!*flag) return; - if (!start) return; - CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global)); - __asan_global *globals_start = (__asan_global*)start; - __asan_global *globals_stop = (__asan_global*)stop; - __asan_unregister_globals(globals_start, globals_stop - globals_start); - *flag = 0; -} - -// Register an array of globals. -void __asan_register_globals(__asan_global *globals, uptr n) { - if (!flags()->report_globals) return; - GET_STACK_TRACE_MALLOC; - u32 stack_id = StackDepotPut(stack); - BlockingMutexLock lock(&mu_for_globals); - if (!global_registration_site_vector) { - global_registration_site_vector = - new (allocator_for_globals) GlobalRegistrationSiteVector; // NOLINT - global_registration_site_vector->reserve(128); - } - GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]}; - global_registration_site_vector->push_back(site); - if (flags()->report_globals >= 2) { - PRINT_CURRENT_STACK(); - Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]); - } - for (uptr i = 0; i < n; i++) { - if (SANITIZER_WINDOWS && globals[i].beg == 0) { - // The MSVC incremental linker may pad globals out to 256 bytes. As long - // as __asan_global is less than 256 bytes large and its size is a power - // of two, we can skip over the padding. - static_assert( - sizeof(__asan_global) < 256 && - (sizeof(__asan_global) & (sizeof(__asan_global) - 1)) == 0, - "sizeof(__asan_global) incompatible with incremental linker padding"); - // If these are padding bytes, the rest of the global should be zero. - CHECK(globals[i].size == 0 && globals[i].size_with_redzone == 0 && - globals[i].name == nullptr && globals[i].module_name == nullptr && - globals[i].odr_indicator == 0); - continue; - } - RegisterGlobal(&globals[i]); - } - - // Poison the metadata. It should not be accessible to user code. - PoisonShadow(reinterpret_cast(globals), n * sizeof(__asan_global), - kAsanGlobalRedzoneMagic); -} - -// Unregister an array of globals. -// We must do this when a shared objects gets dlclosed. -void __asan_unregister_globals(__asan_global *globals, uptr n) { - if (!flags()->report_globals) return; - BlockingMutexLock lock(&mu_for_globals); - for (uptr i = 0; i < n; i++) { - if (SANITIZER_WINDOWS && globals[i].beg == 0) { - // Skip globals that look like padding from the MSVC incremental linker. - // See comment in __asan_register_globals. - continue; - } - UnregisterGlobal(&globals[i]); - } - - // Unpoison the metadata. - PoisonShadow(reinterpret_cast(globals), n * sizeof(__asan_global), 0); -} - -// This method runs immediately prior to dynamic initialization in each TU, -// when all dynamically initialized globals are unpoisoned. This method -// poisons all global variables not defined in this TU, so that a dynamic -// initializer can only touch global variables in the same TU. -void __asan_before_dynamic_init(const char *module_name) { - if (!flags()->check_initialization_order || - !CanPoisonMemory() || - !dynamic_init_globals) - return; - bool strict_init_order = flags()->strict_init_order; - CHECK(module_name); - CHECK(asan_inited); - BlockingMutexLock lock(&mu_for_globals); - if (flags()->report_globals >= 3) - Printf("DynInitPoison module: %s\n", module_name); - for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { - DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; - const Global *g = &dyn_g.g; - if (dyn_g.initialized) - continue; - if (g->module_name != module_name) - PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); - else if (!strict_init_order) - dyn_g.initialized = true; - } -} - -// This method runs immediately after dynamic initialization in each TU, when -// all dynamically initialized globals except for those defined in the current -// TU are poisoned. It simply unpoisons all dynamically initialized globals. -void __asan_after_dynamic_init() { - if (!flags()->check_initialization_order || - !CanPoisonMemory() || - !dynamic_init_globals) - return; - CHECK(asan_inited); - BlockingMutexLock lock(&mu_for_globals); - // FIXME: Optionally report that we're unpoisoning globals from a module. - for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { - DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; - const Global *g = &dyn_g.g; - if (!dyn_g.initialized) { - // Unpoison the whole global. - PoisonShadowForGlobal(g, 0); - // Poison redzones back. - PoisonRedZones(*g); - } - } -} diff --git a/libsanitizer/asan/asan_globals.cpp b/libsanitizer/asan/asan_globals.cpp new file mode 100644 index 00000000000..54e75f3cee7 --- /dev/null +++ b/libsanitizer/asan/asan_globals.cpp @@ -0,0 +1,465 @@ +//===-- asan_globals.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Handle globals. +//===----------------------------------------------------------------------===// + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "asan_suppressions.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +namespace __asan { + +typedef __asan_global Global; + +struct ListOfGlobals { + const Global *g; + ListOfGlobals *next; +}; + +static BlockingMutex mu_for_globals(LINKER_INITIALIZED); +static LowLevelAllocator allocator_for_globals; +static ListOfGlobals *list_of_all_globals; + +static const int kDynamicInitGlobalsInitialCapacity = 512; +struct DynInitGlobal { + Global g; + bool initialized; +}; +typedef InternalMmapVector VectorOfGlobals; +// Lazy-initialized and never deleted. +static VectorOfGlobals *dynamic_init_globals; + +// We want to remember where a certain range of globals was registered. +struct GlobalRegistrationSite { + u32 stack_id; + Global *g_first, *g_last; +}; +typedef InternalMmapVector GlobalRegistrationSiteVector; +static GlobalRegistrationSiteVector *global_registration_site_vector; + +ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { + FastPoisonShadow(g->beg, g->size_with_redzone, value); +} + +ALWAYS_INLINE void PoisonRedZones(const Global &g) { + uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); + FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, + kAsanGlobalRedzoneMagic); + if (g.size != aligned_size) { + FastPoisonShadowPartialRightRedzone( + g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), + g.size % SHADOW_GRANULARITY, + SHADOW_GRANULARITY, + kAsanGlobalRedzoneMagic); + } +} + +const uptr kMinimalDistanceFromAnotherGlobal = 64; + +static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) { + if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; + if (addr >= g.beg + g.size_with_redzone) return false; + return true; +} + +static void ReportGlobal(const Global &g, const char *prefix) { + Report( + "%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu " + "odr_indicator=%p\n", + prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name, + g.module_name, g.has_dynamic_init, (void *)g.odr_indicator); + if (g.location) { + Report(" location (%p): name=%s[%p], %d %d\n", g.location, + g.location->filename, g.location->filename, g.location->line_no, + g.location->column_no); + } +} + +static u32 FindRegistrationSite(const Global *g) { + mu_for_globals.CheckLocked(); + CHECK(global_registration_site_vector); + for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) { + GlobalRegistrationSite &grs = (*global_registration_site_vector)[i]; + if (g >= grs.g_first && g <= grs.g_last) + return grs.stack_id; + } + return 0; +} + +int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites, + int max_globals) { + if (!flags()->report_globals) return 0; + BlockingMutexLock lock(&mu_for_globals); + int res = 0; + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + const Global &g = *l->g; + if (flags()->report_globals >= 2) + ReportGlobal(g, "Search"); + if (IsAddressNearGlobal(addr, g)) { + internal_memcpy(&globals[res], &g, sizeof(g)); + if (reg_sites) + reg_sites[res] = FindRegistrationSite(&g); + res++; + if (res == max_globals) + break; + } + } + return res; +} + +enum GlobalSymbolState { + UNREGISTERED = 0, + REGISTERED = 1 +}; + +// Check ODR violation for given global G via special ODR indicator. We use +// this method in case compiler instruments global variables through their +// local aliases. +static void CheckODRViolationViaIndicator(const Global *g) { + // Instrumentation requests to skip ODR check. + if (g->odr_indicator == UINTPTR_MAX) + return; + u8 *odr_indicator = reinterpret_cast(g->odr_indicator); + if (*odr_indicator == UNREGISTERED) { + *odr_indicator = REGISTERED; + return; + } + // If *odr_indicator is DEFINED, some module have already registered + // externally visible symbol with the same name. This is an ODR violation. + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + if (g->odr_indicator == l->g->odr_indicator && + (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && + !IsODRViolationSuppressed(g->name)) + ReportODRViolation(g, FindRegistrationSite(g), + l->g, FindRegistrationSite(l->g)); + } +} + +// Check ODR violation for given global G by checking if it's already poisoned. +// We use this method in case compiler doesn't use private aliases for global +// variables. +static void CheckODRViolationViaPoisoning(const Global *g) { + if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) { + // This check may not be enough: if the first global is much larger + // the entire redzone of the second global may be within the first global. + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + if (g->beg == l->g->beg && + (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && + !IsODRViolationSuppressed(g->name)) + ReportODRViolation(g, FindRegistrationSite(g), + l->g, FindRegistrationSite(l->g)); + } + } +} + +// Clang provides two different ways for global variables protection: +// it can poison the global itself or its private alias. In former +// case we may poison same symbol multiple times, that can help us to +// cheaply detect ODR violation: if we try to poison an already poisoned +// global, we have ODR violation error. +// In latter case, we poison each symbol exactly once, so we use special +// indicator symbol to perform similar check. +// In either case, compiler provides a special odr_indicator field to Global +// structure, that can contain two kinds of values: +// 1) Non-zero value. In this case, odr_indicator is an address of +// corresponding indicator variable for given global. +// 2) Zero. This means that we don't use private aliases for global variables +// and can freely check ODR violation with the first method. +// +// This routine chooses between two different methods of ODR violation +// detection. +static inline bool UseODRIndicator(const Global *g) { + return g->odr_indicator > 0; +} + +// Register a global variable. +// This function may be called more than once for every global +// so we store the globals in a map. +static void RegisterGlobal(const Global *g) { + CHECK(asan_inited); + if (flags()->report_globals >= 2) + ReportGlobal(*g, "Added"); + CHECK(flags()->report_globals); + CHECK(AddrIsInMem(g->beg)); + if (!AddrIsAlignedByGranularity(g->beg)) { + Report("The following global variable is not properly aligned.\n"); + Report("This may happen if another global with the same name\n"); + Report("resides in another non-instrumented module.\n"); + Report("Or the global comes from a C file built w/o -fno-common.\n"); + Report("In either case this is likely an ODR violation bug,\n"); + Report("but AddressSanitizer can not provide more details.\n"); + ReportODRViolation(g, FindRegistrationSite(g), g, FindRegistrationSite(g)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + } + CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); + if (flags()->detect_odr_violation) { + // Try detecting ODR (One Definition Rule) violation, i.e. the situation + // where two globals with the same name are defined in different modules. + if (UseODRIndicator(g)) + CheckODRViolationViaIndicator(g); + else + CheckODRViolationViaPoisoning(g); + } + if (CanPoisonMemory()) + PoisonRedZones(*g); + ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals; + l->g = g; + l->next = list_of_all_globals; + list_of_all_globals = l; + if (g->has_dynamic_init) { + if (!dynamic_init_globals) { + dynamic_init_globals = + new (allocator_for_globals) VectorOfGlobals; // NOLINT + dynamic_init_globals->reserve(kDynamicInitGlobalsInitialCapacity); + } + DynInitGlobal dyn_global = { *g, false }; + dynamic_init_globals->push_back(dyn_global); + } +} + +static void UnregisterGlobal(const Global *g) { + CHECK(asan_inited); + if (flags()->report_globals >= 2) + ReportGlobal(*g, "Removed"); + CHECK(flags()->report_globals); + CHECK(AddrIsInMem(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); + if (CanPoisonMemory()) + PoisonShadowForGlobal(g, 0); + // We unpoison the shadow memory for the global but we do not remove it from + // the list because that would require O(n^2) time with the current list + // implementation. It might not be worth doing anyway. + + // Release ODR indicator. + if (UseODRIndicator(g) && g->odr_indicator != UINTPTR_MAX) { + u8 *odr_indicator = reinterpret_cast(g->odr_indicator); + *odr_indicator = UNREGISTERED; + } +} + +void StopInitOrderChecking() { + BlockingMutexLock lock(&mu_for_globals); + if (!flags()->check_initialization_order || !dynamic_init_globals) + return; + flags()->check_initialization_order = false; + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } +} + +static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; } + +const char *MaybeDemangleGlobalName(const char *name) { + // We can spoil names of globals with C linkage, so use an heuristic + // approach to check if the name should be demangled. + bool should_demangle = false; + if (name[0] == '_' && name[1] == 'Z') + should_demangle = true; + else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?') + should_demangle = true; + + return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name; +} + +// Check if the global is a zero-terminated ASCII string. If so, print it. +void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) { + for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { + unsigned char c = *(unsigned char *)p; + if (c == '\0' || !IsASCII(c)) return; + } + if (*(char *)(g.beg + g.size - 1) != '\0') return; + str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name), + (char *)g.beg); +} + +static const char *GlobalFilename(const __asan_global &g) { + const char *res = g.module_name; + // Prefer the filename from source location, if is available. + if (g.location) res = g.location->filename; + CHECK(res); + return res; +} + +void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g) { + str->append("%s", GlobalFilename(g)); + if (!g.location) return; + if (g.location->line_no) str->append(":%d", g.location->line_no); + if (g.location->column_no) str->append(":%d", g.location->column_no); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + + +// Apply __asan_register_globals to all globals found in the same loaded +// executable or shared library as `flag'. The flag tracks whether globals have +// already been registered or not for this image. +void __asan_register_image_globals(uptr *flag) { + if (*flag) + return; + AsanApplyToGlobals(__asan_register_globals, flag); + *flag = 1; +} + +// This mirrors __asan_register_image_globals. +void __asan_unregister_image_globals(uptr *flag) { + if (!*flag) + return; + AsanApplyToGlobals(__asan_unregister_globals, flag); + *flag = 0; +} + +void __asan_register_elf_globals(uptr *flag, void *start, void *stop) { + if (*flag) return; + if (!start) return; + CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global)); + __asan_global *globals_start = (__asan_global*)start; + __asan_global *globals_stop = (__asan_global*)stop; + __asan_register_globals(globals_start, globals_stop - globals_start); + *flag = 1; +} + +void __asan_unregister_elf_globals(uptr *flag, void *start, void *stop) { + if (!*flag) return; + if (!start) return; + CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global)); + __asan_global *globals_start = (__asan_global*)start; + __asan_global *globals_stop = (__asan_global*)stop; + __asan_unregister_globals(globals_start, globals_stop - globals_start); + *flag = 0; +} + +// Register an array of globals. +void __asan_register_globals(__asan_global *globals, uptr n) { + if (!flags()->report_globals) return; + GET_STACK_TRACE_MALLOC; + u32 stack_id = StackDepotPut(stack); + BlockingMutexLock lock(&mu_for_globals); + if (!global_registration_site_vector) { + global_registration_site_vector = + new (allocator_for_globals) GlobalRegistrationSiteVector; // NOLINT + global_registration_site_vector->reserve(128); + } + GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]}; + global_registration_site_vector->push_back(site); + if (flags()->report_globals >= 2) { + PRINT_CURRENT_STACK(); + Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]); + } + for (uptr i = 0; i < n; i++) { + if (SANITIZER_WINDOWS && globals[i].beg == 0) { + // The MSVC incremental linker may pad globals out to 256 bytes. As long + // as __asan_global is less than 256 bytes large and its size is a power + // of two, we can skip over the padding. + static_assert( + sizeof(__asan_global) < 256 && + (sizeof(__asan_global) & (sizeof(__asan_global) - 1)) == 0, + "sizeof(__asan_global) incompatible with incremental linker padding"); + // If these are padding bytes, the rest of the global should be zero. + CHECK(globals[i].size == 0 && globals[i].size_with_redzone == 0 && + globals[i].name == nullptr && globals[i].module_name == nullptr && + globals[i].odr_indicator == 0); + continue; + } + RegisterGlobal(&globals[i]); + } + + // Poison the metadata. It should not be accessible to user code. + PoisonShadow(reinterpret_cast(globals), n * sizeof(__asan_global), + kAsanGlobalRedzoneMagic); +} + +// Unregister an array of globals. +// We must do this when a shared objects gets dlclosed. +void __asan_unregister_globals(__asan_global *globals, uptr n) { + if (!flags()->report_globals) return; + BlockingMutexLock lock(&mu_for_globals); + for (uptr i = 0; i < n; i++) { + if (SANITIZER_WINDOWS && globals[i].beg == 0) { + // Skip globals that look like padding from the MSVC incremental linker. + // See comment in __asan_register_globals. + continue; + } + UnregisterGlobal(&globals[i]); + } + + // Unpoison the metadata. + PoisonShadow(reinterpret_cast(globals), n * sizeof(__asan_global), 0); +} + +// This method runs immediately prior to dynamic initialization in each TU, +// when all dynamically initialized globals are unpoisoned. This method +// poisons all global variables not defined in this TU, so that a dynamic +// initializer can only touch global variables in the same TU. +void __asan_before_dynamic_init(const char *module_name) { + if (!flags()->check_initialization_order || + !CanPoisonMemory() || + !dynamic_init_globals) + return; + bool strict_init_order = flags()->strict_init_order; + CHECK(module_name); + CHECK(asan_inited); + BlockingMutexLock lock(&mu_for_globals); + if (flags()->report_globals >= 3) + Printf("DynInitPoison module: %s\n", module_name); + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (dyn_g.initialized) + continue; + if (g->module_name != module_name) + PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); + else if (!strict_init_order) + dyn_g.initialized = true; + } +} + +// This method runs immediately after dynamic initialization in each TU, when +// all dynamically initialized globals except for those defined in the current +// TU are poisoned. It simply unpoisons all dynamically initialized globals. +void __asan_after_dynamic_init() { + if (!flags()->check_initialization_order || + !CanPoisonMemory() || + !dynamic_init_globals) + return; + CHECK(asan_inited); + BlockingMutexLock lock(&mu_for_globals); + // FIXME: Optionally report that we're unpoisoning globals from a module. + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (!dyn_g.initialized) { + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } + } +} diff --git a/libsanitizer/asan/asan_globals_win.cc b/libsanitizer/asan/asan_globals_win.cc deleted file mode 100644 index a78bc878f9c..00000000000 --- a/libsanitizer/asan/asan_globals_win.cc +++ /dev/null @@ -1,60 +0,0 @@ -//===-- asan_globals_win.cc -----------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Global registration code that is linked into every Windows DLL and EXE. -// -//===----------------------------------------------------------------------===// - -#include "asan_interface_internal.h" -#if SANITIZER_WINDOWS - -namespace __asan { - -#pragma section(".ASAN$GA", read, write) // NOLINT -#pragma section(".ASAN$GZ", read, write) // NOLINT -extern "C" __declspec(allocate(".ASAN$GA")) - ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_start = {}; -extern "C" __declspec(allocate(".ASAN$GZ")) - ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_end = {}; -#pragma comment(linker, "/merge:.ASAN=.data") - -static void call_on_globals(void (*hook)(__asan_global *, uptr)) { - __asan_global *start = &__asan_globals_start + 1; - __asan_global *end = &__asan_globals_end; - uptr bytediff = (uptr)end - (uptr)start; - if (bytediff % sizeof(__asan_global) != 0) { -#if defined(SANITIZER_DLL_THUNK) || defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) - __debugbreak(); -#else - CHECK("corrupt asan global array"); -#endif - } - // We know end >= start because the linker sorts the portion after the dollar - // sign alphabetically. - uptr n = end - start; - hook(start, n); -} - -static void register_dso_globals() { - call_on_globals(&__asan_register_globals); -} - -static void unregister_dso_globals() { - call_on_globals(&__asan_unregister_globals); -} - -// Register globals -#pragma section(".CRT$XCU", long, read) // NOLINT -#pragma section(".CRT$XTX", long, read) // NOLINT -extern "C" __declspec(allocate(".CRT$XCU")) -void (*const __asan_dso_reg_hook)() = ®ister_dso_globals; -extern "C" __declspec(allocate(".CRT$XTX")) -void (*const __asan_dso_unreg_hook)() = &unregister_dso_globals; - -} // namespace __asan - -#endif // SANITIZER_WINDOWS diff --git a/libsanitizer/asan/asan_globals_win.cpp b/libsanitizer/asan/asan_globals_win.cpp new file mode 100644 index 00000000000..ff5fe226b53 --- /dev/null +++ b/libsanitizer/asan/asan_globals_win.cpp @@ -0,0 +1,61 @@ +//===-- asan_globals_win.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Global registration code that is linked into every Windows DLL and EXE. +// +//===----------------------------------------------------------------------===// + +#include "asan_interface_internal.h" +#if SANITIZER_WINDOWS + +namespace __asan { + +#pragma section(".ASAN$GA", read, write) // NOLINT +#pragma section(".ASAN$GZ", read, write) // NOLINT +extern "C" __declspec(allocate(".ASAN$GA")) + ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_start = {}; +extern "C" __declspec(allocate(".ASAN$GZ")) + ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_end = {}; +#pragma comment(linker, "/merge:.ASAN=.data") + +static void call_on_globals(void (*hook)(__asan_global *, uptr)) { + __asan_global *start = &__asan_globals_start + 1; + __asan_global *end = &__asan_globals_end; + uptr bytediff = (uptr)end - (uptr)start; + if (bytediff % sizeof(__asan_global) != 0) { +#if defined(SANITIZER_DLL_THUNK) || defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) + __debugbreak(); +#else + CHECK("corrupt asan global array"); +#endif + } + // We know end >= start because the linker sorts the portion after the dollar + // sign alphabetically. + uptr n = end - start; + hook(start, n); +} + +static void register_dso_globals() { + call_on_globals(&__asan_register_globals); +} + +static void unregister_dso_globals() { + call_on_globals(&__asan_unregister_globals); +} + +// Register globals +#pragma section(".CRT$XCU", long, read) // NOLINT +#pragma section(".CRT$XTX", long, read) // NOLINT +extern "C" __declspec(allocate(".CRT$XCU")) +void (*const __asan_dso_reg_hook)() = ®ister_dso_globals; +extern "C" __declspec(allocate(".CRT$XTX")) +void (*const __asan_dso_unreg_hook)() = &unregister_dso_globals; + +} // namespace __asan + +#endif // SANITIZER_WINDOWS diff --git a/libsanitizer/asan/asan_init_version.h b/libsanitizer/asan/asan_init_version.h index 7833133938d..b806d794e05 100644 --- a/libsanitizer/asan/asan_init_version.h +++ b/libsanitizer/asan/asan_init_version.h @@ -1,7 +1,8 @@ //===-- asan_init_version.h -------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/asan/asan_interceptors.cc b/libsanitizer/asan/asan_interceptors.cc deleted file mode 100644 index fc9818bee8a..00000000000 --- a/libsanitizer/asan/asan_interceptors.cc +++ /dev/null @@ -1,665 +0,0 @@ -//===-- asan_interceptors.cc ----------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Intercept various libc functions. -//===----------------------------------------------------------------------===// - -#include "asan_interceptors.h" -#include "asan_allocator.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "asan_poisoning.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "asan_stats.h" -#include "asan_suppressions.h" -#include "lsan/lsan_common.h" -#include "sanitizer_common/sanitizer_libc.h" - -// There is no general interception at all on Fuchsia and RTEMS. -// Only the functions in asan_interceptors_memintrinsics.cc are -// really defined to replace libc functions. -#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS - -#if SANITIZER_POSIX -#include "sanitizer_common/sanitizer_posix.h" -#endif - -#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION || \ - ASAN_INTERCEPT__SJLJ_UNWIND_RAISEEXCEPTION -#include -#endif - -#if defined(__i386) && SANITIZER_LINUX -#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.1" -#elif defined(__mips__) && SANITIZER_LINUX -#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2" -#endif - -namespace __asan { - -#define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \ - ASAN_READ_RANGE((ctx), (s), \ - common_flags()->strict_string_checks ? (len) + 1 : (n)) - -#define ASAN_READ_STRING(ctx, s, n) \ - ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n)) - -static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { -#if SANITIZER_INTERCEPT_STRNLEN - if (REAL(strnlen)) { - return REAL(strnlen)(s, maxlen); - } -#endif - return internal_strnlen(s, maxlen); -} - -void SetThreadName(const char *name) { - AsanThread *t = GetCurrentThread(); - if (t) - asanThreadRegistry().SetThreadName(t->tid(), name); -} - -int OnExit() { - if (CAN_SANITIZE_LEAKS && common_flags()->detect_leaks && - __lsan::HasReportedLeaks()) { - return common_flags()->exitcode; - } - // FIXME: ask frontend whether we need to return failure. - return 0; -} - -} // namespace __asan - -// ---------------------- Wrappers ---------------- {{{1 -using namespace __asan; // NOLINT - -DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr) -DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) - -#define ASAN_INTERCEPTOR_ENTER(ctx, func) \ - AsanInterceptorContext _ctx = {#func}; \ - ctx = (void *)&_ctx; \ - (void) ctx; \ - -#define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name) -#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \ - ASAN_INTERCEPT_FUNC_VER(name, ver) -#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ - ASAN_WRITE_RANGE(ctx, ptr, size) -#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ - ASAN_READ_RANGE(ctx, ptr, size) -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - ASAN_INTERCEPTOR_ENTER(ctx, func); \ - do { \ - if (asan_init_is_running) \ - return REAL(func)(__VA_ARGS__); \ - if (SANITIZER_MAC && UNLIKELY(!asan_inited)) \ - return REAL(func)(__VA_ARGS__); \ - ENSURE_ASAN_INITED(); \ - } while (false) -#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ - do { \ - } while (false) -#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ - do { \ - } while (false) -#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ - do { \ - } while (false) -#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ - do { \ - } while (false) -#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name) -// Should be asanThreadRegistry().SetThreadNameByUserId(thread, name) -// But asan does not remember UserId's for threads (pthread_t); -// and remembers all ever existed threads, so the linear search by UserId -// can be slow. -#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ - do { \ - } while (false) -#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) -// Strict init-order checking is dlopen-hostile: -// https://github.com/google/sanitizers/issues/178 -#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \ - do { \ - if (flags()->strict_init_order) \ - StopInitOrderChecking(); \ - CheckNoDeepBind(filename, flag); \ - } while (false) -#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() -#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) -#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() -#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited) -#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ - if (AsanThread *t = GetCurrentThread()) { \ - *begin = t->tls_begin(); \ - *end = t->tls_end(); \ - } else { \ - *begin = *end = 0; \ - } - -#define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size) \ - do { \ - ASAN_INTERCEPTOR_ENTER(ctx, memmove); \ - ASAN_MEMMOVE_IMPL(ctx, to, from, size); \ - } while (false) - -#define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size) \ - do { \ - ASAN_INTERCEPTOR_ENTER(ctx, memcpy); \ - ASAN_MEMCPY_IMPL(ctx, to, from, size); \ - } while (false) - -#define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size) \ - do { \ - ASAN_INTERCEPTOR_ENTER(ctx, memset); \ - ASAN_MEMSET_IMPL(ctx, block, c, size); \ - } while (false) - -#include "sanitizer_common/sanitizer_common_interceptors.inc" -#include "sanitizer_common/sanitizer_signal_interceptors.inc" - -// Syscall interceptors don't have contexts, we don't support suppressions -// for them. -#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(nullptr, p, s) -#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(nullptr, p, s) -#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ - do { \ - (void)(p); \ - (void)(s); \ - } while (false) -#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ - do { \ - (void)(p); \ - (void)(s); \ - } while (false) -#include "sanitizer_common/sanitizer_common_syscalls.inc" -#include "sanitizer_common/sanitizer_syscalls_netbsd.inc" - -struct ThreadStartParam { - atomic_uintptr_t t; - atomic_uintptr_t is_registered; -}; - -#if ASAN_INTERCEPT_PTHREAD_CREATE -static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { - ThreadStartParam *param = reinterpret_cast(arg); - AsanThread *t = nullptr; - while ((t = reinterpret_cast( - atomic_load(¶m->t, memory_order_acquire))) == nullptr) - internal_sched_yield(); - SetCurrentThread(t); - return t->ThreadStart(GetTid(), ¶m->is_registered); -} - -INTERCEPTOR(int, pthread_create, void *thread, - void *attr, void *(*start_routine)(void*), void *arg) { - EnsureMainThreadIDIsCorrect(); - // Strict init-order checking is thread-hostile. - if (flags()->strict_init_order) - StopInitOrderChecking(); - GET_STACK_TRACE_THREAD; - int detached = 0; - if (attr) - REAL(pthread_attr_getdetachstate)(attr, &detached); - ThreadStartParam param; - atomic_store(¶m.t, 0, memory_order_relaxed); - atomic_store(¶m.is_registered, 0, memory_order_relaxed); - int result; - { - // Ignore all allocations made by pthread_create: thread stack/TLS may be - // stored by pthread for future reuse even after thread destruction, and - // the linked list it's stored in doesn't even hold valid pointers to the - // objects, the latter are calculated by obscure pointer arithmetic. -#if CAN_SANITIZE_LEAKS - __lsan::ScopedInterceptorDisabler disabler; -#endif - result = REAL(pthread_create)(thread, attr, asan_thread_start, ¶m); - } - if (result == 0) { - u32 current_tid = GetCurrentTidOrInvalid(); - AsanThread *t = - AsanThread::Create(start_routine, arg, current_tid, &stack, detached); - atomic_store(¶m.t, reinterpret_cast(t), memory_order_release); - // Wait until the AsanThread object is initialized and the ThreadRegistry - // entry is in "started" state. One reason for this is that after this - // interceptor exits, the child thread's stack may be the only thing holding - // the |arg| pointer. This may cause LSan to report a leak if leak checking - // happens at a point when the interceptor has already exited, but the stack - // range for the child thread is not yet known. - while (atomic_load(¶m.is_registered, memory_order_acquire) == 0) - internal_sched_yield(); - } - return result; -} - -INTERCEPTOR(int, pthread_join, void *t, void **arg) { - return real_pthread_join(t, arg); -} - -DEFINE_REAL_PTHREAD_FUNCTIONS -#endif // ASAN_INTERCEPT_PTHREAD_CREATE - -#if ASAN_INTERCEPT_SWAPCONTEXT -static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) { - // Align to page size. - uptr PageSize = GetPageSizeCached(); - uptr bottom = stack & ~(PageSize - 1); - ssize += stack - bottom; - ssize = RoundUpTo(ssize, PageSize); - static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb - if (AddrIsInMem(bottom) && ssize && ssize <= kMaxSaneContextStackSize) { - PoisonShadow(bottom, ssize, 0); - } -} - -INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp, - struct ucontext_t *ucp) { - static bool reported_warning = false; - if (!reported_warning) { - Report("WARNING: ASan doesn't fully support makecontext/swapcontext " - "functions and may produce false positives in some cases!\n"); - reported_warning = true; - } - // Clear shadow memory for new context (it may share stack - // with current context). - uptr stack, ssize; - ReadContextStack(ucp, &stack, &ssize); - ClearShadowMemoryForContextStack(stack, ssize); -#if __has_attribute(__indirect_return__) && \ - (defined(__x86_64__) || defined(__i386__)) - int (*real_swapcontext)(struct ucontext_t *, struct ucontext_t *) - __attribute__((__indirect_return__)) - = REAL(swapcontext); - int res = real_swapcontext(oucp, ucp); -#else - int res = REAL(swapcontext)(oucp, ucp); -#endif - // swapcontext technically does not return, but program may swap context to - // "oucp" later, that would look as if swapcontext() returned 0. - // We need to clear shadow for ucp once again, as it may be in arbitrary - // state. - ClearShadowMemoryForContextStack(stack, ssize); - return res; -} -#endif // ASAN_INTERCEPT_SWAPCONTEXT - -#if SANITIZER_NETBSD -#define longjmp __longjmp14 -#define siglongjmp __siglongjmp14 -#endif - -INTERCEPTOR(void, longjmp, void *env, int val) { - __asan_handle_no_return(); - REAL(longjmp)(env, val); -} - -#if ASAN_INTERCEPT__LONGJMP -INTERCEPTOR(void, _longjmp, void *env, int val) { - __asan_handle_no_return(); - REAL(_longjmp)(env, val); -} -#endif - -#if ASAN_INTERCEPT___LONGJMP_CHK -INTERCEPTOR(void, __longjmp_chk, void *env, int val) { - __asan_handle_no_return(); - REAL(__longjmp_chk)(env, val); -} -#endif - -#if ASAN_INTERCEPT_SIGLONGJMP -INTERCEPTOR(void, siglongjmp, void *env, int val) { - __asan_handle_no_return(); - REAL(siglongjmp)(env, val); -} -#endif - -#if ASAN_INTERCEPT___CXA_THROW -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 - -#if ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION -INTERCEPTOR(void, __cxa_rethrow_primary_exception, void *a) { - CHECK(REAL(__cxa_rethrow_primary_exception)); - __asan_handle_no_return(); - REAL(__cxa_rethrow_primary_exception)(a); -} -#endif - -#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION -INTERCEPTOR(_Unwind_Reason_Code, _Unwind_RaiseException, - _Unwind_Exception *object) { - CHECK(REAL(_Unwind_RaiseException)); - __asan_handle_no_return(); - return REAL(_Unwind_RaiseException)(object); -} -#endif - -#if ASAN_INTERCEPT__SJLJ_UNWIND_RAISEEXCEPTION -INTERCEPTOR(_Unwind_Reason_Code, _Unwind_SjLj_RaiseException, - _Unwind_Exception *object) { - CHECK(REAL(_Unwind_SjLj_RaiseException)); - __asan_handle_no_return(); - return REAL(_Unwind_SjLj_RaiseException)(object); -} -#endif - -#if ASAN_INTERCEPT_INDEX -# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX -INTERCEPTOR(char*, index, const char *string, int c) - ALIAS(WRAPPER_NAME(strchr)); -# else -# if SANITIZER_MAC -DECLARE_REAL(char*, index, const char *string, int c) -OVERRIDE_FUNCTION(index, strchr); -# else -DEFINE_REAL(char*, index, const char *string, int c) -# endif -# endif -#endif // ASAN_INTERCEPT_INDEX - -// For both strcat() and strncat() we need to check the validity of |to| -// argument irrespective of the |from| length. -INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strcat); // NOLINT - ENSURE_ASAN_INITED(); - if (flags()->replace_str) { - uptr from_length = REAL(strlen)(from); - ASAN_READ_RANGE(ctx, from, from_length + 1); - uptr to_length = REAL(strlen)(to); - ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); - ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); - // If the copying actually happens, the |from| string should not overlap - // with the resulting string starting at |to|, which has a length of - // to_length + from_length + 1. - if (from_length > 0) { - CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1, - from, from_length + 1); - } - } - return REAL(strcat)(to, from); // NOLINT -} - -INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strncat); - ENSURE_ASAN_INITED(); - if (flags()->replace_str) { - uptr from_length = MaybeRealStrnlen(from, size); - uptr copy_length = Min(size, from_length + 1); - ASAN_READ_RANGE(ctx, from, copy_length); - uptr to_length = REAL(strlen)(to); - ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); - ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); - if (from_length > 0) { - CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1, - from, copy_length); - } - } - return REAL(strncat)(to, from, size); -} - -INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strcpy); // NOLINT -#if SANITIZER_MAC - if (UNLIKELY(!asan_inited)) return REAL(strcpy)(to, from); // NOLINT -#endif - // strcpy is called from malloc_default_purgeable_zone() - // in __asan::ReplaceSystemAlloc() on Mac. - if (asan_init_is_running) { - return REAL(strcpy)(to, from); // NOLINT - } - ENSURE_ASAN_INITED(); - if (flags()->replace_str) { - uptr from_size = REAL(strlen)(from) + 1; - CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size); - ASAN_READ_RANGE(ctx, from, from_size); - ASAN_WRITE_RANGE(ctx, to, from_size); - } - return REAL(strcpy)(to, from); // NOLINT -} - -INTERCEPTOR(char*, strdup, const char *s) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strdup); - if (UNLIKELY(!asan_inited)) return internal_strdup(s); - ENSURE_ASAN_INITED(); - uptr length = REAL(strlen)(s); - if (flags()->replace_str) { - ASAN_READ_RANGE(ctx, s, length + 1); - } - GET_STACK_TRACE_MALLOC; - void *new_mem = asan_malloc(length + 1, &stack); - REAL(memcpy)(new_mem, s, length + 1); - return reinterpret_cast(new_mem); -} - -#if ASAN_INTERCEPT___STRDUP -INTERCEPTOR(char*, __strdup, const char *s) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strdup); - if (UNLIKELY(!asan_inited)) return internal_strdup(s); - ENSURE_ASAN_INITED(); - uptr length = REAL(strlen)(s); - if (flags()->replace_str) { - ASAN_READ_RANGE(ctx, s, length + 1); - } - GET_STACK_TRACE_MALLOC; - void *new_mem = asan_malloc(length + 1, &stack); - REAL(memcpy)(new_mem, s, length + 1); - return reinterpret_cast(new_mem); -} -#endif // ASAN_INTERCEPT___STRDUP - -INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strncpy); - ENSURE_ASAN_INITED(); - if (flags()->replace_str) { - uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); - CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size); - ASAN_READ_RANGE(ctx, from, from_size); - ASAN_WRITE_RANGE(ctx, to, size); - } - return REAL(strncpy)(to, from, size); -} - -INTERCEPTOR(long, strtol, const char *nptr, // NOLINT - char **endptr, int base) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strtol); - ENSURE_ASAN_INITED(); - if (!flags()->replace_str) { - return REAL(strtol)(nptr, endptr, base); - } - char *real_endptr; - long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT - StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); - return result; -} - -INTERCEPTOR(int, atoi, const char *nptr) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, atoi); -#if SANITIZER_MAC - if (UNLIKELY(!asan_inited)) return REAL(atoi)(nptr); -#endif - 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_STRING(ctx, nptr, (real_endptr - nptr) + 1); - return result; -} - -INTERCEPTOR(long, atol, const char *nptr) { // NOLINT - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, atol); -#if SANITIZER_MAC - if (UNLIKELY(!asan_inited)) return REAL(atol)(nptr); -#endif - 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_STRING(ctx, nptr, (real_endptr - nptr) + 1); - return result; -} - -#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL -INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT - char **endptr, int base) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strtoll); - ENSURE_ASAN_INITED(); - if (!flags()->replace_str) { - return REAL(strtoll)(nptr, endptr, base); - } - char *real_endptr; - long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT - StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); - return result; -} - -INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, atoll); - ENSURE_ASAN_INITED(); - if (!flags()->replace_str) { - return REAL(atoll)(nptr); - } - char *real_endptr; - long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT - FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1); - return result; -} -#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL - -#if ASAN_INTERCEPT___CXA_ATEXIT -static void AtCxaAtexit(void *unused) { - (void)unused; - StopInitOrderChecking(); -} - -INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, - void *dso_handle) { -#if SANITIZER_MAC - if (UNLIKELY(!asan_inited)) return REAL(__cxa_atexit)(func, arg, dso_handle); -#endif - ENSURE_ASAN_INITED(); - int res = REAL(__cxa_atexit)(func, arg, dso_handle); - REAL(__cxa_atexit)(AtCxaAtexit, nullptr, nullptr); - return res; -} -#endif // ASAN_INTERCEPT___CXA_ATEXIT - -// ---------------------- InitializeAsanInterceptors ---------------- {{{1 -namespace __asan { -void InitializeAsanInterceptors() { - static bool was_called_once; - CHECK(!was_called_once); - was_called_once = true; - InitializeCommonInterceptors(); - InitializeSignalInterceptors(); - - // Intercept str* functions. - ASAN_INTERCEPT_FUNC(strcat); // NOLINT - ASAN_INTERCEPT_FUNC(strcpy); // NOLINT - ASAN_INTERCEPT_FUNC(strncat); - ASAN_INTERCEPT_FUNC(strncpy); - ASAN_INTERCEPT_FUNC(strdup); -#if ASAN_INTERCEPT___STRDUP - ASAN_INTERCEPT_FUNC(__strdup); -#endif -#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX - ASAN_INTERCEPT_FUNC(index); -#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 jump-related functions. - ASAN_INTERCEPT_FUNC(longjmp); - -#if ASAN_INTERCEPT_SWAPCONTEXT - ASAN_INTERCEPT_FUNC(swapcontext); -#endif -#if ASAN_INTERCEPT__LONGJMP - ASAN_INTERCEPT_FUNC(_longjmp); -#endif -#if ASAN_INTERCEPT___LONGJMP_CHK - ASAN_INTERCEPT_FUNC(__longjmp_chk); -#endif -#if ASAN_INTERCEPT_SIGLONGJMP - ASAN_INTERCEPT_FUNC(siglongjmp); -#endif - - // Intercept exception handling functions. -#if ASAN_INTERCEPT___CXA_THROW - ASAN_INTERCEPT_FUNC(__cxa_throw); -#endif -#if ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION - ASAN_INTERCEPT_FUNC(__cxa_rethrow_primary_exception); -#endif - // Indirectly intercept std::rethrow_exception. -#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION - INTERCEPT_FUNCTION(_Unwind_RaiseException); -#endif - // Indirectly intercept std::rethrow_exception. -#if ASAN_INTERCEPT__UNWIND_SJLJ_RAISEEXCEPTION - INTERCEPT_FUNCTION(_Unwind_SjLj_RaiseException); -#endif - - // Intercept threading-related functions -#if ASAN_INTERCEPT_PTHREAD_CREATE -#if defined(ASAN_PTHREAD_CREATE_VERSION) - ASAN_INTERCEPT_FUNC_VER(pthread_create, ASAN_PTHREAD_CREATE_VERSION); -#else - ASAN_INTERCEPT_FUNC(pthread_create); -#endif - ASAN_INTERCEPT_FUNC(pthread_join); -#endif - - // Intercept atexit function. -#if ASAN_INTERCEPT___CXA_ATEXIT - ASAN_INTERCEPT_FUNC(__cxa_atexit); -#endif - - InitializePlatformInterceptors(); - - VReport(1, "AddressSanitizer: libc interceptors initialized\n"); -} - -} // namespace __asan - -#endif // !SANITIZER_FUCHSIA diff --git a/libsanitizer/asan/asan_interceptors.cpp b/libsanitizer/asan/asan_interceptors.cpp new file mode 100644 index 00000000000..482e44d83b7 --- /dev/null +++ b/libsanitizer/asan/asan_interceptors.cpp @@ -0,0 +1,675 @@ +//===-- asan_interceptors.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Intercept various libc functions. +//===----------------------------------------------------------------------===// + +#include "asan_interceptors.h" +#include "asan_allocator.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "asan_suppressions.h" +#include "lsan/lsan_common.h" +#include "sanitizer_common/sanitizer_libc.h" + +// There is no general interception at all on Fuchsia and RTEMS. +// Only the functions in asan_interceptors_memintrinsics.cpp are +// really defined to replace libc functions. +#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS + +#if SANITIZER_POSIX +#include "sanitizer_common/sanitizer_posix.h" +#endif + +#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION || \ + ASAN_INTERCEPT__SJLJ_UNWIND_RAISEEXCEPTION +#include +#endif + +#if defined(__i386) && SANITIZER_LINUX +#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.1" +#elif defined(__mips__) && SANITIZER_LINUX +#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2" +#endif + +namespace __asan { + +#define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \ + ASAN_READ_RANGE((ctx), (s), \ + common_flags()->strict_string_checks ? (len) + 1 : (n)) + +#define ASAN_READ_STRING(ctx, s, n) \ + ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n)) + +static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { +#if SANITIZER_INTERCEPT_STRNLEN + if (REAL(strnlen)) { + return REAL(strnlen)(s, maxlen); + } +#endif + return internal_strnlen(s, maxlen); +} + +void SetThreadName(const char *name) { + AsanThread *t = GetCurrentThread(); + if (t) + asanThreadRegistry().SetThreadName(t->tid(), name); +} + +int OnExit() { + if (CAN_SANITIZE_LEAKS && common_flags()->detect_leaks && + __lsan::HasReportedLeaks()) { + return common_flags()->exitcode; + } + // FIXME: ask frontend whether we need to return failure. + return 0; +} + +} // namespace __asan + +// ---------------------- Wrappers ---------------- {{{1 +using namespace __asan; // NOLINT + +DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr) +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) + +#define ASAN_INTERCEPTOR_ENTER(ctx, func) \ + AsanInterceptorContext _ctx = {#func}; \ + ctx = (void *)&_ctx; \ + (void) ctx; \ + +#define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name) +#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \ + ASAN_INTERCEPT_FUNC_VER(name, ver) +#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ + ASAN_WRITE_RANGE(ctx, ptr, size) +#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ + ASAN_READ_RANGE(ctx, ptr, size) +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + ASAN_INTERCEPTOR_ENTER(ctx, func); \ + do { \ + if (asan_init_is_running) \ + return REAL(func)(__VA_ARGS__); \ + if (SANITIZER_MAC && UNLIKELY(!asan_inited)) \ + return REAL(func)(__VA_ARGS__); \ + ENSURE_ASAN_INITED(); \ + } while (false) +#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name) +// Should be asanThreadRegistry().SetThreadNameByUserId(thread, name) +// But asan does not remember UserId's for threads (pthread_t); +// and remembers all ever existed threads, so the linear search by UserId +// can be slow. +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) +// Strict init-order checking is dlopen-hostile: +// https://github.com/google/sanitizers/issues/178 +#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \ + do { \ + if (flags()->strict_init_order) \ + StopInitOrderChecking(); \ + CheckNoDeepBind(filename, flag); \ + } while (false) +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) +#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() +#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited) +#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ + if (AsanThread *t = GetCurrentThread()) { \ + *begin = t->tls_begin(); \ + *end = t->tls_end(); \ + } else { \ + *begin = *end = 0; \ + } + +#define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size) \ + do { \ + ASAN_INTERCEPTOR_ENTER(ctx, memmove); \ + ASAN_MEMMOVE_IMPL(ctx, to, from, size); \ + } while (false) + +#define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size) \ + do { \ + ASAN_INTERCEPTOR_ENTER(ctx, memcpy); \ + ASAN_MEMCPY_IMPL(ctx, to, from, size); \ + } while (false) + +#define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size) \ + do { \ + ASAN_INTERCEPTOR_ENTER(ctx, memset); \ + ASAN_MEMSET_IMPL(ctx, block, c, size); \ + } while (false) + +#include "sanitizer_common/sanitizer_common_interceptors.inc" +#include "sanitizer_common/sanitizer_signal_interceptors.inc" + +// Syscall interceptors don't have contexts, we don't support suppressions +// for them. +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(nullptr, p, s) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(nullptr, p, s) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ + do { \ + (void)(p); \ + (void)(s); \ + } while (false) +#include "sanitizer_common/sanitizer_common_syscalls.inc" +#include "sanitizer_common/sanitizer_syscalls_netbsd.inc" + +struct ThreadStartParam { + atomic_uintptr_t t; + atomic_uintptr_t is_registered; +}; + +#if ASAN_INTERCEPT_PTHREAD_CREATE +static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { + ThreadStartParam *param = reinterpret_cast(arg); + AsanThread *t = nullptr; + while ((t = reinterpret_cast( + atomic_load(¶m->t, memory_order_acquire))) == nullptr) + internal_sched_yield(); + SetCurrentThread(t); + return t->ThreadStart(GetTid(), ¶m->is_registered); +} + +INTERCEPTOR(int, pthread_create, void *thread, + void *attr, void *(*start_routine)(void*), void *arg) { + EnsureMainThreadIDIsCorrect(); + // Strict init-order checking is thread-hostile. + if (flags()->strict_init_order) + StopInitOrderChecking(); + GET_STACK_TRACE_THREAD; + int detached = 0; + if (attr) + REAL(pthread_attr_getdetachstate)(attr, &detached); + ThreadStartParam param; + atomic_store(¶m.t, 0, memory_order_relaxed); + atomic_store(¶m.is_registered, 0, memory_order_relaxed); + int result; + { + // Ignore all allocations made by pthread_create: thread stack/TLS may be + // stored by pthread for future reuse even after thread destruction, and + // the linked list it's stored in doesn't even hold valid pointers to the + // objects, the latter are calculated by obscure pointer arithmetic. +#if CAN_SANITIZE_LEAKS + __lsan::ScopedInterceptorDisabler disabler; +#endif + result = REAL(pthread_create)(thread, attr, asan_thread_start, ¶m); + } + if (result == 0) { + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = + AsanThread::Create(start_routine, arg, current_tid, &stack, detached); + atomic_store(¶m.t, reinterpret_cast(t), memory_order_release); + // Wait until the AsanThread object is initialized and the ThreadRegistry + // entry is in "started" state. One reason for this is that after this + // interceptor exits, the child thread's stack may be the only thing holding + // the |arg| pointer. This may cause LSan to report a leak if leak checking + // happens at a point when the interceptor has already exited, but the stack + // range for the child thread is not yet known. + while (atomic_load(¶m.is_registered, memory_order_acquire) == 0) + internal_sched_yield(); + } + return result; +} + +INTERCEPTOR(int, pthread_join, void *t, void **arg) { + return real_pthread_join(t, arg); +} + +DEFINE_REAL_PTHREAD_FUNCTIONS +#endif // ASAN_INTERCEPT_PTHREAD_CREATE + +#if ASAN_INTERCEPT_SWAPCONTEXT +static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) { + // Align to page size. + uptr PageSize = GetPageSizeCached(); + uptr bottom = stack & ~(PageSize - 1); + ssize += stack - bottom; + ssize = RoundUpTo(ssize, PageSize); + static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb + if (AddrIsInMem(bottom) && ssize && ssize <= kMaxSaneContextStackSize) { + PoisonShadow(bottom, ssize, 0); + } +} + +INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp, + struct ucontext_t *ucp) { + static bool reported_warning = false; + if (!reported_warning) { + Report("WARNING: ASan doesn't fully support makecontext/swapcontext " + "functions and may produce false positives in some cases!\n"); + reported_warning = true; + } + // Clear shadow memory for new context (it may share stack + // with current context). + uptr stack, ssize; + ReadContextStack(ucp, &stack, &ssize); + ClearShadowMemoryForContextStack(stack, ssize); +#if __has_attribute(__indirect_return__) && \ + (defined(__x86_64__) || defined(__i386__)) + int (*real_swapcontext)(struct ucontext_t *, struct ucontext_t *) + __attribute__((__indirect_return__)) + = REAL(swapcontext); + int res = real_swapcontext(oucp, ucp); +#else + int res = REAL(swapcontext)(oucp, ucp); +#endif + // swapcontext technically does not return, but program may swap context to + // "oucp" later, that would look as if swapcontext() returned 0. + // We need to clear shadow for ucp once again, as it may be in arbitrary + // state. + ClearShadowMemoryForContextStack(stack, ssize); + return res; +} +#endif // ASAN_INTERCEPT_SWAPCONTEXT + +#if SANITIZER_NETBSD +#define longjmp __longjmp14 +#define siglongjmp __siglongjmp14 +#endif + +INTERCEPTOR(void, longjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(longjmp)(env, val); +} + +#if ASAN_INTERCEPT__LONGJMP +INTERCEPTOR(void, _longjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(_longjmp)(env, val); +} +#endif + +#if ASAN_INTERCEPT___LONGJMP_CHK +INTERCEPTOR(void, __longjmp_chk, void *env, int val) { + __asan_handle_no_return(); + REAL(__longjmp_chk)(env, val); +} +#endif + +#if ASAN_INTERCEPT_SIGLONGJMP +INTERCEPTOR(void, siglongjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(siglongjmp)(env, val); +} +#endif + +#if ASAN_INTERCEPT___CXA_THROW +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 + +#if ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION +INTERCEPTOR(void, __cxa_rethrow_primary_exception, void *a) { + CHECK(REAL(__cxa_rethrow_primary_exception)); + __asan_handle_no_return(); + REAL(__cxa_rethrow_primary_exception)(a); +} +#endif + +#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION +INTERCEPTOR(_Unwind_Reason_Code, _Unwind_RaiseException, + _Unwind_Exception *object) { + CHECK(REAL(_Unwind_RaiseException)); + __asan_handle_no_return(); + return REAL(_Unwind_RaiseException)(object); +} +#endif + +#if ASAN_INTERCEPT__SJLJ_UNWIND_RAISEEXCEPTION +INTERCEPTOR(_Unwind_Reason_Code, _Unwind_SjLj_RaiseException, + _Unwind_Exception *object) { + CHECK(REAL(_Unwind_SjLj_RaiseException)); + __asan_handle_no_return(); + return REAL(_Unwind_SjLj_RaiseException)(object); +} +#endif + +#if ASAN_INTERCEPT_INDEX +# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX +INTERCEPTOR(char*, index, const char *string, int c) + ALIAS(WRAPPER_NAME(strchr)); +# else +# if SANITIZER_MAC +DECLARE_REAL(char*, index, const char *string, int c) +OVERRIDE_FUNCTION(index, strchr); +# else +DEFINE_REAL(char*, index, const char *string, int c) +# endif +# endif +#endif // ASAN_INTERCEPT_INDEX + +// For both strcat() and strncat() we need to check the validity of |to| +// argument irrespective of the |from| length. +INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strcat); // NOLINT + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_length = REAL(strlen)(from); + ASAN_READ_RANGE(ctx, from, from_length + 1); + uptr to_length = REAL(strlen)(to); + ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); + ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); + // If the copying actually happens, the |from| string should not overlap + // with the resulting string starting at |to|, which has a length of + // to_length + from_length + 1. + if (from_length > 0) { + CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1, + from, from_length + 1); + } + } + return REAL(strcat)(to, from); // NOLINT +} + +INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strncat); + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_length = MaybeRealStrnlen(from, size); + uptr copy_length = Min(size, from_length + 1); + ASAN_READ_RANGE(ctx, from, copy_length); + uptr to_length = REAL(strlen)(to); + ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); + ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); + if (from_length > 0) { + CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1, + from, copy_length); + } + } + return REAL(strncat)(to, from, size); +} + +INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strcpy); // NOLINT +#if SANITIZER_MAC + if (UNLIKELY(!asan_inited)) return REAL(strcpy)(to, from); // NOLINT +#endif + // strcpy is called from malloc_default_purgeable_zone() + // in __asan::ReplaceSystemAlloc() on Mac. + if (asan_init_is_running) { + return REAL(strcpy)(to, from); // NOLINT + } + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_size = REAL(strlen)(from) + 1; + CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size); + ASAN_READ_RANGE(ctx, from, from_size); + ASAN_WRITE_RANGE(ctx, to, from_size); + } + return REAL(strcpy)(to, from); // NOLINT +} + +INTERCEPTOR(char*, strdup, const char *s) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strdup); + if (UNLIKELY(!asan_inited)) return internal_strdup(s); + ENSURE_ASAN_INITED(); + uptr length = REAL(strlen)(s); + if (flags()->replace_str) { + ASAN_READ_RANGE(ctx, s, length + 1); + } + GET_STACK_TRACE_MALLOC; + void *new_mem = asan_malloc(length + 1, &stack); + REAL(memcpy)(new_mem, s, length + 1); + return reinterpret_cast(new_mem); +} + +#if ASAN_INTERCEPT___STRDUP +INTERCEPTOR(char*, __strdup, const char *s) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strdup); + if (UNLIKELY(!asan_inited)) return internal_strdup(s); + ENSURE_ASAN_INITED(); + uptr length = REAL(strlen)(s); + if (flags()->replace_str) { + ASAN_READ_RANGE(ctx, s, length + 1); + } + GET_STACK_TRACE_MALLOC; + void *new_mem = asan_malloc(length + 1, &stack); + REAL(memcpy)(new_mem, s, length + 1); + return reinterpret_cast(new_mem); +} +#endif // ASAN_INTERCEPT___STRDUP + +INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strncpy); + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); + CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size); + ASAN_READ_RANGE(ctx, from, from_size); + ASAN_WRITE_RANGE(ctx, to, size); + } + return REAL(strncpy)(to, from, size); +} + +INTERCEPTOR(long, strtol, const char *nptr, // NOLINT + char **endptr, int base) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strtol); + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(strtol)(nptr, endptr, base); + } + char *real_endptr; + long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT + StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); + return result; +} + +INTERCEPTOR(int, atoi, const char *nptr) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, atoi); +#if SANITIZER_MAC + if (UNLIKELY(!asan_inited)) return REAL(atoi)(nptr); +#endif + 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_STRING(ctx, nptr, (real_endptr - nptr) + 1); + return result; +} + +INTERCEPTOR(long, atol, const char *nptr) { // NOLINT + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, atol); +#if SANITIZER_MAC + if (UNLIKELY(!asan_inited)) return REAL(atol)(nptr); +#endif + 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_STRING(ctx, nptr, (real_endptr - nptr) + 1); + return result; +} + +#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL +INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT + char **endptr, int base) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strtoll); + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(strtoll)(nptr, endptr, base); + } + char *real_endptr; + long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT + StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); + return result; +} + +INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, atoll); + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(atoll)(nptr); + } + char *real_endptr; + long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1); + return result; +} +#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL + +#if ASAN_INTERCEPT___CXA_ATEXIT +static void AtCxaAtexit(void *unused) { + (void)unused; + StopInitOrderChecking(); +} + +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { +#if SANITIZER_MAC + if (UNLIKELY(!asan_inited)) return REAL(__cxa_atexit)(func, arg, dso_handle); +#endif + ENSURE_ASAN_INITED(); + int res = REAL(__cxa_atexit)(func, arg, dso_handle); + REAL(__cxa_atexit)(AtCxaAtexit, nullptr, nullptr); + return res; +} +#endif // ASAN_INTERCEPT___CXA_ATEXIT + +#if ASAN_INTERCEPT_VFORK +DEFINE_REAL(int, vfork) +DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork) +#endif + +// ---------------------- InitializeAsanInterceptors ---------------- {{{1 +namespace __asan { +void InitializeAsanInterceptors() { + static bool was_called_once; + CHECK(!was_called_once); + was_called_once = true; + InitializeCommonInterceptors(); + InitializeSignalInterceptors(); + + // Intercept str* functions. + ASAN_INTERCEPT_FUNC(strcat); // NOLINT + ASAN_INTERCEPT_FUNC(strcpy); // NOLINT + ASAN_INTERCEPT_FUNC(strncat); + ASAN_INTERCEPT_FUNC(strncpy); + ASAN_INTERCEPT_FUNC(strdup); +#if ASAN_INTERCEPT___STRDUP + ASAN_INTERCEPT_FUNC(__strdup); +#endif +#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX + ASAN_INTERCEPT_FUNC(index); +#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 jump-related functions. + ASAN_INTERCEPT_FUNC(longjmp); + +#if ASAN_INTERCEPT_SWAPCONTEXT + ASAN_INTERCEPT_FUNC(swapcontext); +#endif +#if ASAN_INTERCEPT__LONGJMP + ASAN_INTERCEPT_FUNC(_longjmp); +#endif +#if ASAN_INTERCEPT___LONGJMP_CHK + ASAN_INTERCEPT_FUNC(__longjmp_chk); +#endif +#if ASAN_INTERCEPT_SIGLONGJMP + ASAN_INTERCEPT_FUNC(siglongjmp); +#endif + + // Intercept exception handling functions. +#if ASAN_INTERCEPT___CXA_THROW + ASAN_INTERCEPT_FUNC(__cxa_throw); +#endif +#if ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION + ASAN_INTERCEPT_FUNC(__cxa_rethrow_primary_exception); +#endif + // Indirectly intercept std::rethrow_exception. +#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION + INTERCEPT_FUNCTION(_Unwind_RaiseException); +#endif + // Indirectly intercept std::rethrow_exception. +#if ASAN_INTERCEPT__UNWIND_SJLJ_RAISEEXCEPTION + INTERCEPT_FUNCTION(_Unwind_SjLj_RaiseException); +#endif + + // Intercept threading-related functions +#if ASAN_INTERCEPT_PTHREAD_CREATE +#if defined(ASAN_PTHREAD_CREATE_VERSION) + ASAN_INTERCEPT_FUNC_VER(pthread_create, ASAN_PTHREAD_CREATE_VERSION); +#else + ASAN_INTERCEPT_FUNC(pthread_create); +#endif + ASAN_INTERCEPT_FUNC(pthread_join); +#endif + + // Intercept atexit function. +#if ASAN_INTERCEPT___CXA_ATEXIT + ASAN_INTERCEPT_FUNC(__cxa_atexit); +#endif + +#if ASAN_INTERCEPT_VFORK + ASAN_INTERCEPT_FUNC(vfork); +#endif + + InitializePlatformInterceptors(); + + VReport(1, "AddressSanitizer: libc interceptors initialized\n"); +} + +} // namespace __asan + +#endif // !SANITIZER_FUCHSIA diff --git a/libsanitizer/asan/asan_interceptors.h b/libsanitizer/asan/asan_interceptors.h index beb1dc9532b..155ea4156ab 100644 --- a/libsanitizer/asan/asan_interceptors.h +++ b/libsanitizer/asan/asan_interceptors.h @@ -1,13 +1,14 @@ //===-- asan_interceptors.h -------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_interceptors.cc +// ASan-private header for asan_interceptors.cpp //===----------------------------------------------------------------------===// #ifndef ASAN_INTERCEPTORS_H #define ASAN_INTERCEPTORS_H @@ -79,12 +80,7 @@ void InitializePlatformInterceptors(); #if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS && !SANITIZER_SOLARIS && \ !SANITIZER_NETBSD # define ASAN_INTERCEPT___CXA_THROW 1 -# if ! defined(ASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION) \ - || ASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION -# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 1 -# else -# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 0 -# endif +# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 1 # if defined(_GLIBCXX_SJLJ_EXCEPTIONS) || (SANITIZER_IOS && defined(__arm__)) # define ASAN_INTERCEPT__UNWIND_SJLJ_RAISEEXCEPTION 1 # else @@ -109,6 +105,13 @@ void InitializePlatformInterceptors(); # define ASAN_INTERCEPT___STRDUP 0 #endif +#if SANITIZER_LINUX && (defined(__arm__) || defined(__aarch64__) || \ + defined(__i386__) || defined(__x86_64__)) +# define ASAN_INTERCEPT_VFORK 1 +#else +# define ASAN_INTERCEPT_VFORK 0 +#endif + DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size) DECLARE_REAL(char*, strchr, const char *str, int c) DECLARE_REAL(SIZE_T, strlen, const char *s) @@ -117,16 +120,16 @@ DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen) DECLARE_REAL(char*, strstr, const char *s1, const char *s2) #if !SANITIZER_MAC -#define ASAN_INTERCEPT_FUNC(name) \ - do { \ - if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \ - VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \ +#define ASAN_INTERCEPT_FUNC(name) \ + do { \ + if (!INTERCEPT_FUNCTION(name)) \ + VReport(1, "AddressSanitizer: failed to intercept '%s'\n'", #name); \ } while (0) -#define ASAN_INTERCEPT_FUNC_VER(name, ver) \ - do { \ - if ((!INTERCEPT_FUNCTION_VER(name, ver) || !REAL(name))) \ - VReport( \ - 1, "AddressSanitizer: failed to intercept '" #name "@@" #ver "'\n"); \ +#define ASAN_INTERCEPT_FUNC_VER(name, ver) \ + do { \ + if (!INTERCEPT_FUNCTION_VER(name, ver)) \ + VReport(1, "AddressSanitizer: failed to intercept '%s@@%s'\n", #name, \ + #ver); \ } while (0) #else // OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION. diff --git a/libsanitizer/asan/asan_interceptors_memintrinsics.cc b/libsanitizer/asan/asan_interceptors_memintrinsics.cc deleted file mode 100644 index b0c06a04cac..00000000000 --- a/libsanitizer/asan/asan_interceptors_memintrinsics.cc +++ /dev/null @@ -1,42 +0,0 @@ -//===-- asan_interceptors_memintrinsics.cc --------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===---------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// ASan versions of memcpy, memmove, and memset. -//===---------------------------------------------------------------------===// - -#include "asan_interceptors_memintrinsics.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "asan_suppressions.h" - -using namespace __asan; // NOLINT - -void *__asan_memcpy(void *to, const void *from, uptr size) { - ASAN_MEMCPY_IMPL(nullptr, to, from, size); -} - -void *__asan_memset(void *block, int c, uptr size) { - ASAN_MEMSET_IMPL(nullptr, block, c, size); -} - -void *__asan_memmove(void *to, const void *from, uptr size) { - ASAN_MEMMOVE_IMPL(nullptr, to, from, size); -} - -#if SANITIZER_FUCHSIA || SANITIZER_RTEMS - -// Fuchsia and RTEMS don't use sanitizer_common_interceptors.inc, but -// the only things there it wants are these three. Just define them -// as aliases here rather than repeating the contents. - -extern "C" decltype(__asan_memcpy) memcpy[[gnu::alias("__asan_memcpy")]]; -extern "C" decltype(__asan_memmove) memmove[[gnu::alias("__asan_memmove")]]; -extern "C" decltype(__asan_memset) memset[[gnu::alias("__asan_memset")]]; - -#endif // SANITIZER_FUCHSIA || SANITIZER_RTEMS diff --git a/libsanitizer/asan/asan_interceptors_memintrinsics.cpp b/libsanitizer/asan/asan_interceptors_memintrinsics.cpp new file mode 100644 index 00000000000..56df60ba681 --- /dev/null +++ b/libsanitizer/asan/asan_interceptors_memintrinsics.cpp @@ -0,0 +1,43 @@ +//===-- asan_interceptors_memintrinsics.cpp -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===---------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan versions of memcpy, memmove, and memset. +//===---------------------------------------------------------------------===// + +#include "asan_interceptors_memintrinsics.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_suppressions.h" + +using namespace __asan; // NOLINT + +void *__asan_memcpy(void *to, const void *from, uptr size) { + ASAN_MEMCPY_IMPL(nullptr, to, from, size); +} + +void *__asan_memset(void *block, int c, uptr size) { + ASAN_MEMSET_IMPL(nullptr, block, c, size); +} + +void *__asan_memmove(void *to, const void *from, uptr size) { + ASAN_MEMMOVE_IMPL(nullptr, to, from, size); +} + +#if SANITIZER_FUCHSIA || SANITIZER_RTEMS + +// Fuchsia and RTEMS don't use sanitizer_common_interceptors.inc, but +// the only things there it wants are these three. Just define them +// as aliases here rather than repeating the contents. + +extern "C" decltype(__asan_memcpy) memcpy[[gnu::alias("__asan_memcpy")]]; +extern "C" decltype(__asan_memmove) memmove[[gnu::alias("__asan_memmove")]]; +extern "C" decltype(__asan_memset) memset[[gnu::alias("__asan_memset")]]; + +#endif // SANITIZER_FUCHSIA || SANITIZER_RTEMS diff --git a/libsanitizer/asan/asan_interceptors_memintrinsics.h b/libsanitizer/asan/asan_interceptors_memintrinsics.h index faf8119c937..632f0515a9e 100644 --- a/libsanitizer/asan/asan_interceptors_memintrinsics.h +++ b/libsanitizer/asan/asan_interceptors_memintrinsics.h @@ -1,13 +1,14 @@ //===-- asan_interceptors_memintrinsics.h -----------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===---------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_memintrin.cc +// ASan-private header for asan_interceptors_memintrinsics.cpp //===---------------------------------------------------------------------===// #ifndef ASAN_MEMINTRIN_H #define ASAN_MEMINTRIN_H diff --git a/libsanitizer/asan/asan_interceptors_vfork.S b/libsanitizer/asan/asan_interceptors_vfork.S new file mode 100644 index 00000000000..90a169d4b60 --- /dev/null +++ b/libsanitizer/asan/asan_interceptors_vfork.S @@ -0,0 +1,12 @@ +#include "sanitizer_common/sanitizer_asm.h" + +#if defined(__linux__) +#define COMMON_INTERCEPTOR_SPILL_AREA __asan_extra_spill_area +#define COMMON_INTERCEPTOR_HANDLE_VFORK __asan_handle_vfork +#include "sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S" +#include "sanitizer_common/sanitizer_common_interceptors_vfork_arm.inc.S" +#include "sanitizer_common/sanitizer_common_interceptors_vfork_x86_64.inc.S" +#include "sanitizer_common/sanitizer_common_interceptors_vfork_i386.inc.S" +#endif + +NO_EXEC_STACK_DIRECTIVE diff --git a/libsanitizer/asan/asan_interface.inc b/libsanitizer/asan/asan_interface.inc index b2fcde1c770..7c341f22e15 100644 --- a/libsanitizer/asan/asan_interface.inc +++ b/libsanitizer/asan/asan_interface.inc @@ -1,7 +1,8 @@ //===-- asan_interface.inc ------------------------------------------------===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Asan interface list. @@ -37,6 +38,7 @@ INTERFACE_FUNCTION(__asan_get_report_pc) INTERFACE_FUNCTION(__asan_get_report_sp) INTERFACE_FUNCTION(__asan_get_shadow_mapping) INTERFACE_FUNCTION(__asan_handle_no_return) +INTERFACE_FUNCTION(__asan_handle_vfork) INTERFACE_FUNCTION(__asan_init) INTERFACE_FUNCTION(__asan_load_cxx_array_cookie) INTERFACE_FUNCTION(__asan_load1) diff --git a/libsanitizer/asan/asan_interface_internal.h b/libsanitizer/asan/asan_interface_internal.h index be9605d9e6e..c83aa11d741 100644 --- a/libsanitizer/asan/asan_interface_internal.h +++ b/libsanitizer/asan/asan_interface_internal.h @@ -1,7 +1,8 @@ //===-- asan_interface_internal.h -------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -248,6 +249,8 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char* __asan_default_suppressions(); + + SANITIZER_INTERFACE_ATTRIBUTE void __asan_handle_vfork(void *sp); } // extern "C" #endif // ASAN_INTERFACE_INTERNAL_H diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h index 7c239895e39..72a4c3f22ff 100644 --- a/libsanitizer/asan/asan_internal.h +++ b/libsanitizer/asan/asan_internal.h @@ -1,7 +1,8 @@ //===-- asan_internal.h -----------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -60,29 +61,29 @@ using __sanitizer::StackTrace; void AsanInitFromRtl(); -// asan_win.cc +// asan_win.cpp void InitializePlatformExceptionHandlers(); // Returns whether an address is a valid allocated system heap block. // 'addr' must point to the beginning of the block. bool IsSystemHeapAddress(uptr addr); -// asan_rtl.cc +// asan_rtl.cpp void PrintAddressSpaceLayout(); void NORETURN ShowStatsAndAbort(); -// asan_shadow_setup.cc +// asan_shadow_setup.cpp void InitializeShadowMemory(); -// asan_malloc_linux.cc / asan_malloc_mac.cc +// asan_malloc_linux.cpp / asan_malloc_mac.cpp void ReplaceSystemMalloc(); -// asan_linux.cc / asan_mac.cc / asan_rtems.cc / asan_win.cc +// asan_linux.cpp / asan_mac.cpp / asan_rtems.cpp / asan_win.cpp uptr FindDynamicShadowStart(); void *AsanDoesNotSupportStaticLinkage(); void AsanCheckDynamicRTPrereqs(); void AsanCheckIncompatibleRT(); -// asan_thread.cc +// asan_thread.cpp AsanThread *CreateMainThread(); // Support function for __asan_(un)register_image_globals. Searches for the @@ -109,6 +110,11 @@ void *AsanDlSymNext(const char *sym); void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name); +// Returns `true` iff most of ASan init process should be skipped due to the +// ASan library being loaded via `dlopen()`. Platforms may perform any +// `dlopen()` specific initialization inside this function. +bool HandleDlopenInit(); + // Add convenient macro for interface functions that may be represented as // weak hooks. #define ASAN_MALLOC_HOOK(ptr, size) \ diff --git a/libsanitizer/asan/asan_linux.cc b/libsanitizer/asan/asan_linux.cc deleted file mode 100644 index d92d0596b7c..00000000000 --- a/libsanitizer/asan/asan_linux.cc +++ /dev/null @@ -1,252 +0,0 @@ -//===-- asan_linux.cc -----------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Linux-specific details. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ - SANITIZER_SOLARIS - -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_premap_shadow.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_flags.h" -#include "sanitizer_common/sanitizer_freebsd.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_procmaps.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if SANITIZER_FREEBSD -#include -#endif - -#if SANITIZER_SOLARIS -#include -#endif - -#if SANITIZER_ANDROID || SANITIZER_FREEBSD || SANITIZER_SOLARIS -#include -extern "C" void* _DYNAMIC; -#elif SANITIZER_NETBSD -#include -#include -extern Elf_Dyn _DYNAMIC; -#else -#include -#include -#endif - -// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in -// 32-bit mode. -#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \ - __FreeBSD_version <= 902001 // v9.2 -#define ucontext_t xucontext_t -#endif - -typedef enum { - ASAN_RT_VERSION_UNDEFINED = 0, - ASAN_RT_VERSION_DYNAMIC, - ASAN_RT_VERSION_STATIC, -} asan_rt_version_t; - -// FIXME: perhaps also store abi version here? -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE -asan_rt_version_t __asan_rt_version; -} - -namespace __asan { - -void InitializePlatformInterceptors() {} -void InitializePlatformExceptionHandlers() {} -bool IsSystemHeapAddress (uptr addr) { return false; } - -void *AsanDoesNotSupportStaticLinkage() { - // This will fail to link with -static. - return &_DYNAMIC; // defined in link.h -} - -static void UnmapFromTo(uptr from, uptr to) { - CHECK(to >= from); - if (to == from) return; - uptr res = internal_munmap(reinterpret_cast(from), to - from); - if (UNLIKELY(internal_iserror(res))) { - Report( - "ERROR: AddresSanitizer failed to unmap 0x%zx (%zd) bytes at address " - "%p\n", - to - from, to - from, from); - CHECK("unable to unmap" && 0); - } -} - -#if ASAN_PREMAP_SHADOW -uptr FindPremappedShadowStart() { - uptr granularity = GetMmapGranularity(); - uptr shadow_start = reinterpret_cast(&__asan_shadow); - uptr premap_shadow_size = PremapShadowSize(); - uptr shadow_size = RoundUpTo(kHighShadowEnd, granularity); - // We may have mapped too much. Release extra memory. - UnmapFromTo(shadow_start + shadow_size, shadow_start + premap_shadow_size); - return shadow_start; -} -#endif - -uptr FindDynamicShadowStart() { -#if ASAN_PREMAP_SHADOW - if (!PremapShadowFailed()) - return FindPremappedShadowStart(); -#endif - - uptr granularity = GetMmapGranularity(); - uptr alignment = granularity * 8; - uptr left_padding = granularity; - uptr shadow_size = RoundUpTo(kHighShadowEnd, granularity); - uptr map_size = shadow_size + left_padding + alignment; - - uptr map_start = (uptr)MmapNoAccess(map_size); - CHECK_NE(map_start, ~(uptr)0); - - uptr shadow_start = RoundUpTo(map_start + left_padding, alignment); - UnmapFromTo(map_start, shadow_start - left_padding); - UnmapFromTo(shadow_start + shadow_size, map_start + map_size); - - return shadow_start; -} - -void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { - UNIMPLEMENTED(); -} - -#if SANITIZER_ANDROID -// FIXME: should we do anything for Android? -void AsanCheckDynamicRTPrereqs() {} -void AsanCheckIncompatibleRT() {} -#else -static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size, - void *data) { - VReport(2, "info->dlpi_name = %s\tinfo->dlpi_addr = %p\n", - info->dlpi_name, info->dlpi_addr); - - // Continue until the first dynamic library is found - if (!info->dlpi_name || info->dlpi_name[0] == 0) - return 0; - - // Ignore vDSO - if (internal_strncmp(info->dlpi_name, "linux-", sizeof("linux-") - 1) == 0) - return 0; - -#if SANITIZER_FREEBSD || SANITIZER_NETBSD - // Ignore first entry (the main program) - char **p = (char **)data; - if (!(*p)) { - *p = (char *)-1; - return 0; - } -#endif - -#if SANITIZER_SOLARIS - // Ignore executable on Solaris - if (info->dlpi_addr == 0) - return 0; -#endif - - *(const char **)data = info->dlpi_name; - return 1; -} - -static bool IsDynamicRTName(const char *libname) { - return internal_strstr(libname, "libclang_rt.asan") || - internal_strstr(libname, "libasan.so"); -} - -static void ReportIncompatibleRT() { - Report("Your application is linked against incompatible ASan runtimes.\n"); - Die(); -} - -void AsanCheckDynamicRTPrereqs() { - if (!ASAN_DYNAMIC || !flags()->verify_asan_link_order) - return; - - // Ensure that dynamic RT is the first DSO in the list - const char *first_dso_name = nullptr; - dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name); - if (first_dso_name && !IsDynamicRTName(first_dso_name)) { - Report("ASan runtime does not come first in initial library list; " - "you should either link runtime to your application or " - "manually preload it with LD_PRELOAD.\n"); - Die(); - } -} - -void AsanCheckIncompatibleRT() { - if (ASAN_DYNAMIC) { - if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { - __asan_rt_version = ASAN_RT_VERSION_DYNAMIC; - } else if (__asan_rt_version != ASAN_RT_VERSION_DYNAMIC) { - ReportIncompatibleRT(); - } - } else { - if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { - // Ensure that dynamic runtime is not present. We should detect it - // as early as possible, otherwise ASan interceptors could bind to - // the functions in dynamic ASan runtime instead of the functions in - // system libraries, causing crashes later in ASan initialization. - MemoryMappingLayout proc_maps(/*cache_enabled*/true); - char filename[PATH_MAX]; - MemoryMappedSegment segment(filename, sizeof(filename)); - while (proc_maps.Next(&segment)) { - if (IsDynamicRTName(segment.filename)) { - Report("Your application is linked against " - "incompatible ASan runtimes.\n"); - Die(); - } - } - __asan_rt_version = ASAN_RT_VERSION_STATIC; - } else if (__asan_rt_version != ASAN_RT_VERSION_STATIC) { - ReportIncompatibleRT(); - } - } -} -#endif // SANITIZER_ANDROID - -#if !SANITIZER_ANDROID -void ReadContextStack(void *context, uptr *stack, uptr *ssize) { - ucontext_t *ucp = (ucontext_t*)context; - *stack = (uptr)ucp->uc_stack.ss_sp; - *ssize = ucp->uc_stack.ss_size; -} -#else -void ReadContextStack(void *context, uptr *stack, uptr *ssize) { - UNIMPLEMENTED(); -} -#endif - -void *AsanDlSymNext(const char *sym) { - return dlsym(RTLD_NEXT, sym); -} - -} // namespace __asan - -#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || - // SANITIZER_SOLARIS diff --git a/libsanitizer/asan/asan_linux.cpp b/libsanitizer/asan/asan_linux.cpp new file mode 100644 index 00000000000..ce5e873dc51 --- /dev/null +++ b/libsanitizer/asan/asan_linux.cpp @@ -0,0 +1,260 @@ +//===-- asan_linux.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Linux-specific details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ + SANITIZER_SOLARIS + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_premap_shadow.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_freebsd.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if SANITIZER_FREEBSD +#include +#endif + +#if SANITIZER_SOLARIS +#include +#endif + +#if SANITIZER_ANDROID || SANITIZER_FREEBSD || SANITIZER_SOLARIS +#include +extern "C" void* _DYNAMIC; +#elif SANITIZER_NETBSD +#include +#include +extern Elf_Dyn _DYNAMIC; +#else +#include +#include +#endif + +// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in +// 32-bit mode. +#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \ + __FreeBSD_version <= 902001 // v9.2 +#define ucontext_t xucontext_t +#endif + +typedef enum { + ASAN_RT_VERSION_UNDEFINED = 0, + ASAN_RT_VERSION_DYNAMIC, + ASAN_RT_VERSION_STATIC, +} asan_rt_version_t; + +// FIXME: perhaps also store abi version here? +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +asan_rt_version_t __asan_rt_version; +} + +namespace __asan { + +void InitializePlatformInterceptors() {} +void InitializePlatformExceptionHandlers() {} +bool IsSystemHeapAddress (uptr addr) { return false; } + +void *AsanDoesNotSupportStaticLinkage() { + // This will fail to link with -static. + return &_DYNAMIC; // defined in link.h +} + +static void UnmapFromTo(uptr from, uptr to) { + CHECK(to >= from); + if (to == from) return; + uptr res = internal_munmap(reinterpret_cast(from), to - from); + if (UNLIKELY(internal_iserror(res))) { + Report( + "ERROR: AddresSanitizer failed to unmap 0x%zx (%zd) bytes at address " + "%p\n", + to - from, to - from, from); + CHECK("unable to unmap" && 0); + } +} + +#if ASAN_PREMAP_SHADOW +uptr FindPremappedShadowStart() { + uptr granularity = GetMmapGranularity(); + uptr shadow_start = reinterpret_cast(&__asan_shadow); + uptr premap_shadow_size = PremapShadowSize(); + uptr shadow_size = RoundUpTo(kHighShadowEnd, granularity); + // We may have mapped too much. Release extra memory. + UnmapFromTo(shadow_start + shadow_size, shadow_start + premap_shadow_size); + return shadow_start; +} +#endif + +uptr FindDynamicShadowStart() { +#if ASAN_PREMAP_SHADOW + if (!PremapShadowFailed()) + return FindPremappedShadowStart(); +#endif + + uptr granularity = GetMmapGranularity(); + uptr alignment = granularity * 8; + uptr left_padding = granularity; + uptr shadow_size = RoundUpTo(kHighShadowEnd, granularity); + uptr map_size = shadow_size + left_padding + alignment; + + uptr map_start = (uptr)MmapNoAccess(map_size); + CHECK_NE(map_start, ~(uptr)0); + + uptr shadow_start = RoundUpTo(map_start + left_padding, alignment); + UnmapFromTo(map_start, shadow_start - left_padding); + UnmapFromTo(shadow_start + shadow_size, map_start + map_size); + + return shadow_start; +} + +void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { + UNIMPLEMENTED(); +} + +#if SANITIZER_ANDROID +// FIXME: should we do anything for Android? +void AsanCheckDynamicRTPrereqs() {} +void AsanCheckIncompatibleRT() {} +#else +static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size, + void *data) { + VReport(2, "info->dlpi_name = %s\tinfo->dlpi_addr = %p\n", + info->dlpi_name, info->dlpi_addr); + + // Continue until the first dynamic library is found + if (!info->dlpi_name || info->dlpi_name[0] == 0) + return 0; + + // Ignore vDSO + if (internal_strncmp(info->dlpi_name, "linux-", sizeof("linux-") - 1) == 0) + return 0; + +#if SANITIZER_FREEBSD || SANITIZER_NETBSD + // Ignore first entry (the main program) + char **p = (char **)data; + if (!(*p)) { + *p = (char *)-1; + return 0; + } +#endif + +#if SANITIZER_SOLARIS + // Ignore executable on Solaris + if (info->dlpi_addr == 0) + return 0; +#endif + + *(const char **)data = info->dlpi_name; + return 1; +} + +static bool IsDynamicRTName(const char *libname) { + return internal_strstr(libname, "libclang_rt.asan") || + internal_strstr(libname, "libasan.so"); +} + +static void ReportIncompatibleRT() { + Report("Your application is linked against incompatible ASan runtimes.\n"); + Die(); +} + +void AsanCheckDynamicRTPrereqs() { + if (!ASAN_DYNAMIC || !flags()->verify_asan_link_order) + return; + + // Ensure that dynamic RT is the first DSO in the list + const char *first_dso_name = nullptr; + dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name); + if (first_dso_name && !IsDynamicRTName(first_dso_name)) { + Report("ASan runtime does not come first in initial library list; " + "you should either link runtime to your application or " + "manually preload it with LD_PRELOAD.\n"); + Die(); + } +} + +void AsanCheckIncompatibleRT() { + if (ASAN_DYNAMIC) { + if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { + __asan_rt_version = ASAN_RT_VERSION_DYNAMIC; + } else if (__asan_rt_version != ASAN_RT_VERSION_DYNAMIC) { + ReportIncompatibleRT(); + } + } else { + if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { + // Ensure that dynamic runtime is not present. We should detect it + // as early as possible, otherwise ASan interceptors could bind to + // the functions in dynamic ASan runtime instead of the functions in + // system libraries, causing crashes later in ASan initialization. + MemoryMappingLayout proc_maps(/*cache_enabled*/true); + char filename[PATH_MAX]; + MemoryMappedSegment segment(filename, sizeof(filename)); + while (proc_maps.Next(&segment)) { + if (IsDynamicRTName(segment.filename)) { + Report("Your application is linked against " + "incompatible ASan runtimes.\n"); + Die(); + } + } + __asan_rt_version = ASAN_RT_VERSION_STATIC; + } else if (__asan_rt_version != ASAN_RT_VERSION_STATIC) { + ReportIncompatibleRT(); + } + } +} +#endif // SANITIZER_ANDROID + +#if !SANITIZER_ANDROID +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + ucontext_t *ucp = (ucontext_t*)context; + *stack = (uptr)ucp->uc_stack.ss_sp; + *ssize = ucp->uc_stack.ss_size; +} +#else +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + UNIMPLEMENTED(); +} +#endif + +void *AsanDlSymNext(const char *sym) { + return dlsym(RTLD_NEXT, sym); +} + +bool HandleDlopenInit() { + // Not supported on this platform. + static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN, + "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false"); + return false; +} + +} // namespace __asan + +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || + // SANITIZER_SOLARIS diff --git a/libsanitizer/asan/asan_mac.cc b/libsanitizer/asan/asan_mac.cc deleted file mode 100644 index 89a3db4c2fb..00000000000 --- a/libsanitizer/asan/asan_mac.cc +++ /dev/null @@ -1,330 +0,0 @@ -//===-- asan_mac.cc -------------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Mac-specific details. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_MAC - -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "asan_stack.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_atomic.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_mac.h" - -#include -#include -#include -#include -#include -#include -#include -#include // for free() -#include -#include -#include -#include -#include - -// from , but we don't have that file on iOS -extern "C" { - extern char ***_NSGetArgv(void); - extern char ***_NSGetEnviron(void); -} - -namespace __asan { - -void InitializePlatformInterceptors() {} -void InitializePlatformExceptionHandlers() {} -bool IsSystemHeapAddress (uptr addr) { return false; } - -// No-op. Mac does not support static linkage anyway. -void *AsanDoesNotSupportStaticLinkage() { - return 0; -} - -uptr FindDynamicShadowStart() { - uptr granularity = GetMmapGranularity(); - uptr alignment = 8 * granularity; - uptr left_padding = granularity; - uptr space_size = kHighShadowEnd + left_padding; - - uptr largest_gap_found = 0; - uptr max_occupied_addr = 0; - VReport(2, "FindDynamicShadowStart, space_size = %p\n", space_size); - uptr shadow_start = - FindAvailableMemoryRange(space_size, alignment, granularity, - &largest_gap_found, &max_occupied_addr); - // If the shadow doesn't fit, restrict the address space to make it fit. - if (shadow_start == 0) { - VReport( - 2, - "Shadow doesn't fit, largest_gap_found = %p, max_occupied_addr = %p\n", - largest_gap_found, max_occupied_addr); - uptr new_max_vm = RoundDownTo(largest_gap_found << SHADOW_SCALE, alignment); - if (new_max_vm < max_occupied_addr) { - Report("Unable to find a memory range for dynamic shadow.\n"); - Report( - "space_size = %p, largest_gap_found = %p, max_occupied_addr = %p, " - "new_max_vm = %p\n", - space_size, largest_gap_found, max_occupied_addr, new_max_vm); - CHECK(0 && "cannot place shadow"); - } - RestrictMemoryToMaxAddress(new_max_vm); - kHighMemEnd = new_max_vm - 1; - space_size = kHighShadowEnd + left_padding; - VReport(2, "FindDynamicShadowStart, space_size = %p\n", space_size); - shadow_start = FindAvailableMemoryRange(space_size, alignment, granularity, - nullptr, nullptr); - if (shadow_start == 0) { - Report("Unable to find a memory range after restricting VM.\n"); - CHECK(0 && "cannot place shadow after restricting vm"); - } - } - CHECK_NE((uptr)0, shadow_start); - CHECK(IsAligned(shadow_start, alignment)); - return shadow_start; -} - -// No-op. Mac does not support static linkage anyway. -void AsanCheckDynamicRTPrereqs() {} - -// No-op. Mac does not support static linkage anyway. -void AsanCheckIncompatibleRT() {} - -void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { - // Find the Mach-O header for the image containing the needle - Dl_info info; - int err = dladdr(needle, &info); - if (err == 0) return; - -#if __LP64__ - const struct mach_header_64 *mh = (struct mach_header_64 *)info.dli_fbase; -#else - const struct mach_header *mh = (struct mach_header *)info.dli_fbase; -#endif - - // Look up the __asan_globals section in that image and register its globals - unsigned long size = 0; - __asan_global *globals = (__asan_global *)getsectiondata( - mh, - "__DATA", "__asan_globals", - &size); - - if (!globals) return; - if (size % sizeof(__asan_global) != 0) return; - op(globals, size / sizeof(__asan_global)); -} - -void ReadContextStack(void *context, uptr *stack, uptr *ssize) { - UNIMPLEMENTED(); -} - -// Support for the following functions from libdispatch on Mac OS: -// dispatch_async_f() -// dispatch_async() -// dispatch_sync_f() -// dispatch_sync() -// dispatch_after_f() -// dispatch_after() -// dispatch_group_async_f() -// dispatch_group_async() -// TODO(glider): libdispatch API contains other functions that we don't support -// yet. -// -// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are -// they can cause jobs to run on a thread different from the current one. -// TODO(glider): if so, we need a test for this (otherwise we should remove -// them). -// -// The following functions use dispatch_barrier_async_f() (which isn't a library -// function but is exported) and are thus supported: -// dispatch_source_set_cancel_handler_f() -// dispatch_source_set_cancel_handler() -// dispatch_source_set_event_handler_f() -// dispatch_source_set_event_handler() -// -// The reference manual for Grand Central Dispatch is available at -// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html -// The implementation details are at -// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c - -typedef void* dispatch_group_t; -typedef void* dispatch_queue_t; -typedef void* dispatch_source_t; -typedef u64 dispatch_time_t; -typedef void (*dispatch_function_t)(void *block); -typedef void* (*worker_t)(void *block); - -// 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; - -ALWAYS_INLINE -void asan_register_worker_thread(int parent_tid, StackTrace *stack) { - AsanThread *t = GetCurrentThread(); - if (!t) { - t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr, - parent_tid, stack, /* detached */ true); - t->Init(); - asanThreadRegistry().StartThread(t->tid(), GetTid(), - /* workerthread */ true, 0); - SetCurrentThread(t); - } -} - -// For use by only those functions that allocated the context via -// alloc_asan_context(). -extern "C" -void asan_dispatch_call_block_and_release(void *block) { - GET_STACK_TRACE_THREAD; - asan_block_context_t *context = (asan_block_context_t*)block; - VReport(2, - "asan_dispatch_call_block_and_release(): " - "context: %p, pthread_self: %p\n", - block, pthread_self()); - asan_register_worker_thread(context->parent_tid, &stack); - // Call the original dispatcher for the block. - context->func(context->block); - asan_free(context, &stack, FROM_MALLOC); -} - -} // namespace __asan - -using namespace __asan; // NOLINT - -// Wrap |ctxt| and |func| into an asan_block_context_t. -// The caller retains control of the allocated context. -extern "C" -asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, - BufferedStackTrace *stack) { - asan_block_context_t *asan_ctxt = - (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); - asan_ctxt->block = ctxt; - asan_ctxt->func = func; - asan_ctxt->parent_tid = GetCurrentTidOrInvalid(); - return asan_ctxt; -} - -// Define interceptor for dispatch_*_f function with the three most common -// parameters: dispatch_queue_t, context, dispatch_function_t. -#define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \ - INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \ - dispatch_function_t func) { \ - GET_STACK_TRACE_THREAD; \ - asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ - if (Verbosity() >= 2) { \ - Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ - asan_ctxt, pthread_self()); \ - PRINT_CURRENT_STACK(); \ - } \ - return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \ - asan_dispatch_call_block_and_release); \ - } - -INTERCEPT_DISPATCH_X_F_3(dispatch_async_f) -INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f) -INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f) - -INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, - dispatch_queue_t dq, void *ctxt, - dispatch_function_t func) { - GET_STACK_TRACE_THREAD; - asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (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_group_async_f, dispatch_group_t group, - dispatch_queue_t dq, void *ctxt, - dispatch_function_t func) { - GET_STACK_TRACE_THREAD; - asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (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); -} - -#if !defined(MISSING_BLOCKS_SUPPORT) -extern "C" { -void dispatch_async(dispatch_queue_t dq, void(^work)(void)); -void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, - void(^work)(void)); -void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, - void(^work)(void)); -void dispatch_source_set_cancel_handler(dispatch_source_t ds, - void(^work)(void)); -void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); -} - -#define GET_ASAN_BLOCK(work) \ - void (^asan_block)(void); \ - int parent_tid = GetCurrentTidOrInvalid(); \ - asan_block = ^(void) { \ - GET_STACK_TRACE_THREAD; \ - asan_register_worker_thread(parent_tid, &stack); \ - work(); \ - } - -INTERCEPTOR(void, dispatch_async, - dispatch_queue_t dq, void(^work)(void)) { - ENABLE_FRAME_POINTER; - GET_ASAN_BLOCK(work); - REAL(dispatch_async)(dq, asan_block); -} - -INTERCEPTOR(void, dispatch_group_async, - dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) { - ENABLE_FRAME_POINTER; - GET_ASAN_BLOCK(work); - REAL(dispatch_group_async)(dg, dq, asan_block); -} - -INTERCEPTOR(void, dispatch_after, - dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) { - ENABLE_FRAME_POINTER; - GET_ASAN_BLOCK(work); - REAL(dispatch_after)(when, queue, asan_block); -} - -INTERCEPTOR(void, dispatch_source_set_cancel_handler, - dispatch_source_t ds, void(^work)(void)) { - if (!work) { - REAL(dispatch_source_set_cancel_handler)(ds, work); - return; - } - ENABLE_FRAME_POINTER; - GET_ASAN_BLOCK(work); - REAL(dispatch_source_set_cancel_handler)(ds, asan_block); -} - -INTERCEPTOR(void, dispatch_source_set_event_handler, - dispatch_source_t ds, void(^work)(void)) { - ENABLE_FRAME_POINTER; - GET_ASAN_BLOCK(work); - REAL(dispatch_source_set_event_handler)(ds, asan_block); -} -#endif - -#endif // SANITIZER_MAC diff --git a/libsanitizer/asan/asan_mac.cpp b/libsanitizer/asan/asan_mac.cpp new file mode 100644 index 00000000000..769d499672d --- /dev/null +++ b/libsanitizer/asan/asan_mac.cpp @@ -0,0 +1,331 @@ +//===-- asan_mac.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Mac-specific details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_mac.h" + +#include +#include +#include +#include +#include +#include +#include +#include // for free() +#include +#include +#include +#include +#include + +// from , but we don't have that file on iOS +extern "C" { + extern char ***_NSGetArgv(void); + extern char ***_NSGetEnviron(void); +} + +namespace __asan { + +void InitializePlatformInterceptors() {} +void InitializePlatformExceptionHandlers() {} +bool IsSystemHeapAddress (uptr addr) { return false; } + +// No-op. Mac does not support static linkage anyway. +void *AsanDoesNotSupportStaticLinkage() { + return 0; +} + +uptr FindDynamicShadowStart() { + uptr granularity = GetMmapGranularity(); + uptr alignment = 8 * granularity; + uptr left_padding = granularity; + uptr space_size = kHighShadowEnd + left_padding; + + uptr largest_gap_found = 0; + uptr max_occupied_addr = 0; + VReport(2, "FindDynamicShadowStart, space_size = %p\n", space_size); + uptr shadow_start = + FindAvailableMemoryRange(space_size, alignment, granularity, + &largest_gap_found, &max_occupied_addr); + // If the shadow doesn't fit, restrict the address space to make it fit. + if (shadow_start == 0) { + VReport( + 2, + "Shadow doesn't fit, largest_gap_found = %p, max_occupied_addr = %p\n", + largest_gap_found, max_occupied_addr); + uptr new_max_vm = RoundDownTo(largest_gap_found << SHADOW_SCALE, alignment); + if (new_max_vm < max_occupied_addr) { + Report("Unable to find a memory range for dynamic shadow.\n"); + Report( + "space_size = %p, largest_gap_found = %p, max_occupied_addr = %p, " + "new_max_vm = %p\n", + space_size, largest_gap_found, max_occupied_addr, new_max_vm); + CHECK(0 && "cannot place shadow"); + } + RestrictMemoryToMaxAddress(new_max_vm); + kHighMemEnd = new_max_vm - 1; + space_size = kHighShadowEnd + left_padding; + VReport(2, "FindDynamicShadowStart, space_size = %p\n", space_size); + shadow_start = FindAvailableMemoryRange(space_size, alignment, granularity, + nullptr, nullptr); + if (shadow_start == 0) { + Report("Unable to find a memory range after restricting VM.\n"); + CHECK(0 && "cannot place shadow after restricting vm"); + } + } + CHECK_NE((uptr)0, shadow_start); + CHECK(IsAligned(shadow_start, alignment)); + return shadow_start; +} + +// No-op. Mac does not support static linkage anyway. +void AsanCheckDynamicRTPrereqs() {} + +// No-op. Mac does not support static linkage anyway. +void AsanCheckIncompatibleRT() {} + +void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { + // Find the Mach-O header for the image containing the needle + Dl_info info; + int err = dladdr(needle, &info); + if (err == 0) return; + +#if __LP64__ + const struct mach_header_64 *mh = (struct mach_header_64 *)info.dli_fbase; +#else + const struct mach_header *mh = (struct mach_header *)info.dli_fbase; +#endif + + // Look up the __asan_globals section in that image and register its globals + unsigned long size = 0; + __asan_global *globals = (__asan_global *)getsectiondata( + mh, + "__DATA", "__asan_globals", + &size); + + if (!globals) return; + if (size % sizeof(__asan_global) != 0) return; + op(globals, size / sizeof(__asan_global)); +} + +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + UNIMPLEMENTED(); +} + +// Support for the following functions from libdispatch on Mac OS: +// dispatch_async_f() +// dispatch_async() +// dispatch_sync_f() +// dispatch_sync() +// dispatch_after_f() +// dispatch_after() +// dispatch_group_async_f() +// dispatch_group_async() +// TODO(glider): libdispatch API contains other functions that we don't support +// yet. +// +// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are +// they can cause jobs to run on a thread different from the current one. +// TODO(glider): if so, we need a test for this (otherwise we should remove +// them). +// +// The following functions use dispatch_barrier_async_f() (which isn't a library +// function but is exported) and are thus supported: +// dispatch_source_set_cancel_handler_f() +// dispatch_source_set_cancel_handler() +// dispatch_source_set_event_handler_f() +// dispatch_source_set_event_handler() +// +// The reference manual for Grand Central Dispatch is available at +// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html +// The implementation details are at +// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c + +typedef void* dispatch_group_t; +typedef void* dispatch_queue_t; +typedef void* dispatch_source_t; +typedef u64 dispatch_time_t; +typedef void (*dispatch_function_t)(void *block); +typedef void* (*worker_t)(void *block); + +// 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; + +ALWAYS_INLINE +void asan_register_worker_thread(int parent_tid, StackTrace *stack) { + AsanThread *t = GetCurrentThread(); + if (!t) { + t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr, + parent_tid, stack, /* detached */ true); + t->Init(); + asanThreadRegistry().StartThread(t->tid(), GetTid(), ThreadType::Worker, + nullptr); + SetCurrentThread(t); + } +} + +// For use by only those functions that allocated the context via +// alloc_asan_context(). +extern "C" +void asan_dispatch_call_block_and_release(void *block) { + GET_STACK_TRACE_THREAD; + asan_block_context_t *context = (asan_block_context_t*)block; + VReport(2, + "asan_dispatch_call_block_and_release(): " + "context: %p, pthread_self: %p\n", + block, pthread_self()); + asan_register_worker_thread(context->parent_tid, &stack); + // Call the original dispatcher for the block. + context->func(context->block); + asan_free(context, &stack, FROM_MALLOC); +} + +} // namespace __asan + +using namespace __asan; // NOLINT + +// Wrap |ctxt| and |func| into an asan_block_context_t. +// The caller retains control of the allocated context. +extern "C" +asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, + BufferedStackTrace *stack) { + asan_block_context_t *asan_ctxt = + (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); + asan_ctxt->block = ctxt; + asan_ctxt->func = func; + asan_ctxt->parent_tid = GetCurrentTidOrInvalid(); + return asan_ctxt; +} + +// Define interceptor for dispatch_*_f function with the three most common +// parameters: dispatch_queue_t, context, dispatch_function_t. +#define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \ + INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \ + dispatch_function_t func) { \ + GET_STACK_TRACE_THREAD; \ + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ + if (Verbosity() >= 2) { \ + Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ + asan_ctxt, pthread_self()); \ + PRINT_CURRENT_STACK(); \ + } \ + return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \ + asan_dispatch_call_block_and_release); \ + } + +INTERCEPT_DISPATCH_X_F_3(dispatch_async_f) +INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f) +INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f) + +INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, + dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { + GET_STACK_TRACE_THREAD; + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); + if (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_group_async_f, dispatch_group_t group, + dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { + GET_STACK_TRACE_THREAD; + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); + if (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); +} + +#if !defined(MISSING_BLOCKS_SUPPORT) +extern "C" { +void dispatch_async(dispatch_queue_t dq, void(^work)(void)); +void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, + void(^work)(void)); +void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, + void(^work)(void)); +void dispatch_source_set_cancel_handler(dispatch_source_t ds, + void(^work)(void)); +void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); +} + +#define GET_ASAN_BLOCK(work) \ + void (^asan_block)(void); \ + int parent_tid = GetCurrentTidOrInvalid(); \ + asan_block = ^(void) { \ + GET_STACK_TRACE_THREAD; \ + asan_register_worker_thread(parent_tid, &stack); \ + work(); \ + } + +INTERCEPTOR(void, dispatch_async, + dispatch_queue_t dq, void(^work)(void)) { + ENABLE_FRAME_POINTER; + GET_ASAN_BLOCK(work); + REAL(dispatch_async)(dq, asan_block); +} + +INTERCEPTOR(void, dispatch_group_async, + dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) { + ENABLE_FRAME_POINTER; + GET_ASAN_BLOCK(work); + REAL(dispatch_group_async)(dg, dq, asan_block); +} + +INTERCEPTOR(void, dispatch_after, + dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) { + ENABLE_FRAME_POINTER; + GET_ASAN_BLOCK(work); + REAL(dispatch_after)(when, queue, asan_block); +} + +INTERCEPTOR(void, dispatch_source_set_cancel_handler, + dispatch_source_t ds, void(^work)(void)) { + if (!work) { + REAL(dispatch_source_set_cancel_handler)(ds, work); + return; + } + ENABLE_FRAME_POINTER; + GET_ASAN_BLOCK(work); + REAL(dispatch_source_set_cancel_handler)(ds, asan_block); +} + +INTERCEPTOR(void, dispatch_source_set_event_handler, + dispatch_source_t ds, void(^work)(void)) { + ENABLE_FRAME_POINTER; + GET_ASAN_BLOCK(work); + REAL(dispatch_source_set_event_handler)(ds, asan_block); +} +#endif + +#endif // SANITIZER_MAC diff --git a/libsanitizer/asan/asan_malloc_linux.cc b/libsanitizer/asan/asan_malloc_linux.cc deleted file mode 100644 index a6e692759ce..00000000000 --- a/libsanitizer/asan/asan_malloc_linux.cc +++ /dev/null @@ -1,298 +0,0 @@ -//===-- asan_malloc_linux.cc ----------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Linux-specific malloc interception. -// We simply define functions like malloc, free, realloc, etc. -// They will replace the corresponding libc functions automagically. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || \ - SANITIZER_NETBSD || SANITIZER_RTEMS || SANITIZER_SOLARIS - -#include "sanitizer_common/sanitizer_allocator_checks.h" -#include "sanitizer_common/sanitizer_errno.h" -#include "sanitizer_common/sanitizer_tls_get_addr.h" -#include "asan_allocator.h" -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_malloc_local.h" -#include "asan_stack.h" - -// ---------------------- Replacement functions ---------------- {{{1 -using namespace __asan; // NOLINT - -static uptr allocated_for_dlsym; -static uptr last_dlsym_alloc_size_in_words; -static const uptr kDlsymAllocPoolSize = SANITIZER_RTEMS ? 4096 : 1024; -static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize]; - -static INLINE bool IsInDlsymAllocPool(const void *ptr) { - uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym; - return off < allocated_for_dlsym * sizeof(alloc_memory_for_dlsym[0]); -} - -static void *AllocateFromLocalPool(uptr size_in_bytes) { - uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize; - void *mem = (void*)&alloc_memory_for_dlsym[allocated_for_dlsym]; - last_dlsym_alloc_size_in_words = size_in_words; - allocated_for_dlsym += size_in_words; - CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize); - return mem; -} - -static void DeallocateFromLocalPool(const void *ptr) { - // Hack: since glibc 2.27 dlsym no longer uses stack-allocated memory to store - // error messages and instead uses malloc followed by free. To avoid pool - // exhaustion due to long object filenames, handle that special case here. - uptr prev_offset = allocated_for_dlsym - last_dlsym_alloc_size_in_words; - void *prev_mem = (void*)&alloc_memory_for_dlsym[prev_offset]; - if (prev_mem == ptr) { - REAL(memset)(prev_mem, 0, last_dlsym_alloc_size_in_words * kWordSize); - allocated_for_dlsym = prev_offset; - last_dlsym_alloc_size_in_words = 0; - } -} - -static int PosixMemalignFromLocalPool(void **memptr, uptr alignment, - uptr size_in_bytes) { - if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) - return errno_EINVAL; - - CHECK(alignment >= kWordSize); - - uptr addr = (uptr)&alloc_memory_for_dlsym[allocated_for_dlsym]; - uptr aligned_addr = RoundUpTo(addr, alignment); - uptr aligned_size = RoundUpTo(size_in_bytes, kWordSize); - - uptr *end_mem = (uptr*)(aligned_addr + aligned_size); - uptr allocated = end_mem - alloc_memory_for_dlsym; - if (allocated >= kDlsymAllocPoolSize) - return errno_ENOMEM; - - allocated_for_dlsym = allocated; - *memptr = (void*)aligned_addr; - return 0; -} - -#if SANITIZER_RTEMS -void* MemalignFromLocalPool(uptr alignment, uptr size) { - void *ptr = nullptr; - alignment = Max(alignment, kWordSize); - PosixMemalignFromLocalPool(&ptr, alignment, size); - return ptr; -} - -bool IsFromLocalPool(const void *ptr) { - return IsInDlsymAllocPool(ptr); -} -#endif - -static INLINE bool MaybeInDlsym() { - // Fuchsia doesn't use dlsym-based interceptors. - return !SANITIZER_FUCHSIA && asan_init_is_running; -} - -static INLINE bool UseLocalPool() { - return EarlyMalloc() || MaybeInDlsym(); -} - -static void *ReallocFromLocalPool(void *ptr, uptr size) { - const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym; - const uptr copy_size = Min(size, kDlsymAllocPoolSize - offset); - void *new_ptr; - if (UNLIKELY(UseLocalPool())) { - new_ptr = AllocateFromLocalPool(size); - } else { - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - new_ptr = asan_malloc(size, &stack); - } - internal_memcpy(new_ptr, ptr, copy_size); - return new_ptr; -} - -INTERCEPTOR(void, free, void *ptr) { - GET_STACK_TRACE_FREE; - if (UNLIKELY(IsInDlsymAllocPool(ptr))) { - DeallocateFromLocalPool(ptr); - return; - } - asan_free(ptr, &stack, FROM_MALLOC); -} - -#if SANITIZER_INTERCEPT_CFREE -INTERCEPTOR(void, cfree, void *ptr) { - GET_STACK_TRACE_FREE; - if (UNLIKELY(IsInDlsymAllocPool(ptr))) - return; - asan_free(ptr, &stack, FROM_MALLOC); -} -#endif // SANITIZER_INTERCEPT_CFREE - -INTERCEPTOR(void*, malloc, uptr size) { - if (UNLIKELY(UseLocalPool())) - // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym. - return AllocateFromLocalPool(size); - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - return asan_malloc(size, &stack); -} - -INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { - if (UNLIKELY(UseLocalPool())) - // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. - return AllocateFromLocalPool(nmemb * size); - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - return asan_calloc(nmemb, size, &stack); -} - -INTERCEPTOR(void*, realloc, void *ptr, uptr size) { - if (UNLIKELY(IsInDlsymAllocPool(ptr))) - return ReallocFromLocalPool(ptr, size); - if (UNLIKELY(UseLocalPool())) - return AllocateFromLocalPool(size); - ENSURE_ASAN_INITED(); - GET_STACK_TRACE_MALLOC; - return asan_realloc(ptr, size, &stack); -} - -#if SANITIZER_INTERCEPT_MEMALIGN -INTERCEPTOR(void*, memalign, uptr boundary, uptr size) { - GET_STACK_TRACE_MALLOC; - return asan_memalign(boundary, size, &stack, FROM_MALLOC); -} - -INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) { - GET_STACK_TRACE_MALLOC; - void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC); - DTLS_on_libc_memalign(res, size); - return res; -} -#endif // SANITIZER_INTERCEPT_MEMALIGN - -#if SANITIZER_INTERCEPT_ALIGNED_ALLOC -INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) { - GET_STACK_TRACE_MALLOC; - return asan_aligned_alloc(boundary, size, &stack); -} -#endif // SANITIZER_INTERCEPT_ALIGNED_ALLOC - -INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { - GET_CURRENT_PC_BP_SP; - (void)sp; - return asan_malloc_usable_size(ptr, pc, bp); -} - -#if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO -// 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(int, mallopt, int cmd, int value) { - return -1; -} -#endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO - -INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { - if (UNLIKELY(UseLocalPool())) - return PosixMemalignFromLocalPool(memptr, alignment, size); - GET_STACK_TRACE_MALLOC; - return asan_posix_memalign(memptr, alignment, size, &stack); -} - -INTERCEPTOR(void*, valloc, uptr size) { - GET_STACK_TRACE_MALLOC; - return asan_valloc(size, &stack); -} - -#if SANITIZER_INTERCEPT_PVALLOC -INTERCEPTOR(void*, pvalloc, uptr size) { - GET_STACK_TRACE_MALLOC; - return asan_pvalloc(size, &stack); -} -#endif // SANITIZER_INTERCEPT_PVALLOC - -INTERCEPTOR(void, malloc_stats, void) { - __asan_print_accumulated_stats(); -} - -#if SANITIZER_ANDROID -// Format of __libc_malloc_dispatch has changed in Android L. -// While we are moving towards a solution that does not depend on bionic -// internals, here is something to support both K* and L releases. -struct MallocDebugK { - void *(*malloc)(uptr bytes); - void (*free)(void *mem); - void *(*calloc)(uptr n_elements, uptr elem_size); - void *(*realloc)(void *oldMem, uptr bytes); - void *(*memalign)(uptr alignment, uptr bytes); - uptr (*malloc_usable_size)(void *mem); -}; - -struct MallocDebugL { - void *(*calloc)(uptr n_elements, uptr elem_size); - void (*free)(void *mem); - fake_mallinfo (*mallinfo)(void); - void *(*malloc)(uptr bytes); - uptr (*malloc_usable_size)(void *mem); - void *(*memalign)(uptr alignment, uptr bytes); - int (*posix_memalign)(void **memptr, uptr alignment, uptr size); - void* (*pvalloc)(uptr size); - void *(*realloc)(void *oldMem, uptr bytes); - void* (*valloc)(uptr size); -}; - -ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = { - WRAP(malloc), WRAP(free), WRAP(calloc), - WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)}; - -ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = { - WRAP(calloc), WRAP(free), WRAP(mallinfo), - WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign), - WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc), - WRAP(valloc)}; - -namespace __asan { -void ReplaceSystemMalloc() { - void **__libc_malloc_dispatch_p = - (void **)AsanDlSymNext("__libc_malloc_dispatch"); - if (__libc_malloc_dispatch_p) { - // Decide on K vs L dispatch format by the presence of - // __libc_malloc_default_dispatch export in libc. - void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch"); - if (default_dispatch_p) - *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k; - else - *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l; - } -} -} // namespace __asan - -#else // SANITIZER_ANDROID - -namespace __asan { -void ReplaceSystemMalloc() { -} -} // namespace __asan -#endif // SANITIZER_ANDROID - -#endif // SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || - // SANITIZER_NETBSD || SANITIZER_SOLARIS diff --git a/libsanitizer/asan/asan_malloc_linux.cpp b/libsanitizer/asan/asan_malloc_linux.cpp new file mode 100644 index 00000000000..706bc39f0c4 --- /dev/null +++ b/libsanitizer/asan/asan_malloc_linux.cpp @@ -0,0 +1,307 @@ +//===-- asan_malloc_linux.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Linux-specific malloc interception. +// We simply define functions like malloc, free, realloc, etc. +// They will replace the corresponding libc functions automagically. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || \ + SANITIZER_NETBSD || SANITIZER_RTEMS || SANITIZER_SOLARIS + +#include "sanitizer_common/sanitizer_allocator_checks.h" +#include "sanitizer_common/sanitizer_errno.h" +#include "sanitizer_common/sanitizer_tls_get_addr.h" +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_malloc_local.h" +#include "asan_stack.h" + +// ---------------------- Replacement functions ---------------- {{{1 +using namespace __asan; // NOLINT + +static uptr allocated_for_dlsym; +static uptr last_dlsym_alloc_size_in_words; +static const uptr kDlsymAllocPoolSize = SANITIZER_RTEMS ? 4096 : 1024; +static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize]; + +static INLINE bool IsInDlsymAllocPool(const void *ptr) { + uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym; + return off < allocated_for_dlsym * sizeof(alloc_memory_for_dlsym[0]); +} + +static void *AllocateFromLocalPool(uptr size_in_bytes) { + uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize; + void *mem = (void*)&alloc_memory_for_dlsym[allocated_for_dlsym]; + last_dlsym_alloc_size_in_words = size_in_words; + allocated_for_dlsym += size_in_words; + CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize); + return mem; +} + +static void DeallocateFromLocalPool(const void *ptr) { + // Hack: since glibc 2.27 dlsym no longer uses stack-allocated memory to store + // error messages and instead uses malloc followed by free. To avoid pool + // exhaustion due to long object filenames, handle that special case here. + uptr prev_offset = allocated_for_dlsym - last_dlsym_alloc_size_in_words; + void *prev_mem = (void*)&alloc_memory_for_dlsym[prev_offset]; + if (prev_mem == ptr) { + REAL(memset)(prev_mem, 0, last_dlsym_alloc_size_in_words * kWordSize); + allocated_for_dlsym = prev_offset; + last_dlsym_alloc_size_in_words = 0; + } +} + +static int PosixMemalignFromLocalPool(void **memptr, uptr alignment, + uptr size_in_bytes) { + if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) + return errno_EINVAL; + + CHECK(alignment >= kWordSize); + + uptr addr = (uptr)&alloc_memory_for_dlsym[allocated_for_dlsym]; + uptr aligned_addr = RoundUpTo(addr, alignment); + uptr aligned_size = RoundUpTo(size_in_bytes, kWordSize); + + uptr *end_mem = (uptr*)(aligned_addr + aligned_size); + uptr allocated = end_mem - alloc_memory_for_dlsym; + if (allocated >= kDlsymAllocPoolSize) + return errno_ENOMEM; + + allocated_for_dlsym = allocated; + *memptr = (void*)aligned_addr; + return 0; +} + +#if SANITIZER_RTEMS +void* MemalignFromLocalPool(uptr alignment, uptr size) { + void *ptr = nullptr; + alignment = Max(alignment, kWordSize); + PosixMemalignFromLocalPool(&ptr, alignment, size); + return ptr; +} + +bool IsFromLocalPool(const void *ptr) { + return IsInDlsymAllocPool(ptr); +} +#endif + +static INLINE bool MaybeInDlsym() { + // Fuchsia doesn't use dlsym-based interceptors. + return !SANITIZER_FUCHSIA && asan_init_is_running; +} + +static INLINE bool UseLocalPool() { + return EarlyMalloc() || MaybeInDlsym(); +} + +static void *ReallocFromLocalPool(void *ptr, uptr size) { + const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym; + const uptr copy_size = Min(size, kDlsymAllocPoolSize - offset); + void *new_ptr; + if (UNLIKELY(UseLocalPool())) { + new_ptr = AllocateFromLocalPool(size); + } else { + ENSURE_ASAN_INITED(); + GET_STACK_TRACE_MALLOC; + new_ptr = asan_malloc(size, &stack); + } + internal_memcpy(new_ptr, ptr, copy_size); + return new_ptr; +} + +INTERCEPTOR(void, free, void *ptr) { + GET_STACK_TRACE_FREE; + if (UNLIKELY(IsInDlsymAllocPool(ptr))) { + DeallocateFromLocalPool(ptr); + return; + } + asan_free(ptr, &stack, FROM_MALLOC); +} + +#if SANITIZER_INTERCEPT_CFREE +INTERCEPTOR(void, cfree, void *ptr) { + GET_STACK_TRACE_FREE; + if (UNLIKELY(IsInDlsymAllocPool(ptr))) + return; + asan_free(ptr, &stack, FROM_MALLOC); +} +#endif // SANITIZER_INTERCEPT_CFREE + +INTERCEPTOR(void*, malloc, uptr size) { + if (UNLIKELY(UseLocalPool())) + // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym. + return AllocateFromLocalPool(size); + ENSURE_ASAN_INITED(); + GET_STACK_TRACE_MALLOC; + return asan_malloc(size, &stack); +} + +INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { + if (UNLIKELY(UseLocalPool())) + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + return AllocateFromLocalPool(nmemb * size); + ENSURE_ASAN_INITED(); + GET_STACK_TRACE_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +INTERCEPTOR(void*, realloc, void *ptr, uptr size) { + if (UNLIKELY(IsInDlsymAllocPool(ptr))) + return ReallocFromLocalPool(ptr, size); + if (UNLIKELY(UseLocalPool())) + return AllocateFromLocalPool(size); + ENSURE_ASAN_INITED(); + GET_STACK_TRACE_MALLOC; + return asan_realloc(ptr, size, &stack); +} + +#if SANITIZER_INTERCEPT_REALLOCARRAY +INTERCEPTOR(void*, reallocarray, void *ptr, uptr nmemb, uptr size) { + ENSURE_ASAN_INITED(); + GET_STACK_TRACE_MALLOC; + return asan_reallocarray(ptr, nmemb, size, &stack); +} +#endif // SANITIZER_INTERCEPT_REALLOCARRAY + +#if SANITIZER_INTERCEPT_MEMALIGN +INTERCEPTOR(void*, memalign, uptr boundary, uptr size) { + GET_STACK_TRACE_MALLOC; + return asan_memalign(boundary, size, &stack, FROM_MALLOC); +} + +INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) { + GET_STACK_TRACE_MALLOC; + void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC); + DTLS_on_libc_memalign(res, size); + return res; +} +#endif // SANITIZER_INTERCEPT_MEMALIGN + +#if SANITIZER_INTERCEPT_ALIGNED_ALLOC +INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) { + GET_STACK_TRACE_MALLOC; + return asan_aligned_alloc(boundary, size, &stack); +} +#endif // SANITIZER_INTERCEPT_ALIGNED_ALLOC + +INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(ptr, pc, bp); +} + +#if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO +// 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(int, mallopt, int cmd, int value) { + return 0; +} +#endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO + +INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { + if (UNLIKELY(UseLocalPool())) + return PosixMemalignFromLocalPool(memptr, alignment, size); + GET_STACK_TRACE_MALLOC; + return asan_posix_memalign(memptr, alignment, size, &stack); +} + +INTERCEPTOR(void*, valloc, uptr size) { + GET_STACK_TRACE_MALLOC; + return asan_valloc(size, &stack); +} + +#if SANITIZER_INTERCEPT_PVALLOC +INTERCEPTOR(void*, pvalloc, uptr size) { + GET_STACK_TRACE_MALLOC; + return asan_pvalloc(size, &stack); +} +#endif // SANITIZER_INTERCEPT_PVALLOC + +INTERCEPTOR(void, malloc_stats, void) { + __asan_print_accumulated_stats(); +} + +#if SANITIZER_ANDROID +// Format of __libc_malloc_dispatch has changed in Android L. +// While we are moving towards a solution that does not depend on bionic +// internals, here is something to support both K* and L releases. +struct MallocDebugK { + void *(*malloc)(uptr bytes); + void (*free)(void *mem); + void *(*calloc)(uptr n_elements, uptr elem_size); + void *(*realloc)(void *oldMem, uptr bytes); + void *(*memalign)(uptr alignment, uptr bytes); + uptr (*malloc_usable_size)(void *mem); +}; + +struct MallocDebugL { + void *(*calloc)(uptr n_elements, uptr elem_size); + void (*free)(void *mem); + fake_mallinfo (*mallinfo)(void); + void *(*malloc)(uptr bytes); + uptr (*malloc_usable_size)(void *mem); + void *(*memalign)(uptr alignment, uptr bytes); + int (*posix_memalign)(void **memptr, uptr alignment, uptr size); + void* (*pvalloc)(uptr size); + void *(*realloc)(void *oldMem, uptr bytes); + void* (*valloc)(uptr size); +}; + +ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = { + WRAP(malloc), WRAP(free), WRAP(calloc), + WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)}; + +ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = { + WRAP(calloc), WRAP(free), WRAP(mallinfo), + WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign), + WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc), + WRAP(valloc)}; + +namespace __asan { +void ReplaceSystemMalloc() { + void **__libc_malloc_dispatch_p = + (void **)AsanDlSymNext("__libc_malloc_dispatch"); + if (__libc_malloc_dispatch_p) { + // Decide on K vs L dispatch format by the presence of + // __libc_malloc_default_dispatch export in libc. + void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch"); + if (default_dispatch_p) + *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k; + else + *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l; + } +} +} // namespace __asan + +#else // SANITIZER_ANDROID + +namespace __asan { +void ReplaceSystemMalloc() { +} +} // namespace __asan +#endif // SANITIZER_ANDROID + +#endif // SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || + // SANITIZER_NETBSD || SANITIZER_SOLARIS diff --git a/libsanitizer/asan/asan_malloc_local.h b/libsanitizer/asan/asan_malloc_local.h index 354189315fb..3f784b90c73 100644 --- a/libsanitizer/asan/asan_malloc_local.h +++ b/libsanitizer/asan/asan_malloc_local.h @@ -1,7 +1,8 @@ //===-- asan_malloc_local.h -------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -16,25 +17,34 @@ #include "sanitizer_common/sanitizer_platform.h" #include "asan_internal.h" -// On RTEMS, we use the local pool to handle memory allocation when the ASan -// run-time is not up. static INLINE bool EarlyMalloc() { - return SANITIZER_RTEMS && (!__asan::asan_inited || - __asan::asan_init_is_running); + return SANITIZER_RTEMS && + (!__asan::asan_inited || __asan::asan_init_is_running); } -void* MemalignFromLocalPool(uptr alignment, uptr size); - #if SANITIZER_RTEMS bool IsFromLocalPool(const void *ptr); +void *MemalignFromLocalPool(uptr alignment, uptr size); + +// On RTEMS, we use the local pool to handle memory allocation when the ASan +// run-time is not up. This macro is expanded in the context of the operator new +// implementation. +#define MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow) \ + do { \ + if (UNLIKELY(EarlyMalloc())) { \ + void *res = MemalignFromLocalPool(SHADOW_GRANULARITY, size); \ + if (!nothrow) \ + CHECK(res); \ + return res; \ + } \ + } while (0) -#define ALLOCATE_FROM_LOCAL_POOL UNLIKELY(EarlyMalloc()) #define IS_FROM_LOCAL_POOL(ptr) UNLIKELY(IsFromLocalPool(ptr)) #else // SANITIZER_RTEMS -#define ALLOCATE_FROM_LOCAL_POOL 0 +#define MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow) #define IS_FROM_LOCAL_POOL(ptr) 0 #endif // SANITIZER_RTEMS diff --git a/libsanitizer/asan/asan_malloc_mac.cc b/libsanitizer/asan/asan_malloc_mac.cc deleted file mode 100644 index e34884be85b..00000000000 --- a/libsanitizer/asan/asan_malloc_mac.cc +++ /dev/null @@ -1,62 +0,0 @@ -//===-- asan_malloc_mac.cc ------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Mac-specific malloc interception. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_MAC - -#include "asan_interceptors.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "asan_stats.h" - -using namespace __asan; -#define COMMON_MALLOC_ZONE_NAME "asan" -#define COMMON_MALLOC_ENTER() ENSURE_ASAN_INITED() -#define COMMON_MALLOC_SANITIZER_INITIALIZED asan_inited -#define COMMON_MALLOC_FORCE_LOCK() asan_mz_force_lock() -#define COMMON_MALLOC_FORCE_UNLOCK() asan_mz_force_unlock() -#define COMMON_MALLOC_MEMALIGN(alignment, size) \ - GET_STACK_TRACE_MALLOC; \ - void *p = asan_memalign(alignment, size, &stack, FROM_MALLOC) -#define COMMON_MALLOC_MALLOC(size) \ - GET_STACK_TRACE_MALLOC; \ - void *p = asan_malloc(size, &stack) -#define COMMON_MALLOC_REALLOC(ptr, size) \ - GET_STACK_TRACE_MALLOC; \ - void *p = asan_realloc(ptr, size, &stack); -#define COMMON_MALLOC_CALLOC(count, size) \ - GET_STACK_TRACE_MALLOC; \ - void *p = asan_calloc(count, size, &stack); -#define COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size) \ - GET_STACK_TRACE_MALLOC; \ - int res = asan_posix_memalign(memptr, alignment, size, &stack); -#define COMMON_MALLOC_VALLOC(size) \ - GET_STACK_TRACE_MALLOC; \ - void *p = asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); -#define COMMON_MALLOC_FREE(ptr) \ - GET_STACK_TRACE_FREE; \ - asan_free(ptr, &stack, FROM_MALLOC); -#define COMMON_MALLOC_SIZE(ptr) \ - uptr size = asan_mz_size(ptr); -#define COMMON_MALLOC_FILL_STATS(zone, stats) \ - AsanMallocStats malloc_stats; \ - FillMallocStatistics(&malloc_stats); \ - CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); \ - internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); -#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \ - GET_STACK_TRACE_FREE; \ - ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); -#define COMMON_MALLOC_NAMESPACE __asan - -#include "sanitizer_common/sanitizer_malloc_mac.inc" - -#endif diff --git a/libsanitizer/asan/asan_malloc_mac.cpp b/libsanitizer/asan/asan_malloc_mac.cpp new file mode 100644 index 00000000000..e8484685dae --- /dev/null +++ b/libsanitizer/asan/asan_malloc_mac.cpp @@ -0,0 +1,102 @@ +//===-- asan_malloc_mac.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Mac-specific malloc interception. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC + +#include "asan_interceptors.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "lsan/lsan_common.h" + +using namespace __asan; +#define COMMON_MALLOC_ZONE_NAME "asan" +#define COMMON_MALLOC_ENTER() ENSURE_ASAN_INITED() +#define COMMON_MALLOC_SANITIZER_INITIALIZED asan_inited +#define COMMON_MALLOC_FORCE_LOCK() asan_mz_force_lock() +#define COMMON_MALLOC_FORCE_UNLOCK() asan_mz_force_unlock() +#define COMMON_MALLOC_MEMALIGN(alignment, size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_memalign(alignment, size, &stack, FROM_MALLOC) +#define COMMON_MALLOC_MALLOC(size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_malloc(size, &stack) +#define COMMON_MALLOC_REALLOC(ptr, size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_realloc(ptr, size, &stack); +#define COMMON_MALLOC_CALLOC(count, size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_calloc(count, size, &stack); +#define COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size) \ + GET_STACK_TRACE_MALLOC; \ + int res = asan_posix_memalign(memptr, alignment, size, &stack); +#define COMMON_MALLOC_VALLOC(size) \ + GET_STACK_TRACE_MALLOC; \ + void *p = asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); +#define COMMON_MALLOC_FREE(ptr) \ + GET_STACK_TRACE_FREE; \ + asan_free(ptr, &stack, FROM_MALLOC); +#define COMMON_MALLOC_SIZE(ptr) \ + uptr size = asan_mz_size(ptr); +#define COMMON_MALLOC_FILL_STATS(zone, stats) \ + AsanMallocStats malloc_stats; \ + FillMallocStatistics(&malloc_stats); \ + CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); \ + internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); +#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \ + GET_STACK_TRACE_FREE; \ + ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); +#define COMMON_MALLOC_NAMESPACE __asan +#define COMMON_MALLOC_HAS_ZONE_ENUMERATOR 0 +#define COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT 1 + +#include "sanitizer_common/sanitizer_malloc_mac.inc" + +namespace COMMON_MALLOC_NAMESPACE { + +bool HandleDlopenInit() { + static_assert(SANITIZER_SUPPORTS_INIT_FOR_DLOPEN, + "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be true"); + // We have no reliable way of knowing how we are being loaded + // so make it a requirement on Apple platforms to set this environment + // variable to indicate that we want to perform initialization via + // dlopen(). + auto init_str = GetEnv("APPLE_ASAN_INIT_FOR_DLOPEN"); + if (!init_str) + return false; + if (internal_strncmp(init_str, "1", 1) != 0) + return false; + // When we are loaded via `dlopen()` path we still initialize the malloc zone + // so Symbolication clients (e.g. `leaks`) that load the ASan allocator can + // find an initialized malloc zone. + InitMallocZoneFields(); + return true; +} +} // namespace COMMON_MALLOC_NAMESPACE + +namespace { + +void mi_extra_init(sanitizer_malloc_introspection_t *mi) { + uptr last_byte_plus_one = 0; + mi->allocator_ptr = 0; + // Range is [begin_ptr, end_ptr) + __lsan::GetAllocatorGlobalRange(&(mi->allocator_ptr), &last_byte_plus_one); + CHECK_NE(mi->allocator_ptr, 0); + CHECK_GT(last_byte_plus_one, mi->allocator_ptr); + mi->allocator_size = last_byte_plus_one - (mi->allocator_ptr); + CHECK_GT(mi->allocator_size, 0); +} +} // namespace + +#endif diff --git a/libsanitizer/asan/asan_malloc_win.cc b/libsanitizer/asan/asan_malloc_win.cc deleted file mode 100644 index 24518603792..00000000000 --- a/libsanitizer/asan/asan_malloc_win.cc +++ /dev/null @@ -1,259 +0,0 @@ -//===-- asan_malloc_win.cc ------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Windows-specific malloc interception. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_WINDOWS -// Intentionally not including windows.h here, to avoid the risk of -// pulling in conflicting declarations of these functions. (With mingw-w64, -// there's a risk of windows.h pulling in stdint.h.) -typedef int BOOL; -typedef void *HANDLE; -typedef const void *LPCVOID; -typedef void *LPVOID; - -#define HEAP_ZERO_MEMORY 0x00000008 -#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010 - - -#include "asan_allocator.h" -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_stack.h" -#include "interception/interception.h" - -#include - -using namespace __asan; // NOLINT - -// MT: Simply defining functions with the same signature in *.obj -// files overrides the standard functions in the CRT. -// MD: Memory allocation functions are defined in the CRT .dll, -// so we have to intercept them before they are called for the first time. - -#if ASAN_DYNAMIC -# define ALLOCATION_FUNCTION_ATTRIBUTE -#else -# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE -#endif - -extern "C" { -ALLOCATION_FUNCTION_ATTRIBUTE -void free(void *ptr) { - GET_STACK_TRACE_FREE; - return asan_free(ptr, &stack, FROM_MALLOC); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void _free_dbg(void *ptr, int) { - free(ptr); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void _free_base(void *ptr) { - free(ptr); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *malloc(size_t size) { - GET_STACK_TRACE_MALLOC; - return asan_malloc(size, &stack); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_malloc_base(size_t size) { - return malloc(size); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_malloc_dbg(size_t size, int, const char *, int) { - return malloc(size); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *calloc(size_t nmemb, size_t size) { - GET_STACK_TRACE_MALLOC; - return asan_calloc(nmemb, size, &stack); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_calloc_base(size_t nmemb, size_t size) { - return calloc(nmemb, size); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) { - return calloc(nmemb, size); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { - return calloc(nmemb, size); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *realloc(void *ptr, size_t size) { - GET_STACK_TRACE_MALLOC; - return asan_realloc(ptr, size, &stack); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_realloc_dbg(void *ptr, size_t size, int) { - UNREACHABLE("_realloc_dbg should not exist!"); - return 0; -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_realloc_base(void *ptr, size_t size) { - return realloc(ptr, size); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_recalloc(void *p, size_t n, size_t elem_size) { - if (!p) - return calloc(n, elem_size); - const size_t size = n * elem_size; - if (elem_size != 0 && size / elem_size != n) - return 0; - return realloc(p, size); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_recalloc_base(void *p, size_t n, size_t elem_size) { - return _recalloc(p, n, elem_size); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -size_t _msize(void *ptr) { - GET_CURRENT_PC_BP_SP; - (void)sp; - return asan_malloc_usable_size(ptr, pc, bp); -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_expand(void *memblock, size_t size) { - // _expand is used in realloc-like functions to resize the buffer if possible. - // We don't want memory to stand still while resizing buffers, so return 0. - return 0; -} - -ALLOCATION_FUNCTION_ATTRIBUTE -void *_expand_dbg(void *memblock, size_t size) { - return _expand(memblock, size); -} - -// TODO(timurrrr): Might want to add support for _aligned_* allocation -// functions to detect a bit more bugs. Those functions seem to wrap malloc(). - -int _CrtDbgReport(int, const char*, int, - const char*, const char*, ...) { - ShowStatsAndAbort(); -} - -int _CrtDbgReportW(int reportType, const wchar_t*, int, - const wchar_t*, const wchar_t*, ...) { - ShowStatsAndAbort(); -} - -int _CrtSetReportMode(int, int) { - return 0; -} -} // extern "C" - -INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags, - SIZE_T dwBytes) { - GET_STACK_TRACE_MALLOC; - void *p = asan_malloc(dwBytes, &stack); - // Reading MSDN suggests that the *entire* usable allocation is zeroed out. - // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. - // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 - if (dwFlags == HEAP_ZERO_MEMORY) - internal_memset(p, 0, asan_mz_size(p)); - else - CHECK(dwFlags == 0 && "unsupported heap flags"); - return p; -} - -INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { - CHECK(dwFlags == 0 && "unsupported heap flags"); - GET_STACK_TRACE_FREE; - asan_free(lpMem, &stack, FROM_MALLOC); - return true; -} - -INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, - LPVOID lpMem, SIZE_T dwBytes) { - GET_STACK_TRACE_MALLOC; - // Realloc should never reallocate in place. - if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) - return nullptr; - CHECK(dwFlags == 0 && "unsupported heap flags"); - return asan_realloc(lpMem, dwBytes, &stack); -} - -INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags, - LPCVOID lpMem) { - CHECK(dwFlags == 0 && "unsupported heap flags"); - GET_CURRENT_PC_BP_SP; - (void)sp; - return asan_malloc_usable_size(lpMem, pc, bp); -} - -namespace __asan { - -static void TryToOverrideFunction(const char *fname, uptr new_func) { - // Failure here is not fatal. The CRT may not be present, and different CRT - // versions use different symbols. - if (!__interception::OverrideFunction(fname, new_func)) - VPrintf(2, "Failed to override function %s\n", fname); -} - -void ReplaceSystemMalloc() { -#if defined(ASAN_DYNAMIC) - TryToOverrideFunction("free", (uptr)free); - TryToOverrideFunction("_free_base", (uptr)free); - TryToOverrideFunction("malloc", (uptr)malloc); - TryToOverrideFunction("_malloc_base", (uptr)malloc); - TryToOverrideFunction("_malloc_crt", (uptr)malloc); - TryToOverrideFunction("calloc", (uptr)calloc); - TryToOverrideFunction("_calloc_base", (uptr)calloc); - TryToOverrideFunction("_calloc_crt", (uptr)calloc); - TryToOverrideFunction("realloc", (uptr)realloc); - TryToOverrideFunction("_realloc_base", (uptr)realloc); - TryToOverrideFunction("_realloc_crt", (uptr)realloc); - TryToOverrideFunction("_recalloc", (uptr)_recalloc); - TryToOverrideFunction("_recalloc_base", (uptr)_recalloc); - TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc); - TryToOverrideFunction("_msize", (uptr)_msize); - TryToOverrideFunction("_expand", (uptr)_expand); - TryToOverrideFunction("_expand_base", (uptr)_expand); - - // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which - // enable cross-module inlining. This means our _malloc_base hook won't catch - // all CRT allocations. This code here patches the import table of - // ucrtbase.dll so that all attempts to use the lower-level win32 heap - // allocation API will be directed to ASan's heap. We don't currently - // intercept all calls to HeapAlloc. If we did, we would have to check on - // HeapFree whether the pointer came from ASan of from the system. -#define INTERCEPT_UCRT_FUNCTION(func) \ - if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \ - "api-ms-win-core-heap-l1-1-0.dll", func)) \ - VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); - INTERCEPT_UCRT_FUNCTION(HeapAlloc); - INTERCEPT_UCRT_FUNCTION(HeapFree); - INTERCEPT_UCRT_FUNCTION(HeapReAlloc); - INTERCEPT_UCRT_FUNCTION(HeapSize); -#undef INTERCEPT_UCRT_FUNCTION -#endif -} -} // namespace __asan - -#endif // _WIN32 diff --git a/libsanitizer/asan/asan_malloc_win.cpp b/libsanitizer/asan/asan_malloc_win.cpp new file mode 100644 index 00000000000..291d411ea79 --- /dev/null +++ b/libsanitizer/asan/asan_malloc_win.cpp @@ -0,0 +1,553 @@ +//===-- asan_malloc_win.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Windows-specific malloc interception. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_allocator_interface.h" +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_stack.h" +#include "interception/interception.h" +#include + +// Intentionally not including windows.h here, to avoid the risk of +// pulling in conflicting declarations of these functions. (With mingw-w64, +// there's a risk of windows.h pulling in stdint.h.) +typedef int BOOL; +typedef void *HANDLE; +typedef const void *LPCVOID; +typedef void *LPVOID; + +typedef unsigned long DWORD; +constexpr unsigned long HEAP_ZERO_MEMORY = 0x00000008; +constexpr unsigned long HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010; +constexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = (HEAP_ZERO_MEMORY); +constexpr unsigned long HEAP_ALLOCATE_UNSUPPORTED_FLAGS = + (~HEAP_ALLOCATE_SUPPORTED_FLAGS); +constexpr unsigned long HEAP_FREE_SUPPORTED_FLAGS = (0); +constexpr unsigned long HEAP_FREE_UNSUPPORTED_FLAGS = + (~HEAP_ALLOCATE_SUPPORTED_FLAGS); +constexpr unsigned long HEAP_REALLOC_SUPPORTED_FLAGS = + (HEAP_REALLOC_IN_PLACE_ONLY | HEAP_ZERO_MEMORY); +constexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS = + (~HEAP_ALLOCATE_SUPPORTED_FLAGS); + + +extern "C" { +LPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, size_t dwBytes); +LPVOID WINAPI HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, + size_t dwBytes); +BOOL WINAPI HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); +size_t WINAPI HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); + +BOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); +} + +using namespace __asan; // NOLINT + +// MT: Simply defining functions with the same signature in *.obj +// files overrides the standard functions in the CRT. +// MD: Memory allocation functions are defined in the CRT .dll, +// so we have to intercept them before they are called for the first time. + +#if ASAN_DYNAMIC +# define ALLOCATION_FUNCTION_ATTRIBUTE +#else +# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +#endif + +extern "C" { +ALLOCATION_FUNCTION_ATTRIBUTE +size_t _msize(void *ptr) { + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(ptr, pc, bp); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +size_t _msize_base(void *ptr) { + return _msize(ptr); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void free(void *ptr) { + GET_STACK_TRACE_FREE; + return asan_free(ptr, &stack, FROM_MALLOC); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void _free_dbg(void *ptr, int) { + free(ptr); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void _free_base(void *ptr) { + free(ptr); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *malloc(size_t size) { + GET_STACK_TRACE_MALLOC; + return asan_malloc(size, &stack); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_malloc_base(size_t size) { + return malloc(size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_malloc_dbg(size_t size, int, const char *, int) { + return malloc(size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *calloc(size_t nmemb, size_t size) { + GET_STACK_TRACE_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_calloc_base(size_t nmemb, size_t size) { + return calloc(nmemb, size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) { + return calloc(nmemb, size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { + return calloc(nmemb, size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *realloc(void *ptr, size_t size) { + GET_STACK_TRACE_MALLOC; + return asan_realloc(ptr, size, &stack); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_realloc_dbg(void *ptr, size_t size, int) { + UNREACHABLE("_realloc_dbg should not exist!"); + return 0; +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_realloc_base(void *ptr, size_t size) { + return realloc(ptr, size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_recalloc(void *p, size_t n, size_t elem_size) { + if (!p) + return calloc(n, elem_size); + const size_t size = n * elem_size; + if (elem_size != 0 && size / elem_size != n) + return 0; + + size_t old_size = _msize(p); + void *new_alloc = malloc(size); + if (new_alloc) { + REAL(memcpy)(new_alloc, p, Min(size, old_size)); + if (old_size < size) + REAL(memset)(((u8 *)new_alloc) + old_size, 0, size - old_size); + free(p); + } + return new_alloc; +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_recalloc_base(void *p, size_t n, size_t elem_size) { + return _recalloc(p, n, elem_size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_expand(void *memblock, size_t size) { + // _expand is used in realloc-like functions to resize the buffer if possible. + // We don't want memory to stand still while resizing buffers, so return 0. + return 0; +} + +ALLOCATION_FUNCTION_ATTRIBUTE +void *_expand_dbg(void *memblock, size_t size) { + return _expand(memblock, size); +} + +// TODO(timurrrr): Might want to add support for _aligned_* allocation +// functions to detect a bit more bugs. Those functions seem to wrap malloc(). + +int _CrtDbgReport(int, const char*, int, + const char*, const char*, ...) { + ShowStatsAndAbort(); +} + +int _CrtDbgReportW(int reportType, const wchar_t*, int, + const wchar_t*, const wchar_t*, ...) { + ShowStatsAndAbort(); +} + +int _CrtSetReportMode(int, int) { + return 0; +} +} // extern "C" + +#define OWNED_BY_RTL(heap, memory) \ + (!__sanitizer_get_ownership(memory) && HeapValidate(heap, 0, memory)) + +INTERCEPTOR_WINAPI(size_t, HeapSize, HANDLE hHeap, DWORD dwFlags, + LPCVOID lpMem) { + // If the RTL allocators are hooked we need to check whether the ASAN + // allocator owns the pointer we're about to use. Allocations occur before + // interception takes place, so if it is not owned by the RTL heap we can + // pass it to the ASAN heap for inspection. + if (flags()->windows_hook_rtl_allocators) { + if (!asan_inited || OWNED_BY_RTL(hHeap, lpMem)) + return REAL(HeapSize)(hHeap, dwFlags, lpMem); + } else { + CHECK(dwFlags == 0 && "unsupported heap flags"); + } + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(lpMem, pc, bp); +} + +INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags, + size_t dwBytes) { + // If the ASAN runtime is not initialized, or we encounter an unsupported + // flag, fall back to the original allocator. + if (flags()->windows_hook_rtl_allocators) { + if (UNLIKELY(!asan_inited || + (dwFlags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { + return REAL(HeapAlloc)(hHeap, dwFlags, dwBytes); + } + } else { + // In the case that we don't hook the rtl allocators, + // this becomes an assert since there is no failover to the original + // allocator. + CHECK((HEAP_ALLOCATE_UNSUPPORTED_FLAGS & dwFlags) != 0 && + "unsupported flags"); + } + GET_STACK_TRACE_MALLOC; + void *p = asan_malloc(dwBytes, &stack); + // Reading MSDN suggests that the *entire* usable allocation is zeroed out. + // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. + // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 + if (p && (dwFlags & HEAP_ZERO_MEMORY)) { + GET_CURRENT_PC_BP_SP; + (void)sp; + auto usable_size = asan_malloc_usable_size(p, pc, bp); + internal_memset(p, 0, usable_size); + } + return p; +} + +INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { + // Heap allocations happen before this function is hooked, so we must fall + // back to the original function if the pointer is not from the ASAN heap, + // or unsupported flags are provided. + if (flags()->windows_hook_rtl_allocators) { + if (OWNED_BY_RTL(hHeap, lpMem)) + return REAL(HeapFree)(hHeap, dwFlags, lpMem); + } else { + CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags"); + } + GET_STACK_TRACE_FREE; + asan_free(lpMem, &stack, FROM_MALLOC); + return true; +} + +namespace __asan { +using AllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, size_t); +using ReAllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, LPVOID, size_t); +using SizeFunction = size_t(WINAPI *)(HANDLE, DWORD, LPVOID); +using FreeFunction = BOOL(WINAPI *)(HANDLE, DWORD, LPVOID); + +void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc, + FreeFunction freeFunc, AllocFunction allocFunc, + HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, size_t dwBytes) { + CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc); + GET_STACK_TRACE_MALLOC; + GET_CURRENT_PC_BP_SP; + (void)sp; + if (flags()->windows_hook_rtl_allocators) { + enum AllocationOwnership { NEITHER = 0, ASAN = 1, RTL = 2 }; + AllocationOwnership ownershipState; + bool owned_rtlalloc = false; + bool owned_asan = __sanitizer_get_ownership(lpMem); + + if (!owned_asan) + owned_rtlalloc = HeapValidate(hHeap, 0, lpMem); + + if (owned_asan && !owned_rtlalloc) + ownershipState = ASAN; + else if (!owned_asan && owned_rtlalloc) + ownershipState = RTL; + else if (!owned_asan && !owned_rtlalloc) + ownershipState = NEITHER; + + // If this heap block which was allocated before the ASAN + // runtime came up, use the real HeapFree function. + if (UNLIKELY(!asan_inited)) { + return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); + } + bool only_asan_supported_flags = + (HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) == 0; + + if (ownershipState == RTL || + (ownershipState == NEITHER && !only_asan_supported_flags)) { + if (only_asan_supported_flags) { + // if this is a conversion to ASAN upported flags, transfer this + // allocation to the ASAN allocator + void *replacement_alloc; + if (dwFlags & HEAP_ZERO_MEMORY) + replacement_alloc = asan_calloc(1, dwBytes, &stack); + else + replacement_alloc = asan_malloc(dwBytes, &stack); + if (replacement_alloc) { + size_t old_size = heapSizeFunc(hHeap, dwFlags, lpMem); + if (old_size == ((size_t)0) - 1) { + asan_free(replacement_alloc, &stack, FROM_MALLOC); + return nullptr; + } + REAL(memcpy)(replacement_alloc, lpMem, old_size); + freeFunc(hHeap, dwFlags, lpMem); + } + return replacement_alloc; + } else { + // owned by rtl or neither with unsupported ASAN flags, + // just pass back to original allocator + CHECK(ownershipState == RTL || ownershipState == NEITHER); + CHECK(!only_asan_supported_flags); + return reallocFunc(hHeap, dwFlags, lpMem, dwBytes); + } + } + + if (ownershipState == ASAN && !only_asan_supported_flags) { + // Conversion to unsupported flags allocation, + // transfer this allocation back to the original allocator. + void *replacement_alloc = allocFunc(hHeap, dwFlags, dwBytes); + size_t old_usable_size = 0; + if (replacement_alloc) { + old_usable_size = asan_malloc_usable_size(lpMem, pc, bp); + REAL(memcpy)(replacement_alloc, lpMem, + Min(dwBytes, old_usable_size)); + asan_free(lpMem, &stack, FROM_MALLOC); + } + return replacement_alloc; + } + + CHECK((ownershipState == ASAN || ownershipState == NEITHER) && + only_asan_supported_flags); + // At this point we should either be ASAN owned with ASAN supported flags + // or we owned by neither and have supported flags. + // Pass through even when it's neither since this could be a null realloc or + // UAF that ASAN needs to catch. + } else { + CHECK((HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) != 0 && + "unsupported flags"); + } + // asan_realloc will never reallocate in place, so for now this flag is + // unsupported until we figure out a way to fake this. + if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) + return nullptr; + + // HeapReAlloc and HeapAlloc both happily accept 0 sized allocations. + // passing a 0 size into asan_realloc will free the allocation. + // To avoid this and keep behavior consistent, fudge the size if 0. + // (asan_malloc already does this) + if (dwBytes == 0) + dwBytes = 1; + + size_t old_size; + if (dwFlags & HEAP_ZERO_MEMORY) + old_size = asan_malloc_usable_size(lpMem, pc, bp); + + void *ptr = asan_realloc(lpMem, dwBytes, &stack); + if (ptr == nullptr) + return nullptr; + + if (dwFlags & HEAP_ZERO_MEMORY) { + size_t new_size = asan_malloc_usable_size(ptr, pc, bp); + if (old_size < new_size) + REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size); + } + + return ptr; +} +} // namespace __asan + +INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, + LPVOID lpMem, size_t dwBytes) { + return SharedReAlloc(REAL(HeapReAlloc), (SizeFunction)REAL(HeapSize), + REAL(HeapFree), REAL(HeapAlloc), hHeap, dwFlags, lpMem, + dwBytes); +} + +// The following functions are undocumented and subject to change. +// However, hooking them is necessary to hook Windows heap +// allocations with detours and their definitions are unlikely to change. +// Comments in /minkernel/ntos/rtl/heappublic.c indicate that these functions +// are part of the heap's public interface. +typedef unsigned long LOGICAL; + +// This function is documented as part of the Driver Development Kit but *not* +// the Windows Development Kit. +LOGICAL RtlFreeHeap(void* HeapHandle, DWORD Flags, + void* BaseAddress); + +// This function is documented as part of the Driver Development Kit but *not* +// the Windows Development Kit. +void* RtlAllocateHeap(void* HeapHandle, DWORD Flags, size_t Size); + +// This function is completely undocumented. +void* +RtlReAllocateHeap(void* HeapHandle, DWORD Flags, void* BaseAddress, + size_t Size); + +// This function is completely undocumented. +size_t RtlSizeHeap(void* HeapHandle, DWORD Flags, void* BaseAddress); + +INTERCEPTOR_WINAPI(size_t, RtlSizeHeap, HANDLE HeapHandle, DWORD Flags, + void* BaseAddress) { + if (!flags()->windows_hook_rtl_allocators || + UNLIKELY(!asan_inited || OWNED_BY_RTL(HeapHandle, BaseAddress))) { + return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress); + } + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(BaseAddress, pc, bp); +} + +INTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, DWORD Flags, + void* BaseAddress) { + // Heap allocations happen before this function is hooked, so we must fall + // back to the original function if the pointer is not from the ASAN heap, or + // unsupported flags are provided. + if (!flags()->windows_hook_rtl_allocators || + UNLIKELY((HEAP_FREE_UNSUPPORTED_FLAGS & Flags) != 0 || + OWNED_BY_RTL(HeapHandle, BaseAddress))) { + return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress); + } + GET_STACK_TRACE_FREE; + asan_free(BaseAddress, &stack, FROM_MALLOC); + return true; +} + +INTERCEPTOR_WINAPI(void*, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags, + size_t Size) { + // If the ASAN runtime is not initialized, or we encounter an unsupported + // flag, fall back to the original allocator. + if (!flags()->windows_hook_rtl_allocators || + UNLIKELY(!asan_inited || + (Flags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) { + return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size); + } + GET_STACK_TRACE_MALLOC; + void *p; + // Reading MSDN suggests that the *entire* usable allocation is zeroed out. + // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. + // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 + if (Flags & HEAP_ZERO_MEMORY) { + p = asan_calloc(Size, 1, &stack); + } else { + p = asan_malloc(Size, &stack); + } + return p; +} + +INTERCEPTOR_WINAPI(void*, RtlReAllocateHeap, HANDLE HeapHandle, DWORD Flags, + void* BaseAddress, size_t Size) { + // If it's actually a heap block which was allocated before the ASAN runtime + // came up, use the real RtlFreeHeap function. + if (!flags()->windows_hook_rtl_allocators) + return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size); + + return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap), + REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle, + Flags, BaseAddress, Size); +} + +namespace __asan { + +static void TryToOverrideFunction(const char *fname, uptr new_func) { + // Failure here is not fatal. The CRT may not be present, and different CRT + // versions use different symbols. + if (!__interception::OverrideFunction(fname, new_func)) + VPrintf(2, "Failed to override function %s\n", fname); +} + +void ReplaceSystemMalloc() { +#if defined(ASAN_DYNAMIC) + TryToOverrideFunction("free", (uptr)free); + TryToOverrideFunction("_free_base", (uptr)free); + TryToOverrideFunction("malloc", (uptr)malloc); + TryToOverrideFunction("_malloc_base", (uptr)malloc); + TryToOverrideFunction("_malloc_crt", (uptr)malloc); + TryToOverrideFunction("calloc", (uptr)calloc); + TryToOverrideFunction("_calloc_base", (uptr)calloc); + TryToOverrideFunction("_calloc_crt", (uptr)calloc); + TryToOverrideFunction("realloc", (uptr)realloc); + TryToOverrideFunction("_realloc_base", (uptr)realloc); + TryToOverrideFunction("_realloc_crt", (uptr)realloc); + TryToOverrideFunction("_recalloc", (uptr)_recalloc); + TryToOverrideFunction("_recalloc_base", (uptr)_recalloc); + TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc); + TryToOverrideFunction("_msize", (uptr)_msize); + TryToOverrideFunction("_msize_base", (uptr)_msize); + TryToOverrideFunction("_expand", (uptr)_expand); + TryToOverrideFunction("_expand_base", (uptr)_expand); + + if (flags()->windows_hook_rtl_allocators) { + INTERCEPT_FUNCTION(HeapSize); + INTERCEPT_FUNCTION(HeapFree); + INTERCEPT_FUNCTION(HeapReAlloc); + INTERCEPT_FUNCTION(HeapAlloc); + + // Undocumented functions must be intercepted by name, not by symbol. + __interception::OverrideFunction("RtlSizeHeap", (uptr)WRAP(RtlSizeHeap), + (uptr *)&REAL(RtlSizeHeap)); + __interception::OverrideFunction("RtlFreeHeap", (uptr)WRAP(RtlFreeHeap), + (uptr *)&REAL(RtlFreeHeap)); + __interception::OverrideFunction("RtlReAllocateHeap", + (uptr)WRAP(RtlReAllocateHeap), + (uptr *)&REAL(RtlReAllocateHeap)); + __interception::OverrideFunction("RtlAllocateHeap", + (uptr)WRAP(RtlAllocateHeap), + (uptr *)&REAL(RtlAllocateHeap)); + } else { +#define INTERCEPT_UCRT_FUNCTION(func) \ + if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \ + "api-ms-win-core-heap-l1-1-0.dll", func)) \ + VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); + INTERCEPT_UCRT_FUNCTION(HeapAlloc); + INTERCEPT_UCRT_FUNCTION(HeapFree); + INTERCEPT_UCRT_FUNCTION(HeapReAlloc); + INTERCEPT_UCRT_FUNCTION(HeapSize); +#undef INTERCEPT_UCRT_FUNCTION + } + // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which + // enable cross-module inlining. This means our _malloc_base hook won't catch + // all CRT allocations. This code here patches the import table of + // ucrtbase.dll so that all attempts to use the lower-level win32 heap + // allocation API will be directed to ASan's heap. We don't currently + // intercept all calls to HeapAlloc. If we did, we would have to check on + // HeapFree whether the pointer came from ASan of from the system. + +#endif // defined(ASAN_DYNAMIC) +} +} // namespace __asan + +#endif // _WIN32 diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h index 2357c50e67f..41fb49ee46d 100644 --- a/libsanitizer/asan/asan_mapping.h +++ b/libsanitizer/asan/asan_mapping.h @@ -1,7 +1,8 @@ //===-- asan_mapping.h ------------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -159,14 +160,10 @@ static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000 static const u64 kDefaultShadowOffset64 = 1ULL << 44; static const u64 kDefaultShort64bitShadowOffset = 0x7FFFFFFF & (~0xFFFULL << kDefaultShadowScale); // < 2G. -static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000 -static const u64 kIosShadowOffset64 = 0x120200000; -static const u64 kIosSimShadowOffset32 = 1ULL << 30; -static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64; static const u64 kAArch64_ShadowOffset64 = 1ULL << 36; static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000; static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37; -static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; +static const u64 kPPC64_ShadowOffset64 = 1ULL << 44; static const u64 kSystemZ_ShadowOffset64 = 1ULL << 52; static const u64 kSPARC64_ShadowOffset64 = 1ULL << 43; // 0x80000000000 static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000 @@ -200,11 +197,7 @@ static const u64 kMyriadCacheBitMask32 = 0x40000000ULL; # elif SANITIZER_WINDOWS # define SHADOW_OFFSET kWindowsShadowOffset32 # elif SANITIZER_IOS -# if SANITIZER_IOSSIM -# define SHADOW_OFFSET kIosSimShadowOffset32 -# else -# define SHADOW_OFFSET kIosShadowOffset32 -# endif +# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address # elif SANITIZER_MYRIAD2 # define SHADOW_OFFSET kMyriadShadowOffset32 # else @@ -212,11 +205,7 @@ static const u64 kMyriadCacheBitMask32 = 0x40000000ULL; # endif #else # if SANITIZER_IOS -# if SANITIZER_IOSSIM -# define SHADOW_OFFSET kIosSimShadowOffset64 -# else -# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address -# endif +# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address # elif defined(__aarch64__) # define SHADOW_OFFSET kAArch64_ShadowOffset64 # elif defined(__powerpc64__) @@ -231,8 +220,8 @@ static const u64 kMyriadCacheBitMask32 = 0x40000000ULL; # define SHADOW_OFFSET kDefaultShadowOffset64 # elif defined(__mips64) # define SHADOW_OFFSET kMIPS64_ShadowOffset64 -# elif defined(__sparc__) -# define SHADOW_OFFSET kSPARC64_ShadowOffset64 +#elif defined(__sparc__) +#define SHADOW_OFFSET kSPARC64_ShadowOffset64 # elif SANITIZER_WINDOWS64 # define SHADOW_OFFSET __asan_shadow_memory_dynamic_address # else diff --git a/libsanitizer/asan/asan_mapping_myriad.h b/libsanitizer/asan/asan_mapping_myriad.h index fa8d4fe0270..6969e3a4931 100644 --- a/libsanitizer/asan/asan_mapping_myriad.h +++ b/libsanitizer/asan/asan_mapping_myriad.h @@ -1,7 +1,8 @@ //===-- asan_mapping_myriad.h -----------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/asan/asan_mapping_sparc64.h b/libsanitizer/asan/asan_mapping_sparc64.h index ecde5cac9e1..432a1816f79 100644 --- a/libsanitizer/asan/asan_mapping_sparc64.h +++ b/libsanitizer/asan/asan_mapping_sparc64.h @@ -1,7 +1,8 @@ //===-- asan_mapping_sparc64.h ----------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/asan/asan_memory_profile.cc b/libsanitizer/asan/asan_memory_profile.cc deleted file mode 100644 index 23183bda79b..00000000000 --- a/libsanitizer/asan/asan_memory_profile.cc +++ /dev/null @@ -1,128 +0,0 @@ -//===-- asan_memory_profile.cc.cc -----------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// This file implements __sanitizer_print_memory_profile. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_stackdepot.h" -#include "sanitizer_common/sanitizer_stacktrace.h" -#include "sanitizer_common/sanitizer_stoptheworld.h" -#include "lsan/lsan_common.h" -#include "asan/asan_allocator.h" - -#if CAN_SANITIZE_LEAKS - -namespace __asan { - -struct AllocationSite { - u32 id; - uptr total_size; - uptr count; -}; - -class HeapProfile { - public: - HeapProfile() { allocations_.reserve(1024); } - - void ProcessChunk(const AsanChunkView &cv) { - if (cv.IsAllocated()) { - total_allocated_user_size_ += cv.UsedSize(); - total_allocated_count_++; - u32 id = cv.GetAllocStackId(); - if (id) - Insert(id, cv.UsedSize()); - } else if (cv.IsQuarantined()) { - total_quarantined_user_size_ += cv.UsedSize(); - total_quarantined_count_++; - } else { - total_other_count_++; - } - } - - void Print(uptr top_percent, uptr max_number_of_contexts) { - Sort(allocations_.data(), allocations_.size(), - [](const AllocationSite &a, const AllocationSite &b) { - return a.total_size > b.total_size; - }); - CHECK(total_allocated_user_size_); - uptr total_shown = 0; - Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: " - "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; " - "showing top %zd%% (at most %zd unique contexts)\n", - total_allocated_user_size_, total_allocated_count_, - total_quarantined_user_size_, total_quarantined_count_, - total_other_count_, total_allocated_count_ + - total_quarantined_count_ + total_other_count_, top_percent, - max_number_of_contexts); - for (uptr i = 0; i < Min(allocations_.size(), max_number_of_contexts); - i++) { - auto &a = allocations_[i]; - Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size, - a.total_size * 100 / total_allocated_user_size_, a.count); - StackDepotGet(a.id).Print(); - total_shown += a.total_size; - if (total_shown * 100 / total_allocated_user_size_ > top_percent) - break; - } - } - - private: - uptr total_allocated_user_size_ = 0; - uptr total_allocated_count_ = 0; - uptr total_quarantined_user_size_ = 0; - uptr total_quarantined_count_ = 0; - uptr total_other_count_ = 0; - InternalMmapVector allocations_; - - void Insert(u32 id, uptr size) { - // Linear lookup will be good enough for most cases (although not all). - for (uptr i = 0; i < allocations_.size(); i++) { - if (allocations_[i].id == id) { - allocations_[i].total_size += size; - allocations_[i].count++; - return; - } - } - allocations_.push_back({id, size, 1}); - } -}; - -static void ChunkCallback(uptr chunk, void *arg) { - reinterpret_cast(arg)->ProcessChunk( - FindHeapChunkByAllocBeg(chunk)); -} - -static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list, - void *argument) { - HeapProfile hp; - __lsan::ForEachChunk(ChunkCallback, &hp); - uptr *Arg = reinterpret_cast(argument); - hp.Print(Arg[0], Arg[1]); - - if (Verbosity()) - __asan_print_accumulated_stats(); -} - -} // namespace __asan - -#endif // CAN_SANITIZE_LEAKS - -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_print_memory_profile(uptr top_percent, - uptr max_number_of_contexts) { -#if CAN_SANITIZE_LEAKS - uptr Arg[2]; - Arg[0] = top_percent; - Arg[1] = max_number_of_contexts; - __sanitizer::StopTheWorld(__asan::MemoryProfileCB, Arg); -#endif // CAN_SANITIZE_LEAKS -} -} // extern "C" diff --git a/libsanitizer/asan/asan_memory_profile.cpp b/libsanitizer/asan/asan_memory_profile.cpp new file mode 100644 index 00000000000..4fcd5600ed1 --- /dev/null +++ b/libsanitizer/asan/asan_memory_profile.cpp @@ -0,0 +1,129 @@ +//===-- asan_memory_profile.cpp ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file implements __sanitizer_print_memory_profile. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_stoptheworld.h" +#include "lsan/lsan_common.h" +#include "asan/asan_allocator.h" + +#if CAN_SANITIZE_LEAKS + +namespace __asan { + +struct AllocationSite { + u32 id; + uptr total_size; + uptr count; +}; + +class HeapProfile { + public: + HeapProfile() { allocations_.reserve(1024); } + + void ProcessChunk(const AsanChunkView &cv) { + if (cv.IsAllocated()) { + total_allocated_user_size_ += cv.UsedSize(); + total_allocated_count_++; + u32 id = cv.GetAllocStackId(); + if (id) + Insert(id, cv.UsedSize()); + } else if (cv.IsQuarantined()) { + total_quarantined_user_size_ += cv.UsedSize(); + total_quarantined_count_++; + } else { + total_other_count_++; + } + } + + void Print(uptr top_percent, uptr max_number_of_contexts) { + Sort(allocations_.data(), allocations_.size(), + [](const AllocationSite &a, const AllocationSite &b) { + return a.total_size > b.total_size; + }); + CHECK(total_allocated_user_size_); + uptr total_shown = 0; + Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: " + "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; " + "showing top %zd%% (at most %zd unique contexts)\n", + total_allocated_user_size_, total_allocated_count_, + total_quarantined_user_size_, total_quarantined_count_, + total_other_count_, total_allocated_count_ + + total_quarantined_count_ + total_other_count_, top_percent, + max_number_of_contexts); + for (uptr i = 0; i < Min(allocations_.size(), max_number_of_contexts); + i++) { + auto &a = allocations_[i]; + Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size, + a.total_size * 100 / total_allocated_user_size_, a.count); + StackDepotGet(a.id).Print(); + total_shown += a.total_size; + if (total_shown * 100 / total_allocated_user_size_ > top_percent) + break; + } + } + + private: + uptr total_allocated_user_size_ = 0; + uptr total_allocated_count_ = 0; + uptr total_quarantined_user_size_ = 0; + uptr total_quarantined_count_ = 0; + uptr total_other_count_ = 0; + InternalMmapVector allocations_; + + void Insert(u32 id, uptr size) { + // Linear lookup will be good enough for most cases (although not all). + for (uptr i = 0; i < allocations_.size(); i++) { + if (allocations_[i].id == id) { + allocations_[i].total_size += size; + allocations_[i].count++; + return; + } + } + allocations_.push_back({id, size, 1}); + } +}; + +static void ChunkCallback(uptr chunk, void *arg) { + reinterpret_cast(arg)->ProcessChunk( + FindHeapChunkByAllocBeg(chunk)); +} + +static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list, + void *argument) { + HeapProfile hp; + __lsan::ForEachChunk(ChunkCallback, &hp); + uptr *Arg = reinterpret_cast(argument); + hp.Print(Arg[0], Arg[1]); + + if (Verbosity()) + __asan_print_accumulated_stats(); +} + +} // namespace __asan + +#endif // CAN_SANITIZE_LEAKS + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_print_memory_profile(uptr top_percent, + uptr max_number_of_contexts) { +#if CAN_SANITIZE_LEAKS + uptr Arg[2]; + Arg[0] = top_percent; + Arg[1] = max_number_of_contexts; + __sanitizer::StopTheWorld(__asan::MemoryProfileCB, Arg); +#endif // CAN_SANITIZE_LEAKS +} +} // extern "C" diff --git a/libsanitizer/asan/asan_new_delete.cc b/libsanitizer/asan/asan_new_delete.cc deleted file mode 100644 index 7e194e2229c..00000000000 --- a/libsanitizer/asan/asan_new_delete.cc +++ /dev/null @@ -1,209 +0,0 @@ -//===-- asan_interceptors.cc ----------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Interceptors for operators new and delete. -//===----------------------------------------------------------------------===// - -#include "asan_allocator.h" -#include "asan_internal.h" -#include "asan_malloc_local.h" -#include "asan_report.h" -#include "asan_stack.h" - -#include "interception/interception.h" - -#include - -// C++ operators can't have dllexport attributes on Windows. We export them -// anyway by passing extra -export flags to the linker, which is exactly that -// dllexport would normally do. We need to export them in order to make the -// VS2015 dynamic CRT (MD) work. -#if SANITIZER_WINDOWS && defined(_MSC_VER) -#define CXX_OPERATOR_ATTRIBUTE -#define COMMENT_EXPORT(sym) __pragma(comment(linker, "/export:" sym)) -#ifdef _WIN64 -COMMENT_EXPORT("??2@YAPEAX_K@Z") // operator new -COMMENT_EXPORT("??2@YAPEAX_KAEBUnothrow_t@std@@@Z") // operator new nothrow -COMMENT_EXPORT("??3@YAXPEAX@Z") // operator delete -COMMENT_EXPORT("??3@YAXPEAX_K@Z") // sized operator delete -COMMENT_EXPORT("??_U@YAPEAX_K@Z") // operator new[] -COMMENT_EXPORT("??_V@YAXPEAX@Z") // operator delete[] -#else -COMMENT_EXPORT("??2@YAPAXI@Z") // operator new -COMMENT_EXPORT("??2@YAPAXIABUnothrow_t@std@@@Z") // operator new nothrow -COMMENT_EXPORT("??3@YAXPAX@Z") // operator delete -COMMENT_EXPORT("??3@YAXPAXI@Z") // sized operator delete -COMMENT_EXPORT("??_U@YAPAXI@Z") // operator new[] -COMMENT_EXPORT("??_V@YAXPAX@Z") // operator delete[] -#endif -#undef COMMENT_EXPORT -#else -#define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE -#endif - -using namespace __asan; // NOLINT - -// FreeBSD prior v9.2 have wrong definition of 'size_t'. -// http://svnweb.freebsd.org/base?view=revision&revision=232261 -#if SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32 -#include -#if __FreeBSD_version <= 902001 // v9.2 -#define size_t unsigned -#endif // __FreeBSD_version -#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32 - -// This code has issues on OSX. -// See https://github.com/google/sanitizers/issues/131. - -// Fake std::nothrow_t and std::align_val_t to avoid including . -namespace std { -struct nothrow_t {}; -enum class align_val_t: size_t {}; -} // namespace std - -// TODO(alekseyshl): throw std::bad_alloc instead of dying on OOM. -// For local pool allocation, align to SHADOW_GRANULARITY to match asan -// allocator behavior. -#define OPERATOR_NEW_BODY(type, nothrow) \ - if (ALLOCATE_FROM_LOCAL_POOL) {\ - void *res = MemalignFromLocalPool(SHADOW_GRANULARITY, size);\ - if (!nothrow) CHECK(res);\ - return res;\ - }\ - GET_STACK_TRACE_MALLOC;\ - void *res = asan_memalign(0, size, &stack, type);\ - if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\ - return res; -#define OPERATOR_NEW_BODY_ALIGN(type, nothrow) \ - if (ALLOCATE_FROM_LOCAL_POOL) {\ - void *res = MemalignFromLocalPool((uptr)align, size);\ - if (!nothrow) CHECK(res);\ - return res;\ - }\ - GET_STACK_TRACE_MALLOC;\ - void *res = asan_memalign((uptr)align, size, &stack, type);\ - if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\ - return res; - -// On OS X it's not enough to just provide our own 'operator new' and -// 'operator delete' implementations, because they're going to be in the -// runtime dylib, and the main executable will depend on both the runtime -// dylib and libstdc++, each of those'll have its implementation of new and -// delete. -// To make sure that C++ allocation/deallocation operators are overridden on -// OS X we need to intercept them using their mangled names. -#if !SANITIZER_MAC -CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size) -{ OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/); } -CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size) -{ OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/); } -CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size, std::nothrow_t const&) -{ OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/); } -CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size, std::nothrow_t const&) -{ OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); } -CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size, std::align_val_t align) -{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, false /*nothrow*/); } -CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size, std::align_val_t align) -{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, false /*nothrow*/); } -CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&) -{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, true /*nothrow*/); } -CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&) -{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, true /*nothrow*/); } - -#else // SANITIZER_MAC -INTERCEPTOR(void *, _Znwm, size_t size) { - OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/); -} -INTERCEPTOR(void *, _Znam, size_t size) { - OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/); -} -INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) { - OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/); -} -INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) { - OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); -} -#endif // !SANITIZER_MAC - -#define OPERATOR_DELETE_BODY(type) \ - if (IS_FROM_LOCAL_POOL(ptr)) return;\ - GET_STACK_TRACE_FREE;\ - asan_delete(ptr, 0, 0, &stack, type); - -#define OPERATOR_DELETE_BODY_SIZE(type) \ - if (IS_FROM_LOCAL_POOL(ptr)) return;\ - GET_STACK_TRACE_FREE;\ - asan_delete(ptr, size, 0, &stack, type); - -#define OPERATOR_DELETE_BODY_ALIGN(type) \ - if (IS_FROM_LOCAL_POOL(ptr)) return;\ - GET_STACK_TRACE_FREE;\ - asan_delete(ptr, 0, static_cast(align), &stack, type); - -#define OPERATOR_DELETE_BODY_SIZE_ALIGN(type) \ - if (IS_FROM_LOCAL_POOL(ptr)) return;\ - GET_STACK_TRACE_FREE;\ - asan_delete(ptr, size, static_cast(align), &stack, type); - -#if !SANITIZER_MAC -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr) NOEXCEPT -{ OPERATOR_DELETE_BODY(FROM_NEW); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr) NOEXCEPT -{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY(FROM_NEW); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, size_t size) NOEXCEPT -{ OPERATOR_DELETE_BODY_SIZE(FROM_NEW); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, size_t size) NOEXCEPT -{ OPERATOR_DELETE_BODY_SIZE(FROM_NEW_BR); } -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, std::align_val_t align) NOEXCEPT -{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT -{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); } -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, std::align_val_t align, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); } -CXX_OPERATOR_ATTRIBUTE -void operator delete(void *ptr, size_t size, std::align_val_t align) NOEXCEPT -{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW); } -CXX_OPERATOR_ATTRIBUTE -void operator delete[](void *ptr, size_t size, std::align_val_t align) NOEXCEPT -{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW_BR); } - -#else // SANITIZER_MAC -INTERCEPTOR(void, _ZdlPv, void *ptr) -{ OPERATOR_DELETE_BODY(FROM_NEW); } -INTERCEPTOR(void, _ZdaPv, void *ptr) -{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } -INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY(FROM_NEW); } -INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) -{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } -#endif // !SANITIZER_MAC diff --git a/libsanitizer/asan/asan_new_delete.cpp b/libsanitizer/asan/asan_new_delete.cpp new file mode 100644 index 00000000000..c15e208094e --- /dev/null +++ b/libsanitizer/asan/asan_new_delete.cpp @@ -0,0 +1,204 @@ +//===-- asan_interceptors.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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_malloc_local.h" +#include "asan_report.h" +#include "asan_stack.h" + +#include "interception/interception.h" + +#include + +// C++ operators can't have dllexport attributes on Windows. We export them +// anyway by passing extra -export flags to the linker, which is exactly that +// dllexport would normally do. We need to export them in order to make the +// VS2015 dynamic CRT (MD) work. +#if SANITIZER_WINDOWS && defined(_MSC_VER) +#define CXX_OPERATOR_ATTRIBUTE +#define COMMENT_EXPORT(sym) __pragma(comment(linker, "/export:" sym)) +#ifdef _WIN64 +COMMENT_EXPORT("??2@YAPEAX_K@Z") // operator new +COMMENT_EXPORT("??2@YAPEAX_KAEBUnothrow_t@std@@@Z") // operator new nothrow +COMMENT_EXPORT("??3@YAXPEAX@Z") // operator delete +COMMENT_EXPORT("??3@YAXPEAX_K@Z") // sized operator delete +COMMENT_EXPORT("??_U@YAPEAX_K@Z") // operator new[] +COMMENT_EXPORT("??_V@YAXPEAX@Z") // operator delete[] +#else +COMMENT_EXPORT("??2@YAPAXI@Z") // operator new +COMMENT_EXPORT("??2@YAPAXIABUnothrow_t@std@@@Z") // operator new nothrow +COMMENT_EXPORT("??3@YAXPAX@Z") // operator delete +COMMENT_EXPORT("??3@YAXPAXI@Z") // sized operator delete +COMMENT_EXPORT("??_U@YAPAXI@Z") // operator new[] +COMMENT_EXPORT("??_V@YAXPAX@Z") // operator delete[] +#endif +#undef COMMENT_EXPORT +#else +#define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE +#endif + +using namespace __asan; // NOLINT + +// FreeBSD prior v9.2 have wrong definition of 'size_t'. +// http://svnweb.freebsd.org/base?view=revision&revision=232261 +#if SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32 +#include +#if __FreeBSD_version <= 902001 // v9.2 +#define size_t unsigned +#endif // __FreeBSD_version +#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32 + +// This code has issues on OSX. +// See https://github.com/google/sanitizers/issues/131. + +// Fake std::nothrow_t and std::align_val_t to avoid including . +namespace std { +struct nothrow_t {}; +enum class align_val_t: size_t {}; +} // namespace std + +// TODO(alekseyshl): throw std::bad_alloc instead of dying on OOM. +// For local pool allocation, align to SHADOW_GRANULARITY to match asan +// allocator behavior. +#define OPERATOR_NEW_BODY(type, nothrow) \ + MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow); \ + GET_STACK_TRACE_MALLOC; \ + void *res = asan_memalign(0, size, &stack, type); \ + if (!nothrow && UNLIKELY(!res)) \ + ReportOutOfMemory(size, &stack); \ + return res; +#define OPERATOR_NEW_BODY_ALIGN(type, nothrow) \ + MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow); \ + GET_STACK_TRACE_MALLOC; \ + void *res = asan_memalign((uptr)align, size, &stack, type); \ + if (!nothrow && UNLIKELY(!res)) \ + ReportOutOfMemory(size, &stack); \ + return res; + +// On OS X it's not enough to just provide our own 'operator new' and +// 'operator delete' implementations, because they're going to be in the +// runtime dylib, and the main executable will depend on both the runtime +// dylib and libstdc++, each of those'll have its implementation of new and +// delete. +// To make sure that C++ allocation/deallocation operators are overridden on +// OS X we need to intercept them using their mangled names. +#if !SANITIZER_MAC +CXX_OPERATOR_ATTRIBUTE +void *operator new(size_t size) +{ OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/); } +CXX_OPERATOR_ATTRIBUTE +void *operator new[](size_t size) +{ OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/); } +CXX_OPERATOR_ATTRIBUTE +void *operator new(size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/); } +CXX_OPERATOR_ATTRIBUTE +void *operator new[](size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); } +CXX_OPERATOR_ATTRIBUTE +void *operator new(size_t size, std::align_val_t align) +{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, false /*nothrow*/); } +CXX_OPERATOR_ATTRIBUTE +void *operator new[](size_t size, std::align_val_t align) +{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, false /*nothrow*/); } +CXX_OPERATOR_ATTRIBUTE +void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&) +{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, true /*nothrow*/); } +CXX_OPERATOR_ATTRIBUTE +void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&) +{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, true /*nothrow*/); } + +#else // SANITIZER_MAC +INTERCEPTOR(void *, _Znwm, size_t size) { + OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/); +} +INTERCEPTOR(void *, _Znam, size_t size) { + OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/); +} +INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/); +} +INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); +} +#endif // !SANITIZER_MAC + +#define OPERATOR_DELETE_BODY(type) \ + if (IS_FROM_LOCAL_POOL(ptr)) return;\ + GET_STACK_TRACE_FREE;\ + asan_delete(ptr, 0, 0, &stack, type); + +#define OPERATOR_DELETE_BODY_SIZE(type) \ + if (IS_FROM_LOCAL_POOL(ptr)) return;\ + GET_STACK_TRACE_FREE;\ + asan_delete(ptr, size, 0, &stack, type); + +#define OPERATOR_DELETE_BODY_ALIGN(type) \ + if (IS_FROM_LOCAL_POOL(ptr)) return;\ + GET_STACK_TRACE_FREE;\ + asan_delete(ptr, 0, static_cast(align), &stack, type); + +#define OPERATOR_DELETE_BODY_SIZE_ALIGN(type) \ + if (IS_FROM_LOCAL_POOL(ptr)) return;\ + GET_STACK_TRACE_FREE;\ + asan_delete(ptr, size, static_cast(align), &stack, type); + +#if !SANITIZER_MAC +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr) NOEXCEPT +{ OPERATOR_DELETE_BODY(FROM_NEW); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr) NOEXCEPT +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr, size_t size) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE(FROM_NEW); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr, size_t size) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE(FROM_NEW_BR); } +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); } +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr, std::align_val_t align, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); } +CXX_OPERATOR_ATTRIBUTE +void operator delete(void *ptr, size_t size, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW); } +CXX_OPERATOR_ATTRIBUTE +void operator delete[](void *ptr, size_t size, std::align_val_t align) NOEXCEPT +{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW_BR); } + +#else // SANITIZER_MAC +INTERCEPTOR(void, _ZdlPv, void *ptr) +{ OPERATOR_DELETE_BODY(FROM_NEW); } +INTERCEPTOR(void, _ZdaPv, void *ptr) +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } +INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW); } +INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY(FROM_NEW_BR); } +#endif // !SANITIZER_MAC diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc deleted file mode 100644 index 35409baf612..00000000000 --- a/libsanitizer/asan/asan_poisoning.cc +++ /dev/null @@ -1,459 +0,0 @@ -//===-- asan_poisoning.cc -------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Shadow memory poisoning by ASan RTL and by user application. -//===----------------------------------------------------------------------===// - -#include "asan_poisoning.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "sanitizer_common/sanitizer_atomic.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_flags.h" - -namespace __asan { - -static atomic_uint8_t can_poison_memory; - -void SetCanPoisonMemory(bool value) { - atomic_store(&can_poison_memory, value, memory_order_release); -} - -bool CanPoisonMemory() { - return atomic_load(&can_poison_memory, memory_order_acquire); -} - -void PoisonShadow(uptr addr, uptr size, u8 value) { - if (value && !CanPoisonMemory()) return; - CHECK(AddrIsAlignedByGranularity(addr)); - CHECK(AddrIsInMem(addr)); - CHECK(AddrIsAlignedByGranularity(addr + size)); - CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY)); - CHECK(REAL(memset)); - FastPoisonShadow(addr, size, value); -} - -void PoisonShadowPartialRightRedzone(uptr addr, - uptr size, - uptr redzone_size, - u8 value) { - if (!CanPoisonMemory()) return; - CHECK(AddrIsAlignedByGranularity(addr)); - CHECK(AddrIsInMem(addr)); - FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value); -} - -struct ShadowSegmentEndpoint { - u8 *chunk; - s8 offset; // in [0, SHADOW_GRANULARITY) - s8 value; // = *chunk; - - explicit ShadowSegmentEndpoint(uptr address) { - chunk = (u8*)MemToShadow(address); - offset = address & (SHADOW_GRANULARITY - 1); - value = *chunk; - } -}; - -void FlushUnneededASanShadowMemory(uptr p, uptr size) { - // Since asan's mapping is compacting, the shadow chunk may be - // not page-aligned, so we only flush the page-aligned portion. - ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size)); -} - -void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) { - uptr end = ptr + size; - if (Verbosity()) { - Printf("__asan_%spoison_intra_object_redzone [%p,%p) %zd\n", - poison ? "" : "un", ptr, end, size); - if (Verbosity() >= 2) - PRINT_CURRENT_STACK(); - } - CHECK(size); - CHECK_LE(size, 4096); - CHECK(IsAligned(end, SHADOW_GRANULARITY)); - if (!IsAligned(ptr, SHADOW_GRANULARITY)) { - *(u8 *)MemToShadow(ptr) = - poison ? static_cast(ptr % SHADOW_GRANULARITY) : 0; - ptr |= SHADOW_GRANULARITY - 1; - ptr++; - } - for (; ptr < end; ptr += SHADOW_GRANULARITY) - *(u8*)MemToShadow(ptr) = poison ? kAsanIntraObjectRedzone : 0; -} - -} // namespace __asan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -// Current implementation of __asan_(un)poison_memory_region doesn't check -// that user program (un)poisons the memory it owns. It poisons memory -// conservatively, and unpoisons progressively to make sure asan shadow -// mapping invariant is preserved (see detailed mapping description here: -// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm). -// -// * if user asks to poison region [left, right), the program poisons -// 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, uptr size) { - if (!flags()->allow_user_poisoning || size == 0) return; - uptr beg_addr = (uptr)addr; - uptr end_addr = beg_addr + size; - VPrintf(3, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr, - (void *)end_addr); - ShadowSegmentEndpoint beg(beg_addr); - ShadowSegmentEndpoint end(end_addr); - if (beg.chunk == end.chunk) { - CHECK_LT(beg.offset, end.offset); - s8 value = beg.value; - CHECK_EQ(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. - if (value > 0 && value <= end.offset) { - if (beg.offset > 0) { - *beg.chunk = Min(value, beg.offset); - } else { - *beg.chunk = kAsanUserPoisonedMemoryMagic; - } - } - return; - } - CHECK_LT(beg.chunk, end.chunk); - if (beg.offset > 0) { - // Mark bytes from beg.offset as unaddressable. - if (beg.value == 0) { - *beg.chunk = beg.offset; - } else { - *beg.chunk = Min(beg.value, beg.offset); - } - 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, uptr size) { - if (!flags()->allow_user_poisoning || size == 0) return; - uptr beg_addr = (uptr)addr; - uptr end_addr = beg_addr + size; - VPrintf(3, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr, - (void *)end_addr); - ShadowSegmentEndpoint beg(beg_addr); - ShadowSegmentEndpoint end(end_addr); - if (beg.chunk == end.chunk) { - CHECK_LT(beg.offset, end.offset); - s8 value = beg.value; - CHECK_EQ(value, end.value); - // We unpoison memory bytes up to enbytes up to end.offset if it is not - // unpoisoned already. - if (value != 0) { - *beg.chunk = Max(value, end.offset); - } - return; - } - CHECK_LT(beg.chunk, end.chunk); - if (beg.offset > 0) { - *beg.chunk = 0; - 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); - } -} - -int __asan_address_is_poisoned(void const volatile *addr) { - return __asan::AddressIsPoisoned((uptr)addr); -} - -uptr __asan_region_is_poisoned(uptr beg, uptr size) { - if (!size) return 0; - uptr end = beg + size; - if (SANITIZER_MYRIAD2) { - // On Myriad, address not in DRAM range need to be treated as - // unpoisoned. - if (!AddrIsInMem(beg) && !AddrIsInShadow(beg)) return 0; - if (!AddrIsInMem(end) && !AddrIsInShadow(end)) return 0; - } else { - if (!AddrIsInMem(beg)) return beg; - if (!AddrIsInMem(end)) return end; - } - CHECK_LT(beg, end); - uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY); - uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY); - uptr shadow_beg = MemToShadow(aligned_b); - uptr shadow_end = MemToShadow(aligned_e); - // First check the first and the last application bytes, - // then check the SHADOW_GRANULARITY-aligned region by calling - // mem_is_zero on the corresponding shadow. - if (!__asan::AddressIsPoisoned(beg) && - !__asan::AddressIsPoisoned(end - 1) && - (shadow_end <= shadow_beg || - __sanitizer::mem_is_zero((const char *)shadow_beg, - shadow_end - shadow_beg))) - return 0; - // The fast check failed, so we have a poisoned byte somewhere. - // Find it slowly. - for (; beg < end; beg++) - if (__asan::AddressIsPoisoned(beg)) - return beg; - UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found"); - return 0; -} - -#define CHECK_SMALL_REGION(p, size, isWrite) \ - do { \ - uptr __p = reinterpret_cast(p); \ - uptr __size = size; \ - if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \ - __asan::AddressIsPoisoned(__p + __size - 1))) { \ - GET_CURRENT_PC_BP_SP; \ - uptr __bad = __asan_region_is_poisoned(__p, __size); \ - __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\ - } \ - } while (false) - - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -u16 __sanitizer_unaligned_load16(const uu16 *p) { - CHECK_SMALL_REGION(p, sizeof(*p), false); - return *p; -} - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -u32 __sanitizer_unaligned_load32(const uu32 *p) { - CHECK_SMALL_REGION(p, sizeof(*p), false); - return *p; -} - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -u64 __sanitizer_unaligned_load64(const uu64 *p) { - CHECK_SMALL_REGION(p, sizeof(*p), false); - return *p; -} - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store16(uu16 *p, u16 x) { - CHECK_SMALL_REGION(p, sizeof(*p), true); - *p = x; -} - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store32(uu32 *p, u32 x) { - CHECK_SMALL_REGION(p, sizeof(*p), true); - *p = x; -} - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store64(uu64 *p, u64 x) { - CHECK_SMALL_REGION(p, sizeof(*p), true); - *p = x; -} - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -void __asan_poison_cxx_array_cookie(uptr p) { - if (SANITIZER_WORDSIZE != 64) return; - if (!flags()->poison_array_cookie) return; - uptr s = MEM_TO_SHADOW(p); - *reinterpret_cast(s) = kAsanArrayCookieMagic; -} - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -uptr __asan_load_cxx_array_cookie(uptr *p) { - if (SANITIZER_WORDSIZE != 64) return *p; - if (!flags()->poison_array_cookie) return *p; - uptr s = MEM_TO_SHADOW(reinterpret_cast(p)); - u8 sval = *reinterpret_cast(s); - if (sval == kAsanArrayCookieMagic) return *p; - // If sval is not kAsanArrayCookieMagic it can only be freed memory, - // which means that we are going to get double-free. So, return 0 to avoid - // infinite loop of destructors. We don't want to report a double-free here - // though, so print a warning just in case. - // CHECK_EQ(sval, kAsanHeapFreeMagic); - if (sval == kAsanHeapFreeMagic) { - Report("AddressSanitizer: loaded array cookie from free-d memory; " - "expect a double-free report\n"); - return 0; - } - // The cookie may remain unpoisoned if e.g. it comes from a custom - // operator new defined inside a class. - return *p; -} - -// This is a simplified version of __asan_(un)poison_memory_region, which -// assumes that left border of region to be poisoned is properly aligned. -static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { - if (size == 0) return; - uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1); - PoisonShadow(addr, aligned_size, - do_poison ? kAsanStackUseAfterScopeMagic : 0); - if (size == aligned_size) - return; - s8 end_offset = (s8)(size - aligned_size); - s8* shadow_end = (s8*)MemToShadow(addr + aligned_size); - s8 end_value = *shadow_end; - if (do_poison) { - // If possible, mark all the bytes mapping to last shadow byte as - // unaddressable. - if (end_value > 0 && end_value <= end_offset) - *shadow_end = (s8)kAsanStackUseAfterScopeMagic; - } else { - // If necessary, mark few first bytes mapping to last shadow byte - // as addressable - if (end_value != 0) - *shadow_end = Max(end_value, end_offset); - } -} - -void __asan_set_shadow_00(uptr addr, uptr size) { - REAL(memset)((void *)addr, 0, size); -} - -void __asan_set_shadow_f1(uptr addr, uptr size) { - REAL(memset)((void *)addr, 0xf1, size); -} - -void __asan_set_shadow_f2(uptr addr, uptr size) { - REAL(memset)((void *)addr, 0xf2, size); -} - -void __asan_set_shadow_f3(uptr addr, uptr size) { - REAL(memset)((void *)addr, 0xf3, size); -} - -void __asan_set_shadow_f5(uptr addr, uptr size) { - REAL(memset)((void *)addr, 0xf5, size); -} - -void __asan_set_shadow_f8(uptr addr, uptr size) { - REAL(memset)((void *)addr, 0xf8, size); -} - -void __asan_poison_stack_memory(uptr addr, uptr size) { - VReport(1, "poisoning: %p %zx\n", (void *)addr, size); - PoisonAlignedStackMemory(addr, size, true); -} - -void __asan_unpoison_stack_memory(uptr addr, uptr size) { - VReport(1, "unpoisoning: %p %zx\n", (void *)addr, size); - PoisonAlignedStackMemory(addr, size, false); -} - -void __sanitizer_annotate_contiguous_container(const void *beg_p, - const void *end_p, - const void *old_mid_p, - const void *new_mid_p) { - if (!flags()->detect_container_overflow) return; - VPrintf(2, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p, - new_mid_p); - uptr beg = reinterpret_cast(beg_p); - uptr end = reinterpret_cast(end_p); - uptr old_mid = reinterpret_cast(old_mid_p); - uptr new_mid = reinterpret_cast(new_mid_p); - uptr granularity = SHADOW_GRANULARITY; - if (!(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end && - IsAligned(beg, granularity))) { - GET_STACK_TRACE_FATAL_HERE; - ReportBadParamsToAnnotateContiguousContainer(beg, end, old_mid, new_mid, - &stack); - } - CHECK_LE(end - beg, - FIRST_32_SECOND_64(1UL << 30, 1ULL << 34)); // Sanity check. - - uptr a = RoundDownTo(Min(old_mid, new_mid), granularity); - uptr c = RoundUpTo(Max(old_mid, new_mid), granularity); - uptr d1 = RoundDownTo(old_mid, granularity); - // uptr d2 = RoundUpTo(old_mid, granularity); - // Currently we should be in this state: - // [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good. - // Make a quick sanity check that we are indeed in this state. - // - // FIXME: Two of these three checks are disabled until we fix - // https://github.com/google/sanitizers/issues/258. - // if (d1 != d2) - // CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1); - if (a + granularity <= d1) - CHECK_EQ(*(u8*)MemToShadow(a), 0); - // if (d2 + granularity <= c && c <= end) - // CHECK_EQ(*(u8 *)MemToShadow(c - granularity), - // kAsanContiguousContainerOOBMagic); - - uptr b1 = RoundDownTo(new_mid, granularity); - uptr b2 = RoundUpTo(new_mid, granularity); - // New state: - // [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good. - PoisonShadow(a, b1 - a, 0); - PoisonShadow(b2, c - b2, kAsanContiguousContainerOOBMagic); - if (b1 != b2) { - CHECK_EQ(b2 - b1, granularity); - *(u8*)MemToShadow(b1) = static_cast(new_mid - b1); - } -} - -const void *__sanitizer_contiguous_container_find_bad_address( - const void *beg_p, const void *mid_p, const void *end_p) { - if (!flags()->detect_container_overflow) - return nullptr; - uptr beg = reinterpret_cast(beg_p); - uptr end = reinterpret_cast(end_p); - uptr mid = reinterpret_cast(mid_p); - CHECK_LE(beg, mid); - CHECK_LE(mid, end); - // Check some bytes starting from beg, some bytes around mid, and some bytes - // ending with end. - uptr kMaxRangeToCheck = 32; - uptr r1_beg = beg; - uptr r1_end = Min(beg + kMaxRangeToCheck, mid); - uptr r2_beg = Max(beg, mid - kMaxRangeToCheck); - uptr r2_end = Min(end, mid + kMaxRangeToCheck); - uptr r3_beg = Max(end - kMaxRangeToCheck, mid); - uptr r3_end = end; - for (uptr i = r1_beg; i < r1_end; i++) - if (AddressIsPoisoned(i)) - return reinterpret_cast(i); - for (uptr i = r2_beg; i < mid; i++) - if (AddressIsPoisoned(i)) - return reinterpret_cast(i); - for (uptr i = mid; i < r2_end; i++) - if (!AddressIsPoisoned(i)) - return reinterpret_cast(i); - for (uptr i = r3_beg; i < r3_end; i++) - if (!AddressIsPoisoned(i)) - return reinterpret_cast(i); - return nullptr; -} - -int __sanitizer_verify_contiguous_container(const void *beg_p, - const void *mid_p, - const void *end_p) { - return __sanitizer_contiguous_container_find_bad_address(beg_p, mid_p, - end_p) == nullptr; -} - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -void __asan_poison_intra_object_redzone(uptr ptr, uptr size) { - AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, true); -} - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -void __asan_unpoison_intra_object_redzone(uptr ptr, uptr size) { - AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, false); -} - -// --- Implementation of LSan-specific functions --- {{{1 -namespace __lsan { -bool WordIsPoisoned(uptr addr) { - return (__asan_region_is_poisoned(addr, sizeof(uptr)) != 0); -} -} diff --git a/libsanitizer/asan/asan_poisoning.cpp b/libsanitizer/asan/asan_poisoning.cpp new file mode 100644 index 00000000000..6b36be7d1cd --- /dev/null +++ b/libsanitizer/asan/asan_poisoning.cpp @@ -0,0 +1,460 @@ +//===-- asan_poisoning.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Shadow memory poisoning by ASan RTL and by user application. +//===----------------------------------------------------------------------===// + +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_flags.h" + +namespace __asan { + +static atomic_uint8_t can_poison_memory; + +void SetCanPoisonMemory(bool value) { + atomic_store(&can_poison_memory, value, memory_order_release); +} + +bool CanPoisonMemory() { + return atomic_load(&can_poison_memory, memory_order_acquire); +} + +void PoisonShadow(uptr addr, uptr size, u8 value) { + if (value && !CanPoisonMemory()) return; + CHECK(AddrIsAlignedByGranularity(addr)); + CHECK(AddrIsInMem(addr)); + CHECK(AddrIsAlignedByGranularity(addr + size)); + CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY)); + CHECK(REAL(memset)); + FastPoisonShadow(addr, size, value); +} + +void PoisonShadowPartialRightRedzone(uptr addr, + uptr size, + uptr redzone_size, + u8 value) { + if (!CanPoisonMemory()) return; + CHECK(AddrIsAlignedByGranularity(addr)); + CHECK(AddrIsInMem(addr)); + FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value); +} + +struct ShadowSegmentEndpoint { + u8 *chunk; + s8 offset; // in [0, SHADOW_GRANULARITY) + s8 value; // = *chunk; + + explicit ShadowSegmentEndpoint(uptr address) { + chunk = (u8*)MemToShadow(address); + offset = address & (SHADOW_GRANULARITY - 1); + value = *chunk; + } +}; + +void FlushUnneededASanShadowMemory(uptr p, uptr size) { + // Since asan's mapping is compacting, the shadow chunk may be + // not page-aligned, so we only flush the page-aligned portion. + ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size)); +} + +void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) { + uptr end = ptr + size; + if (Verbosity()) { + Printf("__asan_%spoison_intra_object_redzone [%p,%p) %zd\n", + poison ? "" : "un", ptr, end, size); + if (Verbosity() >= 2) + PRINT_CURRENT_STACK(); + } + CHECK(size); + CHECK_LE(size, 4096); + CHECK(IsAligned(end, SHADOW_GRANULARITY)); + if (!IsAligned(ptr, SHADOW_GRANULARITY)) { + *(u8 *)MemToShadow(ptr) = + poison ? static_cast(ptr % SHADOW_GRANULARITY) : 0; + ptr |= SHADOW_GRANULARITY - 1; + ptr++; + } + for (; ptr < end; ptr += SHADOW_GRANULARITY) + *(u8*)MemToShadow(ptr) = poison ? kAsanIntraObjectRedzone : 0; +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +// Current implementation of __asan_(un)poison_memory_region doesn't check +// that user program (un)poisons the memory it owns. It poisons memory +// conservatively, and unpoisons progressively to make sure asan shadow +// mapping invariant is preserved (see detailed mapping description here: +// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm). +// +// * if user asks to poison region [left, right), the program poisons +// 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, uptr size) { + if (!flags()->allow_user_poisoning || size == 0) return; + uptr beg_addr = (uptr)addr; + uptr end_addr = beg_addr + size; + VPrintf(3, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr, + (void *)end_addr); + ShadowSegmentEndpoint beg(beg_addr); + ShadowSegmentEndpoint end(end_addr); + if (beg.chunk == end.chunk) { + CHECK_LT(beg.offset, end.offset); + s8 value = beg.value; + CHECK_EQ(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. + if (value > 0 && value <= end.offset) { + if (beg.offset > 0) { + *beg.chunk = Min(value, beg.offset); + } else { + *beg.chunk = kAsanUserPoisonedMemoryMagic; + } + } + return; + } + CHECK_LT(beg.chunk, end.chunk); + if (beg.offset > 0) { + // Mark bytes from beg.offset as unaddressable. + if (beg.value == 0) { + *beg.chunk = beg.offset; + } else { + *beg.chunk = Min(beg.value, beg.offset); + } + 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, uptr size) { + if (!flags()->allow_user_poisoning || size == 0) return; + uptr beg_addr = (uptr)addr; + uptr end_addr = beg_addr + size; + VPrintf(3, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr, + (void *)end_addr); + ShadowSegmentEndpoint beg(beg_addr); + ShadowSegmentEndpoint end(end_addr); + if (beg.chunk == end.chunk) { + CHECK_LT(beg.offset, end.offset); + s8 value = beg.value; + CHECK_EQ(value, end.value); + // We unpoison memory bytes up to enbytes up to end.offset if it is not + // unpoisoned already. + if (value != 0) { + *beg.chunk = Max(value, end.offset); + } + return; + } + CHECK_LT(beg.chunk, end.chunk); + if (beg.offset > 0) { + *beg.chunk = 0; + 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); + } +} + +int __asan_address_is_poisoned(void const volatile *addr) { + return __asan::AddressIsPoisoned((uptr)addr); +} + +uptr __asan_region_is_poisoned(uptr beg, uptr size) { + if (!size) return 0; + uptr end = beg + size; + if (SANITIZER_MYRIAD2) { + // On Myriad, address not in DRAM range need to be treated as + // unpoisoned. + if (!AddrIsInMem(beg) && !AddrIsInShadow(beg)) return 0; + if (!AddrIsInMem(end) && !AddrIsInShadow(end)) return 0; + } else { + if (!AddrIsInMem(beg)) return beg; + if (!AddrIsInMem(end)) return end; + } + CHECK_LT(beg, end); + uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY); + uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY); + uptr shadow_beg = MemToShadow(aligned_b); + uptr shadow_end = MemToShadow(aligned_e); + // First check the first and the last application bytes, + // then check the SHADOW_GRANULARITY-aligned region by calling + // mem_is_zero on the corresponding shadow. + if (!__asan::AddressIsPoisoned(beg) && + !__asan::AddressIsPoisoned(end - 1) && + (shadow_end <= shadow_beg || + __sanitizer::mem_is_zero((const char *)shadow_beg, + shadow_end - shadow_beg))) + return 0; + // The fast check failed, so we have a poisoned byte somewhere. + // Find it slowly. + for (; beg < end; beg++) + if (__asan::AddressIsPoisoned(beg)) + return beg; + UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found"); + return 0; +} + +#define CHECK_SMALL_REGION(p, size, isWrite) \ + do { \ + uptr __p = reinterpret_cast(p); \ + uptr __size = size; \ + if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \ + __asan::AddressIsPoisoned(__p + __size - 1))) { \ + GET_CURRENT_PC_BP_SP; \ + uptr __bad = __asan_region_is_poisoned(__p, __size); \ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\ + } \ + } while (false) + + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u16 __sanitizer_unaligned_load16(const uu16 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u32 __sanitizer_unaligned_load32(const uu32 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u64 __sanitizer_unaligned_load64(const uu64 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(uu16 *p, u16 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(uu32 *p, u32 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store64(uu64 *p, u64 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __asan_poison_cxx_array_cookie(uptr p) { + if (SANITIZER_WORDSIZE != 64) return; + if (!flags()->poison_array_cookie) return; + uptr s = MEM_TO_SHADOW(p); + *reinterpret_cast(s) = kAsanArrayCookieMagic; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +uptr __asan_load_cxx_array_cookie(uptr *p) { + if (SANITIZER_WORDSIZE != 64) return *p; + if (!flags()->poison_array_cookie) return *p; + uptr s = MEM_TO_SHADOW(reinterpret_cast(p)); + u8 sval = *reinterpret_cast(s); + if (sval == kAsanArrayCookieMagic) return *p; + // If sval is not kAsanArrayCookieMagic it can only be freed memory, + // which means that we are going to get double-free. So, return 0 to avoid + // infinite loop of destructors. We don't want to report a double-free here + // though, so print a warning just in case. + // CHECK_EQ(sval, kAsanHeapFreeMagic); + if (sval == kAsanHeapFreeMagic) { + Report("AddressSanitizer: loaded array cookie from free-d memory; " + "expect a double-free report\n"); + return 0; + } + // The cookie may remain unpoisoned if e.g. it comes from a custom + // operator new defined inside a class. + return *p; +} + +// This is a simplified version of __asan_(un)poison_memory_region, which +// assumes that left border of region to be poisoned is properly aligned. +static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { + if (size == 0) return; + uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1); + PoisonShadow(addr, aligned_size, + do_poison ? kAsanStackUseAfterScopeMagic : 0); + if (size == aligned_size) + return; + s8 end_offset = (s8)(size - aligned_size); + s8* shadow_end = (s8*)MemToShadow(addr + aligned_size); + s8 end_value = *shadow_end; + if (do_poison) { + // If possible, mark all the bytes mapping to last shadow byte as + // unaddressable. + if (end_value > 0 && end_value <= end_offset) + *shadow_end = (s8)kAsanStackUseAfterScopeMagic; + } else { + // If necessary, mark few first bytes mapping to last shadow byte + // as addressable + if (end_value != 0) + *shadow_end = Max(end_value, end_offset); + } +} + +void __asan_set_shadow_00(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0, size); +} + +void __asan_set_shadow_f1(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0xf1, size); +} + +void __asan_set_shadow_f2(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0xf2, size); +} + +void __asan_set_shadow_f3(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0xf3, size); +} + +void __asan_set_shadow_f5(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0xf5, size); +} + +void __asan_set_shadow_f8(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0xf8, size); +} + +void __asan_poison_stack_memory(uptr addr, uptr size) { + VReport(1, "poisoning: %p %zx\n", (void *)addr, size); + PoisonAlignedStackMemory(addr, size, true); +} + +void __asan_unpoison_stack_memory(uptr addr, uptr size) { + VReport(1, "unpoisoning: %p %zx\n", (void *)addr, size); + PoisonAlignedStackMemory(addr, size, false); +} + +void __sanitizer_annotate_contiguous_container(const void *beg_p, + const void *end_p, + const void *old_mid_p, + const void *new_mid_p) { + if (!flags()->detect_container_overflow) return; + VPrintf(2, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p, + new_mid_p); + uptr beg = reinterpret_cast(beg_p); + uptr end = reinterpret_cast(end_p); + uptr old_mid = reinterpret_cast(old_mid_p); + uptr new_mid = reinterpret_cast(new_mid_p); + uptr granularity = SHADOW_GRANULARITY; + if (!(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end && + IsAligned(beg, granularity))) { + GET_STACK_TRACE_FATAL_HERE; + ReportBadParamsToAnnotateContiguousContainer(beg, end, old_mid, new_mid, + &stack); + } + CHECK_LE(end - beg, + FIRST_32_SECOND_64(1UL << 30, 1ULL << 34)); // Sanity check. + + uptr a = RoundDownTo(Min(old_mid, new_mid), granularity); + uptr c = RoundUpTo(Max(old_mid, new_mid), granularity); + uptr d1 = RoundDownTo(old_mid, granularity); + // uptr d2 = RoundUpTo(old_mid, granularity); + // Currently we should be in this state: + // [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good. + // Make a quick sanity check that we are indeed in this state. + // + // FIXME: Two of these three checks are disabled until we fix + // https://github.com/google/sanitizers/issues/258. + // if (d1 != d2) + // CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1); + if (a + granularity <= d1) + CHECK_EQ(*(u8*)MemToShadow(a), 0); + // if (d2 + granularity <= c && c <= end) + // CHECK_EQ(*(u8 *)MemToShadow(c - granularity), + // kAsanContiguousContainerOOBMagic); + + uptr b1 = RoundDownTo(new_mid, granularity); + uptr b2 = RoundUpTo(new_mid, granularity); + // New state: + // [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good. + PoisonShadow(a, b1 - a, 0); + PoisonShadow(b2, c - b2, kAsanContiguousContainerOOBMagic); + if (b1 != b2) { + CHECK_EQ(b2 - b1, granularity); + *(u8*)MemToShadow(b1) = static_cast(new_mid - b1); + } +} + +const void *__sanitizer_contiguous_container_find_bad_address( + const void *beg_p, const void *mid_p, const void *end_p) { + if (!flags()->detect_container_overflow) + return nullptr; + uptr beg = reinterpret_cast(beg_p); + uptr end = reinterpret_cast(end_p); + uptr mid = reinterpret_cast(mid_p); + CHECK_LE(beg, mid); + CHECK_LE(mid, end); + // Check some bytes starting from beg, some bytes around mid, and some bytes + // ending with end. + uptr kMaxRangeToCheck = 32; + uptr r1_beg = beg; + uptr r1_end = Min(beg + kMaxRangeToCheck, mid); + uptr r2_beg = Max(beg, mid - kMaxRangeToCheck); + uptr r2_end = Min(end, mid + kMaxRangeToCheck); + uptr r3_beg = Max(end - kMaxRangeToCheck, mid); + uptr r3_end = end; + for (uptr i = r1_beg; i < r1_end; i++) + if (AddressIsPoisoned(i)) + return reinterpret_cast(i); + for (uptr i = r2_beg; i < mid; i++) + if (AddressIsPoisoned(i)) + return reinterpret_cast(i); + for (uptr i = mid; i < r2_end; i++) + if (!AddressIsPoisoned(i)) + return reinterpret_cast(i); + for (uptr i = r3_beg; i < r3_end; i++) + if (!AddressIsPoisoned(i)) + return reinterpret_cast(i); + return nullptr; +} + +int __sanitizer_verify_contiguous_container(const void *beg_p, + const void *mid_p, + const void *end_p) { + return __sanitizer_contiguous_container_find_bad_address(beg_p, mid_p, + end_p) == nullptr; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __asan_poison_intra_object_redzone(uptr ptr, uptr size) { + AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, true); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __asan_unpoison_intra_object_redzone(uptr ptr, uptr size) { + AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, false); +} + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +bool WordIsPoisoned(uptr addr) { + return (__asan_region_is_poisoned(addr, sizeof(uptr)) != 0); +} +} diff --git a/libsanitizer/asan/asan_poisoning.h b/libsanitizer/asan/asan_poisoning.h index 7e8c5886831..62dd9bd0edd 100644 --- a/libsanitizer/asan/asan_poisoning.h +++ b/libsanitizer/asan/asan_poisoning.h @@ -1,7 +1,8 @@ //===-- asan_poisoning.h ----------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -14,6 +15,7 @@ #include "asan_internal.h" #include "asan_mapping.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_platform.h" namespace __asan { @@ -37,6 +39,10 @@ void PoisonShadowPartialRightRedzone(uptr addr, ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, u8 value) { DCHECK(!value || CanPoisonMemory()); +#if SANITIZER_FUCHSIA + __sanitizer_fill_shadow(aligned_beg, aligned_size, value, + common_flags()->clear_shadow_mmap_threshold); +#else uptr shadow_beg = MEM_TO_SHADOW(aligned_beg); uptr shadow_end = MEM_TO_SHADOW( aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1; @@ -45,10 +51,6 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, // probably provide higher-level interface for these operations. // For now, just memset on Windows. if (value || SANITIZER_WINDOWS == 1 || - // TODO(mcgrathr): Fuchsia doesn't allow the shadow mapping to be - // changed at all. It doesn't currently have an efficient means - // to zero a bunch of pages, but maybe we should add one. - SANITIZER_FUCHSIA == 1 || // RTEMS doesn't have have pages, let alone a fast way to zero // them, so default to memset. SANITIZER_RTEMS == 1 || @@ -71,6 +73,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, ReserveShadowMemoryRange(page_beg, page_end - 1, nullptr); } } +#endif // SANITIZER_FUCHSIA } ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( diff --git a/libsanitizer/asan/asan_posix.cc b/libsanitizer/asan/asan_posix.cc deleted file mode 100644 index d765dc79c51..00000000000 --- a/libsanitizer/asan/asan_posix.cc +++ /dev/null @@ -1,70 +0,0 @@ -//===-- asan_posix.cc -----------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Posix-specific details. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_POSIX - -#include "asan_internal.h" -#include "asan_interceptors.h" -#include "asan_mapping.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_posix.h" -#include "sanitizer_common/sanitizer_procmaps.h" - -#include -#include -#include -#include -#include - -namespace __asan { - -void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { - StartReportDeadlySignal(); - SignalContext sig(siginfo, context); - ReportDeadlySignal(sig); -} - -// ---------------------- 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_EQ(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); -} - -void PlatformTSDDtor(void *tsd) { - AsanThreadContext *context = (AsanThreadContext*)tsd; - if (context->destructor_iterations > 1) { - context->destructor_iterations--; - CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); - return; - } - AsanThread::TSDDtor(tsd); -} -} // namespace __asan - -#endif // SANITIZER_POSIX diff --git a/libsanitizer/asan/asan_posix.cpp b/libsanitizer/asan/asan_posix.cpp new file mode 100644 index 00000000000..920d216624a --- /dev/null +++ b/libsanitizer/asan/asan_posix.cpp @@ -0,0 +1,117 @@ +//===-- asan_posix.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Posix-specific details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_POSIX + +#include "asan_internal.h" +#include "asan_interceptors.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_posix.h" +#include "sanitizer_common/sanitizer_procmaps.h" + +#include +#include +#include +#include +#include + +namespace __asan { + +void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { + StartReportDeadlySignal(); + SignalContext sig(siginfo, context); + ReportDeadlySignal(sig); +} + +// ---------------------- TSD ---------------- {{{1 + +#if SANITIZER_NETBSD && !ASAN_DYNAMIC +// Thread Static Data cannot be used in early static ASan init on NetBSD. +// Reuse the Asan TSD API for compatibility with existing code +// with an alternative implementation. + +static void (*tsd_destructor)(void *tsd) = nullptr; + +struct tsd_key { + tsd_key() : key(nullptr) {} + ~tsd_key() { + CHECK(tsd_destructor); + if (key) + (*tsd_destructor)(key); + } + void *key; +}; + +static thread_local struct tsd_key key; + +void AsanTSDInit(void (*destructor)(void *tsd)) { + CHECK(!tsd_destructor); + tsd_destructor = destructor; +} + +void *AsanTSDGet() { + CHECK(tsd_destructor); + return key.key; +} + +void AsanTSDSet(void *tsd) { + CHECK(tsd_destructor); + CHECK(tsd); + CHECK(!key.key); + key.key = tsd; +} + +void PlatformTSDDtor(void *tsd) { + CHECK(tsd_destructor); + CHECK_EQ(key.key, tsd); + key.key = nullptr; + // Make sure that signal handler can not see a stale current thread pointer. + atomic_signal_fence(memory_order_seq_cst); + AsanThread::TSDDtor(tsd); +} +#else +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_EQ(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); +} + +void PlatformTSDDtor(void *tsd) { + AsanThreadContext *context = (AsanThreadContext*)tsd; + if (context->destructor_iterations > 1) { + context->destructor_iterations--; + CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); + return; + } + AsanThread::TSDDtor(tsd); +} +#endif +} // namespace __asan + +#endif // SANITIZER_POSIX diff --git a/libsanitizer/asan/asan_preinit.cc b/libsanitizer/asan/asan_preinit.cc deleted file mode 100644 index 6cb115bd369..00000000000 --- a/libsanitizer/asan/asan_preinit.cc +++ /dev/null @@ -1,23 +0,0 @@ -//===-- asan_preinit.cc ---------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Call __asan_init at the very early stage of process startup. -//===----------------------------------------------------------------------===// -#include "asan_internal.h" - -using namespace __asan; - -#if SANITIZER_CAN_USE_PREINIT_ARRAY - // The symbol is called __local_asan_preinit, because it's not intended to be - // exported. - // This code linked into the main executable when -fsanitize=address is in - // the link flags. It can only use exported interface functions. - __attribute__((section(".preinit_array"), used)) - void (*__local_asan_preinit)(void) = __asan_init; -#endif diff --git a/libsanitizer/asan/asan_preinit.cpp b/libsanitizer/asan/asan_preinit.cpp new file mode 100644 index 00000000000..b07556ec96f --- /dev/null +++ b/libsanitizer/asan/asan_preinit.cpp @@ -0,0 +1,24 @@ +//===-- asan_preinit.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Call __asan_init at the very early stage of process startup. +//===----------------------------------------------------------------------===// +#include "asan_internal.h" + +using namespace __asan; + +#if SANITIZER_CAN_USE_PREINIT_ARRAY + // The symbol is called __local_asan_preinit, because it's not intended to be + // exported. + // This code linked into the main executable when -fsanitize=address is in + // the link flags. It can only use exported interface functions. + __attribute__((section(".preinit_array"), used)) + void (*__local_asan_preinit)(void) = __asan_init; +#endif diff --git a/libsanitizer/asan/asan_premap_shadow.cc b/libsanitizer/asan/asan_premap_shadow.cc deleted file mode 100644 index 4273ae5e389..00000000000 --- a/libsanitizer/asan/asan_premap_shadow.cc +++ /dev/null @@ -1,77 +0,0 @@ -//===-- asan_premap_shadow.cc ---------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Reserve shadow memory with an ifunc resolver. -//===----------------------------------------------------------------------===// - -#include "asan_mapping.h" - -#if ASAN_PREMAP_SHADOW - -#include "asan_premap_shadow.h" -#include "sanitizer_common/sanitizer_posix.h" - -namespace __asan { - -// The code in this file needs to run in an unrelocated binary. It may not -// access any external symbol, including its own non-hidden globals. - -// Conservative upper limit. -uptr PremapShadowSize() { - uptr granularity = GetMmapGranularity(); - return RoundUpTo(GetMaxVirtualAddress() >> SHADOW_SCALE, granularity); -} - -// Returns an address aligned to 8 pages, such that one page on the left and -// PremapShadowSize() bytes on the right of it are mapped r/o. -uptr PremapShadow() { - uptr granularity = GetMmapGranularity(); - uptr alignment = granularity * 8; - uptr left_padding = granularity; - uptr shadow_size = PremapShadowSize(); - uptr map_size = shadow_size + left_padding + alignment; - - uptr map_start = (uptr)MmapNoAccess(map_size); - CHECK_NE(map_start, ~(uptr)0); - - uptr shadow_start = RoundUpTo(map_start + left_padding, alignment); - uptr shadow_end = shadow_start + shadow_size; - internal_munmap(reinterpret_cast(map_start), - shadow_start - left_padding - map_start); - internal_munmap(reinterpret_cast(shadow_end), - map_start + map_size - shadow_end); - return shadow_start; -} - -bool PremapShadowFailed() { - uptr shadow = reinterpret_cast(&__asan_shadow); - uptr resolver = reinterpret_cast(&__asan_premap_shadow); - // shadow == resolver is how Android KitKat and older handles ifunc. - // shadow == 0 just in case. - if (shadow == 0 || shadow == resolver) - return true; - return false; -} -} // namespace __asan - -extern "C" { -decltype(__asan_shadow)* __asan_premap_shadow() { - // The resolver may be called multiple times. Map the shadow just once. - static uptr premapped_shadow = 0; - if (!premapped_shadow) premapped_shadow = __asan::PremapShadow(); - return reinterpret_cast(premapped_shadow); -} - -// __asan_shadow is a "function" that has the same address as the first byte of -// the shadow mapping. -INTERFACE_ATTRIBUTE __attribute__((ifunc("__asan_premap_shadow"))) void -__asan_shadow(); -} - -#endif // ASAN_PREMAP_SHADOW diff --git a/libsanitizer/asan/asan_premap_shadow.cpp b/libsanitizer/asan/asan_premap_shadow.cpp new file mode 100644 index 00000000000..7835e99748f --- /dev/null +++ b/libsanitizer/asan/asan_premap_shadow.cpp @@ -0,0 +1,78 @@ +//===-- asan_premap_shadow.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Reserve shadow memory with an ifunc resolver. +//===----------------------------------------------------------------------===// + +#include "asan_mapping.h" + +#if ASAN_PREMAP_SHADOW + +#include "asan_premap_shadow.h" +#include "sanitizer_common/sanitizer_posix.h" + +namespace __asan { + +// The code in this file needs to run in an unrelocated binary. It may not +// access any external symbol, including its own non-hidden globals. + +// Conservative upper limit. +uptr PremapShadowSize() { + uptr granularity = GetMmapGranularity(); + return RoundUpTo(GetMaxVirtualAddress() >> SHADOW_SCALE, granularity); +} + +// Returns an address aligned to 8 pages, such that one page on the left and +// PremapShadowSize() bytes on the right of it are mapped r/o. +uptr PremapShadow() { + uptr granularity = GetMmapGranularity(); + uptr alignment = granularity * 8; + uptr left_padding = granularity; + uptr shadow_size = PremapShadowSize(); + uptr map_size = shadow_size + left_padding + alignment; + + uptr map_start = (uptr)MmapNoAccess(map_size); + CHECK_NE(map_start, ~(uptr)0); + + uptr shadow_start = RoundUpTo(map_start + left_padding, alignment); + uptr shadow_end = shadow_start + shadow_size; + internal_munmap(reinterpret_cast(map_start), + shadow_start - left_padding - map_start); + internal_munmap(reinterpret_cast(shadow_end), + map_start + map_size - shadow_end); + return shadow_start; +} + +bool PremapShadowFailed() { + uptr shadow = reinterpret_cast(&__asan_shadow); + uptr resolver = reinterpret_cast(&__asan_premap_shadow); + // shadow == resolver is how Android KitKat and older handles ifunc. + // shadow == 0 just in case. + if (shadow == 0 || shadow == resolver) + return true; + return false; +} +} // namespace __asan + +extern "C" { +decltype(__asan_shadow)* __asan_premap_shadow() { + // The resolver may be called multiple times. Map the shadow just once. + static uptr premapped_shadow = 0; + if (!premapped_shadow) premapped_shadow = __asan::PremapShadow(); + return reinterpret_cast(premapped_shadow); +} + +// __asan_shadow is a "function" that has the same address as the first byte of +// the shadow mapping. +INTERFACE_ATTRIBUTE __attribute__((ifunc("__asan_premap_shadow"))) void +__asan_shadow(); +} + +#endif // ASAN_PREMAP_SHADOW diff --git a/libsanitizer/asan/asan_premap_shadow.h b/libsanitizer/asan/asan_premap_shadow.h index 345b56ea6a6..c9c886e8dca 100644 --- a/libsanitizer/asan/asan_premap_shadow.h +++ b/libsanitizer/asan/asan_premap_shadow.h @@ -1,7 +1,8 @@ //===-- asan_mapping.h ------------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc deleted file mode 100644 index 787b6890a34..00000000000 --- a/libsanitizer/asan/asan_report.cc +++ /dev/null @@ -1,550 +0,0 @@ -//===-- asan_report.cc ----------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// This file contains error reporting code. -//===----------------------------------------------------------------------===// - -#include "asan_errors.h" -#include "asan_flags.h" -#include "asan_descriptions.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "asan_report.h" -#include "asan_scariness_score.h" -#include "asan_stack.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_flags.h" -#include "sanitizer_common/sanitizer_report_decorator.h" -#include "sanitizer_common/sanitizer_stackdepot.h" -#include "sanitizer_common/sanitizer_symbolizer.h" - -namespace __asan { - -// -------------------- User-specified callbacks ----------------- {{{1 -static void (*error_report_callback)(const char*); -static char *error_message_buffer = nullptr; -static uptr error_message_buffer_pos = 0; -static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED); -static const unsigned kAsanBuggyPcPoolSize = 25; -static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize]; - -void AppendToErrorMessageBuffer(const char *buffer) { - BlockingMutexLock l(&error_message_buf_mutex); - if (!error_message_buffer) { - error_message_buffer = - (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__); - error_message_buffer_pos = 0; - } - uptr length = internal_strlen(buffer); - RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos); - uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos; - internal_strncpy(error_message_buffer + error_message_buffer_pos, - buffer, remaining); - error_message_buffer[kErrorMessageBufferSize - 1] = '\0'; - // FIXME: reallocate the buffer instead of truncating the message. - error_message_buffer_pos += Min(remaining, length); -} - -// ---------------------- Helper functions ----------------------- {{{1 - -void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte, - bool in_shadow, const char *after) { - Decorator d; - str->append("%s%s%x%x%s%s", before, - in_shadow ? d.ShadowByte(byte) : d.MemoryByte(), byte >> 4, - byte & 15, d.Default(), after); -} - -static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, - const char *zone_name) { - if (zone_ptr) { - if (zone_name) { - Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n", - ptr, zone_ptr, zone_name); - } else { - Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n", - ptr, zone_ptr); - } - } else { - Printf("malloc_zone_from_ptr(%p) = 0\n", ptr); - } -} - -// ---------------------- Address Descriptions ------------------- {{{1 - -bool ParseFrameDescription(const char *frame_descr, - InternalMmapVector *vars) { - CHECK(frame_descr); - const char *p; - // This string is created by the compiler and has the following form: - // "n alloc_1 alloc_2 ... alloc_n" - // where alloc_i looks like "offset size len ObjectName" - // or "offset size len ObjectName:line". - uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10); - if (n_objects == 0) - return false; - - for (uptr i = 0; i < n_objects; i++) { - uptr beg = (uptr)internal_simple_strtoll(p, &p, 10); - uptr size = (uptr)internal_simple_strtoll(p, &p, 10); - uptr len = (uptr)internal_simple_strtoll(p, &p, 10); - if (beg == 0 || size == 0 || *p != ' ') { - return false; - } - p++; - char *colon_pos = internal_strchr(p, ':'); - uptr line = 0; - uptr name_len = len; - if (colon_pos != nullptr && colon_pos < p + len) { - name_len = colon_pos - p; - line = (uptr)internal_simple_strtoll(colon_pos + 1, nullptr, 10); - } - StackVarDescr var = {beg, size, p, name_len, line}; - vars->push_back(var); - p += len; - } - - return true; -} - -// -------------------- Different kinds of reports ----------------- {{{1 - -// Use ScopedInErrorReport to run common actions just before and -// immediately after printing error report. -class ScopedInErrorReport { - public: - explicit ScopedInErrorReport(bool fatal = false) - : halt_on_error_(fatal || flags()->halt_on_error) { - // Make sure the registry and sanitizer report mutexes are locked while - // we're printing an error report. - // We can lock them only here to avoid self-deadlock in case of - // recursive reports. - asanThreadRegistry().Lock(); - Printf( - "=================================================================\n"); - } - - ~ScopedInErrorReport() { - if (halt_on_error_ && !__sanitizer_acquire_crash_state()) { - asanThreadRegistry().Unlock(); - return; - } - ASAN_ON_ERROR(); - if (current_error_.IsValid()) current_error_.Print(); - - // Make sure the current thread is announced. - DescribeThread(GetCurrentThread()); - // We may want to grab this lock again when printing stats. - asanThreadRegistry().Unlock(); - // Print memory stats. - if (flags()->print_stats) - __asan_print_accumulated_stats(); - - if (common_flags()->print_cmdline) - PrintCmdline(); - - if (common_flags()->print_module_map == 2) PrintModuleMap(); - - // Copy the message buffer so that we could start logging without holding a - // lock that gets aquired during printing. - InternalMmapVector buffer_copy(kErrorMessageBufferSize); - { - BlockingMutexLock l(&error_message_buf_mutex); - internal_memcpy(buffer_copy.data(), - error_message_buffer, kErrorMessageBufferSize); - } - - LogFullErrorReport(buffer_copy.data()); - - if (error_report_callback) { - error_report_callback(buffer_copy.data()); - } - - if (halt_on_error_ && common_flags()->abort_on_error) { - // On Android the message is truncated to 512 characters. - // FIXME: implement "compact" error format, possibly without, or with - // highly compressed stack traces? - // FIXME: or just use the summary line as abort message? - SetAbortMessage(buffer_copy.data()); - } - - // In halt_on_error = false mode, reset the current error object (before - // unlocking). - if (!halt_on_error_) - internal_memset(¤t_error_, 0, sizeof(current_error_)); - - if (halt_on_error_) { - Report("ABORTING\n"); - Die(); - } - } - - void ReportError(const ErrorDescription &description) { - // Can only report one error per ScopedInErrorReport. - CHECK_EQ(current_error_.kind, kErrorKindInvalid); - current_error_ = description; - } - - static ErrorDescription &CurrentError() { - return current_error_; - } - - private: - ScopedErrorReportLock error_report_lock_; - // Error currently being reported. This enables the destructor to interact - // with the debugger and point it to an error description. - static ErrorDescription current_error_; - bool halt_on_error_; -}; - -ErrorDescription ScopedInErrorReport::current_error_(LINKER_INITIALIZED); - -void ReportDeadlySignal(const SignalContext &sig) { - ScopedInErrorReport in_report(/*fatal*/ true); - ErrorDeadlySignal error(GetCurrentTidOrInvalid(), sig); - in_report.ReportError(error); -} - -void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) { - ScopedInErrorReport in_report; - ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr); - in_report.ReportError(error); -} - -void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size, - uptr delete_alignment, - BufferedStackTrace *free_stack) { - ScopedInErrorReport in_report; - ErrorNewDeleteTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr, - delete_size, delete_alignment); - in_report.ReportError(error); -} - -void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) { - ScopedInErrorReport in_report; - ErrorFreeNotMalloced error(GetCurrentTidOrInvalid(), free_stack, addr); - in_report.ReportError(error); -} - -void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, - AllocType alloc_type, - AllocType dealloc_type) { - ScopedInErrorReport in_report; - ErrorAllocTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr, - alloc_type, dealloc_type); - in_report.ReportError(error); -} - -void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) { - ScopedInErrorReport in_report; - ErrorMallocUsableSizeNotOwned error(GetCurrentTidOrInvalid(), stack, addr); - in_report.ReportError(error); -} - -void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report; - ErrorSanitizerGetAllocatedSizeNotOwned error(GetCurrentTidOrInvalid(), stack, - addr); - in_report.ReportError(error); -} - -void ReportCallocOverflow(uptr count, uptr size, BufferedStackTrace *stack) { - ScopedInErrorReport in_report(/*fatal*/ true); - ErrorCallocOverflow error(GetCurrentTidOrInvalid(), stack, count, size); - in_report.ReportError(error); -} - -void ReportPvallocOverflow(uptr size, BufferedStackTrace *stack) { - ScopedInErrorReport in_report(/*fatal*/ true); - ErrorPvallocOverflow error(GetCurrentTidOrInvalid(), stack, size); - in_report.ReportError(error); -} - -void ReportInvalidAllocationAlignment(uptr alignment, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report(/*fatal*/ true); - ErrorInvalidAllocationAlignment error(GetCurrentTidOrInvalid(), stack, - alignment); - in_report.ReportError(error); -} - -void ReportInvalidAlignedAllocAlignment(uptr size, uptr alignment, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report(/*fatal*/ true); - ErrorInvalidAlignedAllocAlignment error(GetCurrentTidOrInvalid(), stack, - size, alignment); - in_report.ReportError(error); -} - -void ReportInvalidPosixMemalignAlignment(uptr alignment, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report(/*fatal*/ true); - ErrorInvalidPosixMemalignAlignment error(GetCurrentTidOrInvalid(), stack, - alignment); - in_report.ReportError(error); -} - -void ReportAllocationSizeTooBig(uptr user_size, uptr total_size, uptr max_size, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report(/*fatal*/ true); - ErrorAllocationSizeTooBig error(GetCurrentTidOrInvalid(), stack, user_size, - total_size, max_size); - in_report.ReportError(error); -} - -void ReportRssLimitExceeded(BufferedStackTrace *stack) { - ScopedInErrorReport in_report(/*fatal*/ true); - ErrorRssLimitExceeded error(GetCurrentTidOrInvalid(), stack); - in_report.ReportError(error); -} - -void ReportOutOfMemory(uptr requested_size, BufferedStackTrace *stack) { - ScopedInErrorReport in_report(/*fatal*/ true); - ErrorOutOfMemory error(GetCurrentTidOrInvalid(), stack, requested_size); - in_report.ReportError(error); -} - -void ReportStringFunctionMemoryRangesOverlap(const char *function, - const char *offset1, uptr length1, - const char *offset2, uptr length2, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report; - ErrorStringFunctionMemoryRangesOverlap error( - GetCurrentTidOrInvalid(), stack, (uptr)offset1, length1, (uptr)offset2, - length2, function); - in_report.ReportError(error); -} - -void ReportStringFunctionSizeOverflow(uptr offset, uptr size, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report; - ErrorStringFunctionSizeOverflow error(GetCurrentTidOrInvalid(), stack, offset, - size); - in_report.ReportError(error); -} - -void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, - uptr old_mid, uptr new_mid, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report; - ErrorBadParamsToAnnotateContiguousContainer error( - GetCurrentTidOrInvalid(), stack, beg, end, old_mid, new_mid); - in_report.ReportError(error); -} - -void ReportODRViolation(const __asan_global *g1, u32 stack_id1, - const __asan_global *g2, u32 stack_id2) { - ScopedInErrorReport in_report; - ErrorODRViolation error(GetCurrentTidOrInvalid(), g1, stack_id1, g2, - stack_id2); - in_report.ReportError(error); -} - -// ----------------------- CheckForInvalidPointerPair ----------- {{{1 -static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, - uptr a1, uptr a2) { - ScopedInErrorReport in_report; - ErrorInvalidPointerPair error(GetCurrentTidOrInvalid(), pc, bp, sp, a1, a2); - in_report.ReportError(error); -} - -static bool IsInvalidPointerPair(uptr a1, uptr a2) { - if (a1 == a2) - return false; - - // 256B in shadow memory can be iterated quite fast - static const uptr kMaxOffset = 2048; - - uptr left = a1 < a2 ? a1 : a2; - uptr right = a1 < a2 ? a2 : a1; - uptr offset = right - left; - if (offset <= kMaxOffset) - return __asan_region_is_poisoned(left, offset); - - AsanThread *t = GetCurrentThread(); - - // check whether left is a stack memory pointer - if (uptr shadow_offset1 = t->GetStackVariableShadowStart(left)) { - uptr shadow_offset2 = t->GetStackVariableShadowStart(right); - return shadow_offset2 == 0 || shadow_offset1 != shadow_offset2; - } - - // check whether left is a heap memory address - HeapAddressDescription hdesc1, hdesc2; - if (GetHeapAddressInformation(left, 0, &hdesc1) && - hdesc1.chunk_access.access_type == kAccessTypeInside) - return !GetHeapAddressInformation(right, 0, &hdesc2) || - hdesc2.chunk_access.access_type != kAccessTypeInside || - hdesc1.chunk_access.chunk_begin != hdesc2.chunk_access.chunk_begin; - - // check whether left is an address of a global variable - GlobalAddressDescription gdesc1, gdesc2; - if (GetGlobalAddressInformation(left, 0, &gdesc1)) - return !GetGlobalAddressInformation(right - 1, 0, &gdesc2) || - !gdesc1.PointsInsideTheSameVariable(gdesc2); - - if (t->GetStackVariableShadowStart(right) || - GetHeapAddressInformation(right, 0, &hdesc2) || - GetGlobalAddressInformation(right - 1, 0, &gdesc2)) - return true; - - // At this point we know nothing about both a1 and a2 addresses. - return false; -} - -static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { - switch (flags()->detect_invalid_pointer_pairs) { - case 0 : return; - case 1 : if (p1 == nullptr || p2 == nullptr) return; break; - } - - uptr a1 = reinterpret_cast(p1); - uptr a2 = reinterpret_cast(p2); - - if (IsInvalidPointerPair(a1, a2)) { - GET_CALLER_PC_BP_SP; - ReportInvalidPointerPair(pc, bp, sp, a1, a2); - } -} -// ----------------------- Mac-specific reports ----------------- {{{1 - -void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report; - Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" - "This is an unrecoverable problem, exiting now.\n", - addr); - PrintZoneForPointer(addr, zone_ptr, zone_name); - stack->Print(); - DescribeAddressIfHeap(addr); -} - -// -------------- SuppressErrorReport -------------- {{{1 -// Avoid error reports duplicating for ASan recover mode. -static bool SuppressErrorReport(uptr pc) { - if (!common_flags()->suppress_equal_pcs) return false; - for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) { - uptr cmp = atomic_load_relaxed(&AsanBuggyPcPool[i]); - if (cmp == 0 && atomic_compare_exchange_strong(&AsanBuggyPcPool[i], &cmp, - pc, memory_order_relaxed)) - return false; - if (cmp == pc) return true; - } - Die(); -} - -void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, - uptr access_size, u32 exp, bool fatal) { - if (!fatal && SuppressErrorReport(pc)) return; - ENABLE_FRAME_POINTER; - - // Optimization experiments. - // The experiments can be used to evaluate potential optimizations that remove - // instrumentation (assess false negatives). Instead of completely removing - // some instrumentation, compiler can emit special calls into runtime - // (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass - // mask of experiments (exp). - // The reaction to a non-zero value of exp is to be defined. - (void)exp; - - ScopedInErrorReport in_report(fatal); - ErrorGeneric error(GetCurrentTidOrInvalid(), pc, bp, sp, addr, is_write, - access_size); - in_report.ReportError(error); -} - -} // namespace __asan - -// --------------------------- Interface --------------------- {{{1 -using namespace __asan; // NOLINT - -void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, - uptr access_size, u32 exp) { - ENABLE_FRAME_POINTER; - bool fatal = flags()->halt_on_error; - ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal); -} - -void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { - BlockingMutexLock l(&error_message_buf_mutex); - error_report_callback = callback; -} - -void __asan_describe_address(uptr addr) { - // Thread registry must be locked while we're describing an address. - asanThreadRegistry().Lock(); - PrintAddressDescription(addr, 1, ""); - asanThreadRegistry().Unlock(); -} - -int __asan_report_present() { - return ScopedInErrorReport::CurrentError().kind != kErrorKindInvalid; -} - -uptr __asan_get_report_pc() { - if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) - return ScopedInErrorReport::CurrentError().Generic.pc; - return 0; -} - -uptr __asan_get_report_bp() { - if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) - return ScopedInErrorReport::CurrentError().Generic.bp; - return 0; -} - -uptr __asan_get_report_sp() { - if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) - return ScopedInErrorReport::CurrentError().Generic.sp; - return 0; -} - -uptr __asan_get_report_address() { - ErrorDescription &err = ScopedInErrorReport::CurrentError(); - if (err.kind == kErrorKindGeneric) - return err.Generic.addr_description.Address(); - else if (err.kind == kErrorKindDoubleFree) - return err.DoubleFree.addr_description.addr; - return 0; -} - -int __asan_get_report_access_type() { - if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) - return ScopedInErrorReport::CurrentError().Generic.is_write; - return 0; -} - -uptr __asan_get_report_access_size() { - if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) - return ScopedInErrorReport::CurrentError().Generic.access_size; - return 0; -} - -const char *__asan_get_report_description() { - if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) - return ScopedInErrorReport::CurrentError().Generic.bug_descr; - return ScopedInErrorReport::CurrentError().Base.scariness.GetDescription(); -} - -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_ptr_sub(void *a, void *b) { - CheckForInvalidPointerPair(a, b); -} -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_ptr_cmp(void *a, void *b) { - CheckForInvalidPointerPair(a, b); -} -} // extern "C" - -// Provide default implementation of __asan_on_error that does nothing -// and may be overriden by user. -SANITIZER_INTERFACE_WEAK_DEF(void, __asan_on_error, void) {} diff --git a/libsanitizer/asan/asan_report.cpp b/libsanitizer/asan/asan_report.cpp new file mode 100644 index 00000000000..d36b0b4c594 --- /dev/null +++ b/libsanitizer/asan/asan_report.cpp @@ -0,0 +1,558 @@ +//===-- asan_report.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file contains error reporting code. +//===----------------------------------------------------------------------===// + +#include "asan_errors.h" +#include "asan_flags.h" +#include "asan_descriptions.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_scariness_score.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_report_decorator.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +namespace __asan { + +// -------------------- User-specified callbacks ----------------- {{{1 +static void (*error_report_callback)(const char*); +static char *error_message_buffer = nullptr; +static uptr error_message_buffer_pos = 0; +static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED); +static const unsigned kAsanBuggyPcPoolSize = 25; +static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize]; + +void AppendToErrorMessageBuffer(const char *buffer) { + BlockingMutexLock l(&error_message_buf_mutex); + if (!error_message_buffer) { + error_message_buffer = + (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__); + error_message_buffer_pos = 0; + } + uptr length = internal_strlen(buffer); + RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos); + uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos; + internal_strncpy(error_message_buffer + error_message_buffer_pos, + buffer, remaining); + error_message_buffer[kErrorMessageBufferSize - 1] = '\0'; + // FIXME: reallocate the buffer instead of truncating the message. + error_message_buffer_pos += Min(remaining, length); +} + +// ---------------------- Helper functions ----------------------- {{{1 + +void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte, + bool in_shadow, const char *after) { + Decorator d; + str->append("%s%s%x%x%s%s", before, + in_shadow ? d.ShadowByte(byte) : d.MemoryByte(), byte >> 4, + byte & 15, d.Default(), after); +} + +static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, + const char *zone_name) { + if (zone_ptr) { + if (zone_name) { + Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n", + ptr, zone_ptr, zone_name); + } else { + Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n", + ptr, zone_ptr); + } + } else { + Printf("malloc_zone_from_ptr(%p) = 0\n", ptr); + } +} + +// ---------------------- Address Descriptions ------------------- {{{1 + +bool ParseFrameDescription(const char *frame_descr, + InternalMmapVector *vars) { + CHECK(frame_descr); + const char *p; + // This string is created by the compiler and has the following form: + // "n alloc_1 alloc_2 ... alloc_n" + // where alloc_i looks like "offset size len ObjectName" + // or "offset size len ObjectName:line". + uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10); + if (n_objects == 0) + return false; + + for (uptr i = 0; i < n_objects; i++) { + uptr beg = (uptr)internal_simple_strtoll(p, &p, 10); + uptr size = (uptr)internal_simple_strtoll(p, &p, 10); + uptr len = (uptr)internal_simple_strtoll(p, &p, 10); + if (beg == 0 || size == 0 || *p != ' ') { + return false; + } + p++; + char *colon_pos = internal_strchr(p, ':'); + uptr line = 0; + uptr name_len = len; + if (colon_pos != nullptr && colon_pos < p + len) { + name_len = colon_pos - p; + line = (uptr)internal_simple_strtoll(colon_pos + 1, nullptr, 10); + } + StackVarDescr var = {beg, size, p, name_len, line}; + vars->push_back(var); + p += len; + } + + return true; +} + +// -------------------- Different kinds of reports ----------------- {{{1 + +// Use ScopedInErrorReport to run common actions just before and +// immediately after printing error report. +class ScopedInErrorReport { + public: + explicit ScopedInErrorReport(bool fatal = false) + : halt_on_error_(fatal || flags()->halt_on_error) { + // Make sure the registry and sanitizer report mutexes are locked while + // we're printing an error report. + // We can lock them only here to avoid self-deadlock in case of + // recursive reports. + asanThreadRegistry().Lock(); + Printf( + "=================================================================\n"); + } + + ~ScopedInErrorReport() { + if (halt_on_error_ && !__sanitizer_acquire_crash_state()) { + asanThreadRegistry().Unlock(); + return; + } + ASAN_ON_ERROR(); + if (current_error_.IsValid()) current_error_.Print(); + + // Make sure the current thread is announced. + DescribeThread(GetCurrentThread()); + // We may want to grab this lock again when printing stats. + asanThreadRegistry().Unlock(); + // Print memory stats. + if (flags()->print_stats) + __asan_print_accumulated_stats(); + + if (common_flags()->print_cmdline) + PrintCmdline(); + + if (common_flags()->print_module_map == 2) PrintModuleMap(); + + // Copy the message buffer so that we could start logging without holding a + // lock that gets aquired during printing. + InternalMmapVector buffer_copy(kErrorMessageBufferSize); + { + BlockingMutexLock l(&error_message_buf_mutex); + internal_memcpy(buffer_copy.data(), + error_message_buffer, kErrorMessageBufferSize); + } + + LogFullErrorReport(buffer_copy.data()); + + if (error_report_callback) { + error_report_callback(buffer_copy.data()); + } + + if (halt_on_error_ && common_flags()->abort_on_error) { + // On Android the message is truncated to 512 characters. + // FIXME: implement "compact" error format, possibly without, or with + // highly compressed stack traces? + // FIXME: or just use the summary line as abort message? + SetAbortMessage(buffer_copy.data()); + } + + // In halt_on_error = false mode, reset the current error object (before + // unlocking). + if (!halt_on_error_) + internal_memset(¤t_error_, 0, sizeof(current_error_)); + + if (halt_on_error_) { + Report("ABORTING\n"); + Die(); + } + } + + void ReportError(const ErrorDescription &description) { + // Can only report one error per ScopedInErrorReport. + CHECK_EQ(current_error_.kind, kErrorKindInvalid); + internal_memcpy(¤t_error_, &description, sizeof(current_error_)); + } + + static ErrorDescription &CurrentError() { + return current_error_; + } + + private: + ScopedErrorReportLock error_report_lock_; + // Error currently being reported. This enables the destructor to interact + // with the debugger and point it to an error description. + static ErrorDescription current_error_; + bool halt_on_error_; +}; + +ErrorDescription ScopedInErrorReport::current_error_(LINKER_INITIALIZED); + +void ReportDeadlySignal(const SignalContext &sig) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorDeadlySignal error(GetCurrentTidOrInvalid(), sig); + in_report.ReportError(error); +} + +void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) { + ScopedInErrorReport in_report; + ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr); + in_report.ReportError(error); +} + +void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size, + uptr delete_alignment, + BufferedStackTrace *free_stack) { + ScopedInErrorReport in_report; + ErrorNewDeleteTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr, + delete_size, delete_alignment); + in_report.ReportError(error); +} + +void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) { + ScopedInErrorReport in_report; + ErrorFreeNotMalloced error(GetCurrentTidOrInvalid(), free_stack, addr); + in_report.ReportError(error); +} + +void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, + AllocType alloc_type, + AllocType dealloc_type) { + ScopedInErrorReport in_report; + ErrorAllocTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr, + alloc_type, dealloc_type); + in_report.ReportError(error); +} + +void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) { + ScopedInErrorReport in_report; + ErrorMallocUsableSizeNotOwned error(GetCurrentTidOrInvalid(), stack, addr); + in_report.ReportError(error); +} + +void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, + BufferedStackTrace *stack) { + ScopedInErrorReport in_report; + ErrorSanitizerGetAllocatedSizeNotOwned error(GetCurrentTidOrInvalid(), stack, + addr); + in_report.ReportError(error); +} + +void ReportCallocOverflow(uptr count, uptr size, BufferedStackTrace *stack) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorCallocOverflow error(GetCurrentTidOrInvalid(), stack, count, size); + in_report.ReportError(error); +} + +void ReportReallocArrayOverflow(uptr count, uptr size, + BufferedStackTrace *stack) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorReallocArrayOverflow error(GetCurrentTidOrInvalid(), stack, count, size); + in_report.ReportError(error); +} + +void ReportPvallocOverflow(uptr size, BufferedStackTrace *stack) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorPvallocOverflow error(GetCurrentTidOrInvalid(), stack, size); + in_report.ReportError(error); +} + +void ReportInvalidAllocationAlignment(uptr alignment, + BufferedStackTrace *stack) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorInvalidAllocationAlignment error(GetCurrentTidOrInvalid(), stack, + alignment); + in_report.ReportError(error); +} + +void ReportInvalidAlignedAllocAlignment(uptr size, uptr alignment, + BufferedStackTrace *stack) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorInvalidAlignedAllocAlignment error(GetCurrentTidOrInvalid(), stack, + size, alignment); + in_report.ReportError(error); +} + +void ReportInvalidPosixMemalignAlignment(uptr alignment, + BufferedStackTrace *stack) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorInvalidPosixMemalignAlignment error(GetCurrentTidOrInvalid(), stack, + alignment); + in_report.ReportError(error); +} + +void ReportAllocationSizeTooBig(uptr user_size, uptr total_size, uptr max_size, + BufferedStackTrace *stack) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorAllocationSizeTooBig error(GetCurrentTidOrInvalid(), stack, user_size, + total_size, max_size); + in_report.ReportError(error); +} + +void ReportRssLimitExceeded(BufferedStackTrace *stack) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorRssLimitExceeded error(GetCurrentTidOrInvalid(), stack); + in_report.ReportError(error); +} + +void ReportOutOfMemory(uptr requested_size, BufferedStackTrace *stack) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorOutOfMemory error(GetCurrentTidOrInvalid(), stack, requested_size); + in_report.ReportError(error); +} + +void ReportStringFunctionMemoryRangesOverlap(const char *function, + const char *offset1, uptr length1, + const char *offset2, uptr length2, + BufferedStackTrace *stack) { + ScopedInErrorReport in_report; + ErrorStringFunctionMemoryRangesOverlap error( + GetCurrentTidOrInvalid(), stack, (uptr)offset1, length1, (uptr)offset2, + length2, function); + in_report.ReportError(error); +} + +void ReportStringFunctionSizeOverflow(uptr offset, uptr size, + BufferedStackTrace *stack) { + ScopedInErrorReport in_report; + ErrorStringFunctionSizeOverflow error(GetCurrentTidOrInvalid(), stack, offset, + size); + in_report.ReportError(error); +} + +void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, + uptr old_mid, uptr new_mid, + BufferedStackTrace *stack) { + ScopedInErrorReport in_report; + ErrorBadParamsToAnnotateContiguousContainer error( + GetCurrentTidOrInvalid(), stack, beg, end, old_mid, new_mid); + in_report.ReportError(error); +} + +void ReportODRViolation(const __asan_global *g1, u32 stack_id1, + const __asan_global *g2, u32 stack_id2) { + ScopedInErrorReport in_report; + ErrorODRViolation error(GetCurrentTidOrInvalid(), g1, stack_id1, g2, + stack_id2); + in_report.ReportError(error); +} + +// ----------------------- CheckForInvalidPointerPair ----------- {{{1 +static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, + uptr a1, uptr a2) { + ScopedInErrorReport in_report; + ErrorInvalidPointerPair error(GetCurrentTidOrInvalid(), pc, bp, sp, a1, a2); + in_report.ReportError(error); +} + +static bool IsInvalidPointerPair(uptr a1, uptr a2) { + if (a1 == a2) + return false; + + // 256B in shadow memory can be iterated quite fast + static const uptr kMaxOffset = 2048; + + uptr left = a1 < a2 ? a1 : a2; + uptr right = a1 < a2 ? a2 : a1; + uptr offset = right - left; + if (offset <= kMaxOffset) + return __asan_region_is_poisoned(left, offset); + + AsanThread *t = GetCurrentThread(); + + // check whether left is a stack memory pointer + if (uptr shadow_offset1 = t->GetStackVariableShadowStart(left)) { + uptr shadow_offset2 = t->GetStackVariableShadowStart(right); + return shadow_offset2 == 0 || shadow_offset1 != shadow_offset2; + } + + // check whether left is a heap memory address + HeapAddressDescription hdesc1, hdesc2; + if (GetHeapAddressInformation(left, 0, &hdesc1) && + hdesc1.chunk_access.access_type == kAccessTypeInside) + return !GetHeapAddressInformation(right, 0, &hdesc2) || + hdesc2.chunk_access.access_type != kAccessTypeInside || + hdesc1.chunk_access.chunk_begin != hdesc2.chunk_access.chunk_begin; + + // check whether left is an address of a global variable + GlobalAddressDescription gdesc1, gdesc2; + if (GetGlobalAddressInformation(left, 0, &gdesc1)) + return !GetGlobalAddressInformation(right - 1, 0, &gdesc2) || + !gdesc1.PointsInsideTheSameVariable(gdesc2); + + if (t->GetStackVariableShadowStart(right) || + GetHeapAddressInformation(right, 0, &hdesc2) || + GetGlobalAddressInformation(right - 1, 0, &gdesc2)) + return true; + + // At this point we know nothing about both a1 and a2 addresses. + return false; +} + +static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { + switch (flags()->detect_invalid_pointer_pairs) { + case 0 : return; + case 1 : if (p1 == nullptr || p2 == nullptr) return; break; + } + + uptr a1 = reinterpret_cast(p1); + uptr a2 = reinterpret_cast(p2); + + if (IsInvalidPointerPair(a1, a2)) { + GET_CALLER_PC_BP_SP; + ReportInvalidPointerPair(pc, bp, sp, a1, a2); + } +} +// ----------------------- Mac-specific reports ----------------- {{{1 + +void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, + BufferedStackTrace *stack) { + ScopedInErrorReport in_report; + Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" + "This is an unrecoverable problem, exiting now.\n", + addr); + PrintZoneForPointer(addr, zone_ptr, zone_name); + stack->Print(); + DescribeAddressIfHeap(addr); +} + +// -------------- SuppressErrorReport -------------- {{{1 +// Avoid error reports duplicating for ASan recover mode. +static bool SuppressErrorReport(uptr pc) { + if (!common_flags()->suppress_equal_pcs) return false; + for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) { + uptr cmp = atomic_load_relaxed(&AsanBuggyPcPool[i]); + if (cmp == 0 && atomic_compare_exchange_strong(&AsanBuggyPcPool[i], &cmp, + pc, memory_order_relaxed)) + return false; + if (cmp == pc) return true; + } + Die(); +} + +void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, + uptr access_size, u32 exp, bool fatal) { + if (!fatal && SuppressErrorReport(pc)) return; + ENABLE_FRAME_POINTER; + + // Optimization experiments. + // The experiments can be used to evaluate potential optimizations that remove + // instrumentation (assess false negatives). Instead of completely removing + // some instrumentation, compiler can emit special calls into runtime + // (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass + // mask of experiments (exp). + // The reaction to a non-zero value of exp is to be defined. + (void)exp; + + ScopedInErrorReport in_report(fatal); + ErrorGeneric error(GetCurrentTidOrInvalid(), pc, bp, sp, addr, is_write, + access_size); + in_report.ReportError(error); +} + +} // namespace __asan + +// --------------------------- Interface --------------------- {{{1 +using namespace __asan; // NOLINT + +void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, + uptr access_size, u32 exp) { + ENABLE_FRAME_POINTER; + bool fatal = flags()->halt_on_error; + ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal); +} + +void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { + BlockingMutexLock l(&error_message_buf_mutex); + error_report_callback = callback; +} + +void __asan_describe_address(uptr addr) { + // Thread registry must be locked while we're describing an address. + asanThreadRegistry().Lock(); + PrintAddressDescription(addr, 1, ""); + asanThreadRegistry().Unlock(); +} + +int __asan_report_present() { + return ScopedInErrorReport::CurrentError().kind != kErrorKindInvalid; +} + +uptr __asan_get_report_pc() { + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.pc; + return 0; +} + +uptr __asan_get_report_bp() { + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.bp; + return 0; +} + +uptr __asan_get_report_sp() { + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.sp; + return 0; +} + +uptr __asan_get_report_address() { + ErrorDescription &err = ScopedInErrorReport::CurrentError(); + if (err.kind == kErrorKindGeneric) + return err.Generic.addr_description.Address(); + else if (err.kind == kErrorKindDoubleFree) + return err.DoubleFree.addr_description.addr; + return 0; +} + +int __asan_get_report_access_type() { + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.is_write; + return 0; +} + +uptr __asan_get_report_access_size() { + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.access_size; + return 0; +} + +const char *__asan_get_report_description() { + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.bug_descr; + return ScopedInErrorReport::CurrentError().Base.scariness.GetDescription(); +} + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_ptr_sub(void *a, void *b) { + CheckForInvalidPointerPair(a, b); +} +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_ptr_cmp(void *a, void *b) { + CheckForInvalidPointerPair(a, b); +} +} // extern "C" + +// Provide default implementation of __asan_on_error that does nothing +// and may be overriden by user. +SANITIZER_INTERFACE_WEAK_DEF(void, __asan_on_error, void) {} diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h index b48605da41e..dcf60894ef3 100644 --- a/libsanitizer/asan/asan_report.h +++ b/libsanitizer/asan/asan_report.h @@ -1,7 +1,8 @@ //===-- asan_report.h -------------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -60,6 +61,8 @@ void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack); void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, BufferedStackTrace *stack); void ReportCallocOverflow(uptr count, uptr size, BufferedStackTrace *stack); +void ReportReallocArrayOverflow(uptr count, uptr size, + BufferedStackTrace *stack); void ReportPvallocOverflow(uptr size, BufferedStackTrace *stack); void ReportInvalidAllocationAlignment(uptr alignment, BufferedStackTrace *stack); diff --git a/libsanitizer/asan/asan_rtems.cc b/libsanitizer/asan/asan_rtems.cc deleted file mode 100644 index fa68373e63a..00000000000 --- a/libsanitizer/asan/asan_rtems.cc +++ /dev/null @@ -1,251 +0,0 @@ -//===-- asan_rtems.cc -----------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// RTEMS-specific details. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_rtems.h" -#if SANITIZER_RTEMS - -#include "asan_internal.h" -#include "asan_interceptors.h" -#include "asan_mapping.h" -#include "asan_poisoning.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_libc.h" - -#include -#include - -namespace __asan { - -static void ResetShadowMemory() { - uptr shadow_start = SHADOW_OFFSET; - uptr shadow_end = MEM_TO_SHADOW(kMyriadMemoryEnd32); - uptr gap_start = MEM_TO_SHADOW(shadow_start); - uptr gap_end = MEM_TO_SHADOW(shadow_end); - - REAL(memset)((void *)shadow_start, 0, shadow_end - shadow_start); - REAL(memset)((void *)gap_start, kAsanShadowGap, gap_end - gap_start); -} - -void InitializeShadowMemory() { - kHighMemEnd = 0; - kMidMemBeg = 0; - kMidMemEnd = 0; - - ResetShadowMemory(); -} - -void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { - UNIMPLEMENTED(); -} - -void AsanCheckDynamicRTPrereqs() {} -void AsanCheckIncompatibleRT() {} -void InitializeAsanInterceptors() {} -void InitializePlatformInterceptors() {} -void InitializePlatformExceptionHandlers() {} - -// RTEMS only support static linking; it sufficies to return with no -// error. -void *AsanDoesNotSupportStaticLinkage() { return nullptr; } - -void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { - UNIMPLEMENTED(); -} - -void EarlyInit() { - // Provide early initialization of shadow memory so that - // instrumented code running before full initialzation will not - // report spurious errors. - ResetShadowMemory(); -} - -// We can use a plain thread_local variable for TSD. -static thread_local void *per_thread; - -void *AsanTSDGet() { return per_thread; } - -void AsanTSDSet(void *tsd) { per_thread = tsd; } - -// There's no initialization needed, and the passed-in destructor -// will never be called. Instead, our own thread destruction hook -// (below) will call AsanThread::TSDDtor directly. -void AsanTSDInit(void (*destructor)(void *tsd)) { - DCHECK(destructor == &PlatformTSDDtor); -} - -void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); } - -// -// Thread registration. We provide an API similar to the Fushia port. -// - -struct AsanThread::InitOptions { - uptr stack_bottom, stack_size, tls_bottom, tls_size; -}; - -// Shared setup between thread creation and startup for the initial thread. -static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid, - uptr user_id, bool detached, - uptr stack_bottom, uptr stack_size, - uptr tls_bottom, uptr tls_size) { - // In lieu of AsanThread::Create. - AsanThread *thread = (AsanThread *)MmapOrDie(sizeof(AsanThread), __func__); - AsanThreadContext::CreateThreadContextArgs args = {thread, stack}; - asanThreadRegistry().CreateThread(user_id, detached, parent_tid, &args); - - // On other systems, AsanThread::Init() is called from the new - // thread itself. But on RTEMS we already know the stack address - // range beforehand, so we can do most of the setup right now. - const AsanThread::InitOptions options = {stack_bottom, stack_size, - tls_bottom, tls_size}; - thread->Init(&options); - return thread; -} - -// This gets the same arguments passed to Init by CreateAsanThread, above. -// We're in the creator thread before the new thread is actually started, but -// its stack and tls address range are already known. -void AsanThread::SetThreadStackAndTls(const AsanThread::InitOptions *options) { - DCHECK_NE(GetCurrentThread(), this); - DCHECK_NE(GetCurrentThread(), nullptr); - CHECK_NE(options->stack_bottom, 0); - CHECK_NE(options->stack_size, 0); - stack_bottom_ = options->stack_bottom; - stack_top_ = options->stack_bottom + options->stack_size; - tls_begin_ = options->tls_bottom; - tls_end_ = options->tls_bottom + options->tls_size; -} - -// Called by __asan::AsanInitInternal (asan_rtl.c). Unlike other ports, the -// main thread on RTEMS does not require special treatment; its AsanThread is -// already created by the provided hooks. This function simply looks up and -// returns the created thread. -AsanThread *CreateMainThread() { - return GetThreadContextByTidLocked(0)->thread; -} - -// This is called before each thread creation is attempted. So, in -// its first call, the calling thread is the initial and sole thread. -static void *BeforeThreadCreateHook(uptr user_id, bool detached, - uptr stack_bottom, uptr stack_size, - uptr tls_bottom, uptr tls_size) { - EnsureMainThreadIDIsCorrect(); - // Strict init-order checking is thread-hostile. - if (flags()->strict_init_order) StopInitOrderChecking(); - - GET_STACK_TRACE_THREAD; - u32 parent_tid = GetCurrentTidOrInvalid(); - - return CreateAsanThread(&stack, parent_tid, user_id, detached, - stack_bottom, stack_size, tls_bottom, tls_size); -} - -// This is called after creating a new thread (in the creating thread), -// with the pointer returned by BeforeThreadCreateHook (above). -static void ThreadCreateHook(void *hook, bool aborted) { - AsanThread *thread = static_cast(hook); - if (!aborted) { - // The thread was created successfully. - // ThreadStartHook is already running in the new thread. - } else { - // The thread wasn't created after all. - // Clean up everything we set up in BeforeThreadCreateHook. - asanThreadRegistry().FinishThread(thread->tid()); - UnmapOrDie(thread, sizeof(AsanThread)); - } -} - -// This is called (1) in the newly-created thread before it runs anything else, -// with the pointer returned by BeforeThreadCreateHook (above). (2) before a -// thread restart. -static void ThreadStartHook(void *hook, uptr os_id) { - if (!hook) - return; - - AsanThread *thread = static_cast(hook); - SetCurrentThread(thread); - - ThreadStatus status = - asanThreadRegistry().GetThreadLocked(thread->tid())->status; - DCHECK(status == ThreadStatusCreated || status == ThreadStatusRunning); - // Determine whether we are starting or restarting the thread. - if (status == ThreadStatusCreated) - // In lieu of AsanThread::ThreadStart. - asanThreadRegistry().StartThread(thread->tid(), os_id, - /*workerthread*/ false, nullptr); - else { - // In a thread restart, a thread may resume execution at an - // arbitrary function entry point, with its stack and TLS state - // reset. We unpoison the stack in that case. - PoisonShadow(thread->stack_bottom(), thread->stack_size(), 0); - } -} - -// Each thread runs this just before it exits, -// with the pointer returned by BeforeThreadCreateHook (above). -// All per-thread destructors have already been called. -static void ThreadExitHook(void *hook, uptr os_id) { - AsanThread *thread = static_cast(hook); - if (thread) - AsanThread::TSDDtor(thread->context()); -} - -static void HandleExit() { - // Disable ASan by setting it to uninitialized. Also reset the - // shadow memory to avoid reporting errors after the run-time has - // been desroyed. - if (asan_inited) { - asan_inited = false; - ResetShadowMemory(); - } -} - -} // namespace __asan - -// These are declared (in extern "C") by . -// The system runtime will call our definitions directly. - -extern "C" { -void __sanitizer_early_init() { - __asan::EarlyInit(); -} - -void *__sanitizer_before_thread_create_hook(uptr thread, bool detached, - const char *name, - void *stack_base, size_t stack_size, - void *tls_base, size_t tls_size) { - return __asan::BeforeThreadCreateHook( - thread, detached, - reinterpret_cast(stack_base), stack_size, - reinterpret_cast(tls_base), tls_size); -} - -void __sanitizer_thread_create_hook(void *handle, uptr thread, int status) { - __asan::ThreadCreateHook(handle, status != 0); -} - -void __sanitizer_thread_start_hook(void *handle, uptr self) { - __asan::ThreadStartHook(handle, self); -} - -void __sanitizer_thread_exit_hook(void *handle, uptr self) { - __asan::ThreadExitHook(handle, self); -} - -void __sanitizer_exit() { - __asan::HandleExit(); -} -} // "C" - -#endif // SANITIZER_RTEMS diff --git a/libsanitizer/asan/asan_rtems.cpp b/libsanitizer/asan/asan_rtems.cpp new file mode 100644 index 00000000000..360d5780a3f --- /dev/null +++ b/libsanitizer/asan/asan_rtems.cpp @@ -0,0 +1,258 @@ +//===-- asan_rtems.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// RTEMS-specific details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_rtems.h" +#if SANITIZER_RTEMS + +#include "asan_internal.h" +#include "asan_interceptors.h" +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" + +#include +#include + +namespace __asan { + +static void ResetShadowMemory() { + uptr shadow_start = SHADOW_OFFSET; + uptr shadow_end = MEM_TO_SHADOW(kMyriadMemoryEnd32); + uptr gap_start = MEM_TO_SHADOW(shadow_start); + uptr gap_end = MEM_TO_SHADOW(shadow_end); + + REAL(memset)((void *)shadow_start, 0, shadow_end - shadow_start); + REAL(memset)((void *)gap_start, kAsanShadowGap, gap_end - gap_start); +} + +void InitializeShadowMemory() { + kHighMemEnd = 0; + kMidMemBeg = 0; + kMidMemEnd = 0; + + ResetShadowMemory(); +} + +void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { + UNIMPLEMENTED(); +} + +void AsanCheckDynamicRTPrereqs() {} +void AsanCheckIncompatibleRT() {} +void InitializeAsanInterceptors() {} +void InitializePlatformInterceptors() {} +void InitializePlatformExceptionHandlers() {} + +// RTEMS only support static linking; it sufficies to return with no +// error. +void *AsanDoesNotSupportStaticLinkage() { return nullptr; } + +void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { + UNIMPLEMENTED(); +} + +void EarlyInit() { + // Provide early initialization of shadow memory so that + // instrumented code running before full initialzation will not + // report spurious errors. + ResetShadowMemory(); +} + +// We can use a plain thread_local variable for TSD. +static thread_local void *per_thread; + +void *AsanTSDGet() { return per_thread; } + +void AsanTSDSet(void *tsd) { per_thread = tsd; } + +// There's no initialization needed, and the passed-in destructor +// will never be called. Instead, our own thread destruction hook +// (below) will call AsanThread::TSDDtor directly. +void AsanTSDInit(void (*destructor)(void *tsd)) { + DCHECK(destructor == &PlatformTSDDtor); +} + +void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); } + +// +// Thread registration. We provide an API similar to the Fushia port. +// + +struct AsanThread::InitOptions { + uptr stack_bottom, stack_size, tls_bottom, tls_size; +}; + +// Shared setup between thread creation and startup for the initial thread. +static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid, + uptr user_id, bool detached, + uptr stack_bottom, uptr stack_size, + uptr tls_bottom, uptr tls_size) { + // In lieu of AsanThread::Create. + AsanThread *thread = (AsanThread *)MmapOrDie(sizeof(AsanThread), __func__); + AsanThreadContext::CreateThreadContextArgs args = {thread, stack}; + asanThreadRegistry().CreateThread(user_id, detached, parent_tid, &args); + + // On other systems, AsanThread::Init() is called from the new + // thread itself. But on RTEMS we already know the stack address + // range beforehand, so we can do most of the setup right now. + const AsanThread::InitOptions options = {stack_bottom, stack_size, + tls_bottom, tls_size}; + thread->Init(&options); + return thread; +} + +// This gets the same arguments passed to Init by CreateAsanThread, above. +// We're in the creator thread before the new thread is actually started, but +// its stack and tls address range are already known. +void AsanThread::SetThreadStackAndTls(const AsanThread::InitOptions *options) { + DCHECK_NE(GetCurrentThread(), this); + DCHECK_NE(GetCurrentThread(), nullptr); + CHECK_NE(options->stack_bottom, 0); + CHECK_NE(options->stack_size, 0); + stack_bottom_ = options->stack_bottom; + stack_top_ = options->stack_bottom + options->stack_size; + tls_begin_ = options->tls_bottom; + tls_end_ = options->tls_bottom + options->tls_size; +} + +// Called by __asan::AsanInitInternal (asan_rtl.c). Unlike other ports, the +// main thread on RTEMS does not require special treatment; its AsanThread is +// already created by the provided hooks. This function simply looks up and +// returns the created thread. +AsanThread *CreateMainThread() { + return GetThreadContextByTidLocked(0)->thread; +} + +// This is called before each thread creation is attempted. So, in +// its first call, the calling thread is the initial and sole thread. +static void *BeforeThreadCreateHook(uptr user_id, bool detached, + uptr stack_bottom, uptr stack_size, + uptr tls_bottom, uptr tls_size) { + EnsureMainThreadIDIsCorrect(); + // Strict init-order checking is thread-hostile. + if (flags()->strict_init_order) StopInitOrderChecking(); + + GET_STACK_TRACE_THREAD; + u32 parent_tid = GetCurrentTidOrInvalid(); + + return CreateAsanThread(&stack, parent_tid, user_id, detached, + stack_bottom, stack_size, tls_bottom, tls_size); +} + +// This is called after creating a new thread (in the creating thread), +// with the pointer returned by BeforeThreadCreateHook (above). +static void ThreadCreateHook(void *hook, bool aborted) { + AsanThread *thread = static_cast(hook); + if (!aborted) { + // The thread was created successfully. + // ThreadStartHook is already running in the new thread. + } else { + // The thread wasn't created after all. + // Clean up everything we set up in BeforeThreadCreateHook. + asanThreadRegistry().FinishThread(thread->tid()); + UnmapOrDie(thread, sizeof(AsanThread)); + } +} + +// This is called (1) in the newly-created thread before it runs anything else, +// with the pointer returned by BeforeThreadCreateHook (above). (2) before a +// thread restart. +static void ThreadStartHook(void *hook, uptr os_id) { + if (!hook) + return; + + AsanThread *thread = static_cast(hook); + SetCurrentThread(thread); + + ThreadStatus status = + asanThreadRegistry().GetThreadLocked(thread->tid())->status; + DCHECK(status == ThreadStatusCreated || status == ThreadStatusRunning); + // Determine whether we are starting or restarting the thread. + if (status == ThreadStatusCreated) + // In lieu of AsanThread::ThreadStart. + asanThreadRegistry().StartThread(thread->tid(), os_id, ThreadType::Regular, + nullptr); + else { + // In a thread restart, a thread may resume execution at an + // arbitrary function entry point, with its stack and TLS state + // reset. We unpoison the stack in that case. + PoisonShadow(thread->stack_bottom(), thread->stack_size(), 0); + } +} + +// Each thread runs this just before it exits, +// with the pointer returned by BeforeThreadCreateHook (above). +// All per-thread destructors have already been called. +static void ThreadExitHook(void *hook, uptr os_id) { + AsanThread *thread = static_cast(hook); + if (thread) + AsanThread::TSDDtor(thread->context()); +} + +static void HandleExit() { + // Disable ASan by setting it to uninitialized. Also reset the + // shadow memory to avoid reporting errors after the run-time has + // been desroyed. + if (asan_inited) { + asan_inited = false; + ResetShadowMemory(); + } +} + +bool HandleDlopenInit() { + // Not supported on this platform. + static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN, + "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false"); + return false; +} +} // namespace __asan + +// These are declared (in extern "C") by . +// The system runtime will call our definitions directly. + +extern "C" { +void __sanitizer_early_init() { + __asan::EarlyInit(); +} + +void *__sanitizer_before_thread_create_hook(uptr thread, bool detached, + const char *name, + void *stack_base, size_t stack_size, + void *tls_base, size_t tls_size) { + return __asan::BeforeThreadCreateHook( + thread, detached, + reinterpret_cast(stack_base), stack_size, + reinterpret_cast(tls_base), tls_size); +} + +void __sanitizer_thread_create_hook(void *handle, uptr thread, int status) { + __asan::ThreadCreateHook(handle, status != 0); +} + +void __sanitizer_thread_start_hook(void *handle, uptr self) { + __asan::ThreadStartHook(handle, self); +} + +void __sanitizer_thread_exit_hook(void *handle, uptr self) { + __asan::ThreadExitHook(handle, self); +} + +void __sanitizer_exit() { + __asan::HandleExit(); +} +} // "C" + +#endif // SANITIZER_RTEMS diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc deleted file mode 100644 index ba3acf2c5d2..00000000000 --- a/libsanitizer/asan/asan_rtl.cc +++ /dev/null @@ -1,587 +0,0 @@ -//===-- asan_rtl.cc -------------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Main file of the ASan run-time library. -//===----------------------------------------------------------------------===// - -#include "asan_activation.h" -#include "asan_allocator.h" -#include "asan_interceptors.h" -#include "asan_interface_internal.h" -#include "asan_internal.h" -#include "asan_mapping.h" -#include "asan_poisoning.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "asan_stats.h" -#include "asan_suppressions.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_atomic.h" -#include "sanitizer_common/sanitizer_flags.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_symbolizer.h" -#include "lsan/lsan_common.h" -#include "ubsan/ubsan_init.h" -#include "ubsan/ubsan_platform.h" - -uptr __asan_shadow_memory_dynamic_address; // Global interface symbol. -int __asan_option_detect_stack_use_after_return; // Global interface symbol. -uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan. - -namespace __asan { - -uptr AsanMappingProfile[kAsanMappingProfileSize]; - -static void AsanDie() { - 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) { } - } - if (common_flags()->print_module_map >= 1) PrintModuleMap(); - if (flags()->sleep_before_dying) { - Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying); - SleepForSeconds(flags()->sleep_before_dying); - } - if (flags()->unmap_shadow_on_exit) { - if (kMidMemBeg) { - UnmapOrDie((void*)kLowShadowBeg, kMidMemBeg - kLowShadowBeg); - UnmapOrDie((void*)kMidMemEnd, kHighShadowEnd - kMidMemEnd); - } else { - if (kHighShadowEnd) - UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); - } - } -} - -static void AsanCheckFailed(const char *file, int line, const char *cond, - u64 v1, u64 v2) { - Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file, - line, cond, (uptr)v1, (uptr)v2); - - // Print a stack trace the first time we come here. Otherwise, we probably - // failed a CHECK during symbolization. - static atomic_uint32_t num_calls; - if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) == 0) { - PRINT_CURRENT_STACK_CHECK(); - } - - Die(); -} - -// -------------------------- Globals --------------------- {{{1 -int asan_inited; -bool asan_init_is_running; - -#if !ASAN_FIXED_MAPPING -uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; -#endif - -// -------------------------- Misc ---------------- {{{1 -void ShowStatsAndAbort() { - __asan_print_accumulated_stats(); - Die(); -} - -// --------------- LowLevelAllocateCallbac ---------- {{{1 -static void OnLowLevelAllocate(uptr ptr, uptr size) { - PoisonShadow(ptr, size, kAsanInternalHeapMagic); -} - -// -------------------------- Run-time entry ------------------- {{{1 -// exported functions -#define ASAN_REPORT_ERROR(type, is_write, size) \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_ ## type ## size(uptr addr) { \ - GET_CALLER_PC_BP_SP; \ - ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \ -} \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \ - GET_CALLER_PC_BP_SP; \ - ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \ -} \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_ ## type ## size ## _noabort(uptr addr) { \ - GET_CALLER_PC_BP_SP; \ - ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ -} \ - -ASAN_REPORT_ERROR(load, false, 1) -ASAN_REPORT_ERROR(load, false, 2) -ASAN_REPORT_ERROR(load, false, 4) -ASAN_REPORT_ERROR(load, false, 8) -ASAN_REPORT_ERROR(load, false, 16) -ASAN_REPORT_ERROR(store, true, 1) -ASAN_REPORT_ERROR(store, true, 2) -ASAN_REPORT_ERROR(store, true, 4) -ASAN_REPORT_ERROR(store, true, 8) -ASAN_REPORT_ERROR(store, true, 16) - -#define ASAN_REPORT_ERROR_N(type, is_write) \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_ ## type ## _n(uptr addr, uptr size) { \ - GET_CALLER_PC_BP_SP; \ - ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \ -} \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \ - GET_CALLER_PC_BP_SP; \ - ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \ -} \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size) { \ - GET_CALLER_PC_BP_SP; \ - ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ -} \ - -ASAN_REPORT_ERROR_N(load, false) -ASAN_REPORT_ERROR_N(store, true) - -#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \ - if (SANITIZER_MYRIAD2 && !AddrIsInMem(addr) && !AddrIsInShadow(addr)) \ - return; \ - uptr sp = MEM_TO_SHADOW(addr); \ - uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast(sp) \ - : *reinterpret_cast(sp); \ - if (UNLIKELY(s)) { \ - if (UNLIKELY(size >= SHADOW_GRANULARITY || \ - ((s8)((addr & (SHADOW_GRANULARITY - 1)) + size - 1)) >= \ - (s8)s)) { \ - if (__asan_test_only_reported_buggy_pointer) { \ - *__asan_test_only_reported_buggy_pointer = addr; \ - } else { \ - GET_CALLER_PC_BP_SP; \ - ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \ - fatal); \ - } \ - } \ - } - -#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \ - extern "C" NOINLINE INTERFACE_ATTRIBUTE \ - void __asan_##type##size(uptr addr) { \ - ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, true) \ - } \ - extern "C" NOINLINE INTERFACE_ATTRIBUTE \ - void __asan_exp_##type##size(uptr addr, u32 exp) { \ - ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp, true) \ - } \ - extern "C" NOINLINE INTERFACE_ATTRIBUTE \ - void __asan_##type##size ## _noabort(uptr addr) { \ - ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, false) \ - } \ - -ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1) -ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2) -ASAN_MEMORY_ACCESS_CALLBACK(load, false, 4) -ASAN_MEMORY_ACCESS_CALLBACK(load, false, 8) -ASAN_MEMORY_ACCESS_CALLBACK(load, false, 16) -ASAN_MEMORY_ACCESS_CALLBACK(store, true, 1) -ASAN_MEMORY_ACCESS_CALLBACK(store, true, 2) -ASAN_MEMORY_ACCESS_CALLBACK(store, true, 4) -ASAN_MEMORY_ACCESS_CALLBACK(store, true, 8) -ASAN_MEMORY_ACCESS_CALLBACK(store, true, 16) - -extern "C" -NOINLINE INTERFACE_ATTRIBUTE -void __asan_loadN(uptr addr, uptr size) { - if (__asan_region_is_poisoned(addr, size)) { - GET_CALLER_PC_BP_SP; - ReportGenericError(pc, bp, sp, addr, false, size, 0, true); - } -} - -extern "C" -NOINLINE INTERFACE_ATTRIBUTE -void __asan_exp_loadN(uptr addr, uptr size, u32 exp) { - if (__asan_region_is_poisoned(addr, size)) { - GET_CALLER_PC_BP_SP; - ReportGenericError(pc, bp, sp, addr, false, size, exp, true); - } -} - -extern "C" -NOINLINE INTERFACE_ATTRIBUTE -void __asan_loadN_noabort(uptr addr, uptr size) { - if (__asan_region_is_poisoned(addr, size)) { - GET_CALLER_PC_BP_SP; - ReportGenericError(pc, bp, sp, addr, false, size, 0, false); - } -} - -extern "C" -NOINLINE INTERFACE_ATTRIBUTE -void __asan_storeN(uptr addr, uptr size) { - if (__asan_region_is_poisoned(addr, size)) { - GET_CALLER_PC_BP_SP; - ReportGenericError(pc, bp, sp, addr, true, size, 0, true); - } -} - -extern "C" -NOINLINE INTERFACE_ATTRIBUTE -void __asan_exp_storeN(uptr addr, uptr size, u32 exp) { - if (__asan_region_is_poisoned(addr, size)) { - GET_CALLER_PC_BP_SP; - ReportGenericError(pc, bp, sp, addr, true, size, exp, true); - } -} - -extern "C" -NOINLINE INTERFACE_ATTRIBUTE -void __asan_storeN_noabort(uptr addr, uptr size) { - if (__asan_region_is_poisoned(addr, size)) { - GET_CALLER_PC_BP_SP; - ReportGenericError(pc, bp, sp, addr, true, size, 0, false); - } -} - -// Force the linker to keep the symbols for various ASan interface functions. -// We want to keep those in the executable in order to let the instrumented -// dynamic libraries access the symbol even if it is not used by the executable -// itself. This should help if the build system is removing dead code at link -// time. -static NOINLINE void force_interface_symbols() { - volatile int fake_condition = 0; // prevent dead condition elimination. - // __asan_report_* functions are noreturn, so we need a switch to prevent - // the compiler from removing any of them. - // clang-format off - switch (fake_condition) { - case 1: __asan_report_load1(0); break; - case 2: __asan_report_load2(0); break; - case 3: __asan_report_load4(0); break; - case 4: __asan_report_load8(0); break; - case 5: __asan_report_load16(0); break; - case 6: __asan_report_load_n(0, 0); break; - case 7: __asan_report_store1(0); break; - case 8: __asan_report_store2(0); break; - case 9: __asan_report_store4(0); break; - case 10: __asan_report_store8(0); break; - case 11: __asan_report_store16(0); break; - case 12: __asan_report_store_n(0, 0); break; - case 13: __asan_report_exp_load1(0, 0); break; - case 14: __asan_report_exp_load2(0, 0); break; - case 15: __asan_report_exp_load4(0, 0); break; - case 16: __asan_report_exp_load8(0, 0); break; - case 17: __asan_report_exp_load16(0, 0); break; - case 18: __asan_report_exp_load_n(0, 0, 0); break; - case 19: __asan_report_exp_store1(0, 0); break; - case 20: __asan_report_exp_store2(0, 0); break; - case 21: __asan_report_exp_store4(0, 0); break; - case 22: __asan_report_exp_store8(0, 0); break; - case 23: __asan_report_exp_store16(0, 0); break; - case 24: __asan_report_exp_store_n(0, 0, 0); break; - case 25: __asan_register_globals(nullptr, 0); break; - case 26: __asan_unregister_globals(nullptr, 0); break; - case 27: __asan_set_death_callback(nullptr); break; - case 28: __asan_set_error_report_callback(nullptr); break; - case 29: __asan_handle_no_return(); break; - case 30: __asan_address_is_poisoned(nullptr); break; - case 31: __asan_poison_memory_region(nullptr, 0); break; - case 32: __asan_unpoison_memory_region(nullptr, 0); break; - case 34: __asan_before_dynamic_init(nullptr); break; - case 35: __asan_after_dynamic_init(); break; - case 36: __asan_poison_stack_memory(0, 0); break; - case 37: __asan_unpoison_stack_memory(0, 0); break; - case 38: __asan_region_is_poisoned(0, 0); break; - case 39: __asan_describe_address(0); break; - case 40: __asan_set_shadow_00(0, 0); break; - case 41: __asan_set_shadow_f1(0, 0); break; - case 42: __asan_set_shadow_f2(0, 0); break; - case 43: __asan_set_shadow_f3(0, 0); break; - case 44: __asan_set_shadow_f5(0, 0); break; - case 45: __asan_set_shadow_f8(0, 0); break; - } - // clang-format on -} - -static void asan_atexit() { - Printf("AddressSanitizer exit stats:\n"); - __asan_print_accumulated_stats(); - // Print AsanMappingProfile. - for (uptr i = 0; i < kAsanMappingProfileSize; i++) { - if (AsanMappingProfile[i] == 0) continue; - Printf("asan_mapping.h:%zd -- %zd\n", i, AsanMappingProfile[i]); - } -} - -static void InitializeHighMemEnd() { -#if !SANITIZER_MYRIAD2 -#if !ASAN_FIXED_MAPPING - kHighMemEnd = GetMaxUserVirtualAddress(); - // Increase kHighMemEnd to make sure it's properly - // aligned together with kHighMemBeg: - kHighMemEnd |= SHADOW_GRANULARITY * GetMmapGranularity() - 1; -#endif // !ASAN_FIXED_MAPPING - CHECK_EQ((kHighMemBeg % GetMmapGranularity()), 0); -#endif // !SANITIZER_MYRIAD2 -} - -void PrintAddressSpaceLayout() { - if (kHighMemBeg) { - Printf("|| `[%p, %p]` || HighMem ||\n", - (void*)kHighMemBeg, (void*)kHighMemEnd); - Printf("|| `[%p, %p]` || HighShadow ||\n", - (void*)kHighShadowBeg, (void*)kHighShadowEnd); - } - if (kMidMemBeg) { - Printf("|| `[%p, %p]` || ShadowGap3 ||\n", - (void*)kShadowGap3Beg, (void*)kShadowGap3End); - Printf("|| `[%p, %p]` || MidMem ||\n", - (void*)kMidMemBeg, (void*)kMidMemEnd); - Printf("|| `[%p, %p]` || ShadowGap2 ||\n", - (void*)kShadowGap2Beg, (void*)kShadowGap2End); - Printf("|| `[%p, %p]` || MidShadow ||\n", - (void*)kMidShadowBeg, (void*)kMidShadowEnd); - } - Printf("|| `[%p, %p]` || ShadowGap ||\n", - (void*)kShadowGapBeg, (void*)kShadowGapEnd); - if (kLowShadowBeg) { - Printf("|| `[%p, %p]` || LowShadow ||\n", - (void*)kLowShadowBeg, (void*)kLowShadowEnd); - Printf("|| `[%p, %p]` || LowMem ||\n", - (void*)kLowMemBeg, (void*)kLowMemEnd); - } - Printf("MemToShadow(shadow): %p %p", - (void*)MEM_TO_SHADOW(kLowShadowBeg), - (void*)MEM_TO_SHADOW(kLowShadowEnd)); - if (kHighMemBeg) { - Printf(" %p %p", - (void*)MEM_TO_SHADOW(kHighShadowBeg), - (void*)MEM_TO_SHADOW(kHighShadowEnd)); - } - if (kMidMemBeg) { - Printf(" %p %p", - (void*)MEM_TO_SHADOW(kMidShadowBeg), - (void*)MEM_TO_SHADOW(kMidShadowEnd)); - } - Printf("\n"); - Printf("redzone=%zu\n", (uptr)flags()->redzone); - Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone); - Printf("quarantine_size_mb=%zuM\n", (uptr)flags()->quarantine_size_mb); - Printf("thread_local_quarantine_size_kb=%zuK\n", - (uptr)flags()->thread_local_quarantine_size_kb); - Printf("malloc_context_size=%zu\n", - (uptr)common_flags()->malloc_context_size); - - Printf("SHADOW_SCALE: %d\n", (int)SHADOW_SCALE); - Printf("SHADOW_GRANULARITY: %d\n", (int)SHADOW_GRANULARITY); - Printf("SHADOW_OFFSET: 0x%zx\n", (uptr)SHADOW_OFFSET); - CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7); - if (kMidMemBeg) - CHECK(kMidShadowBeg > kLowShadowEnd && - kMidMemBeg > kMidShadowEnd && - kHighShadowBeg > kMidMemEnd); -} - -static void AsanInitInternal() { - if (LIKELY(asan_inited)) return; - SanitizerToolName = "AddressSanitizer"; - CHECK(!asan_init_is_running && "ASan init calls itself!"); - asan_init_is_running = true; - - CacheBinaryName(); - CheckASLR(); - - // Initialize flags. This must be done early, because most of the - // initialization steps look at flags(). - InitializeFlags(); - - AsanCheckIncompatibleRT(); - AsanCheckDynamicRTPrereqs(); - AvoidCVE_2016_2143(); - - SetCanPoisonMemory(flags()->poison_heap); - SetMallocContextSize(common_flags()->malloc_context_size); - - InitializePlatformExceptionHandlers(); - - InitializeHighMemEnd(); - - // Make sure we are not statically linked. - AsanDoesNotSupportStaticLinkage(); - - // Install tool-specific callbacks in sanitizer_common. - AddDieCallback(AsanDie); - SetCheckFailedCallback(AsanCheckFailed); - SetPrintfAndReportCallback(AppendToErrorMessageBuffer); - - __sanitizer_set_report_path(common_flags()->log_path); - - __asan_option_detect_stack_use_after_return = - flags()->detect_stack_use_after_return; - - // Re-exec ourselves if we need to set additional env or command line args. - MaybeReexec(); - - // Setup internal allocator callback. - SetLowLevelAllocateMinAlignment(SHADOW_GRANULARITY); - SetLowLevelAllocateCallback(OnLowLevelAllocate); - - InitializeAsanInterceptors(); - - // Enable system log ("adb logcat") on Android. - // Doing this before interceptors are initialized crashes in: - // AsanInitInternal -> android_log_write -> __interceptor_strcmp - AndroidLogInit(); - - ReplaceSystemMalloc(); - - DisableCoreDumperIfNecessary(); - - InitializeShadowMemory(); - - AsanTSDInit(PlatformTSDDtor); - InstallDeadlySignalHandlers(AsanOnDeadlySignal); - - AllocatorOptions allocator_options; - allocator_options.SetFrom(flags(), common_flags()); - InitializeAllocator(allocator_options); - - MaybeStartBackgroudThread(); - SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback); - - // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited - // should be set to 1 prior to initializing the threads. - asan_inited = 1; - asan_init_is_running = false; - - if (flags()->atexit) - Atexit(asan_atexit); - - InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); - - // Now that ASan runtime is (mostly) initialized, deactivate it if - // necessary, so that it can be re-activated when requested. - if (flags()->start_deactivated) - AsanDeactivate(); - - // interceptors - InitTlsSize(); - - // Create main thread. - AsanThread *main_thread = CreateMainThread(); - CHECK_EQ(0, main_thread->tid()); - force_interface_symbols(); // no-op. - SanitizerInitializeUnwinder(); - - if (CAN_SANITIZE_LEAKS) { - __lsan::InitCommonLsan(); - if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { - if (flags()->halt_on_error) - Atexit(__lsan::DoLeakCheck); - else - Atexit(__lsan::DoRecoverableLeakCheckVoid); - } - } - -#if CAN_SANITIZE_UB - __ubsan::InitAsPlugin(); -#endif - - InitializeSuppressions(); - - if (CAN_SANITIZE_LEAKS) { - // LateInitialize() calls dlsym, which can allocate an error string buffer - // in the TLS. Let's ignore the allocation to avoid reporting a leak. - __lsan::ScopedInterceptorDisabler disabler; - Symbolizer::LateInitialize(); - } else { - Symbolizer::LateInitialize(); - } - - VReport(1, "AddressSanitizer Init done\n"); - - if (flags()->sleep_after_init) { - Report("Sleeping for %d second(s)\n", flags()->sleep_after_init); - SleepForSeconds(flags()->sleep_after_init); - } -} - -// Initialize as requested from some part of ASan runtime library (interceptors, -// allocator, etc). -void AsanInitFromRtl() { - AsanInitInternal(); -} - -#if ASAN_DYNAMIC -// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable -// (and thus normal initializers from .preinit_array or modules haven't run). - -class AsanInitializer { -public: // NOLINT - AsanInitializer() { - AsanInitFromRtl(); - } -}; - -static AsanInitializer asan_initializer; -#endif // ASAN_DYNAMIC - -} // namespace __asan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -void NOINLINE __asan_handle_no_return() { - if (asan_init_is_running) - return; - - int local_stack; - AsanThread *curr_thread = GetCurrentThread(); - uptr PageSize = GetPageSizeCached(); - uptr top, bottom; - if (curr_thread) { - top = curr_thread->stack_top(); - bottom = ((uptr)&local_stack - PageSize) & ~(PageSize - 1); - } else if (SANITIZER_RTEMS) { - // Give up On RTEMS. - return; - } else { - CHECK(!SANITIZER_FUCHSIA); - // If we haven't seen this thread, try asking the OS for stack bounds. - uptr tls_addr, tls_size, stack_size; - GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr, - &tls_size); - top = bottom + stack_size; - } - static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M - if (top - bottom > kMaxExpectedCleanupSize) { - static bool reported_warning = false; - if (reported_warning) - return; - reported_warning = true; - Report("WARNING: ASan is ignoring requested __asan_handle_no_return: " - "stack top: %p; bottom %p; size: %p (%zd)\n" - "False positive error reports may follow\n" - "For details see " - "https://github.com/google/sanitizers/issues/189\n", - top, bottom, top - bottom, top - bottom); - return; - } - PoisonShadow(bottom, top - bottom, 0); - if (curr_thread && curr_thread->has_fake_stack()) - curr_thread->fake_stack()->HandleNoReturn(); -} - -void NOINLINE __asan_set_death_callback(void (*callback)(void)) { - SetUserDieCallback(callback); -} - -// Initialize as requested from instrumented application code. -// We use this call as a trigger to wake up ASan from deactivated state. -void __asan_init() { - AsanActivate(); - AsanInitInternal(); -} - -void __asan_version_mismatch_check() { - // Do nothing. -} diff --git a/libsanitizer/asan/asan_rtl.cpp b/libsanitizer/asan/asan_rtl.cpp new file mode 100644 index 00000000000..b16ca950518 --- /dev/null +++ b/libsanitizer/asan/asan_rtl.cpp @@ -0,0 +1,626 @@ +//===-- asan_rtl.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Main file of the ASan run-time library. +//===----------------------------------------------------------------------===// + +#include "asan_activation.h" +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_interface_internal.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "asan_suppressions.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_symbolizer.h" +#include "lsan/lsan_common.h" +#include "ubsan/ubsan_init.h" +#include "ubsan/ubsan_platform.h" + +uptr __asan_shadow_memory_dynamic_address; // Global interface symbol. +int __asan_option_detect_stack_use_after_return; // Global interface symbol. +uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan. + +namespace __asan { + +uptr AsanMappingProfile[kAsanMappingProfileSize]; + +static void AsanDie() { + 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) { } + } + if (common_flags()->print_module_map >= 1) PrintModuleMap(); + if (flags()->sleep_before_dying) { + Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying); + SleepForSeconds(flags()->sleep_before_dying); + } + if (flags()->unmap_shadow_on_exit) { + if (kMidMemBeg) { + UnmapOrDie((void*)kLowShadowBeg, kMidMemBeg - kLowShadowBeg); + UnmapOrDie((void*)kMidMemEnd, kHighShadowEnd - kMidMemEnd); + } else { + if (kHighShadowEnd) + UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); + } + } +} + +static void AsanCheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2) { + Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file, + line, cond, (uptr)v1, (uptr)v2); + + // Print a stack trace the first time we come here. Otherwise, we probably + // failed a CHECK during symbolization. + static atomic_uint32_t num_calls; + if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) == 0) { + PRINT_CURRENT_STACK_CHECK(); + } + + Die(); +} + +// -------------------------- Globals --------------------- {{{1 +int asan_inited; +bool asan_init_is_running; + +#if !ASAN_FIXED_MAPPING +uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; +#endif + +// -------------------------- Misc ---------------- {{{1 +void ShowStatsAndAbort() { + __asan_print_accumulated_stats(); + Die(); +} + +// --------------- LowLevelAllocateCallbac ---------- {{{1 +static void OnLowLevelAllocate(uptr ptr, uptr size) { + PoisonShadow(ptr, size, kAsanInternalHeapMagic); +} + +// -------------------------- Run-time entry ------------------- {{{1 +// exported functions +#define ASAN_REPORT_ERROR(type, is_write, size) \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## size(uptr addr) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \ +} \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \ +} \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## size ## _noabort(uptr addr) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ +} \ + +ASAN_REPORT_ERROR(load, false, 1) +ASAN_REPORT_ERROR(load, false, 2) +ASAN_REPORT_ERROR(load, false, 4) +ASAN_REPORT_ERROR(load, false, 8) +ASAN_REPORT_ERROR(load, false, 16) +ASAN_REPORT_ERROR(store, true, 1) +ASAN_REPORT_ERROR(store, true, 2) +ASAN_REPORT_ERROR(store, true, 4) +ASAN_REPORT_ERROR(store, true, 8) +ASAN_REPORT_ERROR(store, true, 16) + +#define ASAN_REPORT_ERROR_N(type, is_write) \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## _n(uptr addr, uptr size) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \ +} \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \ +} \ +extern "C" NOINLINE INTERFACE_ATTRIBUTE \ +void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size) { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \ +} \ + +ASAN_REPORT_ERROR_N(load, false) +ASAN_REPORT_ERROR_N(store, true) + +#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \ + if (SANITIZER_MYRIAD2 && !AddrIsInMem(addr) && !AddrIsInShadow(addr)) \ + return; \ + uptr sp = MEM_TO_SHADOW(addr); \ + uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast(sp) \ + : *reinterpret_cast(sp); \ + if (UNLIKELY(s)) { \ + if (UNLIKELY(size >= SHADOW_GRANULARITY || \ + ((s8)((addr & (SHADOW_GRANULARITY - 1)) + size - 1)) >= \ + (s8)s)) { \ + if (__asan_test_only_reported_buggy_pointer) { \ + *__asan_test_only_reported_buggy_pointer = addr; \ + } else { \ + GET_CALLER_PC_BP_SP; \ + ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \ + fatal); \ + } \ + } \ + } + +#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \ + extern "C" NOINLINE INTERFACE_ATTRIBUTE \ + void __asan_##type##size(uptr addr) { \ + ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, true) \ + } \ + extern "C" NOINLINE INTERFACE_ATTRIBUTE \ + void __asan_exp_##type##size(uptr addr, u32 exp) { \ + ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp, true) \ + } \ + extern "C" NOINLINE INTERFACE_ATTRIBUTE \ + void __asan_##type##size ## _noabort(uptr addr) { \ + ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, false) \ + } \ + +ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1) +ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2) +ASAN_MEMORY_ACCESS_CALLBACK(load, false, 4) +ASAN_MEMORY_ACCESS_CALLBACK(load, false, 8) +ASAN_MEMORY_ACCESS_CALLBACK(load, false, 16) +ASAN_MEMORY_ACCESS_CALLBACK(store, true, 1) +ASAN_MEMORY_ACCESS_CALLBACK(store, true, 2) +ASAN_MEMORY_ACCESS_CALLBACK(store, true, 4) +ASAN_MEMORY_ACCESS_CALLBACK(store, true, 8) +ASAN_MEMORY_ACCESS_CALLBACK(store, true, 16) + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_loadN(uptr addr, uptr size) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + ReportGenericError(pc, bp, sp, addr, false, size, 0, true); + } +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_exp_loadN(uptr addr, uptr size, u32 exp) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + ReportGenericError(pc, bp, sp, addr, false, size, exp, true); + } +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_loadN_noabort(uptr addr, uptr size) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + ReportGenericError(pc, bp, sp, addr, false, size, 0, false); + } +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_storeN(uptr addr, uptr size) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + ReportGenericError(pc, bp, sp, addr, true, size, 0, true); + } +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_exp_storeN(uptr addr, uptr size, u32 exp) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + ReportGenericError(pc, bp, sp, addr, true, size, exp, true); + } +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_storeN_noabort(uptr addr, uptr size) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + ReportGenericError(pc, bp, sp, addr, true, size, 0, false); + } +} + +// Force the linker to keep the symbols for various ASan interface functions. +// We want to keep those in the executable in order to let the instrumented +// dynamic libraries access the symbol even if it is not used by the executable +// itself. This should help if the build system is removing dead code at link +// time. +static NOINLINE void force_interface_symbols() { + volatile int fake_condition = 0; // prevent dead condition elimination. + // __asan_report_* functions are noreturn, so we need a switch to prevent + // the compiler from removing any of them. + // clang-format off + switch (fake_condition) { + case 1: __asan_report_load1(0); break; + case 2: __asan_report_load2(0); break; + case 3: __asan_report_load4(0); break; + case 4: __asan_report_load8(0); break; + case 5: __asan_report_load16(0); break; + case 6: __asan_report_load_n(0, 0); break; + case 7: __asan_report_store1(0); break; + case 8: __asan_report_store2(0); break; + case 9: __asan_report_store4(0); break; + case 10: __asan_report_store8(0); break; + case 11: __asan_report_store16(0); break; + case 12: __asan_report_store_n(0, 0); break; + case 13: __asan_report_exp_load1(0, 0); break; + case 14: __asan_report_exp_load2(0, 0); break; + case 15: __asan_report_exp_load4(0, 0); break; + case 16: __asan_report_exp_load8(0, 0); break; + case 17: __asan_report_exp_load16(0, 0); break; + case 18: __asan_report_exp_load_n(0, 0, 0); break; + case 19: __asan_report_exp_store1(0, 0); break; + case 20: __asan_report_exp_store2(0, 0); break; + case 21: __asan_report_exp_store4(0, 0); break; + case 22: __asan_report_exp_store8(0, 0); break; + case 23: __asan_report_exp_store16(0, 0); break; + case 24: __asan_report_exp_store_n(0, 0, 0); break; + case 25: __asan_register_globals(nullptr, 0); break; + case 26: __asan_unregister_globals(nullptr, 0); break; + case 27: __asan_set_death_callback(nullptr); break; + case 28: __asan_set_error_report_callback(nullptr); break; + case 29: __asan_handle_no_return(); break; + case 30: __asan_address_is_poisoned(nullptr); break; + case 31: __asan_poison_memory_region(nullptr, 0); break; + case 32: __asan_unpoison_memory_region(nullptr, 0); break; + case 34: __asan_before_dynamic_init(nullptr); break; + case 35: __asan_after_dynamic_init(); break; + case 36: __asan_poison_stack_memory(0, 0); break; + case 37: __asan_unpoison_stack_memory(0, 0); break; + case 38: __asan_region_is_poisoned(0, 0); break; + case 39: __asan_describe_address(0); break; + case 40: __asan_set_shadow_00(0, 0); break; + case 41: __asan_set_shadow_f1(0, 0); break; + case 42: __asan_set_shadow_f2(0, 0); break; + case 43: __asan_set_shadow_f3(0, 0); break; + case 44: __asan_set_shadow_f5(0, 0); break; + case 45: __asan_set_shadow_f8(0, 0); break; + } + // clang-format on +} + +static void asan_atexit() { + Printf("AddressSanitizer exit stats:\n"); + __asan_print_accumulated_stats(); + // Print AsanMappingProfile. + for (uptr i = 0; i < kAsanMappingProfileSize; i++) { + if (AsanMappingProfile[i] == 0) continue; + Printf("asan_mapping.h:%zd -- %zd\n", i, AsanMappingProfile[i]); + } +} + +static void InitializeHighMemEnd() { +#if !SANITIZER_MYRIAD2 +#if !ASAN_FIXED_MAPPING + kHighMemEnd = GetMaxUserVirtualAddress(); + // Increase kHighMemEnd to make sure it's properly + // aligned together with kHighMemBeg: + kHighMemEnd |= SHADOW_GRANULARITY * GetMmapGranularity() - 1; +#endif // !ASAN_FIXED_MAPPING + CHECK_EQ((kHighMemBeg % GetMmapGranularity()), 0); +#endif // !SANITIZER_MYRIAD2 +} + +void PrintAddressSpaceLayout() { + if (kHighMemBeg) { + Printf("|| `[%p, %p]` || HighMem ||\n", + (void*)kHighMemBeg, (void*)kHighMemEnd); + Printf("|| `[%p, %p]` || HighShadow ||\n", + (void*)kHighShadowBeg, (void*)kHighShadowEnd); + } + if (kMidMemBeg) { + Printf("|| `[%p, %p]` || ShadowGap3 ||\n", + (void*)kShadowGap3Beg, (void*)kShadowGap3End); + Printf("|| `[%p, %p]` || MidMem ||\n", + (void*)kMidMemBeg, (void*)kMidMemEnd); + Printf("|| `[%p, %p]` || ShadowGap2 ||\n", + (void*)kShadowGap2Beg, (void*)kShadowGap2End); + Printf("|| `[%p, %p]` || MidShadow ||\n", + (void*)kMidShadowBeg, (void*)kMidShadowEnd); + } + Printf("|| `[%p, %p]` || ShadowGap ||\n", + (void*)kShadowGapBeg, (void*)kShadowGapEnd); + if (kLowShadowBeg) { + Printf("|| `[%p, %p]` || LowShadow ||\n", + (void*)kLowShadowBeg, (void*)kLowShadowEnd); + Printf("|| `[%p, %p]` || LowMem ||\n", + (void*)kLowMemBeg, (void*)kLowMemEnd); + } + Printf("MemToShadow(shadow): %p %p", + (void*)MEM_TO_SHADOW(kLowShadowBeg), + (void*)MEM_TO_SHADOW(kLowShadowEnd)); + if (kHighMemBeg) { + Printf(" %p %p", + (void*)MEM_TO_SHADOW(kHighShadowBeg), + (void*)MEM_TO_SHADOW(kHighShadowEnd)); + } + if (kMidMemBeg) { + Printf(" %p %p", + (void*)MEM_TO_SHADOW(kMidShadowBeg), + (void*)MEM_TO_SHADOW(kMidShadowEnd)); + } + Printf("\n"); + Printf("redzone=%zu\n", (uptr)flags()->redzone); + Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone); + Printf("quarantine_size_mb=%zuM\n", (uptr)flags()->quarantine_size_mb); + Printf("thread_local_quarantine_size_kb=%zuK\n", + (uptr)flags()->thread_local_quarantine_size_kb); + Printf("malloc_context_size=%zu\n", + (uptr)common_flags()->malloc_context_size); + + Printf("SHADOW_SCALE: %d\n", (int)SHADOW_SCALE); + Printf("SHADOW_GRANULARITY: %d\n", (int)SHADOW_GRANULARITY); + Printf("SHADOW_OFFSET: 0x%zx\n", (uptr)SHADOW_OFFSET); + CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7); + if (kMidMemBeg) + CHECK(kMidShadowBeg > kLowShadowEnd && + kMidMemBeg > kMidShadowEnd && + kHighShadowBeg > kMidMemEnd); +} + +#if defined(__thumb__) && defined(__linux__) +#define START_BACKGROUND_THREAD_IN_ASAN_INTERNAL +#endif + +#ifndef START_BACKGROUND_THREAD_IN_ASAN_INTERNAL +static bool UNUSED __local_asan_dyninit = [] { + MaybeStartBackgroudThread(); + SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback); + + return false; +}(); +#endif + +static void AsanInitInternal() { + if (LIKELY(asan_inited)) return; + SanitizerToolName = "AddressSanitizer"; + CHECK(!asan_init_is_running && "ASan init calls itself!"); + asan_init_is_running = true; + + CacheBinaryName(); + CheckASLR(); + + // Initialize flags. This must be done early, because most of the + // initialization steps look at flags(). + InitializeFlags(); + + // Stop performing init at this point if we are being loaded via + // dlopen() and the platform supports it. + if (SANITIZER_SUPPORTS_INIT_FOR_DLOPEN && UNLIKELY(HandleDlopenInit())) { + asan_init_is_running = false; + VReport(1, "AddressSanitizer init is being performed for dlopen().\n"); + return; + } + + AsanCheckIncompatibleRT(); + AsanCheckDynamicRTPrereqs(); + AvoidCVE_2016_2143(); + + SetCanPoisonMemory(flags()->poison_heap); + SetMallocContextSize(common_flags()->malloc_context_size); + + InitializePlatformExceptionHandlers(); + + InitializeHighMemEnd(); + + // Make sure we are not statically linked. + AsanDoesNotSupportStaticLinkage(); + + // Install tool-specific callbacks in sanitizer_common. + AddDieCallback(AsanDie); + SetCheckFailedCallback(AsanCheckFailed); + SetPrintfAndReportCallback(AppendToErrorMessageBuffer); + + __sanitizer_set_report_path(common_flags()->log_path); + + __asan_option_detect_stack_use_after_return = + flags()->detect_stack_use_after_return; + + __sanitizer::InitializePlatformEarly(); + + // Re-exec ourselves if we need to set additional env or command line args. + MaybeReexec(); + + // Setup internal allocator callback. + SetLowLevelAllocateMinAlignment(SHADOW_GRANULARITY); + SetLowLevelAllocateCallback(OnLowLevelAllocate); + + InitializeAsanInterceptors(); + + // Enable system log ("adb logcat") on Android. + // Doing this before interceptors are initialized crashes in: + // AsanInitInternal -> android_log_write -> __interceptor_strcmp + AndroidLogInit(); + + ReplaceSystemMalloc(); + + DisableCoreDumperIfNecessary(); + + InitializeShadowMemory(); + + AsanTSDInit(PlatformTSDDtor); + InstallDeadlySignalHandlers(AsanOnDeadlySignal); + + AllocatorOptions allocator_options; + allocator_options.SetFrom(flags(), common_flags()); + InitializeAllocator(allocator_options); + +#ifdef START_BACKGROUND_THREAD_IN_ASAN_INTERNAL + MaybeStartBackgroudThread(); + SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback); +#endif + + // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited + // should be set to 1 prior to initializing the threads. + asan_inited = 1; + asan_init_is_running = false; + + if (flags()->atexit) + Atexit(asan_atexit); + + InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); + + // Now that ASan runtime is (mostly) initialized, deactivate it if + // necessary, so that it can be re-activated when requested. + if (flags()->start_deactivated) + AsanDeactivate(); + + // interceptors + InitTlsSize(); + + // Create main thread. + AsanThread *main_thread = CreateMainThread(); + CHECK_EQ(0, main_thread->tid()); + force_interface_symbols(); // no-op. + SanitizerInitializeUnwinder(); + + if (CAN_SANITIZE_LEAKS) { + __lsan::InitCommonLsan(); + if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { + if (flags()->halt_on_error) + Atexit(__lsan::DoLeakCheck); + else + Atexit(__lsan::DoRecoverableLeakCheckVoid); + } + } + +#if CAN_SANITIZE_UB + __ubsan::InitAsPlugin(); +#endif + + InitializeSuppressions(); + + if (CAN_SANITIZE_LEAKS) { + // LateInitialize() calls dlsym, which can allocate an error string buffer + // in the TLS. Let's ignore the allocation to avoid reporting a leak. + __lsan::ScopedInterceptorDisabler disabler; + Symbolizer::LateInitialize(); + } else { + Symbolizer::LateInitialize(); + } + + VReport(1, "AddressSanitizer Init done\n"); + + if (flags()->sleep_after_init) { + Report("Sleeping for %d second(s)\n", flags()->sleep_after_init); + SleepForSeconds(flags()->sleep_after_init); + } +} + +// Initialize as requested from some part of ASan runtime library (interceptors, +// allocator, etc). +void AsanInitFromRtl() { + AsanInitInternal(); +} + +#if ASAN_DYNAMIC +// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable +// (and thus normal initializers from .preinit_array or modules haven't run). + +class AsanInitializer { +public: // NOLINT + AsanInitializer() { + AsanInitFromRtl(); + } +}; + +static AsanInitializer asan_initializer; +#endif // ASAN_DYNAMIC + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +void NOINLINE __asan_handle_no_return() { + if (asan_init_is_running) + return; + + int local_stack; + AsanThread *curr_thread = GetCurrentThread(); + uptr PageSize = GetPageSizeCached(); + uptr top, bottom; + if (curr_thread) { + top = curr_thread->stack_top(); + bottom = ((uptr)&local_stack - PageSize) & ~(PageSize - 1); + } else if (SANITIZER_RTEMS) { + // Give up On RTEMS. + return; + } else { + CHECK(!SANITIZER_FUCHSIA); + // If we haven't seen this thread, try asking the OS for stack bounds. + uptr tls_addr, tls_size, stack_size; + GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr, + &tls_size); + top = bottom + stack_size; + } + static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M + if (top - bottom > kMaxExpectedCleanupSize) { + static bool reported_warning = false; + if (reported_warning) + return; + reported_warning = true; + Report("WARNING: ASan is ignoring requested __asan_handle_no_return: " + "stack top: %p; bottom %p; size: %p (%zd)\n" + "False positive error reports may follow\n" + "For details see " + "https://github.com/google/sanitizers/issues/189\n", + top, bottom, top - bottom, top - bottom); + return; + } + PoisonShadow(bottom, top - bottom, 0); + if (curr_thread && curr_thread->has_fake_stack()) + curr_thread->fake_stack()->HandleNoReturn(); +} + +extern "C" void *__asan_extra_spill_area() { + AsanThread *t = GetCurrentThread(); + CHECK(t); + return t->extra_spill_area(); +} + +void __asan_handle_vfork(void *sp) { + AsanThread *t = GetCurrentThread(); + CHECK(t); + uptr bottom = t->stack_bottom(); + PoisonShadow(bottom, (uptr)sp - bottom, 0); +} + +void NOINLINE __asan_set_death_callback(void (*callback)(void)) { + SetUserDieCallback(callback); +} + +// Initialize as requested from instrumented application code. +// We use this call as a trigger to wake up ASan from deactivated state. +void __asan_init() { + AsanActivate(); + AsanInitInternal(); +} + +void __asan_version_mismatch_check() { + // Do nothing. +} diff --git a/libsanitizer/asan/asan_scariness_score.h b/libsanitizer/asan/asan_scariness_score.h index aa947ed2732..9e7ba47d82d 100644 --- a/libsanitizer/asan/asan_scariness_score.h +++ b/libsanitizer/asan/asan_scariness_score.h @@ -1,7 +1,8 @@ //===-- asan_scariness_score.h ----------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/asan/asan_shadow_setup.cc b/libsanitizer/asan/asan_shadow_setup.cc deleted file mode 100644 index 823187bf5f1..00000000000 --- a/libsanitizer/asan/asan_shadow_setup.cc +++ /dev/null @@ -1,163 +0,0 @@ -//===-- asan_shadow_setup.cc ----------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Set up the shadow memory. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" - -// asan_fuchsia.cc and asan_rtems.cc have their own -// InitializeShadowMemory implementation. -#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS - -#include "asan_internal.h" -#include "asan_mapping.h" - -namespace __asan { - -// ---------------------- mmap -------------------- {{{1 -// Reserve memory range [beg, end]. -// We need to use inclusive range because end+1 may not be representable. -void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) { - CHECK_EQ((beg % GetMmapGranularity()), 0); - CHECK_EQ(((end + 1) % GetMmapGranularity()), 0); - uptr size = end - beg + 1; - DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb. - if (!MmapFixedNoReserve(beg, size, name)) { - Report( - "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " - "Perhaps you're using ulimit -v\n", - size); - Abort(); - } - if (common_flags()->no_huge_pages_for_shadow) NoHugePagesInRegion(beg, size); - if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(beg, size); -} - -static void ProtectGap(uptr addr, uptr size) { - if (!flags()->protect_shadow_gap) { - // The shadow gap is unprotected, so there is a chance that someone - // is actually using this memory. Which means it needs a shadow... - uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached()); - uptr GapShadowEnd = - RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1; - if (Verbosity()) - Printf( - "protect_shadow_gap=0:" - " not protecting shadow gap, allocating gap's shadow\n" - "|| `[%p, %p]` || ShadowGap's shadow ||\n", - GapShadowBeg, GapShadowEnd); - ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd, - "unprotected gap shadow"); - return; - } - void *res = MmapFixedNoAccess(addr, size, "shadow gap"); - if (addr == (uptr)res) return; - // A few pages at the start of the address space can not be protected. - // But we really want to protect as much as possible, to prevent this memory - // being returned as a result of a non-FIXED mmap(). - if (addr == kZeroBaseShadowStart) { - uptr step = GetMmapGranularity(); - while (size > step && addr < kZeroBaseMaxShadowStart) { - addr += step; - size -= step; - void *res = MmapFixedNoAccess(addr, size, "shadow gap"); - if (addr == (uptr)res) return; - } - } - - Report( - "ERROR: Failed to protect the shadow gap. " - "ASan cannot proceed correctly. ABORTING.\n"); - DumpProcessMap(); - Die(); -} - -static void MaybeReportLinuxPIEBug() { -#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__aarch64__)) - Report("This might be related to ELF_ET_DYN_BASE change in Linux 4.12.\n"); - Report( - "See https://github.com/google/sanitizers/issues/856 for possible " - "workarounds.\n"); -#endif -} - -void InitializeShadowMemory() { - // Set the shadow memory address to uninitialized. - __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel; - - uptr shadow_start = kLowShadowBeg; - // Detect if a dynamic shadow address must used and find a available location - // when necessary. When dynamic address is used, the macro |kLowShadowBeg| - // expands to |__asan_shadow_memory_dynamic_address| which is - // |kDefaultShadowSentinel|. - bool full_shadow_is_available = false; - if (shadow_start == kDefaultShadowSentinel) { - __asan_shadow_memory_dynamic_address = 0; - CHECK_EQ(0, kLowShadowBeg); - shadow_start = FindDynamicShadowStart(); - if (SANITIZER_LINUX) full_shadow_is_available = true; - } - // Update the shadow memory address (potentially) used by instrumentation. - __asan_shadow_memory_dynamic_address = shadow_start; - - if (kLowShadowBeg) shadow_start -= GetMmapGranularity(); - - if (!full_shadow_is_available) - full_shadow_is_available = - MemoryRangeIsAvailable(shadow_start, kHighShadowEnd); - -#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \ - !ASAN_FIXED_MAPPING - if (!full_shadow_is_available) { - kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0; - kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0; - } -#endif - - if (Verbosity()) PrintAddressSpaceLayout(); - - if (full_shadow_is_available) { - // mmap the low shadow plus at least one page at the left. - if (kLowShadowBeg) - ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); - // mmap the high shadow. - ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); - // protect the gap. - ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); - CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); - } else if (kMidMemBeg && - MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && - MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { - CHECK(kLowShadowBeg != kLowShadowEnd); - // mmap the low shadow plus at least one page at the left. - ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); - // mmap the mid shadow. - ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow"); - // mmap the high shadow. - ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); - // protect the gaps. - ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); - ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1); - ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1); - } else { - Report( - "Shadow memory range interleaves with an existing memory mapping. " - "ASan cannot proceed correctly. ABORTING.\n"); - Report("ASan shadow was supposed to be located in the [%p-%p] range.\n", - shadow_start, kHighShadowEnd); - MaybeReportLinuxPIEBug(); - DumpProcessMap(); - Die(); - } -} - -} // namespace __asan - -#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS diff --git a/libsanitizer/asan/asan_shadow_setup.cpp b/libsanitizer/asan/asan_shadow_setup.cpp new file mode 100644 index 00000000000..fc9bf51e1b5 --- /dev/null +++ b/libsanitizer/asan/asan_shadow_setup.cpp @@ -0,0 +1,164 @@ +//===-- asan_shadow_setup.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Set up the shadow memory. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" + +// asan_fuchsia.cpp and asan_rtems.cpp have their own +// InitializeShadowMemory implementation. +#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS + +#include "asan_internal.h" +#include "asan_mapping.h" + +namespace __asan { + +// ---------------------- mmap -------------------- {{{1 +// Reserve memory range [beg, end]. +// We need to use inclusive range because end+1 may not be representable. +void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) { + CHECK_EQ((beg % GetMmapGranularity()), 0); + CHECK_EQ(((end + 1) % GetMmapGranularity()), 0); + uptr size = end - beg + 1; + DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb. + if (!MmapFixedNoReserve(beg, size, name)) { + Report( + "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " + "Perhaps you're using ulimit -v\n", + size); + Abort(); + } + SetShadowRegionHugePageMode(beg, size); + if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(beg, size); +} + +static void ProtectGap(uptr addr, uptr size) { + if (!flags()->protect_shadow_gap) { + // The shadow gap is unprotected, so there is a chance that someone + // is actually using this memory. Which means it needs a shadow... + uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached()); + uptr GapShadowEnd = + RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1; + if (Verbosity()) + Printf( + "protect_shadow_gap=0:" + " not protecting shadow gap, allocating gap's shadow\n" + "|| `[%p, %p]` || ShadowGap's shadow ||\n", + GapShadowBeg, GapShadowEnd); + ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd, + "unprotected gap shadow"); + return; + } + void *res = MmapFixedNoAccess(addr, size, "shadow gap"); + if (addr == (uptr)res) return; + // A few pages at the start of the address space can not be protected. + // But we really want to protect as much as possible, to prevent this memory + // being returned as a result of a non-FIXED mmap(). + if (addr == kZeroBaseShadowStart) { + uptr step = GetMmapGranularity(); + while (size > step && addr < kZeroBaseMaxShadowStart) { + addr += step; + size -= step; + void *res = MmapFixedNoAccess(addr, size, "shadow gap"); + if (addr == (uptr)res) return; + } + } + + Report( + "ERROR: Failed to protect the shadow gap. " + "ASan cannot proceed correctly. ABORTING.\n"); + DumpProcessMap(); + Die(); +} + +static void MaybeReportLinuxPIEBug() { +#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__aarch64__)) + Report("This might be related to ELF_ET_DYN_BASE change in Linux 4.12.\n"); + Report( + "See https://github.com/google/sanitizers/issues/856 for possible " + "workarounds.\n"); +#endif +} + +void InitializeShadowMemory() { + // Set the shadow memory address to uninitialized. + __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel; + + uptr shadow_start = kLowShadowBeg; + // Detect if a dynamic shadow address must used and find a available location + // when necessary. When dynamic address is used, the macro |kLowShadowBeg| + // expands to |__asan_shadow_memory_dynamic_address| which is + // |kDefaultShadowSentinel|. + bool full_shadow_is_available = false; + if (shadow_start == kDefaultShadowSentinel) { + __asan_shadow_memory_dynamic_address = 0; + CHECK_EQ(0, kLowShadowBeg); + shadow_start = FindDynamicShadowStart(); + if (SANITIZER_LINUX) full_shadow_is_available = true; + } + // Update the shadow memory address (potentially) used by instrumentation. + __asan_shadow_memory_dynamic_address = shadow_start; + + if (kLowShadowBeg) shadow_start -= GetMmapGranularity(); + + if (!full_shadow_is_available) + full_shadow_is_available = + MemoryRangeIsAvailable(shadow_start, kHighShadowEnd); + +#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \ + !ASAN_FIXED_MAPPING + if (!full_shadow_is_available) { + kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0; + kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0; + } +#endif + + if (Verbosity()) PrintAddressSpaceLayout(); + + if (full_shadow_is_available) { + // mmap the low shadow plus at least one page at the left. + if (kLowShadowBeg) + ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); + // mmap the high shadow. + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); + // protect the gap. + ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); + } else if (kMidMemBeg && + MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && + MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { + CHECK(kLowShadowBeg != kLowShadowEnd); + // mmap the low shadow plus at least one page at the left. + ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); + // mmap the mid shadow. + ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow"); + // mmap the high shadow. + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); + // protect the gaps. + ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1); + ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1); + } else { + Report( + "Shadow memory range interleaves with an existing memory mapping. " + "ASan cannot proceed correctly. ABORTING.\n"); + Report("ASan shadow was supposed to be located in the [%p-%p] range.\n", + shadow_start, kHighShadowEnd); + MaybeReportLinuxPIEBug(); + DumpProcessMap(); + Die(); + } +} + +} // namespace __asan + +#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS diff --git a/libsanitizer/asan/asan_stack.cc b/libsanitizer/asan/asan_stack.cc deleted file mode 100644 index 973c5ce59ef..00000000000 --- a/libsanitizer/asan/asan_stack.cc +++ /dev/null @@ -1,38 +0,0 @@ -//===-- asan_stack.cc -----------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Code for ASan stack trace. -//===----------------------------------------------------------------------===// -#include "asan_internal.h" -#include "asan_stack.h" -#include "sanitizer_common/sanitizer_atomic.h" - -namespace __asan { - -static atomic_uint32_t malloc_context_size; - -void SetMallocContextSize(u32 size) { - atomic_store(&malloc_context_size, size, memory_order_release); -} - -u32 GetMallocContextSize() { - return atomic_load(&malloc_context_size, memory_order_acquire); -} - -} // namespace __asan - -// ------------------ Interface -------------- {{{1 - -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_print_stack_trace() { - using namespace __asan; - PRINT_CURRENT_STACK(); -} -} // extern "C" diff --git a/libsanitizer/asan/asan_stack.cpp b/libsanitizer/asan/asan_stack.cpp new file mode 100644 index 00000000000..b7f4e6aeeab --- /dev/null +++ b/libsanitizer/asan/asan_stack.cpp @@ -0,0 +1,88 @@ +//===-- asan_stack.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Code for ASan stack trace. +//===----------------------------------------------------------------------===// +#include "asan_internal.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_atomic.h" + +namespace __asan { + +static atomic_uint32_t malloc_context_size; + +void SetMallocContextSize(u32 size) { + atomic_store(&malloc_context_size, size, memory_order_release); +} + +u32 GetMallocContextSize() { + return atomic_load(&malloc_context_size, memory_order_acquire); +} + +namespace { + +// ScopedUnwinding is a scope for stacktracing member of a context +class ScopedUnwinding { + public: + explicit ScopedUnwinding(AsanThread *t) : thread(t) { + if (thread) { + can_unwind = !thread->isUnwinding(); + thread->setUnwinding(true); + } + } + ~ScopedUnwinding() { + if (thread) + thread->setUnwinding(false); + } + + bool CanUnwind() const { return can_unwind; } + + private: + AsanThread *thread = nullptr; + bool can_unwind = true; +}; + +} // namespace + +} // namespace __asan + +void __sanitizer::BufferedStackTrace::UnwindImpl( + uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) { + using namespace __asan; + size = 0; + if (UNLIKELY(!asan_inited)) + return; + request_fast = StackTrace::WillUseFastUnwind(request_fast); + AsanThread *t = GetCurrentThread(); + ScopedUnwinding unwind_scope(t); + if (!unwind_scope.CanUnwind()) + return; + if (request_fast) { + if (t) { + Unwind(max_depth, pc, bp, nullptr, t->stack_top(), t->stack_bottom(), + true); + } + return; + } + if (SANITIZER_MIPS && t && + !IsValidFrame(bp, t->stack_top(), t->stack_bottom())) + return; + Unwind(max_depth, pc, bp, context, 0, 0, false); +} + +// ------------------ Interface -------------- {{{1 + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_print_stack_trace() { + using namespace __asan; + PRINT_CURRENT_STACK(); +} +} // extern "C" diff --git a/libsanitizer/asan/asan_stack.h b/libsanitizer/asan/asan_stack.h index 5775e9d325c..4089d3d7340 100644 --- a/libsanitizer/asan/asan_stack.h +++ b/libsanitizer/asan/asan_stack.h @@ -1,13 +1,14 @@ //===-- asan_stack.h --------------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_stack.cc. +// ASan-private header for asan_stack.cpp. //===----------------------------------------------------------------------===// #ifndef ASAN_STACK_H @@ -25,34 +26,6 @@ static const u32 kDefaultMallocContextSize = 30; void SetMallocContextSize(u32 size); u32 GetMallocContextSize(); -// Get the stack trace with the given pc and bp. -// The pc will be in the position 0 of the resulting stack trace. -// The bp may refer to the current frame or to the caller's frame. -ALWAYS_INLINE -void GetStackTrace(BufferedStackTrace *stack, uptr max_depth, uptr pc, uptr bp, - void *context, bool fast) { -#if SANITIZER_WINDOWS - stack->Unwind(max_depth, pc, bp, context, 0, 0, fast); -#else - AsanThread *t; - stack->size = 0; - if (LIKELY(asan_inited)) { - if ((t = GetCurrentThread()) && !t->isUnwinding()) { - uptr stack_top = t->stack_top(); - uptr stack_bottom = t->stack_bottom(); - ScopedUnwinding unwind_scope(t); - if (!SANITIZER_MIPS || IsValidFrame(bp, stack_top, stack_bottom)) { - stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom, - fast); - } - } else if (!t && !fast) { - /* If GetCurrentThread() has failed, try to do slow unwind anyways. */ - stack->Unwind(max_depth, pc, bp, context, 0, 0, false); - } - } -#endif // SANITIZER_WINDOWS -} - } // namespace __asan // NOTE: A Rule of thumb is to retrieve stack trace in the interceptors @@ -69,19 +42,19 @@ void GetStackTrace(BufferedStackTrace *stack, uptr max_depth, uptr pc, uptr bp, if (max_size > 1) stack.trace_buffer[1] = GET_CALLER_PC(); \ } \ } else { \ - GetStackTrace(&stack, max_size, StackTrace::GetCurrentPc(), \ - GET_CURRENT_FRAME(), 0, fast); \ + stack.Unwind(StackTrace::GetCurrentPc(), \ + GET_CURRENT_FRAME(), nullptr, fast, max_size); \ } #define GET_STACK_TRACE_FATAL(pc, bp) \ BufferedStackTrace stack; \ - GetStackTrace(&stack, kStackTraceMax, pc, bp, 0, \ - common_flags()->fast_unwind_on_fatal) + stack.Unwind(pc, bp, nullptr, \ + common_flags()->fast_unwind_on_fatal) #define GET_STACK_TRACE_SIGNAL(sig) \ BufferedStackTrace stack; \ - GetStackTrace(&stack, kStackTraceMax, (sig).pc, (sig).bp, (sig).context, \ - common_flags()->fast_unwind_on_fatal) + stack.Unwind((sig).pc, (sig).bp, (sig).context, \ + common_flags()->fast_unwind_on_fatal) #define GET_STACK_TRACE_FATAL_HERE \ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal) diff --git a/libsanitizer/asan/asan_stats.cc b/libsanitizer/asan/asan_stats.cc deleted file mode 100644 index 64a788a6a5d..00000000000 --- a/libsanitizer/asan/asan_stats.cc +++ /dev/null @@ -1,172 +0,0 @@ -//===-- asan_stats.cc -----------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Code related to statistics collected by AddressSanitizer. -//===----------------------------------------------------------------------===// -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_stats.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_allocator_interface.h" -#include "sanitizer_common/sanitizer_mutex.h" -#include "sanitizer_common/sanitizer_stackdepot.h" - -namespace __asan { - -AsanStats::AsanStats() { - Clear(); -} - -void AsanStats::Clear() { - CHECK(REAL(memset)); - REAL(memset)(this, 0, sizeof(AsanStats)); -} - -static void PrintMallocStatsArray(const char *prefix, - uptr (&array)[kNumberOfSizeClasses]) { - Printf("%s", prefix); - for (uptr i = 0; i < kNumberOfSizeClasses; i++) { - if (!array[i]) continue; - Printf("%zu:%zu; ", i, array[i]); - } - Printf("\n"); -} - -void AsanStats::Print() { - Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n", - malloced>>20, malloced_redzones>>20, mallocs); - Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs); - Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees); - Printf("Stats: %zuM really freed by %zu calls\n", - really_freed>>20, real_frees); - Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n", - (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20, - mmaps, munmaps); - - PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size); - Printf("Stats: malloc large: %zu\n", malloc_large); -} - -void AsanStats::MergeFrom(const AsanStats *stats) { - uptr *dst_ptr = reinterpret_cast(this); - const uptr *src_ptr = reinterpret_cast(stats); - uptr num_fields = sizeof(*this) / sizeof(uptr); - for (uptr i = 0; i < num_fields; i++) - dst_ptr[i] += src_ptr[i]; -} - -static BlockingMutex print_lock(LINKER_INITIALIZED); - -static AsanStats unknown_thread_stats(LINKER_INITIALIZED); -static AsanStats dead_threads_stats(LINKER_INITIALIZED); -static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED); -// Required for malloc_zone_statistics() on OS X. This can't be stored in -// per-thread AsanStats. -static uptr max_malloced_memory; - -static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) { - AsanStats *accumulated_stats = reinterpret_cast(arg); - AsanThreadContext *tctx = static_cast(tctx_base); - if (AsanThread *t = tctx->thread) - accumulated_stats->MergeFrom(&t->stats()); -} - -static void GetAccumulatedStats(AsanStats *stats) { - stats->Clear(); - { - ThreadRegistryLock l(&asanThreadRegistry()); - asanThreadRegistry() - .RunCallbackForEachThreadLocked(MergeThreadStats, stats); - } - stats->MergeFrom(&unknown_thread_stats); - { - BlockingMutexLock lock(&dead_threads_stats_lock); - stats->MergeFrom(&dead_threads_stats); - } - // This is not very accurate: we may miss allocation peaks that happen - // between two updates of accumulated_stats_. For more accurate bookkeeping - // the maximum should be updated on every malloc(), which is unacceptable. - if (max_malloced_memory < stats->malloced) { - max_malloced_memory = stats->malloced; - } -} - -void FlushToDeadThreadStats(AsanStats *stats) { - BlockingMutexLock lock(&dead_threads_stats_lock); - dead_threads_stats.MergeFrom(stats); - stats->Clear(); -} - -void FillMallocStatistics(AsanMallocStats *malloc_stats) { - AsanStats stats; - GetAccumulatedStats(&stats); - malloc_stats->blocks_in_use = stats.mallocs; - malloc_stats->size_in_use = stats.malloced; - malloc_stats->max_size_in_use = max_malloced_memory; - malloc_stats->size_allocated = stats.mmaped; -} - -AsanStats &GetCurrentThreadStats() { - AsanThread *t = GetCurrentThread(); - return (t) ? t->stats() : unknown_thread_stats; -} - -static void PrintAccumulatedStats() { - AsanStats stats; - GetAccumulatedStats(&stats); - // Use lock to keep reports from mixing up. - BlockingMutexLock lock(&print_lock); - stats.Print(); - StackDepotStats *stack_depot_stats = StackDepotGetStats(); - Printf("Stats: StackDepot: %zd ids; %zdM allocated\n", - stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20); - PrintInternalAllocatorStats(); -} - -} // namespace __asan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -uptr __sanitizer_get_current_allocated_bytes() { - AsanStats stats; - GetAccumulatedStats(&stats); - uptr malloced = stats.malloced; - uptr freed = stats.freed; - // Return sane value if malloced < freed due to racy - // way we update accumulated stats. - return (malloced > freed) ? malloced - freed : 1; -} - -uptr __sanitizer_get_heap_size() { - AsanStats stats; - GetAccumulatedStats(&stats); - return stats.mmaped - stats.munmaped; -} - -uptr __sanitizer_get_free_bytes() { - AsanStats stats; - GetAccumulatedStats(&stats); - uptr total_free = stats.mmaped - - stats.munmaped - + stats.really_freed; - uptr total_used = stats.malloced - + stats.malloced_redzones; - // Return sane value if total_free < total_used due to racy - // way we update accumulated stats. - return (total_free > total_used) ? total_free - total_used : 1; -} - -uptr __sanitizer_get_unmapped_bytes() { - return 0; -} - -void __asan_print_accumulated_stats() { - PrintAccumulatedStats(); -} diff --git a/libsanitizer/asan/asan_stats.cpp b/libsanitizer/asan/asan_stats.cpp new file mode 100644 index 00000000000..bc4e8c15cc1 --- /dev/null +++ b/libsanitizer/asan/asan_stats.cpp @@ -0,0 +1,173 @@ +//===-- asan_stats.cpp ----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Code related to statistics collected by AddressSanitizer. +//===----------------------------------------------------------------------===// +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_stats.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_allocator_interface.h" +#include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + +namespace __asan { + +AsanStats::AsanStats() { + Clear(); +} + +void AsanStats::Clear() { + CHECK(REAL(memset)); + REAL(memset)(this, 0, sizeof(AsanStats)); +} + +static void PrintMallocStatsArray(const char *prefix, + uptr (&array)[kNumberOfSizeClasses]) { + Printf("%s", prefix); + for (uptr i = 0; i < kNumberOfSizeClasses; i++) { + if (!array[i]) continue; + Printf("%zu:%zu; ", i, array[i]); + } + Printf("\n"); +} + +void AsanStats::Print() { + Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n", + malloced>>20, malloced_redzones>>20, mallocs); + Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs); + Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees); + Printf("Stats: %zuM really freed by %zu calls\n", + really_freed>>20, real_frees); + Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n", + (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20, + mmaps, munmaps); + + PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size); + Printf("Stats: malloc large: %zu\n", malloc_large); +} + +void AsanStats::MergeFrom(const AsanStats *stats) { + uptr *dst_ptr = reinterpret_cast(this); + const uptr *src_ptr = reinterpret_cast(stats); + uptr num_fields = sizeof(*this) / sizeof(uptr); + for (uptr i = 0; i < num_fields; i++) + dst_ptr[i] += src_ptr[i]; +} + +static BlockingMutex print_lock(LINKER_INITIALIZED); + +static AsanStats unknown_thread_stats(LINKER_INITIALIZED); +static AsanStats dead_threads_stats(LINKER_INITIALIZED); +static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED); +// Required for malloc_zone_statistics() on OS X. This can't be stored in +// per-thread AsanStats. +static uptr max_malloced_memory; + +static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) { + AsanStats *accumulated_stats = reinterpret_cast(arg); + AsanThreadContext *tctx = static_cast(tctx_base); + if (AsanThread *t = tctx->thread) + accumulated_stats->MergeFrom(&t->stats()); +} + +static void GetAccumulatedStats(AsanStats *stats) { + stats->Clear(); + { + ThreadRegistryLock l(&asanThreadRegistry()); + asanThreadRegistry() + .RunCallbackForEachThreadLocked(MergeThreadStats, stats); + } + stats->MergeFrom(&unknown_thread_stats); + { + BlockingMutexLock lock(&dead_threads_stats_lock); + stats->MergeFrom(&dead_threads_stats); + } + // This is not very accurate: we may miss allocation peaks that happen + // between two updates of accumulated_stats_. For more accurate bookkeeping + // the maximum should be updated on every malloc(), which is unacceptable. + if (max_malloced_memory < stats->malloced) { + max_malloced_memory = stats->malloced; + } +} + +void FlushToDeadThreadStats(AsanStats *stats) { + BlockingMutexLock lock(&dead_threads_stats_lock); + dead_threads_stats.MergeFrom(stats); + stats->Clear(); +} + +void FillMallocStatistics(AsanMallocStats *malloc_stats) { + AsanStats stats; + GetAccumulatedStats(&stats); + malloc_stats->blocks_in_use = stats.mallocs; + malloc_stats->size_in_use = stats.malloced; + malloc_stats->max_size_in_use = max_malloced_memory; + malloc_stats->size_allocated = stats.mmaped; +} + +AsanStats &GetCurrentThreadStats() { + AsanThread *t = GetCurrentThread(); + return (t) ? t->stats() : unknown_thread_stats; +} + +static void PrintAccumulatedStats() { + AsanStats stats; + GetAccumulatedStats(&stats); + // Use lock to keep reports from mixing up. + BlockingMutexLock lock(&print_lock); + stats.Print(); + StackDepotStats *stack_depot_stats = StackDepotGetStats(); + Printf("Stats: StackDepot: %zd ids; %zdM allocated\n", + stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20); + PrintInternalAllocatorStats(); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +uptr __sanitizer_get_current_allocated_bytes() { + AsanStats stats; + GetAccumulatedStats(&stats); + uptr malloced = stats.malloced; + uptr freed = stats.freed; + // Return sane value if malloced < freed due to racy + // way we update accumulated stats. + return (malloced > freed) ? malloced - freed : 1; +} + +uptr __sanitizer_get_heap_size() { + AsanStats stats; + GetAccumulatedStats(&stats); + return stats.mmaped - stats.munmaped; +} + +uptr __sanitizer_get_free_bytes() { + AsanStats stats; + GetAccumulatedStats(&stats); + uptr total_free = stats.mmaped + - stats.munmaped + + stats.really_freed; + uptr total_used = stats.malloced + + stats.malloced_redzones; + // Return sane value if total_free < total_used due to racy + // way we update accumulated stats. + return (total_free > total_used) ? total_free - total_used : 1; +} + +uptr __sanitizer_get_unmapped_bytes() { + return 0; +} + +void __asan_print_accumulated_stats() { + PrintAccumulatedStats(); +} diff --git a/libsanitizer/asan/asan_stats.h b/libsanitizer/asan/asan_stats.h index a48e3f916a9..d6da6534081 100644 --- a/libsanitizer/asan/asan_stats.h +++ b/libsanitizer/asan/asan_stats.h @@ -1,7 +1,8 @@ //===-- asan_stats.h --------------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/asan/asan_suppressions.cc b/libsanitizer/asan/asan_suppressions.cc deleted file mode 100644 index 00406020054..00000000000 --- a/libsanitizer/asan/asan_suppressions.cc +++ /dev/null @@ -1,103 +0,0 @@ -//===-- asan_suppressions.cc ----------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Issue suppression and suppression-related functions. -//===----------------------------------------------------------------------===// - -#include "asan_suppressions.h" - -#include "asan_stack.h" -#include "sanitizer_common/sanitizer_placement_new.h" -#include "sanitizer_common/sanitizer_suppressions.h" -#include "sanitizer_common/sanitizer_symbolizer.h" - -namespace __asan { - -ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; -static SuppressionContext *suppression_ctx = nullptr; -static const char kInterceptorName[] = "interceptor_name"; -static const char kInterceptorViaFunction[] = "interceptor_via_fun"; -static const char kInterceptorViaLibrary[] = "interceptor_via_lib"; -static const char kODRViolation[] = "odr_violation"; -static const char *kSuppressionTypes[] = { - kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary, - kODRViolation}; - -SANITIZER_INTERFACE_WEAK_DEF(const char *, __asan_default_suppressions, void) { - return ""; -} - -void InitializeSuppressions() { - CHECK_EQ(nullptr, suppression_ctx); - suppression_ctx = new (suppression_placeholder) // NOLINT - SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); - suppression_ctx->ParseFromFile(flags()->suppressions); - if (&__asan_default_suppressions) - suppression_ctx->Parse(__asan_default_suppressions()); -} - -bool IsInterceptorSuppressed(const char *interceptor_name) { - CHECK(suppression_ctx); - Suppression *s; - // Match "interceptor_name" suppressions. - return suppression_ctx->Match(interceptor_name, kInterceptorName, &s); -} - -bool HaveStackTraceBasedSuppressions() { - CHECK(suppression_ctx); - return suppression_ctx->HasSuppressionType(kInterceptorViaFunction) || - suppression_ctx->HasSuppressionType(kInterceptorViaLibrary); -} - -bool IsODRViolationSuppressed(const char *global_var_name) { - CHECK(suppression_ctx); - Suppression *s; - // Match "odr_violation" suppressions. - return suppression_ctx->Match(global_var_name, kODRViolation, &s); -} - -bool IsStackTraceSuppressed(const StackTrace *stack) { - if (!HaveStackTraceBasedSuppressions()) - return false; - - CHECK(suppression_ctx); - Symbolizer *symbolizer = Symbolizer::GetOrInit(); - Suppression *s; - for (uptr i = 0; i < stack->size && stack->trace[i]; i++) { - uptr addr = stack->trace[i]; - - if (suppression_ctx->HasSuppressionType(kInterceptorViaLibrary)) { - // Match "interceptor_via_lib" suppressions. - if (const char *module_name = symbolizer->GetModuleNameForPc(addr)) - if (suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s)) - return true; - } - - if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) { - SymbolizedStack *frames = symbolizer->SymbolizePC(addr); - CHECK(frames); - for (SymbolizedStack *cur = frames; cur; cur = cur->next) { - const char *function_name = cur->info.function; - if (!function_name) { - continue; - } - // Match "interceptor_via_fun" suppressions. - if (suppression_ctx->Match(function_name, kInterceptorViaFunction, - &s)) { - frames->ClearAll(); - return true; - } - } - frames->ClearAll(); - } - } - return false; -} - -} // namespace __asan diff --git a/libsanitizer/asan/asan_suppressions.cpp b/libsanitizer/asan/asan_suppressions.cpp new file mode 100644 index 00000000000..a9c0d107694 --- /dev/null +++ b/libsanitizer/asan/asan_suppressions.cpp @@ -0,0 +1,104 @@ +//===-- asan_suppressions.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Issue suppression and suppression-related functions. +//===----------------------------------------------------------------------===// + +#include "asan_suppressions.h" + +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_suppressions.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +namespace __asan { + +ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; +static SuppressionContext *suppression_ctx = nullptr; +static const char kInterceptorName[] = "interceptor_name"; +static const char kInterceptorViaFunction[] = "interceptor_via_fun"; +static const char kInterceptorViaLibrary[] = "interceptor_via_lib"; +static const char kODRViolation[] = "odr_violation"; +static const char *kSuppressionTypes[] = { + kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary, + kODRViolation}; + +SANITIZER_INTERFACE_WEAK_DEF(const char *, __asan_default_suppressions, void) { + return ""; +} + +void InitializeSuppressions() { + CHECK_EQ(nullptr, suppression_ctx); + suppression_ctx = new (suppression_placeholder) // NOLINT + SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); + suppression_ctx->ParseFromFile(flags()->suppressions); + if (&__asan_default_suppressions) + suppression_ctx->Parse(__asan_default_suppressions()); +} + +bool IsInterceptorSuppressed(const char *interceptor_name) { + CHECK(suppression_ctx); + Suppression *s; + // Match "interceptor_name" suppressions. + return suppression_ctx->Match(interceptor_name, kInterceptorName, &s); +} + +bool HaveStackTraceBasedSuppressions() { + CHECK(suppression_ctx); + return suppression_ctx->HasSuppressionType(kInterceptorViaFunction) || + suppression_ctx->HasSuppressionType(kInterceptorViaLibrary); +} + +bool IsODRViolationSuppressed(const char *global_var_name) { + CHECK(suppression_ctx); + Suppression *s; + // Match "odr_violation" suppressions. + return suppression_ctx->Match(global_var_name, kODRViolation, &s); +} + +bool IsStackTraceSuppressed(const StackTrace *stack) { + if (!HaveStackTraceBasedSuppressions()) + return false; + + CHECK(suppression_ctx); + Symbolizer *symbolizer = Symbolizer::GetOrInit(); + Suppression *s; + for (uptr i = 0; i < stack->size && stack->trace[i]; i++) { + uptr addr = stack->trace[i]; + + if (suppression_ctx->HasSuppressionType(kInterceptorViaLibrary)) { + // Match "interceptor_via_lib" suppressions. + if (const char *module_name = symbolizer->GetModuleNameForPc(addr)) + if (suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s)) + return true; + } + + if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) { + SymbolizedStack *frames = symbolizer->SymbolizePC(addr); + CHECK(frames); + for (SymbolizedStack *cur = frames; cur; cur = cur->next) { + const char *function_name = cur->info.function; + if (!function_name) { + continue; + } + // Match "interceptor_via_fun" suppressions. + if (suppression_ctx->Match(function_name, kInterceptorViaFunction, + &s)) { + frames->ClearAll(); + return true; + } + } + frames->ClearAll(); + } + } + return false; +} + +} // namespace __asan diff --git a/libsanitizer/asan/asan_suppressions.h b/libsanitizer/asan/asan_suppressions.h index 331d7224548..121d4ddf187 100644 --- a/libsanitizer/asan/asan_suppressions.h +++ b/libsanitizer/asan/asan_suppressions.h @@ -1,13 +1,14 @@ //===-- asan_suppressions.h -------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_suppressions.cc. +// ASan-private header for asan_suppressions.cpp. //===----------------------------------------------------------------------===// #ifndef ASAN_SUPPRESSIONS_H #define ASAN_SUPPRESSIONS_H diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc deleted file mode 100644 index 82da9a28e82..00000000000 --- a/libsanitizer/asan/asan_thread.cc +++ /dev/null @@ -1,527 +0,0 @@ -//===-- asan_thread.cc ----------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Thread-related code. -//===----------------------------------------------------------------------===// -#include "asan_allocator.h" -#include "asan_interceptors.h" -#include "asan_poisoning.h" -#include "asan_stack.h" -#include "asan_thread.h" -#include "asan_mapping.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_placement_new.h" -#include "sanitizer_common/sanitizer_stackdepot.h" -#include "sanitizer_common/sanitizer_tls_get_addr.h" -#include "lsan/lsan_common.h" - -namespace __asan { - -// AsanThreadContext implementation. - -void AsanThreadContext::OnCreated(void *arg) { - CreateThreadContextArgs *args = static_cast(arg); - if (args->stack) - stack_id = StackDepotPut(*args->stack); - thread = args->thread; - thread->set_context(this); -} - -void AsanThreadContext::OnFinished() { - // Drop the link to the AsanThread object. - thread = nullptr; -} - -// MIPS requires aligned address -static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)]; -static ThreadRegistry *asan_thread_registry; - -static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED); -static LowLevelAllocator allocator_for_thread_context; - -static ThreadContextBase *GetAsanThreadContext(u32 tid) { - BlockingMutexLock lock(&mu_for_thread_context); - return new(allocator_for_thread_context) AsanThreadContext(tid); -} - -ThreadRegistry &asanThreadRegistry() { - static bool initialized; - // Don't worry about thread_safety - this should be called when there is - // a single thread. - if (!initialized) { - // Never reuse ASan threads: we store pointer to AsanThreadContext - // in TSD and can't reliably tell when no more TSD destructors will - // be called. It would be wrong to reuse AsanThreadContext for another - // thread before all TSD destructors will be called for it. - asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry( - GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads); - initialized = true; - } - return *asan_thread_registry; -} - -AsanThreadContext *GetThreadContextByTidLocked(u32 tid) { - return static_cast( - asanThreadRegistry().GetThreadLocked(tid)); -} - -// AsanThread implementation. - -AsanThread *AsanThread::Create(thread_callback_t start_routine, void *arg, - u32 parent_tid, StackTrace *stack, - bool detached) { - uptr PageSize = GetPageSizeCached(); - uptr size = RoundUpTo(sizeof(AsanThread), PageSize); - AsanThread *thread = (AsanThread*)MmapOrDie(size, __func__); - thread->start_routine_ = start_routine; - thread->arg_ = arg; - AsanThreadContext::CreateThreadContextArgs args = {thread, stack}; - asanThreadRegistry().CreateThread(*reinterpret_cast(thread), detached, - parent_tid, &args); - - return thread; -} - -void AsanThread::TSDDtor(void *tsd) { - AsanThreadContext *context = (AsanThreadContext*)tsd; - VReport(1, "T%d TSDDtor\n", context->tid); - if (context->thread) - context->thread->Destroy(); -} - -void AsanThread::Destroy() { - int tid = this->tid(); - VReport(1, "T%d exited\n", tid); - - malloc_storage().CommitBack(); - if (common_flags()->use_sigaltstack) UnsetAlternateSignalStack(); - asanThreadRegistry().FinishThread(tid); - FlushToDeadThreadStats(&stats_); - // We also clear the shadow on thread destruction because - // some code may still be executing in later TSD destructors - // and we don't want it to have any poisoned stack. - ClearShadowForThreadStackAndTLS(); - DeleteFakeStack(tid); - uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached()); - UnmapOrDie(this, size); - DTLS_Destroy(); -} - -void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, - uptr size) { - if (atomic_load(&stack_switching_, memory_order_relaxed)) { - Report("ERROR: starting fiber switch while in fiber switch\n"); - Die(); - } - - next_stack_bottom_ = bottom; - next_stack_top_ = bottom + size; - atomic_store(&stack_switching_, 1, memory_order_release); - - FakeStack *current_fake_stack = fake_stack_; - if (fake_stack_save) - *fake_stack_save = fake_stack_; - fake_stack_ = nullptr; - SetTLSFakeStack(nullptr); - // if fake_stack_save is null, the fiber will die, delete the fakestack - if (!fake_stack_save && current_fake_stack) - current_fake_stack->Destroy(this->tid()); -} - -void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save, - uptr *bottom_old, - uptr *size_old) { - if (!atomic_load(&stack_switching_, memory_order_relaxed)) { - Report("ERROR: finishing a fiber switch that has not started\n"); - Die(); - } - - if (fake_stack_save) { - SetTLSFakeStack(fake_stack_save); - fake_stack_ = fake_stack_save; - } - - if (bottom_old) - *bottom_old = stack_bottom_; - if (size_old) - *size_old = stack_top_ - stack_bottom_; - stack_bottom_ = next_stack_bottom_; - stack_top_ = next_stack_top_; - atomic_store(&stack_switching_, 0, memory_order_release); - next_stack_top_ = 0; - next_stack_bottom_ = 0; -} - -inline AsanThread::StackBounds AsanThread::GetStackBounds() const { - if (!atomic_load(&stack_switching_, memory_order_acquire)) { - // Make sure the stack bounds are fully initialized. - if (stack_bottom_ >= stack_top_) return {0, 0}; - return {stack_bottom_, stack_top_}; - } - char local; - const uptr cur_stack = (uptr)&local; - // Note: need to check next stack first, because FinishSwitchFiber - // may be in process of overwriting stack_top_/bottom_. But in such case - // we are already on the next stack. - if (cur_stack >= next_stack_bottom_ && cur_stack < next_stack_top_) - return {next_stack_bottom_, next_stack_top_}; - return {stack_bottom_, stack_top_}; -} - -uptr AsanThread::stack_top() { - return GetStackBounds().top; -} - -uptr AsanThread::stack_bottom() { - return GetStackBounds().bottom; -} - -uptr AsanThread::stack_size() { - const auto bounds = GetStackBounds(); - return bounds.top - bounds.bottom; -} - -// We want to create the FakeStack lazyly on the first use, but not eralier -// than the stack size is known and the procedure has to be async-signal safe. -FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { - uptr stack_size = this->stack_size(); - if (stack_size == 0) // stack_size is not yet available, don't use FakeStack. - return nullptr; - uptr old_val = 0; - // fake_stack_ has 3 states: - // 0 -- not initialized - // 1 -- being initialized - // ptr -- initialized - // This CAS checks if the state was 0 and if so changes it to state 1, - // if that was successful, it initializes the pointer. - if (atomic_compare_exchange_strong( - reinterpret_cast(&fake_stack_), &old_val, 1UL, - memory_order_relaxed)) { - uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size)); - CHECK_LE(flags()->min_uar_stack_size_log, flags()->max_uar_stack_size_log); - stack_size_log = - Min(stack_size_log, static_cast(flags()->max_uar_stack_size_log)); - stack_size_log = - Max(stack_size_log, static_cast(flags()->min_uar_stack_size_log)); - fake_stack_ = FakeStack::Create(stack_size_log); - SetTLSFakeStack(fake_stack_); - return fake_stack_; - } - return nullptr; -} - -void AsanThread::Init(const InitOptions *options) { - next_stack_top_ = next_stack_bottom_ = 0; - atomic_store(&stack_switching_, false, memory_order_release); - CHECK_EQ(this->stack_size(), 0U); - SetThreadStackAndTls(options); - CHECK_GT(this->stack_size(), 0U); - CHECK(AddrIsInMem(stack_bottom_)); - CHECK(AddrIsInMem(stack_top_ - 1)); - ClearShadowForThreadStackAndTLS(); - fake_stack_ = nullptr; - if (__asan_option_detect_stack_use_after_return) - AsyncSignalSafeLazyInitFakeStack(); - int local = 0; - VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), - (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_, - &local); -} - -// Fuchsia and RTEMS don't use ThreadStart. -// asan_fuchsia.c/asan_rtems.c define CreateMainThread and -// SetThreadStackAndTls. -#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS - -thread_return_t AsanThread::ThreadStart( - tid_t os_id, atomic_uintptr_t *signal_thread_is_registered) { - Init(); - asanThreadRegistry().StartThread(tid(), os_id, /*workerthread*/ false, - nullptr); - if (signal_thread_is_registered) - atomic_store(signal_thread_is_registered, 1, memory_order_release); - - if (common_flags()->use_sigaltstack) SetAlternateSignalStack(); - - if (!start_routine_) { - // start_routine_ == 0 if we're on the main thread or on one of the - // OS X libdispatch worker threads. But nobody is supposed to call - // ThreadStart() for the worker threads. - CHECK_EQ(tid(), 0); - return 0; - } - - thread_return_t res = start_routine_(arg_); - - // On POSIX systems we defer this to the TSD destructor. LSan will consider - // the thread's memory as non-live from the moment we call Destroy(), even - // though that memory might contain pointers to heap objects which will be - // cleaned up by a user-defined TSD destructor. Thus, calling Destroy() before - // the TSD destructors have run might cause false positives in LSan. - if (!SANITIZER_POSIX) - this->Destroy(); - - return res; -} - -AsanThread *CreateMainThread() { - AsanThread *main_thread = AsanThread::Create( - /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ 0, - /* stack */ nullptr, /* detached */ true); - SetCurrentThread(main_thread); - main_thread->ThreadStart(internal_getpid(), - /* signal_thread_is_registered */ nullptr); - return main_thread; -} - -// This implementation doesn't use the argument, which is just passed down -// from the caller of Init (which see, above). It's only there to support -// OS-specific implementations that need more information passed through. -void AsanThread::SetThreadStackAndTls(const InitOptions *options) { - DCHECK_EQ(options, nullptr); - uptr tls_size = 0; - uptr stack_size = 0; - GetThreadStackAndTls(tid() == 0, const_cast(&stack_bottom_), - const_cast(&stack_size), &tls_begin_, &tls_size); - stack_top_ = stack_bottom_ + stack_size; - tls_end_ = tls_begin_ + tls_size; - dtls_ = DTLS_Get(); - - int local; - CHECK(AddrIsInStack((uptr)&local)); -} - -#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS - -void AsanThread::ClearShadowForThreadStackAndTLS() { - PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); - if (tls_begin_ != tls_end_) { - uptr tls_begin_aligned = RoundDownTo(tls_begin_, SHADOW_GRANULARITY); - uptr tls_end_aligned = RoundUpTo(tls_end_, SHADOW_GRANULARITY); - FastPoisonShadowPartialRightRedzone(tls_begin_aligned, - tls_end_ - tls_begin_aligned, - tls_end_aligned - tls_end_, 0); - } -} - -bool AsanThread::GetStackFrameAccessByAddr(uptr addr, - StackFrameAccess *access) { - uptr bottom = 0; - if (AddrIsInStack(addr)) { - bottom = stack_bottom(); - } else if (has_fake_stack()) { - bottom = fake_stack()->AddrIsInFakeStack(addr); - CHECK(bottom); - access->offset = addr - bottom; - access->frame_pc = ((uptr*)bottom)[2]; - access->frame_descr = (const char *)((uptr*)bottom)[1]; - return true; - } - uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr. - uptr mem_ptr = RoundDownTo(aligned_addr, SHADOW_GRANULARITY); - u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); - u8 *shadow_bottom = (u8*)MemToShadow(bottom); - - while (shadow_ptr >= shadow_bottom && - *shadow_ptr != kAsanStackLeftRedzoneMagic) { - shadow_ptr--; - mem_ptr -= SHADOW_GRANULARITY; - } - - while (shadow_ptr >= shadow_bottom && - *shadow_ptr == kAsanStackLeftRedzoneMagic) { - shadow_ptr--; - mem_ptr -= SHADOW_GRANULARITY; - } - - if (shadow_ptr < shadow_bottom) { - return false; - } - - uptr* ptr = (uptr*)(mem_ptr + SHADOW_GRANULARITY); - CHECK(ptr[0] == kCurrentStackFrameMagic); - access->offset = addr - (uptr)ptr; - access->frame_pc = ptr[2]; - access->frame_descr = (const char*)ptr[1]; - return true; -} - -uptr AsanThread::GetStackVariableShadowStart(uptr addr) { - uptr bottom = 0; - if (AddrIsInStack(addr)) { - bottom = stack_bottom(); - } else if (has_fake_stack()) { - bottom = fake_stack()->AddrIsInFakeStack(addr); - CHECK(bottom); - } else - return 0; - - uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr. - u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); - u8 *shadow_bottom = (u8*)MemToShadow(bottom); - - while (shadow_ptr >= shadow_bottom && - (*shadow_ptr != kAsanStackLeftRedzoneMagic && - *shadow_ptr != kAsanStackMidRedzoneMagic && - *shadow_ptr != kAsanStackRightRedzoneMagic)) - shadow_ptr--; - - return (uptr)shadow_ptr + 1; -} - -bool AsanThread::AddrIsInStack(uptr addr) { - const auto bounds = GetStackBounds(); - return addr >= bounds.bottom && addr < bounds.top; -} - -static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base, - void *addr) { - AsanThreadContext *tctx = static_cast(tctx_base); - AsanThread *t = tctx->thread; - if (!t) return false; - if (t->AddrIsInStack((uptr)addr)) return true; - if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr)) - return true; - return false; -} - -AsanThread *GetCurrentThread() { - if (SANITIZER_RTEMS && !asan_inited) - return nullptr; - - AsanThreadContext *context = - reinterpret_cast(AsanTSDGet()); - if (!context) { - if (SANITIZER_ANDROID) { - // On Android, libc constructor is called _after_ asan_init, and cleans up - // TSD. Try to figure out if this is still the main thread by the stack - // address. We are not entirely sure that we have correct main thread - // limits, so only do this magic on Android, and only if the found thread - // is the main thread. - AsanThreadContext *tctx = GetThreadContextByTidLocked(0); - if (tctx && ThreadStackContainsAddress(tctx, &context)) { - SetCurrentThread(tctx->thread); - return tctx->thread; - } - } - return nullptr; - } - return context->thread; -} - -void SetCurrentThread(AsanThread *t) { - CHECK(t->context()); - VReport(2, "SetCurrentThread: %p for thread %p\n", t->context(), - (void *)GetThreadSelf()); - // Make sure we do not reset the current AsanThread. - CHECK_EQ(0, AsanTSDGet()); - AsanTSDSet(t->context()); - CHECK_EQ(t->context(), AsanTSDGet()); -} - -u32 GetCurrentTidOrInvalid() { - AsanThread *t = GetCurrentThread(); - return t ? t->tid() : kInvalidTid; -} - -AsanThread *FindThreadByStackAddress(uptr addr) { - asanThreadRegistry().CheckLocked(); - AsanThreadContext *tctx = static_cast( - asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress, - (void *)addr)); - return tctx ? tctx->thread : nullptr; -} - -void EnsureMainThreadIDIsCorrect() { - AsanThreadContext *context = - reinterpret_cast(AsanTSDGet()); - if (context && (context->tid == 0)) - context->os_id = GetTid(); -} - -__asan::AsanThread *GetAsanThreadByOsIDLocked(tid_t os_id) { - __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>( - __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id)); - if (!context) return nullptr; - return context->thread; -} -} // namespace __asan - -// --- Implementation of LSan-specific functions --- {{{1 -namespace __lsan { -bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, - uptr *tls_begin, uptr *tls_end, uptr *cache_begin, - uptr *cache_end, DTLS **dtls) { - __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); - if (!t) return false; - *stack_begin = t->stack_bottom(); - *stack_end = t->stack_top(); - *tls_begin = t->tls_begin(); - *tls_end = t->tls_end(); - // ASan doesn't keep allocator caches in TLS, so these are unused. - *cache_begin = 0; - *cache_end = 0; - *dtls = t->dtls(); - return true; -} - -void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback, - void *arg) { - __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); - if (t && t->has_fake_stack()) - t->fake_stack()->ForEachFakeFrame(callback, arg); -} - -void LockThreadRegistry() { - __asan::asanThreadRegistry().Lock(); -} - -void UnlockThreadRegistry() { - __asan::asanThreadRegistry().Unlock(); -} - -ThreadRegistry *GetThreadRegistryLocked() { - __asan::asanThreadRegistry().CheckLocked(); - return &__asan::asanThreadRegistry(); -} - -void EnsureMainThreadIDIsCorrect() { - __asan::EnsureMainThreadIDIsCorrect(); -} -} // namespace __lsan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_start_switch_fiber(void **fakestacksave, const void *bottom, - uptr size) { - AsanThread *t = GetCurrentThread(); - if (!t) { - VReport(1, "__asan_start_switch_fiber called from unknown thread\n"); - return; - } - t->StartSwitchFiber((FakeStack**)fakestacksave, (uptr)bottom, size); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_finish_switch_fiber(void* fakestack, - const void **bottom_old, - uptr *size_old) { - AsanThread *t = GetCurrentThread(); - if (!t) { - VReport(1, "__asan_finish_switch_fiber called from unknown thread\n"); - return; - } - t->FinishSwitchFiber((FakeStack*)fakestack, - (uptr*)bottom_old, - (uptr*)size_old); -} -} diff --git a/libsanitizer/asan/asan_thread.cpp b/libsanitizer/asan/asan_thread.cpp new file mode 100644 index 00000000000..d48b3414dd5 --- /dev/null +++ b/libsanitizer/asan/asan_thread.cpp @@ -0,0 +1,535 @@ +//===-- asan_thread.cpp ---------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Thread-related code. +//===----------------------------------------------------------------------===// +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_poisoning.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "asan_mapping.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_tls_get_addr.h" +#include "lsan/lsan_common.h" + +namespace __asan { + +// AsanThreadContext implementation. + +void AsanThreadContext::OnCreated(void *arg) { + CreateThreadContextArgs *args = static_cast(arg); + if (args->stack) + stack_id = StackDepotPut(*args->stack); + thread = args->thread; + thread->set_context(this); +} + +void AsanThreadContext::OnFinished() { + // Drop the link to the AsanThread object. + thread = nullptr; +} + +// MIPS requires aligned address +static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)]; +static ThreadRegistry *asan_thread_registry; + +static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED); +static LowLevelAllocator allocator_for_thread_context; + +static ThreadContextBase *GetAsanThreadContext(u32 tid) { + BlockingMutexLock lock(&mu_for_thread_context); + return new(allocator_for_thread_context) AsanThreadContext(tid); +} + +ThreadRegistry &asanThreadRegistry() { + static bool initialized; + // Don't worry about thread_safety - this should be called when there is + // a single thread. + if (!initialized) { + // Never reuse ASan threads: we store pointer to AsanThreadContext + // in TSD and can't reliably tell when no more TSD destructors will + // be called. It would be wrong to reuse AsanThreadContext for another + // thread before all TSD destructors will be called for it. + asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry( + GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads); + initialized = true; + } + return *asan_thread_registry; +} + +AsanThreadContext *GetThreadContextByTidLocked(u32 tid) { + return static_cast( + asanThreadRegistry().GetThreadLocked(tid)); +} + +// AsanThread implementation. + +AsanThread *AsanThread::Create(thread_callback_t start_routine, void *arg, + u32 parent_tid, StackTrace *stack, + bool detached) { + uptr PageSize = GetPageSizeCached(); + uptr size = RoundUpTo(sizeof(AsanThread), PageSize); + AsanThread *thread = (AsanThread*)MmapOrDie(size, __func__); + thread->start_routine_ = start_routine; + thread->arg_ = arg; + AsanThreadContext::CreateThreadContextArgs args = {thread, stack}; + asanThreadRegistry().CreateThread(*reinterpret_cast(thread), detached, + parent_tid, &args); + + return thread; +} + +void AsanThread::TSDDtor(void *tsd) { + AsanThreadContext *context = (AsanThreadContext*)tsd; + VReport(1, "T%d TSDDtor\n", context->tid); + if (context->thread) + context->thread->Destroy(); +} + +void AsanThread::Destroy() { + int tid = this->tid(); + VReport(1, "T%d exited\n", tid); + + malloc_storage().CommitBack(); + if (common_flags()->use_sigaltstack) UnsetAlternateSignalStack(); + asanThreadRegistry().FinishThread(tid); + FlushToDeadThreadStats(&stats_); + // We also clear the shadow on thread destruction because + // some code may still be executing in later TSD destructors + // and we don't want it to have any poisoned stack. + ClearShadowForThreadStackAndTLS(); + DeleteFakeStack(tid); + uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached()); + UnmapOrDie(this, size); + DTLS_Destroy(); +} + +void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, + uptr size) { + if (atomic_load(&stack_switching_, memory_order_relaxed)) { + Report("ERROR: starting fiber switch while in fiber switch\n"); + Die(); + } + + next_stack_bottom_ = bottom; + next_stack_top_ = bottom + size; + atomic_store(&stack_switching_, 1, memory_order_release); + + FakeStack *current_fake_stack = fake_stack_; + if (fake_stack_save) + *fake_stack_save = fake_stack_; + fake_stack_ = nullptr; + SetTLSFakeStack(nullptr); + // if fake_stack_save is null, the fiber will die, delete the fakestack + if (!fake_stack_save && current_fake_stack) + current_fake_stack->Destroy(this->tid()); +} + +void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save, + uptr *bottom_old, + uptr *size_old) { + if (!atomic_load(&stack_switching_, memory_order_relaxed)) { + Report("ERROR: finishing a fiber switch that has not started\n"); + Die(); + } + + if (fake_stack_save) { + SetTLSFakeStack(fake_stack_save); + fake_stack_ = fake_stack_save; + } + + if (bottom_old) + *bottom_old = stack_bottom_; + if (size_old) + *size_old = stack_top_ - stack_bottom_; + stack_bottom_ = next_stack_bottom_; + stack_top_ = next_stack_top_; + atomic_store(&stack_switching_, 0, memory_order_release); + next_stack_top_ = 0; + next_stack_bottom_ = 0; +} + +inline AsanThread::StackBounds AsanThread::GetStackBounds() const { + if (!atomic_load(&stack_switching_, memory_order_acquire)) { + // Make sure the stack bounds are fully initialized. + if (stack_bottom_ >= stack_top_) return {0, 0}; + return {stack_bottom_, stack_top_}; + } + char local; + const uptr cur_stack = (uptr)&local; + // Note: need to check next stack first, because FinishSwitchFiber + // may be in process of overwriting stack_top_/bottom_. But in such case + // we are already on the next stack. + if (cur_stack >= next_stack_bottom_ && cur_stack < next_stack_top_) + return {next_stack_bottom_, next_stack_top_}; + return {stack_bottom_, stack_top_}; +} + +uptr AsanThread::stack_top() { + return GetStackBounds().top; +} + +uptr AsanThread::stack_bottom() { + return GetStackBounds().bottom; +} + +uptr AsanThread::stack_size() { + const auto bounds = GetStackBounds(); + return bounds.top - bounds.bottom; +} + +// We want to create the FakeStack lazyly on the first use, but not eralier +// than the stack size is known and the procedure has to be async-signal safe. +FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { + uptr stack_size = this->stack_size(); + if (stack_size == 0) // stack_size is not yet available, don't use FakeStack. + return nullptr; + uptr old_val = 0; + // fake_stack_ has 3 states: + // 0 -- not initialized + // 1 -- being initialized + // ptr -- initialized + // This CAS checks if the state was 0 and if so changes it to state 1, + // if that was successful, it initializes the pointer. + if (atomic_compare_exchange_strong( + reinterpret_cast(&fake_stack_), &old_val, 1UL, + memory_order_relaxed)) { + uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size)); + CHECK_LE(flags()->min_uar_stack_size_log, flags()->max_uar_stack_size_log); + stack_size_log = + Min(stack_size_log, static_cast(flags()->max_uar_stack_size_log)); + stack_size_log = + Max(stack_size_log, static_cast(flags()->min_uar_stack_size_log)); + fake_stack_ = FakeStack::Create(stack_size_log); + SetTLSFakeStack(fake_stack_); + return fake_stack_; + } + return nullptr; +} + +void AsanThread::Init(const InitOptions *options) { + next_stack_top_ = next_stack_bottom_ = 0; + atomic_store(&stack_switching_, false, memory_order_release); + CHECK_EQ(this->stack_size(), 0U); + SetThreadStackAndTls(options); + if (stack_top_ != stack_bottom_) { + CHECK_GT(this->stack_size(), 0U); + CHECK(AddrIsInMem(stack_bottom_)); + CHECK(AddrIsInMem(stack_top_ - 1)); + } + ClearShadowForThreadStackAndTLS(); + fake_stack_ = nullptr; + if (__asan_option_detect_stack_use_after_return) + AsyncSignalSafeLazyInitFakeStack(); + int local = 0; + VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), + (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_, + &local); +} + +// Fuchsia and RTEMS don't use ThreadStart. +// asan_fuchsia.c/asan_rtems.c define CreateMainThread and +// SetThreadStackAndTls. +#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS + +thread_return_t AsanThread::ThreadStart( + tid_t os_id, atomic_uintptr_t *signal_thread_is_registered) { + Init(); + asanThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular, nullptr); + if (signal_thread_is_registered) + atomic_store(signal_thread_is_registered, 1, memory_order_release); + + if (common_flags()->use_sigaltstack) SetAlternateSignalStack(); + + if (!start_routine_) { + // start_routine_ == 0 if we're on the main thread or on one of the + // OS X libdispatch worker threads. But nobody is supposed to call + // ThreadStart() for the worker threads. + CHECK_EQ(tid(), 0); + return 0; + } + + thread_return_t res = start_routine_(arg_); + + // On POSIX systems we defer this to the TSD destructor. LSan will consider + // the thread's memory as non-live from the moment we call Destroy(), even + // though that memory might contain pointers to heap objects which will be + // cleaned up by a user-defined TSD destructor. Thus, calling Destroy() before + // the TSD destructors have run might cause false positives in LSan. + if (!SANITIZER_POSIX) + this->Destroy(); + + return res; +} + +AsanThread *CreateMainThread() { + AsanThread *main_thread = AsanThread::Create( + /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ 0, + /* stack */ nullptr, /* detached */ true); + SetCurrentThread(main_thread); + main_thread->ThreadStart(internal_getpid(), + /* signal_thread_is_registered */ nullptr); + return main_thread; +} + +// This implementation doesn't use the argument, which is just passed down +// from the caller of Init (which see, above). It's only there to support +// OS-specific implementations that need more information passed through. +void AsanThread::SetThreadStackAndTls(const InitOptions *options) { + DCHECK_EQ(options, nullptr); + uptr tls_size = 0; + uptr stack_size = 0; + GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size, &tls_begin_, + &tls_size); + stack_top_ = stack_bottom_ + stack_size; + tls_end_ = tls_begin_ + tls_size; + dtls_ = DTLS_Get(); + + if (stack_top_ != stack_bottom_) { + int local; + CHECK(AddrIsInStack((uptr)&local)); + } +} + +#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS + +void AsanThread::ClearShadowForThreadStackAndTLS() { + if (stack_top_ != stack_bottom_) + PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); + if (tls_begin_ != tls_end_) { + uptr tls_begin_aligned = RoundDownTo(tls_begin_, SHADOW_GRANULARITY); + uptr tls_end_aligned = RoundUpTo(tls_end_, SHADOW_GRANULARITY); + FastPoisonShadowPartialRightRedzone(tls_begin_aligned, + tls_end_ - tls_begin_aligned, + tls_end_aligned - tls_end_, 0); + } +} + +bool AsanThread::GetStackFrameAccessByAddr(uptr addr, + StackFrameAccess *access) { + if (stack_top_ == stack_bottom_) + return false; + + uptr bottom = 0; + if (AddrIsInStack(addr)) { + bottom = stack_bottom(); + } else if (has_fake_stack()) { + bottom = fake_stack()->AddrIsInFakeStack(addr); + CHECK(bottom); + access->offset = addr - bottom; + access->frame_pc = ((uptr*)bottom)[2]; + access->frame_descr = (const char *)((uptr*)bottom)[1]; + return true; + } + uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr. + uptr mem_ptr = RoundDownTo(aligned_addr, SHADOW_GRANULARITY); + u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); + u8 *shadow_bottom = (u8*)MemToShadow(bottom); + + while (shadow_ptr >= shadow_bottom && + *shadow_ptr != kAsanStackLeftRedzoneMagic) { + shadow_ptr--; + mem_ptr -= SHADOW_GRANULARITY; + } + + while (shadow_ptr >= shadow_bottom && + *shadow_ptr == kAsanStackLeftRedzoneMagic) { + shadow_ptr--; + mem_ptr -= SHADOW_GRANULARITY; + } + + if (shadow_ptr < shadow_bottom) { + return false; + } + + uptr* ptr = (uptr*)(mem_ptr + SHADOW_GRANULARITY); + CHECK(ptr[0] == kCurrentStackFrameMagic); + access->offset = addr - (uptr)ptr; + access->frame_pc = ptr[2]; + access->frame_descr = (const char*)ptr[1]; + return true; +} + +uptr AsanThread::GetStackVariableShadowStart(uptr addr) { + uptr bottom = 0; + if (AddrIsInStack(addr)) { + bottom = stack_bottom(); + } else if (has_fake_stack()) { + bottom = fake_stack()->AddrIsInFakeStack(addr); + CHECK(bottom); + } else + return 0; + + uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr. + u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); + u8 *shadow_bottom = (u8*)MemToShadow(bottom); + + while (shadow_ptr >= shadow_bottom && + (*shadow_ptr != kAsanStackLeftRedzoneMagic && + *shadow_ptr != kAsanStackMidRedzoneMagic && + *shadow_ptr != kAsanStackRightRedzoneMagic)) + shadow_ptr--; + + return (uptr)shadow_ptr + 1; +} + +bool AsanThread::AddrIsInStack(uptr addr) { + const auto bounds = GetStackBounds(); + return addr >= bounds.bottom && addr < bounds.top; +} + +static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base, + void *addr) { + AsanThreadContext *tctx = static_cast(tctx_base); + AsanThread *t = tctx->thread; + if (!t) return false; + if (t->AddrIsInStack((uptr)addr)) return true; + if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr)) + return true; + return false; +} + +AsanThread *GetCurrentThread() { + if (SANITIZER_RTEMS && !asan_inited) + return nullptr; + + AsanThreadContext *context = + reinterpret_cast(AsanTSDGet()); + if (!context) { + if (SANITIZER_ANDROID) { + // On Android, libc constructor is called _after_ asan_init, and cleans up + // TSD. Try to figure out if this is still the main thread by the stack + // address. We are not entirely sure that we have correct main thread + // limits, so only do this magic on Android, and only if the found thread + // is the main thread. + AsanThreadContext *tctx = GetThreadContextByTidLocked(0); + if (tctx && ThreadStackContainsAddress(tctx, &context)) { + SetCurrentThread(tctx->thread); + return tctx->thread; + } + } + return nullptr; + } + return context->thread; +} + +void SetCurrentThread(AsanThread *t) { + CHECK(t->context()); + VReport(2, "SetCurrentThread: %p for thread %p\n", t->context(), + (void *)GetThreadSelf()); + // Make sure we do not reset the current AsanThread. + CHECK_EQ(0, AsanTSDGet()); + AsanTSDSet(t->context()); + CHECK_EQ(t->context(), AsanTSDGet()); +} + +u32 GetCurrentTidOrInvalid() { + AsanThread *t = GetCurrentThread(); + return t ? t->tid() : kInvalidTid; +} + +AsanThread *FindThreadByStackAddress(uptr addr) { + asanThreadRegistry().CheckLocked(); + AsanThreadContext *tctx = static_cast( + asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress, + (void *)addr)); + return tctx ? tctx->thread : nullptr; +} + +void EnsureMainThreadIDIsCorrect() { + AsanThreadContext *context = + reinterpret_cast(AsanTSDGet()); + if (context && (context->tid == 0)) + context->os_id = GetTid(); +} + +__asan::AsanThread *GetAsanThreadByOsIDLocked(tid_t os_id) { + __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>( + __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id)); + if (!context) return nullptr; + return context->thread; +} +} // namespace __asan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, uptr *cache_begin, + uptr *cache_end, DTLS **dtls) { + __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); + if (!t) return false; + *stack_begin = t->stack_bottom(); + *stack_end = t->stack_top(); + *tls_begin = t->tls_begin(); + *tls_end = t->tls_end(); + // ASan doesn't keep allocator caches in TLS, so these are unused. + *cache_begin = 0; + *cache_end = 0; + *dtls = t->dtls(); + return true; +} + +void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback, + void *arg) { + __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); + if (t && t->has_fake_stack()) + t->fake_stack()->ForEachFakeFrame(callback, arg); +} + +void LockThreadRegistry() { + __asan::asanThreadRegistry().Lock(); +} + +void UnlockThreadRegistry() { + __asan::asanThreadRegistry().Unlock(); +} + +ThreadRegistry *GetThreadRegistryLocked() { + __asan::asanThreadRegistry().CheckLocked(); + return &__asan::asanThreadRegistry(); +} + +void EnsureMainThreadIDIsCorrect() { + __asan::EnsureMainThreadIDIsCorrect(); +} +} // namespace __lsan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_start_switch_fiber(void **fakestacksave, const void *bottom, + uptr size) { + AsanThread *t = GetCurrentThread(); + if (!t) { + VReport(1, "__asan_start_switch_fiber called from unknown thread\n"); + return; + } + t->StartSwitchFiber((FakeStack**)fakestacksave, (uptr)bottom, size); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_finish_switch_fiber(void* fakestack, + const void **bottom_old, + uptr *size_old) { + AsanThread *t = GetCurrentThread(); + if (!t) { + VReport(1, "__asan_finish_switch_fiber called from unknown thread\n"); + return; + } + t->FinishSwitchFiber((FakeStack*)fakestack, + (uptr*)bottom_old, + (uptr*)size_old); +} +} diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h index 187cb13e85b..c503f507059 100644 --- a/libsanitizer/asan/asan_thread.h +++ b/libsanitizer/asan/asan_thread.h @@ -1,13 +1,14 @@ //===-- asan_thread.h -------------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_thread.cc. +// ASan-private header for asan_thread.cpp. //===----------------------------------------------------------------------===// #ifndef ASAN_THREAD_H @@ -129,6 +130,8 @@ class AsanThread { AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } AsanStats &stats() { return stats_; } + void *extra_spill_area() { return &extra_spill_area_; } + private: // NOTE: There is no AsanThread constructor. It is allocated // via mmap() and *must* be valid in zero-initialized state. @@ -164,18 +167,7 @@ class AsanThread { AsanThreadLocalMallocStorage malloc_storage_; AsanStats stats_; bool unwinding_; -}; - -// ScopedUnwinding is a scope for stacktracing member of a context -class ScopedUnwinding { - public: - explicit ScopedUnwinding(AsanThread *t) : thread(t) { - t->setUnwinding(true); - } - ~ScopedUnwinding() { thread->setUnwinding(false); } - - private: - AsanThread *thread; + uptr extra_spill_area_; }; // Returns a single instance of registry. diff --git a/libsanitizer/asan/asan_win.cc b/libsanitizer/asan/asan_win.cc deleted file mode 100644 index 8473f59c78a..00000000000 --- a/libsanitizer/asan/asan_win.cc +++ /dev/null @@ -1,354 +0,0 @@ -//===-- asan_win.cc -------------------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Windows-specific details. -//===----------------------------------------------------------------------===// - -#include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_WINDOWS -#define WIN32_LEAN_AND_MEAN -#include - -#include - -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "asan_thread.h" -#include "asan_mapping.h" -#include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_mutex.h" -#include "sanitizer_common/sanitizer_win.h" -#include "sanitizer_common/sanitizer_win_defs.h" - -using namespace __asan; // NOLINT - -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE -int __asan_should_detect_stack_use_after_return() { - __asan_init(); - return __asan_option_detect_stack_use_after_return; -} - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __asan_get_shadow_memory_dynamic_address() { - __asan_init(); - return __asan_shadow_memory_dynamic_address; -} -} // extern "C" - -// ---------------------- Windows-specific interceptors ---------------- {{{ -static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler; -static LPTOP_LEVEL_EXCEPTION_FILTER user_seh_handler; - -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -long __asan_unhandled_exception_filter(EXCEPTION_POINTERS *info) { - EXCEPTION_RECORD *exception_record = info->ExceptionRecord; - CONTEXT *context = info->ContextRecord; - - // FIXME: Handle EXCEPTION_STACK_OVERFLOW here. - - SignalContext sig(exception_record, context); - ReportDeadlySignal(sig); - UNREACHABLE("returned from reporting deadly signal"); -} - -// Wrapper SEH Handler. If the exception should be handled by asan, we call -// __asan_unhandled_exception_filter, otherwise, we execute the user provided -// exception handler or the default. -static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) { - DWORD exception_code = info->ExceptionRecord->ExceptionCode; - if (__sanitizer::IsHandledDeadlyException(exception_code)) - return __asan_unhandled_exception_filter(info); - if (user_seh_handler) - return user_seh_handler(info); - // Bubble out to the default exception filter. - if (default_seh_handler) - return default_seh_handler(info); - return EXCEPTION_CONTINUE_SEARCH; -} - -INTERCEPTOR_WINAPI(LPTOP_LEVEL_EXCEPTION_FILTER, SetUnhandledExceptionFilter, - LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) { - CHECK(REAL(SetUnhandledExceptionFilter)); - if (ExceptionFilter == &SEHHandler) - return REAL(SetUnhandledExceptionFilter)(ExceptionFilter); - // We record the user provided exception handler to be called for all the - // exceptions unhandled by asan. - Swap(ExceptionFilter, user_seh_handler); - return ExceptionFilter; -} - -INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) { - CHECK(REAL(RtlRaiseException)); - // This is a noreturn function, unless it's one of the exceptions raised to - // communicate with the debugger, such as the one from OutputDebugString. - if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C) - __asan_handle_no_return(); - REAL(RtlRaiseException)(ExceptionRecord); -} - -INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) { - CHECK(REAL(RaiseException)); - __asan_handle_no_return(); - REAL(RaiseException)(a, b, c, d); -} - -#ifdef _WIN64 - -INTERCEPTOR_WINAPI(int, __C_specific_handler, void *a, void *b, void *c, void *d) { // NOLINT - CHECK(REAL(__C_specific_handler)); - __asan_handle_no_return(); - return REAL(__C_specific_handler)(a, b, c, d); -} - -#else - -INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) { - CHECK(REAL(_except_handler3)); - __asan_handle_no_return(); - return REAL(_except_handler3)(a, b, c, d); -} - -#if ASAN_DYNAMIC -// This handler is named differently in -MT and -MD CRTs. -#define _except_handler4 _except_handler4_common -#endif -INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { - CHECK(REAL(_except_handler4)); - __asan_handle_no_return(); - return REAL(_except_handler4)(a, b, c, d); -} -#endif - -static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { - AsanThread *t = (AsanThread*)arg; - SetCurrentThread(t); - return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr); -} - -INTERCEPTOR_WINAPI(DWORD, CreateThread, - void* security, uptr stack_size, - DWORD (__stdcall *start_routine)(void*), void* arg, - DWORD thr_flags, void* tid) { - // Strict init-order checking is thread-hostile. - if (flags()->strict_init_order) - StopInitOrderChecking(); - GET_STACK_TRACE_THREAD; - // FIXME: The CreateThread interceptor is not the same as a pthread_create - // one. This is a bandaid fix for PR22025. - bool detached = false; // FIXME: how can we determine it on Windows? - u32 current_tid = GetCurrentTidOrInvalid(); - AsanThread *t = - AsanThread::Create(start_routine, arg, current_tid, &stack, detached); - return REAL(CreateThread)(security, stack_size, - asan_thread_start, t, thr_flags, tid); -} - -// }}} - -namespace __asan { - -void InitializePlatformInterceptors() { - // The interceptors were not designed to be removable, so we have to keep this - // module alive for the life of the process. - HMODULE pinned; - CHECK(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_PIN, - (LPCWSTR)&InitializePlatformInterceptors, - &pinned)); - - ASAN_INTERCEPT_FUNC(CreateThread); - ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter); - -#ifdef _WIN64 - ASAN_INTERCEPT_FUNC(__C_specific_handler); -#else - ASAN_INTERCEPT_FUNC(_except_handler3); - ASAN_INTERCEPT_FUNC(_except_handler4); -#endif - - // Try to intercept kernel32!RaiseException, and if that fails, intercept - // ntdll!RtlRaiseException instead. - if (!::__interception::OverrideFunction("RaiseException", - (uptr)WRAP(RaiseException), - (uptr *)&REAL(RaiseException))) { - CHECK(::__interception::OverrideFunction("RtlRaiseException", - (uptr)WRAP(RtlRaiseException), - (uptr *)&REAL(RtlRaiseException))); - } -} - -void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { - UNIMPLEMENTED(); -} - -// ---------------------- TSD ---------------- {{{ -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; -} - -void PlatformTSDDtor(void *tsd) { - AsanThread::TSDDtor(tsd); -} -// }}} - -// ---------------------- Various stuff ---------------- {{{ -void *AsanDoesNotSupportStaticLinkage() { -#if defined(_DEBUG) -#error Please build the runtime with a non-debug CRT: /MD or /MT -#endif - return 0; -} - -uptr FindDynamicShadowStart() { - uptr granularity = GetMmapGranularity(); - uptr alignment = 8 * granularity; - uptr left_padding = granularity; - uptr space_size = kHighShadowEnd + left_padding; - uptr shadow_start = FindAvailableMemoryRange(space_size, alignment, - granularity, nullptr, nullptr); - CHECK_NE((uptr)0, shadow_start); - CHECK(IsAligned(shadow_start, alignment)); - return shadow_start; -} - -void AsanCheckDynamicRTPrereqs() {} - -void AsanCheckIncompatibleRT() {} - -void ReadContextStack(void *context, uptr *stack, uptr *ssize) { - UNIMPLEMENTED(); -} - -void AsanOnDeadlySignal(int, void *siginfo, void *context) { - UNIMPLEMENTED(); -} - -#if SANITIZER_WINDOWS64 -// Exception handler for dealing with shadow memory. -static LONG CALLBACK -ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) { - uptr page_size = GetPageSizeCached(); - // Only handle access violations. - if (exception_pointers->ExceptionRecord->ExceptionCode != - EXCEPTION_ACCESS_VIOLATION) { - return EXCEPTION_CONTINUE_SEARCH; - } - - // Only handle access violations that land within the shadow memory. - uptr addr = - (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]); - - // Check valid shadow range. - if (!AddrIsInShadow(addr)) return EXCEPTION_CONTINUE_SEARCH; - - // This is an access violation while trying to read from the shadow. Commit - // the relevant page and let execution continue. - - // Determine the address of the page that is being accessed. - uptr page = RoundDownTo(addr, page_size); - - // Commit the page. - uptr result = - (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE); - if (result != page) return EXCEPTION_CONTINUE_SEARCH; - - // The page mapping succeeded, so continue execution as usual. - return EXCEPTION_CONTINUE_EXECUTION; -} - -#endif - -void InitializePlatformExceptionHandlers() { -#if SANITIZER_WINDOWS64 - // On Win64, we map memory on demand with access violation handler. - // Install our exception handler. - CHECK(AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler)); -#endif -} - -bool IsSystemHeapAddress(uptr addr) { - return ::HeapValidate(GetProcessHeap(), 0, (void*)addr) != FALSE; -} - -// We want to install our own exception handler (EH) to print helpful reports -// on access violations and whatnot. Unfortunately, the CRT initializers assume -// they are run before any user code and drop any previously-installed EHs on -// the floor, so we can't install our handler inside __asan_init. -// (See crt0dat.c in the CRT sources for the details) -// -// Things get even more complicated with the dynamic runtime, as it finishes its -// initialization before the .exe module CRT begins to initialize. -// -// For the static runtime (-MT), it's enough to put a callback to -// __asan_set_seh_filter in the last section for C initializers. -// -// For the dynamic runtime (-MD), we want link the same -// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter -// will be called for each instrumented module. This ensures that at least one -// __asan_set_seh_filter call happens after the .exe module CRT is initialized. -extern "C" SANITIZER_INTERFACE_ATTRIBUTE -int __asan_set_seh_filter() { - // We should only store the previous handler if it's not our own handler in - // order to avoid loops in the EH chain. - auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler); - if (prev_seh_handler != &SEHHandler) - default_seh_handler = prev_seh_handler; - return 0; -} - -#if !ASAN_DYNAMIC -// The CRT runs initializers in this order: -// - C initializers, from XIA to XIZ -// - C++ initializers, from XCA to XCZ -// Prior to 2015, the CRT set the unhandled exception filter at priority XIY, -// near the end of C initialization. Starting in 2015, it was moved to the -// beginning of C++ initialization. We set our priority to XCAB to run -// immediately after the CRT runs. This way, our exception filter is called -// first and we can delegate to their filter if appropriate. -#pragma section(".CRT$XCAB", long, read) // NOLINT -__declspec(allocate(".CRT$XCAB")) int (*__intercept_seh)() = - __asan_set_seh_filter; - -// Piggyback on the TLS initialization callback directory to initialize asan as -// early as possible. Initializers in .CRT$XL* are called directly by ntdll, -// which run before the CRT. Users also add code to .CRT$XLC, so it's important -// to run our initializers first. -static void NTAPI asan_thread_init(void *module, DWORD reason, void *reserved) { - if (reason == DLL_PROCESS_ATTACH) __asan_init(); -} - -#pragma section(".CRT$XLAB", long, read) // NOLINT -__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *, - unsigned long, void *) = asan_thread_init; -#endif - -WIN_FORCE_LINK(__asan_dso_reg_hook) - -// }}} -} // namespace __asan - -#endif // SANITIZER_WINDOWS diff --git a/libsanitizer/asan/asan_win.cpp b/libsanitizer/asan/asan_win.cpp new file mode 100644 index 00000000000..f8b98ca3366 --- /dev/null +++ b/libsanitizer/asan/asan_win.cpp @@ -0,0 +1,401 @@ +//===-- asan_win.cpp ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Windows-specific details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS +#define WIN32_LEAN_AND_MEAN +#include + +#include + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_win.h" +#include "sanitizer_common/sanitizer_win_defs.h" + +using namespace __asan; // NOLINT + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +int __asan_should_detect_stack_use_after_return() { + __asan_init(); + return __asan_option_detect_stack_use_after_return; +} + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __asan_get_shadow_memory_dynamic_address() { + __asan_init(); + return __asan_shadow_memory_dynamic_address; +} +} // extern "C" + +// ---------------------- Windows-specific interceptors ---------------- {{{ +static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler; +static LPTOP_LEVEL_EXCEPTION_FILTER user_seh_handler; + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +long __asan_unhandled_exception_filter(EXCEPTION_POINTERS *info) { + EXCEPTION_RECORD *exception_record = info->ExceptionRecord; + CONTEXT *context = info->ContextRecord; + + // FIXME: Handle EXCEPTION_STACK_OVERFLOW here. + + SignalContext sig(exception_record, context); + ReportDeadlySignal(sig); + UNREACHABLE("returned from reporting deadly signal"); +} + +// Wrapper SEH Handler. If the exception should be handled by asan, we call +// __asan_unhandled_exception_filter, otherwise, we execute the user provided +// exception handler or the default. +static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) { + DWORD exception_code = info->ExceptionRecord->ExceptionCode; + if (__sanitizer::IsHandledDeadlyException(exception_code)) + return __asan_unhandled_exception_filter(info); + if (user_seh_handler) + return user_seh_handler(info); + // Bubble out to the default exception filter. + if (default_seh_handler) + return default_seh_handler(info); + return EXCEPTION_CONTINUE_SEARCH; +} + +INTERCEPTOR_WINAPI(LPTOP_LEVEL_EXCEPTION_FILTER, SetUnhandledExceptionFilter, + LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) { + CHECK(REAL(SetUnhandledExceptionFilter)); + if (ExceptionFilter == &SEHHandler) + return REAL(SetUnhandledExceptionFilter)(ExceptionFilter); + // We record the user provided exception handler to be called for all the + // exceptions unhandled by asan. + Swap(ExceptionFilter, user_seh_handler); + return ExceptionFilter; +} + +INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) { + CHECK(REAL(RtlRaiseException)); + // This is a noreturn function, unless it's one of the exceptions raised to + // communicate with the debugger, such as the one from OutputDebugString. + if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C) + __asan_handle_no_return(); + REAL(RtlRaiseException)(ExceptionRecord); +} + +INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) { + CHECK(REAL(RaiseException)); + __asan_handle_no_return(); + REAL(RaiseException)(a, b, c, d); +} + +#ifdef _WIN64 + +INTERCEPTOR_WINAPI(EXCEPTION_DISPOSITION, __C_specific_handler, + _EXCEPTION_RECORD *a, void *b, _CONTEXT *c, + _DISPATCHER_CONTEXT *d) { // NOLINT + CHECK(REAL(__C_specific_handler)); + __asan_handle_no_return(); + return REAL(__C_specific_handler)(a, b, c, d); +} + +#else + +INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) { + CHECK(REAL(_except_handler3)); + __asan_handle_no_return(); + return REAL(_except_handler3)(a, b, c, d); +} + +#if ASAN_DYNAMIC +// This handler is named differently in -MT and -MD CRTs. +#define _except_handler4 _except_handler4_common +#endif +INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { + CHECK(REAL(_except_handler4)); + __asan_handle_no_return(); + return REAL(_except_handler4)(a, b, c, d); +} +#endif + +static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { + AsanThread *t = (AsanThread *)arg; + SetCurrentThread(t); + return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr); +} + +INTERCEPTOR_WINAPI(HANDLE, CreateThread, LPSECURITY_ATTRIBUTES security, + SIZE_T stack_size, LPTHREAD_START_ROUTINE start_routine, + void *arg, DWORD thr_flags, DWORD *tid) { + // Strict init-order checking is thread-hostile. + if (flags()->strict_init_order) + StopInitOrderChecking(); + GET_STACK_TRACE_THREAD; + // FIXME: The CreateThread interceptor is not the same as a pthread_create + // one. This is a bandaid fix for PR22025. + bool detached = false; // FIXME: how can we determine it on Windows? + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = + AsanThread::Create(start_routine, arg, current_tid, &stack, detached); + return REAL(CreateThread)(security, stack_size, asan_thread_start, t, + thr_flags, tid); +} + +// }}} + +namespace __asan { + +void InitializePlatformInterceptors() { + // The interceptors were not designed to be removable, so we have to keep this + // module alive for the life of the process. + HMODULE pinned; + CHECK(GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, + (LPCWSTR)&InitializePlatformInterceptors, &pinned)); + + ASAN_INTERCEPT_FUNC(CreateThread); + ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter); + +#ifdef _WIN64 + ASAN_INTERCEPT_FUNC(__C_specific_handler); +#else + ASAN_INTERCEPT_FUNC(_except_handler3); + ASAN_INTERCEPT_FUNC(_except_handler4); +#endif + + // Try to intercept kernel32!RaiseException, and if that fails, intercept + // ntdll!RtlRaiseException instead. + if (!::__interception::OverrideFunction("RaiseException", + (uptr)WRAP(RaiseException), + (uptr *)&REAL(RaiseException))) { + CHECK(::__interception::OverrideFunction("RtlRaiseException", + (uptr)WRAP(RtlRaiseException), + (uptr *)&REAL(RtlRaiseException))); + } +} + +void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { + UNIMPLEMENTED(); +} + +// ---------------------- TSD ---------------- {{{ +static bool tsd_key_inited = false; + +static __declspec(thread) void *fake_tsd = 0; + +// https://docs.microsoft.com/en-us/windows/desktop/api/winternl/ns-winternl-_teb +// "[This structure may be altered in future versions of Windows. Applications +// should use the alternate functions listed in this topic.]" +typedef struct _TEB { + PVOID Reserved1[12]; + // PVOID ThreadLocalStoragePointer; is here, at the last field in Reserved1. + PVOID ProcessEnvironmentBlock; + PVOID Reserved2[399]; + BYTE Reserved3[1952]; + PVOID TlsSlots[64]; + BYTE Reserved4[8]; + PVOID Reserved5[26]; + PVOID ReservedForOle; + PVOID Reserved6[4]; + PVOID TlsExpansionSlots; +} TEB, *PTEB; + +constexpr size_t TEB_RESERVED_FIELDS_THREAD_LOCAL_STORAGE_OFFSET = 11; +BOOL IsTlsInitialized() { + PTEB teb = (PTEB)NtCurrentTeb(); + return teb->Reserved1[TEB_RESERVED_FIELDS_THREAD_LOCAL_STORAGE_OFFSET] != + nullptr; +} + +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 IsTlsInitialized() ? fake_tsd : nullptr; +} + +void AsanTSDSet(void *tsd) { + CHECK(tsd_key_inited); + fake_tsd = tsd; +} + +void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); } +// }}} + +// ---------------------- Various stuff ---------------- {{{ +void *AsanDoesNotSupportStaticLinkage() { +#if defined(_DEBUG) +#error Please build the runtime with a non-debug CRT: /MD or /MT +#endif + return 0; +} + +uptr FindDynamicShadowStart() { + uptr granularity = GetMmapGranularity(); + uptr alignment = 8 * granularity; + uptr left_padding = granularity; + uptr space_size = kHighShadowEnd + left_padding; + uptr shadow_start = FindAvailableMemoryRange(space_size, alignment, + granularity, nullptr, nullptr); + CHECK_NE((uptr)0, shadow_start); + CHECK(IsAligned(shadow_start, alignment)); + return shadow_start; +} + +void AsanCheckDynamicRTPrereqs() {} + +void AsanCheckIncompatibleRT() {} + +void ReadContextStack(void *context, uptr *stack, uptr *ssize) { + UNIMPLEMENTED(); +} + +void AsanOnDeadlySignal(int, void *siginfo, void *context) { UNIMPLEMENTED(); } + +#if SANITIZER_WINDOWS64 +// Exception handler for dealing with shadow memory. +static LONG CALLBACK +ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) { + uptr page_size = GetPageSizeCached(); + // Only handle access violations. + if (exception_pointers->ExceptionRecord->ExceptionCode != + EXCEPTION_ACCESS_VIOLATION || + exception_pointers->ExceptionRecord->NumberParameters < 2) { + __asan_handle_no_return(); + return EXCEPTION_CONTINUE_SEARCH; + } + + // Only handle access violations that land within the shadow memory. + uptr addr = + (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]); + + // Check valid shadow range. + if (!AddrIsInShadow(addr)) { + __asan_handle_no_return(); + return EXCEPTION_CONTINUE_SEARCH; + } + + // This is an access violation while trying to read from the shadow. Commit + // the relevant page and let execution continue. + + // Determine the address of the page that is being accessed. + uptr page = RoundDownTo(addr, page_size); + + // Commit the page. + uptr result = + (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE); + if (result != page) + return EXCEPTION_CONTINUE_SEARCH; + + // The page mapping succeeded, so continue execution as usual. + return EXCEPTION_CONTINUE_EXECUTION; +} + +#endif + +void InitializePlatformExceptionHandlers() { +#if SANITIZER_WINDOWS64 + // On Win64, we map memory on demand with access violation handler. + // Install our exception handler. + CHECK(AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler)); +#endif +} + +bool IsSystemHeapAddress(uptr addr) { + return ::HeapValidate(GetProcessHeap(), 0, (void *)addr) != FALSE; +} + +// We want to install our own exception handler (EH) to print helpful reports +// on access violations and whatnot. Unfortunately, the CRT initializers assume +// they are run before any user code and drop any previously-installed EHs on +// the floor, so we can't install our handler inside __asan_init. +// (See crt0dat.c in the CRT sources for the details) +// +// Things get even more complicated with the dynamic runtime, as it finishes its +// initialization before the .exe module CRT begins to initialize. +// +// For the static runtime (-MT), it's enough to put a callback to +// __asan_set_seh_filter in the last section for C initializers. +// +// For the dynamic runtime (-MD), we want link the same +// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter +// will be called for each instrumented module. This ensures that at least one +// __asan_set_seh_filter call happens after the .exe module CRT is initialized. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __asan_set_seh_filter() { + // We should only store the previous handler if it's not our own handler in + // order to avoid loops in the EH chain. + auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler); + if (prev_seh_handler != &SEHHandler) + default_seh_handler = prev_seh_handler; + return 0; +} + +bool HandleDlopenInit() { + // Not supported on this platform. + static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN, + "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false"); + return false; +} + +#if !ASAN_DYNAMIC +// The CRT runs initializers in this order: +// - C initializers, from XIA to XIZ +// - C++ initializers, from XCA to XCZ +// Prior to 2015, the CRT set the unhandled exception filter at priority XIY, +// near the end of C initialization. Starting in 2015, it was moved to the +// beginning of C++ initialization. We set our priority to XCAB to run +// immediately after the CRT runs. This way, our exception filter is called +// first and we can delegate to their filter if appropriate. +#pragma section(".CRT$XCAB", long, read) // NOLINT +__declspec(allocate(".CRT$XCAB")) int (*__intercept_seh)() = + __asan_set_seh_filter; + +// Piggyback on the TLS initialization callback directory to initialize asan as +// early as possible. Initializers in .CRT$XL* are called directly by ntdll, +// which run before the CRT. Users also add code to .CRT$XLC, so it's important +// to run our initializers first. +static void NTAPI asan_thread_init(void *module, DWORD reason, void *reserved) { + if (reason == DLL_PROCESS_ATTACH) + __asan_init(); +} + +#pragma section(".CRT$XLAB", long, read) // NOLINT +__declspec(allocate(".CRT$XLAB")) void(NTAPI *__asan_tls_init)( + void *, unsigned long, void *) = asan_thread_init; +#endif + +static void NTAPI asan_thread_exit(void *module, DWORD reason, void *reserved) { + if (reason == DLL_THREAD_DETACH) { + // Unpoison the thread's stack because the memory may be re-used. + NT_TIB *tib = (NT_TIB *)NtCurrentTeb(); + uptr stackSize = (uptr)tib->StackBase - (uptr)tib->StackLimit; + __asan_unpoison_memory_region(tib->StackLimit, stackSize); + } +} + +#pragma section(".CRT$XLY", long, read) // NOLINT +__declspec(allocate(".CRT$XLY")) void(NTAPI *__asan_tls_exit)( + void *, unsigned long, void *) = asan_thread_exit; + +WIN_FORCE_LINK(__asan_dso_reg_hook) + +// }}} +} // namespace __asan + +#endif // SANITIZER_WINDOWS diff --git a/libsanitizer/asan/asan_win_dll_thunk.cc b/libsanitizer/asan/asan_win_dll_thunk.cc deleted file mode 100644 index 8df7ab2b177..00000000000 --- a/libsanitizer/asan/asan_win_dll_thunk.cc +++ /dev/null @@ -1,150 +0,0 @@ -//===-- asan_win_dll_thunk.cc ---------------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// This file defines a family of thunks that should be statically linked into -// the DLLs that have ASan instrumentation in order to delegate the calls to the -// shared runtime that lives in the main binary. -// See https://github.com/google/sanitizers/issues/209 for the details. -//===----------------------------------------------------------------------===// - -#ifdef SANITIZER_DLL_THUNK -#include "asan_init_version.h" -#include "interception/interception.h" -#include "sanitizer_common/sanitizer_win_defs.h" -#include "sanitizer_common/sanitizer_win_dll_thunk.h" -#include "sanitizer_common/sanitizer_platform_interceptors.h" - -// ASan own interface functions. -#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) -#include "asan_interface.inc" - -// Memory allocation functions. -INTERCEPT_WRAP_V_W(free) -INTERCEPT_WRAP_V_W(_free_base) -INTERCEPT_WRAP_V_WW(_free_dbg) - -INTERCEPT_WRAP_W_W(malloc) -INTERCEPT_WRAP_W_W(_malloc_base) -INTERCEPT_WRAP_W_WWWW(_malloc_dbg) - -INTERCEPT_WRAP_W_WW(calloc) -INTERCEPT_WRAP_W_WW(_calloc_base) -INTERCEPT_WRAP_W_WWWWW(_calloc_dbg) -INTERCEPT_WRAP_W_WWW(_calloc_impl) - -INTERCEPT_WRAP_W_WW(realloc) -INTERCEPT_WRAP_W_WW(_realloc_base) -INTERCEPT_WRAP_W_WWW(_realloc_dbg) -INTERCEPT_WRAP_W_WWW(_recalloc) -INTERCEPT_WRAP_W_WWW(_recalloc_base) - -INTERCEPT_WRAP_W_W(_msize) -INTERCEPT_WRAP_W_W(_expand) -INTERCEPT_WRAP_W_W(_expand_dbg) - -// TODO(timurrrr): Might want to add support for _aligned_* allocation -// functions to detect a bit more bugs. Those functions seem to wrap malloc(). - -// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc). - -INTERCEPT_LIBRARY_FUNCTION(atoi); -INTERCEPT_LIBRARY_FUNCTION(atol); -INTERCEPT_LIBRARY_FUNCTION(frexp); -INTERCEPT_LIBRARY_FUNCTION(longjmp); -#if SANITIZER_INTERCEPT_MEMCHR -INTERCEPT_LIBRARY_FUNCTION(memchr); -#endif -INTERCEPT_LIBRARY_FUNCTION(memcmp); -INTERCEPT_LIBRARY_FUNCTION(memcpy); -INTERCEPT_LIBRARY_FUNCTION(memmove); -INTERCEPT_LIBRARY_FUNCTION(memset); -INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT -INTERCEPT_LIBRARY_FUNCTION(strchr); -INTERCEPT_LIBRARY_FUNCTION(strcmp); -INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT -INTERCEPT_LIBRARY_FUNCTION(strcspn); -INTERCEPT_LIBRARY_FUNCTION(strdup); -INTERCEPT_LIBRARY_FUNCTION(strlen); -INTERCEPT_LIBRARY_FUNCTION(strncat); -INTERCEPT_LIBRARY_FUNCTION(strncmp); -INTERCEPT_LIBRARY_FUNCTION(strncpy); -INTERCEPT_LIBRARY_FUNCTION(strnlen); -INTERCEPT_LIBRARY_FUNCTION(strpbrk); -INTERCEPT_LIBRARY_FUNCTION(strrchr); -INTERCEPT_LIBRARY_FUNCTION(strspn); -INTERCEPT_LIBRARY_FUNCTION(strstr); -INTERCEPT_LIBRARY_FUNCTION(strtok); -INTERCEPT_LIBRARY_FUNCTION(strtol); -INTERCEPT_LIBRARY_FUNCTION(wcslen); -INTERCEPT_LIBRARY_FUNCTION(wcsnlen); - -#ifdef _WIN64 -INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler); -#else -INTERCEPT_LIBRARY_FUNCTION(_except_handler3); -// _except_handler4 checks -GS cookie which is different for each module, so we -// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4). -INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { - __asan_handle_no_return(); - return REAL(_except_handler4)(a, b, c, d); -} -#endif - -// Windows specific functions not included in asan_interface.inc. -INTERCEPT_WRAP_W_V(__asan_should_detect_stack_use_after_return) -INTERCEPT_WRAP_W_V(__asan_get_shadow_memory_dynamic_address) -INTERCEPT_WRAP_W_W(__asan_unhandled_exception_filter) - -using namespace __sanitizer; - -extern "C" { -int __asan_option_detect_stack_use_after_return; -uptr __asan_shadow_memory_dynamic_address; -} // extern "C" - -static int asan_dll_thunk_init() { - typedef void (*fntype)(); - static fntype fn = 0; - // asan_dll_thunk_init is expected to be called by only one thread. - if (fn) return 0; - - // Ensure all interception was executed. - __dll_thunk_init(); - - fn = (fntype) dllThunkGetRealAddrOrDie("__asan_init"); - fn(); - __asan_option_detect_stack_use_after_return = - (__asan_should_detect_stack_use_after_return() != 0); - __asan_shadow_memory_dynamic_address = - (uptr)__asan_get_shadow_memory_dynamic_address(); - -#ifndef _WIN64 - INTERCEPT_FUNCTION(_except_handler4); -#endif - // In DLLs, the callbacks are expected to return 0, - // otherwise CRT initialization fails. - return 0; -} - -#pragma section(".CRT$XIB", long, read) // NOLINT -__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = asan_dll_thunk_init; - -static void WINAPI asan_thread_init(void *mod, unsigned long reason, - void *reserved) { - if (reason == /*DLL_PROCESS_ATTACH=*/1) asan_dll_thunk_init(); -} - -#pragma section(".CRT$XLAB", long, read) // NOLINT -__declspec(allocate(".CRT$XLAB")) void (WINAPI *__asan_tls_init)(void *, - unsigned long, void *) = asan_thread_init; - -WIN_FORCE_LINK(__asan_dso_reg_hook) - -#endif // SANITIZER_DLL_THUNK diff --git a/libsanitizer/asan/asan_win_dll_thunk.cpp b/libsanitizer/asan/asan_win_dll_thunk.cpp new file mode 100644 index 00000000000..95eee5eed0d --- /dev/null +++ b/libsanitizer/asan/asan_win_dll_thunk.cpp @@ -0,0 +1,152 @@ +//===-- asan_win_dll_thunk.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file defines a family of thunks that should be statically linked into +// the DLLs that have ASan instrumentation in order to delegate the calls to the +// shared runtime that lives in the main binary. +// See https://github.com/google/sanitizers/issues/209 for the details. +//===----------------------------------------------------------------------===// + +#ifdef SANITIZER_DLL_THUNK +#include "asan_init_version.h" +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_win_defs.h" +#include "sanitizer_common/sanitizer_win_dll_thunk.h" +#include "sanitizer_common/sanitizer_platform_interceptors.h" + +// ASan own interface functions. +#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) +#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) +#include "asan_interface.inc" + +// Memory allocation functions. +INTERCEPT_WRAP_V_W(free) +INTERCEPT_WRAP_V_W(_free_base) +INTERCEPT_WRAP_V_WW(_free_dbg) + +INTERCEPT_WRAP_W_W(malloc) +INTERCEPT_WRAP_W_W(_malloc_base) +INTERCEPT_WRAP_W_WWWW(_malloc_dbg) + +INTERCEPT_WRAP_W_WW(calloc) +INTERCEPT_WRAP_W_WW(_calloc_base) +INTERCEPT_WRAP_W_WWWWW(_calloc_dbg) +INTERCEPT_WRAP_W_WWW(_calloc_impl) + +INTERCEPT_WRAP_W_WW(realloc) +INTERCEPT_WRAP_W_WW(_realloc_base) +INTERCEPT_WRAP_W_WWW(_realloc_dbg) +INTERCEPT_WRAP_W_WWW(_recalloc) +INTERCEPT_WRAP_W_WWW(_recalloc_base) + +INTERCEPT_WRAP_W_W(_msize) +INTERCEPT_WRAP_W_W(_msize_base) +INTERCEPT_WRAP_W_W(_expand) +INTERCEPT_WRAP_W_W(_expand_dbg) + +// TODO(timurrrr): Might want to add support for _aligned_* allocation +// functions to detect a bit more bugs. Those functions seem to wrap malloc(). + +// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cpp) + +INTERCEPT_LIBRARY_FUNCTION(atoi); +INTERCEPT_LIBRARY_FUNCTION(atol); +INTERCEPT_LIBRARY_FUNCTION(frexp); +INTERCEPT_LIBRARY_FUNCTION(longjmp); +#if SANITIZER_INTERCEPT_MEMCHR +INTERCEPT_LIBRARY_FUNCTION(memchr); +#endif +INTERCEPT_LIBRARY_FUNCTION(memcmp); +INTERCEPT_LIBRARY_FUNCTION(memcpy); +INTERCEPT_LIBRARY_FUNCTION(memmove); +INTERCEPT_LIBRARY_FUNCTION(memset); +INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT +INTERCEPT_LIBRARY_FUNCTION(strchr); +INTERCEPT_LIBRARY_FUNCTION(strcmp); +INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT +INTERCEPT_LIBRARY_FUNCTION(strcspn); +INTERCEPT_LIBRARY_FUNCTION(strdup); +INTERCEPT_LIBRARY_FUNCTION(strlen); +INTERCEPT_LIBRARY_FUNCTION(strncat); +INTERCEPT_LIBRARY_FUNCTION(strncmp); +INTERCEPT_LIBRARY_FUNCTION(strncpy); +INTERCEPT_LIBRARY_FUNCTION(strnlen); +INTERCEPT_LIBRARY_FUNCTION(strpbrk); +INTERCEPT_LIBRARY_FUNCTION(strrchr); +INTERCEPT_LIBRARY_FUNCTION(strspn); +INTERCEPT_LIBRARY_FUNCTION(strstr); +INTERCEPT_LIBRARY_FUNCTION(strtok); +INTERCEPT_LIBRARY_FUNCTION(strtol); +INTERCEPT_LIBRARY_FUNCTION(wcslen); +INTERCEPT_LIBRARY_FUNCTION(wcsnlen); + +#ifdef _WIN64 +INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler); +#else +INTERCEPT_LIBRARY_FUNCTION(_except_handler3); +// _except_handler4 checks -GS cookie which is different for each module, so we +// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4). +INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { + __asan_handle_no_return(); + return REAL(_except_handler4)(a, b, c, d); +} +#endif + +// Windows specific functions not included in asan_interface.inc. +INTERCEPT_WRAP_W_V(__asan_should_detect_stack_use_after_return) +INTERCEPT_WRAP_W_V(__asan_get_shadow_memory_dynamic_address) +INTERCEPT_WRAP_W_W(__asan_unhandled_exception_filter) + +using namespace __sanitizer; + +extern "C" { +int __asan_option_detect_stack_use_after_return; +uptr __asan_shadow_memory_dynamic_address; +} // extern "C" + +static int asan_dll_thunk_init() { + typedef void (*fntype)(); + static fntype fn = 0; + // asan_dll_thunk_init is expected to be called by only one thread. + if (fn) return 0; + + // Ensure all interception was executed. + __dll_thunk_init(); + + fn = (fntype) dllThunkGetRealAddrOrDie("__asan_init"); + fn(); + __asan_option_detect_stack_use_after_return = + (__asan_should_detect_stack_use_after_return() != 0); + __asan_shadow_memory_dynamic_address = + (uptr)__asan_get_shadow_memory_dynamic_address(); + +#ifndef _WIN64 + INTERCEPT_FUNCTION(_except_handler4); +#endif + // In DLLs, the callbacks are expected to return 0, + // otherwise CRT initialization fails. + return 0; +} + +#pragma section(".CRT$XIB", long, read) // NOLINT +__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = asan_dll_thunk_init; + +static void WINAPI asan_thread_init(void *mod, unsigned long reason, + void *reserved) { + if (reason == /*DLL_PROCESS_ATTACH=*/1) asan_dll_thunk_init(); +} + +#pragma section(".CRT$XLAB", long, read) // NOLINT +__declspec(allocate(".CRT$XLAB")) void (WINAPI *__asan_tls_init)(void *, + unsigned long, void *) = asan_thread_init; + +WIN_FORCE_LINK(__asan_dso_reg_hook) + +#endif // SANITIZER_DLL_THUNK diff --git a/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc b/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc deleted file mode 100644 index d431b78d605..00000000000 --- a/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc +++ /dev/null @@ -1,129 +0,0 @@ -//===-- asan_win_dynamic_runtime_thunk.cc ---------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// This file defines things that need to be present in the application modules -// to interact with the ASan DLL runtime correctly and can't be implemented -// using the default "import library" generated when linking the DLL RTL. -// -// This includes: -// - creating weak aliases to default implementation imported from asan dll. -// - forwarding the detect_stack_use_after_return runtime option -// - working around deficiencies of the MD runtime -// - installing a custom SEH handler -// -//===----------------------------------------------------------------------===// - -#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK -#define SANITIZER_IMPORT_INTERFACE 1 -#include "sanitizer_common/sanitizer_win_defs.h" -#define WIN32_LEAN_AND_MEAN -#include - -// Define weak alias for all weak functions imported from asan dll. -#define INTERFACE_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name) -#include "asan_interface.inc" - -// First, declare CRT sections we'll be using in this file -#pragma section(".CRT$XIB", long, read) // NOLINT -#pragma section(".CRT$XID", long, read) // NOLINT -#pragma section(".CRT$XCAB", long, read) // NOLINT -#pragma section(".CRT$XTW", long, read) // NOLINT -#pragma section(".CRT$XTY", long, read) // NOLINT -#pragma section(".CRT$XLAB", long, read) // NOLINT - -//////////////////////////////////////////////////////////////////////////////// -// Define a copy of __asan_option_detect_stack_use_after_return that should be -// used when linking an MD runtime with a set of object files on Windows. -// -// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return', -// so normally we would just dllimport it. Unfortunately, the dllimport -// attribute adds __imp_ prefix to the symbol name of a variable. -// Since in general we don't know if a given TU is going to be used -// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows -// just to work around this issue, let's clone the variable that is constant -// after initialization anyways. -extern "C" { -__declspec(dllimport) int __asan_should_detect_stack_use_after_return(); -int __asan_option_detect_stack_use_after_return; - -__declspec(dllimport) void* __asan_get_shadow_memory_dynamic_address(); -void* __asan_shadow_memory_dynamic_address; -} - -static int InitializeClonedVariables() { - __asan_option_detect_stack_use_after_return = - __asan_should_detect_stack_use_after_return(); - __asan_shadow_memory_dynamic_address = - __asan_get_shadow_memory_dynamic_address(); - return 0; -} - -static void NTAPI asan_thread_init(void *mod, unsigned long reason, - void *reserved) { - if (reason == DLL_PROCESS_ATTACH) InitializeClonedVariables(); -} - -// Our cloned variables must be initialized before C/C++ constructors. If TLS -// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB -// initializer is needed as a backup. -__declspec(allocate(".CRT$XIB")) int (*__asan_initialize_cloned_variables)() = - InitializeClonedVariables; -__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *, - unsigned long, void *) = asan_thread_init; - -//////////////////////////////////////////////////////////////////////////////// -// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL -// unload or on exit. ASan relies on LLVM global_dtors to call -// __asan_unregister_globals on these events, which unfortunately doesn't work -// with the MD runtime, see PR22545 for the details. -// To work around this, for each DLL we schedule a call to UnregisterGlobals -// using atexit() that calls a small subset of C terminators -// where LLVM global_dtors is placed. Fingers crossed, no other C terminators -// are there. -extern "C" int __cdecl atexit(void (__cdecl *f)(void)); -extern "C" void __cdecl _initterm(void *a, void *b); - -namespace { -__declspec(allocate(".CRT$XTW")) void* before_global_dtors = 0; -__declspec(allocate(".CRT$XTY")) void* after_global_dtors = 0; - -void UnregisterGlobals() { - _initterm(&before_global_dtors, &after_global_dtors); -} - -int ScheduleUnregisterGlobals() { - return atexit(UnregisterGlobals); -} -} // namespace - -// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after -// atexit() is initialized (.CRT$XIC). As this is executed before C++ -// initializers (think ctors for globals), UnregisterGlobals gets executed after -// dtors for C++ globals. -__declspec(allocate(".CRT$XID")) -int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals; - -//////////////////////////////////////////////////////////////////////////////// -// ASan SEH handling. -// We need to set the ASan-specific SEH handler at the end of CRT initialization -// of each module (see also asan_win.cc). -extern "C" { -__declspec(dllimport) int __asan_set_seh_filter(); -static int SetSEHFilter() { return __asan_set_seh_filter(); } - -// Unfortunately, putting a pointer to __asan_set_seh_filter into -// __asan_intercept_seh gets optimized out, so we have to use an extra function. -__declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() = - SetSEHFilter; -} - -WIN_FORCE_LINK(__asan_dso_reg_hook) - -#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK diff --git a/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cpp b/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cpp new file mode 100644 index 00000000000..5bd457a22b6 --- /dev/null +++ b/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cpp @@ -0,0 +1,130 @@ +//===-- asan_win_dynamic_runtime_thunk.cpp --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file defines things that need to be present in the application modules +// to interact with the ASan DLL runtime correctly and can't be implemented +// using the default "import library" generated when linking the DLL RTL. +// +// This includes: +// - creating weak aliases to default implementation imported from asan dll. +// - forwarding the detect_stack_use_after_return runtime option +// - working around deficiencies of the MD runtime +// - installing a custom SEH handler +// +//===----------------------------------------------------------------------===// + +#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK +#define SANITIZER_IMPORT_INTERFACE 1 +#include "sanitizer_common/sanitizer_win_defs.h" +#define WIN32_LEAN_AND_MEAN +#include + +// Define weak alias for all weak functions imported from asan dll. +#define INTERFACE_FUNCTION(Name) +#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name) +#include "asan_interface.inc" + +// First, declare CRT sections we'll be using in this file +#pragma section(".CRT$XIB", long, read) // NOLINT +#pragma section(".CRT$XID", long, read) // NOLINT +#pragma section(".CRT$XCAB", long, read) // NOLINT +#pragma section(".CRT$XTW", long, read) // NOLINT +#pragma section(".CRT$XTY", long, read) // NOLINT +#pragma section(".CRT$XLAB", long, read) // NOLINT + +//////////////////////////////////////////////////////////////////////////////// +// Define a copy of __asan_option_detect_stack_use_after_return that should be +// used when linking an MD runtime with a set of object files on Windows. +// +// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return', +// so normally we would just dllimport it. Unfortunately, the dllimport +// attribute adds __imp_ prefix to the symbol name of a variable. +// Since in general we don't know if a given TU is going to be used +// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows +// just to work around this issue, let's clone the variable that is constant +// after initialization anyways. +extern "C" { +__declspec(dllimport) int __asan_should_detect_stack_use_after_return(); +int __asan_option_detect_stack_use_after_return; + +__declspec(dllimport) void* __asan_get_shadow_memory_dynamic_address(); +void* __asan_shadow_memory_dynamic_address; +} + +static int InitializeClonedVariables() { + __asan_option_detect_stack_use_after_return = + __asan_should_detect_stack_use_after_return(); + __asan_shadow_memory_dynamic_address = + __asan_get_shadow_memory_dynamic_address(); + return 0; +} + +static void NTAPI asan_thread_init(void *mod, unsigned long reason, + void *reserved) { + if (reason == DLL_PROCESS_ATTACH) InitializeClonedVariables(); +} + +// Our cloned variables must be initialized before C/C++ constructors. If TLS +// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB +// initializer is needed as a backup. +__declspec(allocate(".CRT$XIB")) int (*__asan_initialize_cloned_variables)() = + InitializeClonedVariables; +__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *, + unsigned long, void *) = asan_thread_init; + +//////////////////////////////////////////////////////////////////////////////// +// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL +// unload or on exit. ASan relies on LLVM global_dtors to call +// __asan_unregister_globals on these events, which unfortunately doesn't work +// with the MD runtime, see PR22545 for the details. +// To work around this, for each DLL we schedule a call to UnregisterGlobals +// using atexit() that calls a small subset of C terminators +// where LLVM global_dtors is placed. Fingers crossed, no other C terminators +// are there. +extern "C" int __cdecl atexit(void (__cdecl *f)(void)); +extern "C" void __cdecl _initterm(void *a, void *b); + +namespace { +__declspec(allocate(".CRT$XTW")) void* before_global_dtors = 0; +__declspec(allocate(".CRT$XTY")) void* after_global_dtors = 0; + +void UnregisterGlobals() { + _initterm(&before_global_dtors, &after_global_dtors); +} + +int ScheduleUnregisterGlobals() { + return atexit(UnregisterGlobals); +} +} // namespace + +// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after +// atexit() is initialized (.CRT$XIC). As this is executed before C++ +// initializers (think ctors for globals), UnregisterGlobals gets executed after +// dtors for C++ globals. +__declspec(allocate(".CRT$XID")) +int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals; + +//////////////////////////////////////////////////////////////////////////////// +// ASan SEH handling. +// We need to set the ASan-specific SEH handler at the end of CRT initialization +// of each module (see also asan_win.cpp). +extern "C" { +__declspec(dllimport) int __asan_set_seh_filter(); +static int SetSEHFilter() { return __asan_set_seh_filter(); } + +// Unfortunately, putting a pointer to __asan_set_seh_filter into +// __asan_intercept_seh gets optimized out, so we have to use an extra function. +__declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() = + SetSEHFilter; +} + +WIN_FORCE_LINK(__asan_dso_reg_hook) + +#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK diff --git a/libsanitizer/asan/asan_win_weak_interception.cc b/libsanitizer/asan/asan_win_weak_interception.cc deleted file mode 100644 index 74c1dcdb729..00000000000 --- a/libsanitizer/asan/asan_win_weak_interception.cc +++ /dev/null @@ -1,21 +0,0 @@ -//===-- asan_win_weak_interception.cc -------------------------------------===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// This module should be included in Address Sanitizer when it is implemented as -// a shared library on Windows (dll), in order to delegate the calls of weak -// functions to the implementation in the main executable when a strong -// definition is provided. -//===----------------------------------------------------------------------===// -#ifdef SANITIZER_DYNAMIC -#include "sanitizer_common/sanitizer_win_weak_interception.h" -#include "asan_interface_internal.h" -// Check if strong definitions for weak functions are present in the main -// executable. If that is the case, override dll functions to point to strong -// implementations. -#define INTERFACE_FUNCTION(Name) -#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) -#include "asan_interface.inc" -#endif // SANITIZER_DYNAMIC diff --git a/libsanitizer/asan/asan_win_weak_interception.cpp b/libsanitizer/asan/asan_win_weak_interception.cpp new file mode 100644 index 00000000000..62534e12e2a --- /dev/null +++ b/libsanitizer/asan/asan_win_weak_interception.cpp @@ -0,0 +1,22 @@ +//===-- asan_win_weak_interception.cpp ------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This module should be included in Address Sanitizer when it is implemented as +// a shared library on Windows (dll), in order to delegate the calls of weak +// functions to the implementation in the main executable when a strong +// definition is provided. +//===----------------------------------------------------------------------===// +#ifdef SANITIZER_DYNAMIC +#include "sanitizer_common/sanitizer_win_weak_interception.h" +#include "asan_interface_internal.h" +// Check if strong definitions for weak functions are present in the main +// executable. If that is the case, override dll functions to point to strong +// implementations. +#define INTERFACE_FUNCTION(Name) +#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) +#include "asan_interface.inc" +#endif // SANITIZER_DYNAMIC diff --git a/libsanitizer/builtins/assembly.h b/libsanitizer/builtins/assembly.h index 3f5e59b2544..f437cb87f60 100644 --- a/libsanitizer/builtins/assembly.h +++ b/libsanitizer/builtins/assembly.h @@ -1,17 +1,15 @@ -/* ===-- assembly.h - compiler-rt assembler support macros -----------------=== - * - * 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. - * - * ===----------------------------------------------------------------------=== - * - * This file defines macros for use in compiler-rt assembler source. - * This file is not part of the interface of this library. - * - * ===----------------------------------------------------------------------=== - */ +//===-- assembly.h - compiler-rt assembler support macros -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines macros for use in compiler-rt assembler source. +// This file is not part of the interface of this library. +// +//===----------------------------------------------------------------------===// #ifndef COMPILERRT_ASSEMBLY_H #define COMPILERRT_ASSEMBLY_H @@ -69,11 +67,9 @@ #if defined(__arm__) -/* - * Determine actual [ARM][THUMB[1][2]] ISA using compiler predefined macros: - * - for '-mthumb -march=armv6' compiler defines '__thumb__' - * - for '-mthumb -march=armv7' compiler defines '__thumb__' and '__thumb2__' - */ +// Determine actual [ARM][THUMB[1][2]] ISA using compiler predefined macros: +// - for '-mthumb -march=armv6' compiler defines '__thumb__' +// - for '-mthumb -march=armv7' compiler defines '__thumb__' and '__thumb2__' #if defined(__thumb2__) || defined(__thumb__) #define DEFINE_CODE_STATE .thumb SEPARATOR #define DECLARE_FUNC_ENCODING .thumb_func SEPARATOR @@ -201,4 +197,4 @@ #define END_COMPILERRT_FUNCTION(name) #endif -#endif /* COMPILERRT_ASSEMBLY_H */ +#endif // COMPILERRT_ASSEMBLY_H diff --git a/libsanitizer/configure b/libsanitizer/configure index 2d25147adba..106bc0cfa6a 100755 --- a/libsanitizer/configure +++ b/libsanitizer/configure @@ -708,7 +708,6 @@ am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE -am__quote am__include DEPDIR am__untar @@ -797,7 +796,8 @@ PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR -SHELL' +SHELL +am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking @@ -4221,7 +4221,7 @@ esac -am__api_version='1.15' +am__api_version='1.16' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or @@ -4647,45 +4647,45 @@ DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" - -am_make=${MAKE-make} -cat > confinc << 'END' +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 +$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; } +cat > confinc.mk << 'END' am__doit: - @echo this is the am__doit target + @echo this is the am__doit target >confinc.out .PHONY: am__doit END -# If we don't find an include directive, just comment out the code. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 -$as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 + (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + case $?:`cat confinc.out 2>/dev/null` in #( + '0:this is the am__doit target') : + case $s in #( + BSD) : + am__include='.include' am__quote='"' ;; #( + *) : + am__include='include' am__quote='' ;; +esac ;; #( + *) : ;; - esac -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 -$as_echo "$_am_result" >&6; } -rm -f confinc confmf +esac + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 +$as_echo "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : @@ -4797,8 +4797,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: -# -# +# +# mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The @@ -4977,7 +4977,7 @@ END Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . +that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM @@ -17652,7 +17652,7 @@ CC="$CC" CXX="$CXX" GFORTRAN="$GFORTRAN" GDC="$GDC" -AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" +AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" # The HP-UX ksh and POSIX shell print the target directory to stdout @@ -18662,29 +18662,35 @@ esac ;; # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + case $CONFIG_FILES in #( + *\'*) : + eval set x "$CONFIG_FILES" ;; #( + *) : + set x $CONFIG_FILES ;; #( + *) : + ;; +esac shift - for mf + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf do # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line + am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`$as_dirname -- "$mf" || -$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$mf" : 'X\(//\)[^/]' \| \ - X"$mf" : 'X\(//\)$' \| \ - X"$mf" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$mf" | + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`$as_dirname -- "$am_mf" || +$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$am_mf" : 'X\(//\)[^/]' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -18702,53 +18708,48 @@ $as_echo X"$mf" | q } s/.*/./; q'` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`$as_dirname -- "$file" || -$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$file" : 'X\(//\)[^/]' \| \ - X"$file" : 'X\(//\)$' \| \ - X"$file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ + am_filepart=`$as_basename -- "$am_mf" || +$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$am_mf" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } - /^X\(\/\/\)$/{ + /^X\/\(\/\/\)$/{ s//\1/ q } - /^X\(\/\).*/{ + /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` - as_dir=$dirpart/$fdir; as_fn_mkdir_p - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done + { echo "$as_me:$LINENO: cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles" >&5 + (cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } || am_rc=$? done + if test $am_rc -ne 0; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. Try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking). +See \`config.log' for more details" "$LINENO" 5; } + fi + { am_dirpart=; unset am_dirpart;} + { am_filepart=; unset am_filepart;} + { am_mf=; unset am_mf;} + { am_rc=; unset am_rc;} + rm -f conftest-deps.mk } ;; "libtool":C) diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt index b241ddbfec4..3fb90eab021 100644 --- a/libsanitizer/configure.tgt +++ b/libsanitizer/configure.tgt @@ -28,9 +28,6 @@ case "${target}" in LSAN_SUPPORTED=yes TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_amd64.lo fi - if echo "int x = __x86_64__;" | $CC -c -x c -o /dev/null - > /dev/null 2>&1; then - SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS=sanitizer_linux_x86_64.lo - fi ;; powerpc*-*-linux*) if test x$ac_cv_sizeof_void_p = x8; then diff --git a/libsanitizer/include/sanitizer/allocator_interface.h b/libsanitizer/include/sanitizer/allocator_interface.h index e125ad21d31..6226135ef84 100644 --- a/libsanitizer/include/sanitizer/allocator_interface.h +++ b/libsanitizer/include/sanitizer/allocator_interface.h @@ -1,7 +1,8 @@ //===-- allocator_interface.h ---------------------------------------------===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/include/sanitizer/asan_interface.h b/libsanitizer/include/sanitizer/asan_interface.h index 6e8fe256a95..ab2dc97ed24 100644 --- a/libsanitizer/include/sanitizer/asan_interface.h +++ b/libsanitizer/include/sanitizer/asan_interface.h @@ -1,11 +1,12 @@ //===-- sanitizer/asan_interface.h ------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // -// This file is a part of AddressSanitizer. +// This file is a part of AddressSanitizer (ASan). // // Public interface header. //===----------------------------------------------------------------------===// @@ -17,28 +18,54 @@ #ifdef __cplusplus extern "C" { #endif - // Marks memory region [addr, addr+size) as unaddressable. - // This memory must be previously allocated by the user program. Accessing - // addresses in this region from instrumented code is forbidden until - // this region is unpoisoned. This function is not guaranteed to poison - // the whole region - it may poison only subregion of [addr, addr+size) due - // 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); - // 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. - // This function may unpoison a superregion of [addr, addr+size) due 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_unpoison_memory_region(void const volatile *addr, size_t size); - -// User code should use macros instead of functions. +/// Marks a memory region ([addr, addr+size)) as unaddressable. +/// +/// This memory must be previously allocated by your program. Instrumented +/// code is forbidden from accessing addresses in this region until it is +/// unpoisoned. This function is not guaranteed to poison the entire region - +/// it could poison only a subregion of [addr, addr+size) due to ASan +/// alignment restrictions. +/// +/// \note This function is not thread-safe because no two threads can poison or +/// unpoison memory in the same memory region simultaneously. +/// +/// \param addr Start of memory region. +/// \param size Size of memory region. +void __asan_poison_memory_region(void const volatile *addr, size_t size); + +/// Marks a memory region ([addr, addr+size)) as addressable. +/// +/// This memory must be previously allocated by your program. Accessing +/// addresses in this region is allowed until this region is poisoned again. +/// This function could unpoison a super-region of [addr, addr+size) due +/// to ASan alignment restrictions. +/// +/// \note This function is not thread-safe because no two threads can +/// poison or unpoison memory in the same memory region simultaneously. +/// +/// \param addr Start of memory region. +/// \param size Size of memory region. +void __asan_unpoison_memory_region(void const volatile *addr, size_t size); + +// Macros provided for convenience. #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +/// Marks a memory region as unaddressable. +/// +/// \note Macro provided for convenience; defined as a no-op if ASan is not +/// enabled. +/// +/// \param addr Start of memory region. +/// \param size Size of memory region. #define ASAN_POISON_MEMORY_REGION(addr, size) \ __asan_poison_memory_region((addr), (size)) + +/// Marks a memory region as addressable. +/// +/// \note Macro provided for convenience; defined as a no-op if ASan is not +/// enabled. +/// +/// \param addr Start of memory region. +/// \param size Size of memory region. #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ __asan_unpoison_memory_region((addr), (size)) #else @@ -48,103 +75,245 @@ extern "C" { ((void)(addr), (void)(size)) #endif - // Returns 1 if addr is poisoned (i.e. 1-byte read/write access to this - // address will result in error report from AddressSanitizer). - // Otherwise returns 0. - int __asan_address_is_poisoned(void const volatile *addr); - - // If at least one byte in [beg, beg+size) is poisoned, return the address - // of the first such byte. Otherwise return 0. - void *__asan_region_is_poisoned(void *beg, size_t size); - - // Print the description of addr (useful when debugging in gdb). - void __asan_describe_address(void *addr); - - // Useful for calling from a debugger to get information about an ASan error. - // Returns 1 if an error has been (or is being) reported, otherwise returns 0. - int __asan_report_present(void); - - // Useful for calling from a debugger to get information about an ASan error. - // If an error has been (or is being) reported, the following functions return - // the pc, bp, sp, address, access type (0 = read, 1 = write), access size and - // bug description (e.g. "heap-use-after-free"). Otherwise they return 0. - void *__asan_get_report_pc(void); - void *__asan_get_report_bp(void); - void *__asan_get_report_sp(void); - void *__asan_get_report_address(void); - int __asan_get_report_access_type(void); - size_t __asan_get_report_access_size(void); - const char *__asan_get_report_description(void); - - // Useful for calling from the debugger to get information about a pointer. - // Returns the category of the given pointer as a constant string. - // Possible return values are "global", "stack", "stack-fake", "heap", - // "heap-invalid", "shadow-low", "shadow-gap", "shadow-high", "unknown". - // If global or stack, tries to also return the variable name, address and - // size. If heap, tries to return the chunk address and size. 'name' should - // point to an allocated buffer of size 'name_size'. - const char *__asan_locate_address(void *addr, char *name, size_t name_size, - void **region_address, size_t *region_size); - - // Useful for calling from the debugger to get the allocation stack trace - // and thread ID for a heap address. Stores up to 'size' frames into 'trace', - // returns the number of stored frames or 0 on error. - size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size, - int *thread_id); - - // Useful for calling from the debugger to get the free stack trace - // and thread ID for a heap address. Stores up to 'size' frames into 'trace', - // returns the number of stored frames or 0 on error. - size_t __asan_get_free_stack(void *addr, void **trace, size_t size, - int *thread_id); - - // Useful for calling from the debugger to get the current shadow memory - // mapping. - void __asan_get_shadow_mapping(size_t *shadow_scale, size_t *shadow_offset); - - // This is an internal function that is called to report an error. - // However it is still a part of the interface because users may want to - // set a breakpoint on this function in a debugger. - void __asan_report_error(void *pc, void *bp, void *sp, - void *addr, int is_write, size_t access_size); - - // Deprecated. Call __sanitizer_set_death_callback instead. - void __asan_set_death_callback(void (*callback)(void)); - - void __asan_set_error_report_callback(void (*callback)(const char*)); - - // User may provide function that would be called right when ASan detects - // an error. This can be used to notice cases when ASan detects an error, but - // the program crashes before ASan report is printed. - void __asan_on_error(void); - - // Prints accumulated stats to stderr. Used for debugging. - void __asan_print_accumulated_stats(void); - - // This function may be optionally provided by user and should return - // a string containing ASan runtime options. See asan_flags.h for details. - const char* __asan_default_options(void); - - // The following 2 functions facilitate garbage collection in presence of - // asan's fake stack. - - // Returns an opaque handler to be used later in __asan_addr_is_in_fake_stack. - // Returns NULL if the current thread does not have a fake stack. - void *__asan_get_current_fake_stack(void); - - // If fake_stack is non-NULL and addr belongs to a fake frame in - // fake_stack, returns the address on real stack that corresponds to - // the fake frame and sets beg/end to the boundaries of this fake frame. - // Otherwise returns NULL and does not touch beg/end. - // If beg/end are NULL, they are not touched. - // This function may be called from a thread other than the owner of - // fake_stack, but the owner thread need to be alive. - void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, - void **end); - - // 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(void); +/// Checks if an address is poisoned. +/// +/// Returns 1 if addr is poisoned (that is, 1-byte read/write +/// access to this address would result in an error report from ASan). +/// Otherwise returns 0. +/// +/// \param addr Address to check. +/// +/// \retval 1 Address is poisoned. +/// \retval 0 Address is not poisoned. +int __asan_address_is_poisoned(void const volatile *addr); + +/// Checks if a region is poisoned. +/// +/// If at least one byte in [beg, beg+size) is poisoned, returns the +/// address of the first such byte. Otherwise returns 0. +/// +/// \param beg Start of memory region. +/// \param size Start of memory region. +/// \returns Address of first poisoned byte. +void *__asan_region_is_poisoned(void *beg, size_t size); + +/// Describes an address (useful for calling from the debugger). +/// +/// Prints the description of addr. +/// +/// \param addr Address to describe. +void __asan_describe_address(void *addr); + +/// Checks if an error has been or is being reported (useful for calling from +/// the debugger to get information about an ASan error). +/// +/// Returns 1 if an error has been (or is being) reported. Otherwise returns 0. +/// +/// \returns 1 if an error has been (or is being) reported. Otherwise returns +/// 0. +int __asan_report_present(void); + +/// Gets the PC (program counter) register value of an ASan error (useful for +/// calling from the debugger). +/// +/// Returns PC if an error has been (or is being) reported. +/// Otherwise returns 0. +/// +/// \returns PC value. +void *__asan_get_report_pc(void); + +/// Gets the BP (base pointer) register value of an ASan error (useful for +/// calling from the debugger). +/// +/// Returns BP if an error has been (or is being) reported. +/// Otherwise returns 0. +/// +/// \returns BP value. +void *__asan_get_report_bp(void); + +/// Gets the SP (stack pointer) register value of an ASan error (useful for +/// calling from the debugger). +/// +/// If an error has been (or is being) reported, returns SP. +/// Otherwise returns 0. +/// +/// \returns SP value. +void *__asan_get_report_sp(void); + +/// Gets the address of the report buffer of an ASan error (useful for calling +/// from the debugger). +/// +/// Returns the address of the report buffer if an error has been (or is being) +/// reported. Otherwise returns 0. +/// +/// \returns Address of report buffer. +void *__asan_get_report_address(void); + +/// Gets access type of an ASan error (useful for calling from the debugger). +/// +/// Returns access type (read or write) if an error has been (or is being) +/// reported. Otherwise returns 0. +/// +/// \returns Access type (0 = read, 1 = write). +int __asan_get_report_access_type(void); + +/// Gets access size of an ASan error (useful for calling from the debugger). +/// +/// Returns access size if an error has been (or is being) reported. Otherwise +/// returns 0. +/// +/// \returns Access size in bytes. +size_t __asan_get_report_access_size(void); + +/// Gets the bug description of an ASan error (useful for calling from a +/// debugger). +/// +/// \returns Returns a bug description if an error has been (or is being) +/// reported - for example, "heap-use-after-free". Otherwise returns an empty +/// string. +const char *__asan_get_report_description(void); + +/// Gets information about a pointer (useful for calling from the debugger). +/// +/// Returns the category of the given pointer as a constant string. +/// Possible return values are global, stack, stack-fake, +/// heap, heap-invalid, shadow-low, shadow-gap, +/// shadow-high, and unknown. +/// +/// If the return value is global or stack, tries to also return +/// the variable name, address, and size. If the return value is heap, +/// tries to return the chunk address and size. name should point +/// to an allocated buffer of size name_size. +/// +/// \param addr Address to locate. +/// \param name Buffer to store the variable's name. +/// \param name_size Size in bytes of the variable's name buffer. +/// \param region_address [out] Address of the region. +/// \param region_size [out] Size of the region in bytes. +/// +/// \returns Returns the category of the given pointer as a constant string. +const char *__asan_locate_address(void *addr, char *name, size_t name_size, + void **region_address, size_t *region_size); + +/// Gets the allocation stack trace and thread ID for a heap address (useful +/// for calling from the debugger). +/// +/// Stores up to size frames in trace. Returns +/// the number of stored frames or 0 on error. +/// +/// \param addr A heap address. +/// \param trace A buffer to store the stack trace. +/// \param size Size in bytes of the trace buffer. +/// \param thread_id [out] The thread ID of the address. +/// +/// \returns Returns the number of stored frames or 0 on error. +size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size, + int *thread_id); + +/// Gets the free stack trace and thread ID for a heap address (useful for +/// calling from the debugger). +/// +/// Stores up to size frames in trace. Returns +/// the number of stored frames or 0 on error. +/// +/// \param addr A heap address. +/// \param trace A buffer to store the stack trace. +/// \param size Size in bytes of the trace buffer. +/// \param thread_id [out] The thread ID of the address. +/// +/// \returns Returns the number of stored frames or 0 on error. +size_t __asan_get_free_stack(void *addr, void **trace, size_t size, + int *thread_id); + +/// Gets the current shadow memory mapping (useful for calling from the +/// debugger). +/// +/// \param shadow_scale [out] Shadow scale value. +/// \param shadow_offset [out] Offset value. +void __asan_get_shadow_mapping(size_t *shadow_scale, size_t *shadow_offset); + +/// This is an internal function that is called to report an error. However, +/// it is still a part of the interface because you might want to set a +/// breakpoint on this function in the debugger. +/// +/// \param pc pc value of the ASan error. +/// \param bp bp value of the ASan error. +/// \param sp sp value of the ASan error. +/// \param addr Address of the ASan error. +/// \param is_write True if the error is a write error; false otherwise. +/// \param access_size Size of the memory access of the ASan error. +void __asan_report_error(void *pc, void *bp, void *sp, + void *addr, int is_write, size_t access_size); + +// Deprecated. Call __sanitizer_set_death_callback instead. +void __asan_set_death_callback(void (*callback)(void)); + +/// Sets the callback function to be called during ASan error reporting. +/// +/// The callback provides a string pointer to the report. +/// +/// \param callback User-provided function. +void __asan_set_error_report_callback(void (*callback)(const char *)); + +/// User-provided callback on ASan errors. +/// +/// You can provide a function that would be called immediately when ASan +/// detects an error. This is useful in cases when ASan detects an error but +/// your program crashes before the ASan report is printed. +void __asan_on_error(void); + +/// Prints accumulated statistics to stderr (useful for calling from the +/// debugger). +void __asan_print_accumulated_stats(void); + +/// User-provided default option settings. +/// +/// You can provide your own implementation of this function to return a string +/// containing ASan runtime options (for example, +/// verbosity=1:halt_on_error=0). +/// +/// \returns Default options string. +const char* __asan_default_options(void); + +// The following two functions facilitate garbage collection in presence of +// ASan's fake stack. + +/// Gets an opaque handler to the current thread's fake stack. +/// +/// Returns an opaque handler to be used by +/// __asan_addr_is_in_fake_stack(). Returns NULL if the current thread +/// does not have a fake stack. +/// +/// \returns An opaque handler to the fake stack or NULL. +void *__asan_get_current_fake_stack(void); + +/// Checks if an address belongs to a given fake stack. +/// +/// If fake_stack is non-NULL and addr belongs to a +/// fake frame in fake_stack, returns the address of the real +/// stack that corresponds to the fake frame and sets beg and +/// end to the boundaries of this fake frame. Otherwise returns +/// NULL and does not touch beg and end. +/// +/// If beg or end are NULL, they are not touched. +/// +/// \note This function can be called from a thread other than the owner of +/// fake_stack, but the owner thread needs to be alive. +/// +/// \param fake_stack An opaque handler to a fake stack. +/// \param addr Address to test. +/// \param beg [out] Beginning of fake frame. +/// \param end [out] End of fake frame. +/// \returns Stack address or NULL. +void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, + void **end); + +/// Performs shadow memory cleanup of the current thread's stack before a +/// function marked with the [[noreturn]] attribute is called. +/// +/// To avoid false positives on the stack, must be called before no-return +/// functions like _exit() and execl(). +void __asan_handle_no_return(void); #ifdef __cplusplus } // extern "C" diff --git a/libsanitizer/include/sanitizer/common_interface_defs.h b/libsanitizer/include/sanitizer/common_interface_defs.h index b8ae094ac5f..f979c6a8f63 100644 --- a/libsanitizer/include/sanitizer/common_interface_defs.h +++ b/libsanitizer/include/sanitizer/common_interface_defs.h @@ -1,7 +1,8 @@ //===-- sanitizer/common_interface_defs.h -----------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -16,189 +17,335 @@ // GCC does not understand __has_feature. #if !defined(__has_feature) -# define __has_feature(x) 0 +#define __has_feature(x) 0 #endif #ifdef __cplusplus extern "C" { #endif - // Arguments for __sanitizer_sandbox_on_notify() below. - typedef struct { - // Enable sandbox support in sanitizer coverage. - int coverage_sandboxed; - // File descriptor to write coverage data to. If -1 is passed, a file will - // be pre-opened by __sanitizer_sandobx_on_notify(). This field has no - // effect if coverage_sandboxed == 0. - intptr_t coverage_fd; - // If non-zero, split the coverage data into well-formed blocks. This is - // useful when coverage_fd is a socket descriptor. Each block will contain - // a header, allowing data from multiple processes to be sent over the same - // socket. - unsigned int coverage_max_block_size; - } __sanitizer_sandbox_arguments; - - // Tell the tools to write their reports to "path." instead of stderr. - void __sanitizer_set_report_path(const char *path); - // Tell the tools to write their reports to the provided file descriptor - // (casted to void *). - void __sanitizer_set_report_fd(void *fd); - - // Notify the tools that the sandbox is going to be turned on. The reserved - // parameter will be used in the future to hold a structure with functions - // that the tools may call to bypass the sandbox. - void __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args); - - // This function is called by the tool when it has just finished reporting - // an error. 'error_summary' is a one-line string that summarizes - // the error message. This function can be overridden by the client. - void __sanitizer_report_error_summary(const char *error_summary); - - // Some of the sanitizers (e.g. asan/tsan) may miss bugs that happen - // in unaligned loads/stores. In order to find such bugs reliably one needs - // to replace plain unaligned loads/stores with these calls. - uint16_t __sanitizer_unaligned_load16(const void *p); - uint32_t __sanitizer_unaligned_load32(const void *p); - uint64_t __sanitizer_unaligned_load64(const void *p); - void __sanitizer_unaligned_store16(void *p, uint16_t x); - void __sanitizer_unaligned_store32(void *p, uint32_t x); - void __sanitizer_unaligned_store64(void *p, uint64_t x); - - // Returns 1 on the first call, then returns 0 thereafter. Called by the tool - // to ensure only one report is printed when multiple errors occur - // simultaneously. - int __sanitizer_acquire_crash_state(); - - // Annotate the current state of a contiguous container, such as - // std::vector, std::string or similar. - // A contiguous container is a container that keeps all of its elements - // in a contiguous region of memory. The container owns the region of memory - // [beg, end); the memory [beg, mid) is used to store the current elements - // and the memory [mid, end) is reserved for future elements; - // beg <= mid <= end. For example, in "std::vector<> v" - // beg = &v[0]; - // end = beg + v.capacity() * sizeof(v[0]); - // mid = beg + v.size() * sizeof(v[0]); - // - // This annotation tells the Sanitizer tool about the current state of the - // container so that the tool can report errors when memory from [mid, end) - // is accessed. Insert this annotation into methods like push_back/pop_back. - // Supply the old and the new values of mid (old_mid/new_mid). - // In the initial state mid == end and so should be the final - // state when the container is destroyed or when it reallocates the storage. - // - // Use with caution and don't use for anything other than vector-like classes. - // - // For AddressSanitizer, 'beg' should be 8-aligned and 'end' should - // be either 8-aligned or it should point to the end of a separate heap-, - // stack-, or global- allocated buffer. I.e. the following will not work: - // int64_t x[2]; // 16 bytes, 8-aligned. - // char *beg = (char *)&x[0]; - // char *end = beg + 12; // Not 8 aligned, not the end of the buffer. - // This however will work fine: - // int32_t x[3]; // 12 bytes, but 8-aligned under AddressSanitizer. - // char *beg = (char*)&x[0]; - // char *end = beg + 12; // Not 8-aligned, but is the end of the buffer. - void __sanitizer_annotate_contiguous_container(const void *beg, - const void *end, - const void *old_mid, - const void *new_mid); - // Returns true if the contiguous container [beg, end) is properly poisoned - // (e.g. with __sanitizer_annotate_contiguous_container), i.e. if - // - [beg, mid) is addressable, - // - [mid, end) is unaddressable. - // Full verification requires O(end-beg) time; this function tries to avoid - // such complexity by touching only parts of the container around beg/mid/end. - int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, - const void *end); - - // Similar to __sanitizer_verify_contiguous_container but returns the address - // of the first improperly poisoned byte otherwise. Returns null if the area - // is poisoned properly. - const void *__sanitizer_contiguous_container_find_bad_address( - const void *beg, const void *mid, const void *end); - - // Print the stack trace leading to this call. Useful for debugging user code. - void __sanitizer_print_stack_trace(void); - - // Symbolizes the supplied 'pc' using the format string 'fmt'. - // Outputs at most 'out_buf_size' bytes into 'out_buf'. - // If 'out_buf' is not empty then output is zero or more non empty C strings - // followed by single empty C string. Multiple strings can be returned if PC - // corresponds to inlined function. Inlined frames are printed in the order - // from "most-inlined" to the "least-inlined", so the last frame should be the - // not inlined function. - // Inlined frames can be removed with 'symbolize_inline_frames=0'. - // The format syntax is described in - // lib/sanitizer_common/sanitizer_stacktrace_printer.h. - void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf, - size_t out_buf_size); - // Same as __sanitizer_symbolize_pc, but for data section (i.e. globals). - void __sanitizer_symbolize_global(void *data_ptr, const char *fmt, - char *out_buf, size_t out_buf_size); - - // Sets the callback to be called right before death on error. - // Passing 0 will unset the callback. - void __sanitizer_set_death_callback(void (*callback)(void)); - - // Interceptor hooks. - // Whenever a libc function interceptor is called it checks if the - // corresponding weak hook is defined, and it so -- calls it. - // The primary use case is data-flow-guided fuzzing, where the fuzzer needs - // to know what is being passed to libc functions, e.g. memcmp. - // FIXME: implement more hooks. - void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1, - const void *s2, size_t n, int result); - void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1, - const char *s2, size_t n, int result); - void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, - const char *s2, size_t n, int result); - void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1, - const char *s2, int result); - void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, - const char *s2, int result); - void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1, - const char *s2, char *result); - void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, - const char *s2, char *result); - void __sanitizer_weak_hook_memmem(void *called_pc, - const void *s1, size_t len1, - const void *s2, size_t len2, void *result); - - // Prints stack traces for all live heap allocations ordered by total - // allocation size until `top_percent` of total live heap is shown. - // `top_percent` should be between 1 and 100. - // At most `max_number_of_contexts` contexts (stack traces) is printed. - // Experimental feature currently available only with asan on Linux/x86_64. - void __sanitizer_print_memory_profile(size_t top_percent, - size_t max_number_of_contexts); - - // Fiber annotation interface. - // Before switching to a different stack, one must call - // __sanitizer_start_switch_fiber with a pointer to the bottom of the - // destination stack and its size. When code starts running on the new stack, - // it must call __sanitizer_finish_switch_fiber to finalize the switch. - // The start_switch function takes a void** to store the current fake stack if - // there is one (it is needed when detect_stack_use_after_return is enabled). - // When restoring a stack, this pointer must be given to the finish_switch - // function. In most cases, this void* can be stored on the stack just before - // switching. When leaving a fiber definitely, null must be passed as first - // argument to the start_switch function so that the fake stack is destroyed. - // If you do not want support for stack use-after-return detection, you can - // always pass null to these two functions. - // Note that the fake stack mechanism is disabled during fiber switch, so if a - // signal callback runs during the switch, it will not benefit from the stack - // use-after-return detection. - void __sanitizer_start_switch_fiber(void **fake_stack_save, - const void *bottom, size_t size); - void __sanitizer_finish_switch_fiber(void *fake_stack_save, - const void **bottom_old, - size_t *size_old); - - // Get full module name and calculate pc offset within it. - // Returns 1 if pc belongs to some module, 0 if module was not found. - int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_path, - size_t module_path_len, - void **pc_offset); +// Arguments for __sanitizer_sandbox_on_notify() below. +typedef struct { + // Enable sandbox support in sanitizer coverage. + int coverage_sandboxed; + // File descriptor to write coverage data to. If -1 is passed, a file will + // be pre-opened by __sanitizer_sandobx_on_notify(). This field has no + // effect if coverage_sandboxed == 0. + intptr_t coverage_fd; + // If non-zero, split the coverage data into well-formed blocks. This is + // useful when coverage_fd is a socket descriptor. Each block will contain + // a header, allowing data from multiple processes to be sent over the same + // socket. + unsigned int coverage_max_block_size; +} __sanitizer_sandbox_arguments; + +// Tell the tools to write their reports to "path." instead of stderr. +void __sanitizer_set_report_path(const char *path); +// Tell the tools to write their reports to the provided file descriptor +// (casted to void *). +void __sanitizer_set_report_fd(void *fd); + +// Notify the tools that the sandbox is going to be turned on. The reserved +// parameter will be used in the future to hold a structure with functions +// that the tools may call to bypass the sandbox. +void __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args); + +// This function is called by the tool when it has just finished reporting +// an error. 'error_summary' is a one-line string that summarizes +// the error message. This function can be overridden by the client. +void __sanitizer_report_error_summary(const char *error_summary); + +// Some of the sanitizers (for example ASan/TSan) could miss bugs that happen +// in unaligned loads/stores. To find such bugs reliably, you need to replace +// plain unaligned loads/stores with these calls. + +/// Loads a 16-bit unaligned value. +/// +/// \param p Pointer to unaligned memory. +/// +/// \returns Loaded value. +uint16_t __sanitizer_unaligned_load16(const void *p); + +/// Loads a 32-bit unaligned value. +/// +/// \param p Pointer to unaligned memory. +/// +/// \returns Loaded value. +uint32_t __sanitizer_unaligned_load32(const void *p); + +/// Loads a 64-bit unaligned value. +/// +/// \param p Pointer to unaligned memory. +/// +/// \returns Loaded value. +uint64_t __sanitizer_unaligned_load64(const void *p); + +/// Stores a 16-bit unaligned value. +/// +/// \param p Pointer to unaligned memory. +/// \param x 16-bit value to store. +void __sanitizer_unaligned_store16(void *p, uint16_t x); + +/// Stores a 32-bit unaligned value. +/// +/// \param p Pointer to unaligned memory. +/// \param x 32-bit value to store. +void __sanitizer_unaligned_store32(void *p, uint32_t x); + +/// Stores a 64-bit unaligned value. +/// +/// \param p Pointer to unaligned memory. +/// \param x 64-bit value to store. +void __sanitizer_unaligned_store64(void *p, uint64_t x); + +// Returns 1 on the first call, then returns 0 thereafter. Called by the tool +// to ensure only one report is printed when multiple errors occur +// simultaneously. +int __sanitizer_acquire_crash_state(); + +/// Annotates the current state of a contiguous container, such as +/// std::vector, std::string, or similar. +/// +/// A contiguous container is a container that keeps all of its elements +/// in a contiguous region of memory. The container owns the region of memory +/// [beg, end); the memory [beg, mid) is used to store the +/// current elements, and the memory [mid, end) is reserved for future +/// elements (beg <= mid <= end). For example, in +/// std::vector<> v: +/// +/// \code +/// beg = &v[0]; +/// end = beg + v.capacity() * sizeof(v[0]); +/// mid = beg + v.size() * sizeof(v[0]); +/// \endcode +/// +/// This annotation tells the Sanitizer tool about the current state of the +/// container so that the tool can report errors when memory from +/// [mid, end) is accessed. Insert this annotation into methods like +/// push_back() or pop_back(). Supply the old and new values of +/// mid(old_mid and new_mid). In the initial +/// state mid == end, so that should be the final state when the +/// container is destroyed or when the container reallocates the storage. +/// +/// For ASan, beg should be 8-aligned and end +/// should be either 8-aligned or it should point to the end of a separate +/// heap-, stack-, or global-allocated buffer. So the following example will +/// not work: +/// +/// \code +/// int64_t x[2]; // 16 bytes, 8-aligned +/// char *beg = (char *)&x[0]; +/// char *end = beg + 12; // Not 8-aligned, not the end of the buffer +/// \endcode +/// +/// The following, however, will work: +/// \code +/// int32_t x[3]; // 12 bytes, but 8-aligned under ASan. +/// char *beg = (char*)&x[0]; +/// char *end = beg + 12; // Not 8-aligned, but is the end of the buffer +/// \endcode +/// +/// \note Use this function with caution and do not use for anything other +/// than vector-like classes. +/// +/// \param beg Beginning of memory region. +/// \param end End of memory region. +/// \param old_mid Old middle of memory region. +/// \param new_mid New middle of memory region. +void __sanitizer_annotate_contiguous_container(const void *beg, + const void *end, + const void *old_mid, + const void *new_mid); + +/// Returns true if the contiguous container [beg, end) is properly +/// poisoned. +/// +/// Proper poisoning could occur, for example, with +/// __sanitizer_annotate_contiguous_container), that is, if +/// [beg, mid) is addressable and [mid, end) is unaddressable. +/// Full verification requires O (end - beg) time; this function tries +/// to avoid such complexity by touching only parts of the container around +/// beg, mid, and end. +/// +/// \param beg Beginning of memory region. +/// \param mid Middle of memory region. +/// \param end Old end of memory region. +/// +/// \returns True if the contiguous container [beg, end) is properly +/// poisoned. +int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, + const void *end); + +/// Similar to __sanitizer_verify_contiguous_container() but also +/// returns the address of the first improperly poisoned byte. +/// +/// Returns NULL if the area is poisoned properly. +/// +/// \param beg Beginning of memory region. +/// \param mid Middle of memory region. +/// \param end Old end of memory region. +/// +/// \returns The bad address or NULL. +const void *__sanitizer_contiguous_container_find_bad_address(const void *beg, + const void *mid, + const void *end); + +/// Prints the stack trace leading to this call (useful for calling from the +/// debugger). +void __sanitizer_print_stack_trace(void); + +// Symbolizes the supplied 'pc' using the format string 'fmt'. +// Outputs at most 'out_buf_size' bytes into 'out_buf'. +// If 'out_buf' is not empty then output is zero or more non empty C strings +// followed by single empty C string. Multiple strings can be returned if PC +// corresponds to inlined function. Inlined frames are printed in the order +// from "most-inlined" to the "least-inlined", so the last frame should be the +// not inlined function. +// Inlined frames can be removed with 'symbolize_inline_frames=0'. +// The format syntax is described in +// lib/sanitizer_common/sanitizer_stacktrace_printer.h. +void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf, + size_t out_buf_size); +// Same as __sanitizer_symbolize_pc, but for data section (i.e. globals). +void __sanitizer_symbolize_global(void *data_ptr, const char *fmt, + char *out_buf, size_t out_buf_size); + +/// Sets the callback to be called immediately before death on error. +/// +/// Passing 0 will unset the callback. +/// +/// \param callback User-provided callback. +void __sanitizer_set_death_callback(void (*callback)(void)); + + +// Interceptor hooks. +// Whenever a libc function interceptor is called, it checks if the +// corresponding weak hook is defined, and calls it if it is indeed defined. +// The primary use-case is data-flow-guided fuzzing, where the fuzzer needs +// to know what is being passed to libc functions (for example memcmp). +// FIXME: implement more hooks. + +/// Interceptor hook for memcmp(). +/// +/// \param called_pc PC (program counter) address of the original call. +/// \param s1 Pointer to block of memory. +/// \param s2 Pointer to block of memory. +/// \param n Number of bytes to compare. +/// \param result Value returned by the intercepted function. +void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1, + const void *s2, size_t n, int result); + +/// Interceptor hook for strncmp(). +/// +/// \param called_pc PC (program counter) address of the original call. +/// \param s1 Pointer to block of memory. +/// \param s2 Pointer to block of memory. +/// \param n Number of bytes to compare. +/// \param result Value returned by the intercepted function. +void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result); + +/// Interceptor hook for strncasecmp(). +/// +/// \param called_pc PC (program counter) address of the original call. +/// \param s1 Pointer to block of memory. +/// \param s2 Pointer to block of memory. +/// \param n Number of bytes to compare. +/// \param result Value returned by the intercepted function. +void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result); + +/// Interceptor hook for strcmp(). +/// +/// \param called_pc PC (program counter) address of the original call. +/// \param s1 Pointer to block of memory. +/// \param s2 Pointer to block of memory. +/// \param result Value returned by the intercepted function. +void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1, + const char *s2, int result); + +/// Interceptor hook for strcasecmp(). +/// +/// \param called_pc PC (program counter) address of the original call. +/// \param s1 Pointer to block of memory. +/// \param s2 Pointer to block of memory. +/// \param result Value returned by the intercepted function. +void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, + const char *s2, int result); + +/// Interceptor hook for strstr(). +/// +/// \param called_pc PC (program counter) address of the original call. +/// \param s1 Pointer to block of memory. +/// \param s2 Pointer to block of memory. +/// \param result Value returned by the intercepted function. +void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1, + const char *s2, char *result); + +void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, + const char *s2, char *result); + +void __sanitizer_weak_hook_memmem(void *called_pc, + const void *s1, size_t len1, + const void *s2, size_t len2, void *result); + +// Prints stack traces for all live heap allocations ordered by total +// allocation size until top_percent of total live heap is shown. top_percent +// should be between 1 and 100. At most max_number_of_contexts contexts +// (stack traces) are printed. +// Experimental feature currently available only with ASan on Linux/x86_64. +void __sanitizer_print_memory_profile(size_t top_percent, + size_t max_number_of_contexts); + +/// Notify ASan that a fiber switch has started (required only if implementing +/// your own fiber library). +/// +/// Before switching to a different stack, you must call +/// __sanitizer_start_switch_fiber() with a pointer to the bottom of the +/// destination stack and with its size. When code starts running on the new +/// stack, it must call __sanitizer_finish_switch_fiber() to finalize +/// the switch. The __sanitizer_start_switch_fiber() function takes a +/// void** pointer argument to store the current fake stack if there is +/// one (it is necessary when the runtime option +/// detect_stack_use_after_return is enabled). +/// +/// When restoring a stack, this void** pointer must be given to the +/// __sanitizer_finish_switch_fiber() function. In most cases, this +/// pointer can be stored on the stack immediately before switching. When +/// leaving a fiber definitely, NULL must be passed as the first argument to +/// the __sanitizer_start_switch_fiber() function so that the fake stack +/// is destroyed. If your program does not need stack use-after-return +/// detection, you can always pass NULL to these two functions. +/// +/// \note The fake stack mechanism is disabled during fiber switch, so if a +/// signal callback runs during the switch, it will not benefit from stack +/// use-after-return detection. +/// +/// \param fake_stack_save [out] Fake stack save location. +/// \param bottom Bottom address of stack. +/// \param size Size of stack in bytes. +void __sanitizer_start_switch_fiber(void **fake_stack_save, + const void *bottom, size_t size); + +/// Notify ASan that a fiber switch has completed (required only if +/// implementing your own fiber library). +/// +/// When code starts running on the new stack, it must call +/// __sanitizer_finish_switch_fiber() to finalize +/// the switch. For usage details, see the description of +/// __sanitizer_start_switch_fiber(). +/// +/// \param fake_stack_save Fake stack save location. +/// \param bottom_old [out] Bottom address of old stack. +/// \param size_old [out] Size of old stack in bytes. +void __sanitizer_finish_switch_fiber(void *fake_stack_save, + const void **bottom_old, + size_t *size_old); + +// Get full module name and calculate pc offset within it. +// Returns 1 if pc belongs to some module, 0 if module was not found. +int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_path, + size_t module_path_len, + void **pc_offset); #ifdef __cplusplus } // extern "C" diff --git a/libsanitizer/include/sanitizer/coverage_interface.h b/libsanitizer/include/sanitizer/coverage_interface.h index 2f3613583da..c063cfe60c5 100644 --- a/libsanitizer/include/sanitizer/coverage_interface.h +++ b/libsanitizer/include/sanitizer/coverage_interface.h @@ -1,7 +1,8 @@ //===-- sanitizer/coverage_interface.h --------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/include/sanitizer/dfsan_interface.h b/libsanitizer/include/sanitizer/dfsan_interface.h index 0cebccf945e..c189ee55790 100644 --- a/libsanitizer/include/sanitizer/dfsan_interface.h +++ b/libsanitizer/include/sanitizer/dfsan_interface.h @@ -1,7 +1,8 @@ //===-- dfsan_interface.h -------------------------------------------------===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -78,6 +79,12 @@ dfsan_label dfsan_has_label_with_desc(dfsan_label label, const char *desc); /// Returns the number of labels allocated. size_t dfsan_get_label_count(void); +/// Flushes the DFSan shadow, i.e. forgets about all labels currently associated +/// with the application memory. Will work only if there are no other +/// threads executing DFSan-instrumented code concurrently. +/// Use this call to start over the taint tracking within the same procces. +void dfsan_flush(void); + /// Sets a callback to be invoked on calls to write(). The callback is invoked /// before the write is done. The write is not guaranteed to succeed when the /// callback executes. Pass in NULL to remove any callback. diff --git a/libsanitizer/include/sanitizer/esan_interface.h b/libsanitizer/include/sanitizer/esan_interface.h deleted file mode 100644 index e22b6a8f4d7..00000000000 --- a/libsanitizer/include/sanitizer/esan_interface.h +++ /dev/null @@ -1,48 +0,0 @@ -//===-- sanitizer/esan_interface.h ------------------------------*- C++ -*-===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of EfficiencySanitizer, a family of performance tuners. -// -// Public interface header. -//===----------------------------------------------------------------------===// -#ifndef SANITIZER_ESAN_INTERFACE_H -#define SANITIZER_ESAN_INTERFACE_H - -#include - -// We declare our interface routines as weak to allow the user to avoid -// ifdefs and instead use this pattern to allow building the same sources -// with and without our runtime library: -// if (__esan_report) -// __esan_report(); -#ifdef _MSC_VER -/* selectany is as close to weak as we'll get. */ -#define COMPILER_RT_WEAK __declspec(selectany) -#elif __GNUC__ -#define COMPILER_RT_WEAK __attribute__((weak)) -#else -#define COMPILER_RT_WEAK -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -// This function can be called mid-run (or at the end of a run for -// a server process that doesn't shut down normally) to request that -// data for that point in the run be reported from the tool. -void COMPILER_RT_WEAK __esan_report(void); - -// This function returns the number of samples that the esan tool has collected -// to this point. This is useful for testing. -unsigned int COMPILER_RT_WEAK __esan_get_sample_count(void); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // SANITIZER_ESAN_INTERFACE_H diff --git a/libsanitizer/include/sanitizer/hwasan_interface.h b/libsanitizer/include/sanitizer/hwasan_interface.h index 938e9ac464a..4c9ad13aa0c 100644 --- a/libsanitizer/include/sanitizer/hwasan_interface.h +++ b/libsanitizer/include/sanitizer/hwasan_interface.h @@ -1,7 +1,8 @@ //===-- sanitizer/asan_interface.h ------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -17,11 +18,15 @@ #ifdef __cplusplus extern "C" { #endif - // Initialize shadow but not the rest of the runtime. + // Libc hook for program startup in statically linked executables. + // Initializes enough of the runtime to run instrumented code. This function + // should only be called in statically linked executables because it modifies + // the GOT, which won't work in regular binaries because RELRO will already + // have been applied by the time the function is called. This also means that + // the function should be called before libc applies RELRO. // Does not call libc unless there is an error. - // Can be called multiple times, or not at all (in which case shadow will - // be initialized in compiler-inserted __hwasan_init() call). - void __hwasan_shadow_init(void); + // Can be called multiple times. + void __hwasan_init_static(void); // This function may be optionally provided by user and should return // a string containing HWASan runtime options. See asan_flags.h for details. @@ -45,6 +50,10 @@ extern "C" { // does would cause false reports. void __hwasan_handle_longjmp(const void *sp_dst); + // Set memory tag for the part of the current thread stack below sp_dst to + // zero. Call this in vfork() before returning in the parent process. + void __hwasan_handle_vfork(const void *sp_dst); + // Libc hook for thread creation. Should be called in the child thread before // any instrumented code. void __hwasan_thread_enter(); @@ -60,6 +69,10 @@ extern "C" { // Print one-line report about the memory usage of the current process. void __hwasan_print_memory_usage(); + /* Returns the offset of the first byte in the memory range that can not be + * accessed through the pointer in x, or -1 if the whole range is good. */ + intptr_t __hwasan_test_shadow(const volatile void *x, size_t size); + int __sanitizer_posix_memalign(void **memptr, size_t alignment, size_t size); void * __sanitizer_memalign(size_t alignment, size_t size); void * __sanitizer_aligned_alloc(size_t alignment, size_t size); @@ -74,6 +87,7 @@ extern "C" { void __sanitizer_malloc_stats(void); void * __sanitizer_calloc(size_t nmemb, size_t size); void * __sanitizer_realloc(void *ptr, size_t size); + void * __sanitizer_reallocarray(void *ptr, size_t nmemb, size_t size); void * __sanitizer_malloc(size_t size); #ifdef __cplusplus } // extern "C" diff --git a/libsanitizer/include/sanitizer/linux_syscall_hooks.h b/libsanitizer/include/sanitizer/linux_syscall_hooks.h index 34bb2912406..a1794b71af5 100644 --- a/libsanitizer/include/sanitizer/linux_syscall_hooks.h +++ b/libsanitizer/include/sanitizer/linux_syscall_hooks.h @@ -1,7 +1,8 @@ //===-- linux_syscall_hooks.h ---------------------------------------------===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/include/sanitizer/lsan_interface.h b/libsanitizer/include/sanitizer/lsan_interface.h index 93b2e9ca3f1..2bb992672f2 100644 --- a/libsanitizer/include/sanitizer/lsan_interface.h +++ b/libsanitizer/include/sanitizer/lsan_interface.h @@ -1,7 +1,8 @@ //===-- sanitizer/lsan_interface.h ------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/include/sanitizer/msan_interface.h b/libsanitizer/include/sanitizer/msan_interface.h index 4dfae604f7a..d40c556a46d 100644 --- a/libsanitizer/include/sanitizer/msan_interface.h +++ b/libsanitizer/include/sanitizer/msan_interface.h @@ -1,7 +1,8 @@ //===-- msan_interface.h --------------------------------------------------===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -41,6 +42,9 @@ extern "C" { contents). */ void __msan_unpoison_string(const volatile char *a); + /* Make first n parameters of the next function call fully initialized. */ + void __msan_unpoison_param(size_t n); + /* Make memory region fully uninitialized (without changing its contents). This is a legacy interface that does not update origin information. Use __msan_allocated_memory() instead. */ diff --git a/libsanitizer/include/sanitizer/netbsd_syscall_hooks.h b/libsanitizer/include/sanitizer/netbsd_syscall_hooks.h index 8cf5121726e..27780e0d419 100644 --- a/libsanitizer/include/sanitizer/netbsd_syscall_hooks.h +++ b/libsanitizer/include/sanitizer/netbsd_syscall_hooks.h @@ -1,7 +1,8 @@ //===-- netbsd_syscall_hooks.h --------------------------------------------===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -19,8 +20,8 @@ // DO NOT EDIT! THIS FILE HAS BEEN GENERATED! // // Generated with: generate_netbsd_syscalls.awk -// Generated date: 2018-03-03 -// Generated from: syscalls.master,v 1.291 2018/01/06 16:41:23 kamil Exp +// Generated date: 2018-10-30 +// Generated from: syscalls.master,v 1.293 2018/07/31 13:00:13 rjs Exp // //===----------------------------------------------------------------------===// #ifndef SANITIZER_NETBSD_SYSCALL_HOOKS_H @@ -984,7 +985,15 @@ #define __sanitizer_syscall_post_fpathconf(res, fd, name) \ __sanitizer_syscall_post_impl_fpathconf(res, (long long)(fd), \ (long long)(name)) -/* syscall 193 has been skipped */ +#define __sanitizer_syscall_pre_getsockopt2(s, level, name, val, avalsize) \ + __sanitizer_syscall_pre_impl_getsockopt2( \ + (long long)(s), (long long)(level), (long long)(name), (long long)(val), \ + (long long)(avalsize)) +#define __sanitizer_syscall_post_getsockopt2(res, s, level, name, val, \ + avalsize) \ + __sanitizer_syscall_post_impl_getsockopt2( \ + res, (long long)(s), (long long)(level), (long long)(name), \ + (long long)(val), (long long)(avalsize)) #define __sanitizer_syscall_pre_getrlimit(which, rlp) \ __sanitizer_syscall_pre_impl_getrlimit((long long)(which), (long long)(rlp)) #define __sanitizer_syscall_post_getrlimit(res, which, rlp) \ @@ -1750,18 +1759,8 @@ __sanitizer_syscall_post_impl___sigaction_sigtramp( \ res, (long long)(signum), (long long)(nsa), (long long)(osa), \ (long long)(tramp), (long long)(vers)) -#define __sanitizer_syscall_pre_pmc_get_info(ctr, op, args) \ - __sanitizer_syscall_pre_impl_pmc_get_info((long long)(ctr), (long long)(op), \ - (long long)(args)) -#define __sanitizer_syscall_post_pmc_get_info(res, ctr, op, args) \ - __sanitizer_syscall_post_impl_pmc_get_info( \ - res, (long long)(ctr), (long long)(op), (long long)(args)) -#define __sanitizer_syscall_pre_pmc_control(ctr, op, args) \ - __sanitizer_syscall_pre_impl_pmc_control((long long)(ctr), (long long)(op), \ - (long long)(args)) -#define __sanitizer_syscall_post_pmc_control(res, ctr, op, args) \ - __sanitizer_syscall_post_impl_pmc_control( \ - res, (long long)(ctr), (long long)(op), (long long)(args)) +/* syscall 341 has been skipped */ +/* syscall 342 has been skipped */ #define __sanitizer_syscall_pre_rasctl(addr, len, op) \ __sanitizer_syscall_pre_impl_rasctl((long long)(addr), (long long)(len), \ (long long)(op)) @@ -3442,7 +3441,13 @@ void __sanitizer_syscall_post_impl_pathconf(long long res, long long path, void __sanitizer_syscall_pre_impl_fpathconf(long long fd, long long name); void __sanitizer_syscall_post_impl_fpathconf(long long res, long long fd, long long name); -/* syscall 193 has been skipped */ +void __sanitizer_syscall_pre_impl_getsockopt2(long long s, long long level, + long long name, long long val, + long long avalsize); +void __sanitizer_syscall_post_impl_getsockopt2(long long res, long long s, + long long level, long long name, + long long val, + long long avalsize); void __sanitizer_syscall_pre_impl_getrlimit(long long which, long long rlp); void __sanitizer_syscall_post_impl_getrlimit(long long res, long long which, long long rlp); @@ -3999,14 +4004,8 @@ void __sanitizer_syscall_pre_impl___sigaction_sigtramp(long long signum, void __sanitizer_syscall_post_impl___sigaction_sigtramp( long long res, long long signum, long long nsa, long long osa, long long tramp, long long vers); -void __sanitizer_syscall_pre_impl_pmc_get_info(long long ctr, long long op, - long long args); -void __sanitizer_syscall_post_impl_pmc_get_info(long long res, long long ctr, - long long op, long long args); -void __sanitizer_syscall_pre_impl_pmc_control(long long ctr, long long op, - long long args); -void __sanitizer_syscall_post_impl_pmc_control(long long res, long long ctr, - long long op, long long args); +/* syscall 341 has been skipped */ +/* syscall 342 has been skipped */ void __sanitizer_syscall_pre_impl_rasctl(long long addr, long long len, long long op); void __sanitizer_syscall_post_impl_rasctl(long long res, long long addr, diff --git a/libsanitizer/include/sanitizer/scudo_interface.h b/libsanitizer/include/sanitizer/scudo_interface.h index ca9a6f1fcb7..dd522c1efc2 100644 --- a/libsanitizer/include/sanitizer/scudo_interface.h +++ b/libsanitizer/include/sanitizer/scudo_interface.h @@ -1,7 +1,8 @@ //===-- sanitizer/scudo_interface.h -----------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/include/sanitizer/tsan_interface.h b/libsanitizer/include/sanitizer/tsan_interface.h index b86062bb119..011b23350ca 100644 --- a/libsanitizer/include/sanitizer/tsan_interface.h +++ b/libsanitizer/include/sanitizer/tsan_interface.h @@ -1,7 +1,8 @@ //===-- tsan_interface.h ----------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -135,6 +136,24 @@ void __tsan_external_assign_tag(void *addr, void *tag); void __tsan_external_read(void *addr, void *caller_pc, void *tag); void __tsan_external_write(void *addr, void *caller_pc, void *tag); +// Fiber switching API. +// - TSAN context for fiber can be created by __tsan_create_fiber +// and freed by __tsan_destroy_fiber. +// - TSAN context of current fiber or thread can be obtained +// by calling __tsan_get_current_fiber. +// - __tsan_switch_to_fiber should be called immediatly before switch +// to fiber, such as call of swapcontext. +// - Fiber name can be set by __tsan_set_fiber_name. +void *__tsan_get_current_fiber(void); +void *__tsan_create_fiber(unsigned flags); +void __tsan_destroy_fiber(void *fiber); +void __tsan_switch_to_fiber(void *fiber, unsigned flags); +void __tsan_set_fiber_name(void *fiber, const char *name); + +// Flags for __tsan_switch_to_fiber: +// Do not establish a happens-before relation between fibers +const unsigned __tsan_switch_to_fiber_no_sync = 1 << 0; + #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/include/sanitizer/tsan_interface_atomic.h b/libsanitizer/include/sanitizer/tsan_interface_atomic.h index d19c9109416..9ce0411917d 100644 --- a/libsanitizer/include/sanitizer/tsan_interface_atomic.h +++ b/libsanitizer/include/sanitizer/tsan_interface_atomic.h @@ -1,7 +1,8 @@ //===-- tsan_interface_atomic.h ---------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -29,7 +30,7 @@ __extension__ typedef __int128 __tsan_atomic128; #endif // Part of ABI, do not change. -// http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/atomic?view=markup +// https://github.com/llvm/llvm-project/blob/master/libcxx/include/atomic typedef enum { __tsan_memory_order_relaxed, __tsan_memory_order_consume, diff --git a/libsanitizer/interception/Makefile.am b/libsanitizer/interception/Makefile.am index 22ce8d5edac..a22e0b4e2af 100644 --- a/libsanitizer/interception/Makefile.am +++ b/libsanitizer/interception/Makefile.am @@ -13,10 +13,10 @@ ACLOCAL_AMFLAGS = -I m4 noinst_LTLIBRARIES = libinterception.la interception_files = \ - interception_linux.cc \ - interception_mac.cc \ - interception_win.cc \ - interception_type_test.cc + interception_linux.cpp \ + interception_mac.cpp \ + interception_win.cpp \ + interception_type_test.cpp libinterception_la_SOURCES = $(interception_files) diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in index 08a33e0e567..5f7f0685a77 100644 --- a/libsanitizer/interception/Makefile.in +++ b/libsanitizer/interception/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15.1 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2017 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -134,7 +134,11 @@ am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/../depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/interception_linux.Plo \ + ./$(DEPDIR)/interception_mac.Plo \ + ./$(DEPDIR)/interception_type_test.Plo \ + ./$(DEPDIR)/interception_win.Plo am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) @@ -340,10 +344,10 @@ AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \ ACLOCAL_AMFLAGS = -I m4 noinst_LTLIBRARIES = libinterception.la interception_files = \ - interception_linux.cc \ - interception_mac.cc \ - interception_win.cc \ - interception_type_test.cc + interception_linux.cpp \ + interception_mac.cpp \ + interception_win.cpp \ + interception_type_test.cpp libinterception_la_SOURCES = $(interception_files) @@ -389,7 +393,7 @@ MAKEOVERRIDES = all: all-am .SUFFIXES: -.SUFFIXES: .cc .lo .o .obj +.SUFFIXES: .cpp .lo .o .obj $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -407,8 +411,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -440,26 +444,32 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_linux.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_mac.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_type_test.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_win.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_mac.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_type_test.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_win.Plo@am__quote@ # am--include-marker -.cc.o: +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< -.cc.obj: +.cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` -.cc.lo: +.cpp.lo: @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @@ -563,7 +573,10 @@ clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/interception_linux.Plo + -rm -f ./$(DEPDIR)/interception_mac.Plo + -rm -f ./$(DEPDIR)/interception_type_test.Plo + -rm -f ./$(DEPDIR)/interception_win.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -609,7 +622,10 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/interception_linux.Plo + -rm -f ./$(DEPDIR)/interception_mac.Plo + -rm -f ./$(DEPDIR)/interception_type_test.Plo + -rm -f ./$(DEPDIR)/interception_win.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -630,19 +646,19 @@ uninstall-am: .MAKE: install-am install-strip -.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ - clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ - ctags-am distclean distclean-compile distclean-generic \ - distclean-libtool distclean-tags dvi dvi-am html html-am info \ - info-am install install-am install-data install-data-am \ - install-dvi install-dvi-am install-exec install-exec-am \ - install-html install-html-am install-info install-info-am \ - install-man install-pdf install-pdf-am install-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ - uninstall-am +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags dvi dvi-am \ + html html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am .PRECIOUS: Makefile diff --git a/libsanitizer/interception/interception.h b/libsanitizer/interception/interception.h index 3d43df804f3..dacfa5ede28 100644 --- a/libsanitizer/interception/interception.h +++ b/libsanitizer/interception/interception.h @@ -1,7 +1,8 @@ //===-- interception.h ------------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -184,11 +185,17 @@ const interpose_substitution substitution_##func_name[] \ #endif // SANITIZER_MAC #if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS -#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \ +# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \ DECLARE_REAL(ret_type, func, __VA_ARGS__) \ extern "C" ret_type WRAP(func)(__VA_ARGS__); +// Declare an interceptor and its wrapper defined in a different translation +// unit (ex. asm). +# define DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(ret_type, func, ...) \ + extern "C" ret_type WRAP(func)(__VA_ARGS__); \ + extern "C" ret_type func(__VA_ARGS__); #else -#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) +# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) +# define DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(ret_type, func, ...) #endif // Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR diff --git a/libsanitizer/interception/interception_linux.cc b/libsanitizer/interception/interception_linux.cc deleted file mode 100644 index 781b77e46fd..00000000000 --- a/libsanitizer/interception/interception_linux.cc +++ /dev/null @@ -1,53 +0,0 @@ -//===-- interception_linux.cc -----------------------------------*- C++ -*-===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Linux-specific interception methods. -//===----------------------------------------------------------------------===// - -#include "interception.h" - -#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \ - SANITIZER_OPENBSD || SANITIZER_SOLARIS - -#include // for dlsym() and dlvsym() - -#if SANITIZER_NETBSD -#include "sanitizer_common/sanitizer_libc.h" -#endif - -namespace __interception { -bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, - uptr real, uptr wrapper) { -#if SANITIZER_NETBSD - // XXX: Find a better way to handle renames - if (internal_strcmp(func_name, "sigaction") == 0) func_name = "__sigaction14"; -#endif - *func_addr = (uptr)dlsym(RTLD_NEXT, func_name); - if (!*func_addr) { - // If the lookup using RTLD_NEXT failed, the sanitizer runtime library is - // later in the library search order than the DSO that we are trying to - // intercept, which means that we cannot intercept this function. We still - // want the address of the real definition, though, so look it up using - // RTLD_DEFAULT. - *func_addr = (uptr)dlsym(RTLD_DEFAULT, func_name); - } - return real == wrapper; -} - -// Android and Solaris do not have dlvsym -#if !SANITIZER_ANDROID && !SANITIZER_SOLARIS && !SANITIZER_OPENBSD -void *GetFuncAddrVer(const char *func_name, const char *ver) { - return dlvsym(RTLD_NEXT, func_name, ver); -} -#endif // !SANITIZER_ANDROID - -} // namespace __interception - -#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || - // SANITIZER_OPENBSD || SANITIZER_SOLARIS diff --git a/libsanitizer/interception/interception_linux.cpp b/libsanitizer/interception/interception_linux.cpp new file mode 100644 index 00000000000..950cd512653 --- /dev/null +++ b/libsanitizer/interception/interception_linux.cpp @@ -0,0 +1,83 @@ +//===-- interception_linux.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Linux-specific interception methods. +//===----------------------------------------------------------------------===// + +#include "interception.h" + +#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \ + SANITIZER_OPENBSD || SANITIZER_SOLARIS + +#include // for dlsym() and dlvsym() + +namespace __interception { + +#if SANITIZER_NETBSD +static int StrCmp(const char *s1, const char *s2) { + while (true) { + if (*s1 != *s2) + return false; + if (*s1 == 0) + return true; + s1++; + s2++; + } +} +#endif + +static void *GetFuncAddr(const char *name, uptr wrapper_addr) { +#if SANITIZER_NETBSD + // FIXME: Find a better way to handle renames + if (StrCmp(name, "sigaction")) + name = "__sigaction14"; +#endif + void *addr = dlsym(RTLD_NEXT, name); + if (!addr) { + // If the lookup using RTLD_NEXT failed, the sanitizer runtime library is + // later in the library search order than the DSO that we are trying to + // intercept, which means that we cannot intercept this function. We still + // want the address of the real definition, though, so look it up using + // RTLD_DEFAULT. + addr = dlsym(RTLD_DEFAULT, name); + + // In case `name' is not loaded, dlsym ends up finding the actual wrapper. + // We don't want to intercept the wrapper and have it point to itself. + if ((uptr)addr == wrapper_addr) + addr = nullptr; + } + return addr; +} + +bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func, + uptr wrapper) { + void *addr = GetFuncAddr(name, wrapper); + *ptr_to_real = (uptr)addr; + return addr && (func == wrapper); +} + +// Android and Solaris do not have dlvsym +#if !SANITIZER_ANDROID && !SANITIZER_SOLARIS && !SANITIZER_OPENBSD +static void *GetFuncAddr(const char *name, const char *ver) { + return dlvsym(RTLD_NEXT, name, ver); +} + +bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real, + uptr func, uptr wrapper) { + void *addr = GetFuncAddr(name, ver); + *ptr_to_real = (uptr)addr; + return addr && (func == wrapper); +} +#endif // !SANITIZER_ANDROID + +} // namespace __interception + +#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || + // SANITIZER_OPENBSD || SANITIZER_SOLARIS diff --git a/libsanitizer/interception/interception_linux.h b/libsanitizer/interception/interception_linux.h index 37e6386df5b..e578da0cf64 100644 --- a/libsanitizer/interception/interception_linux.h +++ b/libsanitizer/interception/interception_linux.h @@ -1,7 +1,8 @@ //===-- interception_linux.h ------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -21,23 +22,27 @@ #define INTERCEPTION_LINUX_H namespace __interception { -// returns true if a function with the given name was found. -bool GetRealFunctionAddress(const char *func_name, uptr *func_addr, - uptr real, uptr wrapper); -void *GetFuncAddrVer(const char *func_name, const char *ver); +bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func, + uptr wrapper); +bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real, + uptr func, uptr wrapper); } // namespace __interception -#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \ - ::__interception::GetRealFunctionAddress( \ - #func, (::__interception::uptr *)&__interception::PTR_TO_REAL(func), \ - (::__interception::uptr) & (func), \ +#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \ + ::__interception::InterceptFunction( \ + #func, \ + (::__interception::uptr *) & REAL(func), \ + (::__interception::uptr) & (func), \ (::__interception::uptr) & WRAP(func)) // Android, Solaris and OpenBSD do not have dlvsym #if !SANITIZER_ANDROID && !SANITIZER_SOLARIS && !SANITIZER_OPENBSD #define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ - (::__interception::real_##func = (func##_type)( \ - unsigned long)::__interception::GetFuncAddrVer(#func, symver)) + ::__interception::InterceptFunction( \ + #func, symver, \ + (::__interception::uptr *) & REAL(func), \ + (::__interception::uptr) & (func), \ + (::__interception::uptr) & WRAP(func)) #else #define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) diff --git a/libsanitizer/interception/interception_mac.cc b/libsanitizer/interception/interception_mac.cc deleted file mode 100644 index 1ffc1af9d55..00000000000 --- a/libsanitizer/interception/interception_mac.cc +++ /dev/null @@ -1,17 +0,0 @@ -//===-- interception_mac.cc -------------------------------------*- C++ -*-===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Mac-specific interception methods. -//===----------------------------------------------------------------------===// - -#include "interception.h" - -#if SANITIZER_MAC - -#endif // SANITIZER_MAC diff --git a/libsanitizer/interception/interception_mac.cpp b/libsanitizer/interception/interception_mac.cpp new file mode 100644 index 00000000000..fb6eadcff59 --- /dev/null +++ b/libsanitizer/interception/interception_mac.cpp @@ -0,0 +1,18 @@ +//===-- interception_mac.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Mac-specific interception methods. +//===----------------------------------------------------------------------===// + +#include "interception.h" + +#if SANITIZER_MAC + +#endif // SANITIZER_MAC diff --git a/libsanitizer/interception/interception_mac.h b/libsanitizer/interception/interception_mac.h index c3a3eace53d..eddedb8959c 100644 --- a/libsanitizer/interception/interception_mac.h +++ b/libsanitizer/interception/interception_mac.h @@ -1,7 +1,8 @@ //===-- interception_mac.h --------------------------------------*- C++ -*-===// // -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // diff --git a/libsanitizer/interception/interception_type_test.cc b/libsanitizer/interception/interception_type_test.cc deleted file mode 100644 index 726cc7b71b2..00000000000 --- a/libsanitizer/interception/interception_type_test.cc +++ /dev/null @@ -1,38 +0,0 @@ -//===-- interception_type_test.cc -------------------------------*- C++ -*-===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Compile-time tests of the internal type definitions. -//===----------------------------------------------------------------------===// - -#include "interception.h" - -#if SANITIZER_LINUX || SANITIZER_MAC - -#include -#include -#include - -COMPILER_CHECK(sizeof(::SIZE_T) == sizeof(size_t)); -COMPILER_CHECK(sizeof(::SSIZE_T) == sizeof(ssize_t)); -COMPILER_CHECK(sizeof(::PTRDIFF_T) == sizeof(ptrdiff_t)); -COMPILER_CHECK(sizeof(::INTMAX_T) == sizeof(intmax_t)); - -#if !SANITIZER_MAC -COMPILER_CHECK(sizeof(::OFF64_T) == sizeof(off64_t)); -#endif - -// The following are the cases when pread (and friends) is used instead of -// pread64. In those cases we need OFF_T to match off_t. We don't care about the -// rest (they depend on _FILE_OFFSET_BITS setting when building an application). -# if SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \ - _FILE_OFFSET_BITS != 64 -COMPILER_CHECK(sizeof(::OFF_T) == sizeof(off_t)); -# endif - -#endif diff --git a/libsanitizer/interception/interception_type_test.cpp b/libsanitizer/interception/interception_type_test.cpp new file mode 100644 index 00000000000..a611604a700 --- /dev/null +++ b/libsanitizer/interception/interception_type_test.cpp @@ -0,0 +1,39 @@ +//===-- interception_type_test.cpp ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Compile-time tests of the internal type definitions. +//===----------------------------------------------------------------------===// + +#include "interception.h" + +#if SANITIZER_LINUX || SANITIZER_MAC + +#include +#include +#include + +COMPILER_CHECK(sizeof(::SIZE_T) == sizeof(size_t)); +COMPILER_CHECK(sizeof(::SSIZE_T) == sizeof(ssize_t)); +COMPILER_CHECK(sizeof(::PTRDIFF_T) == sizeof(ptrdiff_t)); +COMPILER_CHECK(sizeof(::INTMAX_T) == sizeof(intmax_t)); + +#if !SANITIZER_MAC +COMPILER_CHECK(sizeof(::OFF64_T) == sizeof(off64_t)); +#endif + +// The following are the cases when pread (and friends) is used instead of +// pread64. In those cases we need OFF_T to match off_t. We don't care about the +// rest (they depend on _FILE_OFFSET_BITS setting when building an application). +# if SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \ + _FILE_OFFSET_BITS != 64 +COMPILER_CHECK(sizeof(::OFF_T) == sizeof(off_t)); +# endif + +#endif diff --git a/libsanitizer/interception/interception_win.cc b/libsanitizer/interception/interception_win.cc deleted file mode 100644 index 74f444d8f4a..00000000000 --- a/libsanitizer/interception/interception_win.cc +++ /dev/null @@ -1,1015 +0,0 @@ -//===-- interception_linux.cc -----------------------------------*- C++ -*-===// -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Windows-specific interception methods. -// -// This file is implementing several hooking techniques to intercept calls -// to functions. The hooks are dynamically installed by modifying the assembly -// code. -// -// The hooking techniques are making assumptions on the way the code is -// generated and are safe under these assumptions. -// -// On 64-bit architecture, there is no direct 64-bit jump instruction. To allow -// arbitrary branching on the whole memory space, the notion of trampoline -// region is used. A trampoline region is a memory space withing 2G boundary -// where it is safe to add custom assembly code to build 64-bit jumps. -// -// Hooking techniques -// ================== -// -// 1) Detour -// -// The Detour hooking technique is assuming the presence of an header with -// padding and an overridable 2-bytes nop instruction (mov edi, edi). The -// nop instruction can safely be replaced by a 2-bytes jump without any need -// to save the instruction. A jump to the target is encoded in the function -// header and the nop instruction is replaced by a short jump to the header. -// -// head: 5 x nop head: jmp -// func: mov edi, edi --> func: jmp short -// [...] real: [...] -// -// This technique is only implemented on 32-bit architecture. -// Most of the time, Windows API are hookable with the detour technique. -// -// 2) Redirect Jump -// -// The redirect jump is applicable when the first instruction is a direct -// jump. The instruction is replaced by jump to the hook. -// -// func: jmp