diff options
author | Wei Mi <wmi@google.com> | 2012-11-12 15:53:47 +0000 |
---|---|---|
committer | Dodji Seketeli <dodji@gcc.gnu.org> | 2012-11-12 16:53:47 +0100 |
commit | f35db108b96cac4fd3f2b62024ed93ac006ff932 (patch) | |
tree | 78af479c74cf29780202765e911b12f4bf192001 /libsanitizer/asan | |
parent | 25ae50273ad2801ecc262ba75fa8bac0c4e0001b (diff) | |
download | gcc-f35db108b96cac4fd3f2b62024ed93ac006ff932.zip gcc-f35db108b96cac4fd3f2b62024ed93ac006ff932.tar.gz gcc-f35db108b96cac4fd3f2b62024ed93ac006ff932.tar.bz2 |
Import the asan runtime library into GCC tree
This patch imports the runtime library in the GCC tree, ensures that
-lasan is passed to the linker when -faddress-sanitizer is used and
sets up the build system accordingly.
ChangeLog:
* configure.ac: Add libsanitizer to target_libraries.
* Makefile.def: Ditto.
* configure: Regenerate.
* Makefile.in: Regenerate.
* libsanitizer: New directory for asan runtime. Contains an empty
tsan directory.
gcc/ChangeLog:
* gcc.c (LINK_COMMAND_SPEC): Add -laddress-sanitizer to link
command if -faddress-sanitizer is on.
libsanitizer:
Initial checkin: migrate asan runtime from llvm.
From-SVN: r193441
Diffstat (limited to 'libsanitizer/asan')
34 files changed, 7197 insertions, 0 deletions
diff --git a/libsanitizer/asan/Makefile.am b/libsanitizer/asan/Makefile.am new file mode 100644 index 0000000..c76c158 --- /dev/null +++ b/libsanitizer/asan/Makefile.am @@ -0,0 +1,76 @@ +AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir) + +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DASAN_HAS_EXCEPTIONS=1 -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 -DASAN_NEEDS_SEGV=1 +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -Wno-c99-extensions +ACLOCAL_AMFLAGS = -I m4 + +toolexeclib_LTLIBRARIES = libasan.la + +asan_files = \ + asan_allocator.cc \ + asan_interceptors.cc \ + asan_mac.cc \ + asan_malloc_mac.cc \ + asan_new_delete.cc \ + asan_posix.cc \ + asan_rtl.cc \ + asan_stats.cc \ + asan_thread_registry.cc \ + asan_globals.cc \ + asan_linux.cc \ + asan_malloc_linux.cc \ + asan_malloc_win.cc \ + asan_poisoning.cc \ + asan_report.cc \ + asan_stack.cc \ + asan_thread.cc \ + asan_win.cc + +libasan_la_SOURCES = $(asan_files) +libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(top_builddir)/../libstdc++-v3/src/libstdc++.la +libasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` -lpthread -ldl + +# Work around what appears to be a GNU make bug handling MAKEFLAGS +# values defined in terms of make variables, as is the case for CC and +# friends when we are called from the top level Makefile. +AM_MAKEFLAGS = \ + "AR_FLAGS=$(AR_FLAGS)" \ + "CC_FOR_BUILD=$(CC_FOR_BUILD)" \ + "CFLAGS=$(CFLAGS)" \ + "CXXFLAGS=$(CXXFLAGS)" \ + "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \ + "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \ + "INSTALL=$(INSTALL)" \ + "INSTALL_DATA=$(INSTALL_DATA)" \ + "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \ + "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \ + "JC1FLAGS=$(JC1FLAGS)" \ + "LDFLAGS=$(LDFLAGS)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \ + "MAKE=$(MAKE)" \ + "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \ + "PICFLAG=$(PICFLAG)" \ + "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \ + "SHELL=$(SHELL)" \ + "RUNTESTFLAGS=$(RUNTESTFLAGS)" \ + "exec_prefix=$(exec_prefix)" \ + "infodir=$(infodir)" \ + "libdir=$(libdir)" \ + "prefix=$(prefix)" \ + "includedir=$(includedir)" \ + "AR=$(AR)" \ + "AS=$(AS)" \ + "CC=$(CC)" \ + "CXX=$(CXX)" \ + "LD=$(LD)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "NM=$(NM)" \ + "PICFLAG=$(PICFLAG)" \ + "RANLIB=$(RANLIB)" \ + "DESTDIR=$(DESTDIR)" + +MAKEOVERRIDES= + +## ################################################################ + diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in new file mode 100644 index 0000000..e84dfb4 --- /dev/null +++ b/libsanitizer/asan/Makefile.in @@ -0,0 +1,631 @@ +# Makefile.in generated by automake 1.11.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = asan +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(toolexeclibdir)" +LTLIBRARIES = $(toolexeclib_LTLIBRARIES) +libasan_la_DEPENDENCIES = \ + $(top_builddir)/sanitizer_common/libsanitizer_common.la \ + $(top_builddir)/interception/libinterception.la \ + $(top_builddir)/../libstdc++-v3/src/libstdc++.la +am__objects_1 = asan_allocator.lo asan_interceptors.lo asan_mac.lo \ + asan_malloc_mac.lo asan_new_delete.lo asan_posix.lo \ + asan_rtl.lo asan_stats.lo asan_thread_registry.lo \ + asan_globals.lo asan_linux.lo asan_malloc_linux.lo \ + asan_malloc_win.lo asan_poisoning.lo asan_report.lo \ + asan_stack.lo asan_thread.lo asan_win.lo +am_libasan_la_OBJECTS = $(am__objects_1) +libasan_la_OBJECTS = $(am_libasan_la_OBJECTS) +libasan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(libasan_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libasan_la_SOURCES) +DIST_SOURCES = $(libasan_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DASAN_HAS_EXCEPTIONS=1 -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=0 -DASAN_NEEDS_SEGV=1 +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +enable_shared = @enable_shared@ +enable_static = @enable_static@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +toolexecdir = @toolexecdir@ +toolexeclibdir = @toolexeclibdir@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -I $(top_srcdir)/include -I $(top_srcdir) +AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -Wno-c99-extensions +ACLOCAL_AMFLAGS = -I m4 +toolexeclib_LTLIBRARIES = libasan.la +asan_files = \ + asan_allocator.cc \ + asan_interceptors.cc \ + asan_mac.cc \ + asan_malloc_mac.cc \ + asan_new_delete.cc \ + asan_posix.cc \ + asan_rtl.cc \ + asan_stats.cc \ + asan_thread_registry.cc \ + asan_globals.cc \ + asan_linux.cc \ + asan_malloc_linux.cc \ + asan_malloc_win.cc \ + asan_poisoning.cc \ + asan_report.cc \ + asan_stack.cc \ + asan_thread.cc \ + asan_win.cc + +libasan_la_SOURCES = $(asan_files) +libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(top_builddir)/../libstdc++-v3/src/libstdc++.la +libasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` -lpthread -ldl + +# Work around what appears to be a GNU make bug handling MAKEFLAGS +# values defined in terms of make variables, as is the case for CC and +# friends when we are called from the top level Makefile. +AM_MAKEFLAGS = \ + "AR_FLAGS=$(AR_FLAGS)" \ + "CC_FOR_BUILD=$(CC_FOR_BUILD)" \ + "CFLAGS=$(CFLAGS)" \ + "CXXFLAGS=$(CXXFLAGS)" \ + "CFLAGS_FOR_BUILD=$(CFLAGS_FOR_BUILD)" \ + "CFLAGS_FOR_TARGET=$(CFLAGS_FOR_TARGET)" \ + "INSTALL=$(INSTALL)" \ + "INSTALL_DATA=$(INSTALL_DATA)" \ + "INSTALL_PROGRAM=$(INSTALL_PROGRAM)" \ + "INSTALL_SCRIPT=$(INSTALL_SCRIPT)" \ + "JC1FLAGS=$(JC1FLAGS)" \ + "LDFLAGS=$(LDFLAGS)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "LIBCFLAGS_FOR_TARGET=$(LIBCFLAGS_FOR_TARGET)" \ + "MAKE=$(MAKE)" \ + "MAKEINFO=$(MAKEINFO) $(MAKEINFOFLAGS)" \ + "PICFLAG=$(PICFLAG)" \ + "PICFLAG_FOR_TARGET=$(PICFLAG_FOR_TARGET)" \ + "SHELL=$(SHELL)" \ + "RUNTESTFLAGS=$(RUNTESTFLAGS)" \ + "exec_prefix=$(exec_prefix)" \ + "infodir=$(infodir)" \ + "libdir=$(libdir)" \ + "prefix=$(prefix)" \ + "includedir=$(includedir)" \ + "AR=$(AR)" \ + "AS=$(AS)" \ + "CC=$(CC)" \ + "CXX=$(CXX)" \ + "LD=$(LD)" \ + "LIBCFLAGS=$(LIBCFLAGS)" \ + "NM=$(NM)" \ + "PICFLAG=$(PICFLAG)" \ + "RANLIB=$(RANLIB)" \ + "DESTDIR=$(DESTDIR)" + +MAKEOVERRIDES = +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign asan/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign asan/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-toolexeclibLTLIBRARIES: $(toolexeclib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(toolexeclibdir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibdir)" + @list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(toolexeclibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(toolexeclibdir)"; \ + } + +uninstall-toolexeclibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(toolexeclib_LTLIBRARIES)'; test -n "$(toolexeclibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(toolexeclibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(toolexeclibdir)/$$f"; \ + done + +clean-toolexeclibLTLIBRARIES: + -test -z "$(toolexeclib_LTLIBRARIES)" || rm -f $(toolexeclib_LTLIBRARIES) + @list='$(toolexeclib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libasan.la: $(libasan_la_OBJECTS) $(libasan_la_DEPENDENCIES) $(EXTRA_libasan_la_DEPENDENCIES) + $(libasan_la_LINK) -rpath $(toolexeclibdir) $(libasan_la_OBJECTS) $(libasan_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_linux.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_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_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_report.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stack.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread_registry.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(toolexeclibdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-toolexeclibLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-toolexeclibLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-toolexeclibLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir 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 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 uninstall uninstall-am uninstall-toolexeclibLTLIBRARIES + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc new file mode 100644 index 0000000..3a92802 --- /dev/null +++ b/libsanitizer/asan/asan_allocator.cc @@ -0,0 +1,1045 @@ +//===-- asan_allocator.cc -------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Implementation of ASan's memory allocator. +// Evey piece of memory (AsanChunk) allocated by the allocator +// has a left redzone of REDZONE bytes and +// a right redzone such that the end of the chunk is aligned by REDZONE +// (i.e. the right redzone is between 0 and REDZONE-1). +// The left redzone is always poisoned. +// The right redzone is poisoned on malloc, the body is poisoned on free. +// Once freed, a chunk is moved to a quarantine (fifo list). +// After quarantine, a chunk is returned to freelists. +// +// The left redzone contains ASan's internal data and the stack trace of +// the malloc call. +// Once freed, the body of the chunk contains the stack trace of the free call. +// +//===----------------------------------------------------------------------===// + +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_lock.h" +#include "asan_mapping.h" +#include "asan_stats.h" +#include "asan_report.h" +#include "asan_thread.h" +#include "asan_thread_registry.h" +#include "sanitizer/asan_interface.h" +#include "sanitizer_common/sanitizer_atomic.h" + +#if defined(_WIN32) && !defined(__clang__) +#include <intrin.h> +#endif + +namespace __asan { + +#define REDZONE ((uptr)(flags()->redzone)) +static const uptr kMinAllocSize = REDZONE * 2; +static const u64 kMaxAvailableRam = 128ULL << 30; // 128G +static const uptr kMaxThreadLocalQuarantine = 1 << 20; // 1M + +static const uptr kMinMmapSize = (ASAN_LOW_MEMORY) ? 4UL << 17 : 4UL << 20; +static const uptr kMaxSizeForThreadLocalFreeList = + (ASAN_LOW_MEMORY) ? 1 << 15 : 1 << 17; + +// Size classes less than kMallocSizeClassStep are powers of two. +// All other size classes are multiples of kMallocSizeClassStep. +static const uptr kMallocSizeClassStepLog = 26; +static const uptr kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog; + +static const uptr kMaxAllowedMallocSize = + (__WORDSIZE == 32) ? 3UL << 30 : 8UL << 30; + +static inline bool IsAligned(uptr a, uptr alignment) { + return (a & (alignment - 1)) == 0; +} + +static inline uptr Log2(uptr x) { + CHECK(IsPowerOfTwo(x)); +#if !defined(_WIN32) || defined(__clang__) + return __builtin_ctzl(x); +#elif defined(_WIN64) + unsigned long ret; // NOLINT + _BitScanForward64(&ret, x); + return ret; +#else + unsigned long ret; // NOLINT + _BitScanForward(&ret, x); + return ret; +#endif +} + +static inline uptr RoundUpToPowerOfTwo(uptr size) { + CHECK(size); + if (IsPowerOfTwo(size)) return size; + + unsigned long up; // NOLINT +#if !defined(_WIN32) || defined(__clang__) + up = __WORDSIZE - 1 - __builtin_clzl(size); +#elif defined(_WIN64) + _BitScanReverse64(&up, size); +#else + _BitScanReverse(&up, size); +#endif + CHECK(size < (1ULL << (up + 1))); + CHECK(size > (1ULL << up)); + return 1UL << (up + 1); +} + +static inline uptr SizeClassToSize(u8 size_class) { + CHECK(size_class < kNumberOfSizeClasses); + if (size_class <= kMallocSizeClassStepLog) { + return 1UL << size_class; + } else { + return (size_class - kMallocSizeClassStepLog) * kMallocSizeClassStep; + } +} + +static inline u8 SizeToSizeClass(uptr size) { + u8 res = 0; + if (size <= kMallocSizeClassStep) { + uptr rounded = RoundUpToPowerOfTwo(size); + res = Log2(rounded); + } else { + res = ((size + kMallocSizeClassStep - 1) / kMallocSizeClassStep) + + kMallocSizeClassStepLog; + } + CHECK(res < kNumberOfSizeClasses); + CHECK(size <= SizeClassToSize(res)); + return res; +} + +// Given REDZONE bytes, we need to mark first size bytes +// as addressable and the rest REDZONE-size bytes as unaddressable. +static void PoisonHeapPartialRightRedzone(uptr mem, uptr size) { + CHECK(size <= REDZONE); + CHECK(IsAligned(mem, REDZONE)); + CHECK(IsPowerOfTwo(SHADOW_GRANULARITY)); + CHECK(IsPowerOfTwo(REDZONE)); + CHECK(REDZONE >= SHADOW_GRANULARITY); + PoisonShadowPartialRightRedzone(mem, size, REDZONE, + kAsanHeapRightRedzoneMagic); +} + +static u8 *MmapNewPagesAndPoisonShadow(uptr size) { + CHECK(IsAligned(size, kPageSize)); + u8 *res = (u8*)MmapOrDie(size, __FUNCTION__); + PoisonShadow((uptr)res, size, kAsanHeapLeftRedzoneMagic); + if (flags()->debug) { + Printf("ASAN_MMAP: [%p, %p)\n", res, res + size); + } + return res; +} + +// Every chunk of memory allocated by this allocator can be in one of 3 states: +// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. +// CHUNK_ALLOCATED: the chunk is allocated and not yet freed. +// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone. +// +// The pseudo state CHUNK_MEMALIGN is used to mark that the address is not +// the beginning of a AsanChunk (in which the actual chunk resides at +// this - this->used_size). +// +// The magic numbers for the enum values are taken randomly. +enum { + CHUNK_AVAILABLE = 0x57, + CHUNK_ALLOCATED = 0x32, + CHUNK_QUARANTINE = 0x19, + CHUNK_MEMALIGN = 0xDC +}; + +struct ChunkBase { + // First 8 bytes. + uptr chunk_state : 8; + uptr alloc_tid : 24; + uptr size_class : 8; + uptr free_tid : 24; + + // Second 8 bytes. + uptr alignment_log : 8; + uptr used_size : FIRST_32_SECOND_64(32, 56); // Size requested by the user. + + // This field may overlap with the user area and thus should not + // be used while the chunk is in CHUNK_ALLOCATED state. + AsanChunk *next; + + // Typically the beginning of the user-accessible memory is 'this'+REDZONE + // and is also aligned by REDZONE. However, if the memory is allocated + // by memalign, the alignment might be higher and the user-accessible memory + // starts at the first properly aligned address after 'this'. + uptr Beg() { return RoundUpTo((uptr)this + 1, 1 << alignment_log); } + uptr Size() { return SizeClassToSize(size_class); } + u8 SizeClass() { return size_class; } +}; + +struct AsanChunk: public ChunkBase { + u32 *compressed_alloc_stack() { + return (u32*)((uptr)this + sizeof(ChunkBase)); + } + u32 *compressed_free_stack() { + return (u32*)((uptr)this + Max((uptr)REDZONE, (uptr)sizeof(ChunkBase))); + } + + // The left redzone after the ChunkBase is given to the alloc stack trace. + uptr compressed_alloc_stack_size() { + if (REDZONE < sizeof(ChunkBase)) return 0; + return (REDZONE - sizeof(ChunkBase)) / sizeof(u32); + } + uptr compressed_free_stack_size() { + if (REDZONE < sizeof(ChunkBase)) return 0; + return (REDZONE) / sizeof(u32); + } +}; + +uptr AsanChunkView::Beg() { return chunk_->Beg(); } +uptr AsanChunkView::End() { return Beg() + UsedSize(); } +uptr AsanChunkView::UsedSize() { return chunk_->used_size; } +uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; } +uptr AsanChunkView::FreeTid() { return chunk_->free_tid; } + +void AsanChunkView::GetAllocStack(StackTrace *stack) { + StackTrace::UncompressStack(stack, chunk_->compressed_alloc_stack(), + chunk_->compressed_alloc_stack_size()); +} + +void AsanChunkView::GetFreeStack(StackTrace *stack) { + StackTrace::UncompressStack(stack, chunk_->compressed_free_stack(), + chunk_->compressed_free_stack_size()); +} + +bool AsanChunkView::AddrIsInside(uptr addr, uptr access_size, uptr *offset) { + if (addr >= Beg() && (addr + access_size) <= End()) { + *offset = addr - Beg(); + return true; + } + return false; +} + +bool AsanChunkView::AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) { + if (addr < Beg()) { + *offset = Beg() - addr; + return true; + } + return false; +} + +bool AsanChunkView::AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) { + if (addr + access_size >= End()) { + if (addr <= End()) + *offset = 0; + else + *offset = addr - End(); + return true; + } + return false; +} + +static AsanChunk *PtrToChunk(uptr ptr) { + AsanChunk *m = (AsanChunk*)(ptr - REDZONE); + if (m->chunk_state == CHUNK_MEMALIGN) { + m = (AsanChunk*)((uptr)m - m->used_size); + } + return m; +} + +void AsanChunkFifoList::PushList(AsanChunkFifoList *q) { + CHECK(q->size() > 0); + if (last_) { + CHECK(first_); + CHECK(!last_->next); + last_->next = q->first_; + last_ = q->last_; + } else { + CHECK(!first_); + last_ = q->last_; + first_ = q->first_; + CHECK(first_); + } + CHECK(last_); + CHECK(!last_->next); + size_ += q->size(); + q->clear(); +} + +void AsanChunkFifoList::Push(AsanChunk *n) { + CHECK(n->next == 0); + if (last_) { + CHECK(first_); + CHECK(!last_->next); + last_->next = n; + last_ = n; + } else { + CHECK(!first_); + last_ = first_ = n; + } + size_ += n->Size(); +} + +// Interesting performance observation: this function takes up to 15% of overal +// allocator time. That's because *first_ has been evicted from cache long time +// ago. Not sure if we can or want to do anything with this. +AsanChunk *AsanChunkFifoList::Pop() { + CHECK(first_); + AsanChunk *res = first_; + first_ = first_->next; + if (first_ == 0) + last_ = 0; + CHECK(size_ >= res->Size()); + size_ -= res->Size(); + if (last_) { + CHECK(!last_->next); + } + return res; +} + +// All pages we ever allocated. +struct PageGroup { + uptr beg; + uptr end; + uptr size_of_chunk; + uptr last_chunk; + bool InRange(uptr addr) { + return addr >= beg && addr < end; + } +}; + +class MallocInfo { + public: + explicit MallocInfo(LinkerInitialized x) : mu_(x) { } + + AsanChunk *AllocateChunks(u8 size_class, uptr n_chunks) { + AsanChunk *m = 0; + AsanChunk **fl = &free_lists_[size_class]; + { + ScopedLock lock(&mu_); + for (uptr i = 0; i < n_chunks; i++) { + if (!(*fl)) { + *fl = GetNewChunks(size_class); + } + AsanChunk *t = *fl; + *fl = t->next; + t->next = m; + CHECK(t->chunk_state == CHUNK_AVAILABLE); + m = t; + } + } + return m; + } + + void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x, + bool eat_free_lists) { + CHECK(flags()->quarantine_size > 0); + ScopedLock lock(&mu_); + AsanChunkFifoList *q = &x->quarantine_; + if (q->size() > 0) { + quarantine_.PushList(q); + while (quarantine_.size() > (uptr)flags()->quarantine_size) { + QuarantinePop(); + } + } + if (eat_free_lists) { + for (uptr size_class = 0; size_class < kNumberOfSizeClasses; + size_class++) { + AsanChunk *m = x->free_lists_[size_class]; + while (m) { + AsanChunk *t = m->next; + m->next = free_lists_[size_class]; + free_lists_[size_class] = m; + m = t; + } + x->free_lists_[size_class] = 0; + } + } + } + + void BypassThreadLocalQuarantine(AsanChunk *chunk) { + ScopedLock lock(&mu_); + quarantine_.Push(chunk); + } + + AsanChunk *FindChunkByAddr(uptr addr) { + ScopedLock lock(&mu_); + return FindChunkByAddrUnlocked(addr); + } + + uptr AllocationSize(uptr ptr) { + if (!ptr) return 0; + ScopedLock lock(&mu_); + + // Make sure this is our chunk and |ptr| actually points to the beginning + // of the allocated memory. + AsanChunk *m = FindChunkByAddrUnlocked(ptr); + if (!m || m->Beg() != ptr) return 0; + + if (m->chunk_state == CHUNK_ALLOCATED) { + return m->used_size; + } else { + return 0; + } + } + + void ForceLock() { + mu_.Lock(); + } + + void ForceUnlock() { + mu_.Unlock(); + } + + void PrintStatus() { + ScopedLock lock(&mu_); + uptr malloced = 0; + + Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ", + quarantine_.size() >> 20, malloced >> 20); + for (uptr j = 1; j < kNumberOfSizeClasses; j++) { + AsanChunk *i = free_lists_[j]; + if (!i) continue; + uptr t = 0; + for (; i; i = i->next) { + t += i->Size(); + } + Printf("%zu:%zu ", j, t >> 20); + } + Printf("\n"); + } + + PageGroup *FindPageGroup(uptr addr) { + ScopedLock lock(&mu_); + return FindPageGroupUnlocked(addr); + } + + private: + PageGroup *FindPageGroupUnlocked(uptr addr) { + int n = atomic_load(&n_page_groups_, memory_order_relaxed); + // If the page groups are not sorted yet, sort them. + if (n_sorted_page_groups_ < n) { + SortArray((uptr*)page_groups_, n); + n_sorted_page_groups_ = n; + } + // Binary search over the page groups. + int beg = 0, end = n; + while (beg < end) { + int med = (beg + end) / 2; + uptr g = (uptr)page_groups_[med]; + if (addr > g) { + // 'g' points to the end of the group, so 'addr' + // may not belong to page_groups_[med] or any previous group. + beg = med + 1; + } else { + // 'addr' may belong to page_groups_[med] or a previous group. + end = med; + } + } + if (beg >= n) + return 0; + PageGroup *g = page_groups_[beg]; + CHECK(g); + if (g->InRange(addr)) + return g; + return 0; + } + + // We have an address between two chunks, and we want to report just one. + AsanChunk *ChooseChunk(uptr addr, + AsanChunk *left_chunk, AsanChunk *right_chunk) { + // Prefer an allocated chunk or a chunk from quarantine. + if (left_chunk->chunk_state == CHUNK_AVAILABLE && + right_chunk->chunk_state != CHUNK_AVAILABLE) + return right_chunk; + if (right_chunk->chunk_state == CHUNK_AVAILABLE && + left_chunk->chunk_state != CHUNK_AVAILABLE) + return left_chunk; + // Choose based on offset. + uptr l_offset = 0, r_offset = 0; + CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); + CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); + if (l_offset < r_offset) + return left_chunk; + return right_chunk; + } + + AsanChunk *FindChunkByAddrUnlocked(uptr addr) { + PageGroup *g = FindPageGroupUnlocked(addr); + if (!g) return 0; + CHECK(g->size_of_chunk); + uptr offset_from_beg = addr - g->beg; + uptr this_chunk_addr = g->beg + + (offset_from_beg / g->size_of_chunk) * g->size_of_chunk; + CHECK(g->InRange(this_chunk_addr)); + AsanChunk *m = (AsanChunk*)this_chunk_addr; + CHECK(m->chunk_state == CHUNK_ALLOCATED || + m->chunk_state == CHUNK_AVAILABLE || + m->chunk_state == CHUNK_QUARANTINE); + uptr offset = 0; + AsanChunkView m_view(m); + if (m_view.AddrIsInside(addr, 1, &offset)) + return m; + + if (m_view.AddrIsAtRight(addr, 1, &offset)) { + if (this_chunk_addr == g->last_chunk) // rightmost chunk + return m; + uptr right_chunk_addr = this_chunk_addr + g->size_of_chunk; + CHECK(g->InRange(right_chunk_addr)); + return ChooseChunk(addr, m, (AsanChunk*)right_chunk_addr); + } else { + CHECK(m_view.AddrIsAtLeft(addr, 1, &offset)); + if (this_chunk_addr == g->beg) // leftmost chunk + return m; + uptr left_chunk_addr = this_chunk_addr - g->size_of_chunk; + CHECK(g->InRange(left_chunk_addr)); + return ChooseChunk(addr, (AsanChunk*)left_chunk_addr, m); + } + } + + void QuarantinePop() { + CHECK(quarantine_.size() > 0); + AsanChunk *m = quarantine_.Pop(); + CHECK(m); + // if (F_v >= 2) Printf("MallocInfo::pop %p\n", m); + + CHECK(m->chunk_state == CHUNK_QUARANTINE); + m->chunk_state = CHUNK_AVAILABLE; + PoisonShadow((uptr)m, m->Size(), kAsanHeapLeftRedzoneMagic); + CHECK(m->alloc_tid >= 0); + CHECK(m->free_tid >= 0); + + uptr size_class = m->SizeClass(); + m->next = free_lists_[size_class]; + free_lists_[size_class] = m; + + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.real_frees++; + thread_stats.really_freed += m->used_size; + thread_stats.really_freed_redzones += m->Size() - m->used_size; + thread_stats.really_freed_by_size[m->SizeClass()]++; + } + + // Get a list of newly allocated chunks. + AsanChunk *GetNewChunks(u8 size_class) { + uptr size = SizeClassToSize(size_class); + CHECK(IsPowerOfTwo(kMinMmapSize)); + CHECK(size < kMinMmapSize || (size % kMinMmapSize) == 0); + uptr mmap_size = Max(size, kMinMmapSize); + uptr n_chunks = mmap_size / size; + CHECK(n_chunks * size == mmap_size); + if (size < kPageSize) { + // Size is small, just poison the last chunk. + n_chunks--; + } else { + // Size is large, allocate an extra page at right and poison it. + mmap_size += kPageSize; + } + CHECK(n_chunks > 0); + u8 *mem = MmapNewPagesAndPoisonShadow(mmap_size); + + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.mmaps++; + thread_stats.mmaped += mmap_size; + thread_stats.mmaped_by_size[size_class] += n_chunks; + + AsanChunk *res = 0; + for (uptr i = 0; i < n_chunks; i++) { + AsanChunk *m = (AsanChunk*)(mem + i * size); + m->chunk_state = CHUNK_AVAILABLE; + m->size_class = size_class; + m->next = res; + res = m; + } + PageGroup *pg = (PageGroup*)(mem + n_chunks * size); + // This memory is already poisoned, no need to poison it again. + pg->beg = (uptr)mem; + pg->end = pg->beg + mmap_size; + pg->size_of_chunk = size; + pg->last_chunk = (uptr)(mem + size * (n_chunks - 1)); + int idx = atomic_fetch_add(&n_page_groups_, 1, memory_order_relaxed); + CHECK(idx < (int)ARRAY_SIZE(page_groups_)); + page_groups_[idx] = pg; + return res; + } + + AsanChunk *free_lists_[kNumberOfSizeClasses]; + AsanChunkFifoList quarantine_; + AsanLock mu_; + + PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize]; + atomic_uint32_t n_page_groups_; + int n_sorted_page_groups_; +}; + +static MallocInfo malloc_info(LINKER_INITIALIZED); + +void AsanThreadLocalMallocStorage::CommitBack() { + malloc_info.SwallowThreadLocalMallocStorage(this, true); +} + +AsanChunkView FindHeapChunkByAddress(uptr address) { + return AsanChunkView(malloc_info.FindChunkByAddr(address)); +} + +static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack) { + __asan_init(); + CHECK(stack); + if (size == 0) { + size = 1; // TODO(kcc): do something smarter + } + CHECK(IsPowerOfTwo(alignment)); + uptr rounded_size = RoundUpTo(size, REDZONE); + uptr needed_size = rounded_size + REDZONE; + if (alignment > REDZONE) { + needed_size += alignment; + } + CHECK(IsAligned(needed_size, REDZONE)); + if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { + Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", + (void*)size); + return 0; + } + + u8 size_class = SizeToSizeClass(needed_size); + uptr size_to_allocate = SizeClassToSize(size_class); + CHECK(size_to_allocate >= kMinAllocSize); + CHECK(size_to_allocate >= needed_size); + CHECK(IsAligned(size_to_allocate, REDZONE)); + + if (flags()->verbosity >= 3) { + Printf("Allocate align: %zu size: %zu class: %u real: %zu\n", + alignment, size, size_class, size_to_allocate); + } + + AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + // Statistics + thread_stats.mallocs++; + thread_stats.malloced += size; + thread_stats.malloced_redzones += size_to_allocate - size; + thread_stats.malloced_by_size[size_class]++; + + AsanChunk *m = 0; + if (!t || size_to_allocate >= kMaxSizeForThreadLocalFreeList) { + // get directly from global storage. + m = malloc_info.AllocateChunks(size_class, 1); + thread_stats.malloc_large++; + } else { + // get from the thread-local storage. + AsanChunk **fl = &t->malloc_storage().free_lists_[size_class]; + if (!*fl) { + uptr n_new_chunks = kMaxSizeForThreadLocalFreeList / size_to_allocate; + *fl = malloc_info.AllocateChunks(size_class, n_new_chunks); + thread_stats.malloc_small_slow++; + } + m = *fl; + *fl = (*fl)->next; + } + CHECK(m); + CHECK(m->chunk_state == CHUNK_AVAILABLE); + m->chunk_state = CHUNK_ALLOCATED; + m->next = 0; + CHECK(m->Size() == size_to_allocate); + uptr addr = (uptr)m + REDZONE; + CHECK(addr <= (uptr)m->compressed_free_stack()); + + if (alignment > REDZONE && (addr & (alignment - 1))) { + addr = RoundUpTo(addr, alignment); + CHECK((addr & (alignment - 1)) == 0); + AsanChunk *p = (AsanChunk*)(addr - REDZONE); + p->chunk_state = CHUNK_MEMALIGN; + p->used_size = (uptr)p - (uptr)m; + m->alignment_log = Log2(alignment); + CHECK(m->Beg() == addr); + } else { + m->alignment_log = Log2(REDZONE); + } + CHECK(m == PtrToChunk(addr)); + m->used_size = size; + CHECK(m->Beg() == addr); + m->alloc_tid = t ? t->tid() : 0; + m->free_tid = kInvalidTid; + StackTrace::CompressStack(stack, m->compressed_alloc_stack(), + m->compressed_alloc_stack_size()); + PoisonShadow(addr, rounded_size, 0); + if (size < rounded_size) { + PoisonHeapPartialRightRedzone(addr + rounded_size - REDZONE, + size & (REDZONE - 1)); + } + if (size <= (uptr)(flags()->max_malloc_fill_size)) { + REAL(memset)((void*)addr, 0, rounded_size); + } + return (u8*)addr; +} + +static void Deallocate(u8 *ptr, StackTrace *stack) { + if (!ptr) return; + CHECK(stack); + + if (flags()->debug) { + CHECK(malloc_info.FindPageGroup((uptr)ptr)); + } + + // Printf("Deallocate %p\n", ptr); + AsanChunk *m = PtrToChunk((uptr)ptr); + + // Flip the chunk_state atomically to avoid race on double-free. + u8 old_chunk_state = atomic_exchange((atomic_uint8_t*)m, CHUNK_QUARANTINE, + memory_order_acq_rel); + + if (old_chunk_state == CHUNK_QUARANTINE) { + ReportDoubleFree((uptr)ptr, stack); + } else if (old_chunk_state != CHUNK_ALLOCATED) { + ReportFreeNotMalloced((uptr)ptr, stack); + } + CHECK(old_chunk_state == CHUNK_ALLOCATED); + // With REDZONE==16 m->next is in the user area, otherwise it should be 0. + CHECK(REDZONE <= 16 || !m->next); + CHECK(m->free_tid == kInvalidTid); + CHECK(m->alloc_tid >= 0); + AsanThread *t = asanThreadRegistry().GetCurrent(); + m->free_tid = t ? t->tid() : 0; + StackTrace::CompressStack(stack, m->compressed_free_stack(), + m->compressed_free_stack_size()); + uptr rounded_size = RoundUpTo(m->used_size, REDZONE); + PoisonShadow((uptr)ptr, rounded_size, kAsanHeapFreeMagic); + + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.frees++; + thread_stats.freed += m->used_size; + thread_stats.freed_by_size[m->SizeClass()]++; + + CHECK(m->chunk_state == CHUNK_QUARANTINE); + + if (t) { + AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); + ms->quarantine_.Push(m); + + if (ms->quarantine_.size() > kMaxThreadLocalQuarantine) { + malloc_info.SwallowThreadLocalMallocStorage(ms, false); + } + } else { + malloc_info.BypassThreadLocalQuarantine(m); + } +} + +static u8 *Reallocate(u8 *old_ptr, uptr new_size, + StackTrace *stack) { + CHECK(old_ptr && new_size); + + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.reallocs++; + thread_stats.realloced += new_size; + + AsanChunk *m = PtrToChunk((uptr)old_ptr); + CHECK(m->chunk_state == CHUNK_ALLOCATED); + uptr old_size = m->used_size; + uptr memcpy_size = Min(new_size, old_size); + u8 *new_ptr = Allocate(0, new_size, stack); + if (new_ptr) { + CHECK(REAL(memcpy) != 0); + REAL(memcpy)(new_ptr, old_ptr, memcpy_size); + Deallocate(old_ptr, stack); + } + return new_ptr; +} + +} // namespace __asan + +// Default (no-op) implementation of malloc hooks. +extern "C" { +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +void __asan_malloc_hook(void *ptr, uptr size) { + (void)ptr; + (void)size; +} +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +void __asan_free_hook(void *ptr) { + (void)ptr; +} +} // extern "C" + +namespace __asan { + +SANITIZER_INTERFACE_ATTRIBUTE +void *asan_memalign(uptr alignment, uptr size, StackTrace *stack) { + void *ptr = (void*)Allocate(alignment, size, stack); + __asan_malloc_hook(ptr, size); + return ptr; +} + +SANITIZER_INTERFACE_ATTRIBUTE +void asan_free(void *ptr, StackTrace *stack) { + __asan_free_hook(ptr); + Deallocate((u8*)ptr, stack); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *asan_malloc(uptr size, StackTrace *stack) { + void *ptr = (void*)Allocate(0, size, stack); + __asan_malloc_hook(ptr, size); + return ptr; +} + +void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { + void *ptr = (void*)Allocate(0, nmemb * size, stack); + if (ptr) + REAL(memset)(ptr, 0, nmemb * size); + __asan_malloc_hook(ptr, nmemb * size); + return ptr; +} + +void *asan_realloc(void *p, uptr size, StackTrace *stack) { + if (p == 0) { + void *ptr = (void*)Allocate(0, size, stack); + __asan_malloc_hook(ptr, size); + return ptr; + } else if (size == 0) { + __asan_free_hook(p); + Deallocate((u8*)p, stack); + return 0; + } + return Reallocate((u8*)p, size, stack); +} + +void *asan_valloc(uptr size, StackTrace *stack) { + void *ptr = (void*)Allocate(kPageSize, size, stack); + __asan_malloc_hook(ptr, size); + return ptr; +} + +void *asan_pvalloc(uptr size, StackTrace *stack) { + size = RoundUpTo(size, kPageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = kPageSize; + } + void *ptr = (void*)Allocate(kPageSize, size, stack); + __asan_malloc_hook(ptr, size); + return ptr; +} + +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, + StackTrace *stack) { + void *ptr = Allocate(alignment, size, stack); + CHECK(IsAligned((uptr)ptr, alignment)); + __asan_malloc_hook(ptr, size); + *memptr = ptr; + return 0; +} + +uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) { + CHECK(stack); + if (ptr == 0) return 0; + uptr usable_size = malloc_info.AllocationSize((uptr)ptr); + if (flags()->check_malloc_usable_size && (usable_size == 0)) { + ReportMallocUsableSizeNotOwned((uptr)ptr, stack); + } + return usable_size; +} + +uptr asan_mz_size(const void *ptr) { + return malloc_info.AllocationSize((uptr)ptr); +} + +void asan_mz_force_lock() { + malloc_info.ForceLock(); +} + +void asan_mz_force_unlock() { + malloc_info.ForceUnlock(); +} + +// ---------------------- Fake stack-------------------- {{{1 +FakeStack::FakeStack() { + CHECK(REAL(memset) != 0); + REAL(memset)(this, 0, sizeof(*this)); +} + +bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) { + uptr mem = allocated_size_classes_[size_class]; + uptr size = ClassMmapSize(size_class); + bool res = mem && addr >= mem && addr < mem + size; + return res; +} + +uptr FakeStack::AddrIsInFakeStack(uptr addr) { + for (uptr i = 0; i < kNumberOfSizeClasses; i++) { + if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i]; + } + return 0; +} + +// We may want to compute this during compilation. +inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) { + uptr rounded_size = RoundUpToPowerOfTwo(alloc_size); + uptr log = Log2(rounded_size); + CHECK(alloc_size <= (1UL << log)); + if (!(alloc_size > (1UL << (log-1)))) { + Printf("alloc_size %zu log %zu\n", alloc_size, log); + } + CHECK(alloc_size > (1UL << (log-1))); + uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog; + CHECK(res < kNumberOfSizeClasses); + CHECK(ClassSize(res) >= rounded_size); + return res; +} + +void FakeFrameFifo::FifoPush(FakeFrame *node) { + CHECK(node); + node->next = 0; + if (first_ == 0 && last_ == 0) { + first_ = last_ = node; + } else { + CHECK(first_); + CHECK(last_); + last_->next = node; + last_ = node; + } +} + +FakeFrame *FakeFrameFifo::FifoPop() { + CHECK(first_ && last_ && "Exhausted fake stack"); + FakeFrame *res = 0; + if (first_ == last_) { + res = first_; + first_ = last_ = 0; + } else { + res = first_; + first_ = first_->next; + } + return res; +} + +void FakeStack::Init(uptr stack_size) { + stack_size_ = stack_size; + alive_ = true; +} + +void FakeStack::Cleanup() { + alive_ = false; + for (uptr i = 0; i < kNumberOfSizeClasses; i++) { + uptr mem = allocated_size_classes_[i]; + if (mem) { + PoisonShadow(mem, ClassMmapSize(i), 0); + allocated_size_classes_[i] = 0; + UnmapOrDie((void*)mem, ClassMmapSize(i)); + } + } +} + +uptr FakeStack::ClassMmapSize(uptr size_class) { + return RoundUpToPowerOfTwo(stack_size_); +} + +void FakeStack::AllocateOneSizeClass(uptr size_class) { + CHECK(ClassMmapSize(size_class) >= kPageSize); + uptr new_mem = (uptr)MmapOrDie( + ClassMmapSize(size_class), __FUNCTION__); + // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n", + // asanThreadRegistry().GetCurrent()->tid(), + // size_class, new_mem, new_mem + ClassMmapSize(size_class), + // ClassMmapSize(size_class)); + uptr i; + for (i = 0; i < ClassMmapSize(size_class); + i += ClassSize(size_class)) { + size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i)); + } + CHECK(i == ClassMmapSize(size_class)); + allocated_size_classes_[size_class] = new_mem; +} + +uptr FakeStack::AllocateStack(uptr size, uptr real_stack) { + if (!alive_) return real_stack; + CHECK(size <= kMaxStackMallocSize && size > 1); + uptr size_class = ComputeSizeClass(size); + if (!allocated_size_classes_[size_class]) { + AllocateOneSizeClass(size_class); + } + FakeFrame *fake_frame = size_classes_[size_class].FifoPop(); + CHECK(fake_frame); + fake_frame->size_minus_one = size - 1; + fake_frame->real_stack = real_stack; + while (FakeFrame *top = call_stack_.top()) { + if (top->real_stack > real_stack) break; + call_stack_.LifoPop(); + DeallocateFrame(top); + } + call_stack_.LifoPush(fake_frame); + uptr ptr = (uptr)fake_frame; + PoisonShadow(ptr, size, 0); + return ptr; +} + +void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { + CHECK(alive_); + uptr size = fake_frame->size_minus_one + 1; + uptr size_class = ComputeSizeClass(size); + CHECK(allocated_size_classes_[size_class]); + uptr ptr = (uptr)fake_frame; + CHECK(AddrIsInSizeClass(ptr, size_class)); + CHECK(AddrIsInSizeClass(ptr + size - 1, size_class)); + size_classes_[size_class].FifoPush(fake_frame); +} + +void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) { + FakeFrame *fake_frame = (FakeFrame*)ptr; + CHECK(fake_frame->magic = kRetiredStackFrameMagic); + CHECK(fake_frame->descr != 0); + CHECK(fake_frame->size_minus_one == size - 1); + PoisonShadow(ptr, size, kAsanStackAfterReturnMagic); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +uptr __asan_stack_malloc(uptr size, uptr real_stack) { + if (!flags()->use_fake_stack) return real_stack; + AsanThread *t = asanThreadRegistry().GetCurrent(); + if (!t) { + // TSD is gone, use the real stack. + return real_stack; + } + uptr ptr = t->fake_stack().AllocateStack(size, real_stack); + // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack); + return ptr; +} + +void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) { + if (!flags()->use_fake_stack) return; + if (ptr != real_stack) { + FakeStack::OnFree(ptr, size, real_stack); + } +} + +// ASan allocator doesn't reserve extra bytes, so normally we would +// just return "size". +uptr __asan_get_estimated_allocated_size(uptr size) { + if (size == 0) return 1; + return Min(size, kMaxAllowedMallocSize); +} + +bool __asan_get_ownership(const void *p) { + return malloc_info.AllocationSize((uptr)p) > 0; +} + +uptr __asan_get_allocated_size(const void *p) { + if (p == 0) return 0; + uptr allocated_size = malloc_info.AllocationSize((uptr)p); + // Die if p is not malloced or if it is already freed. + if (allocated_size == 0) { + GET_STACK_TRACE_HERE(kStackTraceMax); + ReportAsanGetAllocatedSizeNotOwned((uptr)p, &stack); + } + return allocated_size; +} diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h new file mode 100644 index 0000000..372ca06 --- /dev/null +++ b/libsanitizer/asan/asan_allocator.h @@ -0,0 +1,177 @@ +//===-- asan_allocator.h ----------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_allocator.cc. +//===----------------------------------------------------------------------===// + +#ifndef ASAN_ALLOCATOR_H +#define ASAN_ALLOCATOR_H + +#include "asan_internal.h" +#include "asan_interceptors.h" + +namespace __asan { + +static const uptr kNumberOfSizeClasses = 255; +struct AsanChunk; + +class AsanChunkView { + public: + explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {} + bool IsValid() { return chunk_ != 0; } + uptr Beg(); // first byte of user memory. + uptr End(); // last byte of user memory. + uptr UsedSize(); // size requested by the user. + uptr AllocTid(); + uptr FreeTid(); + void GetAllocStack(StackTrace *stack); + void GetFreeStack(StackTrace *stack); + bool AddrIsInside(uptr addr, uptr access_size, uptr *offset); + bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset); + bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset); + private: + AsanChunk *const chunk_; +}; + +AsanChunkView FindHeapChunkByAddress(uptr address); + +class AsanChunkFifoList { + public: + explicit AsanChunkFifoList(LinkerInitialized) { } + AsanChunkFifoList() { clear(); } + void Push(AsanChunk *n); + void PushList(AsanChunkFifoList *q); + AsanChunk *Pop(); + uptr size() { return size_; } + void clear() { + first_ = last_ = 0; + size_ = 0; + } + private: + AsanChunk *first_; + AsanChunk *last_; + uptr size_; +}; + +struct AsanThreadLocalMallocStorage { + explicit AsanThreadLocalMallocStorage(LinkerInitialized x) + : quarantine_(x) { } + AsanThreadLocalMallocStorage() { + CHECK(REAL(memset)); + REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage)); + } + + AsanChunkFifoList quarantine_; + AsanChunk *free_lists_[kNumberOfSizeClasses]; + void CommitBack(); +}; + +// Fake stack frame contains local variables of one function. +// This struct should fit into a stack redzone (32 bytes). +struct FakeFrame { + uptr magic; // Modified by the instrumented code. + uptr descr; // Modified by the instrumented code. + FakeFrame *next; + u64 real_stack : 48; + u64 size_minus_one : 16; +}; + +struct FakeFrameFifo { + public: + void FifoPush(FakeFrame *node); + FakeFrame *FifoPop(); + private: + FakeFrame *first_, *last_; +}; + +class FakeFrameLifo { + public: + void LifoPush(FakeFrame *node) { + node->next = top_; + top_ = node; + } + void LifoPop() { + CHECK(top_); + top_ = top_->next; + } + FakeFrame *top() { return top_; } + private: + FakeFrame *top_; +}; + +// For each thread we create a fake stack and place stack objects on this fake +// stack instead of the real stack. The fake stack is not really a stack but +// a fast malloc-like allocator so that when a function exits the fake stack +// is not poped but remains there for quite some time until gets used again. +// So, we poison the objects on the fake stack when function returns. +// It helps us find use-after-return bugs. +// We can not rely on __asan_stack_free being called on every function exit, +// so we maintain a lifo list of all current fake frames and update it on every +// call to __asan_stack_malloc. +class FakeStack { + public: + FakeStack(); + explicit FakeStack(LinkerInitialized) {} + void Init(uptr stack_size); + void StopUsingFakeStack() { alive_ = false; } + void Cleanup(); + uptr AllocateStack(uptr size, uptr real_stack); + static void OnFree(uptr ptr, uptr size, uptr real_stack); + // Return the bottom of the maped region. + uptr AddrIsInFakeStack(uptr addr); + bool StackSize() { return stack_size_; } + + private: + static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B. + static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K. + static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; + static const uptr kNumberOfSizeClasses = + kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1; + + bool AddrIsInSizeClass(uptr addr, uptr size_class); + + // Each size class should be large enough to hold all frames. + uptr ClassMmapSize(uptr size_class); + + uptr ClassSize(uptr size_class) { + return 1UL << (size_class + kMinStackFrameSizeLog); + } + + void DeallocateFrame(FakeFrame *fake_frame); + + uptr ComputeSizeClass(uptr alloc_size); + void AllocateOneSizeClass(uptr size_class); + + uptr stack_size_; + bool alive_; + + uptr allocated_size_classes_[kNumberOfSizeClasses]; + FakeFrameFifo size_classes_[kNumberOfSizeClasses]; + FakeFrameLifo call_stack_; +}; + +void *asan_memalign(uptr alignment, uptr size, StackTrace *stack); +void asan_free(void *ptr, StackTrace *stack); + +void *asan_malloc(uptr size, StackTrace *stack); +void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack); +void *asan_realloc(void *p, uptr size, StackTrace *stack); +void *asan_valloc(uptr size, StackTrace *stack); +void *asan_pvalloc(uptr size, StackTrace *stack); + +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, + StackTrace *stack); +uptr asan_malloc_usable_size(void *ptr, StackTrace *stack); + +uptr asan_mz_size(const void *ptr); +void asan_mz_force_lock(); +void asan_mz_force_unlock(); + +} // namespace __asan +#endif // ASAN_ALLOCATOR_H diff --git a/libsanitizer/asan/asan_flags.h b/libsanitizer/asan/asan_flags.h new file mode 100644 index 0000000..a0dcf3e --- /dev/null +++ b/libsanitizer/asan/asan_flags.h @@ -0,0 +1,103 @@ +//===-- asan_flags.h -------------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan runtime flags. +//===----------------------------------------------------------------------===// + +#ifndef ASAN_FLAGS_H +#define ASAN_FLAGS_H + +#include "sanitizer/common_interface_defs.h" + +// ASan flag values can be defined in three ways: +// 1) initialized with default values at startup. +// 2) overriden from string returned by user-specified function +// __asan_default_options(). +// 3) overriden from env variable ASAN_OPTIONS. + +namespace __asan { + +struct Flags { + // Size (in bytes) of quarantine used to detect use-after-free errors. + // Lower value may reduce memory usage but increase the chance of + // false negatives. + int quarantine_size; + // If set, uses in-process symbolizer from common sanitizer runtime. + bool symbolize; + // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). + int verbosity; + // Size (in bytes) of redzones around heap objects. + // Requirement: redzone >= 32, is a power of two. + int redzone; + // If set, prints some debugging information and does additional checks. + bool debug; + // Controls the way to handle globals (0 - don't detect buffer overflow + // on globals, 1 - detect buffer overflow, 2 - print data about registered + // globals). + int report_globals; + // If set, attempts to catch initialization order issues. + bool check_initialization_order; + // Max number of stack frames kept for each allocation. + int malloc_context_size; + // If set, uses custom wrappers and replacements for libc string functions + // to find more errors. + bool replace_str; + // If set, uses custom wrappers for memset/memcpy/memmove intinsics. + bool replace_intrin; + // Used on Mac only. See comments in asan_mac.cc and asan_malloc_mac.cc. + bool replace_cfallocator; + // Used on Mac only. + bool mac_ignore_invalid_free; + // ASan allocator flag. See asan_allocator.cc. + bool use_fake_stack; + // ASan allocator flag. Sets the maximal size of allocation request + // that would return memory filled with zero bytes. + int max_malloc_fill_size; + // Override exit status if something was reported. + int exitcode; + // If set, user may manually mark memory regions as poisoned or unpoisoned. + bool allow_user_poisoning; + // Number of seconds to sleep between printing an error report and + // terminating application. Useful for debug purposes (when one needs + // to attach gdb, for example). + int sleep_before_dying; + // If set, registers ASan custom segv handler. + bool handle_segv; + // If set, uses alternate stack for signal handling. + bool use_sigaltstack; + // Allow the users to work around the bug in Nvidia drivers prior to 295.*. + bool check_malloc_usable_size; + // If set, explicitly unmaps (huge) shadow at exit. + bool unmap_shadow_on_exit; + // If set, calls abort() instead of _exit() after printing an error report. + bool abort_on_error; + // If set, prints ASan exit stats even after program terminates successfully. + bool atexit; + // By default, disable core dumper on 64-bit - it makes little sense + // to dump 16T+ core. + bool disable_core; + // Allow the tool to re-exec the program. This may interfere badly with the + // debugger. + bool allow_reexec; + // Strips this prefix from file paths in error reports. + const char *strip_path_prefix; + // If set, prints not only thread creation stacks for threads in error report, + // but also thread creation stacks for threads that created those threads, + // etc. up to main thread. + bool print_full_thread_history; + // ASan will write logs to "log_path.pid" instead of stderr. + const char *log_path; +}; + +Flags *flags(); +void InitializeFlags(Flags *f, const char *env); + +} // namespace __asan + +#endif // ASAN_FLAGS_H diff --git a/libsanitizer/asan/asan_globals.cc b/libsanitizer/asan/asan_globals.cc new file mode 100644 index 0000000..b195a90 --- /dev/null +++ b/libsanitizer/asan/asan_globals.cc @@ -0,0 +1,206 @@ +//===-- 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_lock.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "asan_thread.h" +#include "sanitizer/asan_interface.h" + +namespace __asan { + +typedef __asan_global Global; + +struct ListOfGlobals { + const Global *g; + ListOfGlobals *next; +}; + +static AsanLock mu_for_globals(LINKER_INITIALIZED); +static LowLevelAllocator allocator_for_globals; +static ListOfGlobals *list_of_all_globals; +static ListOfGlobals *list_of_dynamic_init_globals; + +void PoisonRedZones(const Global &g) { + uptr shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE; + CHECK(shadow_rz_size == 1 || shadow_rz_size == 2 || shadow_rz_size == 4); + // full right redzone + uptr g_aligned_size = kGlobalAndStackRedzone * + ((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone); + PoisonShadow(g.beg + g_aligned_size, + kGlobalAndStackRedzone, kAsanGlobalRedzoneMagic); + if ((g.size % kGlobalAndStackRedzone) != 0) { + // partial right redzone + u64 g_aligned_down_size = kGlobalAndStackRedzone * + (g.size / kGlobalAndStackRedzone); + CHECK(g_aligned_down_size == g_aligned_size - kGlobalAndStackRedzone); + PoisonShadowPartialRightRedzone(g.beg + g_aligned_down_size, + g.size % kGlobalAndStackRedzone, + kGlobalAndStackRedzone, + kAsanGlobalRedzoneMagic); + } +} + +static uptr GetAlignedSize(uptr size) { + return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone) + * kGlobalAndStackRedzone; +} + +bool DescribeAddressIfGlobal(uptr addr) { + if (!flags()->report_globals) return false; + ScopedLock lock(&mu_for_globals); + bool res = false; + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + const Global &g = *l->g; + if (flags()->report_globals >= 2) + Report("Search Global: beg=%p size=%zu name=%s\n", + (void*)g.beg, g.size, (char*)g.name); + res |= DescribeAddressRelativeToGlobal(addr, g); + } + return res; +} + +// 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) + Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n", + (void*)g->beg, g->size, g->size_with_redzone, g->name, + g->has_dynamic_init); + CHECK(flags()->report_globals); + CHECK(AddrIsInMem(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); + PoisonRedZones(*g); + ListOfGlobals *l = + (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); + l->g = g; + l->next = list_of_all_globals; + list_of_all_globals = l; + if (g->has_dynamic_init) { + l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); + l->g = g; + l->next = list_of_dynamic_init_globals; + list_of_dynamic_init_globals = l; + } +} + +static void UnregisterGlobal(const Global *g) { + CHECK(asan_inited); + CHECK(flags()->report_globals); + CHECK(AddrIsInMem(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); + PoisonShadow(g->beg, g->size_with_redzone, 0); + // We unpoison the shadow memory for the global but we do not remove it from + // the list because that would require O(n^2) time with the current list + // implementation. It might not be worth doing anyway. +} + +// Poison all shadow memory for a single global. +static void PoisonGlobalAndRedzones(const Global *g) { + CHECK(asan_inited); + CHECK(flags()->check_initialization_order); + CHECK(AddrIsInMem(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); + if (flags()->report_globals >= 3) + Printf("DynInitPoison : %s\n", g->name); + PoisonShadow(g->beg, g->size_with_redzone, kAsanInitializationOrderMagic); +} + +static void UnpoisonGlobal(const Global *g) { + CHECK(asan_inited); + CHECK(flags()->check_initialization_order); + CHECK(AddrIsInMem(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); + if (flags()->report_globals >= 3) + Printf("DynInitUnpoison: %s\n", g->name); + PoisonShadow(g->beg, g->size_with_redzone, 0); + PoisonRedZones(*g); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +// Register one global with a default redzone. +void __asan_register_global(uptr addr, uptr size, + const char *name) { + if (!flags()->report_globals) return; + ScopedLock lock(&mu_for_globals); + Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global)); + g->beg = addr; + g->size = size; + g->size_with_redzone = GetAlignedSize(size) + kGlobalAndStackRedzone; + g->name = name; + RegisterGlobal(g); +} + +// Register an array of globals. +void __asan_register_globals(__asan_global *globals, uptr n) { + if (!flags()->report_globals) return; + ScopedLock lock(&mu_for_globals); + for (uptr i = 0; i < n; i++) { + RegisterGlobal(&globals[i]); + } +} + +// 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; + ScopedLock lock(&mu_for_globals); + for (uptr i = 0; i < n; i++) { + UnregisterGlobal(&globals[i]); + } +} + +// 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(uptr first_addr, uptr last_addr) { + if (!flags()->check_initialization_order) return; + CHECK(list_of_dynamic_init_globals); + ScopedLock lock(&mu_for_globals); + bool from_current_tu = false; + // The list looks like: + // a => ... => b => last_addr => ... => first_addr => c => ... + // The globals of the current TU reside between last_addr and first_addr. + for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) { + if (l->g->beg == last_addr) + from_current_tu = true; + if (!from_current_tu) + PoisonGlobalAndRedzones(l->g); + if (l->g->beg == first_addr) + from_current_tu = false; + } + CHECK(!from_current_tu); +} + +// This method runs immediately after dynamic initialization in each TU, when +// all dynamically initialized globals except for those defined in the current +// TU are poisoned. It simply unpoisons all dynamically initialized globals. +void __asan_after_dynamic_init() { + if (!flags()->check_initialization_order) return; + ScopedLock lock(&mu_for_globals); + for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) + UnpoisonGlobal(l->g); +} diff --git a/libsanitizer/asan/asan_intercepted_functions.h b/libsanitizer/asan/asan_intercepted_functions.h new file mode 100644 index 0000000..ceb596c --- /dev/null +++ b/libsanitizer/asan/asan_intercepted_functions.h @@ -0,0 +1,217 @@ +//===-- asan_intercepted_functions.h ----------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header containing prototypes for wrapper functions and wrappers +//===----------------------------------------------------------------------===// +#ifndef ASAN_INTERCEPTED_FUNCTIONS_H +#define ASAN_INTERCEPTED_FUNCTIONS_H + +#include "asan_internal.h" +#include "interception/interception.h" + +using __sanitizer::uptr; + +// Use macro to describe if specific function should be +// intercepted on a given platform. +#if !defined(_WIN32) +# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1 +# define ASAN_INTERCEPT__LONGJMP 1 +# define ASAN_INTERCEPT_STRDUP 1 +# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 1 +# define ASAN_INTERCEPT_INDEX 1 +# define ASAN_INTERCEPT_PTHREAD_CREATE 1 +# define ASAN_INTERCEPT_MLOCKX 1 +#else +# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0 +# define ASAN_INTERCEPT__LONGJMP 0 +# define ASAN_INTERCEPT_STRDUP 0 +# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 0 +# define ASAN_INTERCEPT_INDEX 0 +# define ASAN_INTERCEPT_PTHREAD_CREATE 0 +# define ASAN_INTERCEPT_MLOCKX 0 +#endif + +#if defined(__linux__) +# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1 +#else +# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0 +#endif + +#if !defined(__APPLE__) +# define ASAN_INTERCEPT_STRNLEN 1 +#else +# define ASAN_INTERCEPT_STRNLEN 0 +#endif + +#if !defined(ANDROID) && !defined(_WIN32) +# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1 +#else +# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0 +#endif + +// On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it +// there. +#if !defined(_WIN32) && (!defined(__APPLE__) || MAC_INTERPOSE_FUNCTIONS) +# define ASAN_INTERCEPT_SIGLONGJMP 1 +#else +# define ASAN_INTERCEPT_SIGLONGJMP 0 +#endif + +#if ASAN_HAS_EXCEPTIONS && !defined(_WIN32) +# define ASAN_INTERCEPT___CXA_THROW 1 +#else +# define ASAN_INTERCEPT___CXA_THROW 0 +#endif + +#define DECLARE_FUNCTION_AND_WRAPPER(ret_type, func, ...) \ + ret_type func(__VA_ARGS__); \ + ret_type WRAP(func)(__VA_ARGS__) + +// Use extern declarations of intercepted functions on Mac and Windows +// to avoid including system headers. +#if defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL)) +extern "C" { +// signal.h +# if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION +struct sigaction; +DECLARE_FUNCTION_AND_WRAPPER(int, sigaction, int sig, + const struct sigaction *act, + struct sigaction *oldact); +DECLARE_FUNCTION_AND_WRAPPER(void*, signal, int signum, void *handler); +# endif + +// setjmp.h +DECLARE_FUNCTION_AND_WRAPPER(void, longjmp, void *env, int value); +# if ASAN_INTERCEPT__LONGJMP +DECLARE_FUNCTION_AND_WRAPPER(void, _longjmp, void *env, int value); +# endif +# if ASAN_INTERCEPT_SIGLONGJMP +DECLARE_FUNCTION_AND_WRAPPER(void, siglongjmp, void *env, int value); +# endif +# if ASAN_INTERCEPT___CXA_THROW +DECLARE_FUNCTION_AND_WRAPPER(void, __cxa_throw, void *a, void *b, void *c); +#endif + +// string.h / strings.h +DECLARE_FUNCTION_AND_WRAPPER(int, memcmp, + const void *a1, const void *a2, uptr size); +DECLARE_FUNCTION_AND_WRAPPER(void*, memmove, + void *to, const void *from, uptr size); +DECLARE_FUNCTION_AND_WRAPPER(void*, memcpy, + void *to, const void *from, uptr size); +DECLARE_FUNCTION_AND_WRAPPER(void*, memset, void *block, int c, uptr size); +DECLARE_FUNCTION_AND_WRAPPER(char*, strchr, const char *str, int c); +DECLARE_FUNCTION_AND_WRAPPER(char*, strcat, /* NOLINT */ + char *to, const char* from); +DECLARE_FUNCTION_AND_WRAPPER(char*, strncat, + char *to, const char* from, uptr size); +DECLARE_FUNCTION_AND_WRAPPER(char*, strcpy, /* NOLINT */ + char *to, const char* from); +DECLARE_FUNCTION_AND_WRAPPER(char*, strncpy, + char *to, const char* from, uptr size); +DECLARE_FUNCTION_AND_WRAPPER(int, strcmp, const char *s1, const char* s2); +DECLARE_FUNCTION_AND_WRAPPER(int, strncmp, + const char *s1, const char* s2, uptr size); +DECLARE_FUNCTION_AND_WRAPPER(uptr, strlen, const char *s); +# if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP +DECLARE_FUNCTION_AND_WRAPPER(int, strcasecmp, const char *s1, const char *s2); +DECLARE_FUNCTION_AND_WRAPPER(int, strncasecmp, + const char *s1, const char *s2, uptr n); +# endif +# if ASAN_INTERCEPT_STRDUP +DECLARE_FUNCTION_AND_WRAPPER(char*, strdup, const char *s); +# endif +# if ASAN_INTERCEPT_STRNLEN +DECLARE_FUNCTION_AND_WRAPPER(uptr, strnlen, const char *s, uptr maxlen); +# endif +#if ASAN_INTERCEPT_INDEX +DECLARE_FUNCTION_AND_WRAPPER(char*, index, const char *string, int c); +#endif + +// stdlib.h +DECLARE_FUNCTION_AND_WRAPPER(int, atoi, const char *nptr); +DECLARE_FUNCTION_AND_WRAPPER(long, atol, const char *nptr); // NOLINT +DECLARE_FUNCTION_AND_WRAPPER(long, strtol, const char *nptr, char **endptr, int base); // NOLINT +# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL +DECLARE_FUNCTION_AND_WRAPPER(long long, atoll, const char *nptr); // NOLINT +DECLARE_FUNCTION_AND_WRAPPER(long long, strtoll, const char *nptr, char **endptr, int base); // NOLINT +# endif + +# if ASAN_INTERCEPT_MLOCKX +// mlock/munlock +DECLARE_FUNCTION_AND_WRAPPER(int, mlock, const void *addr, size_t len); +DECLARE_FUNCTION_AND_WRAPPER(int, munlock, const void *addr, size_t len); +DECLARE_FUNCTION_AND_WRAPPER(int, mlockall, int flags); +DECLARE_FUNCTION_AND_WRAPPER(int, munlockall, void); +# endif + +// Windows threads. +# if defined(_WIN32) +__declspec(dllimport) +void* __stdcall CreateThread(void *sec, uptr st, void* start, + void *arg, DWORD fl, DWORD *id); +# endif +// Posix threads. +# if ASAN_INTERCEPT_PTHREAD_CREATE +DECLARE_FUNCTION_AND_WRAPPER(int, pthread_create, + void *thread, void *attr, + void *(*start_routine)(void*), void *arg); +# endif + +#if defined(__APPLE__) +typedef void* pthread_workqueue_t; +typedef void* pthread_workitem_handle_t; + +typedef void* dispatch_group_t; +typedef void* dispatch_queue_t; +typedef void* dispatch_source_t; +typedef u64 dispatch_time_t; +typedef void (*dispatch_function_t)(void *block); +typedef void* (*worker_t)(void *block); +typedef void* CFStringRef; +typedef void* CFAllocatorRef; + +DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async_f, + dispatch_queue_t dq, + void *ctxt, dispatch_function_t func); +DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_sync_f, + dispatch_queue_t dq, + void *ctxt, dispatch_function_t func); +DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after_f, + dispatch_time_t when, dispatch_queue_t dq, + void *ctxt, dispatch_function_t func); +DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_barrier_async_f, + dispatch_queue_t dq, + void *ctxt, dispatch_function_t func); +DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async_f, + dispatch_group_t group, dispatch_queue_t dq, + void *ctxt, dispatch_function_t func); + +DECLARE_FUNCTION_AND_WRAPPER(void, __CFInitialize, void); +DECLARE_FUNCTION_AND_WRAPPER(CFStringRef, CFStringCreateCopy, + CFAllocatorRef alloc, CFStringRef str); +DECLARE_FUNCTION_AND_WRAPPER(void, free, void* ptr); +#if MAC_INTERPOSE_FUNCTIONS +DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async, + dispatch_group_t dg, + dispatch_queue_t dq, void (^work)(void)); +DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async, + dispatch_queue_t dq, void (^work)(void)); +DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after, + dispatch_queue_t dq, void (^work)(void)); +DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_event_handler, + dispatch_source_t ds, void (^work)(void)); +DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_cancel_handler, + dispatch_source_t ds, void (^work)(void)); +#endif // MAC_INTERPOSE_FUNCTIONS +#endif // __APPLE__ +} // extern "C" +#endif + +#endif // ASAN_INTERCEPTED_FUNCTIONS_H diff --git a/libsanitizer/asan/asan_interceptors.cc b/libsanitizer/asan/asan_interceptors.cc new file mode 100644 index 0000000..8e15d34 --- /dev/null +++ b/libsanitizer/asan/asan_interceptors.cc @@ -0,0 +1,704 @@ +//===-- 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_intercepted_functions.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "asan_thread_registry.h" +#include "interception/interception.h" +#include "sanitizer/asan_interface.h" +#include "sanitizer_common/sanitizer_libc.h" + +namespace __asan { + +// Instruments read/write access to a single byte in memory. +// On error calls __asan_report_error, which aborts the program. +#define ACCESS_ADDRESS(address, isWrite) do { \ + if (!AddrIsInMem(address) || AddressIsPoisoned(address)) { \ + GET_CURRENT_PC_BP_SP; \ + __asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1); \ + } \ +} while (0) + +// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE, +// and ASAN_WRITE_RANGE as macro instead of function so +// that no extra frames are created, and stack trace contains +// relevant information only. + +// Instruments read/write access to a memory range. +// More complex implementation is possible, for now just +// checking the first and the last byte of a range. +#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ + if (size > 0) { \ + uptr ptr = (uptr)(offset); \ + ACCESS_ADDRESS(ptr, isWrite); \ + ACCESS_ADDRESS(ptr + (size) - 1, isWrite); \ + } \ +} while (0) + +#define ASAN_READ_RANGE(offset, size) do { \ + ACCESS_MEMORY_RANGE(offset, size, false); \ +} while (0) + +#define ASAN_WRITE_RANGE(offset, size) do { \ + ACCESS_MEMORY_RANGE(offset, size, true); \ +} while (0) + +// Behavior of functions like "memcpy" or "strcpy" is undefined +// if memory intervals overlap. We report error in this case. +// Macro is used to avoid creation of new frames. +static inline bool RangesOverlap(const char *offset1, uptr length1, + const char *offset2, uptr length2) { + return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1)); +} +#define CHECK_RANGES_OVERLAP(name, _offset1, length1, _offset2, length2) do { \ + const char *offset1 = (const char*)_offset1; \ + const char *offset2 = (const char*)_offset2; \ + if (RangesOverlap(offset1, length1, offset2, length2)) { \ + GET_STACK_TRACE_HERE(kStackTraceMax); \ + ReportStringFunctionMemoryRangesOverlap(name, offset1, length1, \ + offset2, length2, &stack); \ + } \ +} while (0) + +#define ENSURE_ASAN_INITED() do { \ + CHECK(!asan_init_is_running); \ + if (!asan_inited) { \ + __asan_init(); \ + } \ +} while (0) + +static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { +#if ASAN_INTERCEPT_STRNLEN + if (REAL(strnlen) != 0) { + return REAL(strnlen)(s, maxlen); + } +#endif + return internal_strnlen(s, maxlen); +} + +} // namespace __asan + +// ---------------------- Wrappers ---------------- {{{1 +using namespace __asan; // NOLINT + +static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { + AsanThread *t = (AsanThread*)arg; + asanThreadRegistry().SetCurrent(t); + return t->ThreadStart(); +} + +#if ASAN_INTERCEPT_PTHREAD_CREATE +INTERCEPTOR(int, pthread_create, void *thread, + void *attr, void *(*start_routine)(void*), void *arg) { + GET_STACK_TRACE_HERE(kStackTraceMax); + u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); + asanThreadRegistry().RegisterThread(t); + return REAL(pthread_create)(thread, attr, asan_thread_start, t); +} +#endif // ASAN_INTERCEPT_PTHREAD_CREATE + +#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION +INTERCEPTOR(void*, signal, int signum, void *handler) { + if (!AsanInterceptsSignal(signum)) { + return REAL(signal)(signum, handler); + } + return 0; +} + +INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact) { + if (!AsanInterceptsSignal(signum)) { + return REAL(sigaction)(signum, act, oldact); + } + return 0; +} +#elif ASAN_POSIX +// We need to have defined REAL(sigaction) on posix systems. +DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact); +#endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION + +INTERCEPTOR(void, longjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(longjmp)(env, val); +} + +#if ASAN_INTERCEPT__LONGJMP +INTERCEPTOR(void, _longjmp, void *env, int val) { + __asan_handle_no_return(); + REAL(_longjmp)(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 + +// intercept mlock and friends. +// Since asan maps 16T of RAM, mlock is completely unfriendly to asan. +// All functions return 0 (success). +static void MlockIsUnsupported() { + static bool printed = 0; + if (printed) return; + printed = true; + Printf("INFO: AddressSanitizer ignores mlock/mlockall/munlock/munlockall\n"); +} + +extern "C" { +INTERCEPTOR(int, mlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, mlockall, int flags) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlockall, void) { + MlockIsUnsupported(); + return 0; +} +} // extern "C" + +static inline int CharCmp(unsigned char c1, unsigned char c2) { + return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; +} + +static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { + int c1_low = ToLower(c1); + int c2_low = ToLower(c2); + return c1_low - c2_low; +} + +INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { + if (!asan_inited) return internal_memcmp(a1, a2, size); + ENSURE_ASAN_INITED(); + unsigned char c1 = 0, c2 = 0; + const unsigned char *s1 = (const unsigned char*)a1; + const unsigned char *s2 = (const unsigned char*)a2; + uptr i; + for (i = 0; i < size; i++) { + c1 = s1[i]; + c2 = s2[i]; + if (c1 != c2) break; + } + ASAN_READ_RANGE(s1, Min(i + 1, size)); + ASAN_READ_RANGE(s2, Min(i + 1, size)); + return CharCmp(c1, c2); +} + +INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { + if (!asan_inited) return internal_memcpy(to, from, size); + // memcpy is called during __asan_init() from the internals + // of printf(...). + if (asan_init_is_running) { + return REAL(memcpy)(to, from, size); + } + ENSURE_ASAN_INITED(); + if (flags()->replace_intrin) { + if (to != from) { + // We do not treat memcpy with to==from as a bug. + // See http://llvm.org/bugs/show_bug.cgi?id=11763. + CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); + } + ASAN_WRITE_RANGE(from, size); + ASAN_READ_RANGE(to, size); + } + return REAL(memcpy)(to, from, size); +} + +INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { +#if MAC_INTERPOSE_FUNCTIONS + if (!asan_inited) return REAL(memmove)(to, from, size); +#endif + if (asan_init_is_running) { + return REAL(memmove)(to, from, size); + } + ENSURE_ASAN_INITED(); + if (flags()->replace_intrin) { + ASAN_WRITE_RANGE(from, size); + ASAN_READ_RANGE(to, size); + } + return REAL(memmove)(to, from, size); +} + +INTERCEPTOR(void*, memset, void *block, int c, uptr size) { + if (!asan_inited) return internal_memset(block, c, size); + // memset is called inside Printf. + if (asan_init_is_running) { + return REAL(memset)(block, c, size); + } + ENSURE_ASAN_INITED(); + if (flags()->replace_intrin) { + ASAN_WRITE_RANGE(block, size); + } + return REAL(memset)(block, c, size); +} + +INTERCEPTOR(char*, strchr, const char *str, int c) { + if (!asan_inited) return internal_strchr(str, c); + // strchr is called inside create_purgeable_zone() when MallocGuardEdges=1 is + // used. + if (asan_init_is_running) { + return REAL(strchr)(str, c); + } + ENSURE_ASAN_INITED(); + char *result = REAL(strchr)(str, c); + if (flags()->replace_str) { + uptr bytes_read = (result ? result - str : REAL(strlen)(str)) + 1; + ASAN_READ_RANGE(str, bytes_read); + } + return result; +} + +#if ASAN_INTERCEPT_INDEX +# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX +INTERCEPTOR(char*, index, const char *string, int c) + ALIAS(WRAPPER_NAME(strchr)); +# else +DEFINE_REAL(char*, index, const char *string, int c) +# 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 + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_length = REAL(strlen)(from); + ASAN_READ_RANGE(from, from_length + 1); + uptr to_length = REAL(strlen)(to); + ASAN_READ_RANGE(to, to_length); + ASAN_WRITE_RANGE(to + to_length, from_length + 1); + // 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) { + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_length = MaybeRealStrnlen(from, size); + uptr copy_length = Min(size, from_length + 1); + ASAN_READ_RANGE(from, copy_length); + uptr to_length = REAL(strlen)(to); + ASAN_READ_RANGE(to, to_length); + ASAN_WRITE_RANGE(to + to_length, from_length + 1); + if (from_length > 0) { + CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1, + from, copy_length); + } + } + return REAL(strncat)(to, from, size); +} + +INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { + if (!asan_inited) return internal_strcmp(s1, s2); + if (asan_init_is_running) { + return REAL(strcmp)(s1, s2); + } + ENSURE_ASAN_INITED(); + unsigned char c1, c2; + uptr i; + for (i = 0; ; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (c1 != c2 || c1 == '\0') break; + } + ASAN_READ_RANGE(s1, i + 1); + ASAN_READ_RANGE(s2, i + 1); + return CharCmp(c1, c2); +} + +INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT +#if MAC_INTERPOSE_FUNCTIONS + if (!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(from, from_size); + ASAN_WRITE_RANGE(to, from_size); + } + return REAL(strcpy)(to, from); // NOLINT +} + +#if ASAN_INTERCEPT_STRDUP +INTERCEPTOR(char*, strdup, const char *s) { + if (!asan_inited) return internal_strdup(s); + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr length = REAL(strlen)(s); + ASAN_READ_RANGE(s, length + 1); + } + return REAL(strdup)(s); +} +#endif + +INTERCEPTOR(uptr, strlen, const char *s) { + if (!asan_inited) return internal_strlen(s); + // strlen is called from malloc_default_purgeable_zone() + // in __asan::ReplaceSystemAlloc() on Mac. + if (asan_init_is_running) { + return REAL(strlen)(s); + } + ENSURE_ASAN_INITED(); + uptr length = REAL(strlen)(s); + if (flags()->replace_str) { + ASAN_READ_RANGE(s, length + 1); + } + return length; +} + +#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP +INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { + ENSURE_ASAN_INITED(); + unsigned char c1, c2; + uptr i; + for (i = 0; ; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; + } + ASAN_READ_RANGE(s1, i + 1); + ASAN_READ_RANGE(s2, i + 1); + return CharCaseCmp(c1, c2); +} + +INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr n) { + ENSURE_ASAN_INITED(); + unsigned char c1 = 0, c2 = 0; + uptr i; + for (i = 0; i < n; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; + } + ASAN_READ_RANGE(s1, Min(i + 1, n)); + ASAN_READ_RANGE(s2, Min(i + 1, n)); + return CharCaseCmp(c1, c2); +} +#endif // ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP + +INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { + if (!asan_inited) return internal_strncmp(s1, s2, size); + // strncmp is called from malloc_default_purgeable_zone() + // in __asan::ReplaceSystemAlloc() on Mac. + if (asan_init_is_running) { + return REAL(strncmp)(s1, s2, size); + } + ENSURE_ASAN_INITED(); + unsigned char c1 = 0, c2 = 0; + uptr i; + for (i = 0; i < size; i++) { + c1 = (unsigned char)s1[i]; + c2 = (unsigned char)s2[i]; + if (c1 != c2 || c1 == '\0') break; + } + ASAN_READ_RANGE(s1, Min(i + 1, size)); + ASAN_READ_RANGE(s2, Min(i + 1, size)); + return CharCmp(c1, c2); +} + +INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); + CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size); + ASAN_READ_RANGE(from, from_size); + ASAN_WRITE_RANGE(to, size); + } + return REAL(strncpy)(to, from, size); +} + +#if ASAN_INTERCEPT_STRNLEN +INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) { + ENSURE_ASAN_INITED(); + uptr length = REAL(strnlen)(s, maxlen); + if (flags()->replace_str) { + ASAN_READ_RANGE(s, Min(length + 1, maxlen)); + } + return length; +} +#endif // ASAN_INTERCEPT_STRNLEN + +static inline bool IsValidStrtolBase(int base) { + return (base == 0) || (2 <= base && base <= 36); +} + +static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { + CHECK(endptr != 0); + if (nptr == *endptr) { + // No digits were found at strtol call, we need to find out the last + // symbol accessed by strtoll on our own. + // We get this symbol by skipping leading blanks and optional +/- sign. + while (IsSpace(*nptr)) nptr++; + if (*nptr == '+' || *nptr == '-') nptr++; + *endptr = (char*)nptr; + } + CHECK(*endptr >= nptr); +} + +INTERCEPTOR(long, strtol, const char *nptr, // NOLINT + char **endptr, int base) { + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(strtol)(nptr, endptr, base); + } + char *real_endptr; + long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT + if (endptr != 0) { + *endptr = real_endptr; + } + if (IsValidStrtolBase(base)) { + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + } + return result; +} + +INTERCEPTOR(int, atoi, const char *nptr) { +#if MAC_INTERPOSE_FUNCTIONS + if (!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_RANGE(nptr, (real_endptr - nptr) + 1); + return result; +} + +INTERCEPTOR(long, atol, const char *nptr) { // NOLINT +#if MAC_INTERPOSE_FUNCTIONS + if (!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_RANGE(nptr, (real_endptr - nptr) + 1); + return result; +} + +#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL +INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT + char **endptr, int base) { + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(strtoll)(nptr, endptr, base); + } + char *real_endptr; + long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT + if (endptr != 0) { + *endptr = real_endptr; + } + // If base has unsupported value, strtoll can exit with EINVAL + // without reading any characters. So do additional checks only + // if base is valid. + if (IsValidStrtolBase(base)) { + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + } + return result; +} + +INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT + ENSURE_ASAN_INITED(); + if (!flags()->replace_str) { + return REAL(atoll)(nptr); + } + char *real_endptr; + long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT + FixRealStrtolEndptr(nptr, &real_endptr); + ASAN_READ_RANGE(nptr, (real_endptr - nptr) + 1); + return result; +} +#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL + +#define ASAN_INTERCEPT_FUNC(name) do { \ + if (!INTERCEPT_FUNCTION(name) && flags()->verbosity > 0) \ + Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ + } while (0) + +#if defined(_WIN32) +INTERCEPTOR_WINAPI(DWORD, CreateThread, + void* security, uptr stack_size, + DWORD (__stdcall *start_routine)(void*), void* arg, + DWORD flags, void* tid) { + GET_STACK_TRACE_HERE(kStackTraceMax); + u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); + asanThreadRegistry().RegisterThread(t); + return REAL(CreateThread)(security, stack_size, + asan_thread_start, t, flags, tid); +} + +namespace __asan { +void InitializeWindowsInterceptors() { + ASAN_INTERCEPT_FUNC(CreateThread); +} + +} // namespace __asan +#endif + +// ---------------------- InitializeAsanInterceptors ---------------- {{{1 +namespace __asan { +void InitializeAsanInterceptors() { + static bool was_called_once; + CHECK(was_called_once == false); + was_called_once = true; +#if MAC_INTERPOSE_FUNCTIONS + return; +#endif + // Intercept mem* functions. + ASAN_INTERCEPT_FUNC(memcmp); + ASAN_INTERCEPT_FUNC(memmove); + ASAN_INTERCEPT_FUNC(memset); + if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { + ASAN_INTERCEPT_FUNC(memcpy); + } else { +#if !MAC_INTERPOSE_FUNCTIONS + // If we're using dynamic interceptors on Mac, these two are just plain + // functions. + internal_memcpy(&REAL(memcpy), &REAL(memmove), sizeof(REAL(memmove))); +#endif + } + + // Intercept str* functions. + ASAN_INTERCEPT_FUNC(strcat); // NOLINT + ASAN_INTERCEPT_FUNC(strchr); + ASAN_INTERCEPT_FUNC(strcmp); + ASAN_INTERCEPT_FUNC(strcpy); // NOLINT + ASAN_INTERCEPT_FUNC(strlen); + ASAN_INTERCEPT_FUNC(strncat); + ASAN_INTERCEPT_FUNC(strncmp); + ASAN_INTERCEPT_FUNC(strncpy); +#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP + ASAN_INTERCEPT_FUNC(strcasecmp); + ASAN_INTERCEPT_FUNC(strncasecmp); +#endif +#if ASAN_INTERCEPT_STRDUP + ASAN_INTERCEPT_FUNC(strdup); +#endif +#if ASAN_INTERCEPT_STRNLEN + ASAN_INTERCEPT_FUNC(strnlen); +#endif +#if ASAN_INTERCEPT_INDEX +# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX + ASAN_INTERCEPT_FUNC(index); +# else + CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr))); +# endif +#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 + +#if ASAN_INTERCEPT_MLOCKX + // Intercept mlock/munlock. + ASAN_INTERCEPT_FUNC(mlock); + ASAN_INTERCEPT_FUNC(munlock); + ASAN_INTERCEPT_FUNC(mlockall); + ASAN_INTERCEPT_FUNC(munlockall); +#endif + + // Intecept signal- and jump-related functions. + ASAN_INTERCEPT_FUNC(longjmp); +#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION + ASAN_INTERCEPT_FUNC(sigaction); + ASAN_INTERCEPT_FUNC(signal); +#endif +#if ASAN_INTERCEPT__LONGJMP + ASAN_INTERCEPT_FUNC(_longjmp); +#endif +#if ASAN_INTERCEPT_SIGLONGJMP + ASAN_INTERCEPT_FUNC(siglongjmp); +#endif + + // Intercept exception handling functions. +#if ASAN_INTERCEPT___CXA_THROW + INTERCEPT_FUNCTION(__cxa_throw); +#endif + + // Intercept threading-related functions +#if ASAN_INTERCEPT_PTHREAD_CREATE + ASAN_INTERCEPT_FUNC(pthread_create); +#endif + + // Some Windows-specific interceptors. +#if defined(_WIN32) + InitializeWindowsInterceptors(); +#endif + + // Some Mac-specific interceptors. +#if defined(__APPLE__) + InitializeMacInterceptors(); +#endif + + if (flags()->verbosity > 0) { + Report("AddressSanitizer: libc interceptors initialized\n"); + } +} + +} // namespace __asan diff --git a/libsanitizer/asan/asan_interceptors.h b/libsanitizer/asan/asan_interceptors.h new file mode 100644 index 0000000..a6ab030 --- /dev/null +++ b/libsanitizer/asan/asan_interceptors.h @@ -0,0 +1,39 @@ +//===-- asan_interceptors.h -------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_interceptors.cc +//===----------------------------------------------------------------------===// +#ifndef ASAN_INTERCEPTORS_H +#define ASAN_INTERCEPTORS_H + +#include "asan_internal.h" +#include "interception/interception.h" + +DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size) +DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size) +DECLARE_REAL(void*, memset, void *block, int c, uptr size) +DECLARE_REAL(char*, strchr, const char *str, int c) +DECLARE_REAL(uptr, strlen, const char *s) +DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size) +DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen) +DECLARE_REAL(char*, strstr, const char *s1, const char *s2) +struct sigaction; +DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, + struct sigaction *oldact) + +namespace __asan { + +void InitializeAsanInterceptors(); +#if defined(__APPLE__) +void InitializeMacInterceptors(); +#endif // __APPLE__ + +} // namespace __asan + +#endif // ASAN_INTERCEPTORS_H diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h new file mode 100644 index 0000000..21368ee --- /dev/null +++ b/libsanitizer/asan/asan_internal.h @@ -0,0 +1,169 @@ +//===-- asan_internal.h -----------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header which defines various general utilities. +//===----------------------------------------------------------------------===// +#ifndef ASAN_INTERNAL_H +#define ASAN_INTERNAL_H + +#include "asan_flags.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_libc.h" + +#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) +# error "This operating system is not supported by AddressSanitizer" +#endif + +#define ASAN_DEFAULT_FAILURE_EXITCODE 1 + +#if defined(__linux__) +# define ASAN_LINUX 1 +#else +# define ASAN_LINUX 0 +#endif + +#if defined(__APPLE__) +# define ASAN_MAC 1 +#else +# define ASAN_MAC 0 +#endif + +#if defined(_WIN32) +# define ASAN_WINDOWS 1 +#else +# define ASAN_WINDOWS 0 +#endif + +#if defined(__ANDROID__) || defined(ANDROID) +# define ASAN_ANDROID 1 +#else +# define ASAN_ANDROID 0 +#endif + + +#define ASAN_POSIX (ASAN_LINUX || ASAN_MAC) + +#if __has_feature(address_sanitizer) +# error "The AddressSanitizer run-time should not be" + " instrumented by AddressSanitizer" +#endif + +// Build-time configuration options. + +// If set, asan will install its own SEGV signal handler. +#ifndef ASAN_NEEDS_SEGV +# if ASAN_ANDROID == 1 +# define ASAN_NEEDS_SEGV 0 +# else +# define ASAN_NEEDS_SEGV 1 +# endif +#endif + +// If set, asan will intercept C++ exception api call(s). +#ifndef ASAN_HAS_EXCEPTIONS +# define ASAN_HAS_EXCEPTIONS 1 +#endif + +// If set, asan uses the values of SHADOW_SCALE and SHADOW_OFFSET +// provided by the instrumented objects. Otherwise constants are used. +#ifndef ASAN_FLEXIBLE_MAPPING_AND_OFFSET +# define ASAN_FLEXIBLE_MAPPING_AND_OFFSET 0 +#endif + +// If set, values like allocator chunk size, as well as defaults for some flags +// will be changed towards less memory overhead. +#ifndef ASAN_LOW_MEMORY +# ifdef ASAN_ANDROID +# define ASAN_LOW_MEMORY 1 +# else +# define ASAN_LOW_MEMORY 0 +# endif +#endif + +// All internal functions in asan reside inside the __asan namespace +// to avoid namespace collisions with the user programs. +// Seperate namespace also makes it simpler to distinguish the asan run-time +// functions from the instrumented user code in a profile. +namespace __asan { + +class AsanThread; +using __sanitizer::StackTrace; + +// asan_rtl.cc +void NORETURN ShowStatsAndAbort(); + +void ReplaceOperatorsNewAndDelete(); +// asan_malloc_linux.cc / asan_malloc_mac.cc +void ReplaceSystemMalloc(); + +// asan_linux.cc / asan_mac.cc / asan_win.cc +void *AsanDoesNotSupportStaticLinkage(); + +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); + +void MaybeReexec(); +bool AsanInterceptsSignal(int signum); +void SetAlternateSignalStack(); +void UnsetAlternateSignalStack(); +void InstallSignalHandlers(); +void AsanPlatformThreadInit(); + +// Wrapper for TLS/TSD. +void AsanTSDInit(void (*destructor)(void *tsd)); +void *AsanTSDGet(); +void AsanTSDSet(void *tsd); + +void AppendToErrorMessageBuffer(const char *buffer); + +// asan_poisoning.cc +// Poisons the shadow memory for "size" bytes starting from "addr". +void PoisonShadow(uptr addr, uptr size, u8 value); +// Poisons the shadow memory for "redzone_size" bytes starting from +// "addr + size". +void PoisonShadowPartialRightRedzone(uptr addr, + uptr size, + uptr redzone_size, + u8 value); + +// Platfrom-specific options. +#ifdef __APPLE__ +bool PlatformHasDifferentMemcpyAndMemmove(); +# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \ + (PlatformHasDifferentMemcpyAndMemmove()) +#else +# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true +#endif // __APPLE__ + +extern int asan_inited; +// Used to avoid infinite recursion in __asan_init(). +extern bool asan_init_is_running; +extern void (*death_callback)(void); + +// These magic values are written to shadow for better error reporting. +const int kAsanHeapLeftRedzoneMagic = 0xfa; +const int kAsanHeapRightRedzoneMagic = 0xfb; +const int kAsanHeapFreeMagic = 0xfd; +const int kAsanStackLeftRedzoneMagic = 0xf1; +const int kAsanStackMidRedzoneMagic = 0xf2; +const int kAsanStackRightRedzoneMagic = 0xf3; +const int kAsanStackPartialRedzoneMagic = 0xf4; +const int kAsanStackAfterReturnMagic = 0xf5; +const int kAsanInitializationOrderMagic = 0xf6; +const int kAsanUserPoisonedMemoryMagic = 0xf7; +const int kAsanGlobalRedzoneMagic = 0xf9; +const int kAsanInternalHeapMagic = 0xfe; + +static const uptr kCurrentStackFrameMagic = 0x41B58AB3; +static const uptr kRetiredStackFrameMagic = 0x45E0360E; + +} // namespace __asan + +#endif // ASAN_INTERNAL_H diff --git a/libsanitizer/asan/asan_linux.cc b/libsanitizer/asan/asan_linux.cc new file mode 100644 index 0000000..2922740 --- /dev/null +++ b/libsanitizer/asan/asan_linux.cc @@ -0,0 +1,150 @@ +//===-- 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. +//===----------------------------------------------------------------------===// +#ifdef __linux__ + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_lock.h" +#include "asan_thread.h" +#include "asan_thread_registry.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" + +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/mman.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> +#include <unwind.h> + +#if !ASAN_ANDROID +// FIXME: where to get ucontext on Android? +#include <sys/ucontext.h> +#endif + +extern "C" void* _DYNAMIC; + +namespace __asan { + +void MaybeReexec() { + // No need to re-exec on Linux. +} + +void *AsanDoesNotSupportStaticLinkage() { + // This will fail to link with -static. + return &_DYNAMIC; // defined in link.h +} + +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { +#if ASAN_ANDROID + *pc = *sp = *bp = 0; +#elif defined(__arm__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.arm_pc; + *bp = ucontext->uc_mcontext.arm_fp; + *sp = ucontext->uc_mcontext.arm_sp; +# elif defined(__x86_64__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[REG_RIP]; + *bp = ucontext->uc_mcontext.gregs[REG_RBP]; + *sp = ucontext->uc_mcontext.gregs[REG_RSP]; +# elif defined(__i386__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[REG_EIP]; + *bp = ucontext->uc_mcontext.gregs[REG_EBP]; + *sp = ucontext->uc_mcontext.gregs[REG_ESP]; +#else +# error "Unsupported arch" +#endif +} + +bool AsanInterceptsSignal(int signum) { + return signum == SIGSEGV && flags()->handle_segv; +} + +void AsanPlatformThreadInit() { + // Nothing here for now. +} + +AsanLock::AsanLock(LinkerInitialized) { + // We assume that pthread_mutex_t initialized to all zeroes is a valid + // unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers + // a gcc warning: + // extended initializer lists only available with -std=c++0x or -std=gnu++0x +} + +void AsanLock::Lock() { + CHECK(sizeof(pthread_mutex_t) <= sizeof(opaque_storage_)); + pthread_mutex_lock((pthread_mutex_t*)&opaque_storage_); + CHECK(!owner_); + owner_ = (uptr)pthread_self(); +} + +void AsanLock::Unlock() { + CHECK(owner_ == (uptr)pthread_self()); + owner_ = 0; + pthread_mutex_unlock((pthread_mutex_t*)&opaque_storage_); +} + +#ifdef __arm__ +#define UNWIND_STOP _URC_END_OF_STACK +#define UNWIND_CONTINUE _URC_NO_REASON +#else +#define UNWIND_STOP _URC_NORMAL_STOP +#define UNWIND_CONTINUE _URC_NO_REASON +#endif + +uptr Unwind_GetIP(struct _Unwind_Context *ctx) { +#ifdef __arm__ + uptr val; + _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE, + 15 /* r15 = PC */, _UVRSD_UINT32, &val); + CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed"); + // Clear the Thumb bit. + return val & ~(uptr)1; +#else + return _Unwind_GetIP(ctx); +#endif +} + +_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, + void *param) { + StackTrace *b = (StackTrace*)param; + CHECK(b->size < b->max_size); + uptr pc = Unwind_GetIP(ctx); + b->trace[b->size++] = pc; + if (b->size == b->max_size) return UNWIND_STOP; + return UNWIND_CONTINUE; +} + +void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp) { + stack->size = 0; + stack->trace[0] = pc; + if ((max_s) > 1) { + stack->max_size = max_s; +#ifdef __arm__ + _Unwind_Backtrace(Unwind_Trace, stack); +#else + if (!asan_inited) return; + if (AsanThread *t = asanThreadRegistry().GetCurrent()) + stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom()); +#endif + } +} + +} // namespace __asan + +#endif // __linux__ diff --git a/libsanitizer/asan/asan_lock.h b/libsanitizer/asan/asan_lock.h new file mode 100644 index 0000000..2392e3c --- /dev/null +++ b/libsanitizer/asan/asan_lock.h @@ -0,0 +1,40 @@ +//===-- asan_lock.h ---------------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// A wrapper for a simple lock. +//===----------------------------------------------------------------------===// +#ifndef ASAN_LOCK_H +#define ASAN_LOCK_H + +#include "sanitizer_common/sanitizer_mutex.h" +#include "asan_internal.h" + +// The locks in ASan are global objects and they are never destroyed to avoid +// at-exit races (that is, a lock is being used by other threads while the main +// thread is doing atexit destructors). +// We define the class using opaque storage to avoid including system headers. + +namespace __asan { + +class AsanLock { + public: + explicit AsanLock(LinkerInitialized); + void Lock(); + void Unlock(); + bool IsLocked() { return owner_ != 0; } + private: + uptr opaque_storage_[10]; + uptr owner_; // for debugging and for malloc_introspection_t interface +}; + +typedef GenericScopedLock<AsanLock> ScopedLock; + +} // namespace __asan + +#endif // ASAN_LOCK_H diff --git a/libsanitizer/asan/asan_mac.cc b/libsanitizer/asan/asan_mac.cc new file mode 100644 index 0000000..81e25e8 --- /dev/null +++ b/libsanitizer/asan/asan_mac.cc @@ -0,0 +1,526 @@ +//===-- 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. +//===----------------------------------------------------------------------===// + +#ifdef __APPLE__ + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mac.h" +#include "asan_mapping.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "asan_thread_registry.h" +#include "sanitizer_common/sanitizer_libc.h" + +#include <crt_externs.h> // for _NSGetArgv +#include <dlfcn.h> // for dladdr() +#include <mach-o/dyld.h> +#include <mach-o/loader.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sys/sysctl.h> +#include <sys/ucontext.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdlib.h> // for free() +#include <unistd.h> +#include <libkern/OSAtomic.h> +#include <CoreFoundation/CFString.h> + +namespace __asan { + +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { + ucontext_t *ucontext = (ucontext_t*)context; +# if __WORDSIZE == 64 + *pc = ucontext->uc_mcontext->__ss.__rip; + *bp = ucontext->uc_mcontext->__ss.__rbp; + *sp = ucontext->uc_mcontext->__ss.__rsp; +# else + *pc = ucontext->uc_mcontext->__ss.__eip; + *bp = ucontext->uc_mcontext->__ss.__ebp; + *sp = ucontext->uc_mcontext->__ss.__esp; +# endif // __WORDSIZE +} + +int GetMacosVersion() { + int mib[2] = { CTL_KERN, KERN_OSRELEASE }; + char version[100]; + uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]); + for (uptr i = 0; i < maxlen; i++) version[i] = '\0'; + // Get the version length. + CHECK(sysctl(mib, 2, 0, &len, 0, 0) != -1); + CHECK(len < maxlen); + CHECK(sysctl(mib, 2, version, &len, 0, 0) != -1); + switch (version[0]) { + case '9': return MACOS_VERSION_LEOPARD; + case '1': { + switch (version[1]) { + case '0': return MACOS_VERSION_SNOW_LEOPARD; + case '1': return MACOS_VERSION_LION; + default: return MACOS_VERSION_UNKNOWN; + } + } + default: return MACOS_VERSION_UNKNOWN; + } +} + +bool PlatformHasDifferentMemcpyAndMemmove() { + // On OS X 10.7 memcpy() and memmove() are both resolved + // into memmove$VARIANT$sse42. + // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34. + // TODO(glider): need to check dynamically that memcpy() and memmove() are + // actually the same function. + return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD; +} + +extern "C" +void __asan_init(); + +static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; + +void MaybeReexec() { + if (!flags()->allow_reexec) return; +#if MAC_INTERPOSE_FUNCTIONS + // If the program is linked with the dynamic ASan runtime library, make sure + // the library is preloaded so that the wrappers work. If it is not, set + // DYLD_INSERT_LIBRARIES and re-exec ourselves. + Dl_info info; + CHECK(dladdr((void*)((uptr)__asan_init), &info)); + const char *dyld_insert_libraries = GetEnv(kDyldInsertLibraries); + if (!dyld_insert_libraries || + !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) { + // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime + // library. + char program_name[1024]; + uint32_t buf_size = sizeof(program_name); + _NSGetExecutablePath(program_name, &buf_size); + // Ok to use setenv() since the wrappers don't depend on the value of + // asan_inited. + setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); + if (flags()->verbosity >= 1) { + Report("exec()-ing the program with\n"); + Report("%s=%s\n", kDyldInsertLibraries, info.dli_fname); + Report("to enable ASan wrappers.\n"); + Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n"); + } + execv(program_name, *_NSGetArgv()); + } +#endif // MAC_INTERPOSE_FUNCTIONS + // If we're not using the dynamic runtime, do nothing. +} + +// No-op. Mac does not support static linkage anyway. +void *AsanDoesNotSupportStaticLinkage() { + return 0; +} + +bool AsanInterceptsSignal(int signum) { + return (signum == SIGSEGV || signum == SIGBUS) && flags()->handle_segv; +} + +void AsanPlatformThreadInit() { + ReplaceCFAllocator(); +} + +AsanLock::AsanLock(LinkerInitialized) { + // We assume that OS_SPINLOCK_INIT is zero +} + +void AsanLock::Lock() { + CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_)); + CHECK(OS_SPINLOCK_INIT == 0); + CHECK(owner_ != (uptr)pthread_self()); + OSSpinLockLock((OSSpinLock*)&opaque_storage_); + CHECK(!owner_); + owner_ = (uptr)pthread_self(); +} + +void AsanLock::Unlock() { + CHECK(owner_ == (uptr)pthread_self()); + owner_ = 0; + OSSpinLockUnlock((OSSpinLock*)&opaque_storage_); +} + +void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp) { + stack->size = 0; + stack->trace[0] = pc; + if ((max_s) > 1) { + stack->max_size = max_s; + if (!asan_inited) return; + if (AsanThread *t = asanThreadRegistry().GetCurrent()) + stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom()); + } +} + +// The range of pages to be used for escape islands. +// TODO(glider): instead of mapping a fixed range we must find a range of +// unmapped pages in vmmap and take them. +// These constants were chosen empirically and may not work if the shadow +// memory layout changes. Unfortunately they do necessarily depend on +// kHighMemBeg or kHighMemEnd. +static void *island_allocator_pos = 0; + +#if __WORDSIZE == 32 +# define kIslandEnd (0xffdf0000 - kPageSize) +# define kIslandBeg (kIslandEnd - 256 * kPageSize) +#else +# define kIslandEnd (0x7fffffdf0000 - kPageSize) +# define kIslandBeg (kIslandEnd - 256 * kPageSize) +#endif + +extern "C" +mach_error_t __interception_allocate_island(void **ptr, + uptr unused_size, + void *unused_hint) { + if (!island_allocator_pos) { + island_allocator_pos = + internal_mmap((void*)kIslandBeg, kIslandEnd - kIslandBeg, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, 0); + if (island_allocator_pos != (void*)kIslandBeg) { + return KERN_NO_SPACE; + } + if (flags()->verbosity) { + Report("Mapped pages %p--%p for branch islands.\n", + (void*)kIslandBeg, (void*)kIslandEnd); + } + // Should not be very performance-critical. + internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg); + }; + *ptr = island_allocator_pos; + island_allocator_pos = (char*)island_allocator_pos + kPageSize; + if (flags()->verbosity) { + Report("Branch island allocated at %p\n", *ptr); + } + return err_none; +} + +extern "C" +mach_error_t __interception_deallocate_island(void *ptr) { + // Do nothing. + // TODO(glider): allow to free and reuse the island memory. + return err_none; +} + +// Support for the following functions from libdispatch on Mac OS: +// 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* pthread_workqueue_t; +typedef void* pthread_workitem_handle_t; + +typedef void* dispatch_group_t; +typedef void* dispatch_queue_t; +typedef void* dispatch_source_t; +typedef u64 dispatch_time_t; +typedef void (*dispatch_function_t)(void *block); +typedef void* (*worker_t)(void *block); + +// A wrapper for the ObjC blocks used to support libdispatch. +typedef struct { + void *block; + dispatch_function_t func; + u32 parent_tid; +} asan_block_context_t; + +// We use extern declarations of libdispatch functions here instead +// of including <dispatch/dispatch.h>. This header is not present on +// Mac OS X Leopard and eariler, and although we don't expect ASan to +// work on legacy systems, it's bad to break the build of +// LLVM compiler-rt there. +extern "C" { +void dispatch_async_f(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func); +void dispatch_sync_f(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func); +void dispatch_after_f(dispatch_time_t when, dispatch_queue_t dq, void *ctxt, + dispatch_function_t func); +void dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt, + dispatch_function_t func); +void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t dq, + void *ctxt, dispatch_function_t func); +int pthread_workqueue_additem_np(pthread_workqueue_t workq, + void *(*workitem_func)(void *), void * workitem_arg, + pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp); +} // extern "C" + +static ALWAYS_INLINE +void asan_register_worker_thread(int parent_tid, StackTrace *stack) { + AsanThread *t = asanThreadRegistry().GetCurrent(); + if (!t) { + t = AsanThread::Create(parent_tid, 0, 0, stack); + asanThreadRegistry().RegisterThread(t); + t->Init(); + asanThreadRegistry().SetCurrent(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_HERE(kStackTraceMax); + asan_block_context_t *context = (asan_block_context_t*)block; + if (flags()->verbosity >= 2) { + Report("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); +} + +} // 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, + StackTrace *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 = asanThreadRegistry().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_HERE(kStackTraceMax); \ + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ + if (flags()->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_HERE(kStackTraceMax); + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); + if (flags()->verbosity >= 2) { + Report("dispatch_after_f: %p\n", asan_ctxt); + PRINT_CURRENT_STACK(); + } + return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt, + asan_dispatch_call_block_and_release); +} + +INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, + dispatch_queue_t dq, void *ctxt, + dispatch_function_t func) { + GET_STACK_TRACE_HERE(kStackTraceMax); + asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); + if (flags()->verbosity >= 2) { + Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", + asan_ctxt, pthread_self()); + PRINT_CURRENT_STACK(); + } + REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt, + asan_dispatch_call_block_and_release); +} + +#if MAC_INTERPOSE_FUNCTIONS +// dispatch_async, dispatch_group_async and others tailcall the corresponding +// dispatch_*_f functions. When wrapping functions with mach_override, those +// dispatch_*_f are intercepted automatically. But with dylib interposition +// this does not work, because the calls within the same library are not +// interposed. +// Therefore we need to re-implement dispatch_async and friends. + +extern "C" { +// FIXME: consolidate these declarations with asan_intercepted_functions.h. +void dispatch_async(dispatch_queue_t dq, void(^work)(void)); +void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, + void(^work)(void)); +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 = asanThreadRegistry().GetCurrentTidOrInvalid(); \ + asan_block = ^(void) { \ + GET_STACK_TRACE_HERE(kStackTraceMax); \ + asan_register_worker_thread(parent_tid, &stack); \ + work(); \ + } + +INTERCEPTOR(void, dispatch_async, + dispatch_queue_t dq, void(^work)(void)) { + 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)) { + 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)) { + 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)) { + 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)) { + GET_ASAN_BLOCK(work); + REAL(dispatch_source_set_event_handler)(ds, asan_block); +} +#endif + +// The following stuff has been extremely helpful while looking for the +// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity +// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to +// find the points of worker thread creation (each of such threads may be used +// to run several tasks, that's why this is not enough to support the whole +// libdispatch API. +extern "C" +void *wrap_workitem_func(void *arg) { + if (flags()->verbosity >= 2) { + Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self()); + } + asan_block_context_t *ctxt = (asan_block_context_t*)arg; + worker_t fn = (worker_t)(ctxt->func); + void *result = fn(ctxt->block); + GET_STACK_TRACE_HERE(kStackTraceMax); + asan_free(arg, &stack); + return result; +} + +INTERCEPTOR(int, pthread_workqueue_additem_np, pthread_workqueue_t workq, + void *(*workitem_func)(void *), void * workitem_arg, + pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) { + GET_STACK_TRACE_HERE(kStackTraceMax); + asan_block_context_t *asan_ctxt = + (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack); + asan_ctxt->block = workitem_arg; + asan_ctxt->func = (dispatch_function_t)workitem_func; + asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + if (flags()->verbosity >= 2) { + Report("pthread_workqueue_additem_np: %p\n", asan_ctxt); + PRINT_CURRENT_STACK(); + } + return REAL(pthread_workqueue_additem_np)(workq, wrap_workitem_func, + asan_ctxt, itemhandlep, + gencountp); +} + +// See http://opensource.apple.com/source/CF/CF-635.15/CFString.c +int __CFStrIsConstant(CFStringRef str) { + CFRuntimeBase *base = (CFRuntimeBase*)str; +#if __LP64__ + return base->_rc == 0; +#else + return (base->_cfinfo[CF_RC_BITS]) == 0; +#endif +} + +INTERCEPTOR(CFStringRef, CFStringCreateCopy, CFAllocatorRef alloc, + CFStringRef str) { + if (__CFStrIsConstant(str)) { + return str; + } else { + return REAL(CFStringCreateCopy)(alloc, str); + } +} + +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) + +DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize, void) + +namespace __asan { + +void InitializeMacInterceptors() { + CHECK(INTERCEPT_FUNCTION(dispatch_async_f)); + CHECK(INTERCEPT_FUNCTION(dispatch_sync_f)); + CHECK(INTERCEPT_FUNCTION(dispatch_after_f)); + CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f)); + CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f)); + // We don't need to intercept pthread_workqueue_additem_np() to support the + // libdispatch API, but it helps us to debug the unsupported functions. Let's + // intercept it only during verbose runs. + if (flags()->verbosity >= 2) { + CHECK(INTERCEPT_FUNCTION(pthread_workqueue_additem_np)); + } + // Normally CFStringCreateCopy should not copy constant CF strings. + // Replacing the default CFAllocator causes constant strings to be copied + // rather than just returned, which leads to bugs in big applications like + // Chromium and WebKit, see + // http://code.google.com/p/address-sanitizer/issues/detail?id=10 + // Until this problem is fixed we need to check that the string is + // non-constant before calling CFStringCreateCopy. + CHECK(INTERCEPT_FUNCTION(CFStringCreateCopy)); + // Some of the library functions call free() directly, so we have to + // intercept it. + CHECK(INTERCEPT_FUNCTION(free)); + if (flags()->replace_cfallocator) { + CHECK(INTERCEPT_FUNCTION(__CFInitialize)); + } +} + +} // namespace __asan + +#endif // __APPLE__ diff --git a/libsanitizer/asan/asan_mac.h b/libsanitizer/asan/asan_mac.h new file mode 100644 index 0000000..18aca0d --- /dev/null +++ b/libsanitizer/asan/asan_mac.h @@ -0,0 +1,54 @@ +//===-- asan_mac.h ----------------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Mac-specific ASan definitions. +//===----------------------------------------------------------------------===// +#ifndef ASAN_MAC_H +#define ASAN_MAC_H + +// CF_RC_BITS, the layout of CFRuntimeBase and __CFStrIsConstant are internal +// and subject to change in further CoreFoundation versions. Apple does not +// guarantee any binary compatibility from release to release. + +// See http://opensource.apple.com/source/CF/CF-635.15/CFInternal.h +#if defined(__BIG_ENDIAN__) +#define CF_RC_BITS 0 +#endif + +#if defined(__LITTLE_ENDIAN__) +#define CF_RC_BITS 3 +#endif + +// See http://opensource.apple.com/source/CF/CF-635.15/CFRuntime.h +typedef struct __CFRuntimeBase { + uptr _cfisa; + u8 _cfinfo[4]; +#if __LP64__ + u32 _rc; +#endif +} CFRuntimeBase; + +enum { + MACOS_VERSION_UNKNOWN = 0, + MACOS_VERSION_LEOPARD, + MACOS_VERSION_SNOW_LEOPARD, + MACOS_VERSION_LION +}; + +// Used by asan_malloc_mac.cc and asan_mac.cc +extern "C" void __CFInitialize(); + +namespace __asan { + +int GetMacosVersion(); +void ReplaceCFAllocator(); + +} // namespace __asan + +#endif // ASAN_MAC_H diff --git a/libsanitizer/asan/asan_malloc_linux.cc b/libsanitizer/asan/asan_malloc_linux.cc new file mode 100644 index 0000000..b00bbe5 --- /dev/null +++ b/libsanitizer/asan/asan_malloc_linux.cc @@ -0,0 +1,142 @@ +//===-- 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. +//===----------------------------------------------------------------------===// +#ifdef __linux__ + +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_stack.h" + +#if ASAN_ANDROID +DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size) +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) +DECLARE_REAL_AND_INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) +DECLARE_REAL_AND_INTERCEPTOR(void*, realloc, void *ptr, uptr size) +DECLARE_REAL_AND_INTERCEPTOR(void*, memalign, uptr boundary, uptr size) + +struct MallocDebug { + void* (*malloc)(uptr bytes); + void (*free)(void* mem); + void* (*calloc)(uptr n_elements, uptr elem_size); + void* (*realloc)(void* oldMem, uptr bytes); + void* (*memalign)(uptr alignment, uptr bytes); +}; + +const MallocDebug asan_malloc_dispatch ALIGNED(32) = { + WRAP(malloc), WRAP(free), WRAP(calloc), WRAP(realloc), WRAP(memalign) +}; + +extern "C" const MallocDebug* __libc_malloc_dispatch; + +namespace __asan { +void ReplaceSystemMalloc() { + __libc_malloc_dispatch = &asan_malloc_dispatch; +} +} // namespace __asan + +#else // ANDROID + +namespace __asan { +void ReplaceSystemMalloc() { +} +} // namespace __asan +#endif // ANDROID + +// ---------------------- Replacement functions ---------------- {{{1 +using namespace __asan; // NOLINT + +INTERCEPTOR(void, free, void *ptr) { + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + asan_free(ptr, &stack); +} + +INTERCEPTOR(void, cfree, void *ptr) { + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + asan_free(ptr, &stack); +} + +INTERCEPTOR(void*, malloc, uptr size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc(size, &stack); +} + +INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { + if (!asan_inited) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const uptr kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static uptr allocated; + uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +INTERCEPTOR(void*, realloc, void *ptr, uptr size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_realloc(ptr, size, &stack); +} + +INTERCEPTOR(void*, memalign, uptr boundary, uptr size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_memalign(boundary, size, &stack); +} + +INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s) + ALIAS("memalign"); + +INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc_usable_size(ptr, &stack); +} + +// We avoid including malloc.h for portability reasons. +// man mallinfo says the fields are "long", but the implementation uses int. +// It doesn't matter much -- we just need to make sure that the libc's mallinfo +// is not called. +struct fake_mallinfo { + int x[10]; +}; + +INTERCEPTOR(struct fake_mallinfo, mallinfo, void) { + struct fake_mallinfo res; + REAL(memset)(&res, 0, sizeof(res)); + return res; +} + +INTERCEPTOR(int, mallopt, int cmd, int value) { + return -1; +} + +INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + // Printf("posix_memalign: %zx %zu\n", alignment, size); + return asan_posix_memalign(memptr, alignment, size, &stack); +} + +INTERCEPTOR(void*, valloc, uptr size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_valloc(size, &stack); +} + +INTERCEPTOR(void*, pvalloc, uptr size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_pvalloc(size, &stack); +} + +#endif // __linux__ diff --git a/libsanitizer/asan/asan_malloc_mac.cc b/libsanitizer/asan/asan_malloc_mac.cc new file mode 100644 index 0000000..2df3484 --- /dev/null +++ b/libsanitizer/asan/asan_malloc_mac.cc @@ -0,0 +1,427 @@ +//===-- 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. +//===----------------------------------------------------------------------===// + +#ifdef __APPLE__ + +#include <AvailabilityMacros.h> +#include <CoreFoundation/CFBase.h> +#include <dlfcn.h> +#include <malloc/malloc.h> + +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mac.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "asan_thread_registry.h" + +// Similar code is used in Google Perftools, +// http://code.google.com/p/google-perftools. + +// ---------------------- Replacement functions ---------------- {{{1 +using namespace __asan; // NOLINT + +// TODO(glider): do we need both zones? +static malloc_zone_t *system_malloc_zone = 0; +static malloc_zone_t *system_purgeable_zone = 0; +static malloc_zone_t asan_zone; +CFAllocatorRef cf_asan = 0; + +// _CFRuntimeCreateInstance() checks whether the supplied allocator is +// kCFAllocatorSystemDefault and, if it is not, stores the allocator reference +// at the beginning of the allocated memory and returns the pointer to the +// allocated memory plus sizeof(CFAllocatorRef). See +// http://www.opensource.apple.com/source/CF/CF-635.21/CFRuntime.c +// Pointers returned by _CFRuntimeCreateInstance() can then be passed directly +// to free() or CFAllocatorDeallocate(), which leads to false invalid free +// reports. +// The corresponding rdar bug is http://openradar.appspot.com/radar?id=1796404. +void* ALWAYS_INLINE get_saved_cfallocator_ref(void *ptr) { + if (flags()->replace_cfallocator) { + // Make sure we're not hitting the previous page. This may be incorrect + // if ASan's malloc returns an address ending with 0xFF8, which will be + // then padded to a page boundary with a CFAllocatorRef. + uptr arith_ptr = (uptr)ptr; + if ((arith_ptr & 0xFFF) > sizeof(CFAllocatorRef)) { + CFAllocatorRef *saved = + (CFAllocatorRef*)(arith_ptr - sizeof(CFAllocatorRef)); + if ((*saved == cf_asan) && asan_mz_size(saved)) ptr = (void*)saved; + } + } + return ptr; +} + +// The free() implementation provided by OS X calls malloc_zone_from_ptr() +// to find the owner of |ptr|. If the result is 0, an invalid free() is +// reported. Our implementation falls back to asan_free() in this case +// in order to print an ASan-style report. +// +// For the objects created by _CFRuntimeCreateInstance a CFAllocatorRef is +// placed at the beginning of the allocated chunk and the pointer returned by +// our allocator is off by sizeof(CFAllocatorRef). This pointer can be then +// passed directly to free(), which will lead to errors. +// To overcome this we're checking whether |ptr-sizeof(CFAllocatorRef)| +// contains a pointer to our CFAllocator (assuming no other allocator is used). +// See http://code.google.com/p/address-sanitizer/issues/detail?id=70 for more +// info. +INTERCEPTOR(void, free, void *ptr) { + malloc_zone_t *zone = malloc_zone_from_ptr(ptr); + if (zone) { +#if defined(MAC_OS_X_VERSION_10_6) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + if ((zone->version >= 6) && (zone->free_definite_size)) { + zone->free_definite_size(zone, ptr, malloc_size(ptr)); + } else { + malloc_zone_free(zone, ptr); + } +#else + malloc_zone_free(zone, ptr); +#endif + } else { + if (!asan_mz_size(ptr)) ptr = get_saved_cfallocator_ref(ptr); + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + asan_free(ptr, &stack); + } +} + +namespace __asan { + void ReplaceCFAllocator(); +} + +// We can't always replace the default CFAllocator with cf_asan right in +// ReplaceSystemMalloc(), because it is sometimes called before +// __CFInitialize(), when the default allocator is invalid and replacing it may +// crash the program. Instead we wait for the allocator to initialize and jump +// in just after __CFInitialize(). Nobody is going to allocate memory using +// CFAllocators before that, so we won't miss anything. +// +// See http://code.google.com/p/address-sanitizer/issues/detail?id=87 +// and http://opensource.apple.com/source/CF/CF-550.43/CFRuntime.c +INTERCEPTOR(void, __CFInitialize, void) { + // If the runtime is built as dynamic library, __CFInitialize wrapper may be + // called before __asan_init. +#if !MAC_INTERPOSE_FUNCTIONS + CHECK(flags()->replace_cfallocator); + CHECK(asan_inited); +#endif + REAL(__CFInitialize)(); + if (!cf_asan && asan_inited) ReplaceCFAllocator(); +} + +namespace { + +// TODO(glider): the mz_* functions should be united with the Linux wrappers, +// as they are basically copied from there. +size_t mz_size(malloc_zone_t* zone, const void* ptr) { + return asan_mz_size(ptr); +} + +void *mz_malloc(malloc_zone_t *zone, size_t size) { + if (!asan_inited) { + CHECK(system_malloc_zone); + return malloc_zone_malloc(system_malloc_zone, size); + } + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc(size, &stack); +} + +void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) { + if (!asan_inited) { + CHECK(system_malloc_zone); + return malloc_zone_malloc(system_malloc_zone, size); + } + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc(size, &stack); +} + +void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { + if (!asan_inited) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const size_t kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static size_t allocated; + size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +void *mz_valloc(malloc_zone_t *zone, size_t size) { + if (!asan_inited) { + CHECK(system_malloc_zone); + return malloc_zone_valloc(system_malloc_zone, size); + } + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_memalign(kPageSize, size, &stack); +} + +#define GET_ZONE_FOR_PTR(ptr) \ + malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \ + const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name + +void ALWAYS_INLINE free_common(void *context, void *ptr) { + if (!ptr) return; + if (asan_mz_size(ptr)) { + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + asan_free(ptr, &stack); + } else { + // If the pointer does not belong to any of the zones, use one of the + // fallback methods to free memory. + malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); + if (zone_ptr == system_purgeable_zone) { + // allocations from malloc_default_purgeable_zone() done before + // __asan_init() may be occasionally freed via free_common(). + // see http://code.google.com/p/address-sanitizer/issues/detail?id=99. + malloc_zone_free(zone_ptr, ptr); + } else { + // If the memory chunk pointer was moved to store additional + // CFAllocatorRef, fix it back. + ptr = get_saved_cfallocator_ref(ptr); + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + if (!flags()->mac_ignore_invalid_free) { + asan_free(ptr, &stack); + } else { + GET_ZONE_FOR_PTR(ptr); + WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); + return; + } + } + } +} + +// TODO(glider): the allocation callbacks need to be refactored. +void mz_free(malloc_zone_t *zone, void *ptr) { + free_common(zone, ptr); +} + +void cf_free(void *ptr, void *info) { + free_common(info, ptr); +} + +void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { + if (!ptr) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc(size, &stack); + } else { + if (asan_mz_size(ptr)) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_realloc(ptr, size, &stack); + } else { + // We can't recover from reallocating an unknown address, because + // this would require reading at most |size| bytes from + // potentially unaccessible memory. + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + GET_ZONE_FOR_PTR(ptr); + ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); + } + } +} + +void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) { + if (!ptr) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc(size, &stack); + } else { + if (asan_mz_size(ptr)) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_realloc(ptr, size, &stack); + } else { + // We can't recover from reallocating an unknown address, because + // this would require reading at most |size| bytes from + // potentially unaccessible memory. + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + GET_ZONE_FOR_PTR(ptr); + ReportMacCfReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); + } + } +} + +void mz_destroy(malloc_zone_t* zone) { + // A no-op -- we will not be destroyed! + Printf("mz_destroy() called -- ignoring\n"); +} + // from AvailabilityMacros.h +#if defined(MAC_OS_X_VERSION_10_6) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { + if (!asan_inited) { + CHECK(system_malloc_zone); + return malloc_zone_memalign(system_malloc_zone, align, size); + } + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_memalign(align, size, &stack); +} + +// This function is currently unused, and we build with -Werror. +#if 0 +void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) { + // TODO(glider): check that |size| is valid. + UNIMPLEMENTED(); +} +#endif +#endif + +kern_return_t mi_enumerator(task_t task, void *, + unsigned type_mask, vm_address_t zone_address, + memory_reader_t reader, + vm_range_recorder_t recorder) { + // Should enumerate all the pointers we have. Seems like a lot of work. + return KERN_FAILURE; +} + +size_t mi_good_size(malloc_zone_t *zone, size_t size) { + // I think it's always safe to return size, but we maybe could do better. + return size; +} + +boolean_t mi_check(malloc_zone_t *zone) { + UNIMPLEMENTED(); + return true; +} + +void mi_print(malloc_zone_t *zone, boolean_t verbose) { + UNIMPLEMENTED(); + return; +} + +void mi_log(malloc_zone_t *zone, void *address) { + // I don't think we support anything like this +} + +void mi_force_lock(malloc_zone_t *zone) { + asan_mz_force_lock(); +} + +void mi_force_unlock(malloc_zone_t *zone) { + asan_mz_force_unlock(); +} + +void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { + AsanMallocStats malloc_stats; + asanThreadRegistry().FillMallocStatistics(&malloc_stats); + CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); + internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); +} + +#if defined(MAC_OS_X_VERSION_10_6) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +boolean_t mi_zone_locked(malloc_zone_t *zone) { + // UNIMPLEMENTED(); + return false; +} +#endif + +} // unnamed namespace + +extern int __CFRuntimeClassTableSize; + +namespace __asan { +void ReplaceCFAllocator() { + static CFAllocatorContext asan_context = { + /*version*/ 0, /*info*/ &asan_zone, + /*retain*/ 0, /*release*/ 0, + /*copyDescription*/0, + /*allocate*/ &cf_malloc, + /*reallocate*/ &cf_realloc, + /*deallocate*/ &cf_free, + /*preferredSize*/ 0 }; + if (!cf_asan) + cf_asan = CFAllocatorCreate(kCFAllocatorUseContext, &asan_context); + if (CFAllocatorGetDefault() != cf_asan) + CFAllocatorSetDefault(cf_asan); +} + +void ReplaceSystemMalloc() { + static malloc_introspection_t asan_introspection; + // Ok to use internal_memset, these places are not performance-critical. + internal_memset(&asan_introspection, 0, sizeof(asan_introspection)); + + asan_introspection.enumerator = &mi_enumerator; + asan_introspection.good_size = &mi_good_size; + asan_introspection.check = &mi_check; + asan_introspection.print = &mi_print; + asan_introspection.log = &mi_log; + asan_introspection.force_lock = &mi_force_lock; + asan_introspection.force_unlock = &mi_force_unlock; + asan_introspection.statistics = &mi_statistics; + + internal_memset(&asan_zone, 0, sizeof(malloc_zone_t)); + + // Start with a version 4 zone which is used for OS X 10.4 and 10.5. + asan_zone.version = 4; + asan_zone.zone_name = "asan"; + asan_zone.size = &mz_size; + asan_zone.malloc = &mz_malloc; + asan_zone.calloc = &mz_calloc; + asan_zone.valloc = &mz_valloc; + asan_zone.free = &mz_free; + asan_zone.realloc = &mz_realloc; + asan_zone.destroy = &mz_destroy; + asan_zone.batch_malloc = 0; + asan_zone.batch_free = 0; + asan_zone.introspect = &asan_introspection; + + // from AvailabilityMacros.h +#if defined(MAC_OS_X_VERSION_10_6) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + // Switch to version 6 on OSX 10.6 to support memalign. + asan_zone.version = 6; + asan_zone.free_definite_size = 0; + asan_zone.memalign = &mz_memalign; + asan_introspection.zone_locked = &mi_zone_locked; + + // Request the default purgable zone to force its creation. The + // current default zone is registered with the purgable zone for + // doing tiny and small allocs. Sadly, it assumes that the default + // zone is the szone implementation from OS X and will crash if it + // isn't. By creating the zone now, this will be true and changing + // the default zone won't cause a problem. (OS X 10.6 and higher.) + system_purgeable_zone = malloc_default_purgeable_zone(); +#endif + + // Register the ASan zone. At this point, it will not be the + // default zone. + malloc_zone_register(&asan_zone); + + // Unregister and reregister the default zone. Unregistering swaps + // the specified zone with the last one registered which for the + // default zone makes the more recently registered zone the default + // zone. The default zone is then re-registered to ensure that + // allocations made from it earlier will be handled correctly. + // Things are not guaranteed to work that way, but it's how they work now. + system_malloc_zone = malloc_default_zone(); + malloc_zone_unregister(system_malloc_zone); + malloc_zone_register(system_malloc_zone); + // Make sure the default allocator was replaced. + CHECK(malloc_default_zone() == &asan_zone); + + if (flags()->replace_cfallocator) { + // If __CFInitialize() hasn't been called yet, cf_asan will be created and + // installed as the default allocator after __CFInitialize() finishes (see + // the interceptor for __CFInitialize() above). Otherwise install cf_asan + // right now. On both Snow Leopard and Lion __CFInitialize() calls + // __CFAllocatorInitialize(), which initializes the _base._cfisa field of + // the default allocators we check here. + if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) { + ReplaceCFAllocator(); + } + } +} +} // namespace __asan + +#endif // __APPLE__ diff --git a/libsanitizer/asan/asan_malloc_win.cc b/libsanitizer/asan/asan_malloc_win.cc new file mode 100644 index 0000000..7389a25 --- /dev/null +++ b/libsanitizer/asan/asan_malloc_win.cc @@ -0,0 +1,140 @@ +//===-- 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. +//===----------------------------------------------------------------------===// +#ifdef _WIN32 + +#include "asan_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_stack.h" +#include "interception/interception.h" + +#include <stddef.h> + +// ---------------------- Replacement functions ---------------- {{{1 +using namespace __asan; // NOLINT + +// FIXME: Simply defining functions with the same signature in *.obj +// files overrides the standard functions in *.lib +// This works well for simple helloworld-like tests but might need to be +// revisited in the future. + +extern "C" { +void free(void *ptr) { + GET_STACK_TRACE_HERE_FOR_FREE(ptr); + return asan_free(ptr, &stack); +} + +void _free_dbg(void* ptr, int) { + free(ptr); +} + +void cfree(void *ptr) { + CHECK(!"cfree() should not be used on Windows?"); +} + +void *malloc(size_t size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc(size, &stack); +} + +void* _malloc_dbg(size_t size, int , const char*, int) { + return malloc(size); +} + +void *calloc(size_t nmemb, size_t size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_calloc(nmemb, size, &stack); +} + +void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { + return calloc(n, size); +} + +void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { + return calloc(nmemb, size); +} + +void *realloc(void *ptr, size_t size) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_realloc(ptr, size, &stack); +} + +void *_realloc_dbg(void *ptr, size_t size, int) { + CHECK(!"_realloc_dbg should not exist!"); + return 0; +} + +void* _recalloc(void* p, size_t n, size_t elem_size) { + if (!p) + return calloc(n, elem_size); + const size_t size = n * elem_size; + if (elem_size != 0 && size / elem_size != n) + return 0; + return realloc(p, size); +} + +size_t _msize(void *ptr) { + GET_STACK_TRACE_HERE_FOR_MALLOC; + return asan_malloc_usable_size(ptr, &stack); +} + +int _CrtDbgReport(int, const char*, int, + const char*, const char*, ...) { + ShowStatsAndAbort(); +} + +int _CrtDbgReportW(int reportType, const wchar_t*, int, + const wchar_t*, const wchar_t*, ...) { + ShowStatsAndAbort(); +} + +int _CrtSetReportMode(int, int) { + return 0; +} +} // extern "C" + +using __interception::GetRealFunctionAddress; + +// We don't want to include "windows.h" in this file to avoid extra attributes +// set on malloc/free etc (e.g. dllimport), so declare a few things manually: +extern "C" int __stdcall VirtualProtect(void* addr, size_t size, + DWORD prot, DWORD *old_prot); +const int PAGE_EXECUTE_READWRITE = 0x40; + +namespace __asan { +void ReplaceSystemMalloc() { +#if defined(_DLL) +# ifdef _WIN64 +# error ReplaceSystemMalloc was not tested on x64 +# endif + char *crt_malloc; + if (GetRealFunctionAddress("malloc", (void**)&crt_malloc)) { + // Replace malloc in the CRT dll with a jump to our malloc. + DWORD old_prot, unused; + CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot)); + REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16); // just in case. + + ptrdiff_t jmp_offset = (char*)malloc - (char*)crt_malloc - 5; + crt_malloc[0] = 0xE9; // jmp, should be followed by an offset. + REAL(memcpy)(crt_malloc + 1, &jmp_offset, sizeof(jmp_offset)); + + CHECK(VirtualProtect(crt_malloc, 16, old_prot, &unused)); + + // FYI: FlushInstructionCache is needed on Itanium etc but not on x86/x64. + } + + // FIXME: investigate whether anything else is needed. +#endif +} +} // namespace __asan + +#endif // _WIN32 diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h new file mode 100644 index 0000000..40a38db --- /dev/null +++ b/libsanitizer/asan/asan_mapping.h @@ -0,0 +1,120 @@ +//===-- asan_mapping.h ------------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Defines ASan memory mapping. +//===----------------------------------------------------------------------===// +#ifndef ASAN_MAPPING_H +#define ASAN_MAPPING_H + +#include "asan_internal.h" + +// The full explanation of the memory mapping could be found here: +// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm + +#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 +extern __attribute__((visibility("default"))) uptr __asan_mapping_scale; +extern __attribute__((visibility("default"))) uptr __asan_mapping_offset; +# define SHADOW_SCALE (__asan_mapping_scale) +# define SHADOW_OFFSET (__asan_mapping_offset) +#else +# if ASAN_ANDROID +# define SHADOW_SCALE (3) +# define SHADOW_OFFSET (0) +# else +# define SHADOW_SCALE (3) +# if __WORDSIZE == 32 +# define SHADOW_OFFSET (1 << 29) +# else +# define SHADOW_OFFSET (1ULL << 44) +# endif +# endif +#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET + +#define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE) +#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET)) +#define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE) + +#if __WORDSIZE == 64 + static const uptr kHighMemEnd = 0x00007fffffffffffUL; +#else // __WORDSIZE == 32 + static const uptr kHighMemEnd = 0xffffffff; +#endif // __WORDSIZE + + +#define kLowMemBeg 0 +#define kLowMemEnd (SHADOW_OFFSET ? SHADOW_OFFSET - 1 : 0) + +#define kLowShadowBeg SHADOW_OFFSET +#define kLowShadowEnd MEM_TO_SHADOW(kLowMemEnd) + +#define kHighMemBeg (MEM_TO_SHADOW(kHighMemEnd) + 1) + +#define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg) +#define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd) + +#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 : 16 * kPageSize) +#define kShadowGapEnd (kHighShadowBeg - 1) + +#define kGlobalAndStackRedzone \ + (SHADOW_GRANULARITY < 32 ? 32 : SHADOW_GRANULARITY) + +namespace __asan { + +static inline bool AddrIsInLowMem(uptr a) { + return a < kLowMemEnd; +} + +static inline bool AddrIsInLowShadow(uptr a) { + return a >= kLowShadowBeg && a <= kLowShadowEnd; +} + +static inline bool AddrIsInHighMem(uptr a) { + return a >= kHighMemBeg && a <= kHighMemEnd; +} + +static inline bool AddrIsInMem(uptr a) { + return AddrIsInLowMem(a) || AddrIsInHighMem(a); +} + +static inline uptr MemToShadow(uptr p) { + CHECK(AddrIsInMem(p)); + return MEM_TO_SHADOW(p); +} + +static inline bool AddrIsInHighShadow(uptr a) { + return a >= kHighShadowBeg && a <= kHighMemEnd; +} + +static inline bool AddrIsInShadow(uptr a) { + return AddrIsInLowShadow(a) || AddrIsInHighShadow(a); +} + +static inline bool AddrIsInShadowGap(uptr a) { + return a >= kShadowGapBeg && a <= kShadowGapEnd; +} + +static inline bool AddrIsAlignedByGranularity(uptr a) { + return (a & (SHADOW_GRANULARITY - 1)) == 0; +} + +static inline bool AddressIsPoisoned(uptr a) { + const uptr kAccessSize = 1; + u8 *shadow_address = (u8*)MemToShadow(a); + s8 shadow_value = *shadow_address; + if (shadow_value) { + u8 last_accessed_byte = (a & (SHADOW_GRANULARITY - 1)) + + kAccessSize - 1; + return (last_accessed_byte >= shadow_value); + } + return false; +} + +} // namespace __asan + +#endif // ASAN_MAPPING_H diff --git a/libsanitizer/asan/asan_new_delete.cc b/libsanitizer/asan/asan_new_delete.cc new file mode 100644 index 0000000..a2180ae --- /dev/null +++ b/libsanitizer/asan/asan_new_delete.cc @@ -0,0 +1,54 @@ +//===-- 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_stack.h" + +#include <stddef.h> +#include <new> + +namespace __asan { +// This function is a no-op. We need it to make sure that object file +// with our replacements will actually be loaded from static ASan +// run-time library at link-time. +void ReplaceOperatorsNewAndDelete() { } +} + +using namespace __asan; // NOLINT + +#define OPERATOR_NEW_BODY \ + GET_STACK_TRACE_HERE_FOR_MALLOC;\ + return asan_memalign(0, size, &stack); + +#if ASAN_ANDROID +void *operator new(size_t size) { OPERATOR_NEW_BODY; } +void *operator new[](size_t size) { OPERATOR_NEW_BODY; } +#else +void *operator new(size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; } +void *operator new[](size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; } +void *operator new(size_t size, std::nothrow_t const&) throw() +{ OPERATOR_NEW_BODY; } +void *operator new[](size_t size, std::nothrow_t const&) throw() +{ OPERATOR_NEW_BODY; } +#endif + +#define OPERATOR_DELETE_BODY \ + GET_STACK_TRACE_HERE_FOR_FREE(ptr);\ + asan_free(ptr, &stack); + +void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; } +void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; } +void operator delete(void *ptr, std::nothrow_t const&) throw() +{ OPERATOR_DELETE_BODY; } +void operator delete[](void *ptr, std::nothrow_t const&) throw() +{ OPERATOR_DELETE_BODY; } diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc new file mode 100644 index 0000000..716be7e --- /dev/null +++ b/libsanitizer/asan/asan_poisoning.cc @@ -0,0 +1,151 @@ +//===-- 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_interceptors.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "sanitizer/asan_interface.h" + +namespace __asan { + +void PoisonShadow(uptr addr, uptr size, u8 value) { + CHECK(AddrIsAlignedByGranularity(addr)); + CHECK(AddrIsAlignedByGranularity(addr + size)); + uptr shadow_beg = MemToShadow(addr); + uptr shadow_end = MemToShadow(addr + size); + CHECK(REAL(memset) != 0); + REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); +} + +void PoisonShadowPartialRightRedzone(uptr addr, + uptr size, + uptr redzone_size, + u8 value) { + CHECK(AddrIsAlignedByGranularity(addr)); + u8 *shadow = (u8*)MemToShadow(addr); + for (uptr i = 0; i < redzone_size; + i += SHADOW_GRANULARITY, shadow++) { + if (i + SHADOW_GRANULARITY <= size) { + *shadow = 0; // fully addressable + } else if (i >= size) { + *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable + } else { + *shadow = size - i; // first size-i bytes are addressable + } + } +} + + +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; + } +}; + +} // 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: +// http://code.google.com/p/address-sanitizer/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; + if (flags()->verbosity >= 1) { + Printf("Trying to poison memory region [%p, %p)\n", + (void*)beg_addr, (void*)end_addr); + } + ShadowSegmentEndpoint beg(beg_addr); + ShadowSegmentEndpoint end(end_addr); + if (beg.chunk == end.chunk) { + CHECK(beg.offset < end.offset); + s8 value = beg.value; + CHECK(value == end.value); + // We can only poison memory if the byte in end.offset is unaddressable. + // No need to re-poison memory if it is poisoned already. + if (value > 0 && value <= end.offset) { + if (beg.offset > 0) { + *beg.chunk = Min(value, beg.offset); + } else { + *beg.chunk = kAsanUserPoisonedMemoryMagic; + } + } + return; + } + CHECK(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; + if (flags()->verbosity >= 1) { + Printf("Trying to unpoison memory region [%p, %p)\n", + (void*)beg_addr, (void*)end_addr); + } + ShadowSegmentEndpoint beg(beg_addr); + ShadowSegmentEndpoint end(end_addr); + if (beg.chunk == end.chunk) { + CHECK(beg.offset < end.offset); + s8 value = beg.value; + CHECK(value == end.value); + // We unpoison memory bytes up to enbytes up to end.offset if it is not + // unpoisoned already. + if (value != 0) { + *beg.chunk = Max(value, end.offset); + } + return; + } + CHECK(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); + } +} + +bool __asan_address_is_poisoned(void const volatile *addr) { + return __asan::AddressIsPoisoned((uptr)addr); +} diff --git a/libsanitizer/asan/asan_posix.cc b/libsanitizer/asan/asan_posix.cc new file mode 100644 index 0000000..177b84a --- /dev/null +++ b/libsanitizer/asan/asan_posix.cc @@ -0,0 +1,118 @@ +//===-- 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. +// +// Posix-specific details. +//===----------------------------------------------------------------------===// +#if defined(__linux__) || defined(__APPLE__) + +#include "asan_internal.h" +#include "asan_interceptors.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_thread_registry.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_procmaps.h" + +#include <pthread.h> +#include <signal.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> + +static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough. + +namespace __asan { + +static void MaybeInstallSigaction(int signum, + void (*handler)(int, siginfo_t *, void *)) { + if (!AsanInterceptsSignal(signum)) + return; + struct sigaction sigact; + REAL(memset)(&sigact, 0, sizeof(sigact)); + sigact.sa_sigaction = handler; + sigact.sa_flags = SA_SIGINFO; + if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; + CHECK(0 == REAL(sigaction)(signum, &sigact, 0)); + if (flags()->verbosity >= 1) { + Report("Installed the sigaction for signal %d\n", signum); + } +} + +static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) { + uptr addr = (uptr)siginfo->si_addr; + // Write the first message using the bullet-proof write. + if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die(); + uptr pc, sp, bp; + GetPcSpBp(context, &pc, &sp, &bp); + ReportSIGSEGV(pc, sp, bp, addr); +} + +void SetAlternateSignalStack() { + stack_t altstack, oldstack; + CHECK(0 == sigaltstack(0, &oldstack)); + // If the alternate stack is already in place, do nothing. + if ((oldstack.ss_flags & SS_DISABLE) == 0) return; + // TODO(glider): the mapped stack should have the MAP_STACK flag in the + // future. It is not required by man 2 sigaltstack now (they're using + // malloc()). + void* base = MmapOrDie(kAltStackSize, __FUNCTION__); + altstack.ss_sp = base; + altstack.ss_flags = 0; + altstack.ss_size = kAltStackSize; + CHECK(0 == sigaltstack(&altstack, 0)); + if (flags()->verbosity > 0) { + Report("Alternative stack for T%d set: [%p,%p)\n", + asanThreadRegistry().GetCurrentTidOrInvalid(), + altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size); + } +} + +void UnsetAlternateSignalStack() { + stack_t altstack, oldstack; + altstack.ss_sp = 0; + altstack.ss_flags = SS_DISABLE; + altstack.ss_size = 0; + CHECK(0 == sigaltstack(&altstack, &oldstack)); + UnmapOrDie(oldstack.ss_sp, oldstack.ss_size); +} + +void InstallSignalHandlers() { + // Set the alternate signal stack for the main thread. + // This will cause SetAlternateSignalStack to be called twice, but the stack + // will be actually set only once. + if (flags()->use_sigaltstack) SetAlternateSignalStack(); + MaybeInstallSigaction(SIGSEGV, ASAN_OnSIGSEGV); + MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV); +} + +// ---------------------- TSD ---------------- {{{1 + +static pthread_key_t tsd_key; +static bool tsd_key_inited = false; +void AsanTSDInit(void (*destructor)(void *tsd)) { + CHECK(!tsd_key_inited); + tsd_key_inited = true; + CHECK(0 == pthread_key_create(&tsd_key, destructor)); +} + +void *AsanTSDGet() { + CHECK(tsd_key_inited); + return pthread_getspecific(tsd_key); +} + +void AsanTSDSet(void *tsd) { + CHECK(tsd_key_inited); + pthread_setspecific(tsd_key, tsd); +} + +} // namespace __asan + +#endif // __linux__ || __APPLE_ diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc new file mode 100644 index 0000000..7e6381c --- /dev/null +++ b/libsanitizer/asan/asan_report.cc @@ -0,0 +1,492 @@ +//===-- 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_flags.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "asan_thread_registry.h" + +namespace __asan { + +// -------------------- User-specified callbacks ----------------- {{{1 +static void (*error_report_callback)(const char*); +static char *error_message_buffer = 0; +static uptr error_message_buffer_pos = 0; +static uptr error_message_buffer_size = 0; + +void AppendToErrorMessageBuffer(const char *buffer) { + if (error_message_buffer) { + uptr length = internal_strlen(buffer); + CHECK_GE(error_message_buffer_size, error_message_buffer_pos); + uptr remaining = error_message_buffer_size - error_message_buffer_pos; + internal_strncpy(error_message_buffer + error_message_buffer_pos, + buffer, remaining); + error_message_buffer[error_message_buffer_size - 1] = '\0'; + // FIXME: reallocate the buffer instead of truncating the message. + error_message_buffer_pos += remaining > length ? length : remaining; + } +} + +// ---------------------- Helper functions ----------------------- {{{1 + +static void PrintBytes(const char *before, uptr *a) { + u8 *bytes = (u8*)a; + uptr byte_num = (__WORDSIZE) / 8; + Printf("%s%p:", before, (void*)a); + for (uptr i = 0; i < byte_num; i++) { + Printf(" %x%x", bytes[i] >> 4, bytes[i] & 15); + } + Printf("\n"); +} + +static void PrintShadowMemoryForAddress(uptr addr) { + if (!AddrIsInMem(addr)) + return; + uptr shadow_addr = MemToShadow(addr); + Printf("Shadow byte and word:\n"); + Printf(" %p: %x\n", (void*)shadow_addr, *(unsigned char*)shadow_addr); + uptr aligned_shadow = shadow_addr & ~(kWordSize - 1); + PrintBytes(" ", (uptr*)(aligned_shadow)); + Printf("More shadow bytes:\n"); + for (int i = -4; i <= 4; i++) { + const char *prefix = (i == 0) ? "=>" : " "; + PrintBytes(prefix, (uptr*)(aligned_shadow + i * kWordSize)); + } +} + +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 + +static bool IsASCII(unsigned char c) { + return /*0x00 <= c &&*/ c <= 0x7F; +} + +// Check if the global is a zero-terminated ASCII string. If so, print it. +static void PrintGlobalNameIfASCII(const __asan_global &g) { + for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { + if (!IsASCII(*(unsigned char*)p)) return; + } + if (*(char*)(g.beg + g.size - 1) != 0) return; + Printf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg); +} + +bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g) { + if (addr < g.beg - kGlobalAndStackRedzone) return false; + if (addr >= g.beg + g.size_with_redzone) return false; + Printf("%p is located ", (void*)addr); + if (addr < g.beg) { + Printf("%zd bytes to the left", g.beg - addr); + } else if (addr >= g.beg + g.size) { + Printf("%zd bytes to the right", addr - (g.beg + g.size)); + } else { + Printf("%zd bytes inside", addr - g.beg); // Can it happen? + } + Printf(" of global variable '%s' (0x%zx) of size %zu\n", + g.name, g.beg, g.size); + PrintGlobalNameIfASCII(g); + return true; +} + +bool DescribeAddressIfShadow(uptr addr) { + if (AddrIsInMem(addr)) + return false; + static const char kAddrInShadowReport[] = + "Address %p is located in the %s.\n"; + if (AddrIsInShadowGap(addr)) { + Printf(kAddrInShadowReport, addr, "shadow gap area"); + return true; + } + if (AddrIsInHighShadow(addr)) { + Printf(kAddrInShadowReport, addr, "high shadow area"); + return true; + } + if (AddrIsInLowShadow(addr)) { + Printf(kAddrInShadowReport, addr, "low shadow area"); + return true; + } + CHECK(0 && "Address is not in memory and not in shadow?"); + return false; +} + +bool DescribeAddressIfStack(uptr addr, uptr access_size) { + AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr); + if (!t) return false; + const sptr kBufSize = 4095; + char buf[kBufSize]; + uptr offset = 0; + const char *frame_descr = t->GetFrameNameByAddr(addr, &offset); + // This string is created by the compiler and has the following form: + // "FunctioName n alloc_1 alloc_2 ... alloc_n" + // where alloc_i looks like "offset size len ObjectName ". + CHECK(frame_descr); + // Report the function name and the offset. + const char *name_end = internal_strchr(frame_descr, ' '); + CHECK(name_end); + buf[0] = 0; + internal_strncat(buf, frame_descr, + Min(kBufSize, + static_cast<sptr>(name_end - frame_descr))); + Printf("Address %p is located at offset %zu " + "in frame <%s> of T%d's stack:\n", + (void*)addr, offset, buf, t->tid()); + // Report the number of stack objects. + char *p; + uptr n_objects = internal_simple_strtoll(name_end, &p, 10); + CHECK(n_objects > 0); + 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 beg, size; + sptr len; + beg = internal_simple_strtoll(p, &p, 10); + size = internal_simple_strtoll(p, &p, 10); + len = internal_simple_strtoll(p, &p, 10); + if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') { + Printf("AddressSanitizer can't parse the stack frame " + "descriptor: |%s|\n", frame_descr); + break; + } + p++; + buf[0] = 0; + internal_strncat(buf, p, Min(kBufSize, len)); + p += len; + Printf(" [%zu, %zu) '%s'\n", beg, beg + size, buf); + } + Printf("HINT: this may be a false positive if your program uses " + "some custom stack unwind mechanism\n" + " (longjmp and C++ exceptions *are* supported)\n"); + DescribeThread(t->summary()); + return true; +} + +static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, + uptr access_size) { + uptr offset; + Printf("%p is located ", (void*)addr); + if (chunk.AddrIsInside(addr, access_size, &offset)) { + Printf("%zu bytes inside of", offset); + } else if (chunk.AddrIsAtLeft(addr, access_size, &offset)) { + Printf("%zu bytes to the left of", offset); + } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) { + Printf("%zu bytes to the right of", offset); + } else { + Printf(" somewhere around (this is AddressSanitizer bug!)"); + } + Printf(" %zu-byte region [%p,%p)\n", chunk.UsedSize(), + (void*)(chunk.Beg()), (void*)(chunk.End())); +} + +void DescribeHeapAddress(uptr addr, uptr access_size) { + AsanChunkView chunk = FindHeapChunkByAddress(addr); + if (!chunk.IsValid()) return; + DescribeAccessToHeapChunk(chunk, addr, access_size); + CHECK(chunk.AllocTid() != kInvalidTid); + AsanThreadSummary *alloc_thread = + asanThreadRegistry().FindByTid(chunk.AllocTid()); + StackTrace alloc_stack; + chunk.GetAllocStack(&alloc_stack); + AsanThread *t = asanThreadRegistry().GetCurrent(); + CHECK(t); + if (chunk.FreeTid() != kInvalidTid) { + AsanThreadSummary *free_thread = + asanThreadRegistry().FindByTid(chunk.FreeTid()); + Printf("freed by thread T%d here:\n", free_thread->tid()); + StackTrace free_stack; + chunk.GetFreeStack(&free_stack); + PrintStack(&free_stack); + Printf("previously allocated by thread T%d here:\n", alloc_thread->tid()); + PrintStack(&alloc_stack); + DescribeThread(t->summary()); + DescribeThread(free_thread); + DescribeThread(alloc_thread); + } else { + Printf("allocated by thread T%d here:\n", alloc_thread->tid()); + PrintStack(&alloc_stack); + DescribeThread(t->summary()); + DescribeThread(alloc_thread); + } +} + +void DescribeAddress(uptr addr, uptr access_size) { + // Check if this is shadow or shadow gap. + if (DescribeAddressIfShadow(addr)) + return; + CHECK(AddrIsInMem(addr)); + if (DescribeAddressIfGlobal(addr)) + return; + if (DescribeAddressIfStack(addr, access_size)) + return; + // Assume it is a heap address. + DescribeHeapAddress(addr, access_size); +} + +// ------------------- Thread description -------------------- {{{1 + +void DescribeThread(AsanThreadSummary *summary) { + CHECK(summary); + // No need to announce the main thread. + if (summary->tid() == 0 || summary->announced()) { + return; + } + summary->set_announced(true); + Printf("Thread T%d created by T%d here:\n", + summary->tid(), summary->parent_tid()); + PrintStack(summary->stack()); + // Recursively described parent thread if needed. + if (flags()->print_full_thread_history) { + AsanThreadSummary *parent_summary = + asanThreadRegistry().FindByTid(summary->parent_tid()); + DescribeThread(parent_summary); + } +} + +// -------------------- Different kinds of reports ----------------- {{{1 + +// Use ScopedInErrorReport to run common actions just before and +// immediately after printing error report. +class ScopedInErrorReport { + public: + ScopedInErrorReport() { + static atomic_uint32_t num_calls; + static u32 reporting_thread_tid; + if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { + // Do not print more than one report, otherwise they will mix up. + // Error reporting functions shouldn't return at this situation, as + // they are defined as no-return. + Report("AddressSanitizer: while reporting a bug found another one." + "Ignoring.\n"); + u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + if (current_tid != reporting_thread_tid) { + // ASan found two bugs in different threads simultaneously. Sleep + // long enough to make sure that the thread which started to print + // an error report will finish doing it. + SleepForSeconds(Max(100, flags()->sleep_before_dying + 1)); + } + Die(); + } + __asan_on_error(); + reporting_thread_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + Printf("====================================================" + "=============\n"); + if (reporting_thread_tid != kInvalidTid) { + // We started reporting an error message. Stop using the fake stack + // in case we call an instrumented function from a symbolizer. + AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); + CHECK(curr_thread); + curr_thread->fake_stack().StopUsingFakeStack(); + } + } + // Destructor is NORETURN, as functions that report errors are. + NORETURN ~ScopedInErrorReport() { + // Make sure the current thread is announced. + AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); + if (curr_thread) { + DescribeThread(curr_thread->summary()); + } + // Print memory stats. + __asan_print_accumulated_stats(); + if (error_report_callback) { + error_report_callback(error_message_buffer); + } + Report("ABORTING\n"); + Die(); + } +}; + +void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { + ScopedInErrorReport in_report; + Report("ERROR: AddressSanitizer crashed on unknown address %p" + " (pc %p sp %p bp %p T%d)\n", + (void*)addr, (void*)pc, (void*)sp, (void*)bp, + asanThreadRegistry().GetCurrentTidOrInvalid()); + Printf("AddressSanitizer can not provide additional info.\n"); + GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp); + PrintStack(&stack); +} + +void ReportDoubleFree(uptr addr, StackTrace *stack) { + ScopedInErrorReport in_report; + Report("ERROR: AddressSanitizer attempting double-free on %p:\n", addr); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { + ScopedInErrorReport in_report; + Report("ERROR: AddressSanitizer attempting free on address " + "which was not malloc()-ed: %p\n", addr); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) { + ScopedInErrorReport in_report; + Report("ERROR: AddressSanitizer attempting to call " + "malloc_usable_size() for pointer which is " + "not owned: %p\n", addr); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { + ScopedInErrorReport in_report; + Report("ERROR: AddressSanitizer attempting to call " + "__asan_get_allocated_size() for pointer which is " + "not owned: %p\n", addr); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportStringFunctionMemoryRangesOverlap( + const char *function, const char *offset1, uptr length1, + const char *offset2, uptr length2, StackTrace *stack) { + ScopedInErrorReport in_report; + Report("ERROR: AddressSanitizer %s-param-overlap: " + "memory ranges [%p,%p) and [%p, %p) overlap\n", \ + function, offset1, offset1 + length1, offset2, offset2 + length2); + PrintStack(stack); + DescribeAddress((uptr)offset1, length1); + DescribeAddress((uptr)offset2, length2); +} + +// ----------------------- Mac-specific reports ----------------- {{{1 + +void WarnMacFreeUnallocated( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) { + // Just print a warning here. + Printf("free_common(%p) -- attempting to free unallocated memory.\n" + "AddressSanitizer is ignoring this error on Mac OS now.\n", + addr); + PrintZoneForPointer(addr, zone_ptr, zone_name); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportMacMzReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) { + ScopedInErrorReport in_report; + Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" + "This is an unrecoverable problem, exiting now.\n", + addr); + PrintZoneForPointer(addr, zone_ptr, zone_name); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +void ReportMacCfReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) { + ScopedInErrorReport in_report; + Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n" + "This is an unrecoverable problem, exiting now.\n", + addr); + PrintZoneForPointer(addr, zone_ptr, zone_name); + PrintStack(stack); + DescribeHeapAddress(addr, 1); +} + +} // namespace __asan + +// --------------------------- Interface --------------------- {{{1 +using namespace __asan; // NOLINT + +void __asan_report_error(uptr pc, uptr bp, uptr sp, + uptr addr, bool is_write, uptr access_size) { + ScopedInErrorReport in_report; + + // Determine the error type. + const char *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++; + switch (*shadow_addr) { + case kAsanHeapLeftRedzoneMagic: + case kAsanHeapRightRedzoneMagic: + bug_descr = "heap-buffer-overflow"; + break; + case kAsanHeapFreeMagic: + bug_descr = "heap-use-after-free"; + break; + case kAsanStackLeftRedzoneMagic: + bug_descr = "stack-buffer-underflow"; + break; + case kAsanInitializationOrderMagic: + bug_descr = "initialization-order-fiasco"; + break; + case kAsanStackMidRedzoneMagic: + case kAsanStackRightRedzoneMagic: + case kAsanStackPartialRedzoneMagic: + bug_descr = "stack-buffer-overflow"; + break; + case kAsanStackAfterReturnMagic: + bug_descr = "stack-use-after-return"; + break; + case kAsanUserPoisonedMemoryMagic: + bug_descr = "use-after-poison"; + break; + case kAsanGlobalRedzoneMagic: + bug_descr = "global-buffer-overflow"; + break; + } + } + + Report("ERROR: AddressSanitizer %s on address " + "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n", + bug_descr, (void*)addr, pc, bp, sp); + + u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + Printf("%s of size %zu at %p thread T%d\n", + access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", + access_size, (void*)addr, curr_tid); + + GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp); + PrintStack(&stack); + + DescribeAddress(addr, access_size); + + PrintShadowMemoryForAddress(addr); +} + +void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { + error_report_callback = callback; + if (callback) { + error_message_buffer_size = 1 << 16; + error_message_buffer = + (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__); + error_message_buffer_pos = 0; + } +} + +// Provide default implementation of __asan_on_error that does nothing +// and may be overriden by user. +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +void __asan_on_error() {} diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h new file mode 100644 index 0000000..9710bd7 --- /dev/null +++ b/libsanitizer/asan/asan_report.h @@ -0,0 +1,51 @@ +//===-- asan_report.h -------------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for error reporting functions. +//===----------------------------------------------------------------------===// + +#include "asan_internal.h" +#include "asan_thread.h" +#include "sanitizer/asan_interface.h" + +namespace __asan { + +// The following functions prints address description depending +// on the memory type (shadow/heap/stack/global). +void DescribeHeapAddress(uptr addr, uptr access_size); +bool DescribeAddressIfGlobal(uptr addr); +bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g); +bool DescribeAddressIfShadow(uptr addr); +bool DescribeAddressIfStack(uptr addr, uptr access_size); +// Determines memory type on its own. +void DescribeAddress(uptr addr, uptr access_size); + +void DescribeThread(AsanThreadSummary *summary); + +// Different kinds of error reports. +void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr); +void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack); +void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *stack); +void NORETURN ReportMallocUsableSizeNotOwned(uptr addr, + StackTrace *stack); +void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr, + StackTrace *stack); +void NORETURN ReportStringFunctionMemoryRangesOverlap( + const char *function, const char *offset1, uptr length1, + const char *offset2, uptr length2, StackTrace *stack); + +// Mac-specific errors and warnings. +void WarnMacFreeUnallocated( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack); +void NORETURN ReportMacMzReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack); +void NORETURN ReportMacCfReallocUnknown( + uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack); + +} // namespace __asan diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc new file mode 100644 index 0000000..442d41c --- /dev/null +++ b/libsanitizer/asan/asan_rtl.cc @@ -0,0 +1,404 @@ +//===-- 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_allocator.h" +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_lock.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "asan_thread.h" +#include "asan_thread_registry.h" +#include "sanitizer/asan_interface.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" + +namespace __asan { + +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 (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) + UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); + if (death_callback) + death_callback(); + if (flags()->abort_on_error) + Abort(); + Exit(flags()->exitcode); +} + +static void AsanCheckFailed(const char *file, int line, const char *cond, + u64 v1, u64 v2) { + Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", + file, line, cond, (uptr)v1, (uptr)v2); + // FIXME: check for infinite recursion without a thread-local counter here. + PRINT_CURRENT_STACK(); + ShowStatsAndAbort(); +} + +// -------------------------- Flags ------------------------- {{{1 +static const int kDeafultMallocContextSize = 30; + +static Flags asan_flags; + +Flags *flags() { + return &asan_flags; +} + +static void ParseFlagsFromString(Flags *f, const char *str) { + ParseFlag(str, &f->quarantine_size, "quarantine_size"); + ParseFlag(str, &f->symbolize, "symbolize"); + ParseFlag(str, &f->verbosity, "verbosity"); + ParseFlag(str, &f->redzone, "redzone"); + CHECK(f->redzone >= 16); + CHECK(IsPowerOfTwo(f->redzone)); + + ParseFlag(str, &f->debug, "debug"); + ParseFlag(str, &f->report_globals, "report_globals"); + ParseFlag(str, &f->check_initialization_order, "initialization_order"); + ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); + CHECK((uptr)f->malloc_context_size <= kStackTraceMax); + + ParseFlag(str, &f->replace_str, "replace_str"); + ParseFlag(str, &f->replace_intrin, "replace_intrin"); + ParseFlag(str, &f->replace_cfallocator, "replace_cfallocator"); + ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free"); + ParseFlag(str, &f->use_fake_stack, "use_fake_stack"); + ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size"); + ParseFlag(str, &f->exitcode, "exitcode"); + ParseFlag(str, &f->allow_user_poisoning, "allow_user_poisoning"); + ParseFlag(str, &f->sleep_before_dying, "sleep_before_dying"); + ParseFlag(str, &f->handle_segv, "handle_segv"); + ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack"); + ParseFlag(str, &f->check_malloc_usable_size, "check_malloc_usable_size"); + ParseFlag(str, &f->unmap_shadow_on_exit, "unmap_shadow_on_exit"); + ParseFlag(str, &f->abort_on_error, "abort_on_error"); + ParseFlag(str, &f->atexit, "atexit"); + ParseFlag(str, &f->disable_core, "disable_core"); + ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix"); + ParseFlag(str, &f->allow_reexec, "allow_reexec"); + ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history"); + ParseFlag(str, &f->log_path, "log_path"); +} + +extern "C" { +SANITIZER_WEAK_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE +const char* __asan_default_options() { return ""; } +} // extern "C" + +void InitializeFlags(Flags *f, const char *env) { + internal_memset(f, 0, sizeof(*f)); + + f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; + f->symbolize = false; + f->verbosity = 0; + f->redzone = (ASAN_LOW_MEMORY) ? 64 : 128; + f->debug = false; + f->report_globals = 1; + f->check_initialization_order = true; + f->malloc_context_size = kDeafultMallocContextSize; + f->replace_str = true; + f->replace_intrin = true; + f->replace_cfallocator = true; + f->mac_ignore_invalid_free = false; + f->use_fake_stack = true; + f->max_malloc_fill_size = 0; + f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE; + f->allow_user_poisoning = true; + f->sleep_before_dying = 0; + f->handle_segv = ASAN_NEEDS_SEGV; + f->use_sigaltstack = false; + f->check_malloc_usable_size = true; + f->unmap_shadow_on_exit = false; + f->abort_on_error = false; + f->atexit = false; + f->disable_core = (__WORDSIZE == 64); + f->strip_path_prefix = ""; + f->allow_reexec = true; + f->print_full_thread_history = true; + f->log_path = 0; + + // Override from user-specified string. + ParseFlagsFromString(f, __asan_default_options()); + if (flags()->verbosity) { + Report("Using the defaults from __asan_default_options: %s\n", + __asan_default_options()); + } + + // Override from command line. + ParseFlagsFromString(f, env); +} + +// -------------------------- Globals --------------------- {{{1 +int asan_inited; +bool asan_init_is_running; +void (*death_callback)(void); + +// -------------------------- Misc ---------------- {{{1 +void ShowStatsAndAbort() { + __asan_print_accumulated_stats(); + Die(); +} + +// ---------------------- mmap -------------------- {{{1 +// Reserve memory range [beg, end]. +static void ReserveShadowMemoryRange(uptr beg, uptr end) { + CHECK((beg % kPageSize) == 0); + CHECK(((end + 1) % kPageSize) == 0); + uptr size = end - beg + 1; + void *res = MmapFixedNoReserve(beg, size); + if (res != (void*)beg) { + Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " + "Perhaps you're using ulimit -v\n", size); + Abort(); + } +} + +// --------------- 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); \ +void __asan_report_ ## type ## size(uptr addr) { \ + GET_CALLER_PC_BP_SP; \ + __asan_report_error(pc, bp, sp, addr, is_write, size); \ +} + +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) + +// 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. + 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_store1(0); break; + case 7: __asan_report_store2(0); break; + case 8: __asan_report_store4(0); break; + case 9: __asan_report_store8(0); break; + case 10: __asan_report_store16(0); break; + case 11: __asan_register_global(0, 0, 0); break; + case 12: __asan_register_globals(0, 0); break; + case 13: __asan_unregister_globals(0, 0); break; + case 14: __asan_set_death_callback(0); break; + case 15: __asan_set_error_report_callback(0); break; + case 16: __asan_handle_no_return(); break; + case 17: __asan_address_is_poisoned(0); break; + case 18: __asan_get_allocated_size(0); break; + case 19: __asan_get_current_allocated_bytes(); break; + case 20: __asan_get_estimated_allocated_size(0); break; + case 21: __asan_get_free_bytes(); break; + case 22: __asan_get_heap_size(); break; + case 23: __asan_get_ownership(0); break; + case 24: __asan_get_unmapped_bytes(); break; + case 25: __asan_poison_memory_region(0, 0); break; + case 26: __asan_unpoison_memory_region(0, 0); break; + case 27: __asan_set_error_exit_code(0); break; + case 28: __asan_stack_free(0, 0, 0); break; + case 29: __asan_stack_malloc(0, 0); break; + case 30: __asan_on_error(); break; + case 31: __asan_default_options(); break; + case 32: __asan_before_dynamic_init(0, 0); break; + case 33: __asan_after_dynamic_init(); break; + case 34: __asan_malloc_hook(0, 0); break; + case 35: __asan_free_hook(0); break; + case 36: __asan_symbolize(0, 0, 0); break; + } +} + +static void asan_atexit() { + Printf("AddressSanitizer exit stats:\n"); + __asan_print_accumulated_stats(); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +int NOINLINE __asan_set_error_exit_code(int exit_code) { + int old = flags()->exitcode; + flags()->exitcode = exit_code; + return old; +} + +void NOINLINE __asan_handle_no_return() { + int local_stack; + AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); + CHECK(curr_thread); + uptr top = curr_thread->stack_top(); + uptr bottom = ((uptr)&local_stack - kPageSize) & ~(kPageSize-1); + PoisonShadow(bottom, top - bottom, 0); +} + +void NOINLINE __asan_set_death_callback(void (*callback)(void)) { + death_callback = callback; +} + +void __asan_init() { + if (asan_inited) return; + CHECK(!asan_init_is_running && "ASan init calls itself!"); + asan_init_is_running = true; + + // Make sure we are not statically linked. + AsanDoesNotSupportStaticLinkage(); + + // Install tool-specific callbacks in sanitizer_common. + SetDieCallback(AsanDie); + SetCheckFailedCallback(AsanCheckFailed); + SetPrintfAndReportCallback(AppendToErrorMessageBuffer); + + // Initialize flags. This must be done early, because most of the + // initialization steps look at flags(). + const char *options = GetEnv("ASAN_OPTIONS"); + InitializeFlags(flags(), options); + __sanitizer_set_report_path(flags()->log_path); + + if (flags()->verbosity && options) { + Report("Parsed ASAN_OPTIONS: %s\n", options); + } + + // Re-exec ourselves if we need to set additional env or command line args. + MaybeReexec(); + + // Setup internal allocator callback. + SetLowLevelAllocateCallback(OnLowLevelAllocate); + + if (flags()->atexit) { + Atexit(asan_atexit); + } + + // interceptors + InitializeAsanInterceptors(); + + ReplaceSystemMalloc(); + ReplaceOperatorsNewAndDelete(); + + if (flags()->verbosity) { + Printf("|| `[%p, %p]` || HighMem ||\n", + (void*)kHighMemBeg, (void*)kHighMemEnd); + Printf("|| `[%p, %p]` || HighShadow ||\n", + (void*)kHighShadowBeg, (void*)kHighShadowEnd); + Printf("|| `[%p, %p]` || ShadowGap ||\n", + (void*)kShadowGapBeg, (void*)kShadowGapEnd); + Printf("|| `[%p, %p]` || LowShadow ||\n", + (void*)kLowShadowBeg, (void*)kLowShadowEnd); + Printf("|| `[%p, %p]` || LowMem ||\n", + (void*)kLowMemBeg, (void*)kLowMemEnd); + Printf("MemToShadow(shadow): %p %p %p %p\n", + (void*)MEM_TO_SHADOW(kLowShadowBeg), + (void*)MEM_TO_SHADOW(kLowShadowEnd), + (void*)MEM_TO_SHADOW(kHighShadowBeg), + (void*)MEM_TO_SHADOW(kHighShadowEnd)); + Printf("red_zone=%zu\n", (uptr)flags()->redzone); + Printf("malloc_context_size=%zu\n", (uptr)flags()->malloc_context_size); + + Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE); + Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY); + Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET); + CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7); + } + + if (flags()->disable_core) { + DisableCoreDumper(); + } + + uptr shadow_start = kLowShadowBeg; + if (kLowShadowBeg > 0) shadow_start -= kMmapGranularity; + uptr shadow_end = kHighShadowEnd; + if (MemoryRangeIsAvailable(shadow_start, shadow_end)) { + if (kLowShadowBeg != kLowShadowEnd) { + // mmap the low shadow plus at least one page. + ReserveShadowMemoryRange(kLowShadowBeg - kMmapGranularity, kLowShadowEnd); + } + // mmap the high shadow. + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); + // protect the gap + void *prot = Mprotect(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + CHECK(prot == (void*)kShadowGapBeg); + } else { + Report("Shadow memory range interleaves with an existing memory mapping. " + "ASan cannot proceed correctly. ABORTING.\n"); + DumpProcessMap(); + Die(); + } + + InstallSignalHandlers(); + // Start symbolizer process if necessary. + if (flags()->symbolize) { + const char *external_symbolizer = GetEnv("ASAN_SYMBOLIZER_PATH"); + if (external_symbolizer) { + InitializeExternalSymbolizer(external_symbolizer); + } + } + + // 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; + + asanThreadRegistry().Init(); + asanThreadRegistry().GetMain()->ThreadStart(); + force_interface_symbols(); // no-op. + + if (flags()->verbosity) { + Report("AddressSanitizer Init done\n"); + } +} + +#if defined(ASAN_USE_PREINIT_ARRAY) + // On Linux, we force __asan_init to be called before anyone else + // by placing it into .preinit_array section. + // FIXME: do we have anything like this on Mac? + __attribute__((section(".preinit_array"))) + typeof(__asan_init) *__asan_preinit =__asan_init; +#elif defined(_WIN32) && defined(_DLL) + // On Windows, when using dynamic CRT (/MD), we can put a pointer + // to __asan_init into the global list of C initializers. + // See crt0dat.c in the CRT sources for the details. + #pragma section(".CRT$XIB", long, read) // NOLINT + __declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init; +#endif diff --git a/libsanitizer/asan/asan_stack.cc b/libsanitizer/asan/asan_stack.cc new file mode 100644 index 0000000..2531a7f --- /dev/null +++ b/libsanitizer/asan/asan_stack.cc @@ -0,0 +1,35 @@ +//===-- 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_flags.h" +#include "asan_stack.h" +#include "sanitizer/asan_interface.h" + +namespace __asan { + +void PrintStack(StackTrace *stack) { + stack->PrintStack(stack->trace, stack->size, flags()->symbolize, + flags()->strip_path_prefix, __asan_symbolize); +} + +} // namespace __asan + +// ------------------ Interface -------------- {{{1 + +// Provide default implementation of __asan_symbolize that does nothing +// and may be overriden by user if he wants to use his own symbolization. +// ASan on Windows has its own implementation of this. +#ifndef _WIN32 +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) { + return false; +} +#endif diff --git a/libsanitizer/asan/asan_stack.h b/libsanitizer/asan/asan_stack.h new file mode 100644 index 0000000..da622ed --- /dev/null +++ b/libsanitizer/asan/asan_stack.h @@ -0,0 +1,52 @@ +//===-- asan_stack.h --------------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_stack.cc. +//===----------------------------------------------------------------------===// +#ifndef ASAN_STACK_H +#define ASAN_STACK_H + +#include "sanitizer_common/sanitizer_stacktrace.h" + +namespace __asan { + +void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp); +void PrintStack(StackTrace *stack); + +} // namespace __asan + +// Get the stack trace with the given pc and bp. +// The pc will be in the position 0 of the resulting stack trace. +// The bp may refer to the current frame or to the caller's frame. +// fast_unwind is currently unused. +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp) \ + StackTrace stack; \ + GetStackTrace(&stack, max_s, pc, bp) + +// NOTE: A Rule of thumb is to retrieve stack trace in the interceptors +// as early as possible (in functions exposed to the user), as we generally +// don't want stack trace to contain functions from ASan internals. + +#define GET_STACK_TRACE_HERE(max_size) \ + GET_STACK_TRACE_WITH_PC_AND_BP(max_size, \ + StackTrace::GetCurrentPc(), GET_CURRENT_FRAME()) + +#define GET_STACK_TRACE_HERE_FOR_MALLOC \ + GET_STACK_TRACE_HERE(flags()->malloc_context_size) + +#define GET_STACK_TRACE_HERE_FOR_FREE(ptr) \ + GET_STACK_TRACE_HERE(flags()->malloc_context_size) + +#define PRINT_CURRENT_STACK() \ + { \ + GET_STACK_TRACE_HERE(kStackTraceMax); \ + PrintStack(&stack); \ + } + +#endif // ASAN_STACK_H diff --git a/libsanitizer/asan/asan_stats.cc b/libsanitizer/asan/asan_stats.cc new file mode 100644 index 0000000..fa4adcb --- /dev/null +++ b/libsanitizer/asan/asan_stats.cc @@ -0,0 +1,86 @@ +//===-- 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_lock.h" +#include "asan_stats.h" +#include "asan_thread_registry.h" +#include "sanitizer/asan_interface.h" + +namespace __asan { + +AsanStats::AsanStats() { + CHECK(REAL(memset) != 0); + 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 (%zu full pages) mmaped in %zu calls\n", + mmaped>>20, mmaped / kPageSize, mmaps); + + PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size); + PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size); + PrintMallocStatsArray(" frees by size class: ", freed_by_size); + PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size); + Printf("Stats: malloc large: %zu small slow: %zu\n", + malloc_large, malloc_small_slow); +} + +static AsanLock print_lock(LINKER_INITIALIZED); + +static void PrintAccumulatedStats() { + AsanStats stats = asanThreadRegistry().GetAccumulatedStats(); + // Use lock to keep reports from mixing up. + ScopedLock lock(&print_lock); + stats.Print(); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +uptr __asan_get_current_allocated_bytes() { + return asanThreadRegistry().GetCurrentAllocatedBytes(); +} + +uptr __asan_get_heap_size() { + return asanThreadRegistry().GetHeapSize(); +} + +uptr __asan_get_free_bytes() { + return asanThreadRegistry().GetFreeBytes(); +} + +uptr __asan_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 new file mode 100644 index 0000000..c2b3298 --- /dev/null +++ b/libsanitizer/asan/asan_stats.h @@ -0,0 +1,65 @@ +//===-- asan_stats.h --------------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for statistics. +//===----------------------------------------------------------------------===// +#ifndef ASAN_STATS_H +#define ASAN_STATS_H + +#include "asan_allocator.h" +#include "asan_internal.h" + +namespace __asan { + +// AsanStats struct is NOT thread-safe. +// Each AsanThread has its own AsanStats, which are sometimes flushed +// to the accumulated AsanStats. +struct AsanStats { + // AsanStats must be a struct consisting of uptr fields only. + // When merging two AsanStats structs, we treat them as arrays of uptr. + uptr mallocs; + uptr malloced; + uptr malloced_redzones; + uptr frees; + uptr freed; + uptr real_frees; + uptr really_freed; + uptr really_freed_redzones; + uptr reallocs; + uptr realloced; + uptr mmaps; + uptr mmaped; + uptr mmaped_by_size[kNumberOfSizeClasses]; + uptr malloced_by_size[kNumberOfSizeClasses]; + uptr freed_by_size[kNumberOfSizeClasses]; + uptr really_freed_by_size[kNumberOfSizeClasses]; + + uptr malloc_large; + uptr malloc_small_slow; + + // Ctor for global AsanStats (accumulated stats and main thread stats). + explicit AsanStats(LinkerInitialized) { } + // Default ctor for thread-local stats. + AsanStats(); + + // Prints formatted stats to stderr. + void Print(); +}; + +// A cross-platform equivalent of malloc_statistics_t on Mac OS. +struct AsanMallocStats { + uptr blocks_in_use; + uptr size_in_use; + uptr max_size_in_use; + uptr size_allocated; +}; + +} // namespace __asan + +#endif // ASAN_STATS_H diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc new file mode 100644 index 0000000..9295c15 --- /dev/null +++ b/libsanitizer/asan/asan_thread.cc @@ -0,0 +1,153 @@ +//===-- 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_stack.h" +#include "asan_thread.h" +#include "asan_thread_registry.h" +#include "asan_mapping.h" +#include "sanitizer_common/sanitizer_common.h" + +namespace __asan { + +AsanThread::AsanThread(LinkerInitialized x) + : fake_stack_(x), + malloc_storage_(x), + stats_(x) { } + +AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine, + void *arg, StackTrace *stack) { + uptr size = RoundUpTo(sizeof(AsanThread), kPageSize); + AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__); + thread->start_routine_ = start_routine; + thread->arg_ = arg; + + const uptr kSummaryAllocSize = kPageSize; + CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize); + AsanThreadSummary *summary = + (AsanThreadSummary*)MmapOrDie(kPageSize, "AsanThreadSummary"); + summary->Init(parent_tid, stack); + summary->set_thread(thread); + thread->set_summary(summary); + + return thread; +} + +void AsanThreadSummary::TSDDtor(void *tsd) { + AsanThreadSummary *summary = (AsanThreadSummary*)tsd; + if (flags()->verbosity >= 1) { + Report("T%d TSDDtor\n", summary->tid()); + } + if (summary->thread()) { + summary->thread()->Destroy(); + } +} + +void AsanThread::Destroy() { + if (flags()->verbosity >= 1) { + Report("T%d exited\n", tid()); + } + + asanThreadRegistry().UnregisterThread(this); + CHECK(summary()->thread() == 0); + // We also clear the shadow on thread destruction because + // some code may still be executing in later TSD destructors + // and we don't want it to have any poisoned stack. + ClearShadowForThreadStack(); + fake_stack().Cleanup(); + uptr size = RoundUpTo(sizeof(AsanThread), kPageSize); + UnmapOrDie(this, size); +} + +void AsanThread::Init() { + SetThreadStackTopAndBottom(); + CHECK(AddrIsInMem(stack_bottom_)); + CHECK(AddrIsInMem(stack_top_)); + ClearShadowForThreadStack(); + if (flags()->verbosity >= 1) { + int local = 0; + Report("T%d: stack [%p,%p) size 0x%zx; local=%p\n", + tid(), (void*)stack_bottom_, (void*)stack_top_, + stack_top_ - stack_bottom_, &local); + } + fake_stack_.Init(stack_size()); + AsanPlatformThreadInit(); +} + +thread_return_t AsanThread::ThreadStart() { + Init(); + if (flags()->use_sigaltstack) SetAlternateSignalStack(); + + if (!start_routine_) { + // start_routine_ == 0 if we're on the main thread or on one of the + // OS X libdispatch worker threads. But nobody is supposed to call + // ThreadStart() for the worker threads. + CHECK(tid() == 0); + return 0; + } + + thread_return_t res = start_routine_(arg_); + malloc_storage().CommitBack(); + if (flags()->use_sigaltstack) UnsetAlternateSignalStack(); + + this->Destroy(); + + return res; +} + +void AsanThread::SetThreadStackTopAndBottom() { + GetThreadStackTopAndBottom(tid() == 0, &stack_top_, &stack_bottom_); + int local; + CHECK(AddrIsInStack((uptr)&local)); +} + +void AsanThread::ClearShadowForThreadStack() { + PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); +} + +const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { + uptr bottom = 0; + bool is_fake_stack = false; + if (AddrIsInStack(addr)) { + bottom = stack_bottom(); + } else { + bottom = fake_stack().AddrIsInFakeStack(addr); + CHECK(bottom); + is_fake_stack = true; + } + uptr aligned_addr = addr & ~(__WORDSIZE/8 - 1); // align addr. + u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr); + u8 *shadow_bottom = (u8*)MemToShadow(bottom); + + while (shadow_ptr >= shadow_bottom && + *shadow_ptr != kAsanStackLeftRedzoneMagic) { + shadow_ptr--; + } + + while (shadow_ptr >= shadow_bottom && + *shadow_ptr == kAsanStackLeftRedzoneMagic) { + shadow_ptr--; + } + + if (shadow_ptr < shadow_bottom) { + *offset = 0; + return "UNKNOWN"; + } + + uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1)); + CHECK((ptr[0] == kCurrentStackFrameMagic) || + (is_fake_stack && ptr[0] == kRetiredStackFrameMagic)); + *offset = addr - (uptr)ptr; + return (const char*)ptr[1]; +} + +} // namespace __asan diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h new file mode 100644 index 0000000..dff8c88 --- /dev/null +++ b/libsanitizer/asan/asan_thread.h @@ -0,0 +1,103 @@ +//===-- asan_thread.h -------------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_thread.cc. +//===----------------------------------------------------------------------===// +#ifndef ASAN_THREAD_H +#define ASAN_THREAD_H + +#include "asan_allocator.h" +#include "asan_internal.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "sanitizer_common/sanitizer_libc.h" + +namespace __asan { + +const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits. + +class AsanThread; + +// These objects are created for every thread and are never deleted, +// so we can find them by tid even if the thread is long dead. +class AsanThreadSummary { + public: + explicit AsanThreadSummary(LinkerInitialized) { } // for T0. + void Init(u32 parent_tid, StackTrace *stack) { + parent_tid_ = parent_tid; + announced_ = false; + tid_ = kInvalidTid; + if (stack) { + internal_memcpy(&stack_, stack, sizeof(*stack)); + } + thread_ = 0; + } + u32 tid() { return tid_; } + void set_tid(u32 tid) { tid_ = tid; } + u32 parent_tid() { return parent_tid_; } + bool announced() { return announced_; } + void set_announced(bool announced) { announced_ = announced; } + StackTrace *stack() { return &stack_; } + AsanThread *thread() { return thread_; } + void set_thread(AsanThread *thread) { thread_ = thread; } + static void TSDDtor(void *tsd); + + private: + u32 tid_; + u32 parent_tid_; + bool announced_; + StackTrace stack_; + AsanThread *thread_; +}; + +// AsanThread are stored in TSD and destroyed when the thread dies. +class AsanThread { + public: + explicit AsanThread(LinkerInitialized); // for T0. + static AsanThread *Create(u32 parent_tid, thread_callback_t start_routine, + void *arg, StackTrace *stack); + void Destroy(); + + void Init(); // Should be called from the thread itself. + thread_return_t ThreadStart(); + + uptr stack_top() { return stack_top_; } + uptr stack_bottom() { return stack_bottom_; } + uptr stack_size() { return stack_top_ - stack_bottom_; } + u32 tid() { return summary_->tid(); } + AsanThreadSummary *summary() { return summary_; } + void set_summary(AsanThreadSummary *summary) { summary_ = summary; } + + const char *GetFrameNameByAddr(uptr addr, uptr *offset); + + bool AddrIsInStack(uptr addr) { + return addr >= stack_bottom_ && addr < stack_top_; + } + + FakeStack &fake_stack() { return fake_stack_; } + AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } + AsanStats &stats() { return stats_; } + + private: + void SetThreadStackTopAndBottom(); + void ClearShadowForThreadStack(); + AsanThreadSummary *summary_; + thread_callback_t start_routine_; + void *arg_; + uptr stack_top_; + uptr stack_bottom_; + + FakeStack fake_stack_; + AsanThreadLocalMallocStorage malloc_storage_; + AsanStats stats_; +}; + +} // namespace __asan + +#endif // ASAN_THREAD_H diff --git a/libsanitizer/asan/asan_thread_registry.cc b/libsanitizer/asan/asan_thread_registry.cc new file mode 100644 index 0000000..840837e --- /dev/null +++ b/libsanitizer/asan/asan_thread_registry.cc @@ -0,0 +1,188 @@ +//===-- asan_thread_registry.cc -------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// AsanThreadRegistry-related code. AsanThreadRegistry is a container +// for summaries of all created threads. +//===----------------------------------------------------------------------===// + +#include "asan_stack.h" +#include "asan_thread.h" +#include "asan_thread_registry.h" +#include "sanitizer_common/sanitizer_common.h" + +namespace __asan { + +static AsanThreadRegistry asan_thread_registry(LINKER_INITIALIZED); + +AsanThreadRegistry &asanThreadRegistry() { + return asan_thread_registry; +} + +AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x) + : main_thread_(x), + main_thread_summary_(x), + accumulated_stats_(x), + max_malloced_memory_(x), + mu_(x) { } + +void AsanThreadRegistry::Init() { + AsanTSDInit(AsanThreadSummary::TSDDtor); + main_thread_.set_summary(&main_thread_summary_); + main_thread_summary_.set_thread(&main_thread_); + RegisterThread(&main_thread_); + SetCurrent(&main_thread_); + // At this point only one thread exists. + inited_ = true; +} + +void AsanThreadRegistry::RegisterThread(AsanThread *thread) { + ScopedLock lock(&mu_); + u32 tid = n_threads_; + n_threads_++; + CHECK(n_threads_ < kMaxNumberOfThreads); + + AsanThreadSummary *summary = thread->summary(); + CHECK(summary != 0); + summary->set_tid(tid); + thread_summaries_[tid] = summary; +} + +void AsanThreadRegistry::UnregisterThread(AsanThread *thread) { + ScopedLock lock(&mu_); + FlushToAccumulatedStatsUnlocked(&thread->stats()); + AsanThreadSummary *summary = thread->summary(); + CHECK(summary); + summary->set_thread(0); +} + +AsanThread *AsanThreadRegistry::GetMain() { + return &main_thread_; +} + +AsanThread *AsanThreadRegistry::GetCurrent() { + AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet(); + if (!summary) { +#if ASAN_ANDROID + // On Android, libc constructor is called _after_ asan_init, and cleans up + // TSD. Try to figure out if this is still the main thread by the stack + // address. We are not entirely sure that we have correct main thread + // limits, so only do this magic on Android, and only if the found thread is + // the main thread. + AsanThread* thread = FindThreadByStackAddress((uptr)&summary); + if (thread && thread->tid() == 0) { + SetCurrent(thread); + return thread; + } +#endif + return 0; + } + return summary->thread(); +} + +void AsanThreadRegistry::SetCurrent(AsanThread *t) { + CHECK(t->summary()); + if (flags()->verbosity >= 2) { + Report("SetCurrent: %p for thread %p\n", + t->summary(), (void*)GetThreadSelf()); + } + // Make sure we do not reset the current AsanThread. + CHECK(AsanTSDGet() == 0); + AsanTSDSet(t->summary()); + CHECK(AsanTSDGet() == t->summary()); +} + +AsanStats &AsanThreadRegistry::GetCurrentThreadStats() { + AsanThread *t = GetCurrent(); + return (t) ? t->stats() : main_thread_.stats(); +} + +AsanStats AsanThreadRegistry::GetAccumulatedStats() { + ScopedLock lock(&mu_); + UpdateAccumulatedStatsUnlocked(); + return accumulated_stats_; +} + +uptr AsanThreadRegistry::GetCurrentAllocatedBytes() { + ScopedLock lock(&mu_); + UpdateAccumulatedStatsUnlocked(); + return accumulated_stats_.malloced - accumulated_stats_.freed; +} + +uptr AsanThreadRegistry::GetHeapSize() { + ScopedLock lock(&mu_); + UpdateAccumulatedStatsUnlocked(); + return accumulated_stats_.mmaped; +} + +uptr AsanThreadRegistry::GetFreeBytes() { + ScopedLock lock(&mu_); + UpdateAccumulatedStatsUnlocked(); + return accumulated_stats_.mmaped + - accumulated_stats_.malloced + - accumulated_stats_.malloced_redzones + + accumulated_stats_.really_freed + + accumulated_stats_.really_freed_redzones; +} + +// Return several stats counters with a single call to +// UpdateAccumulatedStatsUnlocked(). +void AsanThreadRegistry::FillMallocStatistics(AsanMallocStats *malloc_stats) { + ScopedLock lock(&mu_); + UpdateAccumulatedStatsUnlocked(); + malloc_stats->blocks_in_use = accumulated_stats_.mallocs; + malloc_stats->size_in_use = accumulated_stats_.malloced; + malloc_stats->max_size_in_use = max_malloced_memory_; + malloc_stats->size_allocated = accumulated_stats_.mmaped; +} + +AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) { + CHECK(tid < n_threads_); + CHECK(thread_summaries_[tid]); + return thread_summaries_[tid]; +} + +AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) { + ScopedLock lock(&mu_); + for (u32 tid = 0; tid < n_threads_; tid++) { + AsanThread *t = thread_summaries_[tid]->thread(); + if (!t || !(t->fake_stack().StackSize())) continue; + if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) { + return t; + } + } + return 0; +} + +void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() { + for (u32 tid = 0; tid < n_threads_; tid++) { + AsanThread *t = thread_summaries_[tid]->thread(); + if (t != 0) { + FlushToAccumulatedStatsUnlocked(&t->stats()); + } + } + // This is not very accurate: we may miss allocation peaks that happen + // between two updates of accumulated_stats_. For more accurate bookkeeping + // the maximum should be updated on every malloc(), which is unacceptable. + if (max_malloced_memory_ < accumulated_stats_.malloced) { + max_malloced_memory_ = accumulated_stats_.malloced; + } +} + +void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) { + // AsanStats consists of variables of type uptr only. + uptr *dst = (uptr*)&accumulated_stats_; + uptr *src = (uptr*)stats; + uptr num_fields = sizeof(AsanStats) / sizeof(uptr); + for (uptr i = 0; i < num_fields; i++) { + dst[i] += src[i]; + src[i] = 0; + } +} + +} // namespace __asan diff --git a/libsanitizer/asan/asan_thread_registry.h b/libsanitizer/asan/asan_thread_registry.h new file mode 100644 index 0000000..99d5cb5 --- /dev/null +++ b/libsanitizer/asan/asan_thread_registry.h @@ -0,0 +1,83 @@ +//===-- asan_thread_registry.h ----------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_thread_registry.cc +//===----------------------------------------------------------------------===// + +#ifndef ASAN_THREAD_REGISTRY_H +#define ASAN_THREAD_REGISTRY_H + +#include "asan_lock.h" +#include "asan_stack.h" +#include "asan_stats.h" +#include "asan_thread.h" + +namespace __asan { + +// Stores summaries of all created threads, returns current thread, +// thread by tid, thread by stack address. There is a single instance +// of AsanThreadRegistry for the whole program. +// AsanThreadRegistry is thread-safe. +class AsanThreadRegistry { + public: + explicit AsanThreadRegistry(LinkerInitialized); + void Init(); + void RegisterThread(AsanThread *thread); + void UnregisterThread(AsanThread *thread); + + AsanThread *GetMain(); + // Get the current thread. May return 0. + AsanThread *GetCurrent(); + void SetCurrent(AsanThread *t); + + u32 GetCurrentTidOrInvalid() { + if (!inited_) return 0; + AsanThread *t = GetCurrent(); + return t ? t->tid() : kInvalidTid; + } + + // Returns stats for GetCurrent(), or stats for + // T0 if GetCurrent() returns 0. + AsanStats &GetCurrentThreadStats(); + // Flushes all thread-local stats to accumulated stats, and returns + // a copy of accumulated stats. + AsanStats GetAccumulatedStats(); + uptr GetCurrentAllocatedBytes(); + uptr GetHeapSize(); + uptr GetFreeBytes(); + void FillMallocStatistics(AsanMallocStats *malloc_stats); + + AsanThreadSummary *FindByTid(u32 tid); + AsanThread *FindThreadByStackAddress(uptr addr); + + private: + void UpdateAccumulatedStatsUnlocked(); + // Adds values of all counters in "stats" to accumulated stats, + // and fills "stats" with zeroes. + void FlushToAccumulatedStatsUnlocked(AsanStats *stats); + + static const u32 kMaxNumberOfThreads = (1 << 22); // 4M + AsanThreadSummary *thread_summaries_[kMaxNumberOfThreads]; + AsanThread main_thread_; + AsanThreadSummary main_thread_summary_; + AsanStats accumulated_stats_; + // Required for malloc_zone_statistics() on OS X. This can't be stored in + // per-thread AsanStats. + uptr max_malloced_memory_; + u32 n_threads_; + AsanLock mu_; + bool inited_; +}; + +// Returns a single instance of registry. +AsanThreadRegistry &asanThreadRegistry(); + +} // namespace __asan + +#endif // ASAN_THREAD_REGISTRY_H diff --git a/libsanitizer/asan/asan_win.cc b/libsanitizer/asan/asan_win.cc new file mode 100644 index 0000000..a5c0441 --- /dev/null +++ b/libsanitizer/asan/asan_win.cc @@ -0,0 +1,190 @@ +//===-- 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. +//===----------------------------------------------------------------------===// +#ifdef _WIN32 +#include <windows.h> + +#include <dbghelp.h> +#include <stdlib.h> + +#include <new> // FIXME: temporarily needed for placement new in AsanLock. + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_lock.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_libc.h" + +namespace __asan { + +// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1 +static AsanLock dbghelp_lock(LINKER_INITIALIZED); +static bool dbghelp_initialized = false; +#pragma comment(lib, "dbghelp.lib") + +void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp) { + stack->max_size = max_s; + void *tmp[kStackTraceMax]; + + // FIXME: CaptureStackBackTrace might be too slow for us. + // FIXME: Compare with StackWalk64. + // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc + uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0); + uptr offset = 0; + // Skip the RTL frames by searching for the PC in the stacktrace. + // FIXME: this doesn't work well for the malloc/free stacks yet. + for (uptr i = 0; i < cs_ret; i++) { + if (pc != (uptr)tmp[i]) + continue; + offset = i; + break; + } + + stack->size = cs_ret - offset; + for (uptr i = 0; i < stack->size; i++) + stack->trace[i] = (uptr)tmp[i + offset]; +} + +// ---------------------- AsanLock ---------------- {{{1 +enum LockState { + LOCK_UNINITIALIZED = 0, + LOCK_READY = -1, +}; + +AsanLock::AsanLock(LinkerInitialized li) { + // FIXME: see comments in AsanLock::Lock() for the details. + CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED); + + CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); + InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); + owner_ = LOCK_READY; +} + +void AsanLock::Lock() { + if (owner_ == LOCK_UNINITIALIZED) { + // FIXME: hm, global AsanLock objects are not initialized?!? + // This might be a side effect of the clang+cl+link Frankenbuild... + new(this) AsanLock((LinkerInitialized)(LINKER_INITIALIZED + 1)); + + // FIXME: If it turns out the linker doesn't invoke our + // constructors, we should probably manually Lock/Unlock all the global + // locks while we're starting in one thread to avoid double-init races. + } + EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_); + CHECK(owner_ == LOCK_READY); + owner_ = GetThreadSelf(); +} + +void AsanLock::Unlock() { + CHECK(owner_ == GetThreadSelf()); + owner_ = LOCK_READY; + LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); +} + +// ---------------------- TSD ---------------- {{{1 +static bool tsd_key_inited = false; + +static __declspec(thread) void *fake_tsd = 0; + +void AsanTSDInit(void (*destructor)(void *tsd)) { + // FIXME: we're ignoring the destructor for now. + tsd_key_inited = true; +} + +void *AsanTSDGet() { + CHECK(tsd_key_inited); + return fake_tsd; +} + +void AsanTSDSet(void *tsd) { + CHECK(tsd_key_inited); + fake_tsd = tsd; +} + +// ---------------------- Various stuff ---------------- {{{1 +void MaybeReexec() { + // No need to re-exec on Windows. +} + +void *AsanDoesNotSupportStaticLinkage() { +#if defined(_DEBUG) +#error Please build the runtime with a non-debug CRT: /MD or /MT +#endif + return 0; +} + +void SetAlternateSignalStack() { + // FIXME: Decide what to do on Windows. +} + +void UnsetAlternateSignalStack() { + // FIXME: Decide what to do on Windows. +} + +void InstallSignalHandlers() { + // FIXME: Decide what to do on Windows. +} + +void AsanPlatformThreadInit() { + // Nothing here for now. +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +bool __asan_symbolize(const void *addr, char *out_buffer, int buffer_size) { + ScopedLock lock(&dbghelp_lock); + if (!dbghelp_initialized) { + SymSetOptions(SYMOPT_DEFERRED_LOADS | + SYMOPT_UNDNAME | + SYMOPT_LOAD_LINES); + CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE)); + // FIXME: We don't call SymCleanup() on exit yet - should we? + dbghelp_initialized = true; + } + + // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + DWORD64 offset = 0; + BOOL got_objname = SymFromAddr(GetCurrentProcess(), + (DWORD64)addr, &offset, symbol); + if (!got_objname) + return false; + + DWORD unused; + IMAGEHLP_LINE64 info; + info.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), + (DWORD64)addr, &unused, &info); + int written = 0; + out_buffer[0] = '\0'; + // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too. + if (got_fileline) { + written += internal_snprintf(out_buffer + written, buffer_size - written, + " %s %s:%d", symbol->Name, + info.FileName, info.LineNumber); + } else { + written += internal_snprintf(out_buffer + written, buffer_size - written, + " %s+0x%p", symbol->Name, offset); + } + return true; +} +} // extern "C" + + +#endif // _WIN32 diff --git a/libsanitizer/asan/libtool-version b/libsanitizer/asan/libtool-version new file mode 100644 index 0000000..204fdd2 --- /dev/null +++ b/libsanitizer/asan/libtool-version @@ -0,0 +1,6 @@ +# This file is used to maintain libtool version info for libmudflap. See +# the libtool manual to understand the meaning of the fields. This is +# a separate file so that version updates don't involve re-running +# automake. +# CURRENT:REVISION:AGE +0:0:0 |